From 4bf84c35c65f36a344fb7a6cde6274df4120efb8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Dec 2012 23:49:37 +0000 Subject: net: add change_carrier netdev op This allows a driver to register change_carrier callback which will be called whenever user will like to change carrier state. This is useful for devices like dummy, gre, team and so on. Signed-off-by: Jiri Pirko Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- net/core/dev.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 515473ee52cb..21c5b976dcf3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5027,6 +5027,25 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) } EXPORT_SYMBOL(dev_set_mac_address); +/** + * dev_change_carrier - Change device carrier + * @dev: device + * @new_carries: new value + * + * Change device carrier + */ +int dev_change_carrier(struct net_device *dev, bool new_carrier) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_change_carrier) + return -EOPNOTSUPP; + if (!netif_device_present(dev)) + return -ENODEV; + return ops->ndo_change_carrier(dev, new_carrier); +} +EXPORT_SYMBOL(dev_change_carrier); + /* * Perform the SIOCxIFxxx calls, inside rcu_read_lock() */ -- cgit v1.2.3 From fdae0fde5352790030d89edd09f1b43b9bed97f8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Dec 2012 23:49:38 +0000 Subject: net: allow to change carrier via sysfs Make carrier writable Signed-off-by: Jiri Pirko Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 28c5f5aa7ca7..29c884a74c38 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -126,6 +126,19 @@ static ssize_t show_broadcast(struct device *dev, return -EINVAL; } +static int change_carrier(struct net_device *net, unsigned long new_carrier) +{ + if (!netif_running(net)) + return -EINVAL; + return dev_change_carrier(net, (bool) new_carrier); +} + +static ssize_t store_carrier(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + return netdev_store(dev, attr, buf, len, change_carrier); +} + static ssize_t show_carrier(struct device *dev, struct device_attribute *attr, char *buf) { @@ -331,7 +344,7 @@ static struct device_attribute net_class_attributes[] = { __ATTR(link_mode, S_IRUGO, show_link_mode, NULL), __ATTR(address, S_IRUGO, show_address, NULL), __ATTR(broadcast, S_IRUGO, show_broadcast, NULL), - __ATTR(carrier, S_IRUGO, show_carrier, NULL), + __ATTR(carrier, S_IRUGO | S_IWUSR, show_carrier, store_carrier), __ATTR(speed, S_IRUGO, show_speed, NULL), __ATTR(duplex, S_IRUGO, show_duplex, NULL), __ATTR(dormant, S_IRUGO, show_dormant, NULL), -- cgit v1.2.3 From 9a57247f31e361f80508c40363366222dbbb6aa5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Dec 2012 23:49:39 +0000 Subject: rtnl: expose carrier value with possibility to set it Signed-off-by: Jiri Pirko Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- Documentation/networking/operstates.txt | 4 ++++ include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 10 ++++++++++ 3 files changed, 15 insertions(+) (limited to 'net') diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt index 1a77a3cfae54..97694572338b 100644 --- a/Documentation/networking/operstates.txt +++ b/Documentation/networking/operstates.txt @@ -88,6 +88,10 @@ set this flag. On netif_carrier_off(), the scheduler stops sending packets. The name 'carrier' and the inversion are historical, think of it as lower layer. +Note that for certain kind of soft-devices, which are not managing any +real hardware, there is possible to set this bit from userpsace. +One should use TVL IFLA_CARRIER to do so. + netif_carrier_ok() can be used to query that bit. __LINK_STATE_DORMANT, maps to IFF_DORMANT: diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 60f3b6b90602..c4edfe11f1f7 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -142,6 +142,7 @@ enum { #define IFLA_PROMISCUITY IFLA_PROMISCUITY IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1868625af25e..2ef7a56ba117 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -780,6 +780,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_MTU */ + nla_total_size(4) /* IFLA_LINK */ + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(1) /* IFLA_CARRIER */ + nla_total_size(4) /* IFLA_PROMISCUITY */ + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */ + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */ @@ -909,6 +910,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u32(skb, IFLA_LINK, dev->iflink)) || (dev->master && nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || (dev->ifalias && @@ -1108,6 +1110,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_MTU] = { .type = NLA_U32 }, [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, [IFLA_TXQLEN] = { .type = NLA_U32 }, [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, @@ -1438,6 +1441,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, modified = 1; } + if (tb[IFLA_CARRIER]) { + err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER])); + if (err) + goto errout; + modified = 1; + } + if (tb[IFLA_TXQLEN]) dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); -- cgit v1.2.3 From b2111724a639ec31a19fdca62ea3a0a222d59d11 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Dec 2012 06:06:37 +0000 Subject: net: use per task frag allocator in skb_append_datato_frags Use the new per task frag allocator in skb_append_datato_frags(), to reduce number of frags and page allocator overhead. Tested: ifconfig lo mtu 16436 perf record netperf -t UDP_STREAM ; perf report before : Throughput: 32928 Mbit/s 51.79% netperf [kernel.kallsyms] [k] copy_user_generic_string 5.98% netperf [kernel.kallsyms] [k] __alloc_pages_nodemask 5.58% netperf [kernel.kallsyms] [k] get_page_from_freelist 5.01% netperf [kernel.kallsyms] [k] __rmqueue 3.74% netperf [kernel.kallsyms] [k] skb_append_datato_frags 1.87% netperf [kernel.kallsyms] [k] prep_new_page 1.42% netperf [kernel.kallsyms] [k] next_zones_zonelist 1.28% netperf [kernel.kallsyms] [k] __inc_zone_state 1.26% netperf [kernel.kallsyms] [k] alloc_pages_current 0.78% netperf [kernel.kallsyms] [k] sock_alloc_send_pskb 0.74% netperf [kernel.kallsyms] [k] udp_sendmsg 0.72% netperf [kernel.kallsyms] [k] zone_watermark_ok 0.68% netperf [kernel.kallsyms] [k] __cpuset_node_allowed_softwall 0.67% netperf [kernel.kallsyms] [k] fib_table_lookup 0.60% netperf [kernel.kallsyms] [k] memcpy_fromiovecend 0.55% netperf [kernel.kallsyms] [k] __udp4_lib_lookup after: Throughput: 47185 Mbit/s 61.74% netperf [kernel.kallsyms] [k] copy_user_generic_string 2.07% netperf [kernel.kallsyms] [k] prep_new_page 1.98% netperf [kernel.kallsyms] [k] skb_append_datato_frags 1.02% netperf [kernel.kallsyms] [k] sock_alloc_send_pskb 0.97% netperf [kernel.kallsyms] [k] enqueue_task_fair 0.97% netperf [kernel.kallsyms] [k] udp_sendmsg 0.91% netperf [kernel.kallsyms] [k] __ip_route_output_key 0.88% netperf [kernel.kallsyms] [k] __netif_receive_skb 0.87% netperf [kernel.kallsyms] [k] fib_table_lookup 0.85% netperf [kernel.kallsyms] [k] resched_task 0.78% netperf [kernel.kallsyms] [k] __udp4_lib_lookup 0.77% netperf [kernel.kallsyms] [k] _raw_spin_lock_irqsave Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/skbuff.c | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 3ab989b0de42..ec8737ec59b5 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2686,48 +2686,37 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, int len, int odd, struct sk_buff *skb), void *from, int length) { - int frg_cnt = 0; - skb_frag_t *frag = NULL; - struct page *page = NULL; - int copy, left; + int frg_cnt = skb_shinfo(skb)->nr_frags; + int copy; int offset = 0; int ret; + struct page_frag *pfrag = ¤t->task_frag; do { /* Return error if we don't have space for new frag */ - frg_cnt = skb_shinfo(skb)->nr_frags; if (frg_cnt >= MAX_SKB_FRAGS) - return -EFAULT; - - /* allocate a new page for next frag */ - page = alloc_pages(sk->sk_allocation, 0); + return -EMSGSIZE; - /* If alloc_page fails just return failure and caller will - * free previous allocated pages by doing kfree_skb() - */ - if (page == NULL) + if (!sk_page_frag_refill(sk, pfrag)) return -ENOMEM; - /* initialize the next frag */ - skb_fill_page_desc(skb, frg_cnt, page, 0, 0); - skb->truesize += PAGE_SIZE; - atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc); - - /* get the new initialized frag */ - frg_cnt = skb_shinfo(skb)->nr_frags; - frag = &skb_shinfo(skb)->frags[frg_cnt - 1]; - /* copy the user data to page */ - left = PAGE_SIZE - frag->page_offset; - copy = (length > left)? left : length; + copy = min_t(int, length, pfrag->size - pfrag->offset); - ret = getfrag(from, skb_frag_address(frag) + skb_frag_size(frag), - offset, copy, 0, skb); + ret = getfrag(from, page_address(pfrag->page) + pfrag->offset, + offset, copy, 0, skb); if (ret < 0) return -EFAULT; /* copy was successful so update the size parameters */ - skb_frag_size_add(frag, copy); + skb_fill_page_desc(skb, frg_cnt, pfrag->page, pfrag->offset, + copy); + frg_cnt++; + pfrag->offset += copy; + get_page(pfrag->page); + + skb->truesize += copy; + atomic_add(copy, &sk->sk_wmem_alloc); skb->len += copy; skb->data_len += copy; offset += copy; -- cgit v1.2.3 From bd2a13e2ebedc084ca4d5bc77e8ed03cf793a130 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Dec 2012 18:18:55 +0000 Subject: sctp: make sctp_addr_wq_timeout_handler static Fix sparse warning about local function that should be static. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/sctp/protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index f898b1c58bd2..1c2e46cb9191 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -595,7 +595,7 @@ static void sctp_v4_ecn_capable(struct sock *sk) INET_ECN_xmit(sk); } -void sctp_addr_wq_timeout_handler(unsigned long arg) +static void sctp_addr_wq_timeout_handler(unsigned long arg) { struct net *net = (struct net *)arg; struct sctp_sockaddr_entry *addrw, *temp; -- cgit v1.2.3 From bb717d764926b8748aecd18e3531189b323f6e4d Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Dec 2012 18:20:24 +0000 Subject: tcp: make proc_tcp_fastopen_key static Detected by sparse. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv4/sysctl_net_ipv4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d84400b65049..7547a6d238a2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -232,8 +232,8 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, return 0; } -int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer, - size_t *lenp, loff_t *ppos) +static int proc_tcp_fastopen_key(ctl_table *ctl, int write, void __user *buffer, + size_t *lenp, loff_t *ppos) { ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; -- cgit v1.2.3 From 61c5e88aecd6fbf2480f39394bb495964e6d9984 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Dec 2012 18:24:28 +0000 Subject: skbuff: make __kmalloc_reserve static Sparse detected case where this local function should be static. It may even allow some compiler optimizations. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/skbuff.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ec8737ec59b5..bc96100fe23c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -155,8 +155,9 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) */ #define kmalloc_reserve(size, gfp, node, pfmemalloc) \ __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc) -void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, - bool *pfmemalloc) + +static void *__kmalloc_reserve(size_t size, gfp_t flags, int node, + unsigned long ip, bool *pfmemalloc) { void *obj; bool ret_pfmemalloc = false; -- cgit v1.2.3 From aa1113d9f85da59dcbdd32aeb5d71da566e46def Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 28 Dec 2012 10:50:17 +0000 Subject: net: filter: return -EINVAL if BPF_S_ANC* operation is not supported Currently, we return -EINVAL for malformed or wrong BPF filters. However, this is not done for BPF_S_ANC* operations, which makes it more difficult to detect if it's actually supported or not by the BPF machine. Therefore, we should also return -EINVAL if K is within the SKF_AD_OFF universe and the ancillary operation did not match. Why exactly is it needed? If tools such as libpcap/tcpdump want to make use of new ancillary operations (like filtering VLAN in kernel space), there is currently no sane way to test if this feature / BPF_S_ANC* op is present or not, since no error is returned. This patch will make life easier for that and allow for a proper usage for user space applications. There was concern, if this patch will break userland. Short answer: Yes and no. Long answer: It will "break" only for code that calls ... { BPF_LD | BPF_(W|H|B) | BPF_ABS, 0, 0, }, ... where is in [0xfffff000, 0xffffffff] _and_ is *not* an ancillary. And here comes the BUT: assuming some *old* code will have such an instruction where is between [0xfffff000, 0xffffffff] and it doesn't know ancillary operations, then this will give a non-expected / unwanted behavior as well (since we do not return the BPF machine with 0 after a failed load_pointer(), which was the case before introducing ancillary operations, but load sth. into the accumulator instead, and continue with the next instruction, for instance). Thus, user space code would already have been broken by introducing ancillary operations into the BPF machine per se. Code that does such a direct load, e.g. "load word at packet offset 0xffffffff into accumulator" ("ld [0xffffffff]") is quite broken, isn't it? The whole assumption of ancillary operations is that no-one intentionally calls things like "ld [0xffffffff]" and expect this word to be loaded from such a packet offset. Hence, we can also safely make use of this feature testing patch and facilitate application development. Therefore, at least from this patch onwards, we have *for sure* a check whether current or in future implemented BPF_S_ANC* ops are supported in the kernel. Patch was tested on x86_64. (Thanks to Eric for the previous review.) Cc: Eric Dumazet Reported-by: Ani Sinha Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/core/filter.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index c23543cba132..2ead2a9b1859 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -532,6 +532,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X, }; int pc; + bool anc_found; if (flen == 0 || flen > BPF_MAXINSNS) return -EINVAL; @@ -592,8 +593,10 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) case BPF_S_LD_W_ABS: case BPF_S_LD_H_ABS: case BPF_S_LD_B_ABS: + anc_found = false; #define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \ code = BPF_S_ANC_##CODE; \ + anc_found = true; \ break switch (ftest->k) { ANCILLARY(PROTOCOL); @@ -610,6 +613,10 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ANCILLARY(VLAN_TAG); ANCILLARY(VLAN_TAG_PRESENT); } + + /* ancillary operation unknown or unsupported */ + if (anc_found == false && ftest->k >= SKF_AD_OFF) + return -EINVAL; } ftest->code = code; } -- cgit v1.2.3 From 576eb62598f10c8c7fd75703fe89010cdcfff596 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Dec 2012 18:15:22 +0000 Subject: bridge: respect RFC2863 operational state The bridge link detection should follow the operational state of the lower device, rather than the carrier bit. This allows devices like tunnels that are controlled by userspace control plane to work with bridge STP link management. Signed-off-by: Stephen Hemminger Reviewed-by: Flavio Leitner Signed-off-by: David S. Miller --- net/bridge/br_if.c | 6 +++--- net/bridge/br_netlink.c | 5 ++++- net/bridge/br_notify.c | 2 +- net/bridge/br_stp_if.c | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 37fe693471a8..1edd71d44a4c 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -66,14 +66,14 @@ void br_port_carrier_check(struct net_bridge_port *p) struct net_device *dev = p->dev; struct net_bridge *br = p->br; - if (netif_running(dev) && netif_carrier_ok(dev)) + if (netif_running(dev) && netif_oper_up(dev)) p->path_cost = port_cost(dev); if (!netif_running(br->dev)) return; spin_lock_bh(&br->lock); - if (netif_running(dev) && netif_carrier_ok(dev)) { + if (netif_running(dev) && netif_oper_up(dev)) { if (p->state == BR_STATE_DISABLED) br_stp_enable_port(p); } else { @@ -383,7 +383,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) spin_lock_bh(&br->lock); changed_addr = br_stp_recalculate_bridge_id(br); - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && + if (netif_running(dev) && netif_oper_up(dev) && (br->dev->flags & IFF_UP)) br_stp_enable_port(p); spin_unlock_bh(&br->lock); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 97ba0189c6f7..caa3f6919436 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -181,8 +181,11 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state) if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; + /* if device is not up, change is not allowed + * if link is not present, only allowable state is disabled + */ if (!netif_running(p->dev) || - (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED)) + (!netif_oper_up(p->dev) && state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = state; diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index a76b62135558..1644b3e1f947 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -82,7 +82,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v break; case NETDEV_UP: - if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) { + if (netif_running(br->dev) && netif_oper_up(dev)) { spin_lock_bh(&br->lock); br_stp_enable_port(p); spin_unlock_bh(&br->lock); diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 9d5a414a3943..7b5197c7de13 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -54,7 +54,7 @@ void br_stp_enable_bridge(struct net_bridge *br) br_config_bpdu_generation(br); list_for_each_entry(p, &br->port_list, list) { - if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev)) + if (netif_running(p->dev) && netif_oper_up(p->dev)) br_stp_enable_port(p); } -- cgit v1.2.3 From bc0784d951470ca6597239f1bae6e59caf49fb10 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 12 Dec 2012 16:56:20 -0800 Subject: mac80211: Allow disabling SGI-20 This allows user-space (wpa_supplicant) to disable short guard interval (SGI) for 20Mhz. The SGI-40 disable option is already handled. Signed-off-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 3 +++ net/mac80211/main.c | 1 + 2 files changed, 4 insertions(+) (limited to 'net') diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index a71d891794a4..8a6d68f1f404 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -62,6 +62,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40); __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40); + /* Allow user to disable SGI-20 (SGI-40 is handled above) */ + __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20); + /* Allow user to disable the max-AMSDU bit. */ __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 1b087fff93e7..0c0d4cabd76c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -537,6 +537,7 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_MAX_AMSDU | + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40), .mcs = { .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff, -- cgit v1.2.3 From f4eabc918c3b88763bc20dd9e2b248aa6c757005 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 13 Dec 2012 18:59:57 +0800 Subject: mac80211: use short slot time in mesh for 5GHz Use short slot time in 5GHz for mesh. The performance is increased from 16.4Mbps to 23.4Mbps for two directly connected mesh STAs operating in legacy rate using iperf measurement. Almost similar to the results claimed in IBSS mode. Signed-off-by: Chun-Yeow Yeoh [call ieee80211_get_sdata_band() only once] Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 649ad513547f..8565c00cb644 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -607,6 +607,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; + u32 changed = BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_HT | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_BEACON_INT; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); local->fif_other_bss++; /* mesh ifaces must set allmulti to forward mcast traffic */ @@ -626,13 +632,14 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.basic_rates = - ieee80211_mandatory_rates(sdata->local, - ieee80211_get_sdata_band(sdata)); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_HT | - BSS_CHANGED_BASIC_RATES | - BSS_CHANGED_BEACON_INT); + ieee80211_mandatory_rates(local, band); + + if (band == IEEE80211_BAND_5GHZ) { + sdata->vif.bss_conf.use_short_slot = true; + changed |= BSS_CHANGED_ERP_SLOT; + } + + ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); } -- cgit v1.2.3 From b998e8bb3e1c6eeae5eab9d6a434563270286c3b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:07:46 +0100 Subject: mac80211: remove final sta_info_flush() When all interfaces have been removed, there can't be any stations left over, so there's no need to flush again. Remove this, and all code associated with it, which also simplifies the function. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 7 +++---- net/mac80211/ibss.c | 4 ++-- net/mac80211/iface.c | 5 ++--- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/sta_info.c | 23 ++++++----------------- net/mac80211/sta_info.h | 3 +-- 7 files changed, 16 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 47e0aca614b7..be019533b233 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,8 +1010,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) kfree_rcu(old_probe_resp, rcu_head); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush(local, vlan); - sta_info_flush(local, sdata); + sta_info_flush(vlan); + sta_info_flush(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); @@ -1301,7 +1301,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac) { - struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1309,7 +1308,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (mac) return sta_info_destroy_addr_bss(sdata, mac); - sta_info_flush(local, sdata); + sta_info_flush(sdata); return 0; } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6b7644e818d8..854de974dc9a 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -67,7 +67,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, skb_reserve(skb, sdata->local->hw.extra_tx_headroom); if (!ether_addr_equal(ifibss->bssid, bssid)) - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { @@ -1182,7 +1182,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) memset(ifibss->bssid, 0, ETH_ALEN); ifibss->ssid_len = 0; - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8be854e86cd9..ecf7d15eaa5e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -776,7 +776,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * all other modes we've already removed all stations when * disconnecting etc. */ - sta_info_flush(local, sdata); + sta_info_flush(sdata); /* * Don't count this interface for promisc/allmulti while it @@ -961,7 +961,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev) */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; int flushed; int i; @@ -977,7 +976,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_rmc_free(sdata); - flushed = sta_info_flush(local, sdata); + flushed = sta_info_flush(sdata); WARN_ON(flushed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8565c00cb644..8a9d5d03bbc2 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -656,7 +656,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* flush STAs and mpaths on this iface */ - sta_info_flush(sdata->local, sdata); + sta_info_flush(sdata); mesh_path_flush_by_iface(sdata); del_timer_sync(&sdata->u.mesh.housekeeping_timer); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a3552929a21d..6d61d2f88716 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1521,7 +1521,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ - sta_info_flush(local, sdata); + sta_info_flush(sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ca9fde198188..d743645a17b5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -885,7 +885,6 @@ void sta_info_init(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local) { del_timer_sync(&local->sta_cleanup); - sta_info_flush(local, NULL); } /** @@ -893,12 +892,11 @@ void sta_info_stop(struct ieee80211_local *local) * * Returns the number of removed STA entries. * - * @local: local interface data - * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs + * @sdata: sdata to remove all stations from */ -int sta_info_flush(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +int sta_info_flush(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; int ret = 0; @@ -906,7 +904,7 @@ int sta_info_flush(struct ieee80211_local *local, mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (!sdata || sdata == sta->sdata) { + if (sdata == sta->sdata) { WARN_ON(__sta_info_destroy(sta)); ret++; } @@ -915,17 +913,8 @@ int sta_info_flush(struct ieee80211_local *local, rcu_barrier(); - if (sdata) { - ieee80211_cleanup_sdata_stas(sdata); - cancel_work_sync(&sdata->cleanup_stations_wk); - } else { - mutex_lock(&local->iflist_mtx); - list_for_each_entry(sdata, &local->interfaces, list) { - ieee80211_cleanup_sdata_stas(sdata); - cancel_work_sync(&sdata->cleanup_stations_wk); - } - mutex_unlock(&local->iflist_mtx); - } + ieee80211_cleanup_sdata_stas(sdata); + cancel_work_sync(&sdata->cleanup_stations_wk); return ret; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 37c1889afd3a..13698dae6a88 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -548,8 +548,7 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata); +int sta_info_flush(struct ieee80211_sub_if_data *sdata); void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); -- cgit v1.2.3 From 09f4114e02aac9cbf40553a17580b07ab29715d8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:26:57 +0100 Subject: mac80211: warn if unexpectedly removing stations When an interface is brought down it must have been disconnected (or similar) in all modes other than WDS, so warn if any stations were removed in other modes. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index ecf7d15eaa5e..604b4aae3364 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -747,7 +747,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, unsigned long flags; struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; - int i; + int i, flushed; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -772,11 +772,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * (because if we remove a STA after ops->remove_interface() * the driver will have removed the vif info already!) * - * This is relevant only in AP, WDS and mesh modes, since in - * all other modes we've already removed all stations when - * disconnecting etc. + * This is relevant only in WDS mode, in all other modes we've + * already removed all stations when disconnecting or similar, + * so warn otherwise. */ - sta_info_flush(sdata); + flushed = sta_info_flush(sdata); + WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || + (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); /* * Don't count this interface for promisc/allmulti while it -- cgit v1.2.3 From 051007d9e281cd8ea603a4cc4c96b0170b26c7e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 23:49:02 +0100 Subject: mac80211: optimise roaming time again The last fixes re-added the RCU synchronize penalty on roaming to fix the races. Split up sta_info_flush() now to get rid of that again, and let managed mode (and only it) delay the actual destruction. Tested-by: Ben Greear Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 15 ++++++++++----- net/mac80211/mlme.c | 2 +- net/mac80211/sta_info.c | 27 +++++++++++++++++---------- net/mac80211/sta_info.h | 20 +++++++++++++++++++- 4 files changed, 47 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 604b4aae3364..12341efb109e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -775,8 +775,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * This is relevant only in WDS mode, in all other modes we've * already removed all stations when disconnecting or similar, * so warn otherwise. + * + * We call sta_info_flush_cleanup() later, to combine RCU waits. */ - flushed = sta_info_flush(sdata); + flushed = sta_info_flush_defer(sdata); WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); @@ -861,11 +863,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * Call synchronize_rcu() to wait for the RX path - * should it be using the interface and enqueuing - * frames at this very time on another CPU. + * sta_info_flush_cleanup() calls rcu_barrier to + * wait for the station call_rcu() calls to complete, + * here we require it to wait for the RX path in case + * it is using the interface and enqueuing frames at + * this very time on another CPU. */ - synchronize_rcu(); + sta_info_flush_cleanup(sdata); + skb_queue_purge(&sdata->skb_queue); /* diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d61d2f88716..3cf85d8629a7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1521,7 +1521,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ - sta_info_flush(sdata); + sta_info_flush_defer(sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d743645a17b5..7199b9d5b2f4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -104,6 +104,16 @@ static void cleanup_single_sta(struct sta_info *sta) * neither mac80211 nor the driver can reference this * sta struct any more except by still existing timers * associated with this station that we clean up below. + * + * Note though that this still uses the sdata and even + * calls the driver in AP and mesh mode, so interfaces + * of those types mush use call sta_info_flush_cleanup() + * (typically via sta_info_flush()) before deconfiguring + * the driver. + * + * In station mode, nothing happens here so it doesn't + * have to (and doesn't) do that, this is intentional to + * speed up roaming. */ if (test_sta_flag(sta, WLAN_STA_PS_STA)) { @@ -887,14 +897,8 @@ void sta_info_stop(struct ieee80211_local *local) del_timer_sync(&local->sta_cleanup); } -/** - * sta_info_flush - flush matching STA entries from the STA table - * - * Returns the number of removed STA entries. - * - * @sdata: sdata to remove all stations from - */ -int sta_info_flush(struct ieee80211_sub_if_data *sdata) + +int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; @@ -911,12 +915,15 @@ int sta_info_flush(struct ieee80211_sub_if_data *sdata) } mutex_unlock(&local->sta_mtx); + return ret; +} + +void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) +{ rcu_barrier(); ieee80211_cleanup_sdata_stas(sdata); cancel_work_sync(&sdata->cleanup_stations_wk); - - return ret; } void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 13698dae6a88..c3266aed4a07 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -548,7 +548,25 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush(struct ieee80211_sub_if_data *sdata); +int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); +void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush - flush matching STA entries from the STA table + * + * Returns the number of removed STA entries. + * + * @sdata: sdata to remove all stations from + */ +static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) +{ + int ret = sta_info_flush_defer(sdata); + + sta_info_flush_cleanup(sdata); + + return ret; +} + void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo); -- cgit v1.2.3 From 10ff57f98d5fccb9bb508ba30230e5df030d67e3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 15 Dec 2012 09:45:50 +0100 Subject: mac80211: remove a bit of dead mesh code In a file that's only built when CONFIG_MAC80211_MESH is defined, having an #ifdef on the same is entirely pointless, so remove it. Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 8a9d5d03bbc2..c0008d5dff0a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -20,16 +20,11 @@ int mesh_allocated; static struct kmem_cache *rm_cache; -#ifdef CONFIG_MAC80211_MESH bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) { return (mgmt->u.action.u.mesh_action.action_code == WLAN_MESH_ACTION_HWMP_PATH_SELECTION); } -#else -bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) -{ return false; } -#endif void ieee80211s_init(void) { -- cgit v1.2.3 From 8a57fff0c178febbe28669a0ef68a8e3460a7589 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 17:03:17 +0100 Subject: regulatory: don't write past array when intersecting rules When intersecting rules, we count first to know how many rules need to be allocated, and then do the intersection into the allocated array. However, the code doing this writes past the end of the array because it attempts to do all intersections. Make it stop when the right number of rules has been reached. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6e5308998e30..40646e823d5d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -647,9 +647,9 @@ static struct ieee80211_regdomain *regdom_intersect( if (!rd) return NULL; - for (x = 0; x < rd1->n_reg_rules; x++) { + for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { + for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { rule2 = &rd2->reg_rules[y]; /* * This time around instead of using the stack lets -- cgit v1.2.3 From 82f20856304319d5a931846e0269eb911d52a905 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 12:49:16 +0100 Subject: regulatory: don't allocate too much memory There's no need to allocate one reg rule more than will be used, reduce the allocations. The allocation in nl80211 already doesn't allocate too much space. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 40646e823d5d..62bf212e5648 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -318,8 +318,9 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, int size_of_regd = 0; unsigned int i; - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); + size_of_regd = + sizeof(struct ieee80211_regdomain) + + src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) @@ -641,7 +642,7 @@ static struct ieee80211_regdomain *regdom_intersect( return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + - ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) -- cgit v1.2.3 From 74f53cd8d4474f9ba91c7309feabebae80a60089 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 17:26:17 +0100 Subject: regulatory: clean up regdom_intersect As the dummy_rule (also renamed from irule) is only used for output by the reg_rules_intersect() function there's no need to clear it at all, remove that. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 62bf212e5648..d52ffa2abc3f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -608,12 +608,7 @@ static struct ieee80211_regdomain *regdom_intersect( struct ieee80211_reg_rule *intersected_rule; struct ieee80211_regdomain *rd; /* This is just a dummy holder to help us count */ - struct ieee80211_reg_rule irule; - - /* Uses the stack temporarily for counter arithmetic */ - intersected_rule = &irule; - - memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); + struct ieee80211_reg_rule dummy_rule; if (!rd1 || !rd2) return NULL; @@ -630,11 +625,8 @@ static struct ieee80211_regdomain *regdom_intersect( rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; - if (!reg_rules_intersect(rule1, rule2, - intersected_rule)) + if (!reg_rules_intersect(rule1, rule2, &dummy_rule)) num_rules++; - memset(intersected_rule, 0, - sizeof(struct ieee80211_reg_rule)); } } -- cgit v1.2.3 From e9763c3c295a8b31316e16558b58b226e005b160 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 16:59:46 +0100 Subject: regulatory: clean up reg_copy_regd() Use ERR_PTR/IS_ERR to return the result or errors, also do some code cleanups. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d52ffa2abc3f..52a3598859cd 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -311,11 +311,11 @@ static bool is_user_regdom_saved(void) return true; } -static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, - const struct ieee80211_regdomain *src_regd) +static const struct ieee80211_regdomain * +reg_copy_regd(const struct ieee80211_regdomain *src_regd) { struct ieee80211_regdomain *regd; - int size_of_regd = 0; + int size_of_regd; unsigned int i; size_of_regd = @@ -324,16 +324,15 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) - return -ENOMEM; + return ERR_PTR(-ENOMEM); memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); for (i = 0; i < src_regd->n_reg_rules; i++) memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], - sizeof(struct ieee80211_reg_rule)); + sizeof(struct ieee80211_reg_rule)); - *dst_regd = regd; - return 0; + return regd; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB @@ -348,9 +347,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex); static void reg_regdb_search(struct work_struct *work) { struct reg_regdb_search_request *request; - const struct ieee80211_regdomain *curdom, *regdom; - int i, r; - bool set_reg = false; + const struct ieee80211_regdomain *curdom, *regdom = NULL; + int i; mutex_lock(&cfg80211_mutex); @@ -365,10 +363,7 @@ static void reg_regdb_search(struct work_struct *work) curdom = reg_regdb[i]; if (!memcmp(request->alpha2, curdom->alpha2, 2)) { - r = reg_copy_regd(®dom, curdom); - if (r) - break; - set_reg = true; + regdom = reg_copy_regd(curdom); break; } } @@ -377,7 +372,7 @@ static void reg_regdb_search(struct work_struct *work) } mutex_unlock(®_regdb_search_mutex); - if (set_reg) + if (!IS_ERR_OR_NULL(regdom)) set_regdom(regdom); mutex_unlock(&cfg80211_mutex); @@ -1509,6 +1504,7 @@ static void reg_set_request_processed(void) static int __regulatory_hint(struct wiphy *wiphy, struct regulatory_request *pending_request) { + const struct ieee80211_regdomain *regd; bool intersect = false; int r = 0; @@ -1519,11 +1515,12 @@ static int __regulatory_hint(struct wiphy *wiphy, if (r == REG_INTERSECT) { if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { + regd = reg_copy_regd(cfg80211_regdomain); + if (IS_ERR(regd)) { kfree(pending_request); - return r; + return PTR_ERR(regd); } + wiphy->regd = regd; } intersect = true; } else if (r) { @@ -1535,12 +1532,13 @@ static int __regulatory_hint(struct wiphy *wiphy, if (r == -EALREADY && pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { + regd = reg_copy_regd(cfg80211_regdomain); + if (IS_ERR(regd)) { kfree(pending_request); - return r; + return PTR_ERR(regd); } r = -EALREADY; + wiphy->regd = regd; goto new_request; } kfree(pending_request); @@ -2200,6 +2198,7 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) /* Takes ownership of rd only if it doesn't fail */ static int __set_regdom(const struct ieee80211_regdomain *rd) { + const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ @@ -2257,8 +2256,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) } if (!last_request->intersect) { - int r; - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(false); cfg80211_regdomain = rd; @@ -2277,10 +2274,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (request_wiphy->regd) return -EALREADY; - r = reg_copy_regd(&request_wiphy->regd, rd); - if (r) - return r; + regd = reg_copy_regd(rd); + if (IS_ERR(regd)) + return PTR_ERR(regd); + request_wiphy->regd = regd; reset_regdomains(false); cfg80211_regdomain = rd; return 0; -- cgit v1.2.3 From fea9bcedce2d0c751d4f31155f4634689d46166e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:32:01 +0100 Subject: regulatory: don't test list before iterating There's no need to test whether a list is empty or not before iterating. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 66 ++++++++++++++++-------------------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 52a3598859cd..1496a10da638 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1104,9 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) assert_cfg80211_lock(); - if (list_empty(®_beacon_list)) - return; - list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; @@ -1666,11 +1663,6 @@ static void reg_process_pending_beacon_hints(void) /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); - if (list_empty(®_pending_beacons)) { - spin_unlock_bh(®_pending_beacons_lock); - goto out; - } - list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { @@ -1685,7 +1677,6 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); -out: mutex_unlock(&cfg80211_mutex); } @@ -1949,34 +1940,24 @@ static void restore_regulatory_settings(bool reset_user) * settings. */ spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - if (reg_request->initiator != - NL80211_REGDOM_SET_BY_USER) - continue; - list_move_tail(®_request->list, &tmp_reg_req_list); - } + list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { + if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) + continue; + list_move_tail(®_request->list, &tmp_reg_req_list); } spin_unlock(®_requests_lock); /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_unlock_bh(®_pending_beacons_lock); - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } /* First restore to the basic regulatory settings */ @@ -2490,30 +2471,21 @@ void /* __init_or_exit */ regulatory_exit(void) platform_device_unregister(reg_pdev); spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_unlock_bh(®_pending_beacons_lock); - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } + list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { + list_del(®_beacon->list); + kfree(reg_beacon); } spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - list_del(®_request->list); - kfree(reg_request); - } + list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { + list_del(®_request->list); + kfree(reg_request); } spin_unlock(®_requests_lock); -- cgit v1.2.3 From 75e2dba866706ab9b133983b7fd9a6297b24c22d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:21:26 +0100 Subject: regulatory: simplify regulatory_hint_11d There's no need to unlock before calling queue_regulatory_request(), so simplify the function. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1496a10da638..cd99d285e8d8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1825,12 +1825,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_env = env; - mutex_unlock(®_mutex); - queue_regulatory_request(request); - - return; - out: mutex_unlock(®_mutex); } -- cgit v1.2.3 From 1a9193185f462a51815fe48491f8a6fb6b942551 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:21:11 +0100 Subject: regulatory: code cleanup Clean up various things like indentation, extra parentheses, too many/few line breaks, etc. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 15 ++- net/wireless/reg.c | 286 ++++++++++++++++++------------------------------- net/wireless/reg.h | 4 +- net/wireless/sme.c | 6 +- 4 files changed, 116 insertions(+), 195 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f45706adaf34..a19583cf4b6e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4259,7 +4259,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]); nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { + rem_reg_rules) { num_rules++; if (num_rules > NL80211_MAX_SUPP_REG_RULES) return -EINVAL; @@ -4273,7 +4273,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } size_of_regd = sizeof(struct ieee80211_regdomain) + - (num_rules * sizeof(struct ieee80211_reg_rule)); + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) { @@ -4293,10 +4293,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) rd->dfs_region = dfs_region; nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { + rem_reg_rules) { nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, - nla_data(nl_reg_rule), nla_len(nl_reg_rule), - reg_rule_policy); + nla_data(nl_reg_rule), nla_len(nl_reg_rule), + reg_rule_policy); r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); if (r) goto bad_reg; @@ -4312,10 +4312,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) BUG_ON(rule_idx != num_rules); r = set_regdom(rd); - - mutex_unlock(&cfg80211_mutex); - - return r; + rd = NULL; bad_reg: mutex_unlock(&cfg80211_mutex); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cd99d285e8d8..cf2fb3425a63 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -219,18 +218,14 @@ bool is_world_regdom(const char *alpha2) { if (!alpha2) return false; - if (alpha2[0] == '0' && alpha2[1] == '0') - return true; - return false; + return alpha2[0] == '0' && alpha2[1] == '0'; } static bool is_alpha2_set(const char *alpha2) { if (!alpha2) return false; - if (alpha2[0] != 0 && alpha2[1] != 0) - return true; - return false; + return alpha2[0] && alpha2[1]; } static bool is_unknown_alpha2(const char *alpha2) @@ -241,9 +236,7 @@ static bool is_unknown_alpha2(const char *alpha2) * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ - if (alpha2[0] == '9' && alpha2[1] == '9') - return true; - return false; + return alpha2[0] == '9' && alpha2[1] == '9'; } static bool is_intersected_alpha2(const char *alpha2) @@ -255,28 +248,21 @@ static bool is_intersected_alpha2(const char *alpha2) * result of an intersection between two regulatory domain * structures */ - if (alpha2[0] == '9' && alpha2[1] == '8') - return true; - return false; + return alpha2[0] == '9' && alpha2[1] == '8'; } static bool is_an_alpha2(const char *alpha2) { if (!alpha2) return false; - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - return false; + return isalpha(alpha2[0]) && isalpha(alpha2[1]); } static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) { if (!alpha2_x || !alpha2_y) return false; - if (alpha2_x[0] == alpha2_y[0] && - alpha2_x[1] == alpha2_y[1]) - return true; - return false; + return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; } static bool regdom_changes(const char *alpha2) @@ -285,9 +271,7 @@ static bool regdom_changes(const char *alpha2) if (!cfg80211_regdomain) return true; - if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) - return false; - return true; + return !alpha2_equal(cfg80211_regdomain->alpha2, alpha2); } /* @@ -301,11 +285,9 @@ static bool is_user_regdom_saved(void) return false; /* This would indicate a mistake on the design */ - if (WARN((!is_world_regdom(user_alpha2) && - !is_an_alpha2(user_alpha2)), + if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), "Unexpected user alpha2: %c%c\n", - user_alpha2[0], - user_alpha2[1])) + user_alpha2[0], user_alpha2[1])) return false; return true; @@ -359,10 +341,10 @@ static void reg_regdb_search(struct work_struct *work) list); list_del(&request->list); - for (i=0; ialpha2, curdom->alpha2, 2)) { + if (alpha2_equal(request->alpha2, curdom->alpha2)) { regdom = reg_copy_regd(curdom); break; } @@ -456,7 +438,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->end_freq_khz <= freq_range->start_freq_khz || - freq_range->max_bandwidth_khz > freq_diff) + freq_range->max_bandwidth_khz > freq_diff) return false; return true; @@ -514,7 +496,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, * regulatory rule support for other "bands". **/ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, - u32 freq_khz) + u32 freq_khz) { #define ONE_GHZ_IN_KHZ 1000000 /* @@ -536,10 +518,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, * Helper for regdom_intersect(), this does the real * mathematical intersection fun */ -static int reg_rules_intersect( - const struct ieee80211_reg_rule *rule1, - const struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *intersected_rule) +static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, + const struct ieee80211_reg_rule *rule2, + struct ieee80211_reg_rule *intersected_rule) { const struct ieee80211_freq_range *freq_range1, *freq_range2; struct ieee80211_freq_range *freq_range; @@ -556,11 +537,11 @@ static int reg_rules_intersect( power_rule = &intersected_rule->power_rule; freq_range->start_freq_khz = max(freq_range1->start_freq_khz, - freq_range2->start_freq_khz); + freq_range2->start_freq_khz); freq_range->end_freq_khz = min(freq_range1->end_freq_khz, - freq_range2->end_freq_khz); + freq_range2->end_freq_khz); freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, - freq_range2->max_bandwidth_khz); + freq_range2->max_bandwidth_khz); freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->max_bandwidth_khz > freq_diff) @@ -571,7 +552,7 @@ static int reg_rules_intersect( power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, power_rule2->max_antenna_gain); - intersected_rule->flags = (rule1->flags | rule2->flags); + intersected_rule->flags = rule1->flags | rule2->flags; if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; @@ -592,9 +573,9 @@ static int reg_rules_intersect( * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. */ -static struct ieee80211_regdomain *regdom_intersect( - const struct ieee80211_regdomain *rd1, - const struct ieee80211_regdomain *rd2) +static struct ieee80211_regdomain * +regdom_intersect(const struct ieee80211_regdomain *rd1, + const struct ieee80211_regdomain *rd2) { int r, size_of_regd; unsigned int x, y; @@ -645,8 +626,7 @@ static struct ieee80211_regdomain *regdom_intersect( * a memcpy() */ intersected_rule = &rd->reg_rules[rule_idx]; - r = reg_rules_intersect(rule1, rule2, - intersected_rule); + r = reg_rules_intersect(rule1, rule2, intersected_rule); /* * No need to memset here the intersected rule here as * we're not using the stack anymore @@ -731,9 +711,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, - center_freq, - desired_bw_khz); + bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); if (band_rule_found && bw_fits) { *reg_rule = rr; @@ -747,17 +725,13 @@ static int freq_reg_info_regd(struct wiphy *wiphy, return -EINVAL; } -int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) { assert_cfg80211_lock(); - return freq_reg_info_regd(wiphy, - center_freq, - desired_bw_khz, - reg_rule, - NULL); + + return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, + reg_rule, NULL); } EXPORT_SYMBOL(freq_reg_info); @@ -795,16 +769,12 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - REG_DBG_PRINT("Updating information on frequency %d MHz " - "for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Updating information on frequency %d MHz for a %d MHz width channel with regulatory rule:\n", + chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - max_antenna_gain, + freq_range->start_freq_khz, freq_range->end_freq_khz, + freq_range->max_bandwidth_khz, max_antenna_gain, power_rule->max_eirp); } #else @@ -850,11 +820,8 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule); - + r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, ®_rule); if (r) { /* * We will disable all channels that do not match our @@ -902,8 +869,9 @@ static void handle_channel(struct wiphy *wiphy, chan->beacon_found = false; chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); - chan->max_antenna_gain = min(chan->orig_mag, - (int) MBI_TO_DBI(power_rule->max_antenna_gain)); + chan->max_antenna_gain = + min_t(int, chan->orig_mag, + MBI_TO_DBI(power_rule->max_antenna_gain)); chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); if (chan->orig_mpwr) { /* @@ -922,8 +890,7 @@ static void handle_channel(struct wiphy *wiphy, chan->max_power = chan->max_reg_power; } -static void handle_band(struct wiphy *wiphy, - enum ieee80211_band band, +static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, enum nl80211_reg_initiator initiator) { unsigned int i; @@ -940,51 +907,48 @@ static bool reg_request_cell_base(struct regulatory_request *request) { if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; - if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) - return false; - return true; + return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; } bool reg_last_request_cell_base(void) { bool val; + assert_cfg80211_lock(); mutex_lock(®_mutex); val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); + return val; } #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS - /* Core specific check */ static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { if (!reg_num_devs_support_basehint) return -EOPNOTSUPP; - if (reg_request_cell_base(last_request)) { - if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; - return 0; - } + if (reg_request_cell_base(last_request) && + !regdom_changes(pending_request->alpha2)) + return -EALREADY; + return 0; } /* Device specific check */ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { - if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) - return true; - return false; + return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); } #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { return -EOPNOTSUPP; } -static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) + +static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { return true; } @@ -995,17 +959,14 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { if (!last_request) { - REG_DBG_PRINT("Ignoring regulatory request %s since " - "last_request is not set\n", + REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver uses its own custom " - "regulatory domain\n", + REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", reg_initiator_name(initiator)); return true; } @@ -1017,9 +978,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(last_request->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver requires its own regulatory " - "domain to be set first\n", + REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", reg_initiator_name(initiator)); return true; } @@ -1030,8 +989,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, return false; } -static void handle_reg_beacon(struct wiphy *wiphy, - unsigned int chan_idx, +static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { struct ieee80211_supported_band *sband; @@ -1139,16 +1097,14 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } -static bool is_ht40_not_allowed(struct ieee80211_channel *chan) +static bool is_ht40_allowed(struct ieee80211_channel *chan) { if (!chan) - return true; + return false; if (chan->flags & IEEE80211_CHAN_DISABLED) - return true; + return false; /* This would happen when regulatory rules disallow HT40 completely */ - if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) - return true; - return false; + return !(chan->flags & IEEE80211_CHAN_NO_HT40); } static void reg_process_ht_flags_channel(struct wiphy *wiphy, @@ -1166,7 +1122,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, BUG_ON(chan_idx >= sband->n_channels); channel = &sband->channels[chan_idx]; - if (is_ht40_not_allowed(channel)) { + if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; } @@ -1177,6 +1133,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, */ for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *c = &sband->channels[i]; + if (c->center_freq == (channel->center_freq - 20)) channel_before = c; if (c->center_freq == (channel->center_freq + 20)) @@ -1188,12 +1145,12 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, * if that ever changes we also need to change the below logic * to include that as well. */ - if (is_ht40_not_allowed(channel_before)) + if (!is_ht40_allowed(channel_before)) channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - if (is_ht40_not_allowed(channel_after)) + if (!is_ht40_allowed(channel_after)) channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; @@ -1245,6 +1202,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); + if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, last_request); } @@ -1289,18 +1247,12 @@ static void handle_channel_custom(struct wiphy *wiphy, BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; - r = freq_reg_info_regd(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule, - regd); + r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), + desired_bw_khz, ®_rule, regd); if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom " - "regd has no rule that fits a %d MHz " - "wide channel\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits a %d MHz wide channel\n", + chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); chan->flags = IEEE80211_CHAN_DISABLED; return; } @@ -1350,7 +1302,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, /* * no point in calling this if it won't have any effect - * on your device's supportd bands. + * on your device's supported bands. */ WARN_ON(!bands_set); } @@ -1379,7 +1331,6 @@ static int ignore_request(struct wiphy *wiphy, case NL80211_REGDOM_SET_BY_CORE: return 0; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) @@ -1444,18 +1395,17 @@ static int ignore_request(struct wiphy *wiphy, * to their country before the IE is picked up */ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) + last_request->intersect) return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (regdom_changes(last_request->alpha2)) - return -EAGAIN; - } + if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE || + last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_USER) && + regdom_changes(last_request->alpha2)) + return -EAGAIN; if (!regdom_changes(pending_request->alpha2)) return -EALREADY; @@ -1585,8 +1535,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && - !wiphy) { + if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); return; } @@ -1603,8 +1552,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, * We only time out user hints, given that they should be the only * source of bogus requests. */ - if (r != -EALREADY && - reg_initiator == NL80211_REGDOM_SET_BY_USER) + if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } @@ -1622,8 +1570,7 @@ static void reg_process_pending_hints(void) /* When last_request->processed becomes true this will be rescheduled */ if (last_request && !last_request->processed) { - REG_DBG_PRINT("Pending regulatory request, waiting " - "for it to be processed...\n"); + REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); goto out; } @@ -1665,7 +1612,6 @@ static void reg_process_pending_beacon_hints(void) list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { - list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ @@ -1708,8 +1654,7 @@ static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; - request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); + request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; @@ -1776,10 +1721,8 @@ EXPORT_SYMBOL(regulatory_hint); * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and * therefore cannot iterate over the rdev list here. */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - const u8 *country_ie, - u8 country_ie_len) +void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, + const u8 *country_ie, u8 country_ie_len) { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; @@ -1811,8 +1754,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, * cfg80211_mutex. */ if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) + NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy_idx_valid(last_request->wiphy_idx))) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -1840,8 +1783,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) if (is_user_regdom_saved()) { /* Unless we're asked to ignore it and reset it */ if (reset_user) { - REG_DBG_PRINT("Restoring regulatory settings " - "including user preference\n"); + REG_DBG_PRINT("Restoring regulatory settings including user preference\n"); user_alpha2[0] = '9'; user_alpha2[1] = '7'; @@ -1851,26 +1793,20 @@ static void restore_alpha2(char *alpha2, bool reset_user) * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); + REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { - REG_DBG_PRINT("Restoring regulatory settings " - "while preserving user preference for: %c%c\n", - user_alpha2[0], - user_alpha2[1]); + REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", + user_alpha2[0], user_alpha2[1]); alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); + REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", + ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else @@ -1986,10 +1922,8 @@ static void restore_regulatory_settings(bool reset_user) spin_lock(®_requests_lock); list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back " - "into the queue\n", - reg_request->alpha2[0], - reg_request->alpha2[1]); + REG_DBG_PRINT("Adding request for country %c%c back into the queue\n", + reg_request->alpha2[0], reg_request->alpha2[1]); list_move_tail(®_request->list, ®_requests_list); } spin_unlock(®_requests_lock); @@ -2004,8 +1938,7 @@ static void restore_regulatory_settings(bool reset_user) void regulatory_hint_disconnect(void) { - REG_DBG_PRINT("All devices are disconnected, going to " - "restore regulatory settings\n"); + REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n"); restore_regulatory_settings(false); } @@ -2024,25 +1957,23 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, { struct reg_beacon *reg_beacon; - if (likely((beacon_chan->beacon_found || - (beacon_chan->flags & IEEE80211_CHAN_RADAR) || + if (beacon_chan->beacon_found || + beacon_chan->flags & IEEE80211_CHAN_RADAR || (beacon_chan->band == IEEE80211_BAND_2GHZ && - !freq_is_chan_12_13_14(beacon_chan->center_freq))))) + !freq_is_chan_12_13_14(beacon_chan->center_freq))) return 0; reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; - REG_DBG_PRINT("Found new beacon on " - "frequency: %d MHz (Ch %d) on %s\n", + REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), wiphy_name(wiphy)); memcpy(®_beacon->chan, beacon_chan, - sizeof(struct ieee80211_channel)); - + sizeof(struct ieee80211_channel)); /* * Since we can be called from BH or and non-BH context @@ -2122,7 +2053,7 @@ static void print_dfs_region(u8 dfs_region) pr_info(" DFS Master region JP"); break; default: - pr_info(" DFS Master region Uknown"); + pr_info(" DFS Master region Unknown"); break; } } @@ -2131,7 +2062,6 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) { if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; @@ -2145,22 +2075,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) pr_info("Current regulatory domain intersected:\n"); } else pr_info("Current regulatory domain intersected:\n"); - } else if (is_world_regdom(rd->alpha2)) + } else if (is_world_regdom(rd->alpha2)) { pr_info("World regulatory domain updated:\n"); - else { + } else { if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { if (reg_request_cell_base(last_request)) - pr_info("Regulatory domain changed " - "to country: %c%c by Cell Station\n", + pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else - pr_info("Regulatory domain changed " - "to country: %c%c\n", + pr_info("Regulatory domain changed to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } } + print_dfs_region(rd->dfs_region); print_rd_rules(rd); } @@ -2187,7 +2116,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) } if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && - !is_unknown_alpha2(rd->alpha2)) + !is_unknown_alpha2(rd->alpha2)) return -EINVAL; if (!last_request) @@ -2263,7 +2192,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) return -EINVAL; @@ -2315,8 +2243,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (!last_request->intersect) - BUG_ON(rd != cfg80211_regdomain); + BUG_ON(!last_request->intersect && rd != cfg80211_regdomain); /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); @@ -2393,8 +2320,7 @@ out: static void reg_timeout_work(struct work_struct *work) { - REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " - "restoring regulatory settings\n"); + REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); restore_regulatory_settings(true); } @@ -2448,7 +2374,7 @@ int __init regulatory_init(void) return 0; } -void /* __init_or_exit */ regulatory_exit(void) +void regulatory_exit(void) { struct regulatory_request *reg_request, *tmp; struct reg_beacon *reg_beacon, *btmp; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4c0a32ffd530..37891e813a74 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -55,8 +55,8 @@ bool reg_last_request_cell_base(void); * set the wiphy->disable_beacon_hints to true. */ int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp); + struct ieee80211_channel *beacon_chan, + gfp_t gfp); /** * regulatory_hint_11d - hints a country IE as a regulatory domain diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f2431e41a373..d2d26518cdd7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -519,10 +519,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_11d(wdev->wiphy, - bss->channel->band, - country_ie + 2, - country_ie[1]); + regulatory_hint_11d(wdev->wiphy, bss->channel->band, + country_ie + 2, country_ie[1]); kfree(country_ie); } -- cgit v1.2.3 From 9027b1493b57a6a8f9f56741e4c4189048ea5633 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:59:24 +0100 Subject: regulatory: remove useless locking on exit It would be a major problem if anything were to run concurrently while the module is being unloaded so remove the locking that doesn't help anything. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cf2fb3425a63..cf4386fade8a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2382,34 +2382,29 @@ void regulatory_exit(void) cancel_work_sync(®_work); cancel_delayed_work_sync(®_timeout); + /* Lock to suppress warnings */ mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + mutex_unlock(&cfg80211_mutex); + mutex_unlock(®_mutex); dev_set_uevent_suppress(®_pdev->dev, true); platform_device_unregister(reg_pdev); - spin_lock_bh(®_pending_beacons_lock); list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } - spin_unlock_bh(®_pending_beacons_lock); list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } - spin_lock(®_requests_lock); list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { list_del(®_request->list); kfree(reg_request); } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } -- cgit v1.2.3 From 2f92212b71f198d42060f7133ff1dc4edf438f6c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 17:54:55 +0100 Subject: regulatory: use proper enum for return values Instead of treating special error codes specially, like -EALREADY, introduce a real enum for all the needed possibilities and use it. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 133 +++++++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 64 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cf4386fade8a..129586994662 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -65,6 +65,13 @@ #define REG_DBG_PRINT(args...) #endif +enum reg_request_treatment { + REG_REQ_OK, + REG_REQ_IGNORE, + REG_REQ_INTERSECT, + REG_REQ_ALREADY_SET, +}; + static struct regulatory_request core_request_world = { .initiator = NL80211_REGDOM_SET_BY_CORE, .alpha2[0] = '0', @@ -925,16 +932,17 @@ bool reg_last_request_cell_base(void) #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS /* Core specific check */ -static int reg_ignore_cell_hint(struct regulatory_request *pending_request) +static enum reg_request_treatment +reg_ignore_cell_hint(struct regulatory_request *pending_request) { if (!reg_num_devs_support_basehint) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; if (reg_request_cell_base(last_request) && !regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return 0; + return REG_REQ_OK; } /* Device specific check */ @@ -945,7 +953,7 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) #else static int reg_ignore_cell_hint(struct regulatory_request *pending_request) { - return -EOPNOTSUPP; + return REG_REQ_IGNORE; } static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) @@ -1308,15 +1316,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, } EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -/* - * Return value which can be used by ignore_request() to indicate - * it has been determined we should intersect two regulatory domains - */ -#define REG_INTERSECT 1 - /* This has the logic which determines when a new request * should be ignored. */ -static int ignore_request(struct wiphy *wiphy, +static enum reg_request_treatment +get_reg_request_treatment(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; @@ -1325,17 +1328,17 @@ static int ignore_request(struct wiphy *wiphy, /* All initial requests are respected */ if (!last_request) - return 0; + return REG_REQ_OK; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: - return 0; + return REG_REQ_OK; case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (reg_request_cell_base(last_request)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; + return REG_REQ_IGNORE; + return REG_REQ_ALREADY_SET; } last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); @@ -1352,23 +1355,23 @@ static int ignore_request(struct wiphy *wiphy, * to be correct. Reject second one for now. */ if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; + return REG_REQ_IGNORE; + return REG_REQ_ALREADY_SET; } /* * Two consecutive Country IE hints on the same wiphy. * This should be picked up early by the driver/stack */ if (WARN_ON(regdom_changes(pending_request->alpha2))) - return 0; - return -EALREADY; + return REG_REQ_OK; + return REG_REQ_ALREADY_SET; } return 0; case NL80211_REGDOM_SET_BY_DRIVER: if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) - return 0; - return -EALREADY; + return REG_REQ_OK; + return REG_REQ_ALREADY_SET; } /* @@ -1378,25 +1381,25 @@ static int ignore_request(struct wiphy *wiphy, */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return REG_INTERSECT; + return REG_REQ_INTERSECT; case NL80211_REGDOM_SET_BY_USER: if (reg_request_cell_base(pending_request)) return reg_ignore_cell_hint(pending_request); if (reg_request_cell_base(last_request)) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - return REG_INTERSECT; + return REG_REQ_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && last_request->intersect) - return -EOPNOTSUPP; + return REG_REQ_IGNORE; /* * Process user requests only after previous user/driver/core * requests have been processed @@ -1405,15 +1408,15 @@ static int ignore_request(struct wiphy *wiphy, last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || last_request->initiator == NL80211_REGDOM_SET_BY_USER) && regdom_changes(last_request->alpha2)) - return -EAGAIN; + return REG_REQ_IGNORE; if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; + return REG_REQ_ALREADY_SET; - return 0; + return REG_REQ_OK; } - return -EINVAL; + return REG_REQ_IGNORE; } static void reg_set_request_processed(void) @@ -1443,23 +1446,24 @@ static void reg_set_request_processed(void) * The Wireless subsystem can use this function to hint to the wireless core * what it believes should be the current regulatory domain. * - * Returns zero if all went fine, %-EALREADY if a regulatory domain had - * already been set or other standard error codes. + * Returns one of the different reg request treatment values. * * Caller must hold &cfg80211_mutex and ®_mutex */ -static int __regulatory_hint(struct wiphy *wiphy, - struct regulatory_request *pending_request) +static enum reg_request_treatment +__regulatory_hint(struct wiphy *wiphy, + struct regulatory_request *pending_request) { const struct ieee80211_regdomain *regd; bool intersect = false; - int r = 0; + enum reg_request_treatment treatment; assert_cfg80211_lock(); - r = ignore_request(wiphy, pending_request); + treatment = get_reg_request_treatment(wiphy, pending_request); - if (r == REG_INTERSECT) { + switch (treatment) { + case REG_REQ_INTERSECT: if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { regd = reg_copy_regd(cfg80211_regdomain); @@ -1470,26 +1474,28 @@ static int __regulatory_hint(struct wiphy *wiphy, wiphy->regd = regd; } intersect = true; - } else if (r) { + break; + case REG_REQ_OK: + break; + default: /* * If the regulatory domain being requested by the * driver has already been set just copy it to the * wiphy */ - if (r == -EALREADY && - pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { + if (treatment == REG_REQ_ALREADY_SET && + pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { regd = reg_copy_regd(cfg80211_regdomain); if (IS_ERR(regd)) { kfree(pending_request); - return PTR_ERR(regd); + return REG_REQ_IGNORE; } - r = -EALREADY; + treatment = REG_REQ_ALREADY_SET; wiphy->regd = regd; goto new_request; } kfree(pending_request); - return r; + return treatment; } new_request: @@ -1506,28 +1512,29 @@ new_request: user_alpha2[1] = last_request->alpha2[1]; } - /* When r == REG_INTERSECT we do need to call CRDA */ - if (r < 0) { + /* When r == REG_REQ_INTERSECT we do need to call CRDA */ + if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { /* * Since CRDA will not be called in this case as we already * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ - if (r == -EALREADY) { + if (treatment == REG_REQ_ALREADY_SET) { nl80211_send_reg_change_event(last_request); reg_set_request_processed(); } - return r; + return treatment; } - return call_crda(last_request->alpha2); + if (call_crda(last_request->alpha2)) + return REG_REQ_IGNORE; + return REG_REQ_OK; } /* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request, enum nl80211_reg_initiator reg_initiator) { - int r = 0; struct wiphy *wiphy = NULL; BUG_ON(!reg_request->alpha2); @@ -1540,20 +1547,18 @@ static void reg_process_hint(struct regulatory_request *reg_request, return; } - r = __regulatory_hint(wiphy, reg_request); - /* This is required so that the orig_* parameters are saved */ - if (r == -EALREADY && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - wiphy_update_regulatory(wiphy, reg_initiator); - return; + switch (__regulatory_hint(wiphy, reg_request)) { + case REG_REQ_ALREADY_SET: + /* This is required so that the orig_* parameters are saved */ + if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) + wiphy_update_regulatory(wiphy, reg_initiator); + break; + default: + if (reg_initiator == NL80211_REGDOM_SET_BY_USER) + schedule_delayed_work(®_timeout, + msecs_to_jiffies(3142)); + break; } - - /* - * We only time out user hints, given that they should be the only - * source of bogus requests. - */ - if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); } /* -- cgit v1.2.3 From f41737669d2f4c07c09e90767498243e142171f9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:23:37 +0100 Subject: cfg80211: remove wiphy_idx_valid This is pretty much useless since get_wiphy_idx() always returns true since it's always called with a valid wiphy pointer. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/core.c | 14 +++----------- net/wireless/core.h | 16 +++------------- net/wireless/nl80211.c | 2 +- net/wireless/reg.c | 14 +++++--------- 4 files changed, 12 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 14d990400354..747dd9365a44 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -57,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { struct cfg80211_registered_device *result = NULL, *rdev; - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - assert_cfg80211_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { @@ -74,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *rdev; - if (!wiphy) - return WIPHY_IDX_STALE; - rdev = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; } @@ -86,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { struct cfg80211_registered_device *rdev; - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - assert_cfg80211_lock(); rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); @@ -309,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy_idx = wiphy_counter++; - if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { + if (unlikely(rdev->wiphy_idx < 0)) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ diff --git a/net/wireless/core.h b/net/wireless/core.h index 3563097169cb..b8f4630c6ada 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -18,6 +18,9 @@ #include #include "reg.h" + +#define WIPHY_IDX_INVALID -1 + struct cfg80211_registered_device { const struct cfg80211_ops *ops; struct list_head list; @@ -96,13 +99,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) return container_of(wiphy, struct cfg80211_registered_device, wiphy); } -/* Note 0 is valid, hence phy0 */ -static inline -bool wiphy_idx_valid(int wiphy_idx) -{ - return wiphy_idx >= 0; -} - static inline void cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) { @@ -126,12 +122,6 @@ static inline void assert_cfg80211_lock(void) lockdep_assert_held(&cfg80211_mutex); } -/* - * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL - */ -#define WIPHY_IDX_STALE -1 - struct cfg80211_internal_bss { struct list_head list; struct rb_node rbn; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a19583cf4b6e..5fbc8cd9731b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8048,7 +8048,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) goto nla_put_failure; } - if (wiphy_idx_valid(request->wiphy_idx) && + if (request->wiphy_idx != WIPHY_IDX_INVALID && nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) goto nla_put_failure; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 129586994662..b825c82e5a45 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1539,7 +1539,7 @@ static void reg_process_hint(struct regulatory_request *reg_request, BUG_ON(!reg_request->alpha2); - if (wiphy_idx_valid(reg_request->wiphy_idx)) + if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { @@ -1684,7 +1684,7 @@ int regulatory_hint_user(const char *alpha2, if (!request) return -ENOMEM; - request->wiphy_idx = WIPHY_IDX_STALE; + request->wiphy_idx = WIPHY_IDX_INVALID; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_USER; @@ -1709,9 +1709,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) request->wiphy_idx = get_wiphy_idx(wiphy); - /* Must have registered wiphy first */ - BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); - request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_DRIVER; @@ -1758,9 +1755,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ - if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->wiphy_idx != WIPHY_IDX_INVALID) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -2317,7 +2313,7 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) if (!request_wiphy || request_wiphy != wiphy) goto out; - last_request->wiphy_idx = WIPHY_IDX_STALE; + last_request->wiphy_idx = WIPHY_IDX_INVALID; last_request->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_mutex); -- cgit v1.2.3 From fdc9d7b2863ce67f51ed64f89e5b20f3077be569 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:36:09 +0100 Subject: regulatory: remove BUG_ON This code is a bit too BUG_ON happy, remove all instances and while doing so make some code a bit smarter by passing the right pointer instead of indices into arrays. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 -- net/wireless/reg.c | 95 ++++++++++++++++++++------------------------------ 2 files changed, 37 insertions(+), 60 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5fbc8cd9731b..9c2c91845be7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4309,8 +4309,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } - BUG_ON(rule_idx != num_rules); - r = set_regdom(rd); rd = NULL; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b825c82e5a45..57812dfc297b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -213,7 +213,7 @@ static void reset_regdomains(bool full_reset) */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { - BUG_ON(!last_request); + WARN_ON(!last_request); reset_regdomains(false); @@ -804,8 +804,7 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, - enum ieee80211_band band, - unsigned int chan_idx) + struct ieee80211_channel *chan) { int r; u32 flags, bw_flags = 0; @@ -813,18 +812,12 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; struct wiphy *request_wiphy = NULL; assert_cfg80211_lock(); request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - flags = chan->orig_flags; r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), @@ -897,17 +890,17 @@ static void handle_channel(struct wiphy *wiphy, chan->max_power = chan->max_reg_power; } -static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, - enum nl80211_reg_initiator initiator) +static void handle_band(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator, + struct ieee80211_supported_band *sband) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, initiator, band, i); + handle_channel(wiphy, initiator, &sband->channels[i]); } static bool reg_request_cell_base(struct regulatory_request *request) @@ -1116,20 +1109,14 @@ static bool is_ht40_allowed(struct ieee80211_channel *chan) } static void reg_process_ht_flags_channel(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx) + struct ieee80211_channel *channel) { - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channel; + struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; assert_cfg80211_lock(); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - channel = &sband->channels[chan_idx]; - if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; @@ -1165,16 +1152,15 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, } static void reg_process_ht_flags_band(struct wiphy *wiphy, - enum ieee80211_band band) + struct ieee80211_supported_band *sband) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - reg_process_ht_flags_channel(wiphy, band, i); + reg_process_ht_flags_channel(wiphy, &sband->channels[i]); } static void reg_process_ht_flags(struct wiphy *wiphy) @@ -1184,11 +1170,8 @@ static void reg_process_ht_flags(struct wiphy *wiphy) if (!wiphy) return; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - reg_process_ht_flags_band(wiphy, band); - } - + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + reg_process_ht_flags_band(wiphy, wiphy->bands[band]); } static void wiphy_update_regulatory(struct wiphy *wiphy, @@ -1203,10 +1186,8 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, last_request->dfs_region = cfg80211_regdomain->dfs_region; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - handle_band(wiphy, band, initiator); - } + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + handle_band(wiphy, initiator, wiphy->bands[band]); reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); @@ -1236,8 +1217,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) } static void handle_channel_custom(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx, + struct ieee80211_channel *chan, const struct ieee80211_regdomain *regd) { int r; @@ -1246,15 +1226,9 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; assert_reg_lock(); - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); @@ -1279,17 +1253,17 @@ static void handle_channel_custom(struct wiphy *wiphy, (int) MBM_TO_DBM(power_rule->max_eirp); } -static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, +static void handle_band_custom(struct wiphy *wiphy, + struct ieee80211_supported_band *sband, const struct ieee80211_regdomain *regd) { unsigned int i; - struct ieee80211_supported_band *sband; - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; + if (!sband) + return; for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, band, i, regd); + handle_channel_custom(wiphy, &sband->channels[i], regd); } /* Used by drivers prior to wiphy registration */ @@ -1303,7 +1277,7 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; - handle_band_custom(wiphy, band, regd); + handle_band_custom(wiphy, wiphy->bands[band], regd); bands_set++; } mutex_unlock(®_mutex); @@ -1537,7 +1511,8 @@ static void reg_process_hint(struct regulatory_request *reg_request, { struct wiphy *wiphy = NULL; - BUG_ON(!reg_request->alpha2); + if (WARN_ON(!reg_request->alpha2)) + return; if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); @@ -1678,7 +1653,8 @@ int regulatory_hint_user(const char *alpha2, { struct regulatory_request *request; - BUG_ON(!alpha2); + if (WARN_ON(!alpha2)) + return -EINVAL; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -1700,8 +1676,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; - BUG_ON(!alpha2); - BUG_ON(!wiphy); + if (WARN_ON(!alpha2 || !wiphy)) + return -EINVAL; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) @@ -2239,12 +2215,14 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); kfree(rd); - mutex_unlock(®_mutex); - return r; + goto out; } /* This would make this whole thing pointless */ - BUG_ON(!last_request->intersect && rd != cfg80211_regdomain); + if (WARN_ON(!last_request->intersect && rd != cfg80211_regdomain)) { + r = -EINVAL; + goto out; + } /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); @@ -2255,6 +2233,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); + out: mutex_unlock(®_mutex); return r; -- cgit v1.2.3 From 11cff96c0660e1e361d60ab90514ffbbfbaf7c03 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:56:41 +0100 Subject: regulatory: simplify restore_regulatory_settings Use list_splice_tail_init() and also simplify the locking. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 57812dfc297b..87daba95639c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1878,9 +1878,6 @@ static void restore_regulatory_settings(bool reset_user) restore_custom_reg_settings(&rdev->wiphy); } - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - regulatory_hint_core(world_alpha2); /* @@ -1891,18 +1888,8 @@ static void restore_regulatory_settings(bool reset_user) if (is_an_alpha2(alpha2)) regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); - if (list_empty(&tmp_reg_req_list)) - return; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - spin_lock(®_requests_lock); - list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back into the queue\n", - reg_request->alpha2[0], reg_request->alpha2[1]); - list_move_tail(®_request->list, ®_requests_list); - } + list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); spin_unlock(®_requests_lock); mutex_unlock(®_mutex); -- cgit v1.2.3 From d4f2c8819a51f31ef0f929574ce8d80b323c6a76 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 18:59:58 +0100 Subject: regulatory: remove redundant isalpha() check toupper() only modifies lower-case letters, so the isalpha() check is redundant; remove it. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 87daba95639c..946cd5320ef9 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1614,10 +1614,8 @@ static void reg_todo(struct work_struct *work) static void queue_regulatory_request(struct regulatory_request *request) { - if (isalpha(request->alpha2[0])) - request->alpha2[0] = toupper(request->alpha2[0]); - if (isalpha(request->alpha2[1])) - request->alpha2[1] = toupper(request->alpha2[1]); + request->alpha2[0] = toupper(request->alpha2[0]); + request->alpha2[1] = toupper(request->alpha2[1]); spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); -- cgit v1.2.3 From 0ba857ad671aa8aa2a1276aa7a0c79abd0575146 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 19:12:02 +0100 Subject: regulatory: remove useless warning Even if it never happens and is hidden behind the debug config option, it's completely useless: the calltrace will only show module loading. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 946cd5320ef9..b63bdf899a75 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2322,10 +2322,6 @@ int __init regulatory_init(void) * errors as non-fatal. */ pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); -#ifdef CONFIG_CFG80211_REG_DEBUG - /* We want to find out exactly why when debugging */ - WARN_ON(err); -#endif } /* -- cgit v1.2.3 From 5d885b999c68283c493cf00cc020260d38951ae6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:14:17 +0100 Subject: regulatory: simplify freq_reg_info_regd The function itself has dual-purpose: it can retrieve from a given regdomain or from the globally installed one. Change it to have a single purpose only: to look up from a given regdomain. Pass the correct regdomain in the freq_reg_info() function instead. This also changes the locking rules for it, no locking is required any more. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b63bdf899a75..75239789213a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -678,28 +678,15 @@ static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *custom_regd) + const struct ieee80211_regdomain *regd) { int i; bool band_rule_found = false; - const struct ieee80211_regdomain *regd; bool bw_fits = false; if (!desired_bw_khz) desired_bw_khz = MHZ_TO_KHZ(20); - regd = custom_regd ? custom_regd : cfg80211_regdomain; - - /* - * Follow the driver's regulatory domain, if present, unless a country - * IE has been processed or a user wants to help complaince further - */ - if (!custom_regd && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && - wiphy->regd) - regd = wiphy->regd; - if (!regd) return -EINVAL; @@ -735,10 +722,24 @@ static int freq_reg_info_regd(struct wiphy *wiphy, int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) { + const struct ieee80211_regdomain *regd; + + assert_reg_lock(); assert_cfg80211_lock(); + /* + * Follow the driver's regulatory domain, if present, unless a country + * IE has been processed or a user wants to help complaince further + */ + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->initiator != NL80211_REGDOM_SET_BY_USER && + wiphy->regd) + regd = wiphy->regd; + else + regd = cfg80211_regdomain; + return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, - reg_rule, NULL); + reg_rule, regd); } EXPORT_SYMBOL(freq_reg_info); -- cgit v1.2.3 From e8da2bb4fe9ecb888f44714e2624c7d4268ac09d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2012 23:00:08 +0100 Subject: regulatory: clarify locking rules and assertions Many places that currently check that cfg80211_mutex is held don't actually use any data protected by it. The functions that need to hold the cfg80211_mutex are the ones using the cfg80211_regdomain variable, so add the lock assertion to those and clarify this in the comments. The reason for this is that nl80211 uses the regdom without being able to hold reg_mutex. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 75239789213a..603a01911a50 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -94,14 +94,14 @@ static struct device_type reg_device_type = { /* * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no - * information to give us an alpha2 + * information to give us an alpha2. + * Protected by the cfg80211_mutex. */ const struct ieee80211_regdomain *cfg80211_regdomain; /* * Protects static reg.c components: * - cfg80211_world_regdom - * - cfg80211_regdom * - last_request * - reg_num_devs_support_basehint */ @@ -185,6 +185,9 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset) { + assert_cfg80211_lock(); + assert_reg_lock(); + /* avoid freeing static information or freeing something twice */ if (cfg80211_regdomain == cfg80211_world_regdom) cfg80211_regdomain = NULL; @@ -215,6 +218,9 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { WARN_ON(!last_request); + assert_cfg80211_lock(); + assert_reg_lock(); + reset_regdomains(false); cfg80211_world_regdom = rd; @@ -422,8 +428,6 @@ static int call_crda(const char *alpha2) /* Used by nl80211 before kmalloc'ing our regulatory domain */ bool reg_is_valid_request(const char *alpha2) { - assert_cfg80211_lock(); - if (!last_request) return false; @@ -915,8 +919,6 @@ bool reg_last_request_cell_base(void) { bool val; - assert_cfg80211_lock(); - mutex_lock(®_mutex); val = reg_request_cell_base(last_request); mutex_unlock(®_mutex); @@ -999,8 +1001,6 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, bool channel_changed = false; struct ieee80211_channel chan_before; - assert_cfg80211_lock(); - sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; @@ -1042,8 +1042,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy, unsigned int i; struct ieee80211_supported_band *sband; - assert_cfg80211_lock(); - if (!wiphy->bands[reg_beacon->chan.band]) return; @@ -1062,8 +1060,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) struct ieee80211_supported_band *sband; struct reg_beacon *reg_beacon; - assert_cfg80211_lock(); - list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; @@ -1075,6 +1071,8 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) static bool reg_is_world_roaming(struct wiphy *wiphy) { + assert_cfg80211_lock(); + if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; @@ -1116,8 +1114,6 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; - assert_cfg80211_lock(); - if (!is_ht40_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; @@ -1180,6 +1176,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, { enum ieee80211_band band; + assert_cfg80211_lock(); assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) @@ -1299,8 +1296,6 @@ get_reg_request_treatment(struct wiphy *wiphy, { struct wiphy *last_wiphy = NULL; - assert_cfg80211_lock(); - /* All initial requests are respected */ if (!last_request) return REG_REQ_OK; @@ -2246,8 +2241,6 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) void wiphy_regulatory_register(struct wiphy *wiphy) { - assert_cfg80211_lock(); - mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) @@ -2263,8 +2256,6 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; - assert_cfg80211_lock(); - mutex_lock(®_mutex); if (!reg_dev_ignore_cell_hint(wiphy)) -- cgit v1.2.3 From 540f6f2cc545da9ae2baa9faa3152fc550bedb57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:19:24 +0100 Subject: regulatory: remove locking from wiphy_apply_custom_regulatory wiphy_apply_custom_regulatory() doesn't have to hold the regulatory mutex as it only modifies the given wiphy with the given regulatory domain, it doesn't access any global regulatory data. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 603a01911a50..752729ecd701 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1225,8 +1225,6 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - assert_reg_lock(); - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); @@ -1271,14 +1269,12 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, enum ieee80211_band band; unsigned int bands_set = 0; - mutex_lock(®_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!wiphy->bands[band]) continue; handle_band_custom(wiphy, wiphy->bands[band], regd); bands_set++; } - mutex_unlock(®_mutex); /* * no point in calling this if it won't have any effect -- cgit v1.2.3 From 6913b49a5071064f49f7a74b432286fa735f7612 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 00:48:59 +0100 Subject: regulatory: fix reg_is_valid_request handling There's a bug with the world regulatory domain, it can be updated any time which is different from all other regdomains that can only be updated once after a request for them. Fix this by adding a check for "processed" to the reg_is_valid_request() function and clear that when doing a request. While looking at this I also found another locking bug, last_request is protected by the reg_mutex not the cfg80211_mutex so the code in nl80211 is racy. Remove that code as it only tries to prevent an allocation in an error case, which isn't necessary. Then the function can also become static and locking in nl80211 can have a smaller scope. Also change __set_regdom() to do the checks earlier and not different for world/other regdomains. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 18 ++++++------------ net/wireless/reg.c | 21 +++++++++++---------- net/wireless/reg.h | 1 - 3 files changed, 17 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9c2c91845be7..b387deaf1132 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4265,21 +4265,12 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - mutex_lock(&cfg80211_mutex); - - if (!reg_is_valid_request(alpha2)) { - r = -EINVAL; - goto bad_reg; - } - size_of_regd = sizeof(struct ieee80211_regdomain) + num_rules * sizeof(struct ieee80211_reg_rule); rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) { - r = -ENOMEM; - goto bad_reg; - } + if (!rd) + return -ENOMEM; rd->n_reg_rules = num_rules; rd->alpha2[0] = alpha2[0]; @@ -4309,11 +4300,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } + mutex_lock(&cfg80211_mutex); + r = set_regdom(rd); + /* set_regdom took ownership */ rd = NULL; + mutex_unlock(&cfg80211_mutex); bad_reg: - mutex_unlock(&cfg80211_mutex); kfree(rd); return r; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 752729ecd701..b3f94c957d1d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -425,12 +425,16 @@ static int call_crda(const char *alpha2) return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); } -/* Used by nl80211 before kmalloc'ing our regulatory domain */ -bool reg_is_valid_request(const char *alpha2) +static bool reg_is_valid_request(const char *alpha2) { + assert_reg_lock(); + if (!last_request) return false; + if (last_request->processed) + return false; + return alpha2_equal(last_request->alpha2, alpha2); } @@ -1470,6 +1474,7 @@ new_request: last_request = pending_request; last_request->intersect = intersect; + last_request->processed = false; pending_request = NULL; @@ -2060,11 +2065,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; + /* Some basic sanity checks first */ + if (!reg_is_valid_request(rd->alpha2)) + return -EINVAL; + if (is_world_regdom(rd->alpha2)) { - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; update_world_regdomain(rd); return 0; } @@ -2073,9 +2080,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) !is_unknown_alpha2(rd->alpha2)) return -EINVAL; - if (!last_request) - return -EINVAL; - /* * Lets only bother proceeding on the same alpha2 if the current * rd is non static (it means CRDA was present and was used last) @@ -2097,9 +2101,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * internal EEPROM data */ - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - if (!is_valid_rd(rd)) { pr_err("Invalid regulatory domain detected:\n"); print_regdomain_info(rd); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 37891e813a74..d391b50d2829 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -19,7 +19,6 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); -bool reg_is_valid_request(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); int regulatory_hint_user(const char *alpha2, -- cgit v1.2.3 From fe7ef5e9ba0c60bab01390493a4c6790f7b093af Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 4 Dec 2012 15:07:34 +0100 Subject: regulatory: remove handling of channel bandwidth The channel bandwidth handling isn't really quite right, it assumes that a 40 MHz channel is really two 20 MHz channels, which isn't strictly true. This is the way the regulatory database handling is defined right now though so remove the logic to handle other channel widths. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 7 ++-- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 2 +- drivers/net/wireless/rtlwifi/regd.c | 9 ++--- include/net/cfg80211.h | 8 +---- net/wireless/reg.c | 44 ++++++++--------------- 5 files changed, 21 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index d81698015bf7..8ae58c404c67 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -195,7 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -216,7 +215,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, - bandwidth, ®_rule); if (r) continue; @@ -254,7 +252,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - u32 bandwidth = 0; int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; @@ -283,7 +280,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) @@ -291,7 +288,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index a90b72202ec5..30272519d795 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -686,7 +686,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { ret = freq_reg_info(wiphy, ch->center_freq, - 0, &rule); + &rule); if (ret) continue; diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index c1608cddc529..be55dc9167f0 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -158,7 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - u32 bandwidth = 0; int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -174,8 +173,7 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, ch->center_freq, - bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (r) continue; @@ -211,7 +209,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - u32 bandwidth = 0; int r; if (!wiphy->bands[IEEE80211_BAND_2GHZ]) @@ -240,7 +237,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) @@ -248,7 +245,7 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule); + r = freq_reg_info(wiphy, ch->center_freq, ®_rule); if (!r) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8e6a6b73b9c9..c222e5fbf53a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2938,10 +2938,6 @@ extern void wiphy_apply_custom_regulatory( * freq_reg_info - get regulatory information for the given frequency * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for - * @desired_bw_khz: the desired max bandwidth you want to use per - * channel. Note that this is still 20 MHz if you want to use HT40 - * as HT40 makes use of two channels for its 40 MHz width bandwidth. - * If set to 0 we'll assume you want the standard 20 MHz. * @reg_rule: the regulatory rule which we have for this frequency * * Use this function to get the regulatory rule for a specific frequency on @@ -2956,9 +2952,7 @@ extern void wiphy_apply_custom_regulatory( * freq_in_rule_band() for our current definition of a band -- this is purely * subjective and right now its 802.11 specific. */ -extern int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule); /* diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b3f94c957d1d..2e38b47939a3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -480,8 +480,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) } static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, - u32 center_freq_khz, - u32 bw_khz) + u32 center_freq_khz, u32 bw_khz) { u32 start_freq_khz, end_freq_khz; @@ -682,9 +681,7 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -static int freq_reg_info_regd(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, +static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule, const struct ieee80211_regdomain *regd) { @@ -692,9 +689,6 @@ static int freq_reg_info_regd(struct wiphy *wiphy, bool band_rule_found = false; bool bw_fits = false; - if (!desired_bw_khz) - desired_bw_khz = MHZ_TO_KHZ(20); - if (!regd) return -EINVAL; @@ -713,7 +707,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); + bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); if (band_rule_found && bw_fits) { *reg_rule = rr; @@ -727,7 +721,7 @@ static int freq_reg_info_regd(struct wiphy *wiphy, return -EINVAL; } -int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, +int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule) { const struct ieee80211_regdomain *regd; @@ -746,8 +740,7 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, else regd = cfg80211_regdomain; - return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, - reg_rule, regd); + return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); } EXPORT_SYMBOL(freq_reg_info); @@ -770,7 +763,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) } static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { const struct ieee80211_power_rule *power_rule; @@ -785,8 +777,8 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - REG_DBG_PRINT("Updating information on frequency %d MHz for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", + chan->center_freq); REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, @@ -795,7 +787,6 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, } #else static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { return; @@ -805,11 +796,7 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, /* * Note that right now we assume the desired channel bandwidth * is always 20 MHz for each individual channel (HT40 uses 20 MHz - * per channel, the primary and the extension channel). To support - * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a - * new ieee80211_channel.target_bw and re run the regulatory check - * on the wiphy with the target_bw specified. Then we can simply use - * that below for the desired_bw_khz below. + * per channel, the primary and the extension channel). */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, @@ -817,7 +804,6 @@ static void handle_channel(struct wiphy *wiphy, { int r; u32 flags, bw_flags = 0; - u32 desired_bw_khz = MHZ_TO_KHZ(20); const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; @@ -829,8 +815,7 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, ®_rule); + r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), ®_rule); if (r) { /* * We will disable all channels that do not match our @@ -851,7 +836,7 @@ static void handle_channel(struct wiphy *wiphy, return; } - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + chan_reg_rule_print_dbg(chan, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -1223,23 +1208,22 @@ static void handle_channel_custom(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { int r; - u32 desired_bw_khz = MHZ_TO_KHZ(20); u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, ®_rule, regd); + ®_rule, regd); if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits a %d MHz wide channel\n", - chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); + REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", + chan->center_freq); chan->flags = IEEE80211_CHAN_DISABLED; return; } - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + chan_reg_rule_print_dbg(chan, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; -- cgit v1.2.3 From 379b82f4c9dc6e67bf61aa61b096c06a2f320f60 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:44:07 +0100 Subject: regulatory: pass new regdomain to reset function Instead of assigning after calling the function do it inside the function. This will later avoid a period of time where the pointer is NULL. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2e38b47939a3..35541d6d4145 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -183,7 +183,8 @@ static char user_alpha2[2]; module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); -static void reset_regdomains(bool full_reset) +static void reset_regdomains(bool full_reset, + const struct ieee80211_regdomain *new_regdom) { assert_cfg80211_lock(); assert_reg_lock(); @@ -200,7 +201,7 @@ static void reset_regdomains(bool full_reset) kfree(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = NULL; + cfg80211_regdomain = new_regdom; if (!full_reset) return; @@ -221,10 +222,9 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) assert_cfg80211_lock(); assert_reg_lock(); - reset_regdomains(false); + reset_regdomains(false, rd); cfg80211_world_regdom = rd; - cfg80211_regdomain = rd; } bool is_world_regdom(const char *alpha2) @@ -1817,7 +1817,7 @@ static void restore_regulatory_settings(bool reset_user) mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + reset_regdomains(true, cfg80211_world_regdom); restore_alpha2(alpha2, reset_user); /* @@ -1848,9 +1848,8 @@ static void restore_regulatory_settings(bool reset_user) } /* First restore to the basic regulatory settings */ - cfg80211_regdomain = cfg80211_world_regdom; - world_alpha2[0] = cfg80211_regdomain->alpha2[0]; - world_alpha2[1] = cfg80211_regdomain->alpha2[1]; + world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; + world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) @@ -2101,8 +2100,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!last_request->intersect) { if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { - reset_regdomains(false); - cfg80211_regdomain = rd; + reset_regdomains(false, rd); return 0; } @@ -2123,8 +2121,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return PTR_ERR(regd); request_wiphy->regd = regd; - reset_regdomains(false); - cfg80211_regdomain = rd; + reset_regdomains(false, rd); return 0; } @@ -2147,8 +2144,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) rd = NULL; - reset_regdomains(false); - cfg80211_regdomain = intersected_rd; + reset_regdomains(false, intersected_rd); return 0; } @@ -2319,7 +2315,7 @@ void regulatory_exit(void) /* Lock to suppress warnings */ mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true); + reset_regdomains(true, NULL); mutex_unlock(&cfg80211_mutex); mutex_unlock(®_mutex); -- cgit v1.2.3 From 458f4f9e960b9a3b674c4b87d996eef186b1fe83 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:47:38 +0100 Subject: regulatory: use RCU to protect global and wiphy regdomains To simplify the locking and not require cfg80211_mutex (which nl80211 uses to access the global regdomain) and also to make it possible for drivers to access their wiphy->regd safely, use RCU to protect these pointers. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- include/net/regulatory.h | 2 + net/wireless/nl80211.c | 35 ++++++++------- net/wireless/reg.c | 113 +++++++++++++++++++++++++++-------------------- net/wireless/reg.h | 2 +- 5 files changed, 88 insertions(+), 66 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c222e5fbf53a..f3be58a29642 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2369,7 +2369,7 @@ struct wiphy { /* fields below are read-only, assigned by cfg80211 */ - const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain __rcu *regd; /* the item in /sys/class/ieee80211/ points to this, * you need use set_wiphy_dev() (see below) */ diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 7dcaa2794fde..96b0f07cb85b 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -18,6 +18,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include /** * enum environment_cap - Environment parsed from country IE @@ -101,6 +102,7 @@ struct ieee80211_reg_rule { }; struct ieee80211_regdomain { + struct rcu_head rcu_head; u32 n_reg_rules; char alpha2[2]; u8 dfs_region; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b387deaf1132..b3cf7cc0d4a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3787,12 +3787,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) * window between nl80211_init() and regulatory_init(), if that is * even possible. */ - mutex_lock(&cfg80211_mutex); - if (unlikely(!cfg80211_regdomain)) { - mutex_unlock(&cfg80211_mutex); + if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) return -EINPROGRESS; - } - mutex_unlock(&cfg80211_mutex); if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; @@ -4152,6 +4148,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) { + const struct ieee80211_regdomain *regdom; struct sk_buff *msg; void *hdr = NULL; struct nlattr *nl_reg_rules; @@ -4174,35 +4171,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) if (!hdr) goto put_failure; - if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, - cfg80211_regdomain->alpha2) || - (cfg80211_regdomain->dfs_region && - nla_put_u8(msg, NL80211_ATTR_DFS_REGION, - cfg80211_regdomain->dfs_region))) - goto nla_put_failure; - if (reg_last_request_cell_base() && nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, NL80211_USER_REG_HINT_CELL_BASE)) goto nla_put_failure; + rcu_read_lock(); + regdom = rcu_dereference(cfg80211_regdomain); + + if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || + (regdom->dfs_region && + nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) + goto nla_put_failure_rcu; + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); if (!nl_reg_rules) - goto nla_put_failure; + goto nla_put_failure_rcu; - for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + for (i = 0; i < regdom->n_reg_rules; i++) { struct nlattr *nl_reg_rule; const struct ieee80211_reg_rule *reg_rule; const struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule; - reg_rule = &cfg80211_regdomain->reg_rules[i]; + reg_rule = ®dom->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; nl_reg_rule = nla_nest_start(msg, i); if (!nl_reg_rule) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, reg_rule->flags) || @@ -4216,10 +4214,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) power_rule->max_antenna_gain) || nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, power_rule->max_eirp)) - goto nla_put_failure; + goto nla_put_failure_rcu; nla_nest_end(msg, nl_reg_rule); } + rcu_read_unlock(); nla_nest_end(msg, nl_reg_rules); @@ -4227,6 +4226,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) err = genlmsg_reply(msg, info); goto out; +nla_put_failure_rcu: + rcu_read_unlock(); nla_put_failure: genlmsg_cancel(msg, hdr); put_failure: diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 35541d6d4145..9b64b201cdf1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -95,15 +95,15 @@ static struct device_type reg_device_type = { * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2. - * Protected by the cfg80211_mutex. */ -const struct ieee80211_regdomain *cfg80211_regdomain; +const struct ieee80211_regdomain __rcu *cfg80211_regdomain; /* * Protects static reg.c components: - * - cfg80211_world_regdom - * - last_request - * - reg_num_devs_support_basehint + * - cfg80211_regdomain (if not used with RCU) + * - cfg80211_world_regdom + * - last_request + * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); @@ -118,6 +118,25 @@ static inline void assert_reg_lock(void) lockdep_assert_held(®_mutex); } +static const struct ieee80211_regdomain *get_cfg80211_regdom(void) +{ + return rcu_dereference_protected(cfg80211_regdomain, + lockdep_is_held(®_mutex)); +} + +static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +{ + return rcu_dereference_protected(wiphy->regd, + lockdep_is_held(®_mutex)); +} + +static void rcu_free_regdom(const struct ieee80211_regdomain *r) +{ + if (!r) + return; + kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); +} + /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -186,22 +205,25 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *r; + assert_reg_lock(); + r = get_cfg80211_regdom(); + /* avoid freeing static information or freeing something twice */ - if (cfg80211_regdomain == cfg80211_world_regdom) - cfg80211_regdomain = NULL; + if (r == cfg80211_world_regdom) + r = NULL; if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; - if (cfg80211_regdomain == &world_regdom) - cfg80211_regdomain = NULL; + if (r == &world_regdom) + r = NULL; - kfree(cfg80211_regdomain); - kfree(cfg80211_world_regdom); + rcu_free_regdom(r); + rcu_free_regdom(cfg80211_world_regdom); cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = new_regdom; + rcu_assign_pointer(cfg80211_regdomain, new_regdom); if (!full_reset) return; @@ -219,7 +241,6 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { WARN_ON(!last_request); - assert_cfg80211_lock(); assert_reg_lock(); reset_regdomains(false, rd); @@ -280,11 +301,11 @@ static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) static bool regdom_changes(const char *alpha2) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *r = get_cfg80211_regdom(); - if (!cfg80211_regdomain) + if (!r) return true; - return !alpha2_equal(cfg80211_regdomain->alpha2, alpha2); + return !alpha2_equal(r->alpha2, alpha2); } /* @@ -727,7 +748,6 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_regdomain *regd; assert_reg_lock(); - assert_cfg80211_lock(); /* * Follow the driver's regulatory domain, if present, unless a country @@ -736,9 +756,9 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) - regd = wiphy->regd; + regd = get_wiphy_regdom(wiphy); else - regd = cfg80211_regdomain; + regd = get_cfg80211_regdom(); return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); } @@ -809,8 +829,6 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_freq_range *freq_range = NULL; struct wiphy *request_wiphy = NULL; - assert_cfg80211_lock(); - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); flags = chan->orig_flags; @@ -1060,15 +1078,17 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) static bool reg_is_world_roaming(struct wiphy *wiphy) { - assert_cfg80211_lock(); + const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); + const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); - if (is_world_regdom(cfg80211_regdomain->alpha2) || - (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) + if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) return true; + if (last_request && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) return true; + return false; } @@ -1165,13 +1185,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, { enum ieee80211_band band; - assert_cfg80211_lock(); assert_reg_lock(); if (ignore_reg_update(wiphy, initiator)) return; - last_request->dfs_region = cfg80211_regdomain->dfs_region; + last_request->dfs_region = get_cfg80211_regdom()->dfs_region; for (band = 0; band < IEEE80211_NUM_BANDS; band++) handle_band(wiphy, initiator, wiphy->bands[band]); @@ -1188,6 +1207,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) struct cfg80211_registered_device *rdev; struct wiphy *wiphy; + assert_cfg80211_lock(); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); @@ -1402,7 +1423,7 @@ static void reg_set_request_processed(void) * * Returns one of the different reg request treatment values. * - * Caller must hold &cfg80211_mutex and ®_mutex + * Caller must hold ®_mutex */ static enum reg_request_treatment __regulatory_hint(struct wiphy *wiphy, @@ -1412,20 +1433,18 @@ __regulatory_hint(struct wiphy *wiphy, bool intersect = false; enum reg_request_treatment treatment; - assert_cfg80211_lock(); - treatment = get_reg_request_treatment(wiphy, pending_request); switch (treatment) { case REG_REQ_INTERSECT: if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - regd = reg_copy_regd(cfg80211_regdomain); + regd = reg_copy_regd(get_cfg80211_regdom()); if (IS_ERR(regd)) { kfree(pending_request); return PTR_ERR(regd); } - wiphy->regd = regd; + rcu_assign_pointer(wiphy->regd, regd); } intersect = true; break; @@ -1439,13 +1458,13 @@ __regulatory_hint(struct wiphy *wiphy, */ if (treatment == REG_REQ_ALREADY_SET && pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { - regd = reg_copy_regd(cfg80211_regdomain); + regd = reg_copy_regd(get_cfg80211_regdom()); if (IS_ERR(regd)) { kfree(pending_request); return REG_REQ_IGNORE; } treatment = REG_REQ_ALREADY_SET; - wiphy->regd = regd; + rcu_assign_pointer(wiphy->regd, regd); goto new_request; } kfree(pending_request); @@ -2051,6 +2070,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Some basic sanity checks first */ + assert_reg_lock(); + if (!reg_is_valid_request(rd->alpha2)) return -EINVAL; @@ -2120,7 +2141,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (IS_ERR(regd)) return PTR_ERR(regd); - request_wiphy->regd = regd; + rcu_assign_pointer(request_wiphy->regd, regd); reset_regdomains(false, rd); return 0; } @@ -2128,7 +2149,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); + intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; @@ -2138,7 +2159,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * domain we keep it for its private use */ if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) - request_wiphy->regd = rd; + rcu_assign_pointer(request_wiphy->regd, rd); else kfree(rd); @@ -2156,14 +2177,12 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* * Use this call to set the current regulatory domain. Conflicts with * multiple drivers can be ironed out later. Caller must've already - * kmalloc'd the rd structure. Caller must hold cfg80211_mutex + * kmalloc'd the rd structure. */ int set_regdom(const struct ieee80211_regdomain *rd) { int r; - assert_cfg80211_lock(); - mutex_lock(®_mutex); /* Note that this doesn't update the wiphys, this is done below */ @@ -2177,7 +2196,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (WARN_ON(!last_request->intersect && rd != cfg80211_regdomain)) { + if (WARN_ON(!last_request->intersect && + rd != get_cfg80211_regdom())) { r = -EINVAL; goto out; } @@ -2185,7 +2205,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(last_request->initiator); - print_regdomain(cfg80211_regdomain); + print_regdomain(get_cfg80211_regdom()); nl80211_send_reg_change_event(last_request); @@ -2238,7 +2258,8 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; - kfree(wiphy->regd); + rcu_free_regdom(get_wiphy_regdom(wiphy)); + rcu_assign_pointer(wiphy->regd, NULL); if (last_request) request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); @@ -2273,13 +2294,13 @@ int __init regulatory_init(void) reg_regdb_size_check(); - cfg80211_regdomain = cfg80211_world_regdom; + rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); user_alpha2[0] = '9'; user_alpha2[1] = '7'; /* We always try to get an update for the static regdomain */ - err = regulatory_hint_core(cfg80211_regdomain->alpha2); + err = regulatory_hint_core(cfg80211_world_regdom->alpha2); if (err) { if (err == -ENOMEM) return err; @@ -2313,10 +2334,8 @@ void regulatory_exit(void) cancel_delayed_work_sync(®_timeout); /* Lock to suppress warnings */ - mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); reset_regdomains(true, NULL); - mutex_unlock(&cfg80211_mutex); mutex_unlock(®_mutex); dev_set_uevent_suppress(®_pdev->dev, true); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index d391b50d2829..af2d5f8a5d82 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -16,7 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -extern const struct ieee80211_regdomain *cfg80211_regdomain; +extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; bool is_world_regdom(const char *alpha2); bool reg_supported_dfs_region(u8 dfs_region); -- cgit v1.2.3 From c492db370c17c428a0a58d3673294d4e99634b7d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 16:29:25 +0100 Subject: regulatory: use RCU to protect last_request This will allow making freq_reg_info() lock-free. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/regulatory.h | 2 + net/wireless/reg.c | 199 ++++++++++++++++++++++++++--------------------- 2 files changed, 111 insertions(+), 90 deletions(-) (limited to 'net') diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 96b0f07cb85b..f17ed590d64a 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h @@ -36,6 +36,7 @@ enum environment_cap { /** * struct regulatory_request - used to keep track of regulatory requests * + * @rcu_head: RCU head struct used to free the request * @wiphy_idx: this is set if this request's initiator is * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This * can be used by the wireless core to deal with conflicts @@ -73,6 +74,7 @@ enum environment_cap { * @list: used to insert into the reg_requests_list linked list */ struct regulatory_request { + struct rcu_head rcu_head; int wiphy_idx; enum nl80211_reg_initiator initiator; enum nl80211_user_reg_hint_type user_reg_hint_type; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9b64b201cdf1..2a7c3adf902f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -82,7 +82,8 @@ static struct regulatory_request core_request_world = { }; /* Receipt of information from last regulatory request */ -static struct regulatory_request *last_request = &core_request_world; +static struct regulatory_request __rcu *last_request = + (void __rcu *)&core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; @@ -102,7 +103,7 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; * Protects static reg.c components: * - cfg80211_regdomain (if not used with RCU) * - cfg80211_world_regdom - * - last_request + * - last_request (if not used with RCU) * - reg_num_devs_support_basehint */ static DEFINE_MUTEX(reg_mutex); @@ -137,6 +138,12 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); } +static struct regulatory_request *get_last_request(void) +{ + return rcu_dereference_protected(last_request, + lockdep_is_held(®_mutex)); +} + /* Used to queue up regulatory hints */ static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; @@ -206,6 +213,7 @@ static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) { const struct ieee80211_regdomain *r; + struct regulatory_request *lr; assert_reg_lock(); @@ -228,9 +236,10 @@ static void reset_regdomains(bool full_reset, if (!full_reset) return; - if (last_request != &core_request_world) - kfree(last_request); - last_request = &core_request_world; + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); + rcu_assign_pointer(last_request, &core_request_world); } /* @@ -239,9 +248,11 @@ static void reset_regdomains(bool full_reset, */ static void update_world_regdomain(const struct ieee80211_regdomain *rd) { - WARN_ON(!last_request); + struct regulatory_request *lr; - assert_reg_lock(); + lr = get_last_request(); + + WARN_ON(!lr); reset_regdomains(false, rd); @@ -448,15 +459,12 @@ static int call_crda(const char *alpha2) static bool reg_is_valid_request(const char *alpha2) { - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); - if (!last_request) + if (!lr || lr->processed) return false; - if (last_request->processed) - return false; - - return alpha2_equal(last_request->alpha2, alpha2); + return alpha2_equal(lr->alpha2, alpha2); } /* Sanity check on a regulatory rule */ @@ -746,15 +754,14 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, const struct ieee80211_reg_rule **reg_rule) { const struct ieee80211_regdomain *regd; - - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); /* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + lr->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = get_wiphy_regdom(wiphy); else @@ -828,8 +835,9 @@ static void handle_channel(struct wiphy *wiphy, const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; struct wiphy *request_wiphy = NULL; + struct regulatory_request *lr = get_last_request(); - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); flags = chan->orig_flags; @@ -862,7 +870,7 @@ static void handle_channel(struct wiphy *wiphy, if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { /* @@ -927,7 +935,7 @@ bool reg_last_request_cell_base(void) bool val; mutex_lock(®_mutex); - val = reg_request_cell_base(last_request); + val = reg_request_cell_base(get_last_request()); mutex_unlock(®_mutex); return val; @@ -938,10 +946,12 @@ bool reg_last_request_cell_base(void) static enum reg_request_treatment reg_ignore_cell_hint(struct regulatory_request *pending_request) { + struct regulatory_request *lr = get_last_request(); + if (!reg_num_devs_support_basehint) return REG_REQ_IGNORE; - if (reg_request_cell_base(last_request) && + if (reg_request_cell_base(lr) && !regdom_changes(pending_request->alpha2)) return REG_REQ_ALREADY_SET; @@ -969,7 +979,9 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { - if (!last_request) { + struct regulatory_request *lr = get_last_request(); + + if (!lr) { REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", reg_initiator_name(initiator)); return true; @@ -988,13 +1000,13 @@ static bool ignore_reg_update(struct wiphy *wiphy, */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - !is_world_regdom(last_request->alpha2)) { + !is_world_regdom(lr->alpha2)) { REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", reg_initiator_name(initiator)); return true; } - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) return reg_dev_ignore_cell_hint(wiphy); return false; @@ -1080,12 +1092,12 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) { const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); + struct regulatory_request *lr = get_last_request(); if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) return true; - if (last_request && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) return true; @@ -1184,13 +1196,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; - - assert_reg_lock(); + struct regulatory_request *lr = get_last_request(); if (ignore_reg_update(wiphy, initiator)) return; - last_request->dfs_region = get_cfg80211_regdom()->dfs_region; + lr->dfs_region = get_cfg80211_regdom()->dfs_region; for (band = 0; band < IEEE80211_NUM_BANDS; band++) handle_band(wiphy, initiator, wiphy->bands[band]); @@ -1199,7 +1210,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, reg_process_ht_flags(wiphy); if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); + wiphy->reg_notifier(wiphy, lr); } static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) @@ -1220,7 +1231,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); + wiphy->reg_notifier(wiphy, get_last_request()); } } @@ -1300,28 +1311,28 @@ get_reg_request_treatment(struct wiphy *wiphy, struct regulatory_request *pending_request) { struct wiphy *last_wiphy = NULL; + struct regulatory_request *lr = get_last_request(); /* All initial requests are respected */ - if (!last_request) + if (!lr) return REG_REQ_OK; switch (pending_request->initiator) { case NL80211_REGDOM_SET_BY_CORE: return REG_REQ_OK; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (reg_request_cell_base(last_request)) { + if (reg_request_cell_base(lr)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(pending_request->alpha2)) return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; } - last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different @@ -1343,7 +1354,7 @@ get_reg_request_treatment(struct wiphy *wiphy, } return 0; case NL80211_REGDOM_SET_BY_DRIVER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(pending_request->alpha2)) return REG_REQ_OK; return REG_REQ_ALREADY_SET; @@ -1354,7 +1365,7 @@ get_reg_request_treatment(struct wiphy *wiphy, * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) return REG_REQ_ALREADY_SET; @@ -1363,26 +1374,26 @@ get_reg_request_treatment(struct wiphy *wiphy, if (reg_request_cell_base(pending_request)) return reg_ignore_cell_hint(pending_request); - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) return REG_REQ_IGNORE; - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_REQ_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) + if (lr->initiator == NL80211_REGDOM_SET_BY_USER && + lr->intersect) return REG_REQ_IGNORE; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) && - regdom_changes(last_request->alpha2)) + if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || + lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || + lr->initiator == NL80211_REGDOM_SET_BY_USER) && + regdom_changes(lr->alpha2)) return REG_REQ_IGNORE; if (!regdom_changes(pending_request->alpha2)) @@ -1397,15 +1408,16 @@ get_reg_request_treatment(struct wiphy *wiphy, static void reg_set_request_processed(void) { bool need_more_processing = false; + struct regulatory_request *lr = get_last_request(); - last_request->processed = true; + lr->processed = true; spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) + if (lr->initiator == NL80211_REGDOM_SET_BY_USER) cancel_delayed_work(®_timeout); if (need_more_processing) @@ -1432,6 +1444,7 @@ __regulatory_hint(struct wiphy *wiphy, const struct ieee80211_regdomain *regd; bool intersect = false; enum reg_request_treatment treatment; + struct regulatory_request *lr; treatment = get_reg_request_treatment(wiphy, pending_request); @@ -1472,18 +1485,20 @@ __regulatory_hint(struct wiphy *wiphy, } new_request: - if (last_request != &core_request_world) - kfree(last_request); + lr = get_last_request(); + if (lr != &core_request_world && lr) + kfree_rcu(lr, rcu_head); - last_request = pending_request; - last_request->intersect = intersect; - last_request->processed = false; + pending_request->intersect = intersect; + pending_request->processed = false; + rcu_assign_pointer(last_request, pending_request); + lr = pending_request; pending_request = NULL; - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = last_request->alpha2[0]; - user_alpha2[1] = last_request->alpha2[1]; + if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { + user_alpha2[0] = lr->alpha2[0]; + user_alpha2[1] = lr->alpha2[1]; } /* When r == REG_REQ_INTERSECT we do need to call CRDA */ @@ -1494,13 +1509,13 @@ new_request: * inform userspace we have processed the request */ if (treatment == REG_REQ_ALREADY_SET) { - nl80211_send_reg_change_event(last_request); + nl80211_send_reg_change_event(lr); reg_set_request_processed(); } return treatment; } - if (call_crda(last_request->alpha2)) + if (call_crda(lr->alpha2)) return REG_REQ_IGNORE; return REG_REQ_OK; } @@ -1543,13 +1558,14 @@ static void reg_process_hint(struct regulatory_request *reg_request, */ static void reg_process_pending_hints(void) { - struct regulatory_request *reg_request; + struct regulatory_request *reg_request, *lr; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); + lr = get_last_request(); /* When last_request->processed becomes true this will be rescheduled */ - if (last_request && !last_request->processed) { + if (lr && !lr->processed) { REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); goto out; } @@ -1702,11 +1718,12 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; - struct regulatory_request *request; + struct regulatory_request *request, *lr; mutex_lock(®_mutex); + lr = get_last_request(); - if (unlikely(!last_request)) + if (unlikely(!lr)) goto out; /* IE len must be evenly divisible by 2 */ @@ -1729,8 +1746,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, * We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->wiphy_idx != WIPHY_IDX_INVALID) + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + lr->wiphy_idx != WIPHY_IDX_INVALID) goto out; request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); @@ -2021,13 +2038,12 @@ static void print_dfs_region(u8 dfs_region) static void print_regdomain(const struct ieee80211_regdomain *rd) { + struct regulatory_request *lr = get_last_request(); if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *rdev; - rdev = cfg80211_rdev_by_wiphy_idx( - last_request->wiphy_idx); + rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); if (rdev) { pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], @@ -2042,7 +2058,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (is_unknown_alpha2(rd->alpha2)) pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else { - if (reg_request_cell_base(last_request)) + if (reg_request_cell_base(lr)) pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", rd->alpha2[0], rd->alpha2[1]); else @@ -2067,11 +2083,10 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; struct wiphy *request_wiphy; + struct regulatory_request *lr = get_last_request(); /* Some basic sanity checks first */ - assert_reg_lock(); - if (!reg_is_valid_request(rd->alpha2)) return -EINVAL; @@ -2089,7 +2104,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { /* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called @@ -2111,16 +2126,16 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; } - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (!request_wiphy && - (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { + (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || + lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { schedule_delayed_work(®_timeout, 0); return -ENODEV; } - if (!last_request->intersect) { - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { + if (!lr->intersect) { + if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(false, rd); return 0; } @@ -2148,7 +2163,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { + if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; @@ -2158,7 +2173,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) rcu_assign_pointer(request_wiphy->regd, rd); else kfree(rd); @@ -2181,9 +2196,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) */ int set_regdom(const struct ieee80211_regdomain *rd) { + struct regulatory_request *lr; int r; mutex_lock(®_mutex); + lr = get_last_request(); /* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); @@ -2196,18 +2213,17 @@ int set_regdom(const struct ieee80211_regdomain *rd) } /* This would make this whole thing pointless */ - if (WARN_ON(!last_request->intersect && - rd != get_cfg80211_regdom())) { + if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { r = -EINVAL; goto out; } /* update all wiphys now with the new established regulatory domain */ - update_all_wiphy_regulatory(last_request->initiator); + update_all_wiphy_regulatory(lr->initiator); print_regdomain(get_cfg80211_regdom()); - nl80211_send_reg_change_event(last_request); + nl80211_send_reg_change_event(lr); reg_set_request_processed(); @@ -2220,10 +2236,11 @@ int set_regdom(const struct ieee80211_regdomain *rd) #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - if (last_request && !last_request->processed) { + struct regulatory_request *lr = get_last_request(); + + if (lr && !lr->processed) { if (add_uevent_var(env, "COUNTRY=%c%c", - last_request->alpha2[0], - last_request->alpha2[1])) + lr->alpha2[0], lr->alpha2[1])) return -ENOMEM; } @@ -2252,8 +2269,10 @@ void wiphy_regulatory_register(struct wiphy *wiphy) void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; + struct regulatory_request *lr; mutex_lock(®_mutex); + lr = get_last_request(); if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; @@ -2261,14 +2280,14 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) rcu_free_regdom(get_wiphy_regdom(wiphy)); rcu_assign_pointer(wiphy->regd, NULL); - if (last_request) - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); + if (lr) + request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) goto out; - last_request->wiphy_idx = WIPHY_IDX_INVALID; - last_request->country_ie_env = ENVIRON_ANY; + lr->wiphy_idx = WIPHY_IDX_INVALID; + lr->country_ie_env = ENVIRON_ANY; out: mutex_unlock(®_mutex); } -- cgit v1.2.3 From 361c9c8b0eeeec7d881e018d5143bf883558c566 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2012 15:57:14 +0100 Subject: regulatory: use IS_ERR macro family for freq_reg_info Instead of returning an error and filling a pointer return the pointer and an ERR_PTR value in error cases. Acked-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/regd.c | 16 ++++------ drivers/net/wireless/brcm80211/brcmsmac/channel.c | 7 ++--- drivers/net/wireless/rtlwifi/regd.c | 14 ++++----- include/net/cfg80211.h | 18 +++++------ net/wireless/reg.c | 38 ++++++++++------------- 5 files changed, 41 insertions(+), 52 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 8ae58c404c67..7a6c79e1f819 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -195,7 +195,6 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -213,10 +212,8 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, - ch->center_freq, - ®_rule); - if (r) + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(reg_rule)) continue; /* * If 11d had a rule for this channel ensure @@ -252,7 +249,6 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; if (!sband) @@ -280,16 +276,16 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 30272519d795..4eb3f0d52105 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -670,7 +670,7 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *rule; - int band, i, ret; + int band, i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -685,9 +685,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - ret = freq_reg_info(wiphy, ch->center_freq, - &rule); - if (ret) + rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(rule)) continue; if (!(rule->flags & NL80211_RRF_NO_IBSS)) diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index be55dc9167f0..7e3ead774fb9 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -158,7 +158,6 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; - int r; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { @@ -173,8 +172,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (r) + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (IS_ERR(reg_rule)) continue; /* @@ -209,7 +208,6 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; const struct ieee80211_reg_rule *reg_rule; - int r; if (!wiphy->bands[IEEE80211_BAND_2GHZ]) return; @@ -237,16 +235,16 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; } ch = &sband->channels[12]; /* CH 13 */ - r = freq_reg_info(wiphy, ch->center_freq, ®_rule); - if (!r) { + reg_rule = freq_reg_info(wiphy, ch->center_freq); + if (!IS_ERR(reg_rule)) { if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f3be58a29642..1f74360b527c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2938,22 +2938,22 @@ extern void wiphy_apply_custom_regulatory( * freq_reg_info - get regulatory information for the given frequency * @wiphy: the wiphy for which we want to process this rule for * @center_freq: Frequency in KHz for which we want regulatory information for - * @reg_rule: the regulatory rule which we have for this frequency * * Use this function to get the regulatory rule for a specific frequency on * a given wireless device. If the device has a specific regulatory domain * it wants to follow we respect that unless a country IE has been received * and processed already. * - * Returns 0 if it was able to find a valid regulatory rule which does - * apply to the given center_freq otherwise it returns non-zero. It will - * also return -ERANGE if we determine the given center_freq does not even have - * a regulatory rule for a frequency range in the center_freq's band. See - * freq_in_rule_band() for our current definition of a band -- this is purely - * subjective and right now its 802.11 specific. + * When an error occurs, for example if no rule can be found, the return value + * is encoded using ERR_PTR(). Use IS_ERR() to check and PTR_ERR() to obtain + * the numeric return value. The numeric return value will be -ERANGE if we + * determine the given center_freq does not even have a regulatory rule for a + * frequency range in the center_freq's band. See freq_in_rule_band() for our + * current definition of a band -- this is purely subjective and right now it's + * 802.11 specific. */ -extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule); +const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, + u32 center_freq); /* * callbacks for asynchronous cfg80211 methods, notification diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2a7c3adf902f..fd53d975c0bc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -710,16 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags) return channel_flags; } -static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *regd) +static const struct ieee80211_reg_rule * +freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, + const struct ieee80211_regdomain *regd) { int i; bool band_rule_found = false; bool bw_fits = false; if (!regd) - return -EINVAL; + return ERR_PTR(-EINVAL); for (i = 0; i < regd->n_reg_rules; i++) { const struct ieee80211_reg_rule *rr; @@ -738,20 +738,18 @@ static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); - if (band_rule_found && bw_fits) { - *reg_rule = rr; - return 0; - } + if (band_rule_found && bw_fits) + return rr; } if (!band_rule_found) - return -ERANGE; + return ERR_PTR(-ERANGE); - return -EINVAL; + return ERR_PTR(-EINVAL); } -int freq_reg_info(struct wiphy *wiphy, u32 center_freq, - const struct ieee80211_reg_rule **reg_rule) +const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, + u32 center_freq) { const struct ieee80211_regdomain *regd; struct regulatory_request *lr = get_last_request(); @@ -767,7 +765,7 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, else regd = get_cfg80211_regdom(); - return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); + return freq_reg_info_regd(wiphy, center_freq, regd); } EXPORT_SYMBOL(freq_reg_info); @@ -829,7 +827,6 @@ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ieee80211_channel *chan) { - int r; u32 flags, bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; @@ -841,8 +838,8 @@ static void handle_channel(struct wiphy *wiphy, flags = chan->orig_flags; - r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), ®_rule); - if (r) { + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); + if (IS_ERR(reg_rule)) { /* * We will disable all channels that do not match our * received regulatory rule unless the hint is coming @@ -854,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy, * while 5 GHz is still supported. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - r == -ERANGE) + PTR_ERR(reg_rule) == -ERANGE) return; REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); @@ -1239,16 +1236,15 @@ static void handle_channel_custom(struct wiphy *wiphy, struct ieee80211_channel *chan, const struct ieee80211_regdomain *regd) { - int r; u32 bw_flags = 0; const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; - r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), - ®_rule, regd); + reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), + regd); - if (r) { + if (IS_ERR(reg_rule)) { REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", chan->center_freq); chan->flags = IEEE80211_CHAN_DISABLED; -- cgit v1.2.3 From 75de9113bb9dc4939a7cd54e4bdfad555b35f5b1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:56:03 +0100 Subject: mac80211: optimise AP stop RCU handling If there are VLANs, stopping an AP is inefficient as it calls rcu_barrier() once for each interface (the VLANs and the AP itself). Optimise this by moving rcu_barrier() out of the station cleanups and calling it only once for all interfaces combined. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +++++++-- net/mac80211/iface.c | 13 ++++++++----- net/mac80211/sta_info.c | 2 -- net/mac80211/sta_info.h | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index be019533b233..908f1153942d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,8 +1010,13 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) kfree_rcu(old_probe_resp, rcu_head); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush(vlan); - sta_info_flush(sdata); + sta_info_flush_defer(vlan); + sta_info_flush_defer(sdata); + rcu_barrier(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + sta_info_flush_cleanup(vlan); + sta_info_flush_cleanup(sdata); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 12341efb109e..1ab7d8e57d39 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -863,12 +863,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * sta_info_flush_cleanup() calls rcu_barrier to - * wait for the station call_rcu() calls to complete, - * here we require it to wait for the RX path in case - * it is using the interface and enqueuing frames at - * this very time on another CPU. + * + * sta_info_flush_cleanup() requires rcu_barrier() + * first to wait for the station call_rcu() calls + * to complete, here we need at least sychronize_rcu() + * it to wait for the RX path in case it is using the + * interface and enqueuing frames at this very time on + * another CPU. */ + rcu_barrier(); sta_info_flush_cleanup(sdata); skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7199b9d5b2f4..738f9349c0a2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -920,8 +920,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) { - rcu_barrier(); - ieee80211_cleanup_sdata_stas(sdata); cancel_work_sync(&sdata->cleanup_stations_wk); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c3266aed4a07..031e4a5bbeca 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -549,6 +549,19 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush_cleanup - flush the sta_info cleanup queue + * @sdata: the interface + * + * Flushes the sta_info cleanup queue for a given interface; + * this is necessary before the interface is removed or, for + * AP/mesh interfaces, before it is deconfigured. + * + * Note an rcu_barrier() must precede the function, after all + * stations have been flushed/removed to ensure the call_rcu() + * calls that add stations to the cleanup queue have completed. + */ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); /** @@ -562,6 +575,7 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) { int ret = sta_info_flush_defer(sdata); + rcu_barrier(); sta_info_flush_cleanup(sdata); return ret; -- cgit v1.2.3 From 9cab315190cddbe3aaa31b64841354dd719afdfd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 00:19:08 +0100 Subject: cfg80211: adjacent 80+80 MHz channel segments are invalid In that case, it's really a 160 MHz channel, so disallow this configuration. Signed-off-by: Johannes Berg --- net/wireless/chan.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a7990bb16529..396373f3ec26 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -76,6 +76,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) return false; if (!chandef->center_freq2) return false; + /* adjacent is not allowed -- that's a 160 MHz channel */ + if (chandef->center_freq1 - chandef->center_freq2 == 80 || + chandef->center_freq2 - chandef->center_freq1 == 80) + return false; break; case NL80211_CHAN_WIDTH_80: if (chandef->center_freq1 != control_freq + 30 && -- cgit v1.2.3 From cc3983d8ab174ca43d5817f16c489b3cce3a795a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Dec 2012 12:45:06 +0100 Subject: mac80211: fix ieee80211_ie_build_vht_cap indentation Signed-off-by: Johannes Berg --- net/mac80211/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f11e8c540db4..051313f0d1c2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1864,7 +1864,7 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, } u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, - u32 cap) + u32 cap) { __le32 tmp; -- cgit v1.2.3 From d45c41722a3eca639ced2c4c93665ced229b1da9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 10 Dec 2012 16:19:13 +0200 Subject: mac82011: use frame control to differentiate probe resp/beacon The probe response/beacon management frame RX code passes a bool parameter to differentiate beacons and probe responses. This is useless since we have the frame and can thus use its frame control field. Moreover it is buggy since there is one call to ieee80211_rx_bss_info with a beacon frame that is indicated as a probe response, which is also fixed by using the frame control field, so do that. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 50 ++++++++++++++++------------------------------ net/mac80211/ieee80211_i.h | 3 +-- net/mac80211/mlme.c | 13 +++++------- net/mac80211/scan.c | 9 ++++----- 4 files changed, 27 insertions(+), 48 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 854de974dc9a..272274931982 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -425,11 +425,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems, - bool beacon) + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; int freq; @@ -530,7 +528,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel, beacon); + channel); if (!bss) return; @@ -877,14 +875,21 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb(sdata, skb); } -static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) +static +void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) { size_t baselen; struct ieee802_11_elems elems; + BUILD_BUG_ON(offsetof(typeof(mgmt->u.probe_resp), variable) != + offsetof(typeof(mgmt->u.beacon), variable)); + + /* + * either beacon or probe_resp but the variable field is at the + * same offset + */ baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; if (baselen > len) return; @@ -892,25 +897,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); -} - -static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) -{ - size_t baselen; - struct ieee802_11_elems elems; - - /* Process beacon from the current BSS */ - baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; - if (baselen > len) - return; - - ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); } void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -934,12 +921,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_rx_mgmt_probe_req(sdata, skb); break; case IEEE80211_STYPE_PROBE_RESP: - ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, - rx_status); - break; case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, - rx_status); + ieee80211_rx_mgmt_probe_beacon(sdata, mgmt, skb->len, + rx_status); break; case IEEE80211_STYPE_AUTH: ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8563b9a5cac3..fa6d43e6197f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1346,8 +1346,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - struct ieee80211_channel *channel, - bool beacon); + struct ieee80211_channel *channel); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3cf85d8629a7..7d253a022173 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2369,8 +2369,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems, - bool beacon) + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; int freq; @@ -2404,7 +2403,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, return; bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - channel, beacon); + channel); if (bss) ieee80211_rx_bss_put(local, bss); @@ -2447,7 +2446,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); if (ifmgd->associated && ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) @@ -2528,8 +2527,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, - false); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->sent_assoc = false; /* continue assoc process */ @@ -2682,8 +2680,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; - ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, - true); + ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len)) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d59fc6818b1c..06cbe26892a8 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -65,12 +65,11 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems) struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - struct ieee80211_channel *channel, - bool beacon) + struct ieee80211_channel *channel) { + bool beacon = ieee80211_is_beacon(mgmt->frame_control); struct cfg80211_bss *cbss; struct ieee80211_bss *bss; int clen, srlen; @@ -203,7 +202,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) bss = ieee80211_bss_info_update(local, rx_status, mgmt, skb->len, &elems, - channel, beacon); + channel); if (bss) ieee80211_rx_bss_put(local, bss); } -- cgit v1.2.3 From 61e8a48cc1283c8e4358b3ce06305d886c55f7ce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 16:47:42 +0100 Subject: mac80211: clean up ieee80211_quiesce It's a bit odd that there's a return value that only depends on the iftype, move that logic out of the function into the only caller that needs it. Also, since the quiescing could stop timers that trigger the sdata work, move the sdata work cancel into the function and after the actual quiesce. Finally, there's no need to call it on interfaces that are down, so don't. Change-Id: I1632d46d21ba3558ea713d035184f1939905f2f1 Signed-off-by: Johannes Berg --- net/mac80211/pm.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 79a48f37d409..f3217831d236 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -7,25 +7,23 @@ #include "led.h" /* return value indicates whether the driver should be further notified */ -static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) +static void ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) { switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_quiesce(sdata); - return true; + break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_quiesce(sdata); - return true; + break; case NL80211_IFTYPE_MESH_POINT: ieee80211_mesh_quiesce(sdata); - return true; - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_MONITOR: - /* don't tell driver about this */ - return false; + break; default: - return true; + break; } + + cancel_work_sync(&sdata->work); } int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) @@ -94,10 +92,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) WARN_ON(err != 1); local->wowlan = false; } else { - list_for_each_entry(sdata, &local->interfaces, list) { - cancel_work_sync(&sdata->work); - ieee80211_quiesce(sdata); - } + list_for_each_entry(sdata, &local->interfaces, list) + if (ieee80211_sdata_running(sdata)) + ieee80211_quiesce(sdata); goto suspend; } } @@ -124,13 +121,18 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - cancel_work_sync(&sdata->work); - - if (!ieee80211_quiesce(sdata)) + if (!ieee80211_sdata_running(sdata)) continue; - if (!ieee80211_sdata_running(sdata)) + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* skip these */ continue; + default: + ieee80211_quiesce(sdata); + break; + } /* disable beaconing */ ieee80211_bss_info_change_notify(sdata, -- cgit v1.2.3 From 529ba6e9313dbe60dab7e72c6fdf647a012e9f5b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 17:16:45 +0100 Subject: mac80211: clean up association better in suspend When suspending, bss_info_changed() is called to disable beacons, but managed mode interfaces are simply removed (bss_info_changed() is called with "no change" only). This can lead to problems. To fix this and copy the BSS configuration, clear it during suspend and restore it on resume. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 5 +++++ net/mac80211/pm.c | 19 ++++++++++++++++--- net/mac80211/util.c | 5 +++++ 3 files changed, 26 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fa6d43e6197f..38e7883cff23 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -783,6 +783,11 @@ struct ieee80211_sub_if_data { struct dentry *default_mgmt_key; } debugfs; #endif + +#ifdef CONFIG_PM + struct ieee80211_bss_conf suspend_bss_conf; +#endif + /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index f3217831d236..712c17ff6e22 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,6 +121,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { + u32 changed = BSS_CHANGED_BEACON_ENABLED; + if (!ieee80211_sdata_running(sdata)) continue; @@ -129,14 +131,25 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) case NL80211_IFTYPE_MONITOR: /* skip these */ continue; + case NL80211_IFTYPE_STATION: + if (sdata->vif.bss_conf.assoc) + changed = BSS_CHANGED_ASSOC | + BSS_CHANGED_BSSID | + BSS_CHANGED_IDLE; + else + changed = 0; + /* fall through */ default: ieee80211_quiesce(sdata); break; } - /* disable beaconing */ - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_ENABLED); + sdata->suspend_bss_conf = sdata->vif.bss_conf; + memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); + sdata->vif.bss_conf.idle = true; + + /* disable beaconing or remove association */ + ieee80211_bss_info_change_notify(sdata, changed); if (sdata->vif.type == NL80211_IFTYPE_AP && rcu_access_pointer(sdata->u.ap.beacon)) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 051313f0d1c2..2e2e13bf96b2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1526,6 +1526,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_IDLE | BSS_CHANGED_TXPOWER; +#ifdef CONFIG_PM + if (local->resuming) + sdata->vif.bss_conf = sdata->suspend_bss_conf; +#endif + switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: changed |= BSS_CHANGED_ASSOC | -- cgit v1.2.3 From 8a61af65c6d03781015315dbc43d0942a5b31db9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Dec 2012 17:42:30 +0100 Subject: mac80211: fix channel context iteration During suspend/resume channel contexts might be iterated even if they haven't been re-added to the driver, keep track of this and skip them in iteration. Also use the new status for sanity checks. Also clarify the fact that during HW restart all contexts are iterated over (thanks Eliad.) Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/chan.c | 3 ++- net/mac80211/driver-ops.h | 15 ++++++++++++--- net/mac80211/ieee80211_i.h | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ee50c5eba50c..0978b0faa880 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3754,6 +3754,11 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, * The iterator will not find a context that's being added (during * the driver callback to add it) but will find it while it's being * removed. + * + * Note that during hardware restart, all contexts that existed + * before the restart are considered already present so will be + * found while iterating, whether they've been re-added already + * or not. */ void ieee80211_iter_chan_contexts_atomic( struct ieee80211_hw *hw, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 80e55527504b..1bfe0a8b19d2 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -381,7 +381,8 @@ void ieee80211_iter_chan_contexts_atomic( rcu_read_lock(); list_for_each_entry_rcu(ctx, &local->chanctx_list, list) - iter(hw, &ctx->conf, iter_data); + if (ctx->driver_present) + iter(hw, &ctx->conf, iter_data); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 698dc7e6f309..608ced41548d 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -913,6 +913,8 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, if (local->ops->add_chanctx) ret = local->ops->add_chanctx(&local->hw, &ctx->conf); trace_drv_return_int(local, ret); + if (!ret) + ctx->driver_present = true; return ret; } @@ -924,6 +926,7 @@ static inline void drv_remove_chanctx(struct ieee80211_local *local, if (local->ops->remove_chanctx) local->ops->remove_chanctx(&local->hw, &ctx->conf); trace_drv_return_void(local); + ctx->driver_present = false; } static inline void drv_change_chanctx(struct ieee80211_local *local, @@ -931,8 +934,10 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, u32 changed) { trace_drv_change_chanctx(local, ctx, changed); - if (local->ops->change_chanctx) + if (local->ops->change_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); local->ops->change_chanctx(&local->hw, &ctx->conf, changed); + } trace_drv_return_void(local); } @@ -945,10 +950,12 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local, check_sdata_in_driver(sdata); trace_drv_assign_vif_chanctx(local, sdata, ctx); - if (local->ops->assign_vif_chanctx) + if (local->ops->assign_vif_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); ret = local->ops->assign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); + } trace_drv_return_int(local, ret); return ret; @@ -961,10 +968,12 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, check_sdata_in_driver(sdata); trace_drv_unassign_vif_chanctx(local, sdata, ctx); - if (local->ops->unassign_vif_chanctx) + if (local->ops->unassign_vif_chanctx) { + WARN_ON_ONCE(!ctx->driver_present); local->ops->unassign_vif_chanctx(&local->hw, &sdata->vif, &ctx->conf); + } trace_drv_return_void(local); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 38e7883cff23..23161189b173 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -685,6 +685,7 @@ struct ieee80211_chanctx { enum ieee80211_chanctx_mode mode; int refcount; + bool driver_present; struct ieee80211_chanctx_conf conf; }; -- cgit v1.2.3 From d6a83228823fc0cc8d79d95c9f0bf568b7317862 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:06:28 +0100 Subject: mac80211: track enable_beacon explicitly Instead of calculating in ieee80211_bss_info_change_notify() whether beaconing should be enabled or not, set it in the correct places in the callers. This simplifies the logic in this function at the expense of offchannel, but is also more robust. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 +++ net/mac80211/ibss.c | 3 +++ net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/main.c | 32 -------------------------------- net/mac80211/mesh.c | 3 +++ net/mac80211/offchannel.c | 15 +++++++++------ 6 files changed, 21 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 908f1153942d..3e7d557fd481 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -930,6 +930,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.beacon_int = params->beacon_interval; sdata->vif.bss_conf.dtim_period = params->dtim_period; + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.ssid_len = params->ssid_len; if (params->ssid_len) @@ -1017,6 +1018,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) sta_info_flush_cleanup(vlan); sta_info_flush_cleanup(sdata); + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 272274931982..b4b866f41919 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -191,6 +191,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(ifibss->presp, skb); + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; bss_change = BSS_CHANGED_BEACON_INT; @@ -1189,6 +1190,8 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; sdata->vif.bss_conf.ibss_creator = false; + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); synchronize_rcu(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 23161189b173..c084c1503c04 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -659,10 +659,13 @@ enum ieee80211_sub_if_data_flags { * change handling while the interface is up * @SDATA_STATE_OFFCHANNEL: This interface is currently in offchannel * mode, so queues are stopped + * @SDATA_STATE_OFFCHANNEL_BEACON_STOPPED: Beaconing was stopped due + * to offchannel, reset when offchannel returns */ enum ieee80211_sdata_state_bits { SDATA_STATE_RUNNING, SDATA_STATE_OFFCHANNEL, + SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, }; /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0c0d4cabd76c..71a64f49fe61 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -245,38 +245,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, break; } - if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (local->quiescing || !ieee80211_sdata_running(sdata) || - test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) { - sdata->vif.bss_conf.enable_beacon = false; - } else { - /* - * Beacon should be enabled, but AP mode must - * check whether there is a beacon configured. - */ - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.ap.beacon; - break; - case NL80211_IFTYPE_ADHOC: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.ibss.presp; - break; -#ifdef CONFIG_MAC80211_MESH - case NL80211_IFTYPE_MESH_POINT: - sdata->vif.bss_conf.enable_beacon = - !!sdata->u.mesh.mesh_id_len; - break; -#endif - default: - /* not reached */ - WARN_ON(1); - break; - } - } - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c0008d5dff0a..fcfa7ef9af98 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -626,6 +626,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; + sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); @@ -648,6 +649,8 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) /* stop the beacon */ ifmsh->mesh_id_len = 0; + sdata->vif.bss_conf.enable_beacon = false; + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); /* flush STAs and mpaths on this iface */ diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index a5379aea7d09..1430b48600fc 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -126,11 +126,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); /* Check to see if we should disable beaconing. */ - if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + if (sdata->vif.bss_conf.enable_beacon) { + set_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, + &sdata->state); + sdata->vif.bss_conf.enable_beacon = false; ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); + } if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { netif_tx_stop_all_queues(sdata->dev); @@ -183,11 +185,12 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, netif_tx_wake_all_queues(sdata->dev); } - if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_ADHOC || - sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, + &sdata->state)) { + sdata->vif.bss_conf.enable_beacon = true; ieee80211_bss_info_change_notify( sdata, BSS_CHANGED_BEACON_ENABLED); + } } mutex_unlock(&local->iflist_mtx); } -- cgit v1.2.3 From 8da349329ae52462253052f804ef30d086c9911a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:17:26 +0100 Subject: mac80211: reconfig bss_info_changed only if beaconing For AP/IBSS/mesh interfaces, call the driver to reconfigure bss_info_changed only if the interface was beaconing before suspend, otherwise we call the driver and it might interpret the change as going from enabled to disabled. Signed-off-by: Johannes Berg --- net/mac80211/util.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2e2e13bf96b2..e27c89c27bf4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1555,9 +1555,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* fall through */ case NL80211_IFTYPE_MESH_POINT: - changed |= BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED; - ieee80211_bss_info_change_notify(sdata, changed); + if (sdata->vif.bss_conf.enable_beacon) { + changed |= BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED; + ieee80211_bss_info_change_notify(sdata, changed); + } break; case NL80211_IFTYPE_WDS: break; -- cgit v1.2.3 From b8dc1a35c88dddcf62ce7bc59a7ed4c38c2f7597 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:22:10 +0100 Subject: mac80211: further simplify ieee80211_bss_info_change_notify The special case in the function isn't really needed, instead make the suspend code a bit better and also easier to understand and move the warning into the driver op wrapper inline. Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 6 ++++++ net/mac80211/main.c | 14 -------------- net/mac80211/pm.c | 15 ++++++++++----- 3 files changed, 16 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 608ced41548d..cccc486ae3d0 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -207,6 +207,12 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { might_sleep(); + WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED) && + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT); + check_sdata_in_driver(sdata); trace_drv_bss_info_changed(local, sdata, info, changed); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 71a64f49fe61..b719a16eabde 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -231,20 +231,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, return; } - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - break; - default: - /* do not warn to simplify caller in scan.c */ - changed &= ~BSS_CHANGED_BEACON_ENABLED; - if (WARN_ON(changed & BSS_CHANGED_BEACON)) - return; - break; - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 712c17ff6e22..8a67d7e982f7 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,7 +121,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { - u32 changed = BSS_CHANGED_BEACON_ENABLED; + u32 changed = 0; if (!ieee80211_sdata_running(sdata)) continue; @@ -136,14 +136,19 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) changed = BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID | BSS_CHANGED_IDLE; - else - changed = 0; - /* fall through */ + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + if (sdata->vif.bss_conf.enable_beacon) + changed = BSS_CHANGED_BEACON_ENABLED; + break; default: - ieee80211_quiesce(sdata); break; } + ieee80211_quiesce(sdata); + sdata->suspend_bss_conf = sdata->vif.bss_conf; memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); sdata->vif.bss_conf.idle = true; -- cgit v1.2.3 From ad2d223aa900179031feb40273881e212941573d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:34:25 +0100 Subject: mac80211: assign bss_conf.bssid only once Instead of checking every time bss_info_changed is called, assign the pointer once depending on the interface type and then leave it untouched until the interface type is changed. This makes the ieee80211_bss_info_change_notify() now a simple wrapper to call the driver only. Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 2 ++ net/mac80211/iface.c | 7 +++++++ net/mac80211/main.c | 20 -------------------- net/mac80211/mesh.c | 3 +++ net/mac80211/pm.c | 3 +++ 5 files changed, 15 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index cccc486ae3d0..0c07f94c5378 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -212,6 +212,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC && sdata->vif.type != NL80211_IFTYPE_MESH_POINT); + WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE && + changed & ~BSS_CHANGED_IDLE); check_sdata_in_driver(sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1ab7d8e57d39..06fac2991d40 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1227,6 +1227,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: skb_queue_head_init(&sdata->u.ap.ps.bc_buf); INIT_LIST_HEAD(&sdata->u.ap.vlans); + sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_P2P_CLIENT: type = NL80211_IFTYPE_STATION; @@ -1234,9 +1235,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.p2p = true; /* fall through */ case NL80211_IFTYPE_STATION: + sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; ieee80211_sta_setup_sdata(sdata); break; case NL80211_IFTYPE_ADHOC: + sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; ieee80211_ibss_setup_sdata(sdata); break; case NL80211_IFTYPE_MESH_POINT: @@ -1250,8 +1253,12 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, MONITOR_FLAG_OTHER_BSS; break; case NL80211_IFTYPE_WDS: + sdata->vif.bss_conf.bssid = NULL; + break; case NL80211_IFTYPE_AP_VLAN: + break; case NL80211_IFTYPE_P2P_DEVICE: + sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b719a16eabde..e6514f240fce 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -207,30 +207,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u32 changed) { struct ieee80211_local *local = sdata->local; - static const u8 zero[ETH_ALEN] = { 0 }; if (!changed) return; - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; - } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; - else if (sdata->vif.type == NL80211_IFTYPE_AP) - sdata->vif.bss_conf.bssid = sdata->vif.addr; - else if (sdata->vif.type == NL80211_IFTYPE_WDS) - sdata->vif.bss_conf.bssid = NULL; - else if (ieee80211_vif_is_mesh(&sdata->vif)) { - sdata->vif.bss_conf.bssid = zero; - } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { - sdata->vif.bss_conf.bssid = sdata->vif.addr; - WARN_ONCE(changed & ~(BSS_CHANGED_IDLE), - "P2P Device BSS changed %#x", changed); - } else { - WARN_ON(1); - return; - } - drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index fcfa7ef9af98..245885841c8d 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -810,6 +810,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + static u8 zero_addr[ETH_ALEN] = {}; setup_timer(&ifmsh->housekeeping_timer, ieee80211_mesh_housekeeping_timer, @@ -835,4 +836,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) INIT_LIST_HEAD(&ifmsh->preq_queue.list); spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); + + sdata->vif.bss_conf.bssid = zero_addr; } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 8a67d7e982f7..f81fd30581d9 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -121,6 +121,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* remove all interfaces */ list_for_each_entry(sdata, &local->interfaces, list) { + static u8 zero_addr[ETH_ALEN] = {}; u32 changed = 0; if (!ieee80211_sdata_running(sdata)) @@ -152,6 +153,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) sdata->suspend_bss_conf = sdata->vif.bss_conf; memset(&sdata->vif.bss_conf, 0, sizeof(sdata->vif.bss_conf)); sdata->vif.bss_conf.idle = true; + if (sdata->suspend_bss_conf.bssid) + sdata->vif.bss_conf.bssid = zero_addr; /* disable beaconing or remove association */ ieee80211_bss_info_change_notify(sdata, changed); -- cgit v1.2.3 From 3ebfa6e76beab01e644e1facd5fdb84d4274043e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:02 -0800 Subject: cfg80211: do not process beacon hints if one is already queued Regulatory beacon hints are used to help with world roaming and as it is right now we learn from a beacon hint processed on one wiphy to all other wiphys. The processing of beacon hints however is scheduled and if we have a lot of interfaces we may hit the case that we'll queue a the same beacon hint many times until its processed. To avoid this do a lookup on the queued up beacon hints prior to adding a new beacon hint. If the beacon hint is removed from the pending reg beacon hint list then it would be processed and we'd ensure all wiphys would have learned from it, if its on the pending reg beacon list we'd now find it prior to it being processed. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index fd53d975c0bc..2a3ae4d1001d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1925,11 +1925,23 @@ static bool freq_is_chan_12_13_14(u16 freq) return false; } +static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) +{ + struct reg_beacon *pending_beacon; + + list_for_each_entry(pending_beacon, ®_pending_beacons, list) + if (beacon_chan->center_freq == + pending_beacon->chan.center_freq) + return true; + return false; +} + int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp) { struct reg_beacon *reg_beacon; + bool processing; if (beacon_chan->beacon_found || beacon_chan->flags & IEEE80211_CHAN_RADAR || @@ -1937,6 +1949,13 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, !freq_is_chan_12_13_14(beacon_chan->center_freq))) return 0; + spin_lock_bh(®_pending_beacons_lock); + processing = pending_reg_beacon(beacon_chan); + spin_unlock_bh(®_pending_beacons_lock); + + if (processing) + return 0; + reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; -- cgit v1.2.3 From 3195e489a878aaa3b735d4a1b7c5c79b3786efed Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:03 -0800 Subject: cfg80211: move reg_is_world_roaming() This will be used later by other code. This has no functional change. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/reg.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2a3ae4d1001d..b68eeef45e6c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1009,6 +1009,22 @@ static bool ignore_reg_update(struct wiphy *wiphy, return false; } +static bool reg_is_world_roaming(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); + const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); + struct regulatory_request *lr = get_last_request(); + + if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) + return true; + + if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + return true; + + return false; +} + static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { @@ -1085,22 +1101,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) } } -static bool reg_is_world_roaming(struct wiphy *wiphy) -{ - const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); - const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); - struct regulatory_request *lr = get_last_request(); - - if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) - return true; - - if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) - return true; - - return false; -} - /* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { -- cgit v1.2.3 From 0f500a5f6cadac888d5d3fce326452372d1a8343 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 19 Dec 2012 10:53:04 -0800 Subject: cfg80211: move world roaming check for beacon hints We should not add new beacon hints even if the wiphy is not world roaming. Without this we were always adding a beacon hint if not world roaming for every non world roaming wiphy interface. Tested-by: Ben Greear Reported-by: Ben Greear Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez [fix locking] Signed-off-by: Johannes Berg --- net/wireless/reg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b68eeef45e6c..0b35de001937 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1044,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, chan->beacon_found = true; + if (!reg_is_world_roaming(wiphy)) + return; + if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) return; @@ -1110,8 +1113,6 @@ static void reg_process_beacons(struct wiphy *wiphy) */ if (!last_request) return; - if (!reg_is_world_roaming(wiphy)) - return; wiphy_update_beacon_reg(wiphy); } @@ -1593,11 +1594,8 @@ static void reg_process_pending_beacon_hints(void) struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; - /* - * No need to hold the reg_mutex here as we just touch wiphys - * and do not read or access regulatory variables. - */ mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); @@ -1615,6 +1613,7 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); + mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); } -- cgit v1.2.3 From b08fbbd8ade1fe3eea821a65b98d9f6a828329da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 7 Dec 2012 13:06:48 +0100 Subject: mac80211: restrict assoc request VHT capabilities In interoperability testing some APs showed bad behaviour if some of the VHT capabilities of the station are better than their own. Restrict the assoc request parameters - beamformee capabable, - RX STBC and - RX MCS set to the subset that the AP can support. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 51 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c084c1503c04..7182907e282a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data { u8 ap_ht_param; + struct ieee80211_vht_cap ap_vht_cap; + size_t ie_len; u8 ie[]; }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7d253a022173..6ffbf701de95 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_supported_band *sband) + struct ieee80211_supported_band *sband, + struct ieee80211_vht_cap *ap_vht_cap) { u8 *pos; u32 cap; struct ieee80211_sta_vht_cap vht_cap; + int i; BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); @@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; } + /* + * Some APs apparently get confused if our capabilities are better + * than theirs, so restrict what we advertise in the assoc request. + */ + if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) + cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + + if (!(ap_vht_cap->vht_cap_info & + cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC))) + cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4); + + for (i = 0; i < 8; i++) { + int shift = i * 2; + u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift; + u16 ap_mcs, our_mcs; + + ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) & + mask) >> shift; + our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) & + mask) >> shift; + + switch (ap_mcs) { + default: + if (our_mcs <= ap_mcs) + break; + /* fall through */ + case IEEE80211_VHT_MCS_NOT_SUPPORTED: + vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask); + vht_cap.vht_mcs.rx_mcs_map |= + cpu_to_le16(ap_mcs << shift); + } + } + /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) sband, chan, sdata->smps_mode); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) - ieee80211_add_vht_ie(sdata, skb, sband); + ieee80211_add_vht_ie(sdata, skb, sband, + &assoc_data->ap_vht_cap); /* if present, add any custom non-vendor IEs that go after HT */ if (assoc_data->ie_len && assoc_data->ie) { @@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_mgd_assoc_data *assoc_data; struct ieee80211_supported_band *sband; - const u8 *ssidie, *ht_ie; + const u8 *ssidie, *ht_ie, *vht_ie; int i, err; assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); @@ -3872,6 +3911,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; else ifmgd->flags |= IEEE80211_STA_DISABLE_HT; + vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY); + if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap)) + memcpy(&assoc_data->ap_vht_cap, vht_ie + 2, + sizeof(struct ieee80211_vht_cap)); + else + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; rcu_read_unlock(); if (bss->wmm_used && bss->uapsd_supported && -- cgit v1.2.3 From dfa674da1807bc8fb782fc38a7d48c8ea3168eb7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 20 Dec 2012 18:23:01 +0100 Subject: cfg80211: move some AP code to right file Some AP code ended up in mlme.c as ap.c didn't exist when it was written, move it now. Signed-off-by: Johannes Berg --- net/wireless/ap.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/mlme.c | 62 ----------------------------------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) (limited to 'net') diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 324e8d851dc4..a4a14e8f55cc 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -46,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return err; } + +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + trace_cfg80211_ch_switch_notify(dev, chandef); + + wdev_lock(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + goto out; + + wdev->channel = chandef->chan; + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); +out: + wdev_unlock(wdev); + return; +} +EXPORT_SYMBOL(cfg80211_ch_switch_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_spurious_frame(dev, addr); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) { + trace_cfg80211_return_bool(false); + return false; + } + ret = nl80211_unexpected_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; + + trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { + trace_cfg80211_return_bool(false); + return false; + } + ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); + trace_cfg80211_return_bool(ret); + return ret; +} +EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5e8123ee63fd..461e692cdfec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -987,65 +987,3 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); - -void cfg80211_ch_switch_notify(struct net_device *dev, - struct cfg80211_chan_def *chandef) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - trace_cfg80211_ch_switch_notify(dev, chandef); - - wdev_lock(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) - goto out; - - wdev->channel = chandef->chan; - nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); -out: - wdev_unlock(wdev); - return; -} -EXPORT_SYMBOL(cfg80211_ch_switch_notify); - -bool cfg80211_rx_spurious_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - bool ret; - - trace_cfg80211_rx_spurious_frame(dev, addr); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) { - trace_cfg80211_return_bool(false); - return false; - } - ret = nl80211_unexpected_frame(dev, addr, gfp); - trace_cfg80211_return_bool(ret); - return ret; -} -EXPORT_SYMBOL(cfg80211_rx_spurious_frame); - -bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - bool ret; - - trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { - trace_cfg80211_return_bool(false); - return false; - } - ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); - trace_cfg80211_return_bool(ret); - return ret; -} -EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); -- cgit v1.2.3 From d582cffbcd04eae0bd8a83b05648bfd54bfd21c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 26 Oct 2012 17:53:44 +0200 Subject: nl80211/mac80211: support full station state in AP mode Today, stations are added already associated. That is inefficient if, for example, the driver has no room for stations any more because then the station will go through the entire auth/assoc handshake, only to be kicked out afterwards. To address this a bit better, at least with drivers using the new station state callback, allow hostapd to add stations in unauthenticated mode, just after receiving the AUTH frame, before even replying. Thus if there's no more space at that point, it can send a negative auth frame back. It still needs to handle later state transition errors though, of course. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 16 ++++++ net/mac80211/cfg.c | 115 ++++++++++++++++++++++++++----------------- net/mac80211/main.c | 3 +- net/wireless/nl80211.c | 24 +++++++++ 4 files changed, 113 insertions(+), 45 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e3e19f8b16f2..547017100a30 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1697,6 +1697,9 @@ enum nl80211_iftype { * flag can't be changed, it is only valid while adding a station, and * attempts to change it will silently be ignored (rather than rejected * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1708,6 +1711,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -3140,6 +3144,17 @@ enum nl80211_ap_sme_features { * setting * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3155,6 +3170,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3e7d557fd481..f4d12c71928d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -510,6 +510,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_TDLS_PEER); if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); @@ -521,6 +522,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); if (test_sta_flag(sta, WLAN_STA_AUTH)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_ASSOC)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } @@ -1077,6 +1080,58 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) netif_rx_ni(skb); } +static int sta_apply_auth_flags(struct ieee80211_local *local, + struct sta_info *sta, + u32 mask, u32 set) +{ + int ret; + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && + set & BIT(NL80211_STA_FLAG_ASSOCIATED) && + !test_sta_flag(sta, WLAN_STA_ASSOC)) { + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + else + ret = 0; + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && + !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) && + test_sta_flag(sta, WLAN_STA_ASSOC)) { + ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); + if (ret) + return ret; + } + + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && + test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state(sta, IEEE80211_STA_NONE); + if (ret) + return ret; + } + + return 0; +} + static int sta_apply_parameters(struct ieee80211_local *local, struct sta_info *sta, struct station_parameters *params) @@ -1094,52 +1149,20 @@ static int sta_apply_parameters(struct ieee80211_local *local, mask = params->sta_flags_mask; set = params->sta_flags_set; - /* - * In mesh mode, we can clear AUTHENTICATED flag but must - * also make ASSOCIATED follow appropriately for the driver - * API. See also below, after AUTHORIZED changes. - */ - if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - /* cfg80211 should not allow this in non-mesh modes */ - if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) - return -EINVAL; - - if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && - !test_sta_flag(sta, WLAN_STA_AUTH)) { - ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); - if (ret) - return ret; - ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); - if (ret) - return ret; - } - } - - if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { - if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); - else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); - if (ret) - return ret; - } - - if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - /* cfg80211 should not allow this in non-mesh modes */ - if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) - return -EINVAL; - - if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && - test_sta_flag(sta, WLAN_STA_AUTH)) { - ret = sta_info_move_state(sta, IEEE80211_STA_AUTH); - if (ret) - return ret; - ret = sta_info_move_state(sta, IEEE80211_STA_NONE); - if (ret) - return ret; - } + if (ieee80211_vif_is_mesh(&sdata->vif)) { + /* + * In mesh mode, ASSOCIATED isn't part of the nl80211 + * API but must follow AUTHENTICATED for driver state. + */ + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) + set |= BIT(NL80211_STA_FLAG_ASSOCIATED); } + ret = sta_apply_auth_flags(local, sta, mask, set); + if (ret) + return ret; if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) @@ -1273,6 +1296,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (!sta) return -ENOMEM; + /* + * defaults -- if userspace wants something else we'll + * change it accordingly in sta_apply_parameters() + */ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e6514f240fce..39cfe8f10ad2 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -541,7 +541,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | - NL80211_FEATURE_VIF_TXPOWER; + NL80211_FEATURE_VIF_TXPOWER | + NL80211_FEATURE_FULL_AP_CLIENT_STATE; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b3cf7cc0d4a1..087f68ba6d7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3231,11 +3231,21 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* accept only the listed bits */ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP))) return -EINVAL; + /* but authenticated/associated only if driver handles it */ + if (!(rdev->wiphy.features & + NL80211_FEATURE_FULL_AP_CLIENT_STATE) && + params.sta_flags_mask & + (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED))) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) @@ -3393,17 +3403,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* but don't bother the driver with it */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); + /* allow authenticated/associated only if driver handles it */ + if (!(rdev->wiphy.features & + NL80211_FEATURE_FULL_AP_CLIENT_STATE) && + params.sta_flags_mask & + (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED))) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); break; case NL80211_IFTYPE_MESH_POINT: + /* associated is disallowed */ + if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) + return -EINVAL; /* TDLS peers cannot be added */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) return -EINVAL; break; case NL80211_IFTYPE_STATION: + /* associated is disallowed */ + if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) + return -EINVAL; /* Only TDLS peers can be added */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) return -EINVAL; -- cgit v1.2.3 From c82c4a80bbb9c8ffa1d783070a8caa37f2db45b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 13:31:31 +0200 Subject: mac80211: split aggregation stop by reason The initiator/tx doesn't really identify why an aggregation session is stopped, give a reason for stopping that more clearly identifies what's going on. This will help tell the driver clearly what is expected of it. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 17 ++++++++--------- net/mac80211/ht.c | 15 ++++++++------- net/mac80211/ieee80211_i.h | 9 ++++----- net/mac80211/mlme.c | 2 +- net/mac80211/pm.c | 3 ++- net/mac80211/sta_info.c | 2 +- net/mac80211/sta_info.h | 7 +++++++ net/mac80211/util.c | 3 ++- 8 files changed, 33 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index eb9df22418f0..e0656d77b313 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -150,8 +150,7 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, } int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx) + enum ieee80211_agg_stop_reason reason) { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; @@ -212,8 +211,10 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ synchronize_net(); - tid_tx->stop_initiator = initiator; - tid_tx->tx_stop = tx; + tid_tx->stop_initiator = reason == AGG_STOP_PEER_REQUEST ? + WLAN_BACK_RECIPIENT : + WLAN_BACK_INITIATOR; + tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_STOP, @@ -660,14 +661,13 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx) + enum ieee80211_agg_stop_reason reason) { int ret; mutex_lock(&sta->ampdu_mlme.mtx); - ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator, tx); + ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason); mutex_unlock(&sta->ampdu_mlme.mtx); @@ -868,8 +868,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, } } else { - ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR, - false); + ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); } out: diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 8a6d68f1f404..df46cd8e55f5 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -182,16 +182,19 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, ieee80211_apply_htcap_overrides(sdata, ht_cap); } -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, + enum ieee80211_agg_stop_reason reason) { int i; cancel_work_sync(&sta->ampdu_mlme.work); for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); + __ieee80211_stop_tx_ba_session(sta, i, reason); __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, - WLAN_REASON_QSTA_LEAVE_QBSS, tx); + WLAN_REASON_QSTA_LEAVE_QBSS, + reason != AGG_STOP_DESTROY_STA && + reason != AGG_STOP_PEER_REQUEST); } } @@ -248,8 +251,7 @@ void ieee80211_ba_session_work(struct work_struct *work) if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) ___ieee80211_stop_tx_ba_session(sta, tid, - WLAN_BACK_INITIATOR, - true); + AGG_STOP_LOCAL_REQUEST); } mutex_unlock(&sta->ampdu_mlme.mtx); } @@ -317,8 +319,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, true); else - __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, - true); + __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST); } int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7182907e282a..0fa44a965ad9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1432,7 +1432,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); -void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx); +void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, + enum ieee80211_agg_stop_reason reason); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_mgmt *mgmt, size_t len); @@ -1446,11 +1447,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, size_t len); int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx); + enum ieee80211_agg_stop_reason reason); int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_back_parties initiator, - bool tx); + enum ieee80211_agg_stop_reason reason); void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid); void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); void ieee80211_ba_session_work(struct work_struct *work); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6ffbf701de95..d90c07b1795f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1525,7 +1525,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sta = sta_info_get(sdata, ifmgd->bssid); if (sta) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, false); + ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); } mutex_unlock(&local->sta_mtx); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index f81fd30581d9..e45b83610e85 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -42,7 +42,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, true); + ieee80211_sta_tear_down_BA_sessions( + sta, AGG_STOP_LOCAL_REQUEST); } mutex_unlock(&local->sta_mtx); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 738f9349c0a2..9d864ed5f3da 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -784,7 +784,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) * will be sufficient. */ set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, false); + ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); ret = sta_info_hash_del(local, sta); if (ret) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 031e4a5bbeca..af7d78aa5523 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -92,6 +92,13 @@ enum ieee80211_sta_info_flags { #define HT_AGG_STATE_WANT_START 4 #define HT_AGG_STATE_WANT_STOP 5 +enum ieee80211_agg_stop_reason { + AGG_STOP_DECLINED, + AGG_STOP_LOCAL_REQUEST, + AGG_STOP_PEER_REQUEST, + AGG_STOP_DESTROY_STA, +}; + /** * struct tid_ampdu_tx - TID aggregation information (Tx). * diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e27c89c27bf4..1b9420730d8c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1639,7 +1639,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { - ieee80211_sta_tear_down_BA_sessions(sta, true); + ieee80211_sta_tear_down_BA_sessions( + sta, AGG_STOP_LOCAL_REQUEST); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } -- cgit v1.2.3 From faec12ee2dd92edc09f75aab3d8c5085102052c5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 14:12:44 +0200 Subject: mac80211: split out aggregation TX removal Create the function ieee80211_remove_tid_tx to call it from ___ieee80211_stop_tx_ba_session later. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e0656d77b313..101bbfba9c8d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -713,6 +713,35 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); +static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) +{ + struct tid_ampdu_tx *tid_tx; + + lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_held(&sta->lock); + + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + + /* + * When we get here, the TX path will not be lockless any more wrt. + * aggregation, since the OPERATIONAL bit has long been cleared. + * Thus it will block on getting the lock, if it occurs. So if we + * stop the queue now, we will not get any more packets, and any + * that might be being processed will wait for us here, thereby + * guaranteeing that no packets go to the tid_tx pending queue any + * more. + */ + + ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); + + /* future packets must not find the tid_tx struct any more */ + ieee80211_assign_tid_tx(sta, tid, NULL); + + ieee80211_agg_splice_finish(sta->sdata, tid); + + kfree_rcu(tid_tx, rcu_head); +} + void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -751,24 +780,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); - /* - * When we get here, the TX path will not be lockless any more wrt. - * aggregation, since the OPERATIONAL bit has long been cleared. - * Thus it will block on getting the lock, if it occurs. So if we - * stop the queue now, we will not get any more packets, and any - * that might be being processed will wait for us here, thereby - * guaranteeing that no packets go to the tid_tx pending queue any - * more. - */ - - ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); - - /* future packets must not find the tid_tx struct any more */ - ieee80211_assign_tid_tx(sta, tid, NULL); - - ieee80211_agg_splice_finish(sta->sdata, tid); - - kfree_rcu(tid_tx, rcu_head); + ieee80211_remove_tid_tx(sta, tid); unlock_sta: spin_unlock_bh(&sta->lock); -- cgit v1.2.3 From 30bf5f1f433c7612857ed13c50525945c483dfe0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 09:43:03 +0100 Subject: mac80211: move ieee80211_remove_tid_tx function To call it from ___ieee80211_stop_tx_ba_session, move the function and dependencies up. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 206 +++++++++++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 103 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 101bbfba9c8d..dda8d7df4b54 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -149,6 +149,109 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } +static inline int ieee80211_ac_from_tid(int tid) +{ + return ieee802_1d_to_ac[tid & 7]; +} + +/* + * When multiple aggregation sessions on multiple stations + * are being created/destroyed simultaneously, we need to + * refcount the global queue stop caused by that in order + * to not get into a situation where one of the aggregation + * setup or teardown re-enables queues before the other is + * ready to handle that. + * + * These two functions take care of this issue by keeping + * a global "agg_queue_stop" refcount. + */ +static void __acquires(agg_queue) +ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + + if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) + ieee80211_stop_queue_by_reason( + &sdata->local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + __acquire(agg_queue); +} + +static void __releases(agg_queue) +ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) +{ + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + + if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) + ieee80211_wake_queue_by_reason( + &sdata->local->hw, queue, + IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + __release(agg_queue); +} + +/* + * splice packets from the STA's pending to the local pending, + * requires a call to ieee80211_agg_splice_finish later + */ +static void __acquires(agg_queue) +ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, + struct tid_ampdu_tx *tid_tx, u16 tid) +{ + struct ieee80211_local *local = sdata->local; + int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; + unsigned long flags; + + ieee80211_stop_queue_agg(sdata, tid); + + if (WARN(!tid_tx, + "TID %d gone but expected when splicing aggregates from the pending queue\n", + tid)) + return; + + if (!skb_queue_empty(&tid_tx->pending)) { + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + /* copy over remaining packets */ + skb_queue_splice_tail_init(&tid_tx->pending, + &local->pending[queue]); + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + } +} + +static void __releases(agg_queue) +ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) +{ + ieee80211_wake_queue_agg(sdata, tid); +} + +static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) +{ + struct tid_ampdu_tx *tid_tx; + + lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_held(&sta->lock); + + tid_tx = rcu_dereference_protected_tid_tx(sta, tid); + + /* + * When we get here, the TX path will not be lockless any more wrt. + * aggregation, since the OPERATIONAL bit has long been cleared. + * Thus it will block on getting the lock, if it occurs. So if we + * stop the queue now, we will not get any more packets, and any + * that might be being processed will wait for us here, thereby + * guaranteeing that no packets go to the tid_tx pending queue any + * more. + */ + + ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); + + /* future packets must not find the tid_tx struct any more */ + ieee80211_assign_tid_tx(sta, tid, NULL); + + ieee80211_agg_splice_finish(sta->sdata, tid); + + kfree_rcu(tid_tx, rcu_head); +} + int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_agg_stop_reason reason) { @@ -265,80 +368,6 @@ static void sta_addba_resp_timer_expired(unsigned long data) rcu_read_unlock(); } -static inline int ieee80211_ac_from_tid(int tid) -{ - return ieee802_1d_to_ac[tid & 7]; -} - -/* - * When multiple aggregation sessions on multiple stations - * are being created/destroyed simultaneously, we need to - * refcount the global queue stop caused by that in order - * to not get into a situation where one of the aggregation - * setup or teardown re-enables queues before the other is - * ready to handle that. - * - * These two functions take care of this issue by keeping - * a global "agg_queue_stop" refcount. - */ -static void __acquires(agg_queue) -ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) -{ - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - - if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1) - ieee80211_stop_queue_by_reason( - &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - __acquire(agg_queue); -} - -static void __releases(agg_queue) -ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) -{ - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - - if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0) - ieee80211_wake_queue_by_reason( - &sdata->local->hw, queue, - IEEE80211_QUEUE_STOP_REASON_AGGREGATION); - __release(agg_queue); -} - -/* - * splice packets from the STA's pending to the local pending, - * requires a call to ieee80211_agg_splice_finish later - */ -static void __acquires(agg_queue) -ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, - struct tid_ampdu_tx *tid_tx, u16 tid) -{ - struct ieee80211_local *local = sdata->local; - int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)]; - unsigned long flags; - - ieee80211_stop_queue_agg(sdata, tid); - - if (WARN(!tid_tx, - "TID %d gone but expected when splicing aggregates from the pending queue\n", - tid)) - return; - - if (!skb_queue_empty(&tid_tx->pending)) { - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - /* copy over remaining packets */ - skb_queue_splice_tail_init(&tid_tx->pending, - &local->pending[queue]); - spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - } -} - -static void __releases(agg_queue) -ieee80211_agg_splice_finish(struct ieee80211_sub_if_data *sdata, u16 tid) -{ - ieee80211_wake_queue_agg(sdata, tid); -} - void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) { struct tid_ampdu_tx *tid_tx; @@ -713,35 +742,6 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); -static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) -{ - struct tid_ampdu_tx *tid_tx; - - lockdep_assert_held(&sta->ampdu_mlme.mtx); - lockdep_assert_held(&sta->lock); - - tid_tx = rcu_dereference_protected_tid_tx(sta, tid); - - /* - * When we get here, the TX path will not be lockless any more wrt. - * aggregation, since the OPERATIONAL bit has long been cleared. - * Thus it will block on getting the lock, if it occurs. So if we - * stop the queue now, we will not get any more packets, and any - * that might be being processed will wait for us here, thereby - * guaranteeing that no packets go to the tid_tx pending queue any - * more. - */ - - ieee80211_agg_splice_packets(sta->sdata, tid_tx, tid); - - /* future packets must not find the tid_tx struct any more */ - ieee80211_assign_tid_tx(sta, tid, NULL); - - ieee80211_agg_splice_finish(sta->sdata, tid); - - kfree_rcu(tid_tx, rcu_head); -} - void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); -- cgit v1.2.3 From 18b559d5db47c86b10c14590aa2d26c0243c39e4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jul 2012 13:51:25 +0200 Subject: mac80211: split TX aggregation stop action When TX aggregation is stopped, there are a few different cases: - connection with the peer was dropped - session stop was requested locally - session stop was requested by the peer - connection was dropped while a session is stopping The behaviour in these cases should be different, if the connection is dropped then the driver should drop all frames, otherwise the frames may continue to be transmitted, aggregated in the case of a locally requested session stop or unaggregated in the case of the peer requesting session stop. Split these different cases so that the driver can act accordingly; however, treat local and remote stop the same way and ask the driver to not send frames as aggregated packets any more. In the case of connection drop, the stop callback the driver is otherwise supposed to call is no longer required. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 4 ++- drivers/net/wireless/ath/ath9k/main.c | 4 ++- drivers/net/wireless/ath/carl9170/main.c | 4 ++- .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 4 ++- drivers/net/wireless/iwlegacy/4965-mac.c | 4 ++- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 4 ++- drivers/net/wireless/mac80211_hwsim.c | 4 ++- drivers/net/wireless/mwl8k.c | 4 ++- drivers/net/wireless/rt2x00/rt2800lib.c | 4 ++- drivers/net/wireless/rtlwifi/core.c | 4 ++- drivers/net/wireless/ti/wlcore/main.c | 4 ++- include/net/mac80211.h | 22 +++++++++--- net/mac80211/agg-tx.c | 40 +++++++++++++++++++--- 13 files changed, 85 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 9c07a8fa5134..a8016d70088a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, if (!ret) ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index be30a9af1528..e1fa70596e61 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ath9k_ps_restore(sc); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ath9k_ps_wakeup(sc); ath_tx_aggr_stop(sc, sta, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 25a1e2f4f738..9d2051aeb782 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: rcu_read_lock(); tid_info = rcu_dereference(sta_info->agg[tid]); if (tid_info) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 1fbd8ecbe2ea..f0fc8cd4d5df 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: spin_lock_bh(&wl->lock); brcms_c_ampdu_flush(wl->wlc, sta, tid); spin_unlock_bh(&wl->lock); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index c3fbf6717564..6a86ed45835d 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, D_HT("start Tx\n"); ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: D_HT("stop Tx\n"); ret = il4965_tx_agg_stop(il, vif, sta, tid); if (test_bit(S_EXIT_PENDING, &il->status)) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 3163e0f38c25..02fdcea76b21 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, IWL_DEBUG_HT(priv, "start Tx\n"); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); if ((ret == 0) && (priv->agg_tids_count > 0)) { diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 145498f8411f..d248a4cc6656 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1312,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b3..19b46fdf9f0f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: if (stream) { if (stream->state == AMPDU_STREAM_ACTIVE) { spin_unlock(&priv->stream_lock); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 197b4466a5d2..12f93e42e160 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index be33aa14c8af..d3ce9fbef00e 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, "IEEE80211_AMPDU_TX_START: TID:%d\n", tid); return rtl_tx_agg_start(hw, sta, tid, ssn); break; - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid); return rtl_tx_agg_stop(hw, sta, tid); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ea9d8e011bc9..d7de06359ae1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, * Falling break here on purpose for all TX APDU commands. */ case IEEE80211_AMPDU_TX_START: - case IEEE80211_AMPDU_TX_STOP: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: ret = -EINVAL; break; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0978b0faa880..a464f4fb36a0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags { * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer * might receive the addBA frame and send a delBA right away! * - * @IEEE80211_AMPDU_RX_START: start Rx aggregation - * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation - * @IEEE80211_AMPDU_TX_START: start Tx aggregation - * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation + * @IEEE80211_AMPDU_RX_START: start RX aggregation + * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation + * @IEEE80211_AMPDU_TX_START: start TX aggregation * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational + * @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting + * queued packets, now unaggregated. After all packets are transmitted the + * driver has to call ieee80211_stop_tx_ba_cb_irqsafe(). + * @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets, + * called when the station is removed. There's no need or reason to call + * ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the + * session is gone and removes the station. + * @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped + * but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and + * now the connection is dropped and the station will be removed. Drivers + * should clean up and drop remaining packets when this is called. */ enum ieee80211_ampdu_mlme_action { IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_TX_START, - IEEE80211_AMPDU_TX_STOP, + IEEE80211_AMPDU_TX_STOP_CONT, + IEEE80211_AMPDU_TX_STOP_FLUSH, + IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, IEEE80211_AMPDU_TX_OPERATIONAL, }; diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index dda8d7df4b54..2f0ccbc5f13e 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; + enum ieee80211_ampdu_mlme_action action; int ret; lockdep_assert_held(&sta->ampdu_mlme.mtx); + switch (reason) { + case AGG_STOP_DECLINED: + case AGG_STOP_LOCAL_REQUEST: + case AGG_STOP_PEER_REQUEST: + action = IEEE80211_AMPDU_TX_STOP_CONT; + break; + case AGG_STOP_DESTROY_STA: + action = IEEE80211_AMPDU_TX_STOP_FLUSH; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + spin_lock_bh(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); @@ -269,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, return -ENOENT; } - /* if we're already stopping ignore any new requests to stop */ + /* + * if we're already stopping ignore any new requests to stop + * unless we're destroying it in which case notify the driver + */ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { spin_unlock_bh(&sta->lock); - return -EALREADY; + if (reason != AGG_STOP_DESTROY_STA) + return -EALREADY; + ret = drv_ampdu_action(local, sta->sdata, + IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, + &sta->sta, tid, NULL, 0); + WARN_ON_ONCE(ret); + goto remove_tid_tx; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { @@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, WLAN_BACK_INITIATOR; tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; - ret = drv_ampdu_action(local, sta->sdata, - IEEE80211_AMPDU_TX_STOP, + ret = drv_ampdu_action(local, sta->sdata, action, &sta->sta, tid, NULL, 0); /* HW shall not deny going back to legacy */ @@ -331,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ } - return ret; + if (reason == AGG_STOP_DESTROY_STA) { + remove_tid_tx: + spin_lock_bh(&sta->lock); + ieee80211_remove_tid_tx(sta, tid); + spin_unlock_bh(&sta->lock); + } + + return 0; } /* -- cgit v1.2.3 From ec61cd63dd3f3bf982180b2bcc1b325160d73837 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 12:12:10 +0100 Subject: mac80211: support HT notify channel width action Support the HT notify channel width action frame to update the rate scaling about the bandwidth the peer can receive in. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 9 +++++++++ net/mac80211/rx.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 5db76ebe8810..ccf9ee1dca8c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -701,6 +701,11 @@ enum ieee80211_rann_flags { RANN_FLAG_IS_GATE = 1 << 0, }; +enum ieee80211_ht_chanwidth_values { + IEEE80211_HT_CHANWIDTH_20MHZ = 0, + IEEE80211_HT_CHANWIDTH_ANY = 1, +}; + #define WLAN_SA_QUERY_TR_ID_LEN 2 struct ieee80211_mgmt { @@ -821,6 +826,10 @@ struct ieee80211_mgmt { u8 action; u8 smps_control; } __packed ht_smps; + struct { + u8 action_code; + u8 chanwidth; + } __packed ht_notify_cw; struct { u8 action_code; u8 dialog_token; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 580704eba8b8..a19089565c4b 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2353,7 +2353,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) sdata->vif.type != NL80211_IFTYPE_ADHOC) break; - /* verify action & smps_control are present */ + /* verify action & smps_control/chanwidth are present */ if (len < IEEE80211_MIN_ACTION_SIZE + 2) goto invalid; @@ -2392,6 +2392,35 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) IEEE80211_RC_SMPS_CHANGED); goto handled; } + case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { + struct ieee80211_supported_band *sband; + u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; + bool old_40mhz, new_40mhz; + + /* If it doesn't support 40 MHz it can't change ... */ + if (!rx->sta->supports_40mhz) + goto handled; + + old_40mhz = rx->sta->sta.ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY; + + if (old_40mhz == new_40mhz) + goto handled; + + if (new_40mhz) + rx->sta->sta.ht_cap.cap |= + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + rx->sta->sta.ht_cap.cap &= + ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + sband = rx->local->hw.wiphy->bands[status->band]; + + rate_control_rate_update(local, sband, rx->sta, + IEEE80211_RC_BW_CHANGED); + goto handled; + } default: goto invalid; } -- cgit v1.2.3 From 1c06ef9831c78648f719a174e4598ae2600b0cf7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 12:22:02 +0100 Subject: wireless: use __aligned Use __aligned(...) instead of __attribute__((aligned(...))) in mac80211 and cfg80211. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++-- include/net/mac80211.h | 6 +++--- net/wireless/core.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1f74360b527c..e5f085c89221 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1256,7 +1256,7 @@ struct cfg80211_bss { u8 bssid[ETH_ALEN]; - u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 priv[0] __aligned(sizeof(void *)); }; /** @@ -2392,7 +2392,7 @@ struct wiphy { const struct iw_handler_def *wext; #endif - char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); + char priv[0] __aligned(NETDEV_ALIGN); }; static inline struct net *wiphy_net(struct wiphy *wiphy) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a464f4fb36a0..23daed3c78ed 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -173,7 +173,7 @@ struct ieee80211_chanctx_conf { u8 rx_chains_static, rx_chains_dynamic; - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; /** @@ -1059,7 +1059,7 @@ struct ieee80211_vif { u32 driver_flags; /* must be last */ - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) @@ -1209,7 +1209,7 @@ struct ieee80211_sta { u8 max_sp; /* must be last */ - u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *)))); + u8 drv_priv[0] __aligned(sizeof(void *)); }; /** diff --git a/net/wireless/core.h b/net/wireless/core.h index b8f4630c6ada..f342267e3620 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -89,7 +89,7 @@ struct cfg80211_registered_device { /* 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))); + struct wiphy wiphy __aligned(NETDEV_ALIGN); }; static inline -- cgit v1.2.3 From 21add825f90a50407e0121833c8b7cd2c071c478 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 28 Dec 2012 15:01:57 +0100 Subject: mac80211: unset peer's HT 40 MHz support if not usable When an interface is configured to a 20 MHz channel and the device as well as the peer are 40 MHz capable the HT capabilities of the peer are not restricted to 20 MHz, even though they're supposed to be restricted to the currently possible capabilities. Unset the 40 MHz HT capability bits in this case. Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net') diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index df46cd8e55f5..61ac7c48ac0c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -120,6 +120,21 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40)); + + /* Unset 40 MHz if we're not using a 40 MHz channel */ + switch (sdata->vif.bss_conf.chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40; + ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + break; + case NL80211_CHAN_WIDTH_40: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + break; + } + /* * The STBC bits are asymmetric -- if we don't have * TX then mask out the peer's RX and vice versa. -- cgit v1.2.3 From ba23d2068d85f6616ea5f92320c04e87d4b9e141 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Dec 2012 17:32:09 +0100 Subject: cfg80211: disallow more station changes The following changes are invalid and should be disallowed when a station already exists: * supported rates changes, except for TDLS peers * listen interval changes * HT capability changes Disallow them and also update a mac80211 comment explaining how they would be racy. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +++++---- net/wireless/nl80211.c | 22 ++++++++-------------- 2 files changed, 13 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f4d12c71928d..7d290bce0927 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1208,10 +1208,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.aid = params->aid; /* - * FIXME: updating the following information is racy when this - * function is called from ieee80211_change_station(). - * However, all this information should be static so - * maybe we should just reject attemps to change it. + * Some of the following updates would be racy if called on an + * existing station, via ieee80211_change_station(). However, + * all such changes are rejected by cfg80211 except for updates + * changing the supported rates on an existing but not yet used + * TDLS peer. */ if (params->listen_interval >= 0) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 087f68ba6d7a..9bd8340af999 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3188,13 +3188,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); } - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || + info->attrs[NL80211_ATTR_HT_CAPABILITY]) + return -EINVAL; if (!rdev->ops->change_station) return -EOPNOTSUPP; @@ -3246,6 +3242,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) BIT(NL80211_STA_FLAG_ASSOCIATED))) return -EINVAL; + /* reject other things that can't change */ + if (params.supported_rates) + return -EINVAL; + /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); if (IS_ERR(params.vlan)) @@ -3265,10 +3265,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; - if (params.ht_capa) - return -EINVAL; - if (params.listen_interval >= 0) - return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -3277,9 +3273,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things mesh doesn't support */ if (params.vlan) return -EINVAL; - if (params.ht_capa) - return -EINVAL; - if (params.listen_interval >= 0) + if (params.supported_rates) return -EINVAL; /* * No special handling for TDLS here -- the userspace -- cgit v1.2.3 From e7c3273ec2a04bbce27e0991c405fe6dc6811d7d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 1 Jan 2013 03:30:13 +0000 Subject: rtnl: use dev_set_mac_address() instead of plain ndo_ Benefit from existence of dev_set_mac_address() and remove duplicate code. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2ef7a56ba117..78d05a9dcea6 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1310,7 +1310,6 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, struct nlattr **tb, char *ifname, int modified) { const struct net_device_ops *ops = dev->netdev_ops; - int send_addr_notify = 0; int err; if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]) { @@ -1363,16 +1362,6 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, struct sockaddr *sa; int len; - if (!ops->ndo_set_mac_address) { - err = -EOPNOTSUPP; - goto errout; - } - - if (!netif_device_present(dev)) { - err = -ENODEV; - goto errout; - } - len = sizeof(sa_family_t) + dev->addr_len; sa = kmalloc(len, GFP_KERNEL); if (!sa) { @@ -1382,13 +1371,11 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, sa->sa_family = dev->type; memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]), dev->addr_len); - err = ops->ndo_set_mac_address(dev, sa); + err = dev_set_mac_address(dev, sa); kfree(sa); if (err) goto errout; - send_addr_notify = 1; modified = 1; - add_device_randomness(dev->dev_addr, dev->addr_len); } if (tb[IFLA_MTU]) { @@ -1425,7 +1412,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); - send_addr_notify = 1; + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); } if (ifm->ifi_flags || ifm->ifi_change) { @@ -1546,9 +1533,6 @@ errout: net_warn_ratelimited("A link change request failed with some changes committed already. Interface %s may have been left with an inconsistent configuration, please check.\n", dev->name); - if (send_addr_notify) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - return err; } -- cgit v1.2.3 From f652151640344f8faee21fcf702b4fd46b8fe70b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 1 Jan 2013 03:30:14 +0000 Subject: net: call add_device_randomness() only after successful mac change Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/dev.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 21c5b976dcf3..c85e32b30f04 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5020,10 +5020,11 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) if (!netif_device_present(dev)) return -ENODEV; err = ops->ndo_set_mac_address(dev, sa); - if (!err) - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + if (err) + return err; + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); add_device_randomness(dev->dev_addr, dev->addr_len); - return err; + return 0; } EXPORT_SYMBOL(dev_set_mac_address); -- cgit v1.2.3 From fbdeca2d7753aa1ab929aeb77ccc46489eed02b9 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 1 Jan 2013 03:30:16 +0000 Subject: net: add address assign type "SET" This is the way to indicate that mac address of a device has been set by dev_set_mac_address() Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 1 + 2 files changed, 3 insertions(+) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6835b5837f93..c5031a45e185 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -64,6 +64,8 @@ struct wireless_dev; #define NET_ADDR_PERM 0 /* address is permanent (default) */ #define NET_ADDR_RANDOM 1 /* address is generated randomly */ #define NET_ADDR_STOLEN 2 /* address is stolen from other device */ +#define NET_ADDR_SET 3 /* address is set using + * dev_set_mac_address() */ /* Backlog congestion levels */ #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ diff --git a/net/core/dev.c b/net/core/dev.c index c85e32b30f04..bddb2f2ccaa9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5022,6 +5022,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa) err = ops->ndo_set_mac_address(dev, sa); if (err) return err; + dev->addr_assign_type = NET_ADDR_SET; call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); add_device_randomness(dev->dev_addr, dev->addr_len); return 0; -- cgit v1.2.3 From 15c6ff3bc0ff3464a8c7efcdea09c86454571622 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 1 Jan 2013 03:30:17 +0000 Subject: net: remove unnecessary NET_ADDR_RANDOM "bitclean" NET_ADDR_SET is set in dev_set_mac_address() no need to alter dev->addr_assign_type value in drivers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 1 - drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 1 - drivers/net/ethernet/atheros/atlx/atlx.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 1 - drivers/net/ethernet/calxeda/xgmac.c | 1 - drivers/net/ethernet/cisco/enic/enic_main.c | 1 - drivers/net/ethernet/ethoc.c | 1 - drivers/net/ethernet/intel/igbvf/netdev.c | 1 - drivers/net/ethernet/marvell/pxa168_eth.c | 1 - drivers/net/ethernet/micrel/ks8842.c | 1 - drivers/net/ethernet/micrel/ks8851.c | 1 - drivers/net/ethernet/micrel/ks8851_mll.c | 1 - drivers/net/ethernet/microchip/enc28j60.c | 1 - drivers/net/ethernet/nvidia/forcedeth.c | 1 - drivers/net/ethernet/octeon/octeon_mgmt.c | 6 ++---- drivers/net/ethernet/smsc/smsc911x.c | 1 - drivers/net/ethernet/ti/davinci_emac.c | 1 - drivers/net/ethernet/tile/tilepro.c | 1 - drivers/net/ethernet/wiznet/w5100.c | 1 - drivers/net/ethernet/wiznet/w5300.c | 1 - drivers/net/macvlan.c | 1 - drivers/net/team/team.c | 2 -- drivers/staging/octeon/ethernet.c | 6 ++---- net/batman-adv/soft-interface.c | 1 - net/bridge/br_device.c | 1 - net/ethernet/eth.c | 2 -- net/openvswitch/vport-internal_dev.c | 1 - 27 files changed, 4 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index c1fdb8be8bee..8c3c9cfbf864 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -647,7 +647,6 @@ static int bfin_mac_set_mac_address(struct net_device *dev, void *p) if (netif_running(dev)) return -EBUSY; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; setup_mac_addr(dev->dev_addr); return 0; } diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index 17651c779680..7af95ce971ec 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -472,7 +472,6 @@ static int atl1c_set_mac_addr(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; atl1c_hw_set_mac_addr(&adapter->hw, adapter->hw.mac_addr); diff --git a/drivers/net/ethernet/atheros/atlx/atlx.c b/drivers/net/ethernet/atheros/atlx/atlx.c index 77ffbc4a5071..f82eb1699464 100644 --- a/drivers/net/ethernet/atheros/atlx/atlx.c +++ b/drivers/net/ethernet/atheros/atlx/atlx.c @@ -84,7 +84,6 @@ static int atlx_set_mac(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; atlx_set_mac_addr(&adapter->hw); return 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f9a15cbf73ef..d17fa5ab15a8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3904,7 +3904,6 @@ int bnx2x_change_mac_addr(struct net_device *dev, void *p) return rc; } - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); if (netif_running(dev)) diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index b407043ce9b0..a345e2454b83 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1459,7 +1459,6 @@ static int xgmac_set_mac_address(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); xgmac_set_mac_addr(ioaddr, dev->dev_addr, 0); diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 64866ff1aea0..ebccebff1437 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -865,7 +865,6 @@ static int enic_set_mac_addr(struct net_device *netdev, char *addr) } memcpy(netdev->dev_addr, addr, netdev->addr_len); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index f380bb7653dd..b93062dac482 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -784,7 +784,6 @@ static int ethoc_set_mac_address(struct net_device *dev, void *addr) ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); memcpy(dev->dev_addr, mac, ETH_ALEN); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 277f5dfe3d90..53281ff17315 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1738,7 +1738,6 @@ static int igbvf_set_mac(struct net_device *netdev, void *p) return -EADDRNOTAVAIL; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 10d678d3dd01..aa037ca314e7 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -627,7 +627,6 @@ static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr) if (!is_valid_ether_addr(sa->sa_data)) return -EADDRNOTAVAIL; memcpy(oldMac, dev->dev_addr, ETH_ALEN); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); netif_addr_lock_bh(dev); update_hash_table_mac_address(pep, oldMac, dev->dev_addr); diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c index b71eb39ab448..fbcb9e74d7fc 100644 --- a/drivers/net/ethernet/micrel/ks8842.c +++ b/drivers/net/ethernet/micrel/ks8842.c @@ -1080,7 +1080,6 @@ static int ks8842_set_mac(struct net_device *netdev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(netdev->dev_addr, mac, netdev->addr_len); ks8842_write_mac_addr(adapter, mac); diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 286816a4e783..d59bc0e151b3 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -1052,7 +1052,6 @@ static int ks8851_set_mac_address(struct net_device *dev, void *addr) if (!is_valid_ether_addr(sa->sa_data)) return -EADDRNOTAVAIL; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); return ks8851_write_mac_addr(dev); } diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index ef8f9f92e547..a343066f7b43 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -1237,7 +1237,6 @@ static int ks_set_mac_address(struct net_device *netdev, void *paddr) struct sockaddr *addr = paddr; u8 *da; - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); da = (u8 *)netdev->dev_addr; diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c index a99456c3dd87..5d98a9f7bfc7 100644 --- a/drivers/net/ethernet/microchip/enc28j60.c +++ b/drivers/net/ethernet/microchip/enc28j60.c @@ -527,7 +527,6 @@ static int enc28j60_set_mac_address(struct net_device *dev, void *addr) if (!is_valid_ether_addr(address->sa_data)) return -EADDRNOTAVAIL; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, address->sa_data, dev->addr_len); return enc28j60_set_hw_macaddr(dev); } diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 653487dc7b52..b53bf9db471a 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -3025,7 +3025,6 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) /* synchronized against open : rtnl_lock() held by caller */ memcpy(dev->dev_addr, macaddr->sa_data, ETH_ALEN); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; if (netif_running(dev)) { netif_tx_lock_bh(dev); diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index b5499198e029..b4a842c3b5c4 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1534,12 +1534,10 @@ static int octeon_mgmt_probe(struct platform_device *pdev) mac = of_get_mac_address(pdev->dev.of_node); - if (mac && is_valid_ether_addr(mac)) { + if (mac && is_valid_ether_addr(mac)) memcpy(netdev->dev_addr, mac, ETH_ALEN); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; - } else { + else eth_hw_addr_random(netdev); - } p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index e112877d15d3..04ff63cb6544 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1831,7 +1831,6 @@ static int smsc911x_set_mac_address(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); spin_lock_irq(&pdata->mac_lock); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 2a3e2c56bc60..6841a22c8621 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1264,7 +1264,6 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) /* Store mac addr in priv and rx channel and set it in EMAC hw */ memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len); - ndev->addr_assign_type &= ~NET_ADDR_RANDOM; /* MAC address is configured only after the interface is enabled. */ if (netif_running(ndev)) { diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 96070e9b50dc..36435499814b 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -2195,7 +2195,6 @@ static int tile_net_set_mac_address(struct net_device *dev, void *p) /* ISSUE: Note that "dev_addr" is now a pointer. */ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index 352383890326..545043cc4c0b 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -570,7 +570,6 @@ static int w5100_set_macaddr(struct net_device *ndev, void *addr) if (!is_valid_ether_addr(sock_addr->sa_data)) return -EADDRNOTAVAIL; memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN); - ndev->addr_assign_type &= ~NET_ADDR_RANDOM; w5100_write_macaddr(priv); return 0; } diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 9d1d986f8d40..7cbd0e6fc6f3 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -490,7 +490,6 @@ static int w5300_set_macaddr(struct net_device *ndev, void *addr) if (!is_valid_ether_addr(sock_addr->sa_data)) return -EADDRNOTAVAIL; memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN); - ndev->addr_assign_type &= ~NET_ADDR_RANDOM; w5300_write_macaddr(priv); return 0; } diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 68a43fe602e7..80c6f927a7b4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -375,7 +375,6 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p) if (!(dev->flags & IFF_UP)) { /* Just copy in the new address */ - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); } else { /* Rehash and update the device filters */ diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 78c7d8724614..2ea4f0ec54b0 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1500,7 +1500,6 @@ static int team_set_mac_address(struct net_device *dev, void *p) if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; rcu_read_lock(); list_for_each_entry_rcu(port, &team->port_list, list) if (team->ops.port_change_dev_addr) @@ -1770,7 +1769,6 @@ static void team_setup_by_port(struct net_device *dev, dev->mtu = port_dev->mtu; memcpy(dev->broadcast, port_dev->broadcast, port_dev->addr_len); memcpy(dev->dev_addr, port_dev->dev_addr, port_dev->addr_len); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; } static int team_dev_type_check_change(struct net_device *dev, diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index ef32dc1bbc80..9a2ec251ae4d 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -453,12 +453,10 @@ int cvm_oct_common_init(struct net_device *dev) if (priv->of_node) mac = of_get_mac_address(priv->of_node); - if (mac && is_valid_ether_addr(mac)) { + if (mac && is_valid_ether_addr(mac)) memcpy(dev->dev_addr, mac, ETH_ALEN); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; - } else { + else eth_hw_addr_random(dev); - } /* * Force the interface to use the POW send if always_use_pow diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 6b548fde8e04..fa388b2c60d7 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -124,7 +124,6 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX); } - dev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 7c78e2640190..38974f70ec7b 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -172,7 +172,6 @@ static int br_set_mac_address(struct net_device *dev, void *p) spin_lock_bh(&br->lock); if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) { - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); br_fdb_change_mac_address(br, addr->sa_data); br_stp_change_bridge_id(br, addr->sa_data); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 4efad533e5f6..bc39c8c8f589 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -290,8 +290,6 @@ int eth_mac_addr(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); - /* if device marked as NET_ADDR_RANDOM, reset it */ - dev->addr_assign_type &= ~NET_ADDR_RANDOM; return 0; } EXPORT_SYMBOL(eth_mac_addr); diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 5d460c37df07..44cd5c3a4c22 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -69,7 +69,6 @@ static int internal_dev_mac_addr(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); return 0; } -- cgit v1.2.3 From 75ea719cb4773b05cc48e741feb4e267522b9656 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 3 Jan 2013 00:09:46 -0500 Subject: mac80211: fix mesh_hwmp kerneldoc One of the function names was wrong and some parameters were missing. Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 47aeee2d8db1..9044296c8876 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -219,12 +219,14 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, } /** - * mesh_send_path error - Sends a PERR mesh management frame + * mesh_path_error_tx - Sends a PERR mesh management frame * + * @ttl: allowed remaining hops * @target: broken destination * @target_sn: SN of the broken destination * @target_rcode: reason code for this PERR * @ra: node this frame is addressed to + * @sdata: local mesh subif * * Note: This function may be called with driver locks taken that the driver * also acquires in the TX path. To avoid a deadlock we don't transmit the @@ -350,6 +352,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, * @sdata: local mesh subif * @mgmt: mesh management frame * @hwmp_ie: hwmp information element (PREP or PREQ) + * @action: type of hwmp ie * * This function updates the path routing information to the originator and the * transmitter of a HWMP PREQ or PREP frame. -- cgit v1.2.3 From 9ff162a8b96c96238773972e26288a366e403b0c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:49 +0000 Subject: net: introduce upper device lists This lists are supposed to serve for storing pointers to all upper devices. Eventually it will replace dev->master pointer which is used for bonding, bridge, team but it cannot be used for vlan, macvlan where there might be multiple upper present. In case the upper link is replacement for dev->master, it is marked with "master" flag. New upper device list resolves this limitation. Also, the information stored in lists is used for preventing looping setups like "bond->somethingelse->samebond" Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 14 +++ net/core/dev.c | 239 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 249 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c5031a45e185..e324601f48e8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1174,6 +1174,8 @@ struct net_device { * which this device is member of. */ + struct list_head upper_dev_list; /* List of upper devices */ + /* Interface address info used in eth_type_trans() */ unsigned char *dev_addr; /* hw address, (before bcast because most packets are @@ -2636,6 +2638,18 @@ extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; extern int weight_p; extern int bpf_jit_enable; + +extern bool netdev_has_upper_dev(struct net_device *dev, + struct net_device *upper_dev); +extern bool netdev_has_any_upper_dev(struct net_device *dev); +extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev); +extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); +extern int netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev); +extern int netdev_master_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev); +extern void netdev_upper_dev_unlink(struct net_device *dev, + struct net_device *upper_dev); extern int netdev_set_master(struct net_device *dev, struct net_device *master); extern int netdev_set_bond_master(struct net_device *dev, struct net_device *master); diff --git a/net/core/dev.c b/net/core/dev.c index bddb2f2ccaa9..53a9fefbc9af 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4600,6 +4600,232 @@ static int __init dev_proc_init(void) #endif /* CONFIG_PROC_FS */ +struct netdev_upper { + struct net_device *dev; + bool master; + struct list_head list; + struct rcu_head rcu; + struct list_head search_list; +}; + +static void __append_search_uppers(struct list_head *search_list, + struct net_device *dev) +{ + struct netdev_upper *upper; + + list_for_each_entry(upper, &dev->upper_dev_list, list) { + /* check if this upper is not already in search list */ + if (list_empty(&upper->search_list)) + list_add_tail(&upper->search_list, search_list); + } +} + +static bool __netdev_search_upper_dev(struct net_device *dev, + struct net_device *upper_dev) +{ + LIST_HEAD(search_list); + struct netdev_upper *upper; + struct netdev_upper *tmp; + bool ret = false; + + __append_search_uppers(&search_list, dev); + list_for_each_entry(upper, &search_list, search_list) { + if (upper->dev == upper_dev) { + ret = true; + break; + } + __append_search_uppers(&search_list, upper->dev); + } + list_for_each_entry_safe(upper, tmp, &search_list, search_list) + INIT_LIST_HEAD(&upper->search_list); + return ret; +} + +static struct netdev_upper *__netdev_find_upper(struct net_device *dev, + struct net_device *upper_dev) +{ + struct netdev_upper *upper; + + list_for_each_entry(upper, &dev->upper_dev_list, list) { + if (upper->dev == upper_dev) + return upper; + } + return NULL; +} + +/** + * netdev_has_upper_dev - Check if device is linked to an upper device + * @dev: device + * @upper_dev: upper device to check + * + * Find out if a device is linked to specified upper device and return true + * in case it is. Note that this checks only immediate upper device, + * not through a complete stack of devices. The caller must hold the RTNL lock. + */ +bool netdev_has_upper_dev(struct net_device *dev, + struct net_device *upper_dev) +{ + ASSERT_RTNL(); + + return __netdev_find_upper(dev, upper_dev); +} +EXPORT_SYMBOL(netdev_has_upper_dev); + +/** + * netdev_has_any_upper_dev - Check if device is linked to some device + * @dev: device + * + * Find out if a device is linked to an upper device and return true in case + * it is. The caller must hold the RTNL lock. + */ +bool netdev_has_any_upper_dev(struct net_device *dev) +{ + ASSERT_RTNL(); + + return !list_empty(&dev->upper_dev_list); +} +EXPORT_SYMBOL(netdev_has_any_upper_dev); + +/** + * netdev_master_upper_dev_get - Get master upper device + * @dev: device + * + * Find a master upper device and return pointer to it or NULL in case + * it's not there. The caller must hold the RTNL lock. + */ +struct net_device *netdev_master_upper_dev_get(struct net_device *dev) +{ + struct netdev_upper *upper; + + ASSERT_RTNL(); + + if (list_empty(&dev->upper_dev_list)) + return NULL; + + upper = list_first_entry(&dev->upper_dev_list, + struct netdev_upper, list); + if (likely(upper->master)) + return upper->dev; + return NULL; +} +EXPORT_SYMBOL(netdev_master_upper_dev_get); + +/** + * netdev_master_upper_dev_get_rcu - Get master upper device + * @dev: device + * + * Find a master upper device and return pointer to it or NULL in case + * it's not there. The caller must hold the RCU read lock. + */ +struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev) +{ + struct netdev_upper *upper; + + upper = list_first_or_null_rcu(&dev->upper_dev_list, + struct netdev_upper, list); + if (upper && likely(upper->master)) + return upper->dev; + return NULL; +} +EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu); + +static int __netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev, bool master) +{ + struct netdev_upper *upper; + + ASSERT_RTNL(); + + if (dev == upper_dev) + return -EBUSY; + + /* To prevent loops, check if dev is not upper device to upper_dev. */ + if (__netdev_search_upper_dev(upper_dev, dev)) + return -EBUSY; + + if (__netdev_find_upper(dev, upper_dev)) + return -EEXIST; + + if (master && netdev_master_upper_dev_get(dev)) + return -EBUSY; + + upper = kmalloc(sizeof(*upper), GFP_KERNEL); + if (!upper) + return -ENOMEM; + + upper->dev = upper_dev; + upper->master = master; + INIT_LIST_HEAD(&upper->search_list); + + /* Ensure that master upper link is always the first item in list. */ + if (master) + list_add_rcu(&upper->list, &dev->upper_dev_list); + else + list_add_tail_rcu(&upper->list, &dev->upper_dev_list); + dev_hold(upper_dev); + + return 0; +} + +/** + * netdev_upper_dev_link - Add a link to the upper device + * @dev: device + * @upper_dev: new upper device + * + * Adds a link to device which is upper to this one. The caller must hold + * the RTNL lock. On a failure a negative errno code is returned. + * On success the reference counts are adjusted and the function + * returns zero. + */ +int netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev) +{ + return __netdev_upper_dev_link(dev, upper_dev, false); +} +EXPORT_SYMBOL(netdev_upper_dev_link); + +/** + * netdev_master_upper_dev_link - Add a master link to the upper device + * @dev: device + * @upper_dev: new upper device + * + * Adds a link to device which is upper to this one. In this case, only + * one master upper device can be linked, although other non-master devices + * might be linked as well. The caller must hold the RTNL lock. + * On a failure a negative errno code is returned. On success the reference + * counts are adjusted and the function returns zero. + */ +int netdev_master_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev) +{ + return __netdev_upper_dev_link(dev, upper_dev, true); +} +EXPORT_SYMBOL(netdev_master_upper_dev_link); + +/** + * netdev_upper_dev_unlink - Removes a link to upper device + * @dev: device + * @upper_dev: new upper device + * + * Removes a link to device which is upper to this one. The caller must hold + * the RTNL lock. + */ +void netdev_upper_dev_unlink(struct net_device *dev, + struct net_device *upper_dev) +{ + struct netdev_upper *upper; + + ASSERT_RTNL(); + + upper = __netdev_find_upper(dev, upper_dev); + if (!upper) + return; + list_del_rcu(&upper->list); + dev_put(upper_dev); + kfree_rcu(upper, rcu); +} +EXPORT_SYMBOL(netdev_upper_dev_unlink); + /** * netdev_set_master - set up master pointer * @slave: slave device @@ -4613,19 +4839,23 @@ static int __init dev_proc_init(void) int netdev_set_master(struct net_device *slave, struct net_device *master) { struct net_device *old = slave->master; + int err; ASSERT_RTNL(); if (master) { if (old) return -EBUSY; - dev_hold(master); + err = netdev_master_upper_dev_link(slave, master); + if (err) + return err; } slave->master = master; if (old) - dev_put(old); + netdev_upper_dev_unlink(slave, master); + return 0; } EXPORT_SYMBOL(netdev_set_master); @@ -5503,8 +5733,8 @@ static void rollback_registered_many(struct list_head *head) if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); - /* Notifier chain MUST detach us from master device. */ - WARN_ON(dev->master); + /* Notifier chain MUST detach us all upper devices. */ + WARN_ON(netdev_has_any_upper_dev(dev)); /* Remove entries from kobject tree */ netdev_unregister_kobject(dev); @@ -6212,6 +6442,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->link_watch_list); + INIT_LIST_HEAD(&dev->upper_dev_list); dev->priv_flags = IFF_XMIT_DST_RELEASE; setup(dev); -- cgit v1.2.3 From 126d6c236bb817c8752be41884e4c72a52a50f92 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:51 +0000 Subject: vlan: add link to upper device Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/8021q/vlan.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index a292e8050ef2..babfde9f734c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -105,6 +105,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) */ unregister_netdevice_queue(dev, head); + netdev_upper_dev_unlink(real_dev, dev); + if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); @@ -162,9 +164,13 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_applicant; + err = netdev_upper_dev_link(real_dev, dev); + if (err) + goto out_uninit_applicant; + err = register_netdevice(dev); if (err < 0) - goto out_uninit_applicant; + goto out_upper_dev_unlink; /* Account for reference in struct vlan_dev_priv */ dev_hold(real_dev); @@ -180,6 +186,8 @@ int register_vlan_dev(struct net_device *dev) return 0; +out_upper_dev_unlink: + netdev_upper_dev_unlink(real_dev, dev); out_uninit_applicant: if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); -- cgit v1.2.3 From 898e506171b2c6132cdebd450ab4db81ae9a59bc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:52 +0000 Subject: rtnetlink: remove usage of dev->master Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 69 ++++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 78d05a9dcea6..d26becbcddc4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -880,6 +880,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, const struct rtnl_link_stats64 *stats; struct nlattr *attr, *af_spec; struct rtnl_af_ops *af_ops; + struct net_device *upper_dev = netdev_master_upper_dev_get(dev); ASSERT_RTNL(); nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); @@ -908,8 +909,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, #endif (dev->ifindex != dev->iflink && nla_put_u32(skb, IFLA_LINK, dev->iflink)) || - (dev->master && - nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + (upper_dev && + nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) || nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || @@ -1273,16 +1274,16 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) static int do_set_master(struct net_device *dev, int ifindex) { - struct net_device *master_dev; + struct net_device *upper_dev = netdev_master_upper_dev_get(dev); const struct net_device_ops *ops; int err; - if (dev->master) { - if (dev->master->ifindex == ifindex) + if (upper_dev) { + if (upper_dev->ifindex == ifindex) return 0; - ops = dev->master->netdev_ops; + ops = upper_dev->netdev_ops; if (ops->ndo_del_slave) { - err = ops->ndo_del_slave(dev->master, dev); + err = ops->ndo_del_slave(upper_dev, dev); if (err) return err; } else { @@ -1291,12 +1292,12 @@ static int do_set_master(struct net_device *dev, int ifindex) } if (ifindex) { - master_dev = __dev_get_by_index(dev_net(dev), ifindex); - if (!master_dev) + upper_dev = __dev_get_by_index(dev_net(dev), ifindex); + if (!upper_dev) return -EINVAL; - ops = master_dev->netdev_ops; + ops = upper_dev->netdev_ops; if (ops->ndo_add_slave) { - err = ops->ndo_add_slave(master_dev, dev); + err = ops->ndo_add_slave(upper_dev, dev); if (err) return err; } else { @@ -2048,7 +2049,6 @@ errout: static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); - struct net_device *master = NULL; struct ndmsg *ndm; struct nlattr *tb[NDA_MAX+1]; struct net_device *dev; @@ -2090,10 +2090,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* Support fdb on master device the net/bridge default case */ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && (dev->priv_flags & IFF_BRIDGE_PORT)) { - master = dev->master; - err = master->netdev_ops->ndo_fdb_add(ndm, tb, - dev, addr, - nlh->nlmsg_flags); + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + const struct net_device_ops *ops = br_dev->netdev_ops; + + err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags); if (err) goto out; else @@ -2154,10 +2154,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* Support fdb on master device the net/bridge default case */ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && (dev->priv_flags & IFF_BRIDGE_PORT)) { - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + const struct net_device_ops *ops = br_dev->netdev_ops; - if (master->netdev_ops->ndo_fdb_del) - err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr); + if (ops->ndo_fdb_del) + err = ops->ndo_fdb_del(ndm, dev, addr); if (err) goto out; @@ -2241,9 +2242,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); for_each_netdev_rcu(net, dev) { if (dev->priv_flags & IFF_BRIDGE_PORT) { - struct net_device *master = dev->master; - const struct net_device_ops *ops = master->netdev_ops; + struct net_device *br_dev; + const struct net_device_ops *ops; + br_dev = netdev_master_upper_dev_get(dev); + ops = br_dev->netdev_ops; if (ops->ndo_fdb_dump) idx = ops->ndo_fdb_dump(skb, cb, dev, idx); } @@ -2264,6 +2267,7 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct ifinfomsg *ifm; struct nlattr *br_afspec; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI); if (nlh == NULL) @@ -2281,8 +2285,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, if (nla_put_string(skb, IFLA_IFNAME, dev->name) || nla_put_u32(skb, IFLA_MTU, dev->mtu) || nla_put_u8(skb, IFLA_OPERSTATE, operstate) || - (dev->master && - nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + (br_dev && + nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) || (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || (dev->ifindex != dev->iflink && @@ -2318,11 +2322,11 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); for_each_netdev_rcu(net, dev) { const struct net_device_ops *ops = dev->netdev_ops; - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); - if (master && master->netdev_ops->ndo_bridge_getlink) { + if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { if (idx >= cb->args[0] && - master->netdev_ops->ndo_bridge_getlink( + br_dev->netdev_ops->ndo_bridge_getlink( skb, portid, seq, dev) < 0) break; idx++; @@ -2359,7 +2363,7 @@ static inline size_t bridge_nlmsg_size(void) static int rtnl_bridge_notify(struct net_device *dev, u16 flags) { struct net *net = dev_net(dev); - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); struct sk_buff *skb; int err = -EOPNOTSUPP; @@ -2370,8 +2374,8 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags) } if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && - master && master->netdev_ops->ndo_bridge_getlink) { - err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { + err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); if (err < 0) goto errout; } @@ -2430,13 +2434,14 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, oflags = flags; if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { - if (!dev->master || - !dev->master->netdev_ops->ndo_bridge_setlink) { + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + + if (!br_dev || !br_dev->netdev_ops->ndo_bridge_setlink) { err = -EOPNOTSUPP; goto out; } - err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); + err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh); if (err) goto out; -- cgit v1.2.3 From 74fdd93fbcc16522406f65c564ea104490ad4908 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:54 +0000 Subject: bridge: remove usage of netdev_set_master() Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/bridge/br_if.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 1edd71d44a4c..2148d474a04f 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -148,7 +148,7 @@ static void del_nbp(struct net_bridge_port *p) netdev_rx_handler_unregister(dev); synchronize_net(); - netdev_set_master(dev, NULL); + netdev_upper_dev_unlink(dev, br->dev); br_multicast_del_port(p); @@ -364,7 +364,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL)))) goto err3; - err = netdev_set_master(dev, br->dev); + err = netdev_master_upper_dev_link(dev, br->dev); if (err) goto err4; @@ -403,7 +403,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) return 0; err5: - netdev_set_master(dev, NULL); + netdev_upper_dev_unlink(dev, br->dev); err4: br_netpoll_disable(p); err3: -- cgit v1.2.3 From 49bd8fb0b18944fc0b6b11d999619d3687c3914a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:55 +0000 Subject: netpoll: remove usage of dev->master Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/netpoll.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 3151acf5ec13..d2bda8eb08ec 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -210,9 +210,12 @@ static void netpoll_poll_dev(struct net_device *dev) if (dev->flags & IFF_SLAVE) { if (ni) { - struct net_device *bond_dev = dev->master; + struct net_device *bond_dev; struct sk_buff *skb; - struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo); + struct netpoll_info *bond_ni; + + bond_dev = netdev_master_upper_dev_get_rcu(dev); + bond_ni = rcu_dereference_bh(bond_dev->npinfo); while ((skb = skb_dequeue(&ni->arp_tx))) { skb->dev = bond_dev; skb_queue_tail(&bond_ni->arp_tx, skb); @@ -815,7 +818,7 @@ int netpoll_setup(struct netpoll *np) return -ENODEV; } - if (ndev->master) { + if (netdev_master_upper_dev_get(ndev)) { np_err(np, "%s is a slave device, aborting\n", np->dev_name); err = -EBUSY; goto put; -- cgit v1.2.3 From 1cdfd72f791918ab4690570d7d93a1f86e171cb8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:48:59 +0000 Subject: vlan: remove usage of dev->master in __vlan_find_dev_deep() Also, since all users call __vlan_find_dev_deep() with rcu_read_lock, make no possibility to call this with rtnl mutex held only. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/8021q/vlan_core.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 65e06abe023f..380440b8ea89 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -60,21 +60,25 @@ bool vlan_do_receive(struct sk_buff **skbp) return true; } -/* Must be invoked with rcu_read_lock or with RTNL. */ -struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, +/* Must be invoked with rcu_read_lock. */ +struct net_device *__vlan_find_dev_deep(struct net_device *dev, u16 vlan_id) { - struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); + struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); if (vlan_info) { return vlan_group_get_device(&vlan_info->grp, vlan_id); } else { /* - * Bonding slaves do not have grp assigned to themselves. - * Grp is assigned to bonding master instead. + * Lower devices of master uppers (bonding, team) do not have + * grp assigned to themselves. Grp is assigned to upper device + * instead. */ - if (netif_is_bond_slave(real_dev)) - return __vlan_find_dev_deep(real_dev->master, vlan_id); + struct net_device *upper_dev; + + upper_dev = netdev_master_upper_dev_get_rcu(dev); + if (upper_dev) + return __vlan_find_dev_deep(upper_dev, vlan_id); } return NULL; -- cgit v1.2.3 From 471cb5a33dcbd7c529684a2ac7ba4451414ee4a7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:49:01 +0000 Subject: bonding: remove usage of dev->master Benefit from new upper dev list and free bonding from dev->master usage. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 30 ++++++------- drivers/net/bonding/bond_alb.c | 6 +-- drivers/net/bonding/bond_main.c | 94 ++++++++++++++++++++++++----------------- drivers/net/bonding/bonding.h | 14 +++--- net/core/rtnetlink.c | 1 + 5 files changed, 81 insertions(+), 64 deletions(-) (limited to 'net') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index a030e635f001..84fabd69df4c 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1127,7 +1127,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) // INFO_RECEIVED_LOOPBACK_FRAMES pr_err("%s: An illegal loopback occurred on adapter (%s).\n" "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n", - port->slave->dev->master->name, port->slave->dev->name); + port->slave->bond->dev->name, port->slave->dev->name); return; } __update_selected(lacpdu, port); @@ -1306,7 +1306,7 @@ static void ad_port_selection_logic(struct port *port) } if (!curr_port) { // meaning: the port was related to an aggregator but was not on the aggregator port list pr_warning("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", - port->slave->dev->master->name, + port->slave->bond->dev->name, port->actor_port_number, port->slave->dev->name, port->aggregator->aggregator_identifier); @@ -1386,7 +1386,7 @@ static void ad_port_selection_logic(struct port *port) port->aggregator->aggregator_identifier); } else { pr_err("%s: Port %d (on %s) did not find a suitable aggregator\n", - port->slave->dev->master->name, + port->slave->bond->dev->name, port->actor_port_number, port->slave->dev->name); } } @@ -1463,7 +1463,7 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, default: pr_warning("%s: Impossible agg select mode %d\n", - curr->slave->dev->master->name, + curr->slave->bond->dev->name, __get_agg_selection_mode(curr->lag_ports)); break; } @@ -1571,7 +1571,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) // check if any partner replys if (best->is_individual) { pr_warning("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", - best->slave ? best->slave->dev->master->name : "NULL"); + best->slave ? best->slave->bond->dev->name : "NULL"); } best->is_active = 1; @@ -1898,7 +1898,7 @@ int bond_3ad_bind_slave(struct slave *slave) if (bond == NULL) { pr_err("%s: The slave %s is not attached to its bond\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return -1; } @@ -1973,7 +1973,7 @@ void bond_3ad_unbind_slave(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: Trying to unbind an uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2009,7 +2009,7 @@ void bond_3ad_unbind_slave(struct slave *slave) if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) { pr_info("%s: Removing an active aggregator\n", - aggregator->slave->dev->master->name); + aggregator->slave->bond->dev->name); // select new active aggregator select_new_active_agg = 1; } @@ -2040,7 +2040,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_agg_selection_logic(__get_first_agg(port)); } else { pr_warning("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", - slave->dev->master->name); + slave->bond->dev->name); } } else { // in case that the only port related to this aggregator is the one we want to remove select_new_active_agg = aggregator->is_active; @@ -2048,7 +2048,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_clear_agg(aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", - slave->dev->master->name); + slave->bond->dev->name); // select new active aggregator ad_agg_selection_logic(__get_first_agg(port)); } @@ -2076,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_clear_agg(temp_aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", - slave->dev->master->name); + slave->bond->dev->name); // select new active aggregator ad_agg_selection_logic(__get_first_agg(port)); } @@ -2184,7 +2184,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u1 if (!port->slave) { pr_warning("%s: Warning: port of slave %s is uninitialized\n", - slave->dev->name, slave->dev->master->name); + slave->dev->name, slave->bond->dev->name); return ret; } @@ -2240,7 +2240,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: speed changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2268,7 +2268,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("%s: Warning: duplex changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2297,7 +2297,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: link status changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 7c9d136e74be..f5e052723029 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -507,7 +507,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) client_info->mac_dst); if (!skb) { pr_err("%s: Error: failed to create an ARP packet\n", - client_info->slave->dev->master->name); + client_info->slave->bond->dev->name); continue; } @@ -517,7 +517,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) skb = vlan_put_tag(skb, client_info->vlan_id); if (!skb) { pr_err("%s: Error: failed to insert VLAN tag\n", - client_info->slave->dev->master->name); + client_info->slave->bond->dev->name); continue; } } @@ -1043,7 +1043,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) if (dev_set_mac_address(dev, &s_addr)) { pr_err("%s: Error: dev_set_mac_address of dev %s failed!\n" "ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", - dev->master->name, dev->name); + slave->bond->dev->name, dev->name); return -EOPNOTSUPP; } return 0; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b7d45f367d4a..c4952985c084 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -746,11 +746,9 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) { struct in_device *in_dev; - rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) ip_mc_rejoin_groups(in_dev); - rcu_read_unlock(); } /* @@ -760,9 +758,10 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) */ static void bond_resend_igmp_join_requests(struct bonding *bond) { - struct net_device *bond_dev, *vlan_dev, *master_dev; + struct net_device *bond_dev, *vlan_dev, *upper_dev; struct vlan_entry *vlan; + rcu_read_lock(); read_lock(&bond->lock); bond_dev = bond->dev; @@ -774,18 +773,14 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) * if bond is enslaved to a bridge, * then rejoin all groups on its master */ - master_dev = bond_dev->master; - if (master_dev) - if ((master_dev->priv_flags & IFF_EBRIDGE) - && (bond_dev->priv_flags & IFF_BRIDGE_PORT)) - __bond_resend_igmp_join_requests(master_dev); + upper_dev = netdev_master_upper_dev_get_rcu(bond_dev); + if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE) + __bond_resend_igmp_join_requests(upper_dev); /* rejoin all groups on vlan devices */ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - rcu_read_lock(); vlan_dev = __vlan_find_dev_deep(bond_dev, vlan->vlan_id); - rcu_read_unlock(); if (vlan_dev) __bond_resend_igmp_join_requests(vlan_dev); } @@ -794,13 +789,16 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); read_unlock(&bond->lock); + rcu_read_unlock(); } static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, mcast_work.work); + rcu_read_lock(); bond_resend_igmp_join_requests(bond); + rcu_read_unlock(); } /* @@ -1493,6 +1491,27 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) return ret; } +static int bond_master_upper_dev_link(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + int err; + + err = netdev_master_upper_dev_link(slave_dev, bond_dev); + if (err) + return err; + slave_dev->flags |= IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); + return 0; +} + +static void bond_upper_dev_unlink(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + netdev_upper_dev_unlink(slave_dev, bond_dev); + slave_dev->flags &= ~IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); +} + /* enslave device to bond device */ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) { @@ -1655,9 +1674,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - res = netdev_set_bond_master(slave_dev, bond_dev); + res = bond_master_upper_dev_link(bond_dev, slave_dev); if (res) { - pr_debug("Error %d calling netdev_set_bond_master\n", res); + pr_debug("Error %d calling bond_master_upper_dev_link\n", res); goto err_restore_mac; } @@ -1891,7 +1910,7 @@ err_close: dev_close(slave_dev); err_unset_master: - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); err_restore_mac: if (!bond->params.fail_over_mac) { @@ -1936,7 +1955,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) /* slave is not a slave or master is not master of this slave */ if (!(slave_dev->flags & IFF_SLAVE) || - (slave_dev->master != bond_dev)) { + !netdev_has_upper_dev(slave_dev, bond_dev)) { pr_err("%s: Error: cannot release %s.\n", bond_dev->name, slave_dev->name); return -EINVAL; @@ -2080,7 +2099,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); slave_disable_netpoll(slave); @@ -2195,7 +2214,7 @@ static int bond_release_all(struct net_device *bond_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); slave_disable_netpoll(slave); @@ -2259,8 +2278,9 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi if (!USES_PRIMARY(bond->params.mode)) return -EINVAL; - /* Verify that master_dev is indeed the master of slave_dev */ - if (!(slave_dev->flags & IFF_SLAVE) || (slave_dev->master != bond_dev)) + /* Verify that bond_dev is indeed the master of slave_dev */ + if (!(slave_dev->flags & IFF_SLAVE) || + !netdev_has_upper_dev(slave_dev, bond_dev)) return -EINVAL; read_lock(&bond->lock); @@ -3258,36 +3278,32 @@ static int bond_master_netdev_event(unsigned long event, static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev) { - struct net_device *bond_dev = slave_dev->master; - struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave = NULL; + struct slave *slave = bond_slave_get_rtnl(slave_dev); + struct bonding *bond = slave->bond; + struct net_device *bond_dev = slave->bond->dev; + u32 old_speed; + u8 old_duplex; switch (event) { case NETDEV_UNREGISTER: - if (bond_dev) { - if (bond->setup_by_slave) - bond_release_and_destroy(bond_dev, slave_dev); - else - bond_release(bond_dev, slave_dev); - } + if (bond->setup_by_slave) + bond_release_and_destroy(bond_dev, slave_dev); + else + bond_release(bond_dev, slave_dev); break; case NETDEV_UP: case NETDEV_CHANGE: - slave = bond_get_slave_by_dev(bond, slave_dev); - if (slave) { - u32 old_speed = slave->speed; - u8 old_duplex = slave->duplex; + old_speed = slave->speed; + old_duplex = slave->duplex; - bond_update_speed_duplex(slave); + bond_update_speed_duplex(slave); - if (bond->params.mode == BOND_MODE_8023AD) { - if (old_speed != slave->speed) - bond_3ad_adapter_speed_changed(slave); - if (old_duplex != slave->duplex) - bond_3ad_adapter_duplex_changed(slave); - } + if (bond->params.mode == BOND_MODE_8023AD) { + if (old_speed != slave->speed) + bond_3ad_adapter_speed_changed(slave); + if (old_duplex != slave->duplex) + bond_3ad_adapter_duplex_changed(slave); } - break; case NETDEV_DOWN: /* diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 21b68e5c14fd..0d282d20b5a8 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -258,6 +258,9 @@ static inline bool bond_vlan_used(struct bonding *bond) #define bond_slave_get_rcu(dev) \ ((struct slave *) rcu_dereference(dev->rx_handler_data)) +#define bond_slave_get_rtnl(dev) \ + ((struct slave *) rtnl_dereference(dev->rx_handler_data)) + /** * Returns NULL if the net_device does not belong to any of the bond's slaves * @@ -280,11 +283,9 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond, static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) { - if (!slave || !slave->dev->master) { + if (!slave || !slave->bond) return NULL; - } - - return netdev_priv(slave->dev->master); + return slave->bond; } static inline bool bond_is_lb(const struct bonding *bond) @@ -360,10 +361,9 @@ static inline void bond_netpoll_send_skb(const struct slave *slave, static inline void bond_set_slave_inactive_flags(struct slave *slave) { - struct bonding *bond = netdev_priv(slave->dev->master); - if (!bond_is_lb(bond)) + if (!bond_is_lb(slave->bond)) bond_set_backup_slave(slave); - if (!bond->params.all_slaves_active) + if (!slave->bond->params.all_slaves_active) slave->inactive = 1; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d26becbcddc4..9969afb82271 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1987,6 +1987,7 @@ errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); } +EXPORT_SYMBOL(rtmsg_ifinfo); static int nlmsg_populate_fdb_fill(struct sk_buff *skb, struct net_device *dev, -- cgit v1.2.3 From 8b98a70c28a607a02b3c3d41bc9a4c141f421052 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Jan 2013 22:49:02 +0000 Subject: net: remove no longer used netdev_set_bond_master() and netdev_set_master() Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 +---- net/core/dev.c | 63 ----------------------------------------------- 2 files changed, 1 insertion(+), 68 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e324601f48e8..3cad8eab02b6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -858,8 +858,7 @@ struct netdev_fcoe_hbainfo { * flow_id is a flow ID to be passed to rps_may_expire_flow() later. * Return the filter ID on success, or a negative error code. * - * Slave management functions (for bridge, bonding, etc). User should - * call netdev_set_master() to set dev->master properly. + * Slave management functions (for bridge, bonding, etc). * int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev); * Called to make another netdev an underling. * @@ -2650,9 +2649,6 @@ extern int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); extern void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev); -extern int netdev_set_master(struct net_device *dev, struct net_device *master); -extern int netdev_set_bond_master(struct net_device *dev, - struct net_device *master); extern int skb_checksum_help(struct sk_buff *skb); extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features); diff --git a/net/core/dev.c b/net/core/dev.c index 53a9fefbc9af..a51ccf46e8b7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4826,69 +4826,6 @@ void netdev_upper_dev_unlink(struct net_device *dev, } EXPORT_SYMBOL(netdev_upper_dev_unlink); -/** - * netdev_set_master - set up master pointer - * @slave: slave device - * @master: new master device - * - * Changes the master device of the slave. Pass %NULL to break the - * bonding. The caller must hold the RTNL semaphore. On a failure - * a negative errno code is returned. On success the reference counts - * are adjusted and the function returns zero. - */ -int netdev_set_master(struct net_device *slave, struct net_device *master) -{ - struct net_device *old = slave->master; - int err; - - ASSERT_RTNL(); - - if (master) { - if (old) - return -EBUSY; - err = netdev_master_upper_dev_link(slave, master); - if (err) - return err; - } - - slave->master = master; - - if (old) - netdev_upper_dev_unlink(slave, master); - - return 0; -} -EXPORT_SYMBOL(netdev_set_master); - -/** - * netdev_set_bond_master - set up bonding master/slave pair - * @slave: slave device - * @master: new master device - * - * Changes the master device of the slave. Pass %NULL to break the - * bonding. The caller must hold the RTNL semaphore. On a failure - * a negative errno code is returned. On success %RTM_NEWLINK is sent - * to the routing socket and the function returns zero. - */ -int netdev_set_bond_master(struct net_device *slave, struct net_device *master) -{ - int err; - - ASSERT_RTNL(); - - err = netdev_set_master(slave, master); - if (err) - return err; - if (master) - slave->flags |= IFF_SLAVE; - else - slave->flags &= ~IFF_SLAVE; - - rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE); - return 0; -} -EXPORT_SYMBOL(netdev_set_bond_master); - static void dev_change_rx_flags(struct net_device *dev, int flags) { const struct net_device_ops *ops = dev->netdev_ops; -- cgit v1.2.3 From b7dc8c3959fd43bfa0dbcf65375628c86665cb94 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Fri, 4 Jan 2013 03:58:04 +0000 Subject: ndisc: Remove unused space at tail of skb for ndisc messages. (TAKE 3) Currently, the size of skb allocated for NDISC is MAX_HEADER + LL_RESERVED_SPACE(dev) + packet length + dev->needed_tailroom, but only LL_RESERVED_SPACE(dev) bytes is "reserved" for headers. As a result, the skb looks like this (after construction of the message): head data tail end +--------------------------------------------------------------+ + | | | | +--------------------------------------------------------------+ |<-hlen---->|<---ipv6 packet------>|<--tlen-->|<--MAX_HEADER-->| =LL_ = dev RESERVED_ ->needed_ SPACE(dev) tailroom As the name implies, "MAX_HEADER" is used for headers, and should be "reserved" in prior to packet construction. Or, if some space is really required at the tail of ther skb, it should be explicitly documented. We have several option after construction of NDISC message: Option 1: head data tail end +---------------------------------------------+ + | | | +---------------------------------------------+ |<-hlen---->|<---ipv6 packet------>|<--tlen-->| =LL_ = dev RESERVED_ ->needed_ SPACE(dev) tailroom Option 2: head data tail end +--------------------------------------------------+ + | | | +--------------------------------------------------+ |<--MAX_HEADER-->|<---ipv6 packet------>|<--tlen-->| = dev ->needed_ tailroom Option 3: head data tail end +--------------------------------------------------------------+ + | | | | +--------------------------------------------------------------+ |<--MAX_HEADER-->|<-hlen---->|<---ipv6 packet------>|<--tlen-->| =LL_ = dev RESERVED_ ->needed_ SPACE(dev) tailroom Our tunnel drivers try expanding headroom and the space for tunnel encapsulation was not a mandatory space -- so we are not seeing bugs here --, but just for optimization for performance critial situations. Since NDISC messages are not performance critical unlike TCP, and as we know outgoing device, LL_RESERVED_SPACE(dev) should be just enough for the device in most (if not all) cases: LL_RESERVED_SPACE(dev) <= LL_MAX_HEADER <= MAX_HEADER Note that LL_RESERVED_SPACE(dev) is also enough for NDISC over SIT (e.g., ISATAP). So, I think Option 1 is just fine here. Signed-off-by: YOSHIFUJI Hideaki Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 6574175795df..4c4ccf79d2c5 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -395,7 +395,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, len += ndisc_opt_addr_space(dev); skb = sock_alloc_send_skb(sk, - (MAX_HEADER + sizeof(struct ipv6hdr) + + (sizeof(struct ipv6hdr) + len + hlen + tlen), 1, &err); if (!skb) { @@ -1439,7 +1439,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) hlen = LL_RESERVED_SPACE(dev); tlen = dev->needed_tailroom; buff = sock_alloc_send_skb(sk, - (MAX_HEADER + sizeof(struct ipv6hdr) + + (sizeof(struct ipv6hdr) + len + hlen + tlen), 1, &err); if (buff == NULL) { -- cgit v1.2.3 From 2afb9b533423a9b97f84181e773cf9361d98fed6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sun, 6 Jan 2013 12:41:57 +0000 Subject: ethtool: set addr_assign_type to NET_ADDR_SET when addr is passed on create In case user passed address via netlink during create, NET_ADDR_PERM was set. That is not correct so fix this by setting NET_ADDR_SET. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9969afb82271..9a419b099482 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1667,9 +1667,11 @@ struct net_device *rtnl_create_link(struct net *net, if (tb[IFLA_MTU]) dev->mtu = nla_get_u32(tb[IFLA_MTU]); - if (tb[IFLA_ADDRESS]) + if (tb[IFLA_ADDRESS]) { memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]), nla_len(tb[IFLA_ADDRESS])); + dev->addr_assign_type = NET_ADDR_SET; + } if (tb[IFLA_BROADCAST]) memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]), nla_len(tb[IFLA_BROADCAST])); -- cgit v1.2.3 From 7826d43f2db45c9305a6e0ba165650e1a203f517 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sun, 6 Jan 2013 00:44:26 +0000 Subject: ethtool: fix drvinfo strings set in drivers Use strlcpy where possible to ensure the string is \0 terminated. Use always sizeof(string) instead of 32, ETHTOOL_BUSINFO_LEN and custom defines. Use snprintf instead of sprint. Remove unnecessary inits of ->fw_version Remove unnecessary inits of drvinfo struct. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- arch/um/drivers/net_kern.c | 4 ++-- drivers/infiniband/hw/nes/nes_nic.c | 12 +++++++----- drivers/infiniband/ulp/ipoib/ipoib_ethtool.c | 2 +- drivers/net/bonding/bond_main.c | 9 +++++---- drivers/net/cris/eth_v10.c | 8 ++++---- drivers/net/ethernet/3com/3c501.c | 7 ++++--- drivers/net/ethernet/3com/3c509.c | 4 ++-- drivers/net/ethernet/3com/3c515.c | 7 ++++--- drivers/net/ethernet/8390/3c503.c | 7 ++++--- drivers/net/ethernet/8390/ax88796.c | 6 +++--- drivers/net/ethernet/adi/bfin_mac.c | 8 ++++---- drivers/net/ethernet/aeroflex/greth.c | 9 +++++---- drivers/net/ethernet/amd/au1000_eth.c | 8 ++++---- drivers/net/ethernet/amd/sunlance.c | 4 ++-- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 9 +++++---- drivers/net/ethernet/cirrus/ep93xx_eth.c | 4 ++-- drivers/net/ethernet/davicom/dm9000.c | 7 ++++--- drivers/net/ethernet/dec/ewrk3.c | 8 ++++---- drivers/net/ethernet/dlink/dl2k.c | 7 ++++--- drivers/net/ethernet/emulex/benet/be_ethtool.c | 12 ++++++------ drivers/net/ethernet/faraday/ftgmac100.c | 6 +++--- drivers/net/ethernet/faraday/ftmac100.c | 6 +++--- drivers/net/ethernet/freescale/fec.c | 7 ++++--- drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c | 4 ++-- drivers/net/ethernet/freescale/gianfar.h | 2 -- drivers/net/ethernet/freescale/gianfar_ethtool.c | 9 +++++---- drivers/net/ethernet/freescale/ucc_geth_ethtool.c | 8 ++++---- drivers/net/ethernet/i825xx/3c505.c | 7 ++++--- drivers/net/ethernet/i825xx/3c507.c | 7 ++++--- drivers/net/ethernet/ibm/emac/core.c | 9 ++++----- drivers/net/ethernet/ibm/ibmveth.c | 5 ++--- drivers/net/ethernet/korina.c | 6 +++--- drivers/net/ethernet/lantiq_etop.c | 6 +++--- drivers/net/ethernet/marvell/pxa168_eth.c | 8 ++++---- drivers/net/ethernet/neterion/vxge/vxge-ethtool.c | 6 +++--- drivers/net/ethernet/nuvoton/w90p910_ether.c | 4 ++-- drivers/net/ethernet/nxp/lpc_eth.c | 7 ++++--- drivers/net/ethernet/octeon/octeon_mgmt.c | 8 ++++---- drivers/net/ethernet/packetengines/hamachi.c | 7 ++++--- drivers/net/ethernet/packetengines/yellowfin.c | 7 ++++--- drivers/net/ethernet/rdc/r6040.c | 6 +++--- drivers/net/ethernet/sgi/ioc3-eth.c | 6 +++--- drivers/net/ethernet/smsc/smc911x.c | 7 ++++--- drivers/net/ethernet/smsc/smc91x.c | 7 ++++--- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 3 +-- drivers/net/ethernet/sun/sunbmac.c | 4 ++-- drivers/net/ethernet/sun/sunqe.c | 7 ++++--- drivers/net/ethernet/sun/sunvnet.c | 4 ++-- drivers/net/ethernet/tehuti/tehuti.c | 8 ++++---- drivers/net/ethernet/ti/cpmac.c | 7 +++---- drivers/net/ethernet/ti/cpsw.c | 7 ++++--- drivers/net/ethernet/ti/davinci_emac.c | 4 ++-- drivers/net/ethernet/toshiba/ps3_gelic_net.c | 4 ++-- drivers/net/ethernet/toshiba/spider_net_ethtool.c | 12 +++++++----- drivers/net/ethernet/toshiba/tc35815.c | 7 ++++--- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 5 ++--- drivers/net/ethernet/xircom/xirc2ps_cs.c | 3 ++- drivers/net/ethernet/xscale/ixp4xx_eth.c | 5 +++-- drivers/net/hyperv/netvsc_drv.c | 6 +++--- drivers/net/macvlan.c | 4 ++-- drivers/net/rionet.c | 8 ++++---- drivers/net/usb/asix_common.c | 4 ++-- drivers/net/usb/catc.c | 6 +++--- drivers/net/usb/cdc_ncm.c | 6 +++--- drivers/net/usb/pegasus.c | 5 +++-- drivers/net/usb/rtl8150.c | 6 +++--- drivers/net/usb/sierra_net.c | 4 ++-- drivers/net/vmxnet3/vmxnet3_ethtool.c | 2 +- drivers/net/wimax/i2400m/netdev.c | 10 +++++----- drivers/net/wimax/i2400m/usb.c | 6 +++--- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 8 +++++--- drivers/s390/net/qeth_core_main.c | 19 ++++++++----------- drivers/staging/bcm/Bcmnet.c | 4 ++-- drivers/staging/ccg/u_ether.c | 10 +++++----- drivers/staging/et131x/et131x.c | 8 ++++---- drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c | 9 +++++---- drivers/staging/octeon/ethernet-mdio.c | 6 +++--- drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c | 6 +++--- drivers/staging/wlags49_h2/wl_netdev.c | 14 +++++++------- drivers/usb/gadget/u_ether.c | 10 +++++----- net/8021q/vlan_dev.c | 6 +++--- net/batman-adv/soft-interface.c | 8 ++++---- net/bridge/br_device.c | 8 ++++---- net/dsa/slave.c | 8 ++++---- net/openvswitch/vport-internal_dev.c | 2 +- net/wireless/ethtool.c | 4 ++-- 86 files changed, 297 insertions(+), 277 deletions(-) (limited to 'net') diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index fd9090df64b6..d8926c303629 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -274,8 +274,8 @@ static void uml_net_poll_controller(struct net_device *dev) static void uml_net_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRIVER_NAME); - strcpy(info->version, "42"); + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, "42", sizeof(info->version)); } static const struct ethtool_ops uml_net_ethtool_ops = { diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 9542e1644a5c..177cf7664726 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -1317,11 +1317,13 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev, struct nes_vnic *nesvnic = netdev_priv(netdev); struct nes_adapter *nesadapter = nesvnic->nesdev->nesadapter; - strcpy(drvinfo->driver, DRV_NAME); - strcpy(drvinfo->bus_info, pci_name(nesvnic->nesdev->pcidev)); - sprintf(drvinfo->fw_version, "%u.%u", nesadapter->firmware_version>>16, - nesadapter->firmware_version & 0x000000ff); - strcpy(drvinfo->version, DRV_VERSION); + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->bus_info, pci_name(nesvnic->nesdev->pcidev), + sizeof(drvinfo->bus_info)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%u.%u", nesadapter->firmware_version >> 16, + nesadapter->firmware_version & 0x000000ff); + strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); drvinfo->testinfo_len = 0; drvinfo->eedump_len = 0; drvinfo->regdump_len = 0; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index 29bc7b5724ac..ca131335417b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -39,7 +39,7 @@ static void ipoib_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, "ipoib", sizeof(drvinfo->driver) - 1); + strlcpy(drvinfo->driver, "ipoib", sizeof(drvinfo->driver)); } static int ipoib_get_coalesce(struct net_device *dev, diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c4952985c084..564cf4231f48 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4330,11 +4330,12 @@ void bond_set_mode_ops(struct bonding *bond, int mode) } static void bond_ethtool_get_drvinfo(struct net_device *bond_dev, - struct ethtool_drvinfo *drvinfo) + struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, DRV_NAME, 32); - strncpy(drvinfo->version, DRV_VERSION, 32); - snprintf(drvinfo->fw_version, 32, "%d", BOND_ABI_VERSION); + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d", + BOND_ABI_VERSION); } static const struct ethtool_ops bond_ethtool_ops = { diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 021d69c5d9bc..29e272cc7a98 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1448,10 +1448,10 @@ static int e100_set_settings(struct net_device *dev, static void e100_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, "ETRAX 100LX", sizeof(info->driver) - 1); - strncpy(info->version, "$Revision: 1.31 $", sizeof(info->version) - 1); - strncpy(info->fw_version, "N/A", sizeof(info->fw_version) - 1); - strncpy(info->bus_info, "N/A", sizeof(info->bus_info) - 1); + strlcpy(info->driver, "ETRAX 100LX", sizeof(info->driver)); + strlcpy(info->version, "$Revision: 1.31 $", sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); } static int e100_nway_reset(struct net_device *dev) diff --git a/drivers/net/ethernet/3com/3c501.c b/drivers/net/ethernet/3com/3c501.c index 2038eaabaea4..9abd9a738f8e 100644 --- a/drivers/net/ethernet/3com/3c501.c +++ b/drivers/net/ethernet/3com/3c501.c @@ -823,9 +823,10 @@ static void set_multicast_list(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); } static u32 netdev_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 633c709b9d99..f36ff99fd394 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -1161,8 +1161,8 @@ el3_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } static int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c index 59e1e001bc3f..94c656f5a05d 100644 --- a/drivers/net/ethernet/3com/3c515.c +++ b/drivers/net/ethernet/3com/3c515.c @@ -1542,9 +1542,10 @@ static void set_rx_mode(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); } static u32 netdev_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/8390/3c503.c b/drivers/net/ethernet/8390/3c503.c index 49d76bd0dc86..0e9afe7b61a4 100644 --- a/drivers/net/ethernet/8390/3c503.c +++ b/drivers/net/ethernet/8390/3c503.c @@ -695,9 +695,10 @@ el2_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); } static const struct ethtool_ops netdev_ethtool_ops = { diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 70dba5d01ad3..7eeddf01307f 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -469,9 +469,9 @@ static void ax_get_drvinfo(struct net_device *dev, { struct platform_device *pdev = to_platform_device(dev->dev.parent); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pdev->name); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pdev->name, sizeof(info->bus_info)); } static int ax_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 8c3c9cfbf864..c7a83f6f2382 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -498,10 +498,10 @@ bfin_mac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) static void bfin_mac_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, KBUILD_MODNAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->fw_version, "N/A"); - strcpy(info->bus_info, dev_name(&dev->dev)); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); } static void bfin_mac_ethtool_getwol(struct net_device *dev, diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index aa53115bb38b..480662ba5227 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1127,10 +1127,11 @@ static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in { struct greth_private *greth = netdev_priv(dev); - strncpy(info->driver, dev_driver_string(greth->dev), 32); - strncpy(info->version, "revision: 1.0", 32); - strncpy(info->bus_info, greth->dev->bus->name, 32); - strncpy(info->fw_version, "N/A", 32); + strlcpy(info->driver, dev_driver_string(greth->dev), + sizeof(info->driver)); + strlcpy(info->version, "revision: 1.0", sizeof(info->version)); + strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); info->eedump_len = 0; info->regdump_len = sizeof(struct greth_regs); } diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 2ea221ed4777..65b865a0cc78 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -587,10 +587,10 @@ au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct au1000_private *aup = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - info->fw_version[0] = '\0'; - sprintf(info->bus_info, "%s %d", DRV_NAME, aup->mac_id); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME, + aup->mac_id); info->regdump_len = 0; } diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index c2d696c88e46..6a40290d3727 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -1284,8 +1284,8 @@ static void lance_free_hwresources(struct lance_private *lp) /* Ethtool support... */ static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "sunlance"); - strcpy(info->version, "2.02"); + strlcpy(info->driver, "sunlance", sizeof(info->driver)); + strlcpy(info->version, "2.02", sizeof(info->version)); } static const struct ethtool_ops sparc_lance_ethtool_ops = { diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 39387d67b722..d8a151046728 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1227,10 +1227,11 @@ static const u32 unused_mib_regs[] = { static void bcm_enet_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, bcm_enet_driver_name, 32); - strncpy(drvinfo->version, bcm_enet_driver_version, 32); - strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, "bcm63xx", 32); + strlcpy(drvinfo->driver, bcm_enet_driver_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, bcm_enet_driver_version, + sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); drvinfo->n_stats = BCM_ENET_STATS_LEN; } diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 78c55213eaf7..354cbb78ed50 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -710,8 +710,8 @@ static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_MODULE_NAME); - strcpy(info->version, DRV_MODULE_VERSION); + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index c73472c369cd..8cdf02503d13 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -434,9 +434,10 @@ static void dm9000_get_drvinfo(struct net_device *dev, { board_info_t *dm = to_dm9000_board(dev); - strcpy(info->driver, CARDNAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, to_platform_device(dm->dev)->name); + strlcpy(info->driver, CARDNAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, to_platform_device(dm->dev)->name, + sizeof(info->bus_info)); } static u32 dm9000_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/dec/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c index 9f992b95eddc..501517226f63 100644 --- a/drivers/net/ethernet/dec/ewrk3.c +++ b/drivers/net/ethernet/dec/ewrk3.c @@ -1506,10 +1506,10 @@ static void ewrk3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in { int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->fw_version, "%d", fwrev); - strcpy(info->bus_info, "N/A"); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->fw_version, sizeof(info->fw_version), "%d", fwrev); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); info->eedump_len = EEPROM_MAX; } diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 1d342d37915c..110d26f4c602 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -1156,9 +1156,10 @@ set_multicast (struct net_device *dev) static void rio_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = netdev_priv(dev); - strcpy(info->driver, "dl2k"); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(np->pdev)); + + strlcpy(info->driver, "dl2k", sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(np->pdev), sizeof(info->bus_info)); } static int rio_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 00454a10f88d..76b302f30c87 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -183,12 +183,12 @@ static void be_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, DRV_VER, sizeof(drvinfo->version)); - strncpy(drvinfo->fw_version, adapter->fw_ver, FW_VER_LEN); - if (memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN) != 0) { - strcat(drvinfo->fw_version, " ["); - strcat(drvinfo->fw_version, fw_on_flash); - strcat(drvinfo->fw_version, "]"); - } + if (!memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN)) + strlcpy(drvinfo->fw_version, adapter->fw_ver, + sizeof(drvinfo->fw_version)); + else + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%s [%s]", adapter->fw_ver, fw_on_flash); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 74d749e29aab..96454b5fca63 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -955,9 +955,9 @@ static int ftgmac100_mdiobus_reset(struct mii_bus *bus) static void ftgmac100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, dev_name(&netdev->dev)); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } static int ftgmac100_get_settings(struct net_device *netdev, diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index b901a01e3fa5..b5ea8fbd8a76 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -820,9 +820,9 @@ static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg, static void ftmac100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, dev_name(&netdev->dev)); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } static int ftmac100_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index a3793190437f..0a9748b64c24 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1168,9 +1168,10 @@ static void fec_enet_get_drvinfo(struct net_device *ndev, { struct fec_enet_private *fep = netdev_priv(ndev); - strcpy(info->driver, fep->pdev->dev.driver->name); - strcpy(info->version, "Revision: 1.0"); - strcpy(info->bus_info, dev_name(&ndev->dev)); + strlcpy(info->driver, fep->pdev->dev.driver->name, + sizeof(info->driver)); + strlcpy(info->version, "Revision: 1.0", sizeof(info->version)); + strlcpy(info->bus_info, dev_name(&ndev->dev), sizeof(info->bus_info)); } static const struct ethtool_ops fec_enet_ethtool_ops = { diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index e9879c5af7ba..46df28893c10 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -888,8 +888,8 @@ static struct net_device_stats *fs_enet_get_stats(struct net_device *dev) static void fs_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_MODULE_NAME); - strcpy(info->version, DRV_MODULE_VERSION); + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static int fs_get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 22eabc13ca99..1b6a67cf8bf6 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -649,8 +649,6 @@ struct gfar_extra_stats { /* Number of stats in the stats structure (ignore car and cam regs)*/ #define GFAR_STATS_LEN (GFAR_RMON_LEN + GFAR_EXTRA_STATS_LEN) -#define GFAR_INFOSTR_LEN 32 - struct gfar_stats { u64 extra[GFAR_EXTRA_STATS_LEN]; u64 rmon[GFAR_RMON_LEN]; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index ab6762caa957..d0fe53c5b297 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -184,10 +184,11 @@ static int gfar_sset_count(struct net_device *dev, int sset) static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN); - strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN); - strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN); - strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN); + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, gfar_driver_version, + sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, "N/A", sizeof(drvinfo->bus_info)); drvinfo->regdump_len = 0; drvinfo->eedump_len = 0; } diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 37b035306013..1ebf7128ec04 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -350,10 +350,10 @@ static void uec_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, DRV_NAME, 32); - strncpy(drvinfo->version, DRV_VERSION, 32); - strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, "QUICC ENGINE", 32); + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info)); drvinfo->eedump_len = 0; drvinfo->regdump_len = uec_get_regs_len(netdev); } diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c index 6a5c21b82c51..6d000d678a58 100644 --- a/drivers/net/ethernet/i825xx/3c505.c +++ b/drivers/net/ethernet/i825xx/3c505.c @@ -1138,9 +1138,10 @@ static struct net_device_stats *elp_get_stats(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); } static u32 netdev_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/i825xx/3c507.c b/drivers/net/ethernet/i825xx/3c507.c index e8984b059905..13983ee6bcf8 100644 --- a/drivers/net/ethernet/i825xx/3c507.c +++ b/drivers/net/ethernet/i825xx/3c507.c @@ -888,9 +888,10 @@ static void el16_rx(struct net_device *dev) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); } static u32 netdev_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 256bdb8e1994..4989481c19f0 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2190,11 +2190,10 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, { struct emac_instance *dev = netdev_priv(ndev); - strcpy(info->driver, "ibm_emac"); - strcpy(info->version, DRV_VERSION); - info->fw_version[0] = '\0'; - sprintf(info->bus_info, "PPC 4xx EMAC-%d %s", - dev->cell_index, dev->ofdev->dev.of_node->full_name); + strlcpy(info->driver, "ibm_emac", sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s", + dev->cell_index, dev->ofdev->dev.of_node->full_name); info->regdump_len = emac_ethtool_get_regs_len(ndev); } diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index f2fdbb79837e..4181f6e99c00 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -722,9 +722,8 @@ static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, ibmveth_driver_name, sizeof(info->driver) - 1); - strncpy(info->version, ibmveth_driver_version, - sizeof(info->version) - 1); + strlcpy(info->driver, ibmveth_driver_name, sizeof(info->driver)); + strlcpy(info->version, ibmveth_driver_version, sizeof(info->version)); } static netdev_features_t ibmveth_fix_features(struct net_device *dev, diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index bc58f1dc22f5..5409fe876a44 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -695,9 +695,9 @@ static void netdev_get_drvinfo(struct net_device *dev, { struct korina_private *lp = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, lp->dev->name); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, lp->dev->name, sizeof(info->bus_info)); } static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index cd3d2c09cdd0..8ead46adc21e 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -302,9 +302,9 @@ ltq_etop_hw_init(struct net_device *dev) static void ltq_etop_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "Lantiq ETOP"); - strcpy(info->bus_info, "internal"); - strcpy(info->version, DRV_VERSION); + strlcpy(info->driver, "Lantiq ETOP", sizeof(info->driver)); + strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } static int diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index aa037ca314e7..c7f2fa60fe6f 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1443,10 +1443,10 @@ static int pxa168_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static void pxa168_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, DRIVER_NAME, 32); - strncpy(info->version, DRIVER_VERSION, 32); - strncpy(info->fw_version, "N/A", 32); - strncpy(info->bus_info, "N/A", 32); + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); } static const struct ethtool_ops pxa168_ethtool_ops = { diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index 92dd72d3f9de..f8f073880f84 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -82,9 +82,9 @@ static void vxge_ethtool_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct vxgedev *vdev = netdev_priv(dev); - strlcpy(info->driver, VXGE_DRIVER_NAME, sizeof(VXGE_DRIVER_NAME)); - strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION)); - strlcpy(info->fw_version, vdev->fw_version, VXGE_HW_FW_STRLEN); + strlcpy(info->driver, VXGE_DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, vdev->fw_version, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(vdev->pdev), sizeof(info->bus_info)); info->regdump_len = sizeof(struct vxge_hw_vpath_reg) * vdev->no_of_vpath; diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index cbd6a529d0c0..162da8975b05 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -878,8 +878,8 @@ static int w90p910_ether_ioctl(struct net_device *dev, static void w90p910_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_MODULE_NAME); - strcpy(info->version, DRV_MODULE_VERSION); + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static int w90p910_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 3466ca1e8f6c..6fda51ebcc76 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1239,9 +1239,10 @@ static int lpc_eth_open(struct net_device *ndev) static void lpc_eth_ethtool_getdrvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strcpy(info->driver, MODNAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, dev_name(ndev->dev.parent)); + strlcpy(info->driver, MODNAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(ndev->dev.parent), + sizeof(info->bus_info)); } static u32 lpc_eth_ethtool_getmsglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index b4a842c3b5c4..921729f9c85c 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1350,10 +1350,10 @@ static void octeon_mgmt_poll_controller(struct net_device *netdev) static void octeon_mgmt_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strncpy(info->driver, DRV_NAME, sizeof(info->driver)); - strncpy(info->version, DRV_VERSION, sizeof(info->version)); - strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); - strncpy(info->bus_info, "N/A", sizeof(info->bus_info)); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); info->n_stats = 0; info->testinfo_len = 0; info->regdump_len = 0; diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index bf829ee30077..cac33e5f9bc2 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -1808,9 +1808,10 @@ static int check_if_running(struct net_device *dev) static void hamachi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct hamachi_private *np = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(np->pci_dev)); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static int hamachi_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index fbaed4fa72fa..d28593b1fc3e 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -1326,9 +1326,10 @@ static void set_rx_mode(struct net_device *dev) static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct yellowfin_private *np = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(np->pci_dev)); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } static const struct ethtool_ops ethtool_ops = { diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index 63c13125db6c..ba0be4b943dd 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -957,9 +957,9 @@ static void netdev_get_drvinfo(struct net_device *dev, { struct r6040_private *rp = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(rp->pdev)); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(rp->pdev), sizeof(info->bus_info)); } static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index dc171b4961e4..7ed08c32a9c5 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1565,9 +1565,9 @@ static void ioc3_get_drvinfo (struct net_device *dev, { struct ioc3_private *ip = netdev_priv(dev); - strcpy (info->driver, IOC3_NAME); - strcpy (info->version, IOC3_VERSION); - strcpy (info->bus_info, pci_name(ip->pdev)); + strlcpy(info->driver, IOC3_NAME, sizeof(info->driver)); + strlcpy(info->version, IOC3_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(ip->pdev), sizeof(info->bus_info)); } static int ioc3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 59a6f88da867..1538d5442bab 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -1522,9 +1522,10 @@ smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) static void smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, CARDNAME, sizeof(info->driver)); - strncpy(info->version, version, sizeof(info->version)); - strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); + strlcpy(info->driver, CARDNAME, sizeof(info->driver)); + strlcpy(info->version, version, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); } static int smc911x_ethtool_nwayreset(struct net_device *dev) diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index a670d23d9340..591650a8de38 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -1597,9 +1597,10 @@ smc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) static void smc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, CARDNAME, sizeof(info->driver)); - strncpy(info->version, version, sizeof(info->version)); - strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info)); + strlcpy(info->driver, CARDNAME, sizeof(info->driver)); + strlcpy(info->version, version, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); } static int smc_ethtool_nwayreset(struct net_device *dev) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1372ce210b58..d1ac39c1b05d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -210,8 +210,7 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev, strlcpy(info->driver, MAC100_ETHTOOL_NAME, sizeof(info->driver)); - strcpy(info->version, DRV_MODULE_VERSION); - info->fw_version[0] = '\0'; + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static int stmmac_ethtool_getsettings(struct net_device *dev, diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index be82f6d13c51..5fafca065305 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -1042,8 +1042,8 @@ static void bigmac_set_multicast(struct net_device *dev) /* Ethtool support... */ static void bigmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "sunbmac"); - strcpy(info->version, "2.0"); + strlcpy(info->driver, "sunbmac", sizeof(info->driver)); + strlcpy(info->version, "2.0", sizeof(info->version)); } static u32 bigmac_get_link(struct net_device *dev) diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index 1dcee6915843..49bf3e2eb652 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -685,13 +685,14 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) struct sunqe *qep = netdev_priv(dev); struct platform_device *op; - strcpy(info->driver, "sunqe"); - strcpy(info->version, "3.0"); + strlcpy(info->driver, "sunqe", sizeof(info->driver)); + strlcpy(info->version, "3.0", sizeof(info->version)); op = qep->op; regs = of_get_property(op->dev.of_node, "reg", NULL); if (regs) - sprintf(info->bus_info, "SBUS:%d", regs->which_io); + snprintf(info->bus_info, sizeof(info->bus_info), "SBUS:%d", + regs->which_io); } diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index e1b895530827..1cf767eb3ed5 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -882,8 +882,8 @@ static int vnet_set_mac_addr(struct net_device *dev, void *p) static void vnet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, DRV_MODULE_NAME); - strcpy(info->version, DRV_MODULE_VERSION); + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); } static u32 vnet_get_msglevel(struct net_device *dev) diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 1e4d743ff03e..e15cc71b826d 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2179,10 +2179,10 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct bdx_priv *priv = netdev_priv(netdev); - strlcat(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); - strlcat(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); - strlcat(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strlcat(drvinfo->bus_info, pci_name(priv->pdev), + strlcpy(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, pci_name(priv->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index d9625f62b026..70d1920cac97 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -904,10 +904,9 @@ static int cpmac_set_ringparam(struct net_device *dev, static void cpmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "cpmac"); - strcpy(info->version, CPMAC_VERSION); - info->fw_version[0] = '\0'; - sprintf(info->bus_info, "%s", "cpmac"); + strlcpy(info->driver, "cpmac", sizeof(info->driver)); + strlcpy(info->version, CPMAC_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac"); info->regdump_len = 0; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 40aff684aa23..bea736b8c3ec 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -944,9 +944,10 @@ static void cpsw_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { struct cpsw_priv *priv = netdev_priv(ndev); - strcpy(info->driver, "TI CPSW Driver v1.0"); - strcpy(info->version, "1.0"); - strcpy(info->bus_info, priv->pdev->name); + + strlcpy(info->driver, "TI CPSW Driver v1.0", sizeof(info->driver)); + strlcpy(info->version, "1.0", sizeof(info->version)); + strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); } static u32 cpsw_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 6841a22c8621..6621ae3a98d9 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -480,8 +480,8 @@ static void emac_dump_regs(struct emac_priv *priv) static void emac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - strcpy(info->driver, emac_version_string); - strcpy(info->version, EMAC_MODULE_VERSION); + strlcpy(info->driver, emac_version_string, sizeof(info->driver)); + strlcpy(info->version, EMAC_MODULE_VERSION, sizeof(info->version)); } /** diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index e321d0b6fc88..445c0595c997 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1226,8 +1226,8 @@ int gelic_net_open(struct net_device *netdev) void gelic_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } static int gelic_ether_get_settings(struct net_device *netdev, diff --git a/drivers/net/ethernet/toshiba/spider_net_ethtool.c b/drivers/net/ethernet/toshiba/spider_net_ethtool.c index 9c288cd7d171..ffe519382e11 100644 --- a/drivers/net/ethernet/toshiba/spider_net_ethtool.c +++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c @@ -72,11 +72,13 @@ spider_net_ethtool_get_drvinfo(struct net_device *netdev, card = netdev_priv(netdev); /* clear and fill out info */ - memset(drvinfo, 0, sizeof(struct ethtool_drvinfo)); - strncpy(drvinfo->driver, spider_net_driver_name, 32); - strncpy(drvinfo->version, VERSION, 32); - strcpy(drvinfo->fw_version, "no information"); - strncpy(drvinfo->bus_info, pci_name(card->pdev), 32); + strlcpy(drvinfo->driver, spider_net_driver_name, + sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, VERSION, sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "no information", + sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, pci_name(card->pdev), + sizeof(drvinfo->bus_info)); } static void diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 9819349eaa1e..23a789e86de4 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -1976,9 +1976,10 @@ tc35815_set_multicast_list(struct net_device *dev) static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct tc35815_local *lp = netdev_priv(dev); - strcpy(info->driver, MODNAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(lp->pci_dev)); + + strlcpy(info->driver, MODNAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(lp->pci_dev), sizeof(info->bus_info)); } static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d9f69b82cc4f..d3b3571b6013 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1124,9 +1124,8 @@ static int axienet_ethtools_set_settings(struct net_device *ndev, static void axienet_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { - memset(ed, 0, sizeof(struct ethtool_drvinfo)); - strcpy(ed->driver, DRIVER_NAME); - strcpy(ed->version, DRIVER_VERSION); + strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); + strlcpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); ed->regdump_len = sizeof(u32) * AXIENET_REGS_N; } diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index 94a1f94f74b8..98e09d0d3ce2 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1412,7 +1412,8 @@ static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { strlcpy(info->driver, "xirc2ps_cs", sizeof(info->driver)); - sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); + snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", + dev->base_addr); } static const struct ethtool_ops netdev_ethtool_ops = { diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index d3ebb73277be..a4be1ad886c5 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -977,11 +977,12 @@ static void ixp4xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct port *port = netdev_priv(dev); - strcpy(info->driver, DRV_NAME); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); snprintf(info->fw_version, sizeof(info->fw_version), "%u:%u:%u:%u", port->firmware[0], port->firmware[1], port->firmware[2], port->firmware[3]); - strcpy(info->bus_info, "internal"); + strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); } static int ixp4xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f825a629a699..a9975c7e6461 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -304,9 +304,9 @@ int netvsc_recv_callback(struct hv_device *device_obj, static void netvsc_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { - strcpy(info->driver, KBUILD_MODNAME); - strcpy(info->version, HV_DRV_VERSION); - strcpy(info->fw_version, "N/A"); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->version, HV_DRV_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } static int netvsc_change_mtu(struct net_device *ndev, int mtu) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 0ae45182f40d..1047e5875801 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -585,8 +585,8 @@ static int macvlan_fdb_del(struct ndmsg *ndm, static void macvlan_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - snprintf(drvinfo->driver, 32, "macvlan"); - snprintf(drvinfo->version, 32, "0.1"); + strlcpy(drvinfo->driver, "macvlan", sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, "0.1", sizeof(drvinfo->version)); } static int macvlan_ethtool_get_settings(struct net_device *dev, diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index d8b9b1e8ee02..f433b594388e 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -410,10 +410,10 @@ static void rionet_get_drvinfo(struct net_device *ndev, { struct rionet_private *rnet = netdev_priv(ndev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->fw_version, "n/a"); - strcpy(info->bus_info, rnet->mport->name); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "n/a", sizeof(info->fw_version)); + strlcpy(info->bus_info, rnet->mport->name, sizeof(info->bus_info)); } static u32 rionet_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 50d167330d38..19bc23f20526 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -510,8 +510,8 @@ void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { /* Inherit standard device info */ usbnet_get_drvinfo(net, info); - strncpy (info->driver, DRIVER_NAME, sizeof info->driver); - strncpy (info->version, DRIVER_VERSION, sizeof info->version); + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); info->eedump_len = AX_EEPROM_LEN; } diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 18d9579123ea..8d5cac2d8e33 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -685,9 +685,9 @@ static void catc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct catc *catc = netdev_priv(dev); - strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - usb_make_path (catc->usbdev, info->bus_info, sizeof info->bus_info); + strlcpy(info->driver, driver_name, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); + usb_make_path(catc->usbdev, info->bus_info, sizeof(info->bus_info)); } static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 71b6e92b8e9b..016aa6fe6acb 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -65,9 +65,9 @@ cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { struct usbnet *dev = netdev_priv(net); - strncpy(info->driver, dev->driver_name, sizeof(info->driver)); - strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); - strncpy(info->fw_version, dev->driver_info->description, + strlcpy(info->driver, dev->driver_name, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, dev->driver_info->description, sizeof(info->fw_version)); usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); } diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index a0b5807b30d4..3c02f950a3d2 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1074,8 +1074,9 @@ static void pegasus_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { pegasus_t *pegasus = netdev_priv(dev); - strncpy(info->driver, driver_name, sizeof(info->driver) - 1); - strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); + + strlcpy(info->driver, driver_name, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); usb_make_path(pegasus->usb, info->bus_info, sizeof(info->bus_info)); } diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 5f39a3b225ef..a491d3a95393 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -776,9 +776,9 @@ static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinf { rtl8150_t *dev = netdev_priv(netdev); - strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - usb_make_path(dev->udev, info->bus_info, sizeof info->bus_info); + strlcpy(info->driver, driver_name, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); + usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); } static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index 18dd4257ab17..453aa6c1277c 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -598,8 +598,8 @@ static void sierra_net_get_drvinfo(struct net_device *net, { /* Inherit standard device info */ usbnet_get_drvinfo(net, info); - strncpy(info->driver, driver_name, sizeof info->driver); - strncpy(info->version, DRIVER_VERSION, sizeof info->version); + strlcpy(info->driver, driver_name, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); } static u32 sierra_net_get_link(struct net_device *net) diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 587a218b2345..8c9fa4b41d78 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -207,7 +207,7 @@ vmxnet3_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), - ETHTOOL_BUSINFO_LEN); + sizeof(drvinfo->bus_info)); drvinfo->n_stats = vmxnet3_get_sset_count(netdev, ETH_SS_STATS); drvinfo->testinfo_len = 0; drvinfo->eedump_len = 0; diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index 1d76ae855f07..cedd4d30d996 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -596,12 +596,12 @@ static void i2400m_get_drvinfo(struct net_device *net_dev, { struct i2400m *i2400m = net_dev_to_i2400m(net_dev); - strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1); - strncpy(info->fw_version, - i2400m->fw_name ? : "", sizeof(info->fw_version) - 1); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->fw_version, i2400m->fw_name ? : "", + sizeof(info->fw_version)); if (net_dev->dev.parent) - strncpy(info->bus_info, dev_name(net_dev->dev.parent), - sizeof(info->bus_info) - 1); + strlcpy(info->bus_info, dev_name(net_dev->dev.parent), + sizeof(info->bus_info)); } static const struct ethtool_ops i2400m_ethtool_ops = { diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 080f36303a4f..cd15a93d9084 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -346,9 +346,9 @@ static void i2400mu_get_drvinfo(struct net_device *net_dev, struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); struct usb_device *udev = i2400mu->usb_dev; - strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1); - strncpy(info->fw_version, - i2400m->fw_name ? : "", sizeof(info->fw_version) - 1); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->fw_version, i2400m->fw_name ? : "", + sizeof(info->fw_version)); usb_make_path(udev, info->bus_info, sizeof(info->bus_info)); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 74a616b4de8e..8c28a1518409 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -395,9 +395,11 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - sprintf(info->driver, KBUILD_MODNAME); - sprintf(info->version, "%lu", drvr->drv_version); - sprintf(info->bus_info, "%s", dev_name(drvr->bus_if->dev)); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + snprintf(info->version, sizeof(info->version), "%lu", + drvr->drv_version); + strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), + sizeof(info->bus_info)); } static const struct ethtool_ops brcmf_ethtool_ops = { diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 638a57f4d8a1..029a7acf7b7a 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5444,17 +5444,14 @@ void qeth_core_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct qeth_card *card = dev->ml_priv; - if (card->options.layer2) - strcpy(info->driver, "qeth_l2"); - else - strcpy(info->driver, "qeth_l3"); - - strcpy(info->version, "1.0"); - strcpy(info->fw_version, card->info.mcl_level); - sprintf(info->bus_info, "%s/%s/%s", - CARD_RDEV_ID(card), - CARD_WDEV_ID(card), - CARD_DDEV_ID(card)); + + strlcpy(info->driver, card->options.layer2 ? "qeth_l2" : "qeth_l3", + sizeof(info->driver)); + strlcpy(info->version, "1.0", sizeof(info->version)); + strlcpy(info->fw_version, card->info.mcl_level, + sizeof(info->fw_version)); + snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", + CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); } EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); diff --git a/drivers/staging/bcm/Bcmnet.c b/drivers/staging/bcm/Bcmnet.c index a3b91c7ee8ff..4e470d4bb4e8 100644 --- a/drivers/staging/bcm/Bcmnet.c +++ b/drivers/staging/bcm/Bcmnet.c @@ -145,8 +145,8 @@ static void bcm_get_drvinfo(struct net_device *dev, struct bcm_interface_adapter *psIntfAdapter = Adapter->pvInterfaceAdapter; struct usb_device *udev = interface_to_usbdev(psIntfAdapter->interface); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u", Adapter->uiFlashLayoutMajorVersion, Adapter->uiFlashLayoutMinorVersion); diff --git a/drivers/staging/ccg/u_ether.c b/drivers/staging/ccg/u_ether.c index d0dabcf015a9..fed78865adc6 100644 --- a/drivers/staging/ccg/u_ether.c +++ b/drivers/staging/ccg/u_ether.c @@ -157,12 +157,12 @@ static int ueth_change_mtu(struct net_device *net, int new_mtu) static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, "g_ether", sizeof p->driver); - strlcpy(p->version, UETH__VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); + strlcpy(p->driver, "g_ether", sizeof(p->driver)); + strlcpy(p->version, UETH__VERSION, sizeof(p->version)); + strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); } /* REVISIT can also support: diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c index 84bbcd48e264..f15059ca3781 100644 --- a/drivers/staging/et131x/et131x.c +++ b/drivers/staging/et131x/et131x.c @@ -3560,15 +3560,15 @@ static void et131x_get_regs(struct net_device *netdev, regs_buff[num++] = readl(&aregs->rxdma.fbr1_min_des); } -#define ET131X_DRVINFO_LEN 32 /* value from ethtool.h */ static void et131x_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct et131x_adapter *adapter = netdev_priv(netdev); - strncpy(info->driver, DRIVER_NAME, ET131X_DRVINFO_LEN); - strncpy(info->version, DRIVER_VERSION, ET131X_DRVINFO_LEN); - strncpy(info->bus_info, pci_name(adapter->pdev), ET131X_DRVINFO_LEN); + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(adapter->pdev), + sizeof(info->bus_info)); } static struct ethtool_ops et131x_ethtool_ops = { diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c index 86a680c09ba2..67ca5c990437 100644 --- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c @@ -2077,11 +2077,12 @@ static void ft1000_get_drvinfo(struct net_device *dev, struct ft1000_info *ft_info; ft_info = netdev_priv(dev); - snprintf(info->driver, 32, "ft1000"); - snprintf(info->bus_info, ETHTOOL_BUSINFO_LEN, "PCMCIA 0x%lx", + strlcpy(info->driver, "ft1000", sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx", dev->base_addr); - snprintf(info->fw_version, 32, "%d.%d.%d.%d", ft_info->DspVer[0], - ft_info->DspVer[1], ft_info->DspVer[2], ft_info->DspVer[3]); + snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d", + ft_info->DspVer[0], ft_info->DspVer[1], ft_info->DspVer[2], + ft_info->DspVer[3]); } static u32 ft1000_get_link(struct net_device *dev) diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index f15b31b37ca5..83b103091cf2 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -46,9 +46,9 @@ static void cvm_oct_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "cavium-ethernet"); - strcpy(info->version, OCTEON_ETHERNET_VERSION); - strcpy(info->bus_info, "Builtin"); + strlcpy(info->driver, "cavium-ethernet", sizeof(info->driver)); + strlcpy(info->version, OCTEON_ETHERNET_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, "Builtin", sizeof(info->bus_info)); } static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c index 36452fb7cef8..0cfb3ecaadee 100644 --- a/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c @@ -34,9 +34,9 @@ static void rtl819x_ethtool_get_drvinfo(struct net_device *dev, { struct r8192_priv *priv = rtllib_priv(dev); - strcpy(info->driver, DRV_NAME); - strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, pci_name(priv->pdev)); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info)); } static u32 rtl819x_ethtool_get_link(struct net_device *dev) diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c index fb421407e106..235cc2a7ffe6 100644 --- a/drivers/staging/wlags49_h2/wl_netdev.c +++ b/drivers/staging/wlags49_h2/wl_netdev.c @@ -457,17 +457,17 @@ int wl_close( struct net_device *dev ) static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); - strncpy(info->version, DRV_VERSION_STR, sizeof(info->version) - 1); -// strncpy(info.fw_version, priv->fw_name, -// sizeof(info.fw_version) - 1); + strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION_STR, sizeof(info->version)); +// strlcpy(info.fw_version, priv->fw_name, +// sizeof(info.fw_version)); if (dev->dev.parent) { dev_set_name(dev->dev.parent, "%s", info->bus_info); - //strncpy(info->bus_info, dev->dev.parent->bus_id, - // sizeof(info->bus_info) - 1); + //strlcpy(info->bus_info, dev->dev.parent->bus_id, + // sizeof(info->bus_info)); } else { - snprintf(info->bus_info, sizeof(info->bus_info) - 1, + snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA FIXME"); // "PCMCIA 0x%lx", priv->hw.iobase); } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 4ec3c0d7a18b..a0aa721d8b21 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -159,12 +159,12 @@ static int ueth_change_mtu(struct net_device *net, int new_mtu) static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { - struct eth_dev *dev = netdev_priv(net); + struct eth_dev *dev = netdev_priv(net); - strlcpy(p->driver, "g_ether", sizeof p->driver); - strlcpy(p->version, UETH__VERSION, sizeof p->version); - strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); - strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info); + strlcpy(p->driver, "g_ether", sizeof(p->driver)); + strlcpy(p->version, UETH__VERSION, sizeof(p->version)); + strlcpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); } /* REVISIT can also support: diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 4a6d31a082b9..09f9108d4688 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -640,9 +640,9 @@ static int vlan_ethtool_get_settings(struct net_device *dev, static void vlan_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, vlan_fullname); - strcpy(info->version, vlan_version); - strcpy(info->fw_version, "N/A"); + strlcpy(info->driver, vlan_fullname, sizeof(info->driver)); + strlcpy(info->version, vlan_version, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index fa388b2c60d7..90f4049a90dd 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -580,10 +580,10 @@ static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "B.A.T.M.A.N. advanced"); - strcpy(info->version, BATADV_SOURCE_VERSION); - strcpy(info->fw_version, "N/A"); - strcpy(info->bus_info, "batman"); + strlcpy(info->driver, "B.A.T.M.A.N. advanced", sizeof(info->driver)); + strlcpy(info->version, BATADV_SOURCE_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "batman", sizeof(info->bus_info)); } static u32 batadv_get_msglevel(struct net_device *dev) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 38974f70ec7b..e1bc090bc00a 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -184,10 +184,10 @@ static int br_set_mac_address(struct net_device *dev, void *p) static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "bridge"); - strcpy(info->version, BR_VERSION); - strcpy(info->fw_version, "N/A"); - strcpy(info->bus_info, "N/A"); + strlcpy(info->driver, "bridge", sizeof(info->driver)); + strlcpy(info->version, BR_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); } static netdev_features_t br_fix_features(struct net_device *dev, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e32083d5d8f8..f795b0ca7ee6 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -203,10 +203,10 @@ dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static void dsa_slave_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, "dsa", 32); - strncpy(drvinfo->version, dsa_driver_version, 32); - strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, "platform", 32); + strlcpy(drvinfo->driver, "dsa", sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, dsa_driver_version, sizeof(drvinfo->version)); + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); } static int dsa_slave_nway_reset(struct net_device *dev) diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 44cd5c3a4c22..0531de6c7a4a 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -97,7 +97,7 @@ static int internal_dev_stop(struct net_device *netdev) static void internal_dev_getinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - strcpy(info->driver, "openvswitch"); + strlcpy(info->driver, "openvswitch", sizeof(info->driver)); } static const struct ethtool_ops internal_dev_ethtool_ops = { diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index 48c48ffafa1d..e37862f1b127 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -15,10 +15,10 @@ static void cfg80211_get_drvinfo(struct net_device *dev, strlcpy(info->version, init_utsname()->release, sizeof(info->version)); if (wdev->wiphy->fw_version[0]) - strncpy(info->fw_version, wdev->wiphy->fw_version, + strlcpy(info->fw_version, wdev->wiphy->fw_version, sizeof(info->fw_version)); else - strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), sizeof(info->bus_info)); -- cgit v1.2.3 From 9ca1b22d6d228177e6f929f6818a1cd3d5e30c4a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 5 Jan 2013 21:31:18 +0000 Subject: net: splice: avoid high order page splitting splice() can handle pages of any order, but network code tries hard to split them in PAGE_SIZE units. Not quite successfully anyway, as __splice_segment() assumed poff < PAGE_SIZE. This is true for the skb->data part, not necessarily for the fragments. This patch removes this logic to give the pages as they are in the skb. Signed-off-by: Eric Dumazet Cc: Willy Tarreau Signed-off-by: David S. Miller --- net/core/skbuff.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index bc96100fe23c..b03fc0c6a952 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1707,20 +1707,6 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, return false; } -static inline void __segment_seek(struct page **page, unsigned int *poff, - unsigned int *plen, unsigned int off) -{ - unsigned long n; - - *poff += off; - n = *poff / PAGE_SIZE; - if (n) - *page = nth_page(*page, n); - - *poff = *poff % PAGE_SIZE; - *plen -= off; -} - static bool __splice_segment(struct page *page, unsigned int poff, unsigned int plen, unsigned int *off, unsigned int *len, struct sk_buff *skb, @@ -1728,6 +1714,8 @@ static bool __splice_segment(struct page *page, unsigned int poff, struct sock *sk, struct pipe_inode_info *pipe) { + unsigned int flen; + if (!*len) return true; @@ -1738,24 +1726,16 @@ static bool __splice_segment(struct page *page, unsigned int poff, } /* ignore any bits we already processed */ - if (*off) { - __segment_seek(&page, &poff, &plen, *off); - *off = 0; - } - - do { - unsigned int flen = min(*len, plen); + poff += *off; + plen -= *off; + *off = 0; - /* the linear region may spread across several pages */ - flen = min_t(unsigned int, flen, PAGE_SIZE - poff); + flen = min(*len, plen); - if (spd_fill_page(spd, pipe, page, &flen, poff, skb, linear, sk)) - return true; - - __segment_seek(&page, &poff, &plen, flen); - *len -= flen; + if (spd_fill_page(spd, pipe, page, &flen, poff, skb, linear, sk)) + return true; - } while (*len && plen); + *len -= flen; return false; } -- cgit v1.2.3 From 71bcdba06db91ceaaffe019b6c958b5faf06012a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sat, 5 Jan 2013 16:34:51 +0000 Subject: ndisc: Use struct rd_msg for redirect message. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 25 +++++++++++-------------- net/ipv6/route.c | 24 ++++++++++-------------- 2 files changed, 21 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 4c4ccf79d2c5..5733cd27f4a3 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1355,12 +1355,11 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct net_device *dev = skb->dev; struct net *net = dev_net(dev); struct sock *sk = net->ipv6.ndisc_sk; - int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); + int len = sizeof(struct rd_msg); struct inet_peer *peer; struct sk_buff *buff; - struct icmp6hdr *icmph; + struct rd_msg *msg; struct in6_addr saddr_buf; - struct in6_addr *addrp; struct rt6_info *rt; struct dst_entry *dst; struct inet6_dev *idev; @@ -1455,21 +1454,19 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); skb_put(buff, len); - icmph = icmp6_hdr(buff); + msg = (struct rd_msg *)icmp6_hdr(buff); - memset(icmph, 0, sizeof(struct icmp6hdr)); - icmph->icmp6_type = NDISC_REDIRECT; + memset(&msg->icmph, 0, sizeof(struct icmp6hdr)); + msg->icmph.icmp6_type = NDISC_REDIRECT; /* * copy target and destination addresses */ - addrp = (struct in6_addr *)(icmph + 1); - *addrp = *target; - addrp++; - *addrp = ipv6_hdr(skb)->daddr; + msg->target = *target; + msg->dest = ipv6_hdr(skb)->daddr; - opt = (u8*) (addrp + 1); + opt = msg->opt; /* * include target_address option @@ -1490,9 +1487,9 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) memcpy(opt, ipv6_hdr(skb), rd_len - 8); - icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, - len, IPPROTO_ICMPV6, - csum_partial(icmph, len, 0)); + msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, + len, IPPROTO_ICMPV6, + csum_partial(msg, len, 0)); skb_dst_set(buff, dst); rcu_read_lock(); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e229a3bc345d..621b68ecf16f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1705,37 +1705,33 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu struct net *net = dev_net(skb->dev); struct netevent_redirect netevent; struct rt6_info *rt, *nrt = NULL; - const struct in6_addr *target; struct ndisc_options ndopts; - const struct in6_addr *dest; struct neighbour *old_neigh; struct inet6_dev *in6_dev; struct neighbour *neigh; - struct icmp6hdr *icmph; + struct rd_msg *msg; int optlen, on_link; u8 *lladdr; optlen = skb->tail - skb->transport_header; - optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); + optlen -= sizeof(*msg); if (optlen < 0) { net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); return; } - icmph = icmp6_hdr(skb); - target = (const struct in6_addr *) (icmph + 1); - dest = target + 1; + msg = (struct rd_msg *)icmp6_hdr(skb); - if (ipv6_addr_is_multicast(dest)) { + if (ipv6_addr_is_multicast(&msg->dest)) { net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); return; } on_link = 0; - if (ipv6_addr_equal(dest, target)) { + if (ipv6_addr_equal(&msg->dest, &msg->target)) { on_link = 1; - } else if (ipv6_addr_type(target) != + } else if (ipv6_addr_type(&msg->target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); return; @@ -1752,7 +1748,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu * first-hop router for the specified ICMP Destination Address. */ - if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { + if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); return; } @@ -1779,7 +1775,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu */ dst_confirm(&rt->dst); - neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); + neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); if (!neigh) return; @@ -1799,7 +1795,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu NEIGH_UPDATE_F_ISROUTER)) ); - nrt = ip6_rt_copy(rt, dest); + nrt = ip6_rt_copy(rt, &msg->dest); if (!nrt) goto out; @@ -1817,7 +1813,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu netevent.old_neigh = old_neigh; netevent.new = &nrt->dst; netevent.new_neigh = neigh; - netevent.daddr = dest; + netevent.daddr = &msg->dest; call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); if (rt->rt6i_flags & RTF_CACHE) { -- cgit v1.2.3 From 5d134f1c1f36166e8a738de92c4d2f4c262ff91b Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sat, 5 Jan 2013 16:10:48 +0000 Subject: tcp: make sysctl_tcp_ecn namespace aware As per suggestion from Eric Dumazet this patch makes tcp_ecn sysctl namespace aware. The reason behind this patch is to ease the testing of ecn problems on the internet and allows applications to tune their own use of ecn. Cc: Eric Dumazet Cc: David Miller Cc: Stephen Hemminger Signed-off-by: Hannes Frederic Sowa Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 ++ include/net/tcp.h | 9 +++++---- net/ipv4/syncookies.c | 7 ++++--- net/ipv4/sysctl_net_ipv4.c | 16 +++++++++------- net/ipv4/tcp_input.c | 2 -- net/ipv4/tcp_ipv4.c | 3 ++- net/ipv4/tcp_output.c | 2 +- net/ipv6/syncookies.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 9 files changed, 25 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2ae2b8372cfd..9b78862014a4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -61,6 +61,8 @@ struct netns_ipv4 { int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; + int sysctl_tcp_ecn; + kgid_t sysctl_ping_group_range[2]; long sysctl_tcp_mem[3]; diff --git a/include/net/tcp.h b/include/net/tcp.h index aed42c785153..614af8b7758e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -266,7 +266,6 @@ extern int sysctl_tcp_abort_on_overflow; extern int sysctl_tcp_max_orphans; extern int sysctl_tcp_fack; extern int sysctl_tcp_reordering; -extern int sysctl_tcp_ecn; extern int sysctl_tcp_dsack; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; @@ -504,7 +503,8 @@ static inline __u32 cookie_v4_init_sequence(struct sock *sk, #endif extern __u32 cookie_init_timestamp(struct request_sock *req); -extern bool cookie_check_timestamp(struct tcp_options_received *opt, bool *); +extern bool cookie_check_timestamp(struct tcp_options_received *opt, + struct net *net, bool *ecn_ok); /* From net/ipv6/syncookies.c */ extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); @@ -728,11 +728,12 @@ struct tcp_skb_cb { * notifications, we disable TCP ECN negociation. */ static inline void -TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb) +TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb, + struct net *net) { const struct tcphdr *th = tcp_hdr(skb); - if (sysctl_tcp_ecn && th->ece && th->cwr && + if (net->ipv4.sysctl_tcp_ecn && th->ece && th->cwr && INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield)) inet_rsk(req)->ecn_ok = 1; } diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index b236ef04914f..ef54377fb11c 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -232,7 +232,8 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, * * return false if we decode an option that should not be. */ -bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, bool *ecn_ok) +bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, + struct net *net, bool *ecn_ok) { /* echoed timestamp, lowest bits contain options */ u32 options = tcp_opt->rcv_tsecr & TSMASK; @@ -247,7 +248,7 @@ bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, bool *ecn_ok) tcp_opt->sack_ok = (options & (1 << 4)) ? TCP_SACK_SEEN : 0; *ecn_ok = (options >> 5) & 1; - if (*ecn_ok && !sysctl_tcp_ecn) + if (*ecn_ok && !net->ipv4.sysctl_tcp_ecn) return false; if (tcp_opt->sack_ok && !sysctl_tcp_sack) @@ -295,7 +296,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, memset(&tcp_opt, 0, sizeof(tcp_opt)); tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); - if (!cookie_check_timestamp(&tcp_opt, &ecn_ok)) + if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; ret = NULL; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 7547a6d238a2..a25e1d286b99 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -537,13 +537,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_ecn", - .data = &sysctl_tcp_ecn, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_dsack", .data = &sysctl_tcp_dsack, @@ -849,6 +842,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = ipv4_ping_group_range, }, + { + .procname = "tcp_ecn", + .data = &init_net.ipv4.sysctl_tcp_ecn, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "tcp_mem", .maxlen = sizeof(init_net.ipv4.sysctl_tcp_mem), @@ -882,6 +882,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) &net->ipv4.sysctl_icmp_ratemask; table[6].data = &net->ipv4.sysctl_ping_group_range; + table[7].data = + &net->ipv4.sysctl_tcp_ecn; /* Don't export sysctls to unprivileged users */ if (net->user_ns != &init_user_ns) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a28e4db8a952..38e11841be09 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,8 +81,6 @@ int sysctl_tcp_sack __read_mostly = 1; int sysctl_tcp_fack __read_mostly = 1; int sysctl_tcp_reordering __read_mostly = TCP_FASTRETRANS_THRESH; EXPORT_SYMBOL(sysctl_tcp_reordering); -int sysctl_tcp_ecn __read_mostly = 2; -EXPORT_SYMBOL(sysctl_tcp_ecn); int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; int sysctl_tcp_adv_win_scale __read_mostly = 1; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 54139fa514e6..c6ce9ca98d23 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1568,7 +1568,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_free; if (!want_cookie || tmp_opt.tstamp_ok) - TCP_ECN_create_request(req, skb); + TCP_ECN_create_request(req, skb, sock_net(sk)); if (want_cookie) { isn = cookie_v4_init_sequence(sk, skb, &req->mss); @@ -2888,6 +2888,7 @@ EXPORT_SYMBOL(tcp_prot); static int __net_init tcp_sk_init(struct net *net) { + net->ipv4.sysctl_tcp_ecn = 2; return 0; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5d451593ef16..667a6adfccf8 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -314,7 +314,7 @@ static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb) struct tcp_sock *tp = tcp_sk(sk); tp->ecn_flags = 0; - if (sysctl_tcp_ecn == 1) { + if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1) { TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR; tp->ecn_flags = TCP_ECN_OK; } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 40161977f7cf..8a0848b60b35 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -179,7 +179,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) memset(&tcp_opt, 0, sizeof(tcp_opt)); tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); - if (!cookie_check_timestamp(&tcp_opt, &ecn_ok)) + if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; ret = NULL; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 93825dd3a7c0..3164ad272a74 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1027,7 +1027,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) treq->rmt_addr = ipv6_hdr(skb)->saddr; treq->loc_addr = ipv6_hdr(skb)->daddr; if (!want_cookie || tmp_opt.tstamp_ok) - TCP_ECN_create_request(req, skb); + TCP_ECN_create_request(req, skb, sock_net(sk)); treq->iif = sk->sk_bound_dev_if; -- cgit v1.2.3 From bb65a9cb953fdfe9c507e8dbb6c4ec2540484bd3 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 28 Dec 2012 16:06:28 +0800 Subject: xfrm: removes a superfluous check and add a statistic Remove the check if x->km.state equal to XFRM_STATE_VALID in xfrm_state_check_expire(), which will be done before call xfrm_state_check_expire(). add a LINUX_MIB_XFRMOUTSTATEINVALID statistic to record the outbound error due to invalid xfrm state. Signed-off-by: Li RongQing Signed-off-by: Steffen Klassert --- include/uapi/linux/snmp.h | 1 + net/xfrm/xfrm_output.c | 6 ++++++ net/xfrm/xfrm_proc.c | 1 + net/xfrm/xfrm_state.c | 3 --- 4 files changed, 8 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index fdfba235f9f1..b49eab89c9fd 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -278,6 +278,7 @@ enum LINUX_MIB_XFRMOUTPOLDEAD, /* XfrmOutPolDead */ LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */ LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/ + LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */ __LINUX_MIB_XFRMMAX }; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 95a338c89f99..3670526e70b9 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -61,6 +61,12 @@ static int xfrm_output_one(struct sk_buff *skb, int err) } spin_lock_bh(&x->lock); + + if (unlikely(x->km.state != XFRM_STATE_VALID)) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID); + goto error_nolock; + } + err = xfrm_state_check_expire(x); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED); diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index d0a1af8ed584..603903853e89 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -43,6 +43,7 @@ static const struct snmp_mib xfrm_mib_list[] = { SNMP_MIB_ITEM("XfrmOutPolDead", LINUX_MIB_XFRMOUTPOLDEAD), SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR), SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR), + SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID), SNMP_MIB_SENTINEL }; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 3459692092ec..05db2362a231 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1370,9 +1370,6 @@ int xfrm_state_check_expire(struct xfrm_state *x) if (!x->curlft.use_time) x->curlft.use_time = get_seconds(); - if (x->km.state != XFRM_STATE_VALID) - return -EINVAL; - if (x->curlft.bytes >= x->lft.hard_byte_limit || x->curlft.packets >= x->lft.hard_packet_limit) { x->km.state = XFRM_STATE_EXPIRED; -- cgit v1.2.3 From c03a14e8db06d6687f470f9769f47aa5764b1fe3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Jan 2013 09:02:08 +0000 Subject: ethtool: consolidate work with ethtool_ops No need to check if ethtool_ops == NULL since it can't be. Use local variable "ops" in functions where it is present instead of dev->ethtool_ops Introduce local variable "ops" in functions where dev->ethtool_ops is used many times. Signed-off-by: Jiri Pirko Reviewed-by: Ben Hutchings Reviewed-by: Flavio Leitner Signed-off-by: David S. Miller --- net/core/ethtool.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/core/ethtool.c b/net/core/ethtool.c index a8705432e4b1..d9d55209db67 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -175,7 +175,7 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset) if (sset == ETH_SS_FEATURES) return ARRAY_SIZE(netdev_features_strings); - if (ops && ops->get_sset_count && ops->get_strings) + if (ops->get_sset_count && ops->get_strings) return ops->get_sset_count(dev, sset); else return -EOPNOTSUPP; @@ -311,7 +311,7 @@ int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { ASSERT_RTNL(); - if (!dev->ethtool_ops || !dev->ethtool_ops->get_settings) + if (!dev->ethtool_ops->get_settings) return -EOPNOTSUPP; memset(cmd, 0, sizeof(struct ethtool_cmd)); @@ -355,7 +355,7 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, memset(&info, 0, sizeof(info)); info.cmd = ETHTOOL_GDRVINFO; - if (ops && ops->get_drvinfo) { + if (ops->get_drvinfo) { ops->get_drvinfo(dev, &info); } else if (dev->dev.parent && dev->dev.parent->driver) { strlcpy(info.bus_info, dev_name(dev->dev.parent), @@ -370,7 +370,7 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, * this method of obtaining string set info is deprecated; * Use ETHTOOL_GSSET_INFO instead. */ - if (ops && ops->get_sset_count) { + if (ops->get_sset_count) { int rc; rc = ops->get_sset_count(dev, ETH_SS_TEST); @@ -383,9 +383,9 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, if (rc >= 0) info.n_priv_flags = rc; } - if (ops && ops->get_regs_len) + if (ops->get_regs_len) info.regdump_len = ops->get_regs_len(dev); - if (ops && ops->get_eeprom_len) + if (ops->get_eeprom_len) info.eedump_len = ops->get_eeprom_len(dev); if (copy_to_user(useraddr, &info, sizeof(info))) @@ -590,13 +590,14 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, struct ethtool_rxnfc rx_rings; u32 user_size, dev_size, i; u32 *indir; + const struct ethtool_ops *ops = dev->ethtool_ops; int ret; - if (!dev->ethtool_ops->get_rxfh_indir_size || - !dev->ethtool_ops->set_rxfh_indir || - !dev->ethtool_ops->get_rxnfc) + if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || + !ops->get_rxnfc) return -EOPNOTSUPP; - dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); + + dev_size = ops->get_rxfh_indir_size(dev); if (dev_size == 0) return -EOPNOTSUPP; @@ -613,7 +614,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, return -ENOMEM; rx_rings.cmd = ETHTOOL_GRXRINGS; - ret = dev->ethtool_ops->get_rxnfc(dev, &rx_rings, NULL); + ret = ops->get_rxnfc(dev, &rx_rings, NULL); if (ret) goto out; @@ -639,7 +640,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, } } - ret = dev->ethtool_ops->set_rxfh_indir(dev, indir); + ret = ops->set_rxfh_indir(dev, indir); out: kfree(indir); @@ -1082,9 +1083,10 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) { struct ethtool_value id; static bool busy; + const struct ethtool_ops *ops = dev->ethtool_ops; int rc; - if (!dev->ethtool_ops->set_phys_id) + if (!ops->set_phys_id) return -EOPNOTSUPP; if (busy) @@ -1093,7 +1095,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) if (copy_from_user(&id, useraddr, sizeof(id))) return -EFAULT; - rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); + rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); if (rc < 0) return rc; @@ -1118,7 +1120,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) i = n; do { rtnl_lock(); - rc = dev->ethtool_ops->set_phys_id(dev, + rc = ops->set_phys_id(dev, (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); rtnl_unlock(); if (rc) @@ -1133,7 +1135,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) dev_put(dev); busy = false; - (void)dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); + (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); return rc; } @@ -1275,7 +1277,7 @@ static int ethtool_get_dump_flag(struct net_device *dev, struct ethtool_dump dump; const struct ethtool_ops *ops = dev->ethtool_ops; - if (!dev->ethtool_ops->get_dump_flag) + if (!ops->get_dump_flag) return -EOPNOTSUPP; if (copy_from_user(&dump, useraddr, sizeof(dump))) @@ -1299,8 +1301,7 @@ static int ethtool_get_dump_data(struct net_device *dev, const struct ethtool_ops *ops = dev->ethtool_ops; void *data = NULL; - if (!dev->ethtool_ops->get_dump_data || - !dev->ethtool_ops->get_dump_flag) + if (!ops->get_dump_data || !ops->get_dump_flag) return -EOPNOTSUPP; if (copy_from_user(&dump, useraddr, sizeof(dump))) @@ -1346,13 +1347,9 @@ static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) info.cmd = ETHTOOL_GET_TS_INFO; if (phydev && phydev->drv && phydev->drv->ts_info) { - err = phydev->drv->ts_info(phydev, &info); - - } else if (dev->ethtool_ops && dev->ethtool_ops->get_ts_info) { - + } else if (ops->get_ts_info) { err = ops->get_ts_info(dev, &info); - } else { info.so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | -- cgit v1.2.3 From 71331da500cc13dc3c7e5db397d553c80b2e9994 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Fri, 28 Dec 2012 12:04:53 +0200 Subject: xfrm_algo: probe asynchronous block ciphers instead of synchronous IPSEC uses block ciphers asynchronous, but probes only for synchronous block ciphers and makes ealg entries only available if synchronous block cipher is found. So with setup, where hardware crypto driver registers asynchronous block ciphers and software crypto module is not build, ealg is not marked as being available. Use crypto_has_ablkcipher instead and remove ASYNC mask. Signed-off-by: Jussi Kivilinna Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_algo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 4ce2d93162c1..f9a549554740 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -700,8 +700,7 @@ void xfrm_probe_algs(void) } for (i = 0; i < ealg_entries(); i++) { - status = crypto_has_blkcipher(ealg_list[i].name, 0, - CRYPTO_ALG_ASYNC); + status = crypto_has_ablkcipher(ealg_list[i].name, 0, 0); if (ealg_list[i].available != status) ealg_list[i].available = status; } -- cgit v1.2.3 From fda55eca5a33f33ffcd4192c6b2d75179714a52c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 7 Jan 2013 09:28:21 +0000 Subject: net: introduce skb_transport_header_was_set() We have skb_mac_header_was_set() helper to tell if mac_header was set on a skb. We would like the same for transport_header. __netif_receive_skb() doesn't reset the transport header if already set by GRO layer. Note that network stacks usually reset the transport header anyway, after pulling the network header, so this change only allows a followup patch to have more precise qdisc pkt_len computation for GSO packets at ingress side. Signed-off-by: Eric Dumazet Cc: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/linux/skbuff.h | 10 ++++++++++ net/core/dev.c | 3 ++- net/core/skbuff.c | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 320e976d5ab8..8b2256e880e0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1492,6 +1492,11 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb, skb->inner_network_header += offset; } +static inline bool skb_transport_header_was_set(const struct sk_buff *skb) +{ + return skb->transport_header != ~0U; +} + static inline unsigned char *skb_transport_header(const struct sk_buff *skb) { return skb->head + skb->transport_header; @@ -1580,6 +1585,11 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb, skb->inner_network_header = skb->data + offset; } +static inline bool skb_transport_header_was_set(const struct sk_buff *skb) +{ + return skb->transport_header != NULL; +} + static inline unsigned char *skb_transport_header(const struct sk_buff *skb) { return skb->transport_header; diff --git a/net/core/dev.c b/net/core/dev.c index a51ccf46e8b7..2e2448201a76 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3352,7 +3352,8 @@ static int __netif_receive_skb(struct sk_buff *skb) orig_dev = skb->dev; skb_reset_network_header(skb); - skb_reset_transport_header(skb); + if (!skb_transport_header_was_set(skb)) + skb_reset_transport_header(skb); skb_reset_mac_len(skb); pt_prev = NULL; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b03fc0c6a952..1e1b9ea0296d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -260,6 +260,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, skb->end = skb->tail + size; #ifdef NET_SKBUFF_DATA_USES_OFFSET skb->mac_header = ~0U; + skb->transport_header = ~0U; #endif /* make sure we initialize shinfo sequentially */ @@ -328,6 +329,7 @@ struct sk_buff *build_skb(void *data, unsigned int frag_size) skb->end = skb->tail + size; #ifdef NET_SKBUFF_DATA_USES_OFFSET skb->mac_header = ~0U; + skb->transport_header = ~0U; #endif /* make sure we initialize shinfo sequentially */ -- cgit v1.2.3 From b7394d2429c198b1da3d46ac39192e891029ec0f Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 7 Jan 2013 20:52:39 +0000 Subject: netpoll: prepare for ipv6 This patch adjusts some struct and functions, to prepare for supporting IPv6. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/netconsole.c | 12 +- include/linux/netpoll.h | 13 +- net/core/netpoll.c | 402 ++++++++++++++++++++++++++--------------------- 3 files changed, 243 insertions(+), 184 deletions(-) (limited to 'net') diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 6989ebe2bc79..998fa0257a92 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -269,12 +269,14 @@ static ssize_t show_remote_port(struct netconsole_target *nt, char *buf) static ssize_t show_local_ip(struct netconsole_target *nt, char *buf) { - return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); + if (!nt->np.ipv6) + return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); } static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf) { - return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); + if (!nt->np.ipv6) + return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); } static ssize_t show_local_mac(struct netconsole_target *nt, char *buf) @@ -410,7 +412,8 @@ static ssize_t store_local_ip(struct netconsole_target *nt, return -EINVAL; } - nt->np.local_ip = in_aton(buf); + if (!strnchr(buf, count, ':')) + nt->np.local_ip.ip = in_aton(buf); return strnlen(buf, count); } @@ -426,7 +429,8 @@ static ssize_t store_remote_ip(struct netconsole_target *nt, return -EINVAL; } - nt->np.remote_ip = in_aton(buf); + if (!strnchr(buf, count, ':')) + nt->np.remote_ip.ip = in_aton(buf); return strnlen(buf, count); } diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 66d5379c305e..f54c3bb6a22b 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -12,13 +12,22 @@ #include #include +union inet_addr { + __u32 all[4]; + __be32 ip; + __be32 ip6[4]; + struct in_addr in; + struct in6_addr in6; +}; + struct netpoll { struct net_device *dev; char dev_name[IFNAMSIZ]; const char *name; void (*rx_hook)(struct netpoll *, int, char *, int); - __be32 local_ip, remote_ip; + union inet_addr local_ip, remote_ip; + bool ipv6; u16 local_port, remote_port; u8 remote_mac[ETH_ALEN]; @@ -33,7 +42,7 @@ struct netpoll_info { spinlock_t rx_lock; struct list_head rx_np; /* netpolls that registered an rx_hook */ - struct sk_buff_head arp_tx; /* list of arp requests to reply to */ + struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */ struct sk_buff_head txq; struct delayed_work tx_work; diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d2bda8eb08ec..6bd073688f68 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -55,7 +55,7 @@ static atomic_t trapped; MAX_UDP_CHUNK) static void zap_completion_queue(void); -static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo); +static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo); static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); @@ -181,13 +181,13 @@ static void poll_napi(struct net_device *dev) } } -static void service_arp_queue(struct netpoll_info *npi) +static void service_neigh_queue(struct netpoll_info *npi) { if (npi) { struct sk_buff *skb; - while ((skb = skb_dequeue(&npi->arp_tx))) - netpoll_arp_reply(skb, npi); + while ((skb = skb_dequeue(&npi->neigh_tx))) + netpoll_neigh_reply(skb, npi); } } @@ -216,14 +216,14 @@ static void netpoll_poll_dev(struct net_device *dev) bond_dev = netdev_master_upper_dev_get_rcu(dev); bond_ni = rcu_dereference_bh(bond_dev->npinfo); - while ((skb = skb_dequeue(&ni->arp_tx))) { + while ((skb = skb_dequeue(&ni->neigh_tx))) { skb->dev = bond_dev; - skb_queue_tail(&bond_ni->arp_tx, skb); + skb_queue_tail(&bond_ni->neigh_tx, skb); } } } - service_arp_queue(ni); + service_neigh_queue(ni); zap_completion_queue(); } @@ -386,7 +386,9 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) static atomic_t ip_ident; udp_len = len + sizeof(*udph); - ip_len = udp_len + sizeof(*iph); + if (!np->ipv6) + ip_len = udp_len + sizeof(*iph); + total_len = ip_len + LL_RESERVED_SPACE(np->dev); skb = find_skb(np, total_len + np->dev->needed_tailroom, @@ -403,34 +405,38 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) udph->source = htons(np->local_port); udph->dest = htons(np->remote_port); udph->len = htons(udp_len); - udph->check = 0; - udph->check = csum_tcpudp_magic(np->local_ip, - np->remote_ip, - udp_len, IPPROTO_UDP, - csum_partial(udph, udp_len, 0)); - if (udph->check == 0) - udph->check = CSUM_MANGLED_0; - - skb_push(skb, sizeof(*iph)); - skb_reset_network_header(skb); - iph = ip_hdr(skb); - - /* iph->version = 4; iph->ihl = 5; */ - put_unaligned(0x45, (unsigned char *)iph); - iph->tos = 0; - put_unaligned(htons(ip_len), &(iph->tot_len)); - iph->id = htons(atomic_inc_return(&ip_ident)); - iph->frag_off = 0; - iph->ttl = 64; - iph->protocol = IPPROTO_UDP; - iph->check = 0; - put_unaligned(np->local_ip, &(iph->saddr)); - put_unaligned(np->remote_ip, &(iph->daddr)); - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - - eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); - skb_reset_mac_header(skb); - skb->protocol = eth->h_proto = htons(ETH_P_IP); + + if (!np->ipv6) { + udph->check = 0; + udph->check = csum_tcpudp_magic(np->local_ip.ip, + np->remote_ip.ip, + udp_len, IPPROTO_UDP, + csum_partial(udph, udp_len, 0)); + if (udph->check == 0) + udph->check = CSUM_MANGLED_0; + + skb_push(skb, sizeof(*iph)); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + + /* iph->version = 4; iph->ihl = 5; */ + put_unaligned(0x45, (unsigned char *)iph); + iph->tos = 0; + put_unaligned(htons(ip_len), &(iph->tot_len)); + iph->id = htons(atomic_inc_return(&ip_ident)); + iph->frag_off = 0; + iph->ttl = 64; + iph->protocol = IPPROTO_UDP; + iph->check = 0; + put_unaligned(np->local_ip.ip, &(iph->saddr)); + put_unaligned(np->remote_ip.ip, &(iph->daddr)); + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + skb->protocol = eth->h_proto = htons(ETH_P_IP); + } + memcpy(eth->h_source, np->dev->dev_addr, ETH_ALEN); memcpy(eth->h_dest, np->remote_mac, ETH_ALEN); @@ -440,7 +446,7 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) } EXPORT_SYMBOL(netpoll_send_udp); -static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo) +static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo) { struct arphdr *arp; unsigned char *arp_ptr; @@ -451,7 +457,7 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo) struct netpoll *np, *tmp; unsigned long flags; int hlen, tlen; - int hits = 0; + int hits = 0, proto; if (list_empty(&npinfo->rx_np)) return; @@ -469,94 +475,97 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo) if (!hits) return; - /* No arp on this interface */ - if (skb->dev->flags & IFF_NOARP) - return; - - if (!pskb_may_pull(skb, arp_hdr_len(skb->dev))) - return; + proto = ntohs(eth_hdr(skb)->h_proto); + if (proto == ETH_P_IP) { + /* No arp on this interface */ + if (skb->dev->flags & IFF_NOARP) + return; - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - arp = arp_hdr(skb); + if (!pskb_may_pull(skb, arp_hdr_len(skb->dev))) + return; - if ((arp->ar_hrd != htons(ARPHRD_ETHER) && - arp->ar_hrd != htons(ARPHRD_IEEE802)) || - arp->ar_pro != htons(ETH_P_IP) || - arp->ar_op != htons(ARPOP_REQUEST)) - return; + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + arp = arp_hdr(skb); - arp_ptr = (unsigned char *)(arp+1); - /* save the location of the src hw addr */ - sha = arp_ptr; - arp_ptr += skb->dev->addr_len; - memcpy(&sip, arp_ptr, 4); - arp_ptr += 4; - /* If we actually cared about dst hw addr, - it would get copied here */ - arp_ptr += skb->dev->addr_len; - memcpy(&tip, arp_ptr, 4); - - /* Should we ignore arp? */ - if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) - return; + if ((arp->ar_hrd != htons(ARPHRD_ETHER) && + arp->ar_hrd != htons(ARPHRD_IEEE802)) || + arp->ar_pro != htons(ETH_P_IP) || + arp->ar_op != htons(ARPOP_REQUEST)) + return; - size = arp_hdr_len(skb->dev); + arp_ptr = (unsigned char *)(arp+1); + /* save the location of the src hw addr */ + sha = arp_ptr; + arp_ptr += skb->dev->addr_len; + memcpy(&sip, arp_ptr, 4); + arp_ptr += 4; + /* If we actually cared about dst hw addr, + it would get copied here */ + arp_ptr += skb->dev->addr_len; + memcpy(&tip, arp_ptr, 4); - spin_lock_irqsave(&npinfo->rx_lock, flags); - list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { - if (tip != np->local_ip) - continue; + /* Should we ignore arp? */ + if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) + return; - hlen = LL_RESERVED_SPACE(np->dev); - tlen = np->dev->needed_tailroom; - send_skb = find_skb(np, size + hlen + tlen, hlen); - if (!send_skb) - continue; + size = arp_hdr_len(skb->dev); - skb_reset_network_header(send_skb); - arp = (struct arphdr *) skb_put(send_skb, size); - send_skb->dev = skb->dev; - send_skb->protocol = htons(ETH_P_ARP); + spin_lock_irqsave(&npinfo->rx_lock, flags); + list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { + if (tip != np->local_ip.ip) + continue; + + hlen = LL_RESERVED_SPACE(np->dev); + tlen = np->dev->needed_tailroom; + send_skb = find_skb(np, size + hlen + tlen, hlen); + if (!send_skb) + continue; + + skb_reset_network_header(send_skb); + arp = (struct arphdr *) skb_put(send_skb, size); + send_skb->dev = skb->dev; + send_skb->protocol = htons(ETH_P_ARP); + + /* Fill the device header for the ARP frame */ + if (dev_hard_header(send_skb, skb->dev, ptype, + sha, np->dev->dev_addr, + send_skb->len) < 0) { + kfree_skb(send_skb); + continue; + } - /* Fill the device header for the ARP frame */ - if (dev_hard_header(send_skb, skb->dev, ptype, - sha, np->dev->dev_addr, - send_skb->len) < 0) { - kfree_skb(send_skb); - continue; + /* + * Fill out the arp protocol part. + * + * we only support ethernet device type, + * which (according to RFC 1390) should + * always equal 1 (Ethernet). + */ + + arp->ar_hrd = htons(np->dev->type); + arp->ar_pro = htons(ETH_P_IP); + arp->ar_hln = np->dev->addr_len; + arp->ar_pln = 4; + arp->ar_op = htons(type); + + arp_ptr = (unsigned char *)(arp + 1); + memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len); + arp_ptr += np->dev->addr_len; + memcpy(arp_ptr, &tip, 4); + arp_ptr += 4; + memcpy(arp_ptr, sha, np->dev->addr_len); + arp_ptr += np->dev->addr_len; + memcpy(arp_ptr, &sip, 4); + + netpoll_send_skb(np, send_skb); + + /* If there are several rx_hooks for the same address, + we're fine by sending a single reply */ + break; } - - /* - * Fill out the arp protocol part. - * - * we only support ethernet device type, - * which (according to RFC 1390) should - * always equal 1 (Ethernet). - */ - - arp->ar_hrd = htons(np->dev->type); - arp->ar_pro = htons(ETH_P_IP); - arp->ar_hln = np->dev->addr_len; - arp->ar_pln = 4; - arp->ar_op = htons(type); - - arp_ptr = (unsigned char *)(arp + 1); - memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len); - arp_ptr += np->dev->addr_len; - memcpy(arp_ptr, &tip, 4); - arp_ptr += 4; - memcpy(arp_ptr, sha, np->dev->addr_len); - arp_ptr += np->dev->addr_len; - memcpy(arp_ptr, &sip, 4); - - netpoll_send_skb(np, send_skb); - - /* If there are several rx_hooks for the same address, - we're fine by sending a single reply */ - break; + spin_unlock_irqrestore(&npinfo->rx_lock, flags); } - spin_unlock_irqrestore(&npinfo->rx_lock, flags); } int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) @@ -576,7 +585,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) /* check if netpoll clients need ARP */ if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) { - skb_queue_tail(&npinfo->arp_tx, skb); + skb_queue_tail(&npinfo->neigh_tx, skb); return 1; } @@ -587,60 +596,61 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) } proto = ntohs(eth_hdr(skb)->h_proto); - if (proto != ETH_P_IP) + if (proto != ETH_P_IP && proto != ETH_P_IPV6) goto out; if (skb->pkt_type == PACKET_OTHERHOST) goto out; if (skb_shared(skb)) goto out; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) - goto out; - iph = (struct iphdr *)skb->data; - if (iph->ihl < 5 || iph->version != 4) - goto out; - if (!pskb_may_pull(skb, iph->ihl*4)) - goto out; - iph = (struct iphdr *)skb->data; - if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) - goto out; - - len = ntohs(iph->tot_len); - if (skb->len < len || len < iph->ihl*4) - goto out; - - /* - * Our transport medium may have padded the buffer out. - * Now We trim to the true length of the frame. - */ - if (pskb_trim_rcsum(skb, len)) - goto out; + if (proto == ETH_P_IP) { + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto out; + iph = (struct iphdr *)skb->data; + if (iph->ihl < 5 || iph->version != 4) + goto out; + if (!pskb_may_pull(skb, iph->ihl*4)) + goto out; + iph = (struct iphdr *)skb->data; + if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) + goto out; - iph = (struct iphdr *)skb->data; - if (iph->protocol != IPPROTO_UDP) - goto out; + len = ntohs(iph->tot_len); + if (skb->len < len || len < iph->ihl*4) + goto out; - len -= iph->ihl*4; - uh = (struct udphdr *)(((char *)iph) + iph->ihl*4); - ulen = ntohs(uh->len); + /* + * Our transport medium may have padded the buffer out. + * Now We trim to the true length of the frame. + */ + if (pskb_trim_rcsum(skb, len)) + goto out; - if (ulen != len) - goto out; - if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) - goto out; + iph = (struct iphdr *)skb->data; + if (iph->protocol != IPPROTO_UDP) + goto out; - list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { - if (np->local_ip && np->local_ip != iph->daddr) - continue; - if (np->remote_ip && np->remote_ip != iph->saddr) - continue; - if (np->local_port && np->local_port != ntohs(uh->dest)) - continue; + len -= iph->ihl*4; + uh = (struct udphdr *)(((char *)iph) + iph->ihl*4); + ulen = ntohs(uh->len); - np->rx_hook(np, ntohs(uh->source), - (char *)(uh+1), - ulen - sizeof(struct udphdr)); - hits++; + if (ulen != len) + goto out; + if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) + goto out; + list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { + if (np->local_ip.ip && np->local_ip.ip != iph->daddr) + continue; + if (np->remote_ip.ip && np->remote_ip.ip != iph->saddr) + continue; + if (np->local_port && np->local_port != ntohs(uh->dest)) + continue; + + np->rx_hook(np, ntohs(uh->source), + (char *)(uh+1), + ulen - sizeof(struct udphdr)); + hits++; + } } if (!hits) @@ -661,17 +671,40 @@ out: void netpoll_print_options(struct netpoll *np) { np_info(np, "local port %d\n", np->local_port); - np_info(np, "local IP %pI4\n", &np->local_ip); + if (!np->ipv6) + np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip); np_info(np, "interface '%s'\n", np->dev_name); np_info(np, "remote port %d\n", np->remote_port); - np_info(np, "remote IP %pI4\n", &np->remote_ip); + if (!np->ipv6) + np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip); np_info(np, "remote ethernet address %pM\n", np->remote_mac); } EXPORT_SYMBOL(netpoll_print_options); +static int netpoll_parse_ip_addr(const char *str, union inet_addr *addr) +{ + const char *end; + + if (!strchr(str, ':') && + in4_pton(str, -1, (void *)addr, -1, &end) > 0) { + if (!*end) + return 0; + } + if (in6_pton(str, -1, addr->in6.s6_addr, -1, &end) > 0) { +#if IS_ENABLED(CONFIG_IPV6) + if (!*end) + return 1; +#else + return -1; +#endif + } + return -1; +} + int netpoll_parse_options(struct netpoll *np, char *opt) { char *cur=opt, *delim; + int ipv6; if (*cur != '@') { if ((delim = strchr(cur, '@')) == NULL) @@ -687,7 +720,11 @@ int netpoll_parse_options(struct netpoll *np, char *opt) if ((delim = strchr(cur, '/')) == NULL) goto parse_failed; *delim = 0; - np->local_ip = in_aton(cur); + ipv6 = netpoll_parse_ip_addr(cur, &np->local_ip); + if (ipv6 < 0) + goto parse_failed; + else + np->ipv6 = (bool)ipv6; cur = delim; } cur++; @@ -719,7 +756,13 @@ int netpoll_parse_options(struct netpoll *np, char *opt) if ((delim = strchr(cur, '/')) == NULL) goto parse_failed; *delim = 0; - np->remote_ip = in_aton(cur); + ipv6 = netpoll_parse_ip_addr(cur, &np->remote_ip); + if (ipv6 < 0) + goto parse_failed; + else if (np->ipv6 != (bool)ipv6) + goto parse_failed; + else + np->ipv6 = (bool)ipv6; cur = delim + 1; if (*cur != 0) { @@ -767,7 +810,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) INIT_LIST_HEAD(&npinfo->rx_np); spin_lock_init(&npinfo->rx_lock); - skb_queue_head_init(&npinfo->arp_tx); + skb_queue_head_init(&npinfo->neigh_tx); skb_queue_head_init(&npinfo->txq); INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); @@ -859,21 +902,24 @@ int netpoll_setup(struct netpoll *np) } } - if (!np->local_ip) { - rcu_read_lock(); - in_dev = __in_dev_get_rcu(ndev); + if (!np->local_ip.ip) { + if (!np->ipv6) { + rcu_read_lock(); + in_dev = __in_dev_get_rcu(ndev); - if (!in_dev || !in_dev->ifa_list) { + + if (!in_dev || !in_dev->ifa_list) { + rcu_read_unlock(); + np_err(np, "no IP address for %s, aborting\n", + np->dev_name); + err = -EDESTADDRREQ; + goto put; + } + + np->local_ip.ip = in_dev->ifa_list->ifa_local; rcu_read_unlock(); - np_err(np, "no IP address for %s, aborting\n", - np->dev_name); - err = -EDESTADDRREQ; - goto put; + np_info(np, "local IP %pI4\n", &np->local_ip.ip); } - - np->local_ip = in_dev->ifa_list->ifa_local; - rcu_read_unlock(); - np_info(np, "local IP %pI4\n", &np->local_ip); } /* fill up the skb queue */ @@ -906,7 +952,7 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) struct netpoll_info *npinfo = container_of(rcu_head, struct netpoll_info, rcu); - skb_queue_purge(&npinfo->arp_tx); + skb_queue_purge(&npinfo->neigh_tx); skb_queue_purge(&npinfo->txq); /* we can't call cancel_delayed_work_sync here, as we are in softirq */ -- cgit v1.2.3 From acb3e04119fbf9145eb6d6bb707f6fb662ab4d3b Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 7 Jan 2013 20:52:40 +0000 Subject: ipv6: move csum_ipv6_magic() and udp6_csum_init() into static library As suggested by David, udp6_csum_init() is too big to be inlined, move it to ipv6 static library, net/ipv6/ip6_checksum.c. And the generic csum_ipv6_magic() too. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip6_checksum.h | 62 +++-------------------------- net/ipv6/Makefile | 2 +- net/ipv6/ip6_checksum.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv6/udp.c | 34 ---------------- 4 files changed, 103 insertions(+), 92 deletions(-) create mode 100644 net/ipv6/ip6_checksum.c (limited to 'net') diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h index 652d3d309357..7686e3f5033d 100644 --- a/include/net/ip6_checksum.h +++ b/include/net/ip6_checksum.h @@ -35,63 +35,10 @@ #include #ifndef _HAVE_ARCH_IPV6_CSUM - -static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr, - const struct in6_addr *daddr, - __u32 len, unsigned short proto, - __wsum csum) -{ - - int carry; - __u32 ulen; - __u32 uproto; - __u32 sum = (__force u32)csum; - - sum += (__force u32)saddr->s6_addr32[0]; - carry = (sum < (__force u32)saddr->s6_addr32[0]); - sum += carry; - - sum += (__force u32)saddr->s6_addr32[1]; - carry = (sum < (__force u32)saddr->s6_addr32[1]); - sum += carry; - - sum += (__force u32)saddr->s6_addr32[2]; - carry = (sum < (__force u32)saddr->s6_addr32[2]); - sum += carry; - - sum += (__force u32)saddr->s6_addr32[3]; - carry = (sum < (__force u32)saddr->s6_addr32[3]); - sum += carry; - - sum += (__force u32)daddr->s6_addr32[0]; - carry = (sum < (__force u32)daddr->s6_addr32[0]); - sum += carry; - - sum += (__force u32)daddr->s6_addr32[1]; - carry = (sum < (__force u32)daddr->s6_addr32[1]); - sum += carry; - - sum += (__force u32)daddr->s6_addr32[2]; - carry = (sum < (__force u32)daddr->s6_addr32[2]); - sum += carry; - - sum += (__force u32)daddr->s6_addr32[3]; - carry = (sum < (__force u32)daddr->s6_addr32[3]); - sum += carry; - - ulen = (__force u32)htonl((__u32) len); - sum += ulen; - carry = (sum < ulen); - sum += carry; - - uproto = (__force u32)htonl(proto); - sum += uproto; - carry = (sum < uproto); - sum += carry; - - return csum_fold((__force __wsum)sum); -} - +__sum16 csum_ipv6_magic(const struct in6_addr *saddr, + const struct in6_addr *daddr, + __u32 len, unsigned short proto, + __wsum csum); #endif static __inline__ __sum16 tcp_v6_check(int len, @@ -126,4 +73,5 @@ static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) __tcp_v6_send_check(skb, &np->saddr, &np->daddr); } +int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto); #endif diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 4ea244891b58..309af19a0a0a 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_GRE) += ip6_gre.o -obj-y += addrconf_core.o exthdrs_core.o +obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o diff --git a/net/ipv6/ip6_checksum.c b/net/ipv6/ip6_checksum.c new file mode 100644 index 000000000000..72d198b8e4d2 --- /dev/null +++ b/net/ipv6/ip6_checksum.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#ifndef _HAVE_ARCH_IPV6_CSUM +__sum16 csum_ipv6_magic(const struct in6_addr *saddr, + const struct in6_addr *daddr, + __u32 len, unsigned short proto, + __wsum csum) +{ + + int carry; + __u32 ulen; + __u32 uproto; + __u32 sum = (__force u32)csum; + + sum += (__force u32)saddr->s6_addr32[0]; + carry = (sum < (__force u32)saddr->s6_addr32[0]); + sum += carry; + + sum += (__force u32)saddr->s6_addr32[1]; + carry = (sum < (__force u32)saddr->s6_addr32[1]); + sum += carry; + + sum += (__force u32)saddr->s6_addr32[2]; + carry = (sum < (__force u32)saddr->s6_addr32[2]); + sum += carry; + + sum += (__force u32)saddr->s6_addr32[3]; + carry = (sum < (__force u32)saddr->s6_addr32[3]); + sum += carry; + + sum += (__force u32)daddr->s6_addr32[0]; + carry = (sum < (__force u32)daddr->s6_addr32[0]); + sum += carry; + + sum += (__force u32)daddr->s6_addr32[1]; + carry = (sum < (__force u32)daddr->s6_addr32[1]); + sum += carry; + + sum += (__force u32)daddr->s6_addr32[2]; + carry = (sum < (__force u32)daddr->s6_addr32[2]); + sum += carry; + + sum += (__force u32)daddr->s6_addr32[3]; + carry = (sum < (__force u32)daddr->s6_addr32[3]); + sum += carry; + + ulen = (__force u32)htonl((__u32) len); + sum += ulen; + carry = (sum < ulen); + sum += carry; + + uproto = (__force u32)htonl(proto); + sum += uproto; + carry = (sum < uproto); + sum += carry; + + return csum_fold((__force __wsum)sum); +} +EXPORT_SYMBOL(csum_ipv6_magic); +#endif + +int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto) +{ + int err; + + UDP_SKB_CB(skb)->partial_cov = 0; + UDP_SKB_CB(skb)->cscov = skb->len; + + if (proto == IPPROTO_UDPLITE) { + err = udplite_checksum_init(skb, uh); + if (err) + return err; + } + + if (uh->check == 0) { + /* RFC 2460 section 8.1 says that we SHOULD log + this error. Well, it is reasonable. + */ + LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0\n"); + return 1; + } + if (skb->ip_summed == CHECKSUM_COMPLETE && + !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, + skb->len, proto, skb->csum)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (!skb_csum_unnecessary(skb)) + skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->len, proto, 0)); + + return 0; +} +EXPORT_SYMBOL(udp6_csum_init); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index dfaa29b8b293..1afb635d9b57 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -752,40 +752,6 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, return 0; } -static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, - int proto) -{ - int err; - - UDP_SKB_CB(skb)->partial_cov = 0; - UDP_SKB_CB(skb)->cscov = skb->len; - - if (proto == IPPROTO_UDPLITE) { - err = udplite_checksum_init(skb, uh); - if (err) - return err; - } - - if (uh->check == 0) { - /* RFC 2460 section 8.1 says that we SHOULD log - this error. Well, it is reasonable. - */ - LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0\n"); - return 1; - } - if (skb->ip_summed == CHECKSUM_COMPLETE && - !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, - skb->len, proto, skb->csum)) - skb->ip_summed = CHECKSUM_UNNECESSARY; - - if (!skb_csum_unnecessary(skb)) - skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - skb->len, proto, 0)); - - return 0; -} - int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, int proto) { -- cgit v1.2.3 From b3d936f3ea1c97c32680e0cd235474cf9dadb762 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 7 Jan 2013 20:52:41 +0000 Subject: netpoll: add IPv6 support Currently, netpoll only supports IPv4. This patch adds IPv6 support to netpoll so that we can run netconsole over IPv6 network. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/netconsole.c | 44 +++++++-- net/core/netpoll.c | 246 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 274 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 998fa0257a92..37add21a3d7d 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -269,13 +269,17 @@ static ssize_t show_remote_port(struct netconsole_target *nt, char *buf) static ssize_t show_local_ip(struct netconsole_target *nt, char *buf) { - if (!nt->np.ipv6) + if (nt->np.ipv6) + return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.local_ip.in6); + else return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip); } static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf) { - if (!nt->np.ipv6) + if (nt->np.ipv6) + return snprintf(buf, PAGE_SIZE, "%pI6c\n", &nt->np.remote_ip.in6); + else return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip); } @@ -412,8 +416,22 @@ static ssize_t store_local_ip(struct netconsole_target *nt, return -EINVAL; } - if (!strnchr(buf, count, ':')) - nt->np.local_ip.ip = in_aton(buf); + if (strnchr(buf, count, ':')) { + const char *end; + if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) { + if (*end && *end != '\n') { + printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end); + return -EINVAL; + } + nt->np.ipv6 = true; + } else + return -EINVAL; + } else { + if (!nt->np.ipv6) { + nt->np.local_ip.ip = in_aton(buf); + } else + return -EINVAL; + } return strnlen(buf, count); } @@ -429,8 +447,22 @@ static ssize_t store_remote_ip(struct netconsole_target *nt, return -EINVAL; } - if (!strnchr(buf, count, ':')) - nt->np.remote_ip.ip = in_aton(buf); + if (strnchr(buf, count, ':')) { + const char *end; + if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) { + if (*end && *end != '\n') { + printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end); + return -EINVAL; + } + nt->np.ipv6 = true; + } else + return -EINVAL; + } else { + if (!nt->np.ipv6) { + nt->np.remote_ip.ip = in_aton(buf); + } else + return -EINVAL; + } return strnlen(buf, count); } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 6bd073688f68..9f0506726101 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #include @@ -384,9 +387,12 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) struct iphdr *iph; struct ethhdr *eth; static atomic_t ip_ident; + struct ipv6hdr *ip6h; udp_len = len + sizeof(*udph); - if (!np->ipv6) + if (np->ipv6) + ip_len = udp_len + sizeof(*ip6h); + else ip_len = udp_len + sizeof(*iph); total_len = ip_len + LL_RESERVED_SPACE(np->dev); @@ -406,7 +412,35 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) udph->dest = htons(np->remote_port); udph->len = htons(udp_len); - if (!np->ipv6) { + if (np->ipv6) { + udph->check = 0; + udph->check = csum_ipv6_magic(&np->local_ip.in6, + &np->remote_ip.in6, + udp_len, IPPROTO_UDP, + csum_partial(udph, udp_len, 0)); + if (udph->check == 0) + udph->check = CSUM_MANGLED_0; + + skb_push(skb, sizeof(*ip6h)); + skb_reset_network_header(skb); + ip6h = ipv6_hdr(skb); + + /* ip6h->version = 6; ip6h->priority = 0; */ + put_unaligned(0x60, (unsigned char *)ip6h); + ip6h->flow_lbl[0] = 0; + ip6h->flow_lbl[1] = 0; + ip6h->flow_lbl[2] = 0; + + ip6h->payload_len = htons(sizeof(struct udphdr) + len); + ip6h->nexthdr = IPPROTO_UDP; + ip6h->hop_limit = 32; + ip6h->saddr = np->local_ip.in6; + ip6h->daddr = np->remote_ip.in6; + + eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); + skb_reset_mac_header(skb); + skb->protocol = eth->h_proto = htons(ETH_P_IPV6); + } else { udph->check = 0; udph->check = csum_tcpudp_magic(np->local_ip.ip, np->remote_ip.ip, @@ -448,9 +482,7 @@ EXPORT_SYMBOL(netpoll_send_udp); static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo) { - struct arphdr *arp; - unsigned char *arp_ptr; - int size, type = ARPOP_REPLY, ptype = ETH_P_ARP; + int size, type = ARPOP_REPLY; __be32 sip, tip; unsigned char *sha; struct sk_buff *send_skb; @@ -477,6 +509,8 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo proto = ntohs(eth_hdr(skb)->h_proto); if (proto == ETH_P_IP) { + struct arphdr *arp; + unsigned char *arp_ptr; /* No arp on this interface */ if (skb->dev->flags & IFF_NOARP) return; @@ -528,7 +562,7 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo send_skb->protocol = htons(ETH_P_ARP); /* Fill the device header for the ARP frame */ - if (dev_hard_header(send_skb, skb->dev, ptype, + if (dev_hard_header(send_skb, skb->dev, ETH_P_ARP, sha, np->dev->dev_addr, send_skb->len) < 0) { kfree_skb(send_skb); @@ -565,9 +599,124 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo break; } spin_unlock_irqrestore(&npinfo->rx_lock, flags); + } else if( proto == ETH_P_IPV6) { +#if IS_ENABLED(CONFIG_IPV6) + struct nd_msg *msg; + u8 *lladdr = NULL; + struct ipv6hdr *hdr; + struct icmp6hdr *icmp6h; + const struct in6_addr *saddr; + const struct in6_addr *daddr; + struct inet6_dev *in6_dev = NULL; + struct in6_addr *target; + + in6_dev = in6_dev_get(skb->dev); + if (!in6_dev || !in6_dev->cnf.accept_ra) + return; + + if (!pskb_may_pull(skb, skb->len)) + return; + + msg = (struct nd_msg *)skb_transport_header(skb); + + __skb_push(skb, skb->data - skb_transport_header(skb)); + + if (ipv6_hdr(skb)->hop_limit != 255) + return; + if (msg->icmph.icmp6_code != 0) + return; + if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) + return; + + saddr = &ipv6_hdr(skb)->saddr; + daddr = &ipv6_hdr(skb)->daddr; + + size = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); + + spin_lock_irqsave(&npinfo->rx_lock, flags); + list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { + if (memcmp(daddr, &np->local_ip, sizeof(*daddr))) + continue; + + hlen = LL_RESERVED_SPACE(np->dev); + tlen = np->dev->needed_tailroom; + send_skb = find_skb(np, size + hlen + tlen, hlen); + if (!send_skb) + continue; + + send_skb->protocol = htons(ETH_P_IPV6); + send_skb->dev = skb->dev; + + skb_reset_network_header(send_skb); + skb_put(send_skb, sizeof(struct ipv6hdr)); + hdr = ipv6_hdr(send_skb); + + *(__be32*)hdr = htonl(0x60000000); + + hdr->payload_len = htons(size); + hdr->nexthdr = IPPROTO_ICMPV6; + hdr->hop_limit = 255; + hdr->saddr = *saddr; + hdr->daddr = *daddr; + + send_skb->transport_header = send_skb->tail; + skb_put(send_skb, size); + + icmp6h = (struct icmp6hdr *)skb_transport_header(skb); + icmp6h->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; + icmp6h->icmp6_router = 0; + icmp6h->icmp6_solicited = 1; + target = (struct in6_addr *)skb_transport_header(send_skb) + sizeof(struct icmp6hdr); + *target = msg->target; + icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, size, + IPPROTO_ICMPV6, + csum_partial(icmp6h, + size, 0)); + + if (dev_hard_header(send_skb, skb->dev, ETH_P_IPV6, + lladdr, np->dev->dev_addr, + send_skb->len) < 0) { + kfree_skb(send_skb); + continue; + } + + netpoll_send_skb(np, send_skb); + + /* If there are several rx_hooks for the same address, + we're fine by sending a single reply */ + break; + } + spin_unlock_irqrestore(&npinfo->rx_lock, flags); +#endif } } +static bool pkt_is_ns(struct sk_buff *skb) +{ + struct nd_msg *msg; + struct ipv6hdr *hdr; + + if (skb->protocol != htons(ETH_P_ARP)) + return false; + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct nd_msg))) + return false; + + msg = (struct nd_msg *)skb_transport_header(skb); + __skb_push(skb, skb->data - skb_transport_header(skb)); + hdr = ipv6_hdr(skb); + + if (hdr->nexthdr != IPPROTO_ICMPV6) + return false; + if (hdr->hop_limit != 255) + return false; + if (msg->icmph.icmp6_code != 0) + return false; + if (msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) + return false; + + return true; +} + int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) { int proto, len, ulen; @@ -583,8 +732,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) goto out; /* check if netpoll clients need ARP */ - if (skb->protocol == htons(ETH_P_ARP) && - atomic_read(&trapped)) { + if (skb->protocol == htons(ETH_P_ARP) && atomic_read(&trapped)) { + skb_queue_tail(&npinfo->neigh_tx, skb); + return 1; + } else if (pkt_is_ns(skb) && atomic_read(&trapped)) { skb_queue_tail(&npinfo->neigh_tx, skb); return 1; } @@ -651,6 +802,45 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) ulen - sizeof(struct udphdr)); hits++; } + } else { +#if IS_ENABLED(CONFIG_IPV6) + const struct ipv6hdr *ip6h; + + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto out; + ip6h = (struct ipv6hdr *)skb->data; + if (ip6h->version != 6) + goto out; + len = ntohs(ip6h->payload_len); + if (!len) + goto out; + if (len + sizeof(struct ipv6hdr) > skb->len) + goto out; + if (pskb_trim_rcsum(skb, len + sizeof(struct ipv6hdr))) + goto out; + ip6h = ipv6_hdr(skb); + if (!pskb_may_pull(skb, sizeof(struct udphdr))) + goto out; + uh = udp_hdr(skb); + ulen = ntohs(uh->len); + if (ulen != skb->len) + goto out; + if (udp6_csum_init(skb, uh, IPPROTO_UDP)) + goto out; + list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { + if (memcmp(&np->local_ip.in6, &ip6h->daddr, sizeof(struct in6_addr)) != 0) + continue; + if (memcmp(&np->remote_ip.in6, &ip6h->saddr, sizeof(struct in6_addr)) != 0) + continue; + if (np->local_port && np->local_port != ntohs(uh->dest)) + continue; + + np->rx_hook(np, ntohs(uh->source), + (char *)(uh+1), + ulen - sizeof(struct udphdr)); + hits++; + } +#endif } if (!hits) @@ -671,11 +861,15 @@ out: void netpoll_print_options(struct netpoll *np) { np_info(np, "local port %d\n", np->local_port); - if (!np->ipv6) + if (np->ipv6) + np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6); + else np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip); np_info(np, "interface '%s'\n", np->dev_name); np_info(np, "remote port %d\n", np->remote_port); - if (!np->ipv6) + if (np->ipv6) + np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6); + else np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip); np_info(np, "remote ethernet address %pM\n", np->remote_mac); } @@ -919,6 +1113,38 @@ int netpoll_setup(struct netpoll *np) np->local_ip.ip = in_dev->ifa_list->ifa_local; rcu_read_unlock(); np_info(np, "local IP %pI4\n", &np->local_ip.ip); + } else { +#if IS_ENABLED(CONFIG_IPV6) + struct inet6_dev *idev; + + err = -EDESTADDRREQ; + rcu_read_lock(); + idev = __in6_dev_get(ndev); + if (idev) { + struct inet6_ifaddr *ifp; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifp, &idev->addr_list, if_list) { + if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) + continue; + np->local_ip.in6 = ifp->addr; + err = 0; + break; + } + read_unlock_bh(&idev->lock); + } + rcu_read_unlock(); + if (err) { + np_err(np, "no IPv6 address for %s, aborting\n", + np->dev_name); + goto put; + } else + np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6); +#else + np_err(np, "IPv6 is not supported %s, aborting\n", + np->dev_name); + goto put; +#endif } } -- cgit v1.2.3 From 948b337e62ca96b6e7de06fdd5f6fe44769d7e9c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 8 Jan 2013 01:38:25 +0000 Subject: net: init perm_addr in register_netdevice() Benefit from the fact that dev->addr_assign_type is set to NET_ADDR_PERM in case the device has permanent address. This also fixes the problem that many drivers do not set perm_addr at all. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/dev.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 2e2448201a76..594830e83589 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6004,6 +6004,13 @@ int register_netdevice(struct net_device *dev) list_netdevice(dev); add_device_randomness(dev->dev_addr, dev->addr_len); + /* If the device has permanent device address, driver should + * set dev_addr and also addr_assign_type should be set to + * NET_ADDR_PERM (default value). + */ + if (dev->addr_assign_type == NET_ADDR_PERM) + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + /* Notify protocols, that a new device appeared. */ ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); ret = notifier_to_errno(ret); -- cgit v1.2.3 From 4a484cffc5e1836114e9556f7c704412d491200c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 9 Jan 2013 10:22:07 +0100 Subject: regulatory: fix uevent The uevent callback doesn't protect its access to last_request, which now causes a warning since the conversion to get_last_request(). Fix this by allowing to use RCU protection for last_request. Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/reg.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0b35de001937..2193f62bb8ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -140,8 +140,8 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) static struct regulatory_request *get_last_request(void) { - return rcu_dereference_protected(last_request, - lockdep_is_held(®_mutex)); + return rcu_dereference_check(last_request, + lockdep_is_held(®_mutex)); } /* Used to queue up regulatory hints */ @@ -2250,14 +2250,21 @@ int set_regdom(const struct ieee80211_regdomain *rd) #ifdef CONFIG_HOTPLUG int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct regulatory_request *lr = get_last_request(); + struct regulatory_request *lr; + u8 alpha2[2]; + bool add = false; + rcu_read_lock(); + lr = get_last_request(); if (lr && !lr->processed) { - if (add_uevent_var(env, "COUNTRY=%c%c", - lr->alpha2[0], lr->alpha2[1])) - return -ENOMEM; + memcpy(alpha2, lr->alpha2, 2); + add = true; } + rcu_read_unlock(); + if (add) + return add_uevent_var(env, "COUNTRY=%c%c", + alpha2[0], alpha2[1]); return 0; } #else -- cgit v1.2.3 From 2d31986764926630a6df72123a5e217219b32594 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 9 Jan 2013 12:01:38 +0100 Subject: regulatory: fix restore_regulatory_settings My commit 379b82f4c9dc6e67bf61aa61b096c06a2f320f60 ("regulatory: pass new regdomain to reset function") broke the restore_regulatory_settings() function due to a logic change. Consider this change: - reset_regdomains(true); - cfg80211_regdomain = cfg80211_world_regdom; + reset_regdomains(true, cfg80211_world_regdom); This looks innocent enough, until you realise that the called function (reset_regdomains) also resets the cfg80211_world_regdom pointer, so that the old version of the code would use the new object it pointed to and the new version of the code uses the old object. This lead to a double-free of this object. Since reset_regdomains() sets it to &world_regdom, use that directly. Reported-by: Sujith Manoharan Tested-by: Sujith Manoharan Reported-by: Bob Copeland Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2193f62bb8ae..8c114e8a9135 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1848,7 +1848,7 @@ static void restore_regulatory_settings(bool reset_user) mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); - reset_regdomains(true, cfg80211_world_regdom); + reset_regdomains(true, &world_regdom); restore_alpha2(alpha2, reset_user); /* -- cgit v1.2.3 From d6b67c6c0c54c1b18507c15cf1667a362959a0d3 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Sat, 8 Dec 2012 07:08:25 +0200 Subject: Bluetooth: Remove unnecessary include l2cap.h This patch removes unnecessary include of in bluetooth/bnep/core.c. Signed-off-by: Rami Rosen Signed-off-by: Gustavo Padovan --- net/bluetooth/bnep/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index a5b639702637..e430b1abcd2f 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -33,7 +33,6 @@ #include #include -#include #include "bnep.h" -- cgit v1.2.3 From 8e05e3ba88adcf7ac644e6ef26676ea7c048a08c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:05 +0200 Subject: Bluetooth: AMP: Send A2MP Create Phylink Rsp after Assoc write Postpone sending A2MP Create Phylink Response until we got successful HCI Command Complete after HCI Write Remote AMP Assoc. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ net/bluetooth/a2mp.c | 38 ++++++++++++++++++++++++++++++++++++-- net/bluetooth/amp.c | 4 +++- 3 files changed, 41 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 42f21766c538..8b39327a5200 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -23,6 +23,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, READ_LOC_AMP_ASSOC, READ_LOC_AMP_ASSOC_FINAL, + WRITE_REMOTE_AMP_ASSOC, }; struct amp_mgr { @@ -144,5 +145,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status); +void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 2f67d5ecc907..a200edf63117 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -499,8 +499,16 @@ send_rsp: if (hdev) hci_dev_put(hdev); - a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), - &rsp); + /* Reply error now and success after HCI Write Remote AMP Assoc + command complete with success status + */ + if (rsp.status != A2MP_STATUS_SUCCESS) { + a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, + sizeof(rsp), &rsp); + } else { + mgr->state = WRITE_REMOTE_AMP_ASSOC; + mgr->ident = hdr->ident; + } skb_pull(skb, le16_to_cpu(hdr->len)); return 0; @@ -949,6 +957,32 @@ clean: kfree(req); } +void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct a2mp_physlink_rsp rsp; + struct hci_conn *hs_hcon; + + mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC); + if (!mgr) + return; + + hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT); + if (!hs_hcon) { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + } else { + rsp.remote_id = hs_hcon->remote_id; + rsp.status = A2MP_STATUS_SUCCESS; + } + + BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon, + status); + + rsp.local_id = hdev->id; + a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp); + amp_mgr_put(mgr); +} + void a2mp_discover_amp(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 1b0d92c0643a..522865776ec6 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -317,7 +317,9 @@ void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle) if (!hcon) return; - amp_write_rem_assoc_frag(hdev, hcon); + /* Send A2MP create phylink rsp when all fragments are written */ + if (amp_write_rem_assoc_frag(hdev, hcon)) + a2mp_send_create_phy_link_rsp(hdev, 0); } void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle) -- cgit v1.2.3 From 7a9898c6ff67ad640304fd3d02f9a22874483c3d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:06 +0200 Subject: Bluetooth: AMP: Clean up logical link create / accept Use chan->hs_hcon instead of lookup by dst address. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 522865776ec6..6b829b28eb80 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -405,26 +405,20 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) void amp_create_logical_link(struct l2cap_chan *chan) { + struct hci_conn *hs_hcon = chan->hs_hcon; struct hci_cp_create_accept_logical_link cp; - struct hci_conn *hcon; struct hci_dev *hdev; - BT_DBG("chan %p", chan); + BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst); - if (!chan->hs_hcon) + if (!hs_hcon) return; hdev = hci_dev_hold(chan->hs_hcon->hdev); if (!hdev) return; - BT_DBG("chan %p dst %pMR", chan, chan->conn->dst); - - hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst); - if (!hcon) - goto done; - - cp.phy_handle = hcon->handle; + cp.phy_handle = hs_hcon->handle; cp.tx_flow_spec.id = chan->local_id; cp.tx_flow_spec.stype = chan->local_stype; @@ -440,14 +434,13 @@ void amp_create_logical_link(struct l2cap_chan *chan) cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat); cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to); - if (hcon->out) + if (hs_hcon->out) hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), &cp); else hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), &cp); -done: hci_dev_put(hdev); } -- cgit v1.2.3 From cbf54ad104cb2ec6f5734d95be1dc783bea0343b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:07 +0200 Subject: Bluetooth: AMP: Remove dead code Remove code which cannot execute. l2cap_conn_add for AMP_LINK might only be invoked when receiving data in l2cap_recv_acldata. But this case is checked in the first statement there. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c78208d793e..82a3bdc8dc5b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1527,17 +1527,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); switch (hcon->type) { - case AMP_LINK: - conn->mtu = hcon->hdev->block_mtu; - break; - case LE_LINK: if (hcon->hdev->le_mtu) { conn->mtu = hcon->hdev->le_mtu; break; } /* fall through */ - default: conn->mtu = hcon->hdev->acl_mtu; break; -- cgit v1.2.3 From cb6801c640c759fe02c812728c2661bd8ba5a302 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 7 Dec 2012 14:59:08 +0200 Subject: Bluetooth: AMP: Use set_bit / test_bit for amp_mgr state Using bit operations solves problems with multiple requests and clearing state. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 +- net/bluetooth/a2mp.c | 6 +++--- net/bluetooth/amp.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 8b39327a5200..487b54c1308f 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -34,7 +34,7 @@ struct amp_mgr { struct kref kref; __u8 ident; __u8 handle; - enum amp_mgr_state state; + unsigned long state; unsigned long flags; struct list_head amp_ctrls; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index a200edf63117..eb0f4b16ff09 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, goto done; } - mgr->state = READ_LOC_AMP_INFO; + set_bit(READ_LOC_AMP_INFO, &mgr->state); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); done: @@ -506,7 +506,7 @@ send_rsp: a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp); } else { - mgr->state = WRITE_REMOTE_AMP_ASSOC; + set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state); mgr->ident = hdr->ident; } @@ -848,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state) mutex_lock(&_mgr_list_lock); list_for_each_entry(mgr, &_mgr_list, list) { - if (mgr->state == state) { + if (test_and_clear_bit(state, &mgr->state)) { amp_mgr_get(mgr); mutex_unlock(&_mgr_list_lock); return mgr; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 6b829b28eb80..d459ed43c779 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -236,7 +236,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) cp.max_len = cpu_to_le16(hdev->amp_assoc_size); - mgr->state = READ_LOC_AMP_ASSOC; + set_bit(READ_LOC_AMP_ASSOC, &mgr->state); hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } @@ -250,7 +250,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, cp.len_so_far = cpu_to_le16(0); cp.max_len = cpu_to_le16(hdev->amp_assoc_size); - mgr->state = READ_LOC_AMP_ASSOC_FINAL; + set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state); /* Read Local AMP Assoc final link information data */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); -- cgit v1.2.3 From 47c37941b8895557409db286e6a441e6d557d62f Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 13 Dec 2012 15:11:20 +0100 Subject: Bluetooth: mgmt: Remove not needed restriction on add/remove OOB data Those commands don't send any HCI commands to controller so there is no need to restrict them to only powered up controller. This also makes implementation more consistent as already stored remote OOB data persist power toggle. Signed-off-by: Szymon Janc Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f559b966279c..ef385824a144 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2254,13 +2254,6 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, cp->randomizer); if (err < 0) @@ -2271,7 +2264,6 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); -unlock: hci_dev_unlock(hdev); return err; } @@ -2287,14 +2279,6 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; @@ -2304,7 +2288,6 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); -unlock: hci_dev_unlock(hdev); return err; } -- cgit v1.2.3 From a6785be2f76e2c39b3008820e7bfef8f5fd838bc Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 13 Dec 2012 15:11:21 +0100 Subject: Bluetooth: mgmt: Avoid using magic number in status code Use MGMT_STATUS_SUCCESS for success return code. Signed-off-by: Szymon Janc Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ef385824a144..e5502a56365b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2259,7 +2259,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (err < 0) status = MGMT_STATUS_FAILED; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); @@ -2283,7 +2283,7 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); @@ -2515,7 +2515,7 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) status = MGMT_STATUS_FAILED; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -2540,7 +2540,7 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else - status = 0; + status = MGMT_STATUS_SUCCESS; err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); -- cgit v1.2.3 From 2c2d45bdcb7063ef58dc2c27d72026c394a8f584 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Tue, 27 Nov 2012 15:44:24 +0100 Subject: NFC: Add support for SO_TIMESTAMP LLCP socket option Set timestamp in sent and received sk_buffs. timestamp is then put in msghdr structure in llcp_sock_recvmsg(). Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- net/nfc/llcp/commands.c | 2 ++ net/nfc/llcp/llcp.c | 4 ++++ net/nfc/llcp/sock.c | 2 ++ 3 files changed, 8 insertions(+) (limited to 'net') diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index df24be48d4da..c6bc3bd95052 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -304,6 +304,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev) skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); return nfc_data_exchange(dev, local->target_idx, skb, diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index ec43914c92a9..6c5971437826 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -668,6 +668,8 @@ static void nfc_llcp_tx_work(struct work_struct *work) if (ptype == LLCP_PDU_I) copy_skb = skb_copy(skb, GFP_ATOMIC); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); @@ -1245,6 +1247,8 @@ static void nfc_llcp_rx_work(struct work_struct *work) print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); + __net_timestamp(skb); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX); switch (ptype) { diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index fea22eb41b82..ba1900700958 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -678,6 +678,8 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return -EFAULT; } + sock_recv_timestamp(msg, sk, skb); + if (sk->sk_type == SOCK_DGRAM && msg->msg_name) { struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); struct sockaddr_nfc_llcp sockaddr; -- cgit v1.2.3 From 2593c2c6ec236c6aedbb8182d103185ab6580285 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 7 Dec 2012 15:55:19 +0100 Subject: NFC: llcp: Remove the tx backlog queue Not only it was improperly use to queue backlogged RX skbuffs, but it was also not processed at all. If the socket receive queue is full we simply drop the incoming packets. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 5 ++--- net/nfc/llcp/llcp.h | 1 - net/nfc/llcp/sock.c | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 6c5971437826..003c82fe8bd7 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -54,7 +54,6 @@ static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock) skb_queue_purge(&sock->tx_queue); skb_queue_purge(&sock->tx_pending_queue); - skb_queue_purge(&sock->tx_backlog_queue); if (local == NULL) return; @@ -785,7 +784,7 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local, skb_pull(skb, LLCP_HEADER_SIZE); if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { pr_err("receive queue is full\n"); - skb_queue_head(&llcp_sock->tx_backlog_queue, skb); + kfree_skb(skb); } nfc_llcp_sock_put(llcp_sock); @@ -980,7 +979,7 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE); if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { pr_err("receive queue is full\n"); - skb_queue_head(&llcp_sock->tx_backlog_queue, skb); + kfree_skb(skb); } } diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 0d62366f8cc3..0eae5c509504 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -121,7 +121,6 @@ struct nfc_llcp_sock { struct sk_buff_head tx_queue; struct sk_buff_head tx_pending_queue; - struct sk_buff_head tx_backlog_queue; struct list_head accept_queue; struct sock *parent; diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index ba1900700958..12ec4879b4ba 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -808,7 +808,6 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp) llcp_sock->reserved_ssap = LLCP_SAP_MAX; skb_queue_head_init(&llcp_sock->tx_queue); skb_queue_head_init(&llcp_sock->tx_pending_queue); - skb_queue_head_init(&llcp_sock->tx_backlog_queue); INIT_LIST_HEAD(&llcp_sock->accept_queue); if (sock != NULL) @@ -823,7 +822,6 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock) skb_queue_purge(&sock->tx_queue); skb_queue_purge(&sock->tx_pending_queue); - skb_queue_purge(&sock->tx_backlog_queue); list_del_init(&sock->accept_queue); -- cgit v1.2.3 From 1727cf937401945a590ebf25c7fa65a4853fe9bb Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 7 Dec 2012 16:37:30 +0100 Subject: NFC: llcp: Fix Rx memory leak The reference count bump on the llcp Rx path is leading to a memory leak whenever we're not receiving an I frame. We fix that by removing the refcount bump (drivers must not free their received skb) and using it only in the I frame path, when the frame is actually queued. In that case, the skb will only be freed when someone fetches it from userspace. in all other cases, LLCP received frames will be freed when leaving the Rx work queue. Reported-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- net/nfc/llcp/llcp.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 003c82fe8bd7..85bc75c38dea 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -782,8 +782,14 @@ static void nfc_llcp_recv_ui(struct nfc_llcp_local *local, /* There is no sequence with UI frames */ skb_pull(skb, LLCP_HEADER_SIZE); - if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { - pr_err("receive queue is full\n"); + if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) { + /* + * UI frames will be freed from the socket layer, so we + * need to keep them alive until someone receives them. + */ + skb_get(skb); + } else { + pr_err("Receive queue is full\n"); kfree_skb(skb); } @@ -977,8 +983,14 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local, pr_err("Received out of sequence I PDU\n"); skb_pull(skb, LLCP_HEADER_SIZE + LLCP_SEQUENCE_SIZE); - if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) { - pr_err("receive queue is full\n"); + if (!sock_queue_rcv_skb(&llcp_sock->sk, skb)) { + /* + * I frames will be freed from the socket layer, so we + * need to keep them alive until someone receives them. + */ + skb_get(skb); + } else { + pr_err("Receive queue is full\n"); kfree_skb(skb); } } @@ -1299,6 +1311,13 @@ static void nfc_llcp_rx_work(struct work_struct *work) local->rx_pending = NULL; } +static void __nfc_llcp_recv(struct nfc_llcp_local *local, struct sk_buff *skb) +{ + local->rx_pending = skb; + del_timer(&local->link_timer); + schedule_work(&local->rx_work); +} + void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) { struct nfc_llcp_local *local = (struct nfc_llcp_local *) data; @@ -1309,9 +1328,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) return; } - local->rx_pending = skb_get(skb); - del_timer(&local->link_timer); - schedule_work(&local->rx_work); + __nfc_llcp_recv(local, skb); } int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) @@ -1322,9 +1339,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) if (local == NULL) return -ENODEV; - local->rx_pending = skb_get(skb); - del_timer(&local->link_timer); - schedule_work(&local->rx_work); + __nfc_llcp_recv(local, skb); return 0; } -- cgit v1.2.3 From f0c9103813b3045bd5b43220b6a78c9908a45d24 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Mon, 26 Nov 2012 18:06:27 +0100 Subject: NFC: Fixed nfc core and hci unregistration and cleanup When an adapter is removed, it will unregister itself from hci and/or nfc core. In order to do that safely, work tasks must first be canceled and prevented to be scheduled again, before the hci or nfc device can be destroyed. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 2 ++ include/net/nfc/nfc.h | 2 ++ net/nfc/core.c | 47 ++++++++++++++++++++++------------------------- net/nfc/hci/core.c | 31 ++++++++++++++++++++++++------- net/nfc/hci/hcp.c | 7 +++++++ 5 files changed, 57 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 671953e11575..e6224571e5e6 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -87,6 +87,8 @@ struct nfc_hci_dev { u32 max_data_link_payload; + bool shutting_down; + struct mutex msg_tx_mutex; struct list_head msg_tx_queue; diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index fce80b2f9be7..1665674e86b2 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -115,6 +115,8 @@ struct nfc_dev { struct timer_list check_pres_timer; struct work_struct check_pres_work; + bool shutting_down; + struct nfc_ops *ops; }; #define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev) diff --git a/net/nfc/core.c b/net/nfc/core.c index aa64ea441676..7d7b4ee34015 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -338,7 +338,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) dev->active_target = target; dev->rf_mode = NFC_RF_INITIATOR; - if (dev->ops->check_presence) + if (dev->ops->check_presence && !dev->shutting_down) mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } @@ -429,7 +429,7 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, cb_context); - if (!rc && dev->ops->check_presence) + if (!rc && dev->ops->check_presence && !dev->shutting_down) mod_timer(&dev->check_pres_timer, jiffies + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { @@ -684,11 +684,6 @@ static void nfc_release(struct device *d) pr_debug("dev_name=%s\n", dev_name(&dev->dev)); - if (dev->ops->check_presence) { - del_timer_sync(&dev->check_pres_timer); - cancel_work_sync(&dev->check_pres_work); - } - nfc_genl_data_exit(&dev->genl_data); kfree(dev->targets); kfree(dev); @@ -706,15 +701,16 @@ static void nfc_check_pres_work(struct work_struct *work) rc = dev->ops->check_presence(dev, dev->active_target); if (rc == -EOPNOTSUPP) goto exit; - if (!rc) { - mod_timer(&dev->check_pres_timer, jiffies + - msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); - } else { + if (rc) { u32 active_target_idx = dev->active_target->idx; device_unlock(&dev->dev); nfc_target_lost(dev, active_target_idx); return; } + + if (!dev->shutting_down) + mod_timer(&dev->check_pres_timer, jiffies + + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); } exit: @@ -853,26 +849,27 @@ void nfc_unregister_device(struct nfc_dev *dev) id = dev->idx; - mutex_lock(&nfc_devlist_mutex); - nfc_devlist_generation++; - - /* lock to avoid unregistering a device while an operation - is in progress */ - device_lock(&dev->dev); - device_del(&dev->dev); - device_unlock(&dev->dev); + if (dev->ops->check_presence) { + device_lock(&dev->dev); + dev->shutting_down = true; + device_unlock(&dev->dev); + del_timer_sync(&dev->check_pres_timer); + cancel_work_sync(&dev->check_pres_work); + } - mutex_unlock(&nfc_devlist_mutex); + rc = nfc_genl_device_removed(dev); + if (rc) + pr_debug("The userspace won't be notified that the device %s " + "was removed\n", dev_name(&dev->dev)); nfc_llcp_unregister_device(dev); - rc = nfc_genl_device_removed(dev); - if (rc) - pr_debug("The userspace won't be notified that the device %s was removed\n", - dev_name(&dev->dev)); + mutex_lock(&nfc_devlist_mutex); + nfc_devlist_generation++; + device_del(&dev->dev); + mutex_unlock(&nfc_devlist_mutex); ida_simple_remove(&nfc_index_ida, id); - } EXPORT_SYMBOL(nfc_unregister_device); diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 7bea574d5934..b4b84268653d 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -57,6 +57,8 @@ static void nfc_hci_msg_tx_work(struct work_struct *work) int r = 0; mutex_lock(&hdev->msg_tx_mutex); + if (hdev->shutting_down) + goto exit; if (hdev->cmd_pending_msg) { if (timer_pending(&hdev->cmd_timer) == 0) { @@ -868,6 +870,28 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) { struct hci_msg *msg, *n; + mutex_lock(&hdev->msg_tx_mutex); + + if (hdev->cmd_pending_msg) { + if (hdev->cmd_pending_msg->cb) + hdev->cmd_pending_msg->cb( + hdev->cmd_pending_msg->cb_context, + NULL, -ESHUTDOWN); + kfree(hdev->cmd_pending_msg); + hdev->cmd_pending_msg = NULL; + } + + hdev->shutting_down = true; + + mutex_unlock(&hdev->msg_tx_mutex); + + del_timer_sync(&hdev->cmd_timer); + cancel_work_sync(&hdev->msg_tx_work); + + cancel_work_sync(&hdev->msg_rx_work); + + nfc_unregister_device(hdev->ndev); + skb_queue_purge(&hdev->rx_hcp_frags); skb_queue_purge(&hdev->msg_rx_queue); @@ -876,13 +900,6 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev) skb_queue_purge(&msg->msg_frags); kfree(msg); } - - del_timer_sync(&hdev->cmd_timer); - - nfc_unregister_device(hdev->ndev); - - cancel_work_sync(&hdev->msg_tx_work); - cancel_work_sync(&hdev->msg_rx_work); } EXPORT_SYMBOL(nfc_hci_unregister_device); diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c index bc308a7ca609..b6b4109f2343 100644 --- a/net/nfc/hci/hcp.c +++ b/net/nfc/hci/hcp.c @@ -105,6 +105,13 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, } mutex_lock(&hdev->msg_tx_mutex); + + if (hdev->shutting_down) { + err = -ESHUTDOWN; + mutex_unlock(&hdev->msg_tx_mutex); + goto out_skb_err; + } + list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); mutex_unlock(&hdev->msg_tx_mutex); -- cgit v1.2.3 From 27c31191b3d7ff32c266a5dbea344b9aa96ebf14 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Wed, 28 Nov 2012 15:48:44 +0100 Subject: NFC: Added error handling in event_received hci ops There is no use to return an error if the caller doesn't get it. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 27 +++++++++++++-------------- include/net/nfc/hci.h | 4 ++-- net/nfc/hci/core.c | 8 +++++--- 3 files changed, 20 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cc666de3b8e5..4af70f9c01aa 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -714,8 +714,8 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return 0; } -static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, - u8 event, struct sk_buff *skb) +static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; int r = 0; @@ -724,25 +724,23 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, switch (event) { case PN544_HCI_EVT_ACTIVATED: if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) - nfc_hci_target_discovered(hdev, gate); + r = nfc_hci_target_discovered(hdev, gate); else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, - &rgb_skb); - + &rgb_skb); if (r < 0) goto exit; - nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, - NFC_COMM_PASSIVE, rgb_skb->data, - rgb_skb->len); + r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, rgb_skb->data, + rgb_skb->len); kfree_skb(rgb_skb); } - break; case PN544_HCI_EVT_DEACTIVATED: - nfc_hci_send_event(hdev, gate, - NFC_HCI_EVT_END_OPERATION, NULL, 0); + r = nfc_hci_send_event(hdev, gate, NFC_HCI_EVT_END_OPERATION, + NULL, 0); break; case PN544_HCI_EVT_RCV_DATA: if (skb->len < 2) { @@ -757,15 +755,16 @@ static void pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, } skb_pull(skb, 2); - nfc_tm_data_received(hdev->ndev, skb); - - return; + return nfc_tm_data_received(hdev->ndev, skb); default: + pr_err("Discarded unknown event %x to gate %x\n", event, gate); break; } exit: kfree_skb(skb); + + return r; } static struct nfc_hci_ops pn544_hci_ops = { diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index e6224571e5e6..834e36481aff 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -57,8 +57,8 @@ struct nfc_hci_ops { int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); - void (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, - struct sk_buff *skb); + int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb); }; /* Pipes */ diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index b4b84268653d..f30f6fe815b4 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -323,16 +323,18 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, break; default: if (hdev->ops->event_received) { - hdev->ops->event_received(hdev, gate, event, skb); - return; + r = hdev->ops->event_received(hdev, gate, event, skb); + goto exit_noskb; + } else { + r = -EINVAL; } - break; } exit: kfree_skb(skb); +exit_noskb: if (r) { /* TODO: There was an error dispatching the event, * how to propagate up to nfc core? -- cgit v1.2.3 From 40d06d3647ea872a7346be1f6859f18cd0fe08d3 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 4 Dec 2012 16:43:24 +0100 Subject: NFC: Changed event_received hci ops result semantic Some chips use a standard HCI event code, destined to a proprietary gate, with a different meaning. Therefore, the HCI driver must always have a chance to intercept the event before standard processing is attempted. The new semantic specifies that the result value "1" means that the driver doesn't especially handle the event. result <= 0 means it was handled. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 16 +++++++++++----- net/nfc/hci/core.c | 14 ++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 4af70f9c01aa..9349c548e8dc 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -714,18 +714,23 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, return 0; } +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; - int r = 0; + int r; pr_debug("hci event %d", event); switch (event) { case PN544_HCI_EVT_ACTIVATED: - if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) + if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) { r = nfc_hci_target_discovered(hdev, gate); - else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { + } else if (gate == PN544_RF_READER_NFCIP1_TARGET_GATE) { r = nfc_hci_get_param(hdev, gate, PN544_DEP_ATR_REQ, &rgb_skb); if (r < 0) @@ -736,6 +741,8 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, rgb_skb->len); kfree_skb(rgb_skb); + } else { + r = -EINVAL; } break; case PN544_HCI_EVT_DEACTIVATED: @@ -757,8 +764,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, skb_pull(skb, 2); return nfc_tm_data_received(hdev->ndev, skb); default: - pr_err("Discarded unknown event %x to gate %x\n", event, gate); - break; + return 1; } exit: diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index f30f6fe815b4..0430f3086e41 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -297,6 +297,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, goto exit; } + if (hdev->ops->event_received) { + r = hdev->ops->event_received(hdev, gate, event, skb); + if (r <= 0) + goto exit_noskb; + } + switch (event) { case NFC_HCI_EVT_TARGET_DISCOVERED: if (skb->len < 1) { /* no status data? */ @@ -322,12 +328,8 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, r = nfc_hci_target_discovered(hdev, gate); break; default: - if (hdev->ops->event_received) { - r = hdev->ops->event_received(hdev, gate, event, skb); - goto exit_noskb; - } else { - r = -EINVAL; - } + pr_info("Discarded unknown event %x to gate %x\n", event, gate); + r = -EINVAL; break; } -- cgit v1.2.3 From 924d4a023ee6da2e40c78578829e68bcbabee2dd Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 4 Dec 2012 16:44:25 +0100 Subject: NFC: Fixed skb leak in tm_send() nfc and hci ops implementations Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 10 ++++++++-- net/nfc/hci/core.c | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 9349c548e8dc..cd8fb16f5416 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -675,11 +675,17 @@ static int pn544_hci_im_transceive(struct nfc_hci_dev *hdev, static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) { + int r; + /* Set default false for multiple information chaining */ *skb_push(skb, 1) = 0; - return nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, - PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + r = nfc_hci_send_event(hdev, PN544_RF_READER_NFCIP1_TARGET_GATE, + PN544_HCI_EVT_SND_DATA, skb->data, skb->len); + + kfree_skb(skb); + + return r; } static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 0430f3086e41..d9190da4a403 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -675,8 +675,10 @@ static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) if (hdev->ops->tm_send) return hdev->ops->tm_send(hdev, skb); - else - return -ENOTSUPP; + + kfree_skb(skb); + + return -ENOTSUPP; } static int hci_check_presence(struct nfc_dev *nfc_dev, -- cgit v1.2.3 From bf71ab8ba53081c28b960d48e0c4cd1c17588aa6 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Dec 2012 14:15:49 +0100 Subject: NFC: Add HCI quirks to support driver (non)standard implementations Some chips diverge from the HCI spec in their implementation of standard features. This adds a new quirks parameter to nfc_hci_allocate_device() to let the driver indicate its divergence. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/pn544.c | 2 +- include/net/nfc/hci.h | 13 +++++++++++++ net/nfc/hci/command.c | 7 ++++++- net/nfc/hci/core.c | 3 +++ 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index cd8fb16f5416..ece834239852 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -833,7 +833,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; - info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, protocols, llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 834e36481aff..2ff71750c428 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -82,6 +82,16 @@ typedef int (*xmit) (struct sk_buff *skb, void *cb_data); #define NFC_HCI_MAX_GATES 256 +/* + * These values can be specified by a driver to indicate it requires some + * adaptation of the HCI standard. + * + * NFC_HCI_QUIRK_SHORT_CLEAR - send HCI_ADM_CLEAR_ALL_PIPE cmd with no params + */ +enum { + NFC_HCI_QUIRK_SHORT_CLEAR = 0, +}; + struct nfc_hci_dev { struct nfc_dev *ndev; @@ -131,11 +141,14 @@ struct nfc_hci_dev { u8 *gb; size_t gb_len; + + unsigned long quirks; }; /* hci device allocation */ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, + unsigned long quirks, u32 protocols, const char *llc_name, int tx_headroom, diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 7d99410e6c1a..64f922be9281 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -280,14 +280,19 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) { u8 param[2]; + size_t param_len = 2; /* TODO: Find out what the identity reference data is * and fill param with it. HCI spec 6.1.3.5 */ pr_debug("\n"); + if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks)) + param_len = 0; + return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, - NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL); + NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len, + NULL); } int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index d9190da4a403..755a6b9774ab 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -795,6 +795,7 @@ static struct nfc_ops hci_nfc_ops = { struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, + unsigned long quirks, u32 protocols, const char *llc_name, int tx_headroom, @@ -838,6 +839,8 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe)); + hdev->quirks = quirks; + return hdev; } EXPORT_SYMBOL(nfc_hci_allocate_device); -- cgit v1.2.3 From 390a1bd8538132186ddb679cafe9e75b7ef7e2d2 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 19 Dec 2012 19:11:32 +0100 Subject: NFC: Initial Secure Element API Each NFC adapter can have several links to different secure elements and that property needs to be exported by the drivers. A secure element link can be enabled and disabled, and card emulation will be handled by the currently active one. Otherwise card emulation will be host implemented. Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 1 + drivers/nfc/pn533.c | 1 + drivers/nfc/pn544/pn544.c | 6 ++++-- include/net/nfc/hci.h | 3 +++ include/net/nfc/nci_core.h | 1 + include/net/nfc/nfc.h | 6 ++++++ include/uapi/linux/nfc.h | 14 ++++++++++++++ net/nfc/core.c | 3 +++ net/nfc/hci/core.c | 3 ++- net/nfc/nci/core.c | 2 ++ net/nfc/netlink.c | 1 + 11 files changed, 38 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index c7c182d2b7df..3b731acbc408 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -542,6 +542,7 @@ static int nfcwilink_probe(struct platform_device *pdev) drv->ndev = nci_allocate_device(&nfcwilink_ops, protocols, + NFC_SE_NONE, NFCWILINK_HDR_LEN, 0); if (!drv->ndev) { diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index e8c083203b33..31a5b3b53b2a 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -2525,6 +2525,7 @@ static int pn533_probe(struct usb_interface *interface, dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, + NFC_SE_NONE, dev->ops->tx_header_len + PN533_CMD_DATAEXCH_HEAD_LEN, dev->ops->tx_tail_len); diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index d108c794008d..9c5f16e7baef 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -801,7 +801,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, struct nfc_hci_dev **hdev) { struct pn544_hci_info *info; - u32 protocols; + u32 protocols, se; struct nfc_hci_init_data init_data; int r; @@ -834,8 +834,10 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; + se = NFC_SE_UICC | NFC_SE_EMBEDDED; + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, 0, - protocols, llc_name, + protocols, se, llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); if (!info->hdev) { diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 2ff71750c428..b87a1692b086 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -59,6 +59,8 @@ struct nfc_hci_ops { struct nfc_target *target); int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, struct sk_buff *skb); + int (*enable_se)(struct nfc_dev *dev, u32 secure_element); + int (*disable_se)(struct nfc_dev *dev, u32 secure_element); }; /* Pipes */ @@ -150,6 +152,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, unsigned long quirks, u32 protocols, + u32 supported_se, const char *llc_name, int tx_headroom, int tx_tailroom, diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index d705d8674949..5bc0c460edc0 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -147,6 +147,7 @@ struct nci_dev { /* ----- NCI Devices ----- */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, __u32 supported_protocols, + __u32 supported_se, int tx_headroom, int tx_tailroom); void nci_free_device(struct nci_dev *ndev); diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 1665674e86b2..87a6417fc934 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -68,6 +68,8 @@ struct nfc_ops { void *cb_context); int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb); int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target); + int (*enable_se)(struct nfc_dev *dev, u32 secure_element); + int (*disable_se)(struct nfc_dev *dev, u32 secure_element); }; #define NFC_TARGET_IDX_ANY -1 @@ -109,6 +111,9 @@ struct nfc_dev { struct nfc_genl_data genl_data; u32 supported_protocols; + u32 supported_se; + u32 active_se; + int tx_headroom; int tx_tailroom; @@ -125,6 +130,7 @@ extern struct class nfc_class; struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, + u32 supported_se, int tx_headroom, int tx_tailroom); diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 0e63cee8d810..80e4ecd8c04c 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -67,6 +67,11 @@ * subsequent CONNECT and CC messages. * If one of the passed parameters is wrong none is set and -EINVAL is * returned. + * @NFC_CMD_ENABLE_SE: Enable the physical link to a specific secure element. + * Once enabled a secure element will handle card emulation mode, i.e. + * starting a poll from a device which has a secure element enabled means + * we want to do SE based card emulation. + * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -86,6 +91,8 @@ enum nfc_commands { NFC_EVENT_TM_DEACTIVATED, NFC_CMD_LLC_GET_PARAMS, NFC_CMD_LLC_SET_PARAMS, + NFC_CMD_ENABLE_SE, + NFC_CMD_DISABLE_SE, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -114,6 +121,7 @@ enum nfc_commands { * @NFC_ATTR_LLC_PARAM_LTO: Link TimeOut parameter * @NFC_ATTR_LLC_PARAM_RW: Receive Window size parameter * @NFC_ATTR_LLC_PARAM_MIUX: MIU eXtension parameter + * @NFC_ATTR_SE: Available Secure Elements */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -134,6 +142,7 @@ enum nfc_attrs { NFC_ATTR_LLC_PARAM_LTO, NFC_ATTR_LLC_PARAM_RW, NFC_ATTR_LLC_PARAM_MIUX, + NFC_ATTR_SE, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; @@ -172,6 +181,11 @@ enum nfc_attrs { #define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) #define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B) +/* NFC Secure Elements */ +#define NFC_SE_NONE 0x0 +#define NFC_SE_UICC 0x1 +#define NFC_SE_EMBEDDED 0x2 + struct sockaddr_nfc { sa_family_t sa_family; __u32 dev_idx; diff --git a/net/nfc/core.c b/net/nfc/core.c index 7d7b4ee34015..25522e56d350 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -757,6 +757,7 @@ struct nfc_dev *nfc_get_device(unsigned int idx) */ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, u32 supported_protocols, + u32 supported_se, int tx_headroom, int tx_tailroom) { struct nfc_dev *dev; @@ -774,6 +775,8 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, dev->ops = ops; dev->supported_protocols = supported_protocols; + dev->supported_se = supported_se; + dev->active_se = NFC_SE_NONE; dev->tx_headroom = tx_headroom; dev->tx_tailroom = tx_tailroom; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 755a6b9774ab..91020b210d87 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -797,6 +797,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, struct nfc_hci_init_data *init_data, unsigned long quirks, u32 protocols, + u32 supported_se, const char *llc_name, int tx_headroom, int tx_tailroom, @@ -822,7 +823,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, return NULL; } - hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, + hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols, supported_se, tx_headroom + HCI_CMDS_HEADROOM, tx_tailroom); if (!hdev->ndev) { diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 5f98dc1bf039..48ada0ec749e 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -658,6 +658,7 @@ static struct nfc_ops nci_nfc_ops = { */ struct nci_dev *nci_allocate_device(struct nci_ops *ops, __u32 supported_protocols, + __u32 supported_se, int tx_headroom, int tx_tailroom) { struct nci_dev *ndev; @@ -680,6 +681,7 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops, ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, supported_protocols, + supported_se, tx_headroom + NCI_DATA_HDR_SIZE, tx_tailroom); if (!ndev->nfc_dev) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 3568ae16786d..504b883439f1 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -366,6 +366,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || + nla_put_u32(msg, NFC_ATTR_SE, dev->supported_se) || nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) goto nla_put_failure; -- cgit v1.2.3 From 95c7e0e4d4792f9c9629ad759c9be047a3ddbcc5 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:19:37 +0000 Subject: ipv4: Use FIELD_SIZEOF() in inet_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 24b384b7903e..4fdf96710d45 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1705,12 +1705,11 @@ static struct packet_type ip_packet_type __read_mostly = { static int __init inet_init(void) { - struct sk_buff *dummy_skb; struct inet_protosw *q; struct list_head *r; int rc = -EINVAL; - BUILD_BUG_ON(sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb)); sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL); if (!sysctl_local_reserved_ports) -- cgit v1.2.3 From ba96bcbcd213e66306e8669e3442b0b3689dd768 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:19:43 +0000 Subject: ipv6: Use FIELD_SIZEOF() in inet6_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/af_inet6.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index b043c60429bd..6b793bfc0e10 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -811,11 +811,10 @@ static struct pernet_operations inet6_net_ops = { static int __init inet6_init(void) { - struct sk_buff *dummy_skb; struct list_head *r; int err = 0; - BUILD_BUG_ON(sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct inet6_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb)); /* Register the socket-side information for inet6_create. */ for (r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r) -- cgit v1.2.3 From fab25745912a82baa7c9e73f59079108d56b5d26 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:19:48 +0000 Subject: netlink: Use FIELD_SIZEOF() in netlink_proto_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c0353d55d56f..74827e3b26a1 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2185,7 +2185,6 @@ static struct pernet_operations __net_initdata netlink_net_ops = { static int __init netlink_proto_init(void) { - struct sk_buff *dummy_skb; int i; unsigned long limit; unsigned int order; @@ -2194,7 +2193,7 @@ static int __init netlink_proto_init(void) if (err != 0) goto out; - BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL); if (!nl_table) -- cgit v1.2.3 From 3523b29bd2cd58432ea1bf3629695ec1ab607722 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:19:55 +0000 Subject: openvswitch: Use FIELD_SIZEOF() in dp_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/openvswitch/datapath.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f996db343247..d8c13a965459 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1989,10 +1989,9 @@ static struct pernet_operations ovs_net_ops = { static int __init dp_init(void) { - struct sk_buff *dummy_skb; int err; - BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb)); pr_info("Open vSwitch switching datapath\n"); -- cgit v1.2.3 From ce6654cfc12558cc65b198cca991684e58e10bc6 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:20:01 +0000 Subject: rxrpc: Use FIELD_SIZEOF() in af_rxrpc_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/rxrpc/af_rxrpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 05996d0dd828..5b0fd291babb 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -792,10 +793,9 @@ static const struct net_proto_family rxrpc_family_ops = { */ static int __init af_rxrpc_init(void) { - struct sk_buff *dummy_skb; int ret = -1; - BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > FIELD_SIZEOF(struct sk_buff, cb)); rxrpc_epoch = htonl(get_seconds()); -- cgit v1.2.3 From b4fff5f8bf32766ecaa4af6d5c7ef7f0ffb88ed2 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 9 Jan 2013 07:20:07 +0000 Subject: unix: Use FIELD_SIZEOF() in af_unix_init(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/unix/af_unix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 5b5c876c80e9..0c612361c153 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2426,9 +2426,8 @@ static struct pernet_operations unix_net_ops = { static int __init af_unix_init(void) { int rc = -1; - struct sk_buff *dummy_skb; - BUILD_BUG_ON(sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb)); + BUILD_BUG_ON(sizeof(struct unix_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb)); rc = proto_register(&unix_proto, 1); if (rc != 0) { -- cgit v1.2.3 From 6c40d100ce3311a44ef82c678fb46833a270c007 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 8 Jan 2013 06:44:23 +0000 Subject: ipv6: Use container_of macro instead of magic number to get ipv6 header. In ipv6_recv_error(), addr_offset points to daddr field of the ip header. To get ipv6 header, use container_of() macro instead of substracting magic number (24). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/datagram.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 8edf2601065a..56b692bb94a7 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -356,12 +356,11 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) sin->sin6_port = serr->port; sin->sin6_scope_id = 0; if (skb->protocol == htons(ETH_P_IPV6)) { - sin->sin6_addr = - *(struct in6_addr *)(nh + serr->addr_offset); + const struct ipv6hdr *ip6h = container_of((struct in6_addr *)(nh + serr->addr_offset), + struct ipv6hdr, daddr); + sin->sin6_addr = ip6h->daddr; if (np->sndflow) - sin->sin6_flowinfo = - (*(__be32 *)(nh + serr->addr_offset - 24) & - IPV6_FLOWINFO_MASK); + sin->sin6_flowinfo = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK; if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin->sin6_scope_id = IP6CB(skb)->iif; } else { -- cgit v1.2.3 From e384662b1c1004e6b1e9d2c4979945a383c07b7d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:33 +0200 Subject: Bluetooth: Fix missing command complete event for mgmt_confirm_name All management commands are expected to indicate successful completion through a command complete event however the confirm name command was missing it. This patch add the sending of the missing event. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5502a56365b..577f316d87ad 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2493,7 +2493,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, hci_inquiry_cache_update_resolve(hdev, e); } - err = 0; + err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr, + sizeof(cp->addr)); failed: hci_dev_unlock(hdev); -- cgit v1.2.3 From 715a5bf2db4df4a7df64f420d21fb49ba146b3fa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:34 +0200 Subject: Bluetooth: Fix missing command complete for mgmt_load_long_term_keys All management events are expected to indicate successful completion through a command complete event, however the load long term keys command was missing this. This patch adds the missing event. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 577f316d87ad..1dd41d48eb66 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2648,7 +2648,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, { struct mgmt_cp_load_long_term_keys *cp = cp_data; u16 key_count, expected_len; - int i; + int i, err; key_count = __le16_to_cpu(cp->key_count); @@ -2682,9 +2682,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, key->enc_size, key->ediv, key->rand); } + err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0, + NULL, 0); + hci_dev_unlock(hdev); - return 0; + return err; } static const struct mgmt_handler { -- cgit v1.2.3 From 575b3a02e20a10bb8110378ef363a8a174018680 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:35 +0200 Subject: Bluetooth: Fix checking for valid device class values The two lowest bits of the minor device class value are reserved and should be zero, and the three highest bits of the major device class likewise. The management code should therefore test for this and return a proper "invalid params" error if the condition is not met. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1dd41d48eb66..f3fec4264dcf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1430,6 +1430,12 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + hdev->major_class = cp->major; hdev->minor_class = cp->minor; -- cgit v1.2.3 From ee98f4738050bb93823ce9ba849f5d78f5b8c1a1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:36 +0200 Subject: Bluetooth: Fix accepting set_dev_class for non-BR/EDR controllers The concept of Class of Device only exists for BR/EDR controllers. The mgmt_set_dev_class command should therefore return a proper "not supported" error if it is attempted for a controller that doesn't support BR/EDR (e.g. a single mode LE-only one). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f3fec4264dcf..d78ce81d2cf4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1424,6 +1424,12 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); + if (!lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_NOT_SUPPORTED); + goto unlock; + } + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, MGMT_STATUS_BUSY); -- cgit v1.2.3 From 13ecd8b6628c14c9a27832ce7c48315385272208 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:38 +0200 Subject: Bluetooth: Move non-critical sections outside of the dev lock This patch fixes sections of code that do not need hci_lock_dev to be outside of the lock. Such sections include code that do not touch the hdev at all as well as sections which just read a single byte from the supported_features value (i.e. all lmp_*_capable() macros). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d78ce81d2cf4..28e01f992231 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1133,13 +1133,11 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - hci_dev_lock(hdev); + if (!lmp_ssp_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, + MGMT_STATUS_NOT_SUPPORTED); - if (!lmp_ssp_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_SUPPORTED); - goto failed; - } + hci_dev_lock(hdev); val = !!cp->val; @@ -1217,13 +1215,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - hci_dev_lock(hdev); + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, + MGMT_STATUS_NOT_SUPPORTED); - if (!lmp_le_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } + hci_dev_lock(hdev); val = !!cp->val; enabled = lmp_host_le_capable(hdev); @@ -1422,25 +1418,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - hci_dev_lock(hdev); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_NOT_SUPPORTED); - if (!lmp_bredr_capable(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } + if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_BUSY); - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); - goto unlock; - } + if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); - if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } + hci_dev_lock(hdev); hdev->major_class = cp->major; hdev->minor_class = cp->minor; -- cgit v1.2.3 From a7e80f25ae2296d78163d75d753c796270464000 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 16:05:19 +0200 Subject: Bluetooth: Fix checking for exact values of boolean mgmt parameters All mgmt_set_* commands that take a boolean value encoded in the form of a byte should only accept the values 0x00 and 0x01. This patch adds the necessary checks for this and returns "invalid params" responses if anything else is provided as the value. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 28e01f992231..3959c471b2b4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -777,6 +777,10 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { @@ -872,6 +876,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_INVALID_PARAMS); + timeout = __le16_to_cpu(cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, @@ -971,6 +979,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1041,6 +1053,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (cp->val) @@ -1073,6 +1089,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1137,6 +1157,10 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); val = !!cp->val; @@ -1197,6 +1221,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, + MGMT_STATUS_INVALID_PARAMS); + if (cp->val) set_bit(HCI_HS_ENABLED, &hdev->dev_flags); else @@ -1219,6 +1247,10 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); val = !!cp->val; @@ -2598,6 +2630,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); + if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_POWERED); -- cgit v1.2.3 From 04106755763f558886a631338d12546345bae6e9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jan 2013 14:54:09 +0200 Subject: Bluetooth: Fix returning proper command status for start_discovery Management commands should whenever possible fail with proper command status or command complete events. This patch fixes the mgmt_start_discovery command to do this for the failure cases where an incorrect parameter value was passed to it ("not supported" if the parameter value was valid but the controller doesn't support it and "invalid params" if it isn't valid at all). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3959c471b2b4..b1b0a36e1e80 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2382,31 +2382,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - if (lmp_bredr_capable(hdev)) - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); - else - err = -ENOTSUPP; + if (!lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); break; case DISCOV_TYPE_LE: - if (lmp_host_le_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); - else - err = -ENOTSUPP; + if (!lmp_host_le_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, + LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); break; case DISCOV_TYPE_INTERLEAVED: - if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, - LE_SCAN_TIMEOUT_BREDR_LE); - else - err = -ENOTSUPP; + if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED); + mgmt_pending_remove(cmd); + goto failed; + } + + err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN, + LE_SCAN_TIMEOUT_BREDR_LE); break; default: - err = -EINVAL; + err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + mgmt_pending_remove(cmd); + goto failed; } if (err < 0) -- cgit v1.2.3 From a1d704509d5b96756d3d4cfb7f10a555efeadb87 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2013 15:29:40 +0200 Subject: Bluetooth: Fix sending incorrect new_settings for mgmt_set_powered The socket from which a mgmt_set_powered command was received should only receive the command response but no new_settings event. The mgmt_powered() function which is used to handle the situation with the HCI_AUTO_OFF flag tries to check for a pending command to know which socket to skip the event for, but since the pending command hasn't been added this will not happen. This patch fixes the issue by adding the pending command for the HCI_AUTO_OFF case and thereby ensures that mgmt_powered() will skip the right socket when sending the new_settings event, but still send the proper response to the socket where the command came from. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b1b0a36e1e80..37add53ce613 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -787,8 +787,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, cancel_delayed_work(&hdev->power_off); if (cp->val) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - mgmt_powered(hdev, 1); + mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, + data, len); + err = mgmt_powered(hdev, 1); goto failed; } } -- cgit v1.2.3 From 52e0b011e29f36bc5c02ea3adbf4d864a38373de Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Thu, 10 Jan 2013 06:06:29 -0200 Subject: Bluetooth: Fix uuid output in debugfs The uuid should be printed in the CPU endianness and not in little-endian. Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_sysfs.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 55cceee02a84..23b4e242a31a 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -461,19 +462,18 @@ static const struct file_operations blacklist_fops = { static void print_bt_uuid(struct seq_file *f, u8 *uuid) { - __be32 data0, data4; - __be16 data1, data2, data3, data5; + u32 data0, data5; + u16 data1, data2, data3, data4; - memcpy(&data0, &uuid[0], 4); - memcpy(&data1, &uuid[4], 2); - memcpy(&data2, &uuid[6], 2); - memcpy(&data3, &uuid[8], 2); - memcpy(&data4, &uuid[10], 4); - memcpy(&data5, &uuid[14], 2); + data5 = get_unaligned_le32(uuid); + data4 = get_unaligned_le16(uuid + 4); + data3 = get_unaligned_le16(uuid + 6); + data2 = get_unaligned_le16(uuid + 8); + data1 = get_unaligned_le16(uuid + 10); + data0 = get_unaligned_le32(uuid + 12); - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", - ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3), - ntohl(data4), ntohs(data5)); + seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n", + data0, data1, data2, data3, data4, data5); } static int uuids_show(struct seq_file *f, void *p) -- cgit v1.2.3 From 1def9238d4aa2146924994aa4b7dc861f03b9362 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 10 Jan 2013 12:36:42 +0000 Subject: net_sched: more precise pkt_len computation One long standing problem with TSO/GSO/GRO packets is that skb->len doesn't represent a precise amount of bytes on wire. Headers are only accounted for the first segment. For TCP, thats typically 66 bytes per 1448 bytes segment missing, an error of 4.5 % for normal MSS value. As consequences : 1) TBF/CBQ/HTB/NETEM/... can send more bytes than the assigned limits. 2) Device stats are slightly under estimated as well. Fix this by taking account of headers in qdisc_skb_cb(skb)->pkt_len computation. Packet schedulers should use qdisc pkt_len instead of skb->len for their bandwidth limitations, and TSO enabled devices drivers could use pkt_len if their statistics are not hardware assisted, and if they don't scratch skb->cb[] first word. Both egress and ingress paths work, thanks to commit fda55eca5a (net: introduce skb_transport_header_was_set()) : If GRO built a GSO packet, it also set the transport header for us. Signed-off-by: Eric Dumazet Cc: Jamal Hadi Salim Cc: Stephen Hemminger Cc: Paolo Valente Cc: Herbert Xu Cc: Patrick McHardy Signed-off-by: David S. Miller --- net/core/dev.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 594830e83589..4794cae84939 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2532,6 +2532,26 @@ struct netdev_queue *netdev_pick_tx(struct net_device *dev, return netdev_get_tx_queue(dev, queue_index); } +static void qdisc_pkt_len_init(struct sk_buff *skb) +{ + const struct skb_shared_info *shinfo = skb_shinfo(skb); + + qdisc_skb_cb(skb)->pkt_len = skb->len; + + /* To get more precise estimation of bytes sent on wire, + * we add to pkt_len the headers size of all segments + */ + if (shinfo->gso_size) { + unsigned int hdr_len = skb_transport_offset(skb); + + if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) + hdr_len += tcp_hdrlen(skb); + else + hdr_len += sizeof(struct udphdr); + qdisc_skb_cb(skb)->pkt_len += (shinfo->gso_segs - 1) * hdr_len; + } +} + static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct net_device *dev, struct netdev_queue *txq) @@ -2540,7 +2560,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, bool contended; int rc; - qdisc_skb_cb(skb)->pkt_len = skb->len; + qdisc_pkt_len_init(skb); qdisc_calculate_pkt_len(skb, q); /* * Heuristic to force contended enqueues to serialize on a -- cgit v1.2.3 From 416186fbf8c5b4e4465a10c6ac7a45b6c47144b2 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Jan 2013 08:56:51 +0000 Subject: net: Split core bits of netdev_pick_tx into __netdev_pick_tx This change splits the core bits of netdev_pick_tx into a separate function. The main idea behind this is to make this code accessible to select queue functions when they decide to process the standard path instead of their own custom path in their select queue routine. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 57 ++++++++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0209ac328e8a..608c3ac4d045 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1403,6 +1403,7 @@ static inline void netdev_for_each_tx_queue(struct net_device *dev, extern struct netdev_queue *netdev_pick_tx(struct net_device *dev, struct sk_buff *skb); +extern u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb); /* * Net namespace inlines diff --git a/net/core/dev.c b/net/core/dev.c index 4794cae84939..81ff67149f62 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2495,37 +2495,44 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) #endif } -struct netdev_queue *netdev_pick_tx(struct net_device *dev, - struct sk_buff *skb) +u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) { - int queue_index; - const struct net_device_ops *ops = dev->netdev_ops; - - if (dev->real_num_tx_queues == 1) - queue_index = 0; - else if (ops->ndo_select_queue) { - queue_index = ops->ndo_select_queue(dev, skb); - queue_index = dev_cap_txqueue(dev, queue_index); - } else { - struct sock *sk = skb->sk; - queue_index = sk_tx_queue_get(sk); + struct sock *sk = skb->sk; + int queue_index = sk_tx_queue_get(sk); - if (queue_index < 0 || skb->ooo_okay || - queue_index >= dev->real_num_tx_queues) { - int old_index = queue_index; + if (queue_index < 0 || skb->ooo_okay || + queue_index >= dev->real_num_tx_queues) { + int new_index = get_xps_queue(dev, skb); + if (new_index < 0) + new_index = skb_tx_hash(dev, skb); - queue_index = get_xps_queue(dev, skb); - if (queue_index < 0) - queue_index = skb_tx_hash(dev, skb); - - if (queue_index != old_index && sk) { - struct dst_entry *dst = + if (queue_index != new_index && sk) { + struct dst_entry *dst = rcu_dereference_check(sk->sk_dst_cache, 1); - if (dst && skb_dst(skb) == dst) - sk_tx_queue_set(sk, queue_index); - } + if (dst && skb_dst(skb) == dst) + sk_tx_queue_set(sk, queue_index); + } + + queue_index = new_index; + } + + return queue_index; +} + +struct netdev_queue *netdev_pick_tx(struct net_device *dev, + struct sk_buff *skb) +{ + int queue_index = 0; + + if (dev->real_num_tx_queues != 1) { + const struct net_device_ops *ops = dev->netdev_ops; + if (ops->ndo_select_queue) + queue_index = ops->ndo_select_queue(dev, skb); + else + queue_index = __netdev_pick_tx(dev, skb); + queue_index = dev_cap_txqueue(dev, queue_index); } skb_set_queue_mapping(skb, queue_index); -- cgit v1.2.3 From 537c00de1c9ba9876b91d869e84caceefe2b8bf9 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Jan 2013 08:57:02 +0000 Subject: net: Add functions netif_reset_xps_queue and netif_set_xps_queue This patch adds two functions, netif_reset_xps_queue and netif_set_xps_queue. The main idea behind these two functions is to provide a mechanism through which drivers can update their defaults in regards to XPS. Currently no such mechanism exists and as a result we cannot use XPS for things such as ATR which would require a basic configuration to start in which the Tx queues are mapped to CPUs via a 1:1 mapping. With this change I am making it possible for drivers such as ixgbe to be able to use the XPS feature by controlling the default configuration. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 13 ++++ net/core/dev.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++ net/core/net-sysfs.c | 148 ++----------------------------------------- 3 files changed, 173 insertions(+), 143 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 608c3ac4d045..59fe9da4e315 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2103,6 +2103,19 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) __netif_schedule(txq->qdisc); } +#ifdef CONFIG_XPS +extern void netif_reset_xps_queue(struct net_device *dev, u16 index); +extern int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, + u16 index); +#else +static inline int netif_set_xps_queue(struct net_device *dev, + struct cpumask *mask, + u16 index) +{ + return 0; +} +#endif + /* * Returns a Tx hash for the given packet when dev->real_num_tx_queues is used * as a distribution range limit for the returned value. diff --git a/net/core/dev.c b/net/core/dev.c index 81ff67149f62..257b29516f69 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1857,6 +1857,161 @@ static void netif_setup_tc(struct net_device *dev, unsigned int txq) } } +#ifdef CONFIG_XPS +static DEFINE_MUTEX(xps_map_mutex); +#define xmap_dereference(P) \ + rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) + +void netif_reset_xps_queue(struct net_device *dev, u16 index) +{ + struct xps_dev_maps *dev_maps; + struct xps_map *map; + int i, pos, nonempty = 0; + + mutex_lock(&xps_map_mutex); + dev_maps = xmap_dereference(dev->xps_maps); + + if (!dev_maps) + goto out_no_maps; + + for_each_possible_cpu(i) { + map = xmap_dereference(dev_maps->cpu_map[i]); + if (!map) + continue; + + for (pos = 0; pos < map->len; pos++) + if (map->queues[pos] == index) + break; + + if (pos < map->len) { + if (map->len > 1) { + map->queues[pos] = map->queues[--map->len]; + } else { + RCU_INIT_POINTER(dev_maps->cpu_map[i], NULL); + kfree_rcu(map, rcu); + map = NULL; + } + } + if (map) + nonempty = 1; + } + + if (!nonempty) { + RCU_INIT_POINTER(dev->xps_maps, NULL); + kfree_rcu(dev_maps, rcu); + } + +out_no_maps: + mutex_unlock(&xps_map_mutex); +} + +int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, u16 index) +{ + int i, cpu, pos, map_len, alloc_len, need_set; + struct xps_map *map, *new_map; + struct xps_dev_maps *dev_maps, *new_dev_maps; + int nonempty = 0; + int numa_node_id = -2; + int maps_sz = max_t(unsigned int, XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES); + + new_dev_maps = kzalloc(maps_sz, GFP_KERNEL); + if (!new_dev_maps) + return -ENOMEM; + + mutex_lock(&xps_map_mutex); + + dev_maps = xmap_dereference(dev->xps_maps); + + for_each_possible_cpu(cpu) { + map = dev_maps ? + xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; + new_map = map; + if (map) { + for (pos = 0; pos < map->len; pos++) + if (map->queues[pos] == index) + break; + map_len = map->len; + alloc_len = map->alloc_len; + } else + pos = map_len = alloc_len = 0; + + need_set = cpumask_test_cpu(cpu, mask) && cpu_online(cpu); +#ifdef CONFIG_NUMA + if (need_set) { + if (numa_node_id == -2) + numa_node_id = cpu_to_node(cpu); + else if (numa_node_id != cpu_to_node(cpu)) + numa_node_id = -1; + } +#endif + if (need_set && pos >= map_len) { + /* Need to add queue to this CPU's map */ + if (map_len >= alloc_len) { + alloc_len = alloc_len ? + 2 * alloc_len : XPS_MIN_MAP_ALLOC; + new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), + GFP_KERNEL, + cpu_to_node(cpu)); + if (!new_map) + goto error; + new_map->alloc_len = alloc_len; + for (i = 0; i < map_len; i++) + new_map->queues[i] = map->queues[i]; + new_map->len = map_len; + } + new_map->queues[new_map->len++] = index; + } else if (!need_set && pos < map_len) { + /* Need to remove queue from this CPU's map */ + if (map_len > 1) + new_map->queues[pos] = + new_map->queues[--new_map->len]; + else + new_map = NULL; + } + RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], new_map); + } + + /* Cleanup old maps */ + for_each_possible_cpu(cpu) { + map = dev_maps ? + xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; + if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) + kfree_rcu(map, rcu); + if (new_dev_maps->cpu_map[cpu]) + nonempty = 1; + } + + if (nonempty) { + rcu_assign_pointer(dev->xps_maps, new_dev_maps); + } else { + kfree(new_dev_maps); + RCU_INIT_POINTER(dev->xps_maps, NULL); + } + + if (dev_maps) + kfree_rcu(dev_maps, rcu); + + netdev_queue_numa_node_write(netdev_get_tx_queue(dev, index), + (numa_node_id >= 0) ? numa_node_id : + NUMA_NO_NODE); + + mutex_unlock(&xps_map_mutex); + + return 0; +error: + mutex_unlock(&xps_map_mutex); + + if (new_dev_maps) + for_each_possible_cpu(i) + kfree(rcu_dereference_protected( + new_dev_maps->cpu_map[i], + 1)); + kfree(new_dev_maps); + return -ENOMEM; +} +EXPORT_SYMBOL(netif_set_xps_queue); + +#endif /* * Routine to help set real_num_tx_queues. To avoid skbs mapped to queues * greater then real_num_tx_queues stale skbs on the qdisc must be flushed. diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 29c884a74c38..5ad489d5d062 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1002,54 +1002,14 @@ static ssize_t show_xps_map(struct netdev_queue *queue, return len; } -static DEFINE_MUTEX(xps_map_mutex); -#define xmap_dereference(P) \ - rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) - static void xps_queue_release(struct netdev_queue *queue) { struct net_device *dev = queue->dev; - struct xps_dev_maps *dev_maps; - struct xps_map *map; unsigned long index; - int i, pos, nonempty = 0; index = get_netdev_queue_index(queue); - mutex_lock(&xps_map_mutex); - dev_maps = xmap_dereference(dev->xps_maps); - - if (dev_maps) { - for_each_possible_cpu(i) { - map = xmap_dereference(dev_maps->cpu_map[i]); - if (!map) - continue; - - for (pos = 0; pos < map->len; pos++) - if (map->queues[pos] == index) - break; - - if (pos < map->len) { - if (map->len > 1) - map->queues[pos] = - map->queues[--map->len]; - else { - RCU_INIT_POINTER(dev_maps->cpu_map[i], - NULL); - kfree_rcu(map, rcu); - map = NULL; - } - } - if (map) - nonempty = 1; - } - - if (!nonempty) { - RCU_INIT_POINTER(dev->xps_maps, NULL); - kfree_rcu(dev_maps, rcu); - } - } - mutex_unlock(&xps_map_mutex); + netif_reset_xps_queue(dev, index); } static ssize_t store_xps_map(struct netdev_queue *queue, @@ -1057,13 +1017,9 @@ static ssize_t store_xps_map(struct netdev_queue *queue, const char *buf, size_t len) { struct net_device *dev = queue->dev; - cpumask_var_t mask; - int err, i, cpu, pos, map_len, alloc_len, need_set; unsigned long index; - struct xps_map *map, *new_map; - struct xps_dev_maps *dev_maps, *new_dev_maps; - int nonempty = 0; - int numa_node_id = -2; + cpumask_var_t mask; + int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -1079,105 +1035,11 @@ static ssize_t store_xps_map(struct netdev_queue *queue, return err; } - new_dev_maps = kzalloc(max_t(unsigned int, - XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES), GFP_KERNEL); - if (!new_dev_maps) { - free_cpumask_var(mask); - return -ENOMEM; - } - - mutex_lock(&xps_map_mutex); - - dev_maps = xmap_dereference(dev->xps_maps); - - for_each_possible_cpu(cpu) { - map = dev_maps ? - xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; - new_map = map; - if (map) { - for (pos = 0; pos < map->len; pos++) - if (map->queues[pos] == index) - break; - map_len = map->len; - alloc_len = map->alloc_len; - } else - pos = map_len = alloc_len = 0; - - need_set = cpumask_test_cpu(cpu, mask) && cpu_online(cpu); -#ifdef CONFIG_NUMA - if (need_set) { - if (numa_node_id == -2) - numa_node_id = cpu_to_node(cpu); - else if (numa_node_id != cpu_to_node(cpu)) - numa_node_id = -1; - } -#endif - if (need_set && pos >= map_len) { - /* Need to add queue to this CPU's map */ - if (map_len >= alloc_len) { - alloc_len = alloc_len ? - 2 * alloc_len : XPS_MIN_MAP_ALLOC; - new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), - GFP_KERNEL, - cpu_to_node(cpu)); - if (!new_map) - goto error; - new_map->alloc_len = alloc_len; - for (i = 0; i < map_len; i++) - new_map->queues[i] = map->queues[i]; - new_map->len = map_len; - } - new_map->queues[new_map->len++] = index; - } else if (!need_set && pos < map_len) { - /* Need to remove queue from this CPU's map */ - if (map_len > 1) - new_map->queues[pos] = - new_map->queues[--new_map->len]; - else - new_map = NULL; - } - RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], new_map); - } - - /* Cleanup old maps */ - for_each_possible_cpu(cpu) { - map = dev_maps ? - xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; - if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) - kfree_rcu(map, rcu); - if (new_dev_maps->cpu_map[cpu]) - nonempty = 1; - } - - if (nonempty) { - rcu_assign_pointer(dev->xps_maps, new_dev_maps); - } else { - kfree(new_dev_maps); - RCU_INIT_POINTER(dev->xps_maps, NULL); - } - - if (dev_maps) - kfree_rcu(dev_maps, rcu); - - netdev_queue_numa_node_write(queue, (numa_node_id >= 0) ? numa_node_id : - NUMA_NO_NODE); - - mutex_unlock(&xps_map_mutex); + err = netif_set_xps_queue(dev, mask, index); free_cpumask_var(mask); - return len; -error: - mutex_unlock(&xps_map_mutex); - - if (new_dev_maps) - for_each_possible_cpu(i) - kfree(rcu_dereference_protected( - new_dev_maps->cpu_map[i], - 1)); - kfree(new_dev_maps); - free_cpumask_var(mask); - return -ENOMEM; + return err ? : len; } static struct netdev_queue_attribute xps_cpus_attribute = -- cgit v1.2.3 From 10cdc3f3cd541bfeaaf1c6e1710b1500ca19aa7f Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Jan 2013 08:57:17 +0000 Subject: net: Rewrite netif_reset_xps_queue to allow for better code reuse This patch does a minor refactor on netif_reset_xps_queue to address a few items I noticed. First is the fact that we are doing removal of queues in both netif_reset_xps_queue and netif_set_xps_queue. Since there is no need to have the code in two places I am pushing it out into a separate function and will come back in another patch and reuse the code in netif_set_xps_queue. The second item this change addresses is the fact that the Tx queues were not getting their numa_node value cleared as a part of the XPS queue reset. This patch resolves that by resetting the numa_node value if the dev_maps value is set. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/dev.c | 56 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 257b29516f69..231de8738149 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1862,45 +1862,55 @@ static DEFINE_MUTEX(xps_map_mutex); #define xmap_dereference(P) \ rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) -void netif_reset_xps_queue(struct net_device *dev, u16 index) +static struct xps_map *remove_xps_queue(struct xps_dev_maps *dev_maps, + int cpu, u16 index) { - struct xps_dev_maps *dev_maps; - struct xps_map *map; - int i, pos, nonempty = 0; - - mutex_lock(&xps_map_mutex); - dev_maps = xmap_dereference(dev->xps_maps); - - if (!dev_maps) - goto out_no_maps; + struct xps_map *map = NULL; + int pos; - for_each_possible_cpu(i) { - map = xmap_dereference(dev_maps->cpu_map[i]); - if (!map) - continue; - - for (pos = 0; pos < map->len; pos++) - if (map->queues[pos] == index) - break; + if (dev_maps) + map = xmap_dereference(dev_maps->cpu_map[cpu]); - if (pos < map->len) { + for (pos = 0; map && pos < map->len; pos++) { + if (map->queues[pos] == index) { if (map->len > 1) { map->queues[pos] = map->queues[--map->len]; } else { - RCU_INIT_POINTER(dev_maps->cpu_map[i], NULL); + RCU_INIT_POINTER(dev_maps->cpu_map[cpu], NULL); kfree_rcu(map, rcu); map = NULL; } + break; } - if (map) - nonempty = 1; } - if (!nonempty) { + return map; +} + +void netif_reset_xps_queue(struct net_device *dev, u16 index) +{ + struct xps_dev_maps *dev_maps; + int cpu; + bool active = false; + + mutex_lock(&xps_map_mutex); + dev_maps = xmap_dereference(dev->xps_maps); + + if (!dev_maps) + goto out_no_maps; + + for_each_possible_cpu(cpu) { + if (remove_xps_queue(dev_maps, cpu, index)) + active = true; + } + + if (!active) { RCU_INIT_POINTER(dev->xps_maps, NULL); kfree_rcu(dev_maps, rcu); } + netdev_queue_numa_node_write(netdev_get_tx_queue(dev, index), + NUMA_NO_NODE); out_no_maps: mutex_unlock(&xps_map_mutex); } -- cgit v1.2.3 From 01c5f864e62b1c1ee72fb492c6c5e47a162a507b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Jan 2013 08:57:35 +0000 Subject: net: Rewrite netif_set_xps_queues to address several issues This change is meant to address several issues I found within the netif_set_xps_queues function. If the allocation of one of the maps to be assigned to new_dev_maps failed we could end up with the device map in an inconsistent state since we had already worked through a number of CPUs and removed or added the queue. To address that I split the process into several steps. The first of which is just the allocation of updated maps for CPUs that will need larger maps to store the queue. By doing this we can fail gracefully without actually altering the contents of the current device map. The second issue I found was the fact that we were always allocating a new device map even if we were not adding any queues. I have updated the code so that we only allocate a new device map if we are adding queues, otherwise if we are not adding any queues to CPUs we just skip to the removal process. The last change I made was to reuse the code from remove_xps_queue to remove the queue from the CPU. By making this change we can be consistent in how we go about adding and removing the queues from the CPUs. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/dev.c | 183 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 117 insertions(+), 66 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 231de8738149..41d5120df469 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1915,107 +1915,158 @@ out_no_maps: mutex_unlock(&xps_map_mutex); } +static struct xps_map *expand_xps_map(struct xps_map *map, + int cpu, u16 index) +{ + struct xps_map *new_map; + int alloc_len = XPS_MIN_MAP_ALLOC; + int i, pos; + + for (pos = 0; map && pos < map->len; pos++) { + if (map->queues[pos] != index) + continue; + return map; + } + + /* Need to add queue to this CPU's existing map */ + if (map) { + if (pos < map->alloc_len) + return map; + + alloc_len = map->alloc_len * 2; + } + + /* Need to allocate new map to store queue on this CPU's map */ + new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), GFP_KERNEL, + cpu_to_node(cpu)); + if (!new_map) + return NULL; + + for (i = 0; i < pos; i++) + new_map->queues[i] = map->queues[i]; + new_map->alloc_len = alloc_len; + new_map->len = pos; + + return new_map; +} + int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, u16 index) { - int i, cpu, pos, map_len, alloc_len, need_set; + struct xps_dev_maps *dev_maps, *new_dev_maps = NULL; struct xps_map *map, *new_map; - struct xps_dev_maps *dev_maps, *new_dev_maps; - int nonempty = 0; - int numa_node_id = -2; int maps_sz = max_t(unsigned int, XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES); - - new_dev_maps = kzalloc(maps_sz, GFP_KERNEL); - if (!new_dev_maps) - return -ENOMEM; + int cpu, numa_node_id = -2; + bool active = false; mutex_lock(&xps_map_mutex); dev_maps = xmap_dereference(dev->xps_maps); + /* allocate memory for queue storage */ + for_each_online_cpu(cpu) { + if (!cpumask_test_cpu(cpu, mask)) + continue; + + if (!new_dev_maps) + new_dev_maps = kzalloc(maps_sz, GFP_KERNEL); + if (!new_dev_maps) + return -ENOMEM; + + map = dev_maps ? xmap_dereference(dev_maps->cpu_map[cpu]) : + NULL; + + map = expand_xps_map(map, cpu, index); + if (!map) + goto error; + + RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], map); + } + + if (!new_dev_maps) + goto out_no_new_maps; + for_each_possible_cpu(cpu) { - map = dev_maps ? - xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; - new_map = map; - if (map) { - for (pos = 0; pos < map->len; pos++) - if (map->queues[pos] == index) - break; - map_len = map->len; - alloc_len = map->alloc_len; - } else - pos = map_len = alloc_len = 0; + if (cpumask_test_cpu(cpu, mask) && cpu_online(cpu)) { + /* add queue to CPU maps */ + int pos = 0; - need_set = cpumask_test_cpu(cpu, mask) && cpu_online(cpu); + map = xmap_dereference(new_dev_maps->cpu_map[cpu]); + while ((pos < map->len) && (map->queues[pos] != index)) + pos++; + + if (pos == map->len) + map->queues[map->len++] = index; #ifdef CONFIG_NUMA - if (need_set) { if (numa_node_id == -2) numa_node_id = cpu_to_node(cpu); else if (numa_node_id != cpu_to_node(cpu)) numa_node_id = -1; - } #endif - if (need_set && pos >= map_len) { - /* Need to add queue to this CPU's map */ - if (map_len >= alloc_len) { - alloc_len = alloc_len ? - 2 * alloc_len : XPS_MIN_MAP_ALLOC; - new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), - GFP_KERNEL, - cpu_to_node(cpu)); - if (!new_map) - goto error; - new_map->alloc_len = alloc_len; - for (i = 0; i < map_len; i++) - new_map->queues[i] = map->queues[i]; - new_map->len = map_len; - } - new_map->queues[new_map->len++] = index; - } else if (!need_set && pos < map_len) { - /* Need to remove queue from this CPU's map */ - if (map_len > 1) - new_map->queues[pos] = - new_map->queues[--new_map->len]; - else - new_map = NULL; + } else if (dev_maps) { + /* fill in the new device map from the old device map */ + map = xmap_dereference(dev_maps->cpu_map[cpu]); + RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], map); } - RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], new_map); + } + rcu_assign_pointer(dev->xps_maps, new_dev_maps); + /* Cleanup old maps */ - for_each_possible_cpu(cpu) { - map = dev_maps ? - xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; - if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) - kfree_rcu(map, rcu); - if (new_dev_maps->cpu_map[cpu]) - nonempty = 1; - } + if (dev_maps) { + for_each_possible_cpu(cpu) { + new_map = xmap_dereference(new_dev_maps->cpu_map[cpu]); + map = xmap_dereference(dev_maps->cpu_map[cpu]); + if (map && map != new_map) + kfree_rcu(map, rcu); + } - if (nonempty) { - rcu_assign_pointer(dev->xps_maps, new_dev_maps); - } else { - kfree(new_dev_maps); - RCU_INIT_POINTER(dev->xps_maps, NULL); + kfree_rcu(dev_maps, rcu); } - if (dev_maps) - kfree_rcu(dev_maps, rcu); + dev_maps = new_dev_maps; + active = true; +out_no_new_maps: + /* update Tx queue numa node */ netdev_queue_numa_node_write(netdev_get_tx_queue(dev, index), (numa_node_id >= 0) ? numa_node_id : NUMA_NO_NODE); + if (!dev_maps) + goto out_no_maps; + + /* removes queue from unused CPUs */ + for_each_possible_cpu(cpu) { + if (cpumask_test_cpu(cpu, mask) && cpu_online(cpu)) + continue; + + if (remove_xps_queue(dev_maps, cpu, index)) + active = true; + } + + /* free map if not active */ + if (!active) { + RCU_INIT_POINTER(dev->xps_maps, NULL); + kfree_rcu(dev_maps, rcu); + } + +out_no_maps: mutex_unlock(&xps_map_mutex); return 0; error: + /* remove any maps that we added */ + for_each_possible_cpu(cpu) { + new_map = xmap_dereference(new_dev_maps->cpu_map[cpu]); + map = dev_maps ? xmap_dereference(dev_maps->cpu_map[cpu]) : + NULL; + if (new_map && new_map != map) + kfree(new_map); + } + mutex_unlock(&xps_map_mutex); - if (new_dev_maps) - for_each_possible_cpu(i) - kfree(rcu_dereference_protected( - new_dev_maps->cpu_map[i], - 1)); kfree(new_dev_maps); return -ENOMEM; } -- cgit v1.2.3 From 024e9679a2daaa67642693366fb63a6b8c61b9f3 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 10 Jan 2013 08:57:46 +0000 Subject: net: Add support for XPS without sysfs being defined This patch makes it so that we can support transmit packet steering without sysfs needing to be enabled. The reason for making this change is to make it so that a driver can make use of the XPS even while the sysfs portion of the interface is not present. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - net/Kconfig | 2 +- net/core/dev.c | 26 ++++++++++++++++++++------ net/core/net-sysfs.c | 14 -------------- 4 files changed, 21 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 59fe9da4e315..aa7ad8a96e70 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2104,7 +2104,6 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) } #ifdef CONFIG_XPS -extern void netif_reset_xps_queue(struct net_device *dev, u16 index); extern int netif_set_xps_queue(struct net_device *dev, struct cpumask *mask, u16 index); #else diff --git a/net/Kconfig b/net/Kconfig index 30b48f523135..3cc5be0fe420 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -232,7 +232,7 @@ config RFS_ACCEL config XPS boolean - depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS + depends on SMP && USE_GENERIC_SMP_HELPERS default y config NETPRIO_CGROUP diff --git a/net/core/dev.c b/net/core/dev.c index 41d5120df469..95de4c011808 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1887,10 +1887,10 @@ static struct xps_map *remove_xps_queue(struct xps_dev_maps *dev_maps, return map; } -void netif_reset_xps_queue(struct net_device *dev, u16 index) +static void netif_reset_xps_queues_gt(struct net_device *dev, u16 index) { struct xps_dev_maps *dev_maps; - int cpu; + int cpu, i; bool active = false; mutex_lock(&xps_map_mutex); @@ -1900,7 +1900,11 @@ void netif_reset_xps_queue(struct net_device *dev, u16 index) goto out_no_maps; for_each_possible_cpu(cpu) { - if (remove_xps_queue(dev_maps, cpu, index)) + for (i = index; i < dev->num_tx_queues; i++) { + if (!remove_xps_queue(dev_maps, cpu, i)) + break; + } + if (i == dev->num_tx_queues) active = true; } @@ -1909,8 +1913,10 @@ void netif_reset_xps_queue(struct net_device *dev, u16 index) kfree_rcu(dev_maps, rcu); } - netdev_queue_numa_node_write(netdev_get_tx_queue(dev, index), - NUMA_NO_NODE); + for (i = index; i < dev->num_tx_queues; i++) + netdev_queue_numa_node_write(netdev_get_tx_queue(dev, i), + NUMA_NO_NODE); + out_no_maps: mutex_unlock(&xps_map_mutex); } @@ -2096,8 +2102,12 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) if (dev->num_tc) netif_setup_tc(dev, txq); - if (txq < dev->real_num_tx_queues) + if (txq < dev->real_num_tx_queues) { qdisc_reset_all_tx_gt(dev, txq); +#ifdef CONFIG_XPS + netif_reset_xps_queues_gt(dev, txq); +#endif + } } dev->real_num_tx_queues = txq; @@ -5919,6 +5929,10 @@ static void rollback_registered_many(struct list_head *head) /* Remove entries from kobject tree */ netdev_unregister_kobject(dev); +#ifdef CONFIG_XPS + /* Remove XPS queueing entries */ + netif_reset_xps_queues_gt(dev, 0); +#endif } synchronize_net(); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 5ad489d5d062..a5b89a6fec6d 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1002,16 +1002,6 @@ static ssize_t show_xps_map(struct netdev_queue *queue, return len; } -static void xps_queue_release(struct netdev_queue *queue) -{ - struct net_device *dev = queue->dev; - unsigned long index; - - index = get_netdev_queue_index(queue); - - netif_reset_xps_queue(dev, index); -} - static ssize_t store_xps_map(struct netdev_queue *queue, struct netdev_queue_attribute *attribute, const char *buf, size_t len) @@ -1058,10 +1048,6 @@ static void netdev_queue_release(struct kobject *kobj) { struct netdev_queue *queue = to_netdev_queue(kobj); -#ifdef CONFIG_XPS - xps_queue_release(queue); -#endif - memset(kobj, 0, sizeof(*kobj)); dev_put(queue->dev); } -- cgit v1.2.3 From 75e6934a9e996ee7cfe7a488b7f7cc2baa86717a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Jan 2013 12:32:37 +0100 Subject: mac80211: fix HT40 connections My commit 4bf88530be971bf95a7830ca61b4120980bf4347 ("mac80211: convert to channel definition struct") accidentally broke HT40 connections due to swapped channel flag checks -- fix that. Reported-by: Sujith Manoharan Tested-by: Sujith Manoharan Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d90c07b1795f..e930175771ff 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -199,11 +199,11 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, case NL80211_CHAN_WIDTH_40: if (sdata->vif.bss_conf.chandef.chan->center_freq > sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; if (sdata->vif.bss_conf.chandef.chan->center_freq < sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; break; default: -- cgit v1.2.3 From 55b183ad8635589ea731a31653de79a2626491c4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 11 Jan 2013 14:22:58 +0100 Subject: wireless: fix regulatory HT40 allowed check commit 1a9193185f462a51815fe48491f8a6fb6b942551 "regulatory: code cleanup" changed is_ht40_allowed without considering that IEEE80211_CHAN_NO_HT40 is not just one flag, but two. This is causing HT40- to be blocked completely. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 8c114e8a9135..6ea626b30a2a 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1123,7 +1123,9 @@ static bool is_ht40_allowed(struct ieee80211_channel *chan) if (chan->flags & IEEE80211_CHAN_DISABLED) return false; /* This would happen when regulatory rules disallow HT40 completely */ - return !(chan->flags & IEEE80211_CHAN_NO_HT40); + if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) + return false; + return true; } static void reg_process_ht_flags_channel(struct wiphy *wiphy, -- cgit v1.2.3 From fad2e371bb5a9f64dced90309fa7cb8bce09e92f Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 11 Jan 2013 14:48:48 +0100 Subject: NFC: Avoid memcpy on LLCP connection less Rx path We can cast msg_name to a sockaddr_nfc_llcp pointer directly. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/sock.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 12ec4879b4ba..463231bed8d4 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -682,17 +682,17 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_type == SOCK_DGRAM && msg->msg_name) { struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb); - struct sockaddr_nfc_llcp sockaddr; + struct sockaddr_nfc_llcp *sockaddr = + (struct sockaddr_nfc_llcp *) msg->msg_name; - pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap); + msg->msg_namelen = sizeof(struct sockaddr_nfc_llcp); - sockaddr.sa_family = AF_NFC; - sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP; - sockaddr.dsap = ui_cb->dsap; - sockaddr.ssap = ui_cb->ssap; + pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap); - memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr)); - msg->msg_namelen = sizeof(sockaddr); + sockaddr->sa_family = AF_NFC; + sockaddr->nfc_protocol = NFC_PROTO_NFC_DEP; + sockaddr->dsap = ui_cb->dsap; + sockaddr->ssap = ui_cb->ssap; } /* Mark read part of skb as used */ -- cgit v1.2.3 From fb97c3e80f99988079df4fb5f3c84aea4e7f806a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 11 Jan 2013 14:54:22 +0100 Subject: NFC: Use skb_copy_datagram_iovec Safer and more robust than than memcpy_toiovec. Signed-off-by: Samuel Ortiz --- net/nfc/llcp/sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 463231bed8d4..5332751943a9 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -672,7 +672,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, copied = min_t(unsigned int, rlen, len); cskb = skb; - if (memcpy_toiovec(msg->msg_iov, cskb->data, copied)) { + if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { if (!(flags & MSG_PEEK)) skb_queue_head(&sk->sk_receive_queue, skb); return -EFAULT; -- cgit v1.2.3 From 87696f9234cb681ded2697b0f0fd01bb15a2aff2 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 11 Jan 2013 10:38:42 -0800 Subject: net: Export __netdev_pick_tx so that it can be used in modules When testing with FCoE enabled we discovered that I had not exported __netdev_pick_tx. As a result ixgbe doesn't build with the RFC patches applied because ixgbe_select_queue was calling the function. This change corrects that build issue by correctly exporting __netdev_pick_tx so it can be used by modules. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- net/core/dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 95de4c011808..d1e8116e93cd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2746,6 +2746,7 @@ u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) return queue_index; } +EXPORT_SYMBOL(__netdev_pick_tx); struct netdev_queue *netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) -- cgit v1.2.3 From 28a28283f8220e0b397a18f624fbcbbe046b063c Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 11 Jan 2013 09:59:20 +0000 Subject: ipv4: fib: fix a comment. In fib_frontend.c, there is a confusing comment; NETLINK_CB(skb).portid does not refer to a pid of sending process, but rather to a netlink portid. Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- net/ipv4/fib_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 5cd75e2dab2c..99f00d39d10b 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -974,7 +974,7 @@ static void nl_fib_input(struct sk_buff *skb) nl_fib_lookup(frn, tb); - portid = NETLINK_CB(skb).portid; /* pid of sending process */ + portid = NETLINK_CB(skb).portid; /* netlink portid */ NETLINK_CB(skb).portid = 0; /* from kernel */ NETLINK_CB(skb).dst_group = 0; /* unicast */ netlink_unicast(net->ipv4.fibnl, skb, portid, MSG_DONTWAIT); -- cgit v1.2.3 From 18aafc622abf492809723d9c5a3c5dcea287169e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 11 Jan 2013 14:46:37 +0000 Subject: net: splice: fix __splice_segment() commit 9ca1b22d6d2 (net: splice: avoid high order page splitting) forgot that skb->head could need a copy into several page frags. This could be the case for loopback traffic mostly. Also remove now useless skb argument from linear_to_page() and __splice_segment() prototypes. Signed-off-by: Eric Dumazet Cc: Willy Tarreau Signed-off-by: David S. Miller --- net/core/skbuff.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1e1b9ea0296d..2568c449fe36 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1652,7 +1652,7 @@ static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) static struct page *linear_to_page(struct page *page, unsigned int *len, unsigned int *offset, - struct sk_buff *skb, struct sock *sk) + struct sock *sk) { struct page_frag *pfrag = sk_page_frag(sk); @@ -1685,14 +1685,14 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd, static bool spd_fill_page(struct splice_pipe_desc *spd, struct pipe_inode_info *pipe, struct page *page, unsigned int *len, unsigned int offset, - struct sk_buff *skb, bool linear, + bool linear, struct sock *sk) { if (unlikely(spd->nr_pages == MAX_SKB_FRAGS)) return true; if (linear) { - page = linear_to_page(page, len, &offset, skb, sk); + page = linear_to_page(page, len, &offset, sk); if (!page) return true; } @@ -1711,13 +1711,11 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, static bool __splice_segment(struct page *page, unsigned int poff, unsigned int plen, unsigned int *off, - unsigned int *len, struct sk_buff *skb, + unsigned int *len, struct splice_pipe_desc *spd, bool linear, struct sock *sk, struct pipe_inode_info *pipe) { - unsigned int flen; - if (!*len) return true; @@ -1732,12 +1730,16 @@ static bool __splice_segment(struct page *page, unsigned int poff, plen -= *off; *off = 0; - flen = min(*len, plen); - - if (spd_fill_page(spd, pipe, page, &flen, poff, skb, linear, sk)) - return true; + do { + unsigned int flen = min(*len, plen); - *len -= flen; + if (spd_fill_page(spd, pipe, page, &flen, poff, + linear, sk)) + return true; + poff += flen; + plen -= flen; + *len -= flen; + } while (*len && plen); return false; } @@ -1760,7 +1762,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, if (__splice_segment(virt_to_page(skb->data), (unsigned long) skb->data & (PAGE_SIZE - 1), skb_headlen(skb), - offset, len, skb, spd, + offset, len, spd, skb_head_is_locked(skb), sk, pipe)) return true; @@ -1773,7 +1775,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, if (__splice_segment(skb_frag_page(f), f->page_offset, skb_frag_size(f), - offset, len, skb, spd, false, sk, pipe)) + offset, len, spd, false, sk, pipe)) return true; } -- cgit v1.2.3 From 569174433d3bd96e206a4b5969a4498371c70d16 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Tue, 13 Nov 2012 09:53:26 +0800 Subject: batman-adv: use per_cpu_add helper this_cpu_add is an atomic operation. and be more faster than per_cpu_ptr operation. Signed-off-by: Shan Wei Reviewed-by: Christoph Lameter Signed-off-by: Sven Eckelmann Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/main.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 2f85577086a7..c4fe41f7c2f0 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -276,9 +276,7 @@ static inline bool batadv_has_timed_out(unsigned long timestamp, static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx, size_t count) { - int cpu = get_cpu(); - per_cpu_ptr(bat_priv->bat_counters, cpu)[idx] += count; - put_cpu(); + this_cpu_add(bat_priv->bat_counters[idx], count); } #define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1) -- cgit v1.2.3 From 02233e0c75fc512ca60e7e82943f0a4395a85411 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Wed, 17 Oct 2012 15:07:35 +0200 Subject: batman-adv: Do not add multicast MAC addresses to translation table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current translation table mechanism is not suitable for multicast addresses and we are currently flooding such frames anyway. Therefore this patch prevents multicast MAC addresses being added to the translation table. Signed-off-by: Linus Lüssing Acked-by: Antonio Quartulli Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/soft-interface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 90f4049a90dd..f8cc1429942f 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -180,7 +180,8 @@ static int batadv_interface_tx(struct sk_buff *skb, goto dropped; /* Register the client MAC in the transtable */ - batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); + if (!is_multicast_ether_addr(ethhdr->h_source)) + batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); /* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... -- cgit v1.2.3 From 7cf4d520fdb72250b769920274279249629881ce Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 8 Nov 2012 22:16:15 +0100 Subject: batman-adv: reduce local TT entry timeout to 10 minutes The current timeout is set to one hour. However a client connected to the mesh network will always generate traffic. In the worst case it will send ARP requests every 4 or 5 minutes. On the other hand having a long timeout means storing dead entries for one hour and it leads to very big trans-tables containing useless clients. This patch reduces the timeout to 10 minutes Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index c4fe41f7c2f0..d04b209a8295 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -41,7 +41,7 @@ * -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE */ #define BATADV_PURGE_TIMEOUT 200000 /* 200 seconds */ -#define BATADV_TT_LOCAL_TIMEOUT 3600000 /* in milliseconds */ +#define BATADV_TT_LOCAL_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ #define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ -- cgit v1.2.3 From 85766a82003ec5ecc35403e855c47ce69c4658fc Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 8 Nov 2012 22:16:16 +0100 Subject: batman-adv: improve local translation table output This patch adds a nice header to the local translation table and the last_seen time for each local entry Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 22457a7952ba..426a3ae47788 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -472,10 +472,16 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) struct batadv_priv *bat_priv = netdev_priv(net_dev); struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_tt_common_entry *tt_common_entry; + struct batadv_tt_local_entry *tt_local; struct batadv_hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; uint32_t i; + int last_seen_secs; + int last_seen_msecs; + unsigned long last_seen_jiffies; + bool no_purge; + uint16_t np_flag = BATADV_TT_CLIENT_NOPURGE; primary_if = batadv_seq_print_text_primary_if_get(seq); if (!primary_if) @@ -484,6 +490,8 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn)); + seq_printf(seq, " %-13s %-7s %-10s\n", "Client", "Flags", + "Last seen"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -491,18 +499,29 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) rcu_read_lock(); hlist_for_each_entry_rcu(tt_common_entry, node, head, hash_entry) { - seq_printf(seq, " * %pM [%c%c%c%c%c]\n", + tt_local = container_of(tt_common_entry, + struct batadv_tt_local_entry, + common); + last_seen_jiffies = jiffies - tt_local->last_seen; + last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); + last_seen_secs = last_seen_msecs / 1000; + last_seen_msecs = last_seen_msecs % 1000; + + no_purge = tt_common_entry->flags & np_flag; + + seq_printf(seq, " * %pM [%c%c%c%c%c] %3u.%03u\n", tt_common_entry->addr, (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), - (tt_common_entry->flags & - BATADV_TT_CLIENT_NOPURGE ? 'P' : '.'), + no_purge ? 'P' : '.', (tt_common_entry->flags & BATADV_TT_CLIENT_NEW ? 'N' : '.'), (tt_common_entry->flags & BATADV_TT_CLIENT_PENDING ? 'X' : '.'), (tt_common_entry->flags & - BATADV_TT_CLIENT_WIFI ? 'W' : '.')); + BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + no_purge ? last_seen_secs : 0, + no_purge ? last_seen_msecs : 0); } rcu_read_unlock(); } -- cgit v1.2.3 From f9d8a53784a53a528e2c180291874500e87d9c91 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 Nov 2012 09:01:42 +0100 Subject: batman-adv: print the CRC together with the translation tables To simplify debugging operations, it is better to print the related CRC together with the translation table (local CRC for the local table and global CRC for each entry in the global table) Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 426a3ae47788..408807eb9df3 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -488,8 +488,9 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) goto out; seq_printf(seq, - "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", - net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn)); + "Locally retrieved addresses (from %s) announced via TT (TTVN: %u CRC: %#.4x):\n", + net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn), + bat_priv->tt.local_crc); seq_printf(seq, " %-13s %-7s %-10s\n", "Client", "Flags", "Last seen"); @@ -986,10 +987,11 @@ batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry, best_entry = batadv_transtable_best_orig(tt_global_entry); if (best_entry) { last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn); - seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n", + seq_printf(seq, + " %c %pM (%3u) via %pM (%3u) (%#.4x) [%c%c%c]\n", '*', tt_global_entry->common.addr, best_entry->ttvn, best_entry->orig_node->orig, - last_ttvn, + last_ttvn, best_entry->orig_node->tt_crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); @@ -1031,8 +1033,9 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Globally announced TT entries received via the mesh %s\n", net_dev->name); - seq_printf(seq, " %-13s %s %-15s %s %s\n", - "Client", "(TTVN)", "Originator", "(Curr TTVN)", "Flags"); + seq_printf(seq, " %-13s %s %-15s %s (%-6s) %s\n", + "Client", "(TTVN)", "Originator", "(Curr TTVN)", "CRC", + "Flags"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; -- cgit v1.2.3 From 39a329915889a220b5f2c3f2eb3d7fd892567715 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 Nov 2012 09:01:43 +0100 Subject: batman-adv: unify and properly print hex values Values are printed in hexadecimal format in several points in the code, but they are not printed using the same format string. This patches unifies the format used for such numbers so that they look the same everywhere. Given the fact that all the variables printed as hexadecimal are 16 bit long, this is the chosen printing format: %#.4x Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bridge_loop_avoidance.c | 18 +++++++++--------- net/batman-adv/translation-table.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 7d02ebd11a7f..f2a364970f57 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1033,7 +1033,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, is_single_hop_neigh = true; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %u, changes %u, td %d, TTL %d, V %d, IDF %d)\n", + "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, td %d, TTL %d, V %d, IDF %d)\n", ethhdr->h_source, if_incoming->net_dev->name, if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig, batadv_ogm_packet->prev_sender, diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 5aebe9327d68..ec12c79104ec 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -661,12 +661,12 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, crc = ntohs(*((__be16 *)(&an_addr[4]))); batadv_dbg(BATADV_DBG_BLA, bat_priv, - "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %04x\n", + "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n", vid, backbone_gw->orig, crc); if (backbone_gw->crc != crc) { batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, - "handle_announce(): CRC FAILED for %pM/%d (my = %04x, sent = %04x)\n", + "handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n", backbone_gw->orig, backbone_gw->vid, backbone_gw->crc, crc); @@ -835,7 +835,7 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv, /* if our mesh friends mac is bigger, use it for ourselves. */ if (ntohs(bla_dst->group) > ntohs(bla_dst_own->group)) { batadv_dbg(BATADV_DBG_BLA, bat_priv, - "taking other backbones claim group: %04x\n", + "taking other backbones claim group: %#.4x\n", ntohs(bla_dst->group)); bla_dst_own->group = bla_dst->group; } @@ -1626,10 +1626,10 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, - "Claims announced for the mesh %s (orig %pM, group id %04x)\n", + "Claims announced for the mesh %s (orig %pM, group id %#.4x)\n", net_dev->name, primary_addr, ntohs(bat_priv->bla.claim_dest.group)); - seq_printf(seq, " %-17s %-5s %-17s [o] (%-4s)\n", + seq_printf(seq, " %-17s %-5s %-17s [o] (%-6s)\n", "Client", "VID", "Originator", "CRC"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -1638,7 +1638,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(claim, node, head, hash_entry) { is_own = batadv_compare_eth(claim->backbone_gw->orig, primary_addr); - seq_printf(seq, " * %pM on % 5d by %pM [%c] (%04x)\n", + seq_printf(seq, " * %pM on % 5d by %pM [%c] (%#.4x)\n", claim->addr, claim->vid, claim->backbone_gw->orig, (is_own ? 'x' : ' '), @@ -1672,10 +1672,10 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, - "Backbones announced for the mesh %s (orig %pM, group id %04x)\n", + "Backbones announced for the mesh %s (orig %pM, group id %#.4x)\n", net_dev->name, primary_addr, ntohs(bat_priv->bla.claim_dest.group)); - seq_printf(seq, " %-17s %-5s %-9s (%-4s)\n", + seq_printf(seq, " %-17s %-5s %-9s (%-6s)\n", "Originator", "VID", "last seen", "CRC"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -1693,7 +1693,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) continue; seq_printf(seq, - " * %pM on % 5d % 4i.%03is (%04x)\n", + " * %pM on % 5d % 4i.%03is (%#.4x)\n", backbone_gw->orig, backbone_gw->vid, secs, msecs, backbone_gw->crc); } diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 408807eb9df3..40ef9557288a 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2518,7 +2518,7 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv, orig_node->tt_crc != tt_crc) { request_table: batadv_dbg(BATADV_DBG_TT, bat_priv, - "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u crc: %u last_crc: %u num_changes: %u)\n", + "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u crc: %#.4x last_crc: %#.4x num_changes: %u)\n", orig_node->orig, ttvn, orig_ttvn, tt_crc, orig_node->tt_crc, tt_num_changes); batadv_send_tt_request(bat_priv, orig_node, ttvn, -- cgit v1.2.3 From 8425ec6aea20f8c8e1783d7390f5ea6ca01c58e1 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 19 Nov 2012 09:01:44 +0100 Subject: batman-adv: remove useless assignment in tt_local_add() The flag field of the tt_local_entry->common structure in tt_local_add() is first assigned NO_FLAGS and then TT_CLIENT_NEW so nullifying the first operation. For this reason it is safe to remove the first assignment. This was introuduced by ("batman-adv: keep local table consistency for further TT_RESPONSE") Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 40ef9557288a..5f442328f2e6 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -305,7 +305,11 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, (uint8_t)atomic_read(&bat_priv->tt.vn)); memcpy(tt_local->common.addr, addr, ETH_ALEN); - tt_local->common.flags = BATADV_NO_FLAGS; + /* The local entry has to be marked as NEW to avoid to send it in + * a full table response going out before the next ttvn increment + * (consistency check) + */ + tt_local->common.flags = BATADV_TT_CLIENT_NEW; if (batadv_is_wifi_iface(ifindex)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; atomic_set(&tt_local->common.refcount, 2); @@ -316,12 +320,6 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, if (batadv_compare_eth(addr, soft_iface->dev_addr)) tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE; - /* The local entry has to be marked as NEW to avoid to send it in - * a full table response going out before the next ttvn increment - * (consistency check) - */ - tt_local->common.flags |= BATADV_TT_CLIENT_NEW; - hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, batadv_choose_orig, &tt_local->common, &tt_local->common.hash_entry); -- cgit v1.2.3 From dec05074b1a322f5fbbe3f81bc438a75bfec3c34 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sat, 10 Nov 2012 11:00:32 +0100 Subject: batman-adv: Initialize lockdep class keys for hashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different hashes have the same class key because they get initialised with the same one. For this reason lockdep can create false warning when they are used recursively. Re-initialise the key for each hash after the invocation to hash_new() to avoid this problem. Signed-off-by: Antonio Quartulli Tested-by: Linus Lüssing Signed-off-by: Marek Lindner --- net/batman-adv/originator.c | 6 ++++++ net/batman-adv/translation-table.c | 10 ++++++++++ net/batman-adv/vis.c | 6 ++++++ 3 files changed, 22 insertions(+) (limited to 'net') diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 8c32cf1c2dec..109081c49bbd 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -29,6 +29,9 @@ #include "soft-interface.h" #include "bridge_loop_avoidance.h" +/* hash class keys */ +static struct lock_class_key batadv_orig_hash_lock_class_key; + static void batadv_purge_orig(struct work_struct *work); static void batadv_start_purge_timer(struct batadv_priv *bat_priv) @@ -57,6 +60,9 @@ int batadv_originator_init(struct batadv_priv *bat_priv) if (!bat_priv->orig_hash) goto err; + batadv_hash_set_lock_class(bat_priv->orig_hash, + &batadv_orig_hash_lock_class_key); + batadv_start_purge_timer(bat_priv); return 0; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 5f442328f2e6..c6fd0b760d3e 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -29,6 +29,10 @@ #include +/* hash class keys */ +static struct lock_class_key batadv_tt_local_hash_lock_class_key; +static struct lock_class_key batadv_tt_global_hash_lock_class_key; + static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, struct batadv_orig_node *orig_node); static void batadv_tt_purge(struct work_struct *work); @@ -235,6 +239,9 @@ static int batadv_tt_local_init(struct batadv_priv *bat_priv) if (!bat_priv->tt.local_hash) return -ENOMEM; + batadv_hash_set_lock_class(bat_priv->tt.local_hash, + &batadv_tt_local_hash_lock_class_key); + return 0; } @@ -694,6 +701,9 @@ static int batadv_tt_global_init(struct batadv_priv *bat_priv) if (!bat_priv->tt.global_hash) return -ENOMEM; + batadv_hash_set_lock_class(bat_priv->tt.global_hash, + &batadv_tt_global_hash_lock_class_key); + return 0; } diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 0f65a9de5f74..60eb9b7ca8d1 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -28,6 +28,9 @@ #define BATADV_MAX_VIS_PACKET_SIZE 1000 +/* hash class keys */ +static struct lock_class_key batadv_vis_hash_lock_class_key; + static void batadv_start_vis_timer(struct batadv_priv *bat_priv); /* free the info */ @@ -852,6 +855,9 @@ int batadv_vis_init(struct batadv_priv *bat_priv) goto err; } + batadv_hash_set_lock_class(bat_priv->vis.hash, + &batadv_vis_hash_lock_class_key); + bat_priv->vis.my_info = kmalloc(BATADV_MAX_VIS_PACKET_SIZE, GFP_ATOMIC); if (!bat_priv->vis.my_info) goto err; -- cgit v1.2.3 From 46d160ef887963f26fee2df3d3b99eab54637014 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sat, 1 Dec 2012 02:32:01 +0100 Subject: batman-adv: remove useless blank lines before and after brackets Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 6 ------ net/batman-adv/bridge_loop_avoidance.c | 4 ---- net/batman-adv/debugfs.c | 1 - net/batman-adv/originator.c | 3 --- net/batman-adv/routing.c | 4 ---- net/batman-adv/send.c | 2 -- net/batman-adv/translation-table.c | 7 ------- net/batman-adv/unicast.c | 2 -- 8 files changed, 29 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index f2a364970f57..d90e925d152a 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -183,7 +183,6 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, /* adjust all flags and log packets */ while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len, batadv_ogm_packet->tt_num_changes)) { - /* we might have aggregated direct link packets with an * ordinary base packet */ @@ -261,7 +260,6 @@ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) */ if ((directlink && (batadv_ogm_packet->header.ttl == 1)) || (forw_packet->own && (forw_packet->if_incoming != primary_if))) { - /* FIXME: what about aggregated packets ? */ batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n", @@ -325,7 +323,6 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, if (time_before(send_time, forw_packet->send_time) && time_after_eq(aggregation_end_time, forw_packet->send_time) && (aggregated_bytes <= BATADV_MAX_AGGREGATION_BYTES)) { - /* check aggregation compatibility * -> direct link packets are broadcasted on * their interface only @@ -815,7 +812,6 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, rcu_read_lock(); hlist_for_each_entry_rcu(tmp_neigh_node, node, &orig_neigh_node->neigh_list, list) { - if (!batadv_compare_eth(tmp_neigh_node->addr, orig_neigh_node->orig)) continue; @@ -949,7 +945,6 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, rcu_read_lock(); hlist_for_each_entry_rcu(tmp_neigh_node, node, &orig_node->neigh_list, list) { - is_duplicate |= batadv_test_bit(tmp_neigh_node->real_bits, orig_node->last_real_seqno, seqno); @@ -1223,7 +1218,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, /* is single hop (direct) neighbor */ if (is_single_hop_neigh) { - /* mark direct link on incoming interface */ batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, is_single_hop_neigh, diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index ec12c79104ec..724adf053dcc 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -235,7 +235,6 @@ batadv_bla_del_backbone_claims(struct batadv_backbone_gw *backbone_gw) spin_lock_bh(list_lock); hlist_for_each_entry_safe(claim, node, node_tmp, head, hash_entry) { - if (claim->backbone_gw != backbone_gw) continue; @@ -338,7 +337,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, "bla_send_claim(): REQUEST of %pM to %pMon vid %d\n", ethhdr->h_source, ethhdr->h_dest, vid); break; - } if (vid != -1) @@ -539,7 +537,6 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv, batadv_bla_send_claim(bat_priv, mac, backbone_gw->vid, BATADV_CLAIM_TYPE_ANNOUNCE); - } /** @@ -598,7 +595,6 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); batadv_backbone_gw_free_ref(claim->backbone_gw); - } /* set (new) backbone gw */ atomic_inc(&backbone_gw->refcount); diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 6f58ddd53bff..0049837e1f2a 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -164,7 +164,6 @@ static ssize_t batadv_log_read(struct file *file, char __user *buf, buf++; i++; - } spin_unlock_bh(&debug_log->lock); diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 109081c49bbd..fa88b2bec986 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -184,7 +184,6 @@ void batadv_originator_free(struct batadv_priv *bat_priv) spin_lock_bh(list_lock); hlist_for_each_entry_safe(orig_node, node, node_tmp, head, hash_entry) { - hlist_del_rcu(node); batadv_orig_node_free_ref(orig_node); } @@ -291,7 +290,6 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, /* for all neighbors towards this originator ... */ hlist_for_each_entry_safe(neigh_node, node, node_tmp, &orig_node->neigh_list, list) { - last_seen = neigh_node->last_seen; if_incoming = neigh_node->if_incoming; @@ -299,7 +297,6 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, (if_incoming->if_status == BATADV_IF_INACTIVE) || (if_incoming->if_status == BATADV_IF_NOT_IN_USE) || (if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)) { - if ((if_incoming->if_status == BATADV_IF_INACTIVE) || (if_incoming->if_status == BATADV_IF_NOT_IN_USE) || (if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 1aa1722d0187..db89238d6767 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -80,7 +80,6 @@ static void _batadv_update_route(struct batadv_priv *bat_priv, /* route added */ } else if ((!curr_router) && (neigh_node)) { - batadv_dbg(BATADV_DBG_ROUTES, bat_priv, "Adding route towards: %pM (via %pM)\n", orig_node->orig, neigh_node->addr); @@ -172,7 +171,6 @@ void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node, */ hlist_for_each_entry_rcu(tmp_neigh_node, node, &orig_node->neigh_list, list) { - if (tmp_neigh_node == neigh_node) continue; @@ -836,7 +834,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, if (unicast_packet->header.packet_type == BATADV_UNICAST_FRAG && batadv_frag_can_reassemble(skb, neigh_node->if_incoming->net_dev->mtu)) { - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); if (ret == NET_RX_DROP) @@ -1103,7 +1100,6 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb, /* packet for me */ if (batadv_is_my_mac(unicast_packet->dest)) { - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); if (ret == NET_RX_DROP) diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 4425af9dad40..89810cea0530 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -330,7 +330,6 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, spin_lock_bh(&bat_priv->forw_bcast_list_lock); hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, &bat_priv->forw_bcast_list, list) { - /* if purge_outstanding_packets() was called with an argument * we delete only packets belonging to the given interface */ @@ -357,7 +356,6 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, spin_lock_bh(&bat_priv->forw_bat_list_lock); hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, &bat_priv->forw_bat_list, list) { - /* if purge_outstanding_packets() was called with an argument * we delete only packets belonging to the given interface */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index c6fd0b760d3e..d4b27b65d6e9 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -116,7 +116,6 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const void *data) struct batadv_tt_global_entry, common); return tt_global_entry; - } static void @@ -256,7 +255,6 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, batadv_choose_orig, tt_global->common.addr); batadv_tt_global_entry_free_ref(tt_global); - } void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, @@ -652,7 +650,6 @@ static void batadv_tt_local_purge(struct batadv_priv *bat_priv) batadv_tt_local_purge_list(bat_priv, head); spin_unlock_bh(list_lock); } - } static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) @@ -1079,7 +1076,6 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry) batadv_tt_orig_list_entry_free_ref(orig_entry); } spin_unlock_bh(&tt_global_entry->list_lock); - } static void @@ -1855,7 +1851,6 @@ out: if (!ret) kfree_skb(skb); return ret; - } static bool @@ -2382,7 +2377,6 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) } spin_unlock_bh(list_lock); } - } static int batadv_tt_commit_changes(struct batadv_priv *bat_priv, @@ -2579,7 +2573,6 @@ bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, batadv_tt_local_entry_free_ref(tt_local_entry); out: return ret; - } bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 10aff49fcf25..f56bccf4229e 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -133,7 +133,6 @@ batadv_frag_search_packet(struct list_head *head, is_head = !!(up->flags & BATADV_UNI_FRAG_HEAD); list_for_each_entry(tfp, head, list) { - if (!tfp->skb) continue; @@ -162,7 +161,6 @@ void batadv_frag_list_free(struct list_head *head) struct batadv_frag_packet_list_entry *pf, *tmp_pf; if (!list_empty(head)) { - list_for_each_entry_safe(pf, tmp_pf, head, list) { kfree_skb(pf->skb); list_del(&pf->list); -- cgit v1.2.3 From 3f87c4239fa1df61beffbee2ca00e8ffbe31a5e3 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 29 Nov 2012 01:03:31 +0100 Subject: batman-adv: remove useless NULL check debugfs_remove_recursive() checks whether its argument is not null on its own, therefore it is possible to remove the external check. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/debugfs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 0049837e1f2a..5136d32bc9b9 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -396,10 +396,8 @@ err: void batadv_debugfs_destroy(void) { - if (batadv_debugfs) { - debugfs_remove_recursive(batadv_debugfs); - batadv_debugfs = NULL; - } + debugfs_remove_recursive(batadv_debugfs); + batadv_debugfs = NULL; } int batadv_debugfs_add_meshif(struct net_device *dev) -- cgit v1.2.3 From fa706554d6553331e886ce6fb86ca40b6dbc2083 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 26 Nov 2012 01:27:29 +0100 Subject: batman-adv: don't compile the BLA switch if not requested When the Bridge Loop Avoidance component is not compiled-in, its boolean switch should be not compiled as well. This patch surrounds the switch with a proper ifdef. This behaviour was introduced by 9fd6b0615b5499b270d39a92b8790e206cf75833 ("batman-adv: add bridge loop avoidance compile option") Signed-off-by: Antonio Quartulli Acked-by: Simon Wunderlich Signed-off-by: Marek Lindner --- net/batman-adv/soft-interface.c | 2 ++ net/batman-adv/types.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'net') diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index f8cc1429942f..57714f83ede4 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -480,7 +480,9 @@ struct net_device *batadv_softif_create(const char *name) atomic_set(&bat_priv->aggregated_ogms, 1); atomic_set(&bat_priv->bonding, 0); +#ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bridge_loop_avoidance, 0); +#endif #ifdef CONFIG_BATMAN_ADV_DAT atomic_set(&bat_priv->distributed_arp_table, 1); #endif diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index ae9ac9aca8c5..030ce412227e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -273,7 +273,9 @@ struct batadv_priv { atomic_t bonding; /* boolean */ atomic_t fragmentation; /* boolean */ atomic_t ap_isolation; /* boolean */ +#ifdef CONFIG_BATMAN_ADV_BLA atomic_t bridge_loop_avoidance; /* boolean */ +#endif #ifdef CONFIG_BATMAN_ADV_DAT atomic_t distributed_arp_table; /* boolean */ #endif -- cgit v1.2.3 From 467b5fe69716257ebe17a9df9c428ad32eba540a Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Fri, 7 Dec 2012 14:35:36 +0100 Subject: batman-adv: use the const qualifier in hash functions The data argument in each hash function should carry the "const" qualifier as it is never modified. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bridge_loop_avoidance.c | 2 +- net/batman-adv/hash.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 724adf053dcc..5e834c1df469 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -57,7 +57,7 @@ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) static inline uint32_t batadv_choose_backbone_gw(const void *data, uint32_t size) { - struct batadv_claim *claim = (struct batadv_claim *)data; + const struct batadv_claim *claim = (struct batadv_claim *)data; uint32_t hash = 0; hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr)); diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index e05333905afd..ea0214824327 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -89,7 +89,7 @@ static inline void batadv_hash_delete(struct batadv_hashtable *hash, * * Returns the new hash value. */ -static inline uint32_t batadv_hash_bytes(uint32_t hash, void *data, +static inline uint32_t batadv_hash_bytes(uint32_t hash, const void *data, uint32_t size) { const unsigned char *key = data; -- cgit v1.2.3 From c0275e243c65be76a5e1352c58c8a4aeeb3854c6 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 11 Dec 2012 20:29:22 +0100 Subject: batman-adv: fix typo in debug message in bat_iv_ogm.c a debug message should print "tq" instead of "td" Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index d90e925d152a..8f2de43b7597 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1028,7 +1028,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, is_single_hop_neigh = true; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, td %d, TTL %d, V %d, IDF %d)\n", + "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n", ethhdr->h_source, if_incoming->net_dev->name, if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig, batadv_ogm_packet->prev_sender, -- cgit v1.2.3 From 9338026107166b54d54ef875978c9c3dca3aaee5 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Sat, 15 Dec 2012 19:09:11 +0800 Subject: batman-adv: remove unused variable from orig_node struct Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 030ce412227e..441880a0caa7 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -119,7 +119,6 @@ struct batadv_orig_node { spinlock_t ogm_cnt_lock; /* bcast_seqno_lock protects bcast_bits, last_bcast_seqno */ spinlock_t bcast_seqno_lock; - spinlock_t tt_list_lock; /* protects tt_list */ atomic_t bond_candidates; struct list_head bond_list; }; -- cgit v1.2.3 From 0c430d0d7b8bafa74959a84156ef0bda1417a3d4 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Sun, 16 Dec 2012 13:53:15 +0800 Subject: batman-adv: unbloat batadv_priv if debug is not enabled Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/debugfs.c | 1 - net/batman-adv/soft-interface.c | 2 ++ net/batman-adv/types.h | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 5136d32bc9b9..55a90078dc66 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -229,7 +229,6 @@ static void batadv_debug_log_cleanup(struct batadv_priv *bat_priv) #else /* CONFIG_BATMAN_ADV_DEBUG */ static int batadv_debug_log_setup(struct batadv_priv *bat_priv) { - bat_priv->debug_log = NULL; return 0; } diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 57714f83ede4..3d6816667bfc 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -493,7 +493,9 @@ struct net_device *batadv_softif_create(const char *name) atomic_set(&bat_priv->gw_bandwidth, 41); atomic_set(&bat_priv->orig_interval, 1000); atomic_set(&bat_priv->hop_penalty, 30); +#ifdef CONFIG_BATMAN_ADV_DEBUG atomic_set(&bat_priv->log_level, 0); +#endif atomic_set(&bat_priv->fragmentation, 1); atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN); atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 441880a0caa7..d8061ac28409 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -284,12 +284,16 @@ struct batadv_priv { atomic_t gw_bandwidth; /* gw bandwidth */ atomic_t orig_interval; /* uint */ atomic_t hop_penalty; /* uint */ +#ifdef CONFIG_BATMAN_ADV_DEBUG atomic_t log_level; /* uint */ +#endif atomic_t bcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left; char num_ifaces; +#ifdef CONFIG_BATMAN_ADV_DEBUG struct batadv_debug_log *debug_log; +#endif struct kobject *mesh_obj; struct dentry *debug_dir; struct hlist_head forw_bat_list; -- cgit v1.2.3 From 3e4e4c1f2da66b29ee9379ca29f8dd620c2b5a1f Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:01:39 +0000 Subject: ipv6: Introduce ip6_flow_hdr() to fill version, tclass and flowlabel. This is not only for readability but also for optimization. What we do here is to build the 32bit word at the beginning of the ipv6 header (the "ip6_flow" virtual member of struct ip6_hdr in RFC3542) and we do not need to read the tclass portion of the target buffer. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 9 +++++++++ net/ipv6/ip6_gre.c | 6 ++---- net/ipv6/ip6_output.c | 8 +++----- net/ipv6/ip6_tunnel.c | 4 +--- net/ipv6/netfilter/ip6t_REJECT.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 5af66b26ebdd..fcbc6464b014 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -546,6 +546,15 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); +/* + * Header manipulation + */ +static inline void ip6_flow_hdr(struct ipv6hdr *hdr, unsigned int tclass, + __be32 flowlabel) +{ + *(__be32 *)hdr = ntohl(0x60000000 | (tclass << 20)) | flowlabel; +} + /* * Prototypes exported by ipv6 */ diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index c727e4712751..db91fe3466a3 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -772,9 +772,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, * Push down and install the IP header. */ ipv6h = ipv6_hdr(skb); - *(__be32 *)ipv6h = fl6->flowlabel | htonl(0x60000000); - dsfield = INET_ECN_encapsulate(0, dsfield); - ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); + ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel); ipv6h->hop_limit = tunnel->parms.hop_limit; ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; @@ -1240,7 +1238,7 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev, struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen); __be16 *p = (__be16 *)(ipv6h+1); - *(__be32 *)ipv6h = t->fl.u.ip6.flowlabel | htonl(0x60000000); + ip6_flow_hdr(ipv6h, 0, t->fl.u.ip6.flowlabel); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = NEXTHDR_GRE; ipv6h->saddr = t->parms.laddr; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 5552d13ae92f..9250c696de9d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -216,7 +216,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); - *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl6->flowlabel; + ip6_flow_hdr(hdr, tclass, fl6->flowlabel); hdr->payload_len = htons(seg_len); hdr->nexthdr = proto; @@ -267,7 +267,7 @@ int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev, skb_put(skb, sizeof(struct ipv6hdr)); hdr = ipv6_hdr(skb); - *(__be32*)hdr = htonl(0x60000000); + ip6_flow_hdr(hdr, 0, 0); hdr->payload_len = htons(len); hdr->nexthdr = proto; @@ -1548,9 +1548,7 @@ int ip6_push_pending_frames(struct sock *sk) skb_reset_network_header(skb); hdr = ipv6_hdr(skb); - *(__be32*)hdr = fl6->flowlabel | - htonl(0x60000000 | ((int)np->cork.tclass << 20)); - + ip6_flow_hdr(hdr, np->cork.tclass, fl6->flowlabel); hdr->hop_limit = np->cork.hop_limit; hdr->nexthdr = proto; hdr->saddr = fl6->saddr; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index a14f28b280f5..fff83cbc197f 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1030,9 +1030,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); ipv6h = ipv6_hdr(skb); - *(__be32*)ipv6h = fl6->flowlabel | htonl(0x60000000); - dsfield = INET_ECN_encapsulate(0, dsfield); - ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield); + ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index fd4fb34c51c7..8e75b6f3db91 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -126,7 +126,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); - *(__be32 *)ip6h = htonl(0x60000000 | (tclass << 20)); + ip6_flow_hdr(ip6h, tclass, 0); ip6h->hop_limit = ip6_dst_hoplimit(dst); ip6h->nexthdr = IPPROTO_TCP; ip6h->saddr = oip6h->daddr; -- cgit v1.2.3 From 6502ca527f8ed2c3bb327d9db8e7e6e7dcbef511 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:01:51 +0000 Subject: ipv6: Introduce ip6_flowinfo() to extract flowinfo (tclass + flowlabel). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 +++++ net/ipv6/datagram.c | 9 +++++---- net/ipv6/route.c | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index fcbc6464b014..ed672080c690 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -555,6 +555,11 @@ static inline void ip6_flow_hdr(struct ipv6hdr *hdr, unsigned int tclass, *(__be32 *)hdr = ntohl(0x60000000 | (tclass << 20)) | flowlabel; } +static inline __be32 ip6_flowinfo(const struct ipv6hdr *hdr) +{ + return *(__be32 *)hdr & IPV6_FLOWINFO_MASK; +} + /* * Prototypes exported by ipv6 */ diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 56b692bb94a7..7f65df41c396 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -360,7 +360,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) struct ipv6hdr, daddr); sin->sin6_addr = ip6h->daddr; if (np->sndflow) - sin->sin6_flowinfo = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK; + sin->sin6_flowinfo = ip6_flowinfo(ip6h); if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin->sin6_scope_id = IP6CB(skb)->iif; } else { @@ -491,9 +491,10 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); } - if (np->rxopt.bits.rxflow && (*(__be32 *)nh & IPV6_FLOWINFO_MASK)) { - __be32 flowinfo = *(__be32 *)nh & IPV6_FLOWINFO_MASK; - put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); + if (np->rxopt.bits.rxflow) { + __be32 flowinfo = ip6_flowinfo((struct ipv6hdr *)nh); + if (flowinfo) + put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); } /* HbH is allowed only once */ diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 621b68ecf16f..6238eb5037a7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -994,7 +994,7 @@ void ip6_route_input(struct sk_buff *skb) .flowi6_iif = skb->dev->ifindex, .daddr = iph->daddr, .saddr = iph->saddr, - .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, + .flowlabel = ip6_flowinfo(iph), .flowi6_mark = skb->mark, .flowi6_proto = iph->nexthdr, }; @@ -1159,7 +1159,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; - fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; + fl6.flowlabel = ip6_flowinfo(iph); dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1187,7 +1187,7 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; - fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; + fl6.flowlabel = ip6_flowinfo(iph); dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) -- cgit v1.2.3 From e7219858ac1f98213a4714d0e24e7a003e1bf6a2 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:02:01 +0000 Subject: ipv6: Use ipv6_get_dsfield() instead of ipv6_tclass(). Commit 7a3198a8 ("ipv6: helper function to get tclass") introduced ipv6_tclass(), but similar function is already available as ipv6_get_dsfield(). We might be able to call ipv6_tclass() from ipv6_get_dsfield(), but it is confusing to have two versions. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/linux/ipv6.h | 5 ----- net/ipv6/datagram.c | 3 ++- net/ipv6/tcp_ipv6.c | 6 +++--- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index faed1e357dd6..304a9f46b578 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -77,11 +77,6 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) return (struct ipv6hdr *)skb_transport_header(skb); } -static inline __u8 ipv6_tclass(const struct ipv6hdr *iph) -{ - return (ntohl(*(__be32 *)iph) >> 20) & 0xff; -} - /* This structure contains results of exthdrs parsing as offsets from skb->nh. diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 7f65df41c396..33be36398a78 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -487,7 +488,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } if (np->rxopt.bits.rxtclass) { - int tclass = ipv6_tclass(ipv6_hdr(skb)); + int tclass = ipv6_get_dsfield(ipv6_hdr(skb)); put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3164ad272a74..3701c3c6e2eb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1163,7 +1163,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; - newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); + newnp->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb)); /* * No need to charge this sock to the relevant IPv6 refcnt debug socks count @@ -1243,7 +1243,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newnp->opt = NULL; newnp->mcast_oif = inet6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; - newnp->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); + newnp->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb)); /* Clone native IPv6 options from listening socket (if any) @@ -1456,7 +1456,7 @@ ipv6_pktoptions: if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; if (np->rxopt.bits.rxtclass) - np->rcv_tclass = ipv6_tclass(ipv6_hdr(skb)); + np->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb)); if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); opt_skb = xchg(&np->pktoptions, opt_skb); -- cgit v1.2.3 From daad151263cf334d57fcc0270e2483d4b4639650 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:02:18 +0000 Subject: ipv6: Make ipv6_is_mld() inline and use it from ip6_mc_input(). Move generalized version of ipv6_is_mld() to header, and use it from ip6_mc_input(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/addrconf.h | 26 +++++++++++++++++++++++++- net/ipv6/ip6_input.c | 21 +++------------------ net/ipv6/mcast.c | 27 --------------------------- 3 files changed, 28 insertions(+), 46 deletions(-) (limited to 'net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index df4ef9453384..7cd14c007fc5 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -150,7 +150,31 @@ extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); extern bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, const struct in6_addr *src_addr); -extern bool ipv6_is_mld(struct sk_buff *skb, int nexthdr); + +/* + * identify MLD packets for MLD filter exceptions + */ +static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) +{ + struct icmp6hdr *hdr; + + if (nexthdr != IPPROTO_ICMPV6 || + !pskb_network_may_pull(skb, offset + sizeof(struct icmp6hdr))) + return false; + + hdr = (struct icmp6hdr *)(skb_network_header(skb) + offset); + + switch (hdr->icmp6_type) { + case ICMPV6_MGM_QUERY: + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_REDUCTION: + case ICMPV6_MLD2_REPORT: + return true; + default: + break; + } + return false; +} extern void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index a52d864d562b..2ccd35ec3628 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -212,7 +212,7 @@ resubmit: if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && - !ipv6_is_mld(skb, nexthdr)) + !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) goto discard; } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && @@ -283,7 +283,6 @@ int ip6_mc_input(struct sk_buff *skb) if (unlikely(opt->ra)) { /* Check if this is a mld message */ u8 *ptr = skb_network_header(skb) + opt->ra; - struct icmp6hdr *icmp6; u8 nexthdr = hdr->nexthdr; __be16 frag_off; int offset; @@ -303,24 +302,10 @@ int ip6_mc_input(struct sk_buff *skb) if (offset < 0) goto out; - if (nexthdr != IPPROTO_ICMPV6) + if (!ipv6_is_mld(skb, nexthdr, offset)) goto out; - if (!pskb_may_pull(skb, (skb_network_header(skb) + - offset + 1 - skb->data))) - goto out; - - icmp6 = (struct icmp6hdr *)(skb_network_header(skb) + offset); - - switch (icmp6->icmp6_type) { - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - case ICMPV6_MLD2_REPORT: - deliver = true; - break; - } - goto out; + deliver = true; } /* unknown RA - process it normally */ } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 28dfa5f3801f..8237ee15eafd 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -934,33 +934,6 @@ int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) return err; } -/* - * identify MLD packets for MLD filter exceptions - */ -bool ipv6_is_mld(struct sk_buff *skb, int nexthdr) -{ - struct icmp6hdr *pic; - - if (nexthdr != IPPROTO_ICMPV6) - return false; - - if (!pskb_may_pull(skb, sizeof(struct icmp6hdr))) - return false; - - pic = icmp6_hdr(skb); - - switch (pic->icmp6_type) { - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - case ICMPV6_MLD2_REPORT: - return true; - default: - break; - } - return false; -} - /* * check if the interface/address pair is valid */ -- cgit v1.2.3 From c08977bb2b198cca1827d982b0c137458f4c2927 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:02:29 +0000 Subject: ipv6 route: Use ipv6_addr_hash() in rt6_info_hash_nhsfn(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6238eb5037a7..34f392f050c1 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -388,15 +388,8 @@ static int rt6_info_hash_nhsfn(unsigned int candidate_count, { unsigned int val = fl6->flowi6_proto; - val ^= (__force u32)fl6->daddr.s6_addr32[0]; - val ^= (__force u32)fl6->daddr.s6_addr32[1]; - val ^= (__force u32)fl6->daddr.s6_addr32[2]; - val ^= (__force u32)fl6->daddr.s6_addr32[3]; - - val ^= (__force u32)fl6->saddr.s6_addr32[0]; - val ^= (__force u32)fl6->saddr.s6_addr32[1]; - val ^= (__force u32)fl6->saddr.s6_addr32[2]; - val ^= (__force u32)fl6->saddr.s6_addr32[3]; + val ^= ipv6_addr_hash(&fl6->daddr); + val ^= ipv6_addr_hash(&fl6->saddr); /* Work only if this not encapsulated */ switch (fl6->flowi6_proto) { -- cgit v1.2.3 From 2b464f61f036f7fb9cbc34710c9b2390499e87d0 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:02:38 +0000 Subject: ipv6 xfrm: Use ipv6_addr_hash() in xfrm6_tunnel_spi_hash_byaddr(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/xfrm6_tunnel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index ee5a7065aacc..babd1674388a 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -72,7 +72,7 @@ static inline unsigned int xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *ad { unsigned int h; - h = (__force u32)(addr->a6[0] ^ addr->a6[1] ^ addr->a6[2] ^ addr->a6[3]); + h = ipv6_addr_hash((const struct in6_addr *)addr); h ^= h >> 16; h ^= h >> 8; h &= XFRM6_TUNNEL_SPI_BYADDR_HSIZE - 1; -- cgit v1.2.3 From dd3332bfcb2223458f553f341d3388cb84040e6a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 13 Jan 2013 05:02:45 +0000 Subject: ipv6: Store Router Alert option in IP6CB directly. Router Alert option is very small and we can store the value itself in the skb. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/linux/ipv6.h | 3 ++- include/uapi/linux/ipv6.h | 2 ++ net/ipv6/exthdrs.c | 3 ++- net/ipv6/ip6_input.c | 5 ++--- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 304a9f46b578..e971e3742172 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -84,7 +84,7 @@ static inline struct ipv6hdr *ipipv6_hdr(const struct sk_buff *skb) struct inet6_skb_parm { int iif; - __u16 ra; + __be16 ra; __u16 hop; __u16 dst0; __u16 srcrt; @@ -100,6 +100,7 @@ struct inet6_skb_parm { #define IP6SKB_XFRM_TRANSFORMED 1 #define IP6SKB_FORWARDED 2 #define IP6SKB_REROUTED 4 +#define IP6SKB_ROUTERALERT 8 }; #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 5a2991cf0251..4bda4cf5b0f5 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -63,6 +63,8 @@ struct ipv6_opt_hdr { #define ipv6_destopt_hdr ipv6_opt_hdr #define ipv6_hopopt_hdr ipv6_opt_hdr +/* Router Alert option values (RFC2711) */ +#define IPV6_OPT_ROUTERALERT_MLD 0x0000 /* MLD(RFC2710) */ /* * routing header type 0 (used in cmsghdr struct) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 473f628f9f20..07a7d65a7cb6 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -553,7 +553,8 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) const unsigned char *nh = skb_network_header(skb); if (nh[optoff + 1] == 2) { - IP6CB(skb)->ra = optoff; + IP6CB(skb)->flags |= IP6SKB_ROUTERALERT; + memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra)); return true; } LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 2ccd35ec3628..4ac5bf30e16a 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -280,9 +280,8 @@ int ip6_mc_input(struct sk_buff *skb) struct inet6_skb_parm *opt = IP6CB(skb); /* Check for MLD */ - if (unlikely(opt->ra)) { + if (unlikely(opt->flags & IP6SKB_ROUTERALERT)) { /* Check if this is a mld message */ - u8 *ptr = skb_network_header(skb) + opt->ra; u8 nexthdr = hdr->nexthdr; __be16 frag_off; int offset; @@ -290,7 +289,7 @@ int ip6_mc_input(struct sk_buff *skb) /* Check if the value of Router Alert * is for MLD (0x0000). */ - if ((ptr[2] | ptr[3]) == 0) { + if (opt->ra == htons(IPV6_OPT_ROUTERALERT_MLD)) { deliver = false; if (!ipv6_ext_hdr(nexthdr)) { -- cgit v1.2.3 From 605928337866c6369ae60509fa2b10af325a25eb Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 14 Jan 2013 09:28:27 +0000 Subject: ipv6 netevent: Remove old_neigh from netevent_redirect. The only user is cxgb3 driver. old_neigh is used to check device change, but it must not happen on redirect. In this sense, we can remove old_neigh argument. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 35 +++++++--------------- include/net/netevent.h | 3 +- net/ipv6/route.c | 3 +- 3 files changed, 13 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 91d02eb51b8b..4232767862b5 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -64,9 +64,8 @@ static const unsigned int MAX_ATIDS = 64 * 1024; static const unsigned int ATID_BASE = 0x10000; static void cxgb_neigh_update(struct neighbour *neigh); -static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, - struct dst_entry *new, struct neighbour *new_neigh, - const void *daddr); +static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new, + struct neighbour *neigh, const void *daddr); static inline int offload_activated(struct t3cdev *tdev) { @@ -970,10 +969,9 @@ static int nb_callback(struct notifier_block *self, unsigned long event, } case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; - cxgb_redirect(nr->old, nr->old_neigh, - nr->new, nr->new_neigh, + cxgb_redirect(nr->old, nr->new, nr->neigh, nr->daddr); - cxgb_neigh_update(nr->new_neigh); + cxgb_neigh_update(nr->neigh); break; } default: @@ -1109,11 +1107,11 @@ static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) tdev->send(tdev, skb); } -static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, - struct dst_entry *new, struct neighbour *new_neigh, +static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new, + struct neighbour *neigh, const void *daddr) { - struct net_device *olddev, *newdev; + struct net_device *dev; struct tid_info *ti; struct t3cdev *tdev; u32 tid; @@ -1121,26 +1119,15 @@ static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, struct l2t_entry *e; struct t3c_tid_entry *te; - olddev = old_neigh->dev; - newdev = new_neigh->dev; + dev = neigh->dev; - if (!is_offloading(olddev)) - return; - if (!is_offloading(newdev)) { - pr_warn("%s: Redirect to non-offload device ignored\n", - __func__); + if (!is_offloading(dev)) return; - } - tdev = dev2t3cdev(olddev); + tdev = dev2t3cdev(dev); BUG_ON(!tdev); - if (tdev != dev2t3cdev(newdev)) { - pr_warn("%s: Redirect to different offload device ignored\n", - __func__); - return; - } /* Add new L2T entry */ - e = t3_l2t_get(tdev, new, newdev, daddr); + e = t3_l2t_get(tdev, new, dev, daddr); if (!e) { pr_err("%s: couldn't allocate new l2t entry!\n", __func__); return; diff --git a/include/net/netevent.h b/include/net/netevent.h index 3ce4988c9c08..fe630dde35c3 100644 --- a/include/net/netevent.h +++ b/include/net/netevent.h @@ -16,9 +16,8 @@ struct neighbour; struct netevent_redirect { struct dst_entry *old; - struct neighbour *old_neigh; struct dst_entry *new; - struct neighbour *new_neigh; + struct neighbour *neigh; const void *daddr; }; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 34f392f050c1..7c34c01b515b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1803,10 +1803,9 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu goto out; netevent.old = &rt->dst; - netevent.old_neigh = old_neigh; netevent.new = &nrt->dst; - netevent.new_neigh = neigh; netevent.daddr = &msg->dest; + netevent.neigh = neigh; call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); if (rt->rt6i_flags & RTF_CACHE) { -- cgit v1.2.3 From c1b52739e45f5969b208ebc377f52468280af11e Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Mon, 14 Jan 2013 05:15:39 +0000 Subject: pkt_sched: namespace aware act_mirred Eric Dumazet pointed out that act_mirred needs to find the current net_ns, and struct net pointer is not provided in the call chain. His original patch made use of current->nsproxy->net_ns to find the network namespace, but this fails to work correctly for userspace code that makes use of netlink sockets in different network namespaces. Instead, pass the "struct net *" down along the call chain to where it is needed. This version removes the ifb changes as Eric has submitted that patch separately, but is otherwise identical to the previous version. Signed-off-by: Benjamin LaHaise Tested-by: Eric Dumazet Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 12 +++++++++--- include/net/pkt_cls.h | 7 ++++--- include/net/sch_generic.h | 2 +- net/sched/act_api.c | 18 ++++++++++-------- net/sched/act_csum.c | 2 +- net/sched/act_gact.c | 5 +++-- net/sched/act_ipt.c | 2 +- net/sched/act_mirred.c | 7 ++++--- net/sched/act_nat.c | 2 +- net/sched/act_pedit.c | 5 +++-- net/sched/act_police.c | 5 +++-- net/sched/act_simple.c | 5 +++-- net/sched/act_skbedit.c | 5 +++-- net/sched/cls_api.c | 11 ++++++----- net/sched/cls_basic.c | 13 +++++++------ net/sched/cls_cgroup.c | 5 +++-- net/sched/cls_flow.c | 4 ++-- net/sched/cls_fw.c | 10 +++++----- net/sched/cls_route.c | 15 ++++++++------- net/sched/cls_rsvp.h | 4 ++-- net/sched/cls_tcindex.c | 14 ++++++++------ net/sched/cls_u32.c | 13 +++++++------ 22 files changed, 94 insertions(+), 72 deletions(-) (limited to 'net') diff --git a/include/net/act_api.h b/include/net/act_api.h index c739531e1564..112c25c393a2 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -91,7 +91,9 @@ struct tc_action_ops { int (*dump)(struct sk_buff *, struct tc_action *, int, int); int (*cleanup)(struct tc_action *, int bind); int (*lookup)(struct tc_action *, u32); - int (*init)(struct nlattr *, struct nlattr *, struct tc_action *, int , int); + int (*init)(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *act, int ovr, + int bind); int (*walk)(struct sk_buff *, struct netlink_callback *, int, struct tc_action *); }; @@ -116,8 +118,12 @@ extern int tcf_register_action(struct tc_action_ops *a); extern int tcf_unregister_action(struct tc_action_ops *a); extern void tcf_action_destroy(struct tc_action *a, int bind); extern int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res); -extern struct tc_action *tcf_action_init(struct nlattr *nla, struct nlattr *est, char *n, int ovr, int bind); -extern struct tc_action *tcf_action_init_1(struct nlattr *nla, struct nlattr *est, char *n, int ovr, int bind); +extern struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, + struct nlattr *est, char *n, int ovr, + int bind); +extern struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, + struct nlattr *est, char *n, int ovr, + int bind); extern int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); extern int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); extern int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 9fcc680ab6b9..13174509cdfd 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -126,9 +126,10 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, return 0; } -extern int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb, - struct nlattr *rate_tlv, struct tcf_exts *exts, - const struct tcf_ext_map *map); +extern int tcf_exts_validate(struct net *net, struct tcf_proto *tp, + struct nlattr **tb, struct nlattr *rate_tlv, + struct tcf_exts *exts, + const struct tcf_ext_map *map); extern void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts); extern void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, struct tcf_exts *src); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 1540f9c2fcf4..2d06c2a53de1 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -195,7 +195,7 @@ struct tcf_proto_ops { unsigned long (*get)(struct tcf_proto*, u32 handle); void (*put)(struct tcf_proto*, unsigned long); - int (*change)(struct sk_buff *, + int (*change)(struct net *net, struct sk_buff *, struct tcf_proto*, unsigned long, u32 handle, struct nlattr **, unsigned long *); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 65d240cbf74b..8579c4bb20c9 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -485,8 +485,9 @@ errout: return err; } -struct tc_action *tcf_action_init_1(struct nlattr *nla, struct nlattr *est, - char *name, int ovr, int bind) +struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, + struct nlattr *est, char *name, int ovr, + int bind) { struct tc_action *a; struct tc_action_ops *a_o; @@ -542,9 +543,9 @@ struct tc_action *tcf_action_init_1(struct nlattr *nla, struct nlattr *est, /* backward compatibility for policer */ if (name == NULL) - err = a_o->init(tb[TCA_ACT_OPTIONS], est, a, ovr, bind); + err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind); else - err = a_o->init(nla, est, a, ovr, bind); + err = a_o->init(net, nla, est, a, ovr, bind); if (err < 0) goto err_free; @@ -566,8 +567,9 @@ err_out: return ERR_PTR(err); } -struct tc_action *tcf_action_init(struct nlattr *nla, struct nlattr *est, - char *name, int ovr, int bind) +struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, + struct nlattr *est, char *name, int ovr, + int bind) { struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; struct tc_action *head = NULL, *act, *act_prev = NULL; @@ -579,7 +581,7 @@ struct tc_action *tcf_action_init(struct nlattr *nla, struct nlattr *est, return ERR_PTR(err); for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { - act = tcf_action_init_1(tb[i], est, name, ovr, bind); + act = tcf_action_init_1(net, tb[i], est, name, ovr, bind); if (IS_ERR(act)) goto err; act->order = i; @@ -960,7 +962,7 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, struct tc_action *a; u32 seq = n->nlmsg_seq; - act = tcf_action_init(nla, NULL, NULL, ovr, 0); + act = tcf_action_init(net, nla, NULL, NULL, ovr, 0); if (act == NULL) goto done; if (IS_ERR(act)) { diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 2c8ad7c86e43..08fa1e8a4ca4 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -51,7 +51,7 @@ static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, }; -static int tcf_csum_init(struct nlattr *nla, struct nlattr *est, +static int tcf_csum_init(struct net *n, struct nlattr *nla, struct nlattr *est, struct tc_action *a, int ovr, int bind) { struct nlattr *tb[TCA_CSUM_MAX + 1]; diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 05d60859d8e3..fd2b3cff5fa2 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -58,8 +58,9 @@ static const struct nla_policy gact_policy[TCA_GACT_MAX + 1] = { [TCA_GACT_PROB] = { .len = sizeof(struct tc_gact_p) }, }; -static int tcf_gact_init(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_gact_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) { struct nlattr *tb[TCA_GACT_MAX + 1]; struct tc_gact *parm; diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 58fb3c7aab9e..0fb9e3f567e6 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -102,7 +102,7 @@ static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = { [TCA_IPT_TARG] = { .len = sizeof(struct xt_entry_target) }, }; -static int tcf_ipt_init(struct nlattr *nla, struct nlattr *est, +static int tcf_ipt_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action *a, int ovr, int bind) { struct nlattr *tb[TCA_IPT_MAX + 1]; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 9c0fd0c78814..5d676edc22a6 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -62,8 +62,9 @@ static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { [TCA_MIRRED_PARMS] = { .len = sizeof(struct tc_mirred) }, }; -static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_mirred_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, int ovr, + int bind) { struct nlattr *tb[TCA_MIRRED_MAX + 1]; struct tc_mirred *parm; @@ -88,7 +89,7 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est, return -EINVAL; } if (parm->ifindex) { - dev = __dev_get_by_index(&init_net, parm->ifindex); + dev = __dev_get_by_index(net, parm->ifindex); if (dev == NULL) return -ENODEV; switch (dev->type) { diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index b5d029eb44f2..876f0ef29694 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -44,7 +44,7 @@ static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) }, }; -static int tcf_nat_init(struct nlattr *nla, struct nlattr *est, +static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, struct tc_action *a, int ovr, int bind) { struct nlattr *tb[TCA_NAT_MAX + 1]; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 45c53ab067a6..0c3faddf3f2c 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -38,8 +38,9 @@ static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = { [TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) }, }; -static int tcf_pedit_init(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_pedit_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) { struct nlattr *tb[TCA_PEDIT_MAX + 1]; struct tc_pedit *parm; diff --git a/net/sched/act_police.c b/net/sched/act_police.c index a9de23297d47..8dbd695c160b 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -130,8 +130,9 @@ static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { [TCA_POLICE_RESULT] = { .type = NLA_U32 }, }; -static int tcf_act_police_locate(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_act_police_locate(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) { unsigned int h; int ret = 0, err; diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 3714f60f0b3c..7725eb4ab756 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -95,8 +95,9 @@ static const struct nla_policy simple_policy[TCA_DEF_MAX + 1] = { [TCA_DEF_DATA] = { .type = NLA_STRING, .len = SIMP_MAX_DATA }, }; -static int tcf_simp_init(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_simp_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) { struct nlattr *tb[TCA_DEF_MAX + 1]; struct tc_defact *parm; diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 476e0fac6712..cb4221171f93 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -67,8 +67,9 @@ static const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = { [TCA_SKBEDIT_MARK] = { .len = sizeof(u32) }, }; -static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est, - struct tc_action *a, int ovr, int bind) +static int tcf_skbedit_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) { struct nlattr *tb[TCA_SKBEDIT_MAX + 1]; struct tc_skbedit *parm; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index ff55ed6c49b2..964f5e4f4b8a 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -321,7 +321,7 @@ replay: } } - err = tp->ops->change(skb, tp, cl, t->tcm_handle, tca, &fh); + err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh); if (err == 0) { if (tp_created) { spin_lock_bh(root_lock); @@ -508,7 +508,7 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) } EXPORT_SYMBOL(tcf_exts_destroy); -int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb, +int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, struct tcf_exts *exts, const struct tcf_ext_map *map) { @@ -519,7 +519,7 @@ int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb, struct tc_action *act; if (map->police && tb[map->police]) { - act = tcf_action_init_1(tb[map->police], rate_tlv, + act = tcf_action_init_1(net, tb[map->police], rate_tlv, "police", TCA_ACT_NOREPLACE, TCA_ACT_BIND); if (IS_ERR(act)) @@ -528,8 +528,9 @@ int tcf_exts_validate(struct tcf_proto *tp, struct nlattr **tb, act->type = TCA_OLD_COMPAT; exts->action = act; } else if (map->action && tb[map->action]) { - act = tcf_action_init(tb[map->action], rate_tlv, NULL, - TCA_ACT_NOREPLACE, TCA_ACT_BIND); + act = tcf_action_init(net, tb[map->action], rate_tlv, + NULL, TCA_ACT_NOREPLACE, + TCA_ACT_BIND); if (IS_ERR(act)) return PTR_ERR(act); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 344a11b342e5..d76a35d0dc85 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -132,15 +132,16 @@ static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = { [TCA_BASIC_EMATCHES] = { .type = NLA_NESTED }, }; -static int basic_set_parms(struct tcf_proto *tp, struct basic_filter *f, - unsigned long base, struct nlattr **tb, +static int basic_set_parms(struct net *net, struct tcf_proto *tp, + struct basic_filter *f, unsigned long base, + struct nlattr **tb, struct nlattr *est) { int err = -EINVAL; struct tcf_exts e; struct tcf_ematch_tree t; - err = tcf_exts_validate(tp, tb, est, &e, &basic_ext_map); + err = tcf_exts_validate(net, tp, tb, est, &e, &basic_ext_map); if (err < 0) return err; @@ -162,7 +163,7 @@ errout: return err; } -static int basic_change(struct sk_buff *in_skb, +static int basic_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { @@ -182,7 +183,7 @@ static int basic_change(struct sk_buff *in_skb, if (f != NULL) { if (handle && f->handle != handle) return -EINVAL; - return basic_set_parms(tp, f, base, tb, tca[TCA_RATE]); + return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]); } err = -ENOBUFS; @@ -208,7 +209,7 @@ static int basic_change(struct sk_buff *in_skb, f->handle = head->hgenerator; } - err = basic_set_parms(tp, f, base, tb, tca[TCA_RATE]); + err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]); if (err < 0) goto errout; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 6db7855b9029..3a294eb98d61 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -178,7 +178,7 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, }; -static int cls_cgroup_change(struct sk_buff *in_skb, +static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) @@ -215,7 +215,8 @@ static int cls_cgroup_change(struct sk_buff *in_skb, if (err < 0) return err; - err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &cgroup_ext_map); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, + &cgroup_ext_map); if (err < 0) return err; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index ce82d0cb1b47..aa36a8c8b33b 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -351,7 +351,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, }; -static int flow_change(struct sk_buff *in_skb, +static int flow_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) @@ -397,7 +397,7 @@ static int flow_change(struct sk_buff *in_skb, return -EOPNOTSUPP; } - err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &flow_ext_map); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &flow_ext_map); if (err < 0) return err; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 4075a0aef2aa..1135d8227f9b 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -192,7 +192,7 @@ static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { }; static int -fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f, +fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, struct nlattr **tb, struct nlattr **tca, unsigned long base) { struct fw_head *head = (struct fw_head *)tp->root; @@ -200,7 +200,7 @@ fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f, u32 mask; int err; - err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &fw_ext_map); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &fw_ext_map); if (err < 0) return err; @@ -233,7 +233,7 @@ errout: return err; } -static int fw_change(struct sk_buff *in_skb, +static int fw_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, @@ -255,7 +255,7 @@ static int fw_change(struct sk_buff *in_skb, if (f != NULL) { if (f->id != handle && handle) return -EINVAL; - return fw_change_attrs(tp, f, tb, tca, base); + return fw_change_attrs(net, tp, f, tb, tca, base); } if (!handle) @@ -282,7 +282,7 @@ static int fw_change(struct sk_buff *in_skb, f->id = handle; - err = fw_change_attrs(tp, f, tb, tca, base); + err = fw_change_attrs(net, tp, f, tb, tca, base); if (err < 0) goto errout; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index c10d57bf98f2..37da567d833e 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -335,9 +335,10 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = { [TCA_ROUTE4_IIF] = { .type = NLA_U32 }, }; -static int route4_set_parms(struct tcf_proto *tp, unsigned long base, - struct route4_filter *f, u32 handle, struct route4_head *head, - struct nlattr **tb, struct nlattr *est, int new) +static int route4_set_parms(struct net *net, struct tcf_proto *tp, + unsigned long base, struct route4_filter *f, + u32 handle, struct route4_head *head, + struct nlattr **tb, struct nlattr *est, int new) { int err; u32 id = 0, to = 0, nhandle = 0x8000; @@ -346,7 +347,7 @@ static int route4_set_parms(struct tcf_proto *tp, unsigned long base, struct route4_bucket *b; struct tcf_exts e; - err = tcf_exts_validate(tp, tb, est, &e, &route_ext_map); + err = tcf_exts_validate(net, tp, tb, est, &e, &route_ext_map); if (err < 0) return err; @@ -427,7 +428,7 @@ errout: return err; } -static int route4_change(struct sk_buff *in_skb, +static int route4_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, @@ -457,7 +458,7 @@ static int route4_change(struct sk_buff *in_skb, if (f->bkt) old_handle = f->handle; - err = route4_set_parms(tp, base, f, handle, head, tb, + err = route4_set_parms(net, tp, base, f, handle, head, tb, tca[TCA_RATE], 0); if (err < 0) return err; @@ -480,7 +481,7 @@ static int route4_change(struct sk_buff *in_skb, if (f == NULL) goto errout; - err = route4_set_parms(tp, base, f, handle, head, tb, + err = route4_set_parms(net, tp, base, f, handle, head, tb, tca[TCA_RATE], 1); if (err < 0) goto errout; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 494bbb90924a..252d8b05872e 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -416,7 +416,7 @@ static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = { [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) }, }; -static int rsvp_change(struct sk_buff *in_skb, +static int rsvp_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, @@ -440,7 +440,7 @@ static int rsvp_change(struct sk_buff *in_skb, if (err < 0) return err; - err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &rsvp_ext_map); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &rsvp_ext_map); if (err < 0) return err; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index a1293b4ab7a1..b86535a40169 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -197,9 +197,10 @@ static const struct nla_policy tcindex_policy[TCA_TCINDEX_MAX + 1] = { }; static int -tcindex_set_parms(struct tcf_proto *tp, unsigned long base, u32 handle, - struct tcindex_data *p, struct tcindex_filter_result *r, - struct nlattr **tb, struct nlattr *est) +tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, + u32 handle, struct tcindex_data *p, + struct tcindex_filter_result *r, struct nlattr **tb, + struct nlattr *est) { int err, balloc = 0; struct tcindex_filter_result new_filter_result, *old_r = r; @@ -208,7 +209,7 @@ tcindex_set_parms(struct tcf_proto *tp, unsigned long base, u32 handle, struct tcindex_filter *f = NULL; /* make gcc behave */ struct tcf_exts e; - err = tcf_exts_validate(tp, tb, est, &e, &tcindex_ext_map); + err = tcf_exts_validate(net, tp, tb, est, &e, &tcindex_ext_map); if (err < 0) return err; @@ -332,7 +333,7 @@ errout: } static int -tcindex_change(struct sk_buff *in_skb, +tcindex_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) { @@ -353,7 +354,8 @@ tcindex_change(struct sk_buff *in_skb, if (err < 0) return err; - return tcindex_set_parms(tp, base, handle, p, r, tb, tca[TCA_RATE]); + return tcindex_set_parms(net, tp, base, handle, p, r, tb, + tca[TCA_RATE]); } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index c7c27bc91b5a..eb07a1e536e6 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -488,15 +488,15 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { [TCA_U32_MARK] = { .len = sizeof(struct tc_u32_mark) }, }; -static int u32_set_parms(struct tcf_proto *tp, unsigned long base, - struct tc_u_hnode *ht, +static int u32_set_parms(struct net *net, struct tcf_proto *tp, + unsigned long base, struct tc_u_hnode *ht, struct tc_u_knode *n, struct nlattr **tb, struct nlattr *est) { int err; struct tcf_exts e; - err = tcf_exts_validate(tp, tb, est, &e, &u32_ext_map); + err = tcf_exts_validate(net, tp, tb, est, &e, &u32_ext_map); if (err < 0) return err; @@ -544,7 +544,7 @@ errout: return err; } -static int u32_change(struct sk_buff *in_skb, +static int u32_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, unsigned long *arg) @@ -570,7 +570,8 @@ static int u32_change(struct sk_buff *in_skb, if (TC_U32_KEY(n->handle) == 0) return -EINVAL; - return u32_set_parms(tp, base, n->ht_up, n, tb, tca[TCA_RATE]); + return u32_set_parms(net, tp, base, n->ht_up, n, tb, + tca[TCA_RATE]); } if (tb[TCA_U32_DIVISOR]) { @@ -656,7 +657,7 @@ static int u32_change(struct sk_buff *in_skb, } #endif - err = u32_set_parms(tp, base, ht, n, tb, tca[TCA_RATE]); + err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE]); if (err == 0) { struct tc_u_knode **ins; for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) -- cgit v1.2.3 From f9a8f83b04e0c362a2fc660dbad980d24af209fc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 14 Jan 2013 00:52:52 +0000 Subject: net: phy: remove flags argument from phy_{attach, connect, connect_direct} The flags argument of the phy_{attach,connect,connect_direct} functions is then used to assign a struct phy_device dev_flags with its value. All callers but the tg3 driver pass the flag 0, which results in the underlying PHY drivers in drivers/net/phy/ not being able to actually use any of the flags they would set in dev_flags. This patch gets rid of the flags argument, and passes phydev->dev_flags to the internal PHY library call phy_attach_direct() such that drivers which actually modify a phy device dev_flags get the value preserved for use by the underlying phy driver. Acked-by: Kosta Zertsekel Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 11 ++++++----- drivers/net/ethernet/8390/ax88796.c | 2 +- drivers/net/ethernet/adi/bfin_mac.c | 4 ++-- drivers/net/ethernet/aeroflex/greth.c | 4 +--- drivers/net/ethernet/amd/au1000_eth.c | 4 ++-- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +- drivers/net/ethernet/broadcom/sb1250-mac.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 ++-- drivers/net/ethernet/cadence/macb.c | 2 +- drivers/net/ethernet/dnet.c | 4 ++-- drivers/net/ethernet/ethoc.c | 4 ++-- drivers/net/ethernet/faraday/ftgmac100.c | 3 +-- drivers/net/ethernet/freescale/fec.c | 2 +- drivers/net/ethernet/lantiq_etop.c | 4 ++-- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- drivers/net/ethernet/nxp/lpc_eth.c | 2 +- drivers/net/ethernet/rdc/r6040.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 2 +- drivers/net/ethernet/s6gmac.c | 2 +- drivers/net/ethernet/smsc/smsc911x.c | 5 ++--- drivers/net/ethernet/smsc/smsc9420.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +-- drivers/net/ethernet/ti/cpmac.c | 4 ++-- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/davinci_emac.c | 2 +- drivers/net/ethernet/toshiba/tc35815.c | 5 ++--- drivers/net/ethernet/xscale/ixp4xx_eth.c | 2 +- drivers/net/phy/phy_device.c | 15 ++++++--------- drivers/net/usb/ax88172a.c | 2 +- drivers/of/of_mdio.c | 4 ++-- drivers/staging/et131x/et131x.c | 2 +- include/linux/phy.h | 6 +++--- net/dsa/slave.c | 2 +- 34 files changed, 56 insertions(+), 64 deletions(-) (limited to 'net') diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 95e5f5985a2a..d5b1a3935245 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -103,7 +103,7 @@ Letting the PHY Abstraction Layer do Everything Now, to connect, just call this function: - phydev = phy_connect(dev, phy_name, &adjust_link, flags, interface); + phydev = phy_connect(dev, phy_name, &adjust_link, interface); phydev is a pointer to the phy_device structure which represents the PHY. If phy_connect is successful, it will return the pointer. dev, here, is the @@ -113,7 +113,9 @@ Letting the PHY Abstraction Layer do Everything current state, though the PHY will not yet be truly operational at this point. - flags is a u32 which can optionally contain phy-specific flags. + PHY-specific flags should be set in phydev->dev_flags prior to the call + to phy_connect() such that the underlying PHY driver can check for flags + and perform specific operations based on them. This is useful if the system has put hardware restrictions on the PHY/controller, of which the PHY needs to be aware. @@ -185,11 +187,10 @@ Doing it all yourself start, or disables then frees them for stop. struct phy_device * phy_attach(struct net_device *dev, const char *phy_id, - u32 flags, phy_interface_t interface); + phy_interface_t interface); Attaches a network device to a particular PHY, binding the PHY to a generic - driver if none was found during bus initialization. Passes in - any phy-specific flags as needed. + driver if none was found during bus initialization. int phy_start_aneg(struct phy_device *phydev); diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 7eeddf01307f..cab306a9888e 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -358,7 +358,7 @@ static int ax_mii_probe(struct net_device *dev) return -ENODEV; } - ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, 0, + ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, PHY_INTERFACE_MODE_MII); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index c7a83f6f2382..a175d0be1ae1 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -425,8 +425,8 @@ static int mii_probe(struct net_device *dev, int phy_mode) return -EINVAL; } - phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link, - 0, phy_mode); + phydev = phy_connect(dev, dev_name(&phydev->dev), + &bfin_mac_adjust_link, phy_mode); if (IS_ERR(phydev)) { netdev_err(dev, "could not attach PHY\n"); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 480662ba5227..0be2195e5034 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1288,9 +1288,7 @@ static int greth_mdio_probe(struct net_device *dev) } ret = phy_connect_direct(dev, phy, &greth_link_change, - 0, greth->gbit_mac ? - PHY_INTERFACE_MODE_GMII : - PHY_INTERFACE_MODE_MII); + greth->gbit_mac ? PHY_INTERFACE_MODE_GMII : PHY_INTERFACE_MODE_MII); if (ret) { if (netif_msg_ifup(greth)) dev_err(&dev->dev, "could not attach to PHY\n"); diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 65b865a0cc78..de774d419144 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -437,8 +437,8 @@ static int au1000_mii_probe(struct net_device *dev) /* now we are supposed to have a proper phydev, to attach to... */ BUG_ON(phydev->attached_dev); - phydev = phy_connect(dev, dev_name(&phydev->dev), &au1000_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + phydev = phy_connect(dev, dev_name(&phydev->dev), + &au1000_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index d8a151046728..f5b6b4715d45 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -799,7 +799,7 @@ static int bcm_enet_open(struct net_device *dev) snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, priv->mii_bus->id, priv->phy_id); - phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, 0, + phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 3a1c8a3cf7c9..e9b35da375cb 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2385,7 +2385,7 @@ static int sbmac_mii_probe(struct net_device *dev) return -ENXIO; } - phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll, 0, + phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phy_dev)) { printk(KERN_ERR "%s: could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 88f2d41c009b..227749107789 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2004,8 +2004,8 @@ static int tg3_phy_init(struct tg3 *tp) phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; /* Attach the MAC to the PHY. */ - phydev = phy_connect(tp->dev, dev_name(&phydev->dev), tg3_adjust_link, - phydev->dev_flags, phydev->interface); + phydev = phy_connect(tp->dev, dev_name(&phydev->dev), + tg3_adjust_link, phydev->interface); if (IS_ERR(phydev)) { dev_err(&tp->pdev->dev, "Could not attach to PHY\n"); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index a9b0830fb39d..352190b9ebe7 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -287,7 +287,7 @@ static int macb_mii_probe(struct net_device *dev) } /* attach the mac to the phy */ - ret = phy_connect_direct(dev, phydev, &macb_handle_link_change, 0, + ret = phy_connect_direct(dev, phydev, &macb_handle_link_change, bp->phy_interface); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 2c177b329c8b..f3d60eb13c3a 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -281,11 +281,11 @@ static int dnet_mii_probe(struct net_device *dev) /* attach the mac to the phy */ if (bp->capabilities & DNET_HAS_RMII) { phydev = phy_connect(dev, dev_name(&phydev->dev), - &dnet_handle_link_change, 0, + &dnet_handle_link_change, PHY_INTERFACE_MODE_RMII); } else { phydev = phy_connect(dev, dev_name(&phydev->dev), - &dnet_handle_link_change, 0, + &dnet_handle_link_change, PHY_INTERFACE_MODE_MII); } diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index b51c81ac0b6f..aa47ef9689a8 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -682,8 +682,8 @@ static int ethoc_mdio_probe(struct net_device *dev) return -ENXIO; } - err = phy_connect_direct(dev, phy, ethoc_mdio_poll, 0, - PHY_INTERFACE_MODE_GMII); + err = phy_connect_direct(dev, phy, ethoc_mdio_poll, + PHY_INTERFACE_MODE_GMII); if (err) { dev_err(&dev->dev, "could not attach to PHY\n"); return err; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 96454b5fca63..7c361d1db94c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -858,8 +858,7 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) } phydev = phy_connect(netdev, dev_name(&phydev->dev), - &ftgmac100_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); + &ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phydev)) { netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name); diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 5f2b4acf4836..1b7684a8851e 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1008,7 +1008,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) } snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id); - phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, + phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, fep->phy_interface); if (IS_ERR(phy_dev)) { printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 8ead46adc21e..6a2127489af7 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -393,8 +393,8 @@ ltq_etop_mdio_probe(struct net_device *dev) return -ENODEV; } - phydev = phy_connect(dev, dev_name(&phydev->dev), <q_etop_mdio_link, - 0, priv->pldata->mii_mode); + phydev = phy_connect(dev, dev_name(&phydev->dev), + <q_etop_mdio_link, priv->pldata->mii_mode); if (IS_ERR(phydev)) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 84c13263c514..c27b23d8f4fc 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2789,7 +2789,7 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) phy_reset(mp); - phy_attach(mp->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_GMII); + phy_attach(mp->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_GMII); if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index c7f2fa60fe6f..037ed866c22f 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1390,7 +1390,7 @@ static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex) struct phy_device *phy = pep->phy; ethernet_phy_reset(pep); - phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII); + phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII); if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 6fda51ebcc76..c4122c86f829 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -800,7 +800,7 @@ static int lpc_mii_probe(struct net_device *ndev) else netdev_info(ndev, "using RMII interface\n"); phydev = phy_connect(ndev, dev_name(&phydev->dev), - &lpc_handle_link_change, 0, + &lpc_handle_link_change, lpc_phy_interface_mode(&pldat->pdev->dev)); if (IS_ERR(phydev)) { diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index be3616d060d9..34f76e99dc8a 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1042,7 +1042,7 @@ static int r6040_mii_probe(struct net_device *dev) } phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { dev_err(&lp->pdev->dev, "could not attach to PHY\n"); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 3d705862bd7d..e195c1e89d61 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1422,7 +1422,7 @@ static int sh_eth_phy_init(struct net_device *ndev) /* Try connect to PHY */ phydev = phy_connect(ndev, phy_id, sh_eth_adjust_link, - 0, mdp->phy_interface); + mdp->phy_interface); if (IS_ERR(phydev)) { dev_err(&ndev->dev, "phy_connect failed\n"); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/s6gmac.c b/drivers/net/ethernet/s6gmac.c index 72fc57dd084d..21683e2b1ff4 100644 --- a/drivers/net/ethernet/s6gmac.c +++ b/drivers/net/ethernet/s6gmac.c @@ -795,7 +795,7 @@ static inline int s6gmac_phy_start(struct net_device *dev) struct phy_device *p = NULL; while ((i < PHY_MAX_ADDR) && (!(p = pd->mii.bus->phy_map[i]))) i++; - p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, 0, + p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, PHY_INTERFACE_MODE_RGMII); if (IS_ERR(p)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 04ff63cb6544..da5cc9a3b34c 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -997,9 +997,8 @@ static int smsc911x_mii_probe(struct net_device *dev) SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X", phydev->addr, phydev->phy_id); - ret = phy_connect_direct(dev, phydev, - &smsc911x_phy_adjust_link, 0, - pdata->config.phy_interface); + ret = phy_connect_direct(dev, phydev, &smsc911x_phy_adjust_link, + pdata->config.phy_interface); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 3c586585e1b3..ecfb43614d7b 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1179,7 +1179,7 @@ static int smsc9420_mii_probe(struct net_device *dev) phydev->phy_id); phydev = phy_connect(dev, dev_name(&phydev->dev), - smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII); + smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f07c0612abf6..8c657294ce56 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -428,8 +428,7 @@ static int stmmac_init_phy(struct net_device *dev) priv->plat->phy_addr); pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); - phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, 0, - interface); + phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 70d1920cac97..31bbbca341a7 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -1172,8 +1172,8 @@ static int cpmac_probe(struct platform_device *pdev) snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); - priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, 0, - PHY_INTERFACE_MODE_MII); + priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { if (netif_msg_drv(priv)) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index bea736b8c3ec..3772804fb697 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -592,7 +592,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) 1 << slave_port, 0, ALE_MCAST_FWD_2); slave->phy = phy_connect(priv->ndev, slave->data->phy_id, - &cpsw_adjust_link, 0, slave->data->phy_if); + &cpsw_adjust_link, slave->data->phy_if); if (IS_ERR(slave->phy)) { dev_err(priv->dev, "phy %s not found on slave %d\n", slave->data->phy_id, slave->slave_num); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 6621ae3a98d9..8478d98c1092 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1599,7 +1599,7 @@ static int emac_dev_open(struct net_device *ndev) if (priv->phy_id && *priv->phy_id) { priv->phydev = phy_connect(ndev, priv->phy_id, - &emac_adjust_link, 0, + &emac_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index f16410e599f4..fe256094db35 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -633,9 +633,8 @@ static int tc_mii_probe(struct net_device *dev) /* attach the mac to the phy */ phydev = phy_connect(dev, dev_name(&phydev->dev), - &tc_handle_link_change, 0, - lp->chiptype == TC35815_TX4939 ? - PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); + &tc_handle_link_change, + lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index a4be1ad886c5..6958a5e87703 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1451,7 +1451,7 @@ static int eth_init_one(struct platform_device *pdev) snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, mdio_bus->id, plat->phy); - port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0, + port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(port->phydev)) { err = PTR_ERR(port->phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8af46e88a181..9930f9999561 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -416,16 +416,15 @@ static void phy_prepare_link(struct phy_device *phydev, * @dev: the network device to connect * @phydev: the pointer to the phy device * @handler: callback function for state change notifications - * @flags: PHY device's dev_flags * @interface: PHY device's interface */ int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface) { int rc; - rc = phy_attach_direct(dev, phydev, flags, interface); + rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); if (rc) return rc; @@ -443,7 +442,6 @@ EXPORT_SYMBOL(phy_connect_direct); * @dev: the network device to connect * @bus_id: the id string of the PHY device to connect * @handler: callback function for state change notifications - * @flags: PHY device's dev_flags * @interface: PHY device's interface * * Description: Convenience function for connecting ethernet @@ -455,7 +453,7 @@ EXPORT_SYMBOL(phy_connect_direct); * the desired functionality. */ struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface) { struct phy_device *phydev; @@ -471,7 +469,7 @@ struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, } phydev = to_phy_device(d); - rc = phy_connect_direct(dev, phydev, handler, flags, interface); + rc = phy_connect_direct(dev, phydev, handler, interface); if (rc) return ERR_PTR(rc); @@ -576,14 +574,13 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, * phy_attach - attach a network device to a particular PHY device * @dev: network device to attach * @bus_id: Bus ID of PHY device to attach - * @flags: PHY device's dev_flags * @interface: PHY device's interface * * Description: Same as phy_attach_direct() except that a PHY bus_id * string is passed instead of a pointer to a struct phy_device. */ struct phy_device *phy_attach(struct net_device *dev, - const char *bus_id, u32 flags, phy_interface_t interface) + const char *bus_id, phy_interface_t interface) { struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; @@ -599,7 +596,7 @@ struct phy_device *phy_attach(struct net_device *dev, } phydev = to_phy_device(d); - rc = phy_attach_direct(dev, phydev, flags, interface); + rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); if (rc) return ERR_PTR(rc); diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index c8e0aa85fb8e..fdbab72926bd 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -377,7 +377,7 @@ static int ax88172a_reset(struct usbnet *dev) priv->phydev = phy_connect(dev->net, priv->phy_name, &ax88172a_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { netdev_err(dev->net, "Could not connect to PHY device %s\n", priv->phy_name); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 83ca06f4312b..e3a8b22ef9dd 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -157,7 +157,7 @@ struct phy_device *of_phy_connect(struct net_device *dev, if (!phy) return NULL; - return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy; + return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); @@ -194,7 +194,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); - phy = phy_connect(dev, bus_id, hndlr, 0, iface); + phy = phy_connect(dev, bus_id, hndlr, iface); return IS_ERR(phy) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect_fixed_link); diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c index f15059ca3781..a0a30b3f2dcd 100644 --- a/drivers/staging/et131x/et131x.c +++ b/drivers/staging/et131x/et131x.c @@ -3917,7 +3917,7 @@ static int et131x_mii_probe(struct net_device *netdev) } phydev = phy_connect(netdev, dev_name(&phydev->dev), - &et131x_adjust_link, 0, PHY_INTERFACE_MODE_MII); + &et131x_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { dev_err(&adapter->pdev->dev, "Could not attach to PHY\n"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 93b3cf77f564..33999adbf8c8 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -506,13 +506,13 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); int phy_device_register(struct phy_device *phy); int phy_init_hw(struct phy_device *phydev); struct phy_device * phy_attach(struct net_device *dev, - const char *bus_id, u32 flags, phy_interface_t interface); + const char *bus_id, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus); int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface); struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface); void phy_disconnect(struct phy_device *phydev); void phy_detach(struct phy_device *phydev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f795b0ca7ee6..f4345582a6b9 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -391,7 +391,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, if (p->phy != NULL) { phy_attach(slave_dev, dev_name(&p->phy->dev), - 0, PHY_INTERFACE_MODE_GMII); + PHY_INTERFACE_MODE_GMII); p->phy->autoneg = AUTONEG_ENABLE; p->phy->speed = 0; -- cgit v1.2.3 From 757b8b1d2bfa1210592bfd2984852b155eb58746 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 15 Jan 2013 21:14:21 -0800 Subject: net_sched: fix qdisc_pkt_len_init() commit 1def9238d4aa2 (net_sched: more precise pkt_len computation) does a wrong computation of mac + network headers length, as it includes the padding before the frame. Signed-off-by: Eric Dumazet Cc: Yuval Mintz Signed-off-by: David S. Miller --- net/core/dev.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 862eaa744a54..b6d2b32933ba 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2776,8 +2776,12 @@ static void qdisc_pkt_len_init(struct sk_buff *skb) * we add to pkt_len the headers size of all segments */ if (shinfo->gso_size) { - unsigned int hdr_len = skb_transport_offset(skb); + unsigned int hdr_len; + /* mac layer + network layer */ + hdr_len = skb_transport_header(skb) - skb_mac_header(skb); + + /* + transport layer */ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) hdr_len += tcp_hdrlen(skb); else -- cgit v1.2.3 From 44abdc3047aecafc141dfbaf1ed4bcb77f5d1652 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 16 Jan 2013 16:05:05 +0800 Subject: xfrm: replace rwlock on xfrm_state_afinfo with rcu Similar to commit 418a99ac6ad487dc9c42e6b0e85f941af56330f2 (Replace rwlock on xfrm_policy_afinfo with rcu), the rwlock on xfrm_state_afinfo can be replaced by RCU too. Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 05db2362a231..64780a6f7b86 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -158,8 +158,8 @@ out_unlock: mutex_unlock(&hash_resize_mutex); } -static DEFINE_RWLOCK(xfrm_state_afinfo_lock); -static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; +static DEFINE_SPINLOCK(xfrm_state_afinfo_lock); +static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO]; static DEFINE_SPINLOCK(xfrm_state_gc_lock); @@ -173,17 +173,16 @@ static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family) struct xfrm_state_afinfo *afinfo; if (unlikely(family >= NPROTO)) return NULL; - write_lock_bh(&xfrm_state_afinfo_lock); + spin_lock_bh(&xfrm_state_afinfo_lock); afinfo = xfrm_state_afinfo[family]; if (unlikely(!afinfo)) - write_unlock_bh(&xfrm_state_afinfo_lock); + spin_unlock_bh(&xfrm_state_afinfo_lock); return afinfo; } static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo) - __releases(xfrm_state_afinfo_lock) { - write_unlock_bh(&xfrm_state_afinfo_lock); + spin_unlock_bh(&xfrm_state_afinfo_lock); } int xfrm_register_type(const struct xfrm_type *type, unsigned short family) @@ -1845,12 +1844,12 @@ int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo) return -EINVAL; if (unlikely(afinfo->family >= NPROTO)) return -EAFNOSUPPORT; - write_lock_bh(&xfrm_state_afinfo_lock); + spin_lock_bh(&xfrm_state_afinfo_lock); if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL)) err = -ENOBUFS; else - xfrm_state_afinfo[afinfo->family] = afinfo; - write_unlock_bh(&xfrm_state_afinfo_lock); + rcu_assign_pointer(xfrm_state_afinfo[afinfo->family], afinfo); + spin_unlock_bh(&xfrm_state_afinfo_lock); return err; } EXPORT_SYMBOL(xfrm_state_register_afinfo); @@ -1862,14 +1861,15 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo) return -EINVAL; if (unlikely(afinfo->family >= NPROTO)) return -EAFNOSUPPORT; - write_lock_bh(&xfrm_state_afinfo_lock); + spin_lock_bh(&xfrm_state_afinfo_lock); if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) { if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo)) err = -EINVAL; else - xfrm_state_afinfo[afinfo->family] = NULL; + RCU_INIT_POINTER(xfrm_state_afinfo[afinfo->family], NULL); } - write_unlock_bh(&xfrm_state_afinfo_lock); + spin_unlock_bh(&xfrm_state_afinfo_lock); + synchronize_rcu(); return err; } EXPORT_SYMBOL(xfrm_state_unregister_afinfo); @@ -1879,17 +1879,16 @@ static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family) struct xfrm_state_afinfo *afinfo; if (unlikely(family >= NPROTO)) return NULL; - read_lock(&xfrm_state_afinfo_lock); - afinfo = xfrm_state_afinfo[family]; + rcu_read_lock(); + afinfo = rcu_dereference(xfrm_state_afinfo[family]); if (unlikely(!afinfo)) - read_unlock(&xfrm_state_afinfo_lock); + rcu_read_unlock(); return afinfo; } static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) - __releases(xfrm_state_afinfo_lock) { - read_unlock(&xfrm_state_afinfo_lock); + rcu_read_unlock(); } /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ -- cgit v1.2.3 From 85168c0036fadf3657470b93cd9babeee8d123d7 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 16 Jan 2013 16:05:06 +0800 Subject: xfrm: replace rwlock on xfrm_km_list with rcu Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 58 ++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 64780a6f7b86..1522f19f273a 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1644,27 +1644,26 @@ static void xfrm_replay_timer_handler(unsigned long data) } static LIST_HEAD(xfrm_km_list); -static DEFINE_RWLOCK(xfrm_km_lock); void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) { struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) if (km->notify_policy) km->notify_policy(xp, dir, c); - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); } void km_state_notify(struct xfrm_state *x, const struct km_event *c) { struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) if (km->notify) km->notify(x, c); - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); } EXPORT_SYMBOL(km_policy_notify); @@ -1694,13 +1693,13 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol) int err = -EINVAL, acqret; struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { acqret = km->acquire(x, t, pol); if (!acqret) err = acqret; } - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); return err; } EXPORT_SYMBOL(km_query); @@ -1710,14 +1709,14 @@ int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport) int err = -EINVAL; struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { if (km->new_mapping) err = km->new_mapping(x, ipaddr, sport); if (!err) break; } - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); return err; } EXPORT_SYMBOL(km_new_mapping); @@ -1746,15 +1745,15 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, int ret; struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { if (km->migrate) { ret = km->migrate(sel, dir, type, m, num_migrate, k); if (!ret) err = ret; } } - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); return err; } EXPORT_SYMBOL(km_migrate); @@ -1766,15 +1765,15 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address int ret; struct xfrm_mgr *km; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { if (km->report) { ret = km->report(net, proto, sel, addr); if (!ret) err = ret; } } - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); return err; } EXPORT_SYMBOL(km_report); @@ -1798,14 +1797,14 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen goto out; err = -EINVAL; - read_lock(&xfrm_km_lock); - list_for_each_entry(km, &xfrm_km_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(km, &xfrm_km_list, list) { pol = km->compile_policy(sk, optname, data, optlen, &err); if (err >= 0) break; } - read_unlock(&xfrm_km_lock); + rcu_read_unlock(); if (err >= 0) { xfrm_sk_policy_insert(sk, err, pol); @@ -1819,20 +1818,23 @@ out: } EXPORT_SYMBOL(xfrm_user_policy); +static DEFINE_SPINLOCK(xfrm_km_lock); + int xfrm_register_km(struct xfrm_mgr *km) { - write_lock_bh(&xfrm_km_lock); - list_add_tail(&km->list, &xfrm_km_list); - write_unlock_bh(&xfrm_km_lock); + spin_lock_bh(&xfrm_km_lock); + list_add_tail_rcu(&km->list, &xfrm_km_list); + spin_unlock_bh(&xfrm_km_lock); return 0; } EXPORT_SYMBOL(xfrm_register_km); int xfrm_unregister_km(struct xfrm_mgr *km) { - write_lock_bh(&xfrm_km_lock); - list_del(&km->list); - write_unlock_bh(&xfrm_km_lock); + spin_lock_bh(&xfrm_km_lock); + list_del_rcu(&km->list); + spin_unlock_bh(&xfrm_km_lock); + synchronize_rcu(); return 0; } EXPORT_SYMBOL(xfrm_unregister_km); -- cgit v1.2.3 From d888130a024d1d1e8b2c3d0c9841a218fc0aed9e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 10 Jan 2013 23:55:33 +0100 Subject: mac80211: fix resume from WoWLAN My commit 529ba6e9313dbe60dab7e72c6fdf647a012e9f5b ("mac80211: clean up association better in suspend") introduced a bug when resuming from WoWLAN when a device reset is desired. This case must not use the suspend_bss_conf as it hasn't been stored. Signed-off-by: Johannes Berg --- net/mac80211/util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1b9420730d8c..0ad51e14f3c8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1358,8 +1358,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; struct sta_info *sta; int res, i; - #ifdef CONFIG_PM + bool reconfig_due_to_wowlan = false; + if (local->suspended) local->resuming = true; @@ -1377,6 +1378,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) * res is 1, which means the driver requested * to go through a regular reset on wakeup. */ + reconfig_due_to_wowlan = true; } #endif /* everything else happens only if HW was up & running */ @@ -1527,7 +1529,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_TXPOWER; #ifdef CONFIG_PM - if (local->resuming) + if (local->resuming && !reconfig_due_to_wowlan) sdata->vif.bss_conf = sdata->suspend_bss_conf; #endif -- cgit v1.2.3 From 8f21b0adfe95907926da1bb0bcd3382b13d0143d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Jan 2013 00:28:01 +0100 Subject: mac80211: call restart complete at wowlan resume time When the driver's resume function can't completely restore the configuration in the device, it returns 1 from the callback which will be treated like a HW restart request, but done directly. In this case, also call the driver's restart_complete() function so it can finish the reconfiguration there. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 ++++- net/mac80211/util.c | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 79bc8709e83b..3037f49e51c8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2488,7 +2488,10 @@ enum ieee80211_rate_control_changed { * * @restart_complete: Called after a call to ieee80211_restart_hw(), when the * reconfiguration has completed. This can help the driver implement the - * reconfiguration step. This callback may sleep. + * reconfiguration step. Also called when reconfiguring because the + * driver's resume function returned 1, as this is just like an "inline" + * hardware restart. This callback may sleep. + * */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0ad51e14f3c8..7519018ff71a 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1358,9 +1358,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; struct sta_info *sta; int res, i; -#ifdef CONFIG_PM bool reconfig_due_to_wowlan = false; +#ifdef CONFIG_PM if (local->suspended) local->resuming = true; @@ -1656,10 +1656,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) * If this is for hw restart things are still running. * We may want to change that later, however. */ - if (!local->suspended) { + if (!local->suspended || reconfig_due_to_wowlan) drv_restart_complete(local); + + if (!local->suspended) return 0; - } #ifdef CONFIG_PM /* first set suspended false, then resuming */ -- cgit v1.2.3 From ee1f668136b2fb6640ee2d54c2a525ea41f98211 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 10 Jan 2013 23:31:54 +0800 Subject: mac80211: allow disable power save in mesh Allow mesh interface to disable the power save which is by default turn on in certain chipset. Testing with 2 units of ZCN-1523H-5-16 featuring AR9280 chipset which have power save enabled by default. Constant reset if the average signal of the peer mesh STA is below -80 dBm and power save is enabled. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7d290bce0927..817992b51fe7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2232,7 +2232,8 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - if (sdata->vif.type != NL80211_IFTYPE_STATION) + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) -- cgit v1.2.3 From f92d318023ff70a273690a91568597168ac32f05 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 14 Jan 2013 23:34:06 +0000 Subject: netpoll: fix a rtnl lock assertion failure v4: hold rtnl lock for the whole netpoll_setup() v3: remove the comment v2: use RCU read lock This patch fixes the following warning: [ 72.013864] RTNL: assertion failed at net/core/dev.c (4955) [ 72.017758] Pid: 668, comm: netpoll-prep-v6 Not tainted 3.8.0-rc1+ #474 [ 72.019582] Call Trace: [ 72.020295] [] netdev_master_upper_dev_get+0x35/0x58 [ 72.022545] [] netpoll_setup+0x61/0x340 [ 72.024846] [] store_enabled+0x82/0xc3 [ 72.027466] [] netconsole_target_attr_store+0x35/0x37 [ 72.029348] [] configfs_write_file+0xe2/0x10c [ 72.030959] [] vfs_write+0xaf/0xf6 [ 72.032359] [] ? sysret_check+0x22/0x5d [ 72.033824] [] sys_write+0x5c/0x84 [ 72.035328] [] system_call_fastpath+0x16/0x1b In case of other races, hold rtnl lock for the entire netpoll_setup() function. Cc: Eric Dumazet Cc: Jiri Pirko Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/netpoll.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 9f0506726101..a5ad1c1c4b18 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -1048,11 +1048,13 @@ int netpoll_setup(struct netpoll *np) struct in_device *in_dev; int err; + rtnl_lock(); if (np->dev_name) - ndev = dev_get_by_name(&init_net, np->dev_name); + ndev = __dev_get_by_name(&init_net, np->dev_name); if (!ndev) { np_err(np, "%s doesn't exist, aborting\n", np->dev_name); - return -ENODEV; + err = -ENODEV; + goto unlock; } if (netdev_master_upper_dev_get(ndev)) { @@ -1066,15 +1068,14 @@ int netpoll_setup(struct netpoll *np) np_info(np, "device %s not up yet, forcing it\n", np->dev_name); - rtnl_lock(); err = dev_open(ndev); - rtnl_unlock(); if (err) { np_err(np, "failed to open %s\n", ndev->name); goto put; } + rtnl_unlock(); atleast = jiffies + HZ/10; atmost = jiffies + carrier_timeout * HZ; while (!netif_carrier_ok(ndev)) { @@ -1094,16 +1095,14 @@ int netpoll_setup(struct netpoll *np) np_notice(np, "carrier detect appears untrustworthy, waiting 4 seconds\n"); msleep(4000); } + rtnl_lock(); } if (!np->local_ip.ip) { if (!np->ipv6) { - rcu_read_lock(); - in_dev = __in_dev_get_rcu(ndev); - + in_dev = __in_dev_get_rtnl(ndev); if (!in_dev || !in_dev->ifa_list) { - rcu_read_unlock(); np_err(np, "no IP address for %s, aborting\n", np->dev_name); err = -EDESTADDRREQ; @@ -1111,14 +1110,12 @@ int netpoll_setup(struct netpoll *np) } np->local_ip.ip = in_dev->ifa_list->ifa_local; - rcu_read_unlock(); np_info(np, "local IP %pI4\n", &np->local_ip.ip); } else { #if IS_ENABLED(CONFIG_IPV6) struct inet6_dev *idev; err = -EDESTADDRREQ; - rcu_read_lock(); idev = __in6_dev_get(ndev); if (idev) { struct inet6_ifaddr *ifp; @@ -1133,7 +1130,6 @@ int netpoll_setup(struct netpoll *np) } read_unlock_bh(&idev->lock); } - rcu_read_unlock(); if (err) { np_err(np, "no IPv6 address for %s, aborting\n", np->dev_name); @@ -1151,17 +1147,17 @@ int netpoll_setup(struct netpoll *np) /* fill up the skb queue */ refill_skbs(); - rtnl_lock(); err = __netpoll_setup(np, ndev, GFP_KERNEL); - rtnl_unlock(); - if (err) goto put; + rtnl_unlock(); return 0; put: dev_put(ndev); +unlock: + rtnl_unlock(); return err; } EXPORT_SYMBOL(netpoll_setup); -- cgit v1.2.3 From ea54fba20985b7a0cb8e1c8e2760a0865ee47763 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:48 +0100 Subject: nl80211: add range checks to mesh parameters Ranges are taken from IEEE 802.11-2012, common sense or current implementation requirements. Signed-off-by: Marco Porsch Acked-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 79 ++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9bd8340af999..ceb27fda9ec5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3981,13 +3981,15 @@ static int nl80211_parse_mesh_config(struct genl_info *info, struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; u32 mask = 0; -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ - if (table[attr_num]) {\ - cfg->param = nla_fn(table[attr_num]); \ - mask |= (1 << (attr_num - 1)); \ - } \ -} while (0);\ +#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \ +do { \ + if (tb[attr]) { \ + if (fn(tb[attr]) < min || fn(tb[attr]) > max) \ + return -EINVAL; \ + cfg->param = fn(tb[attr]); \ + mask |= (1 << (attr - 1)); \ + } \ +} while (0) if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) @@ -4002,81 +4004,88 @@ do {\ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); /* Fill in the params struct */ - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255, mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255, mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255, mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255, mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16, mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255, mask, NL80211_MESHCONF_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255, mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, + 1, 255, mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255, mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535, mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535, mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, + 1, 65535, mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, - mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + 1, 65535, mask, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, - mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + 1, 65535, mask, + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPnetDiameterTraversalTime, mask, + dot11MeshHWMPnetDiameterTraversalTime, + 1, 65535, mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, - NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, - NL80211_MESHCONF_HWMP_RANN_INTERVAL, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4, + mask, NL80211_MESHCONF_HWMP_ROOTMODE, + nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535, + mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshGateAnnouncementProtocol, mask, - NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + dot11MeshGateAnnouncementProtocol, 0, 1, + mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1, mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255, mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16, mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, - mask, + 1, 65535, mask, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535, mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPconfirmationInterval, mask, + dot11MeshHWMPconfirmationInterval, + 1, 65535, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, nla_get_u16); if (mask_out) -- cgit v1.2.3 From 1617bab8db81427392d424f2c7ffb5a3282c417c Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:49 +0100 Subject: mac80211: update mesh peer link counter during userspace peering The established peer link count is indicated in mesh beacons and used for other internal tasks. Previously it was not updated when authenticated peering is performed in userspace. Signed-off-by: Marco Porsch Acked-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 22 +++++++++++++++++++--- net/mac80211/mesh.h | 14 ++++++++++++++ net/mac80211/mesh_plink.c | 14 -------------- 3 files changed, 33 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 817992b51fe7..8a91dd22d571 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1243,18 +1243,33 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) { + u32 changed = 0; + switch (params->plink_state) { - case NL80211_PLINK_LISTEN: case NL80211_PLINK_ESTAB: + if (sta->plink_state != NL80211_PLINK_ESTAB) + changed = mesh_plink_inc_estab_count( + sdata); + sta->plink_state = params->plink_state; + break; + case NL80211_PLINK_LISTEN: case NL80211_PLINK_BLOCKED: + case NL80211_PLINK_OPN_SNT: + case NL80211_PLINK_OPN_RCVD: + case NL80211_PLINK_CNF_RCVD: + case NL80211_PLINK_HOLDING: + if (sta->plink_state == NL80211_PLINK_ESTAB) + changed = mesh_plink_dec_estab_count( + sdata); sta->plink_state = params->plink_state; break; default: /* nothing */ break; } - else + ieee80211_bss_info_change_notify(sdata, changed); + } else { switch (params->plink_action) { case PLINK_ACTION_OPEN: mesh_plink_open(sta); @@ -1263,6 +1278,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, mesh_plink_block(sta); break; } + } #endif } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 84c28c6101cd..c07f6fc63dc6 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -307,6 +307,20 @@ extern int mesh_paths_generation; #ifdef CONFIG_MAC80211_MESH extern int mesh_allocated; +static inline +u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) +{ + atomic_inc(&sdata->u.mesh.estab_plinks); + return mesh_accept_plinks_update(sdata); +} + +static inline +u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) +{ + atomic_dec(&sdata->u.mesh.estab_plinks); + return mesh_accept_plinks_update(sdata); +} + static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata) { return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks - diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4b274e9c91a5..9e0416696a83 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -41,20 +41,6 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason); -static inline -u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) -{ - atomic_inc(&sdata->u.mesh.estab_plinks); - return mesh_accept_plinks_update(sdata); -} - -static inline -u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) -{ - atomic_dec(&sdata->u.mesh.estab_plinks); - return mesh_accept_plinks_update(sdata); -} - /** * mesh_plink_fsm_restart - restart a mesh peer link finite state machine * -- cgit v1.2.3 From eac70c135def117849faa7cc8b7ccb941498085f Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:50 +0100 Subject: mac80211: move add_tim to subfunction This functions will be used for mesh beacons, too. Signed-off-by: Marco Porsch [some formatting fixes] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e9eadc40c09c..0d7381679a5c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2261,9 +2261,8 @@ void ieee80211_tx_pending(unsigned long data) /* functions for drivers to get certain frames */ -static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, - struct ps_data *ps, - struct sk_buff *skb) +static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ps_data *ps, struct sk_buff *skb) { u8 *pos, *tim; int aid0 = 0; @@ -2325,6 +2324,31 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, } } +static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, + struct ps_data *ps, struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + + /* + * Not very nice, but we want to allow the driver to call + * ieee80211_beacon_get() as a response to the set_tim() + * callback. That, however, is already invoked under the + * sta_lock to guarantee consistent and race-free update + * of the tim bitmap in mac80211 and the driver. + */ + if (local->tim_in_locked_section) { + __ieee80211_beacon_add_tim(sdata, ps, skb); + } else { + unsigned long flags; + + spin_lock_irqsave(&local->tim_lock, flags); + __ieee80211_beacon_add_tim(sdata, ps, skb); + spin_unlock_irqrestore(&local->tim_lock, flags); + } + + return 0; +} + struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length) @@ -2369,22 +2393,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, memcpy(skb_put(skb, beacon->head_len), beacon->head, beacon->head_len); - /* - * Not very nice, but we want to allow the driver to call - * ieee80211_beacon_get() as a response to the set_tim() - * callback. That, however, is already invoked under the - * sta_lock to guarantee consistent and race-free update - * of the tim bitmap in mac80211 and the driver. - */ - if (local->tim_in_locked_section) { - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); - } else { - unsigned long flags; - - spin_lock_irqsave(&local->tim_lock, flags); - ieee80211_beacon_add_tim(sdata, &ap->ps, skb); - spin_unlock_irqrestore(&local->tim_lock, flags); - } + ieee80211_beacon_add_tim(sdata, &ap->ps, skb); if (tim_offset) *tim_offset = beacon->head_len; -- cgit v1.2.3 From 9bdbf04db099c11bbbaea9dcea7465c508531fb8 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:51 +0100 Subject: {cfg,nl,mac}80211: set beacon interval and DTIM period on mesh join Move the default mesh beacon interval and DTIM period to cfg80211 and make them accessible to nl80211. This enables setting both values when joining an MBSS. Previously the DTIM parameter was not set by mac80211 so the driver's default value was used. Signed-off-by: Marco Porsch Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 3 +++ net/mac80211/mesh.c | 1 - net/mac80211/mesh.h | 2 -- net/wireless/mesh.c | 5 +++++ net/wireless/nl80211.c | 15 +++++++++++++++ 6 files changed, 27 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d8717a0d3b2..516aded3697f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1036,6 +1036,8 @@ struct mesh_config { * @ie_len: length of vendor information elements * @is_authenticated: this mesh requires authentication * @is_secure: this mesh uses security + * @dtim_period: DTIM period to use + * @beacon_interval: beacon interval to use * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a] * * These parameters are fixed when the mesh is created. @@ -1051,6 +1053,8 @@ struct mesh_setup { u8 ie_len; bool is_authenticated; bool is_secure; + u8 dtim_period; + u16 beacon_interval; int mcast_rate[IEEE80211_NUM_BANDS]; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8a91dd22d571..36aa65f495ff 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1666,6 +1666,9 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, sizeof(setup->mcast_rate)); + sdata->vif.bss_conf.beacon_int = setup->beacon_interval; + sdata->vif.bss_conf.dtim_period = setup->dtim_period; + return 0; } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 245885841c8d..694e27376afa 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -625,7 +625,6 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_queue_work(&local->hw, &sdata->work); sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; - sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL; sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c07f6fc63dc6..aff301544c7f 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -191,8 +191,6 @@ struct mesh_rmc { #define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) -#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */ - #define MESH_PATH_EXPIRE (600 * HZ) /* Default maximum number of plinks per interface */ diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index f9d6ce5cfabb..0fe8ceb5444e 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -44,6 +44,9 @@ #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 +#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */ +#define MESH_DEFAULT_DTIM_PERIOD 2 + const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, .dot11MeshConfirmTimeout = MESH_CONF_T, @@ -79,6 +82,8 @@ const struct mesh_setup default_mesh_setup = { .ie = NULL, .ie_len = 0, .is_secure = false, + .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL, + .dtim_period = MESH_DEFAULT_DTIM_PERIOD, }; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ceb27fda9ec5..d5842eb35aec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6669,6 +6669,21 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) return -EINVAL; + if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { + setup.beacon_interval = + nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); + if (setup.beacon_interval < 10 || + setup.beacon_interval > 10000) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { + setup.dtim_period = + nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); + if (setup.dtim_period < 1 || setup.dtim_period > 100) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_MESH_SETUP]) { /* parse additional setup parameters if given */ err = nl80211_parse_mesh_setup(info, &setup); -- cgit v1.2.3 From 3b1c5a5307fb5277f395efdcf330c064d79df07d Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Mon, 7 Jan 2013 16:04:52 +0100 Subject: {cfg,nl}80211: mesh power mode primitives and userspace access Add the nl80211_mesh_power_mode enumeration which holds possible values for the mesh power mode. These modes are unknown, active, light sleep and deep sleep. Add power_mode entry to the mesh config structure to hold the user-configured default mesh power mode. This value will be used for new peer links. Add the dot11MeshAwakeWindowDuration value to the mesh config. The awake window is a duration in TU describing how long the STA will stay awake after transmitting its beacon in PS mode. Add access routines to: - get/set local link-specific power mode (STA) - get remote STA's link-specific power mode (STA) - get remote STA's non-peer power mode (STA) - get/set default mesh power mode (mesh config) - get/set mesh awake window duration (mesh config) All config changes may be done at mesh runtime and take effect immediately. Signed-off-by: Marco Porsch Signed-off-by: Ivan Bezyazychnyy Signed-off-by: Mike Krinkin [fix commit message line length, error handling in set station] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 21 ++++++++++++++++++++ include/uapi/linux/nl80211.h | 47 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/mesh.c | 3 +++ net/wireless/nl80211.c | 43 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 516aded3697f..d9f08f65f7a5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -610,6 +610,8 @@ enum station_parameters_apply_mask { * @sta_modify_mask: bitmap indicating which parameters changed * (for those that don't have a natural "no change" value), * see &enum station_parameters_apply_mask + * @local_pm: local link-specific mesh power save mode (no change when set + * to unknown) */ struct station_parameters { u8 *supported_rates; @@ -625,6 +627,7 @@ struct station_parameters { struct ieee80211_vht_cap *vht_capa; u8 uapsd_queues; u8 max_sp; + enum nl80211_mesh_power_mode local_pm; }; /** @@ -655,6 +658,9 @@ struct station_parameters { * @STATION_INFO_STA_FLAGS: @sta_flags filled * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled * @STATION_INFO_T_OFFSET: @t_offset filled + * @STATION_INFO_LOCAL_PM: @local_pm filled + * @STATION_INFO_PEER_PM: @peer_pm filled + * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -678,6 +684,9 @@ enum station_info_flags { STATION_INFO_STA_FLAGS = 1<<18, STATION_INFO_BEACON_LOSS_COUNT = 1<<19, STATION_INFO_T_OFFSET = 1<<20, + STATION_INFO_LOCAL_PM = 1<<21, + STATION_INFO_PEER_PM = 1<<22, + STATION_INFO_NONPEER_PM = 1<<23, }; /** @@ -791,6 +800,9 @@ struct sta_bss_parameters { * @sta_flags: station flags mask & values * @beacon_loss_count: Number of times beacon loss event has triggered. * @t_offset: Time offset of the station relative to this host. + * @local_pm: local mesh STA power save mode + * @peer_pm: peer mesh STA power save mode + * @nonpeer_pm: non-peer mesh STA power save mode */ struct station_info { u32 filled; @@ -820,6 +832,9 @@ struct station_info { u32 beacon_loss_count; s64 t_offset; + enum nl80211_mesh_power_mode local_pm; + enum nl80211_mesh_power_mode peer_pm; + enum nl80211_mesh_power_mode nonpeer_pm; /* * Note: Add a new enum station_info_flags value for each new field and @@ -995,6 +1010,10 @@ struct bss_parameters { * @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs) * during which a mesh STA can send only one Action frame containing * a PREQ element for root path confirmation. + * @power_mode: The default mesh power save mode which will be the initial + * setting for new peer links. + * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake + * after transmitting its beacon. */ struct mesh_config { u16 dot11MeshRetryTimeout; @@ -1022,6 +1041,8 @@ struct mesh_config { u32 dot11MeshHWMPactivePathToRootTimeout; u16 dot11MeshHWMProotInterval; u16 dot11MeshHWMPconfirmationInterval; + enum nl80211_mesh_power_mode power_mode; + u16 dot11MeshAwakeWindowDuration; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 547017100a30..6c4f703ae890 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1310,6 +1310,9 @@ enum nl80211_commands { * if not given in START_AP 0 is assumed, if not given in SET_BSS * no change is made. * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1580,6 +1583,8 @@ enum nl80211_attrs { NL80211_ATTR_P2P_CTWINDOW, NL80211_ATTR_P2P_OPPPS, + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1838,6 +1843,10 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1862,6 +1871,9 @@ enum nl80211_sta_info { NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2252,6 +2264,34 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + /** * enum nl80211_meshconf_params - mesh configuration parameters * @@ -2346,6 +2386,11 @@ enum nl80211_mntr_flags { * (in TUs) during which a mesh STA can send only one Action frame * containing a PREQ element for root path confirmation. * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2375,6 +2420,8 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0fe8ceb5444e..55957a284f6c 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -46,6 +46,7 @@ #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */ #define MESH_DEFAULT_DTIM_PERIOD 2 +#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */ const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, @@ -72,6 +73,8 @@ const struct mesh_config default_mesh_config = { .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, + .power_mode = NL80211_MESH_POWER_ACTIVE, + .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d5842eb35aec..1a7a710fe9bf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3001,6 +3001,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, sinfo->beacon_loss_count)) goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_LOCAL_PM) && + nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, + sinfo->local_pm)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_PEER_PM) && + nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, + sinfo->peer_pm)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_NONPEER_PM) && + nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, + sinfo->nonpeer_pm)) + goto nla_put_failure; if (sinfo->filled & STATION_INFO_BSS_PARAM) { bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); if (!bss_param) @@ -3206,6 +3218,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.plink_state = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) { + enum nl80211_mesh_power_mode pm = nla_get_u32( + info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); + + if (pm <= NL80211_MESH_POWER_UNKNOWN || + pm > NL80211_MESH_POWER_MAX) + return -EINVAL; + + params.local_pm = pm; + } + switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: @@ -3213,6 +3236,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow mesh-specific things */ if (params.plink_action) return -EINVAL; + if (params.local_pm) + return -EINVAL; /* TDLS can't be set, ... */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) @@ -3265,6 +3290,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; + if (params.local_pm) + return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -3922,7 +3949,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, cur_params.dot11MeshHWMProotInterval) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, - cur_params.dot11MeshHWMPconfirmationInterval)) + cur_params.dot11MeshHWMPconfirmationInterval) || + nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE, + cur_params.power_mode) || + nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, + cur_params.dot11MeshAwakeWindowDuration)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -3961,6 +3992,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 }, + [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, }; static const struct nla_policy @@ -4088,6 +4121,14 @@ do { \ 1, 65535, mask, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_MAX, + mask, NL80211_MESHCONF_POWER_MODE, + nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, + 0, 65535, mask, + NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); if (mask_out) *mask_out = mask; -- cgit v1.2.3 From 1c18f1452a772dfe884ed25677bddb3ecaf9c43a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 8 Jan 2013 10:17:27 +0100 Subject: nl80211: allow user-space to set address for P2P_DEVICE As per email discussion Jouni Malinen pointed out that: "P2P message exchanges can be executed on the current operating channel of any operation (both P2P and non-P2P station). These can be on 5 GHz and even on 60 GHz (so yes, you _can_ do GO Negotiation on 60 GHz). As an example, it would be possible to receive a GO Negotiation Request frame on a 5 GHz only radio and then to complete GO Negotiation on that band. This can happen both when connected to a P2P group (through client discoverability mechanism) and when connected to a legacy AP (assuming the station receive Probe Request frame from full scan in the beginning of P2P device discovery)." This means that P2P messages can be sent over different radio devices. However, these should use the same P2P device address so it should be able to provision this from user-space. This patch adds a parameter for this to struct vif_params which should only be used during creation of the P2P device interface. Cc: Jouni Malinen Cc: Greg Goldman Cc: Jithu Jance Signed-off-by: Arend van Spriel [add error checking] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/wireless/nl80211.c | 7 +++++++ 2 files changed, 11 insertions(+) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d9f08f65f7a5..d81e730962cc 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -281,9 +281,13 @@ struct ieee80211_supported_band { /** * struct vif_params - describes virtual interface parameters * @use_4addr: use 4-address frames + * @macaddr: address to use for this virtual interface. This will only + * be used for non-netdevice interfaces. If this parameter is set + * to zero address the driver may determine the address as needed. */ struct vif_params { int use_4addr; + u8 macaddr[ETH_ALEN]; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1a7a710fe9bf..d543cf152100 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2079,6 +2079,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; + if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { + nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], + ETH_ALEN); + if (!is_valid_ether_addr(params.macaddr)) + return -EADDRNOTAVAIL; + } + if (info->attrs[NL80211_ATTR_4ADDR]) { params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); -- cgit v1.2.3 From cee00a959c0a86571e6f99cf42f0261d7e54d2ae Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 15 Jan 2013 17:15:57 +0200 Subject: cfg80211: Allow use_mfp to be specified with the connect command The NL80211_ATTR_USE_MFP attribute was originally added for NL80211_CMD_ASSOCIATE, but it is actually as useful (if not even more useful) with NL80211_CMD_CONNECT, so process that attribute with the connect command, too. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 6 +++--- net/wireless/nl80211.c | 9 +++++++++ net/wireless/sme.c | 3 ++- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d81e730962cc..f1686d460e6b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1465,6 +1465,7 @@ struct cfg80211_ibss_params { * @ie: IEs for association request * @ie_len: Length of assoc_ie in octets * @privacy: indicates whether privacy-enabled APs should be used + * @mfp: indicate whether management frame protection is used * @crypto: crypto settings * @key_len: length of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication @@ -1485,6 +1486,7 @@ struct cfg80211_connect_params { u8 *ie; size_t ie_len; bool privacy; + enum nl80211_mfp mfp; struct cfg80211_crypto_settings crypto; const u8 *key; u8 key_len, key_idx; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6c4f703ae890..d01c16220dc5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -374,8 +374,8 @@ * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association - * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. * Background scan period can optionally be @@ -958,7 +958,7 @@ enum nl80211_commands { * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used - * with %NL80211_CMD_ASSOCIATE request + * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d543cf152100..df82a5c9faee 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5932,6 +5932,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } + if (info->attrs[NL80211_ATTR_USE_MFP]) { + connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); + if (connect.mfp != NL80211_MFP_REQUIRED && + connect.mfp != NL80211_MFP_NO) + return -EINVAL; + } else { + connect.mfp = NL80211_MFP_NO; + } + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { connect.channel = ieee80211_get_channel(wiphy, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d2d26518cdd7..a825dfe12cf7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -192,7 +192,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) prev_bssid, params->ssid, params->ssid_len, params->ie, params->ie_len, - false, ¶ms->crypto, + params->mfp != NL80211_MFP_NO, + ¶ms->crypto, params->flags, ¶ms->ht_capa, ¶ms->ht_capa_mask); if (err) -- cgit v1.2.3 From 11c4a075db2f8774d37544342c8cb9752b4db9e1 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Tue, 8 Jan 2013 14:04:07 +0100 Subject: cfg80211: check radar interface combinations To ease further DFS development regarding interface combinations, use the interface combinations structure to test for radar capabilities. Drivers can specify which channel widths they support, and in which modes. Right now only a single AP interface is allowed, but as the DFS code evolves other combinations can be enabled. Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 3 +++ net/wireless/core.c | 12 ++++++++++-- net/wireless/core.h | 7 ++++--- net/wireless/nl80211.c | 3 +++ net/wireless/util.c | 41 ++++++++++++++++++++++++++++++++++++++--- 6 files changed, 60 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f1686d460e6b..970da4420676 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2125,6 +2125,7 @@ struct ieee80211_iface_limit { * @beacon_int_infra_match: In this combination, the beacon intervals * between infrastructure and AP types must match. This is required * only in special cases. + * @radar_detect_widths: bitmap of channel widths supported for radar detection * * These examples can be expressed as follows: * @@ -2177,6 +2178,7 @@ struct ieee80211_iface_combination { u16 max_interfaces; u8 n_limits; bool beacon_int_infra_match; + u8 radar_detect_widths; }; struct mac_address { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d01c16220dc5..e6eeb4ba5dc5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2984,6 +2984,8 @@ enum nl80211_iface_limit_attrs { * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -3016,6 +3018,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, /* keep last */ NUM_NL80211_IFACE_COMB, diff --git a/net/wireless/core.c b/net/wireless/core.c index 747dd9365a44..0e702cdc6043 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -382,8 +382,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) c = &wiphy->iface_combinations[i]; - /* Combinations with just one interface aren't real */ - if (WARN_ON(c->max_interfaces < 2)) + /* + * Combinations with just one interface aren't real, + * however we make an exception for DFS. + */ + if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) return -EINVAL; /* Need at least one channel */ @@ -398,6 +401,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) return -EINVAL; + /* DFS only works on one channel. */ + if (WARN_ON(c->radar_detect_widths && + (c->num_different_channels > 1))) + return -EINVAL; + if (WARN_ON(!c->n_limits)) return -EINVAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index f342267e3620..8396f7671c8d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -425,7 +425,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, - enum cfg80211_chan_mode chanmode); + enum cfg80211_chan_mode chanmode, + u8 radar_detect); static inline int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, @@ -433,7 +434,7 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype) { return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, - CHAN_MODE_UNDEFINED); + CHAN_MODE_UNDEFINED, 0); } static inline int @@ -450,7 +451,7 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chanmode) { return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - chan, chanmode); + chan, chanmode, 0); } void diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index df82a5c9faee..33de80364c5c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -856,6 +856,9 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM, c->max_interfaces)) goto nla_put_failure; + if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + c->radar_detect_widths)) + goto nla_put_failure; nla_nest_end(msg, nl_combi); } diff --git a/net/wireless/util.c b/net/wireless/util.c index 16d76a807c2f..1c2795d52db0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1184,7 +1184,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, enum nl80211_iftype iftype, struct ieee80211_channel *chan, - enum cfg80211_chan_mode chanmode) + enum cfg80211_chan_mode chanmode, + u8 radar_detect) { struct wireless_dev *wdev_iter; u32 used_iftypes = BIT(iftype); @@ -1195,14 +1196,45 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; + bool radar_required; int i, j; ASSERT_RTNL(); lockdep_assert_held(&rdev->devlist_mtx); + if (WARN_ON(hweight32(radar_detect) > 1)) + return -EINVAL; + + switch (iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_WDS: + radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + radar_required = false; + break; + case NL80211_IFTYPE_P2P_DEVICE: + case NUM_NL80211_IFTYPES: + case NL80211_IFTYPE_UNSPECIFIED: + default: + return -EINVAL; + } + + if (radar_required && !radar_detect) + return -EINVAL; + /* Always allow software iftypes */ - if (rdev->wiphy.software_iftypes & BIT(iftype)) + if (rdev->wiphy.software_iftypes & BIT(iftype)) { + if (radar_detect) + return -EINVAL; return 0; + } memset(num, 0, sizeof(num)); memset(used_channels, 0, sizeof(used_channels)); @@ -1275,7 +1307,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, used_iftypes |= BIT(wdev_iter->iftype); } - if (total == 1) + if (total == 1 && !radar_detect) return 0; for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { @@ -1308,6 +1340,9 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, } } + if (radar_detect && !(c->radar_detect_widths & radar_detect)) + goto cont; + /* * Finally check that all iftypes that we're currently * using are actually part of this combination. If they -- cgit v1.2.3 From 5a32aff37a02ebc959837f08d09ac8ba65d4f1b0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 21 Dec 2012 12:36:33 +0100 Subject: mac80211: split out chandef tracing macros Some new APIs will require tracing a chandef without it being part of a channel context, so separate out the tracing macros for that. Signed-off-by: Johannes Berg --- net/mac80211/trace.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a8270b441a6f..41861b91daa3 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -28,21 +28,27 @@ #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" -#define CHANCTX_ENTRY __field(u32, control_freq) \ +#define CHANDEF_ENTRY __field(u32, control_freq) \ __field(u32, chan_width) \ __field(u32, center_freq1) \ - __field(u32, center_freq2) \ + __field(u32, center_freq2) +#define CHANDEF_ASSIGN(c) \ + __entry->control_freq = (c)->chan->center_freq; \ + __entry->chan_width = (c)->width; \ + __entry->center_freq1 = (c)->center_freq1; \ + __entry->center_freq1 = (c)->center_freq2; +#define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz" +#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \ + __entry->center_freq1, __entry->center_freq2 + +#define CHANCTX_ENTRY CHANDEF_ENTRY \ __field(u8, rx_chains_static) \ __field(u8, rx_chains_dynamic) -#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\ - __entry->chan_width = ctx->conf.def.width; \ - __entry->center_freq1 = ctx->conf.def.center_freq1; \ - __entry->center_freq2 = ctx->conf.def.center_freq2; \ +#define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def) \ __entry->rx_chains_static = ctx->conf.rx_chains_static; \ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d" -#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \ - __entry->center_freq1, __entry->center_freq2, \ +#define CHANCTX_PR_FMT CHANDEF_PR_FMT " chains:%d/%d" +#define CHANCTX_PR_ARG CHANDEF_PR_ARG, \ __entry->rx_chains_static, __entry->rx_chains_dynamic -- cgit v1.2.3 From 5bd30d398792eb6351da2087fe81bbf755900991 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 17 Jan 2013 12:21:08 +0800 Subject: netpoll: fix a missing dev refcounting __dev_get_by_name() doesn't refcount the network device, so we have to do this by ourselves. Noticed by Eric. Cc: Eric Dumazet Cc: Jiri Pirko Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/netpoll.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index a5ad1c1c4b18..a9b10048388a 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -1056,6 +1056,7 @@ int netpoll_setup(struct netpoll *np) err = -ENODEV; goto unlock; } + dev_hold(ndev); if (netdev_master_upper_dev_get(ndev)) { np_err(np, "%s is a slave device, aborting\n", np->dev_name); -- cgit v1.2.3 From d59577b6ffd313d0ab3be39cb1ab47e29bdc9182 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 16 Jan 2013 22:55:49 +0100 Subject: sk-filter: Add ability to lock a socket filter program While a privileged program can open a raw socket, attach some restrictive filter and drop its privileges (or send the socket to an unprivileged program through some Unix socket), the filter can still be removed or modified by the unprivileged program. This commit adds a socket option to lock the filter (SO_LOCK_FILTER) preventing any modification of a socket filter program. This is similar to OpenBSD BIOCLOCK ioctl on bpf sockets, except even root is not allowed change/drop the filter. The state of the lock can be read with getsockopt(). No error is triggered if the state is not changed. -EPERM is returned when a user tries to remove the lock or to change/remove the filter while the lock is active. The check is done directly in sk_attach_filter() and sk_detach_filter() and does not affect only setsockopt() syscall. Signed-off-by: Vincent Bernat Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 11 +++++++++-- arch/alpha/include/uapi/asm/socket.h | 1 + arch/avr32/include/uapi/asm/socket.h | 2 ++ arch/cris/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/h8300/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 1 + arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 1 + arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 1 + arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/net/sock.h | 1 + include/uapi/asm-generic/socket.h | 2 ++ net/core/filter.c | 6 ++++++ net/core/sock.c | 11 +++++++++++ 19 files changed, 53 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index bbf2005270b5..cdb3e40b9d14 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -17,12 +17,12 @@ creating filters. LSF is much simpler than BPF. One does not have to worry about devices or anything like that. You simply create your filter -code, send it to the kernel via the SO_ATTACH_FILTER ioctl and +code, send it to the kernel via the SO_ATTACH_FILTER option and if your filter code passes the kernel check on it, you then immediately begin filtering data on that socket. You can also detach filters from your socket via the -SO_DETACH_FILTER ioctl. This will probably not be used much +SO_DETACH_FILTER option. This will probably not be used much since when you close a socket that has a filter on it the filter is automagically removed. The other less common case may be adding a different filter on the same socket where you had another @@ -31,12 +31,19 @@ the old one and placing your new one in its place, assuming your filter has passed the checks, otherwise if it fails the old filter will remain on that socket. +SO_LOCK_FILTER option allows to lock the filter attached to a +socket. Once set, a filter cannot be removed or changed. This allows +one process to setup a socket, attach a filter, lock it then drop +privileges and be assured that the filter will be kept until the +socket is closed. + Examples ======== Ioctls- setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)); setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value)); +setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER, &value, sizeof(value)); See the BSD bpf.4 manpage and the BSD Packet Filter paper written by Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory. diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 097c1577735a..755702eefd9c 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -77,5 +77,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 486df68abeec..f3f38a0e2ef9 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h index b681b043f6c8..406b5838defd 100644 --- a/arch/cris/include/uapi/asm/socket.h +++ b/arch/cris/include/uapi/asm/socket.h @@ -72,6 +72,8 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index 871f89b7fbda..d8e1132a1ab6 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -70,5 +70,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/uapi/asm/socket.h b/arch/h8300/include/uapi/asm/socket.h index 90a2e573c7e6..c8b87a828206 100644 --- a/arch/h8300/include/uapi/asm/socket.h +++ b/arch/h8300/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 23d6759bb57b..f390896c3104 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -79,4 +79,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 5e7088a26726..6a895155e7a3 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 17307ab90474..9d11a7713923 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -90,5 +90,6 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index af5366bbfe62..ab702c40b30e 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index d9ff4731253b..da2c8d3c209e 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -69,6 +69,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 0x4024 +#define SO_LOCK_FILTER 0x4025 /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index eb0b1864d400..e6ca31816cc9 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -77,4 +77,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 436d07c23be8..9ce60b68f070 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -76,4 +76,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index c83a937ead00..fbbba57547d1 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -66,6 +66,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 0x0027 +#define SO_LOCK_FILTER 0x0028 /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 38079be1cf1e..dbf316487b51 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -81,4 +81,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 182ca99405ad..5a34e2f03657 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -664,6 +664,7 @@ enum sock_flags { * Will use last 4 bytes of packet sent from * user-space instead. */ + SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */ }; static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 2d32d073a6f9..3f6a99201410 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -73,4 +73,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/filter.c b/net/core/filter.c index 2ead2a9b1859..2e20b55a7830 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -721,6 +721,9 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) unsigned int fsize = sizeof(struct sock_filter) * fprog->len; int err; + if (sock_flag(sk, SOCK_FILTER_LOCKED)) + return -EPERM; + /* Make sure new filter is there and in the right amounts. */ if (fprog->filter == NULL) return -EINVAL; @@ -757,6 +760,9 @@ int sk_detach_filter(struct sock *sk) int ret = -ENOENT; struct sk_filter *filter; + if (sock_flag(sk, SOCK_FILTER_LOCKED)) + return -EPERM; + filter = rcu_dereference_protected(sk->sk_filter, sock_owned_by_user(sk)); if (filter) { diff --git a/net/core/sock.c b/net/core/sock.c index bc131d419683..8258fb741e9a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -861,6 +861,13 @@ set_rcvbuf: ret = sk_detach_filter(sk); break; + case SO_LOCK_FILTER: + if (sock_flag(sk, SOCK_FILTER_LOCKED) && !valbool) + ret = -EPERM; + else + sock_valbool_flag(sk, SOCK_FILTER_LOCKED, valbool); + break; + case SO_PASSSEC: if (valbool) set_bit(SOCK_PASSSEC, &sock->flags); @@ -1140,6 +1147,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, goto lenout; + case SO_LOCK_FILTER: + v.val = sock_flag(sk, SOCK_FILTER_LOCKED); + break; + default: return -ENOPROTOOPT; } -- cgit v1.2.3 From 7a9885b93bb623524468b37d04146d3422e099eb Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Thu, 17 Jan 2013 16:34:11 +0800 Subject: xfrm: use separated locks to protect pointers of struct xfrm_state_afinfo afinfo->type_map and afinfo->mode_map deserve separated locks, they are different things. We should just take RCU read lock to protect afinfo itself, but not for the inner pointers. Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_state.c | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 1522f19f273a..0adae918a7a2 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -168,57 +168,45 @@ int __xfrm_state_delete(struct xfrm_state *x); int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); void km_state_expired(struct xfrm_state *x, int hard, u32 portid); -static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family) -{ - struct xfrm_state_afinfo *afinfo; - if (unlikely(family >= NPROTO)) - return NULL; - spin_lock_bh(&xfrm_state_afinfo_lock); - afinfo = xfrm_state_afinfo[family]; - if (unlikely(!afinfo)) - spin_unlock_bh(&xfrm_state_afinfo_lock); - return afinfo; -} - -static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo) -{ - spin_unlock_bh(&xfrm_state_afinfo_lock); -} - +static DEFINE_SPINLOCK(xfrm_type_lock); int xfrm_register_type(const struct xfrm_type *type, unsigned short family) { - struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; + spin_lock_bh(&xfrm_type_lock); if (likely(typemap[type->proto] == NULL)) typemap[type->proto] = type; else err = -EEXIST; - xfrm_state_unlock_afinfo(afinfo); + spin_unlock_bh(&xfrm_type_lock); + xfrm_state_put_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_register_type); int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family) { - struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family); + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); const struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; + spin_lock_bh(&xfrm_type_lock); if (unlikely(typemap[type->proto] != type)) err = -ENOENT; else typemap[type->proto] = NULL; - xfrm_state_unlock_afinfo(afinfo); + spin_unlock_bh(&xfrm_type_lock); + xfrm_state_put_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_unregister_type); @@ -255,6 +243,7 @@ static void xfrm_put_type(const struct xfrm_type *type) module_put(type->owner); } +static DEFINE_SPINLOCK(xfrm_mode_lock); int xfrm_register_mode(struct xfrm_mode *mode, int family) { struct xfrm_state_afinfo *afinfo; @@ -264,12 +253,13 @@ int xfrm_register_mode(struct xfrm_mode *mode, int family) if (unlikely(mode->encap >= XFRM_MODE_MAX)) return -EINVAL; - afinfo = xfrm_state_lock_afinfo(family); + afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -EEXIST; modemap = afinfo->mode_map; + spin_lock_bh(&xfrm_mode_lock); if (modemap[mode->encap]) goto out; @@ -282,7 +272,8 @@ int xfrm_register_mode(struct xfrm_mode *mode, int family) err = 0; out: - xfrm_state_unlock_afinfo(afinfo); + spin_unlock_bh(&xfrm_mode_lock); + xfrm_state_put_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_register_mode); @@ -296,19 +287,21 @@ int xfrm_unregister_mode(struct xfrm_mode *mode, int family) if (unlikely(mode->encap >= XFRM_MODE_MAX)) return -EINVAL; - afinfo = xfrm_state_lock_afinfo(family); + afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; err = -ENOENT; modemap = afinfo->mode_map; + spin_lock_bh(&xfrm_mode_lock); if (likely(modemap[mode->encap] == mode)) { modemap[mode->encap] = NULL; module_put(mode->afinfo->owner); err = 0; } - xfrm_state_unlock_afinfo(afinfo); + spin_unlock_bh(&xfrm_mode_lock); + xfrm_state_put_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_unregister_mode); -- cgit v1.2.3 From c2a936600f78aea00d3312ea4b66a79a4619f9b4 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 15 Jan 2013 07:16:35 +0000 Subject: net: increase fragment memory usage limits Increase the amount of memory usage limits for incomplete IP fragments. Arguing for new thresh high/low values: High threshold = 4 MBytes Low threshold = 3 MBytes The fragmentation memory accounting code, tries to account for the real memory usage, by measuring both the size of frag queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) and the SKB's truesize. We want to be able to handle/hold-on-to enough fragments, to ensure good performance, without causing incomplete fragments to hurt scalability, by causing the number of inet_frag_queue to grow too much (resulting longer searches for frag queues). For IPv4, how much memory does the largest frag consume. Maximum size fragment is 64K, which is approx 44 fragments with MTU(1500) sized packets. Sizeof(struct ipq) is 200. A 1500 byte packet results in a truesize of 2944 (not 2048 as I first assumed) (44*2944)+200 = 129736 bytes The current default high thresh of 262144 bytes, is obviously problematic, as only two 64K fragments can fit in the queue at the same time. How many 64K fragment can we fit into 4 MBytes: 4*2^20/((44*2944)+200) = 32.34 fragment in queues An attacker could send a separate/distinct fake fragment packets per queue, causing us to allocate one inet_frag_queue per packet, and thus attacking the hash table and its lists. How many frag queue do we need to store, and given a current hash size of 64, what is the average list length. Using one MTU sized fragment per inet_frag_queue, each consuming (2944+200) 3144 bytes. 4*2^20/(2944+200) = 1334 frag queues -> 21 avg list length An attack could send small fragments, the smallest packet I could send resulted in a truesize of 896 bytes (I'm a little surprised by this). 4*2^20/(896+200) = 3827 frag queues -> 59 avg list length When increasing these number, we also need to followup with improvements, that is going to help scalability. Simply increasing the hash size, is not enough as the current implementation does not have a per hash bucket locking. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/ipv6.h | 4 ++-- net/ipv4/ip_fragment.c | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index a0909c79374b..51a068ad064b 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -292,8 +292,8 @@ static inline int ip6_frag_mem(struct net *net) } #endif -#define IPV6_FRAG_HIGH_THRESH (256 * 1024) /* 262144 */ -#define IPV6_FRAG_LOW_THRESH (192 * 1024) /* 196608 */ +#define IPV6_FRAG_HIGH_THRESH (4 * 1024*1024) /* 4194304 */ +#define IPV6_FRAG_LOW_THRESH (3 * 1024*1024) /* 3145728 */ #define IPV6_FRAG_TIMEOUT (60 * HZ) /* 60 seconds */ extern int __ipv6_addr_type(const struct in6_addr *addr); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index eb9d63a570cd..f55a4e61bfb8 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -851,14 +851,22 @@ static inline void ip4_frags_ctl_register(void) static int __net_init ipv4_frags_init_net(struct net *net) { - /* - * Fragment cache limits. We will commit 256K at one time. Should we - * cross that limit we will prune down to 192K. This should cope with - * even the most extreme cases without allowing an attacker to - * measurably harm machine performance. + /* Fragment cache limits. + * + * The fragment memory accounting code, (tries to) account for + * the real memory usage, by measuring both the size of frag + * queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) + * and the SKB's truesize. + * + * A 64K fragment consumes 129736 bytes (44*2944)+200 + * (1500 truesize == 2944, sizeof(struct ipq) == 200) + * + * We will commit 4MB at one time. Should we cross that limit + * we will prune down to 3MB, making room for approx 8 big 64K + * fragments 8x128k. */ - net->ipv4.frags.high_thresh = 256 * 1024; - net->ipv4.frags.low_thresh = 192 * 1024; + net->ipv4.frags.high_thresh = 4 * 1024 * 1024; + net->ipv4.frags.low_thresh = 3 * 1024 * 1024; /* * Important NOTE! Fragment queue must be destroyed before MSL expires. * RFC791 is wrong proposing to prolongate timer each fragment arrival -- cgit v1.2.3 From 7266507d89991fa1e989283e4e032c6d9357fe26 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Mon, 17 Dec 2012 18:33:58 +0000 Subject: netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones Most SIP devices use a source port of 5060/udp on SIP requests, so the response automatically comes back to port 5060: phone_ip:5060 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:5060 100 Trying The newer Cisco IP phones, however, use a randomly chosen high source port for the SIP request but expect the response on port 5060: phone_ip:49173 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:5060 100 Trying Standard Linux NAT, with or without nf_nat_sip, will send the reply back to port 49173, not 5060: phone_ip:49173 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:49173 100 Trying But the phone is not listening on 49173, so it will never see the reply. This patch modifies nf_*_sip to work around this quirk by extracting the SIP response port from the Via: header, iff the source IP in the packet header matches the source IP in the SIP request. Signed-off-by: Kevin Cernekee Acked-by: Eric Dumazet Cc: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_sip.h | 3 +++ net/netfilter/nf_conntrack_sip.c | 17 +++++++++++++++++ net/netfilter/nf_nat_sip.c | 27 ++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 387bdd02945d..ba7f571a2b1c 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -4,12 +4,15 @@ #include +#include + #define SIP_PORT 5060 #define SIP_TIMEOUT 3600 struct nf_ct_sip_master { unsigned int register_cseq; unsigned int invite_cseq; + __be16 forced_dport; }; enum sip_expectation_classes { diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index df8f4f284481..72a67bbe3518 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1440,8 +1440,25 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned int matchoff, matchlen; unsigned int cseq, i; + union nf_inet_addr addr; + __be16 port; + + /* Many Cisco IP phones use a high source port for SIP requests, but + * listen for the response on port 5060. If we are the local + * router for one of these phones, save the port number from the + * Via: header so that nf_nat_sip can redirect the responses to + * the correct port. + */ + if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, + SIP_HDR_VIA_UDP, NULL, &matchoff, + &matchlen, &addr, &port) > 0 && + port != ct->tuplehash[dir].tuple.src.u.udp.port && + nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3)) + ct_sip_info->forced_dport = port; for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { const struct sip_handler *handler; diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 16303c752213..5951146e7688 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -95,6 +95,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; union nf_inet_addr newaddr; @@ -107,7 +108,8 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && ct->tuplehash[dir].tuple.dst.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.src.u3; - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; + newport = ct_sip_info->forced_dport ? : + ct->tuplehash[!dir].tuple.src.u.udp.port; } else return 1; @@ -144,6 +146,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); unsigned int coff, matchoff, matchlen; enum sip_header_types hdr; union nf_inet_addr addr; @@ -258,6 +261,21 @@ next: !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; + /* Mangle destination port for Cisco phones, then fix up checksums */ + if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { + struct udphdr *uh; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + uh = (void *)skb->data + protoff; + uh->dest = ct_sip_info->forced_dport; + + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, + 0, 0, NULL, 0)) + return NF_DROP; + } + return NF_ACCEPT; } @@ -311,8 +329,10 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); union nf_inet_addr newaddr; u_int16_t port; + __be16 srcport; char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; @@ -326,8 +346,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, /* If the signalling port matches the connection's source port in the * original direction, try to use the destination port in the opposite * direction. */ - if (exp->tuple.dst.u.udp.port == - ct->tuplehash[dir].tuple.src.u.udp.port) + srcport = ct_sip_info->forced_dport ? : + ct->tuplehash[dir].tuple.src.u.udp.port; + if (exp->tuple.dst.u.udp.port == srcport) port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); else port = ntohs(exp->tuple.dst.u.udp.port); -- cgit v1.2.3 From c539f01717c239cfa0921dd43927afc976f1eedc Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 11 Jan 2013 06:30:44 +0000 Subject: netfilter: add connlabel conntrack extension similar to connmarks, except labels are bit-based; i.e. all labels may be attached to a flow at the same time. Up to 128 labels are supported. Supporting more labels is possible, but requires increasing the ct offset delta from u8 to u16 type due to increased extension sizes. Mapping of bit-identifier to label name is done in userspace. The extension is enabled at run-time once "-m connlabel" netfilter rules are added. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++ include/net/netfilter/nf_conntrack_labels.h | 55 ++++++++++++++++ include/net/netns/conntrack.h | 4 ++ include/uapi/linux/netfilter/xt_connlabel.h | 12 ++++ net/netfilter/Kconfig | 18 ++++++ net/netfilter/Makefile | 2 + net/netfilter/nf_conntrack_core.c | 12 ++++ net/netfilter/nf_conntrack_labels.c | 72 +++++++++++++++++++++ net/netfilter/nf_conntrack_netlink.c | 3 + net/netfilter/xt_connlabel.c | 99 +++++++++++++++++++++++++++++ 10 files changed, 281 insertions(+) create mode 100644 include/net/netfilter/nf_conntrack_labels.h create mode 100644 include/uapi/linux/netfilter/xt_connlabel.h create mode 100644 net/netfilter/nf_conntrack_labels.c create mode 100644 net/netfilter/xt_connlabel.c (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 8b4d1fc29096..977bc8a46444 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -22,6 +22,9 @@ enum nf_ct_ext_id { #endif #ifdef CONFIG_NF_CONNTRACK_TIMEOUT NF_CT_EXT_TIMEOUT, +#endif +#ifdef CONFIG_NF_CONNTRACK_LABELS + NF_CT_EXT_LABELS, #endif NF_CT_EXT_NUM, }; @@ -33,6 +36,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout +#define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h new file mode 100644 index 000000000000..b94fe31c7b39 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_labels.h @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include + +#include + +struct nf_conn_labels { + u8 words; + unsigned long bits[]; +}; + +static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_LABELS + return nf_ct_ext_find(ct, NF_CT_EXT_LABELS); +#else + return NULL; +#endif +} + +static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct) +{ +#ifdef CONFIG_NF_CONNTRACK_LABELS + struct nf_conn_labels *cl_ext; + struct net *net = nf_ct_net(ct); + u8 words; + + words = ACCESS_ONCE(net->ct.label_words); + if (words == 0 || WARN_ON_ONCE(words > 8)) + return NULL; + + cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS, + words * sizeof(long), GFP_ATOMIC); + if (cl_ext != NULL) + cl_ext->words = words; + + return cl_ext; +#else + return NULL; +#endif +} + +bool nf_connlabel_match(const struct nf_conn *ct, u16 bit); +int nf_connlabel_set(struct nf_conn *ct, u16 bit); + +#ifdef CONFIG_NF_CONNTRACK_LABELS +int nf_conntrack_labels_init(struct net *net); +void nf_conntrack_labels_fini(struct net *net); +#else +static inline int nf_conntrack_labels_init(struct net *n) { return 0; } +static inline void nf_conntrack_labels_fini(struct net *net) {} +#endif diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 923cb20051ed..c9c0c538b68b 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -84,6 +84,10 @@ struct netns_ct { int sysctl_auto_assign_helper; bool auto_assign_helper_warned; struct nf_ip_net nf_ct_proto; +#if defined(CONFIG_NF_CONNTRACK_LABELS) + unsigned int labels_used; + u8 label_words; +#endif #ifdef CONFIG_NF_NAT_NEEDED struct hlist_head *nat_bysource; unsigned int nat_htable_size; diff --git a/include/uapi/linux/netfilter/xt_connlabel.h b/include/uapi/linux/netfilter/xt_connlabel.h new file mode 100644 index 000000000000..c4bc9ee9b330 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_connlabel.h @@ -0,0 +1,12 @@ +#include + +#define XT_CONNLABEL_MAXBIT 127 +enum xt_connlabel_mtopts { + XT_CONNLABEL_OP_INVERT = 1 << 0, + XT_CONNLABEL_OP_SET = 1 << 1, +}; + +struct xt_connlabel_mtinfo { + __u16 bit; + __u16 options; +}; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 49e96df5fbc4..bb48607d4ee4 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -124,6 +124,12 @@ config NF_CONNTRACK_TIMESTAMP If unsure, say `N'. +config NF_CONNTRACK_LABELS + bool + help + This option enables support for assigning user-defined flag bits + to connection tracking entries. It selected by the connlabel match. + config NF_CT_PROTO_DCCP tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' depends on EXPERIMENTAL @@ -842,6 +848,18 @@ config NETFILTER_XT_MATCH_CONNBYTES If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_CONNLABEL + tristate '"connlabel" match support' + select NF_CONNTRACK_LABELS + depends on NETFILTER_ADVANCED + ---help--- + This match allows you to test and assign userspace-defined labels names + to a connection. The kernel only stores bit values - mapping + names to bits is done by userspace. + + Unlike connmark, more than 32 flag bits may be assigned to a + connection simultaneously. + config NETFILTER_XT_MATCH_CONNLIMIT tristate '"connlimit" match support"' depends on NF_CONNTRACK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 32596978df1d..b3bbda60945e 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -4,6 +4,7 @@ nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o +nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o obj-$(CONFIG_NETFILTER) = netfilter.o @@ -101,6 +102,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o 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_CONNLABEL) += xt_connlabel.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index e4a0c4fb3a7c..85aa4b7149c5 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -763,6 +764,7 @@ void nf_conntrack_free(struct nf_conn *ct) } EXPORT_SYMBOL_GPL(nf_conntrack_free); + /* Allocate a new conntrack: we return -ENOMEM if classification failed due to stress. Otherwise it really is unclassifiable. */ static struct nf_conntrack_tuple_hash * @@ -809,6 +811,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, nf_ct_acct_ext_add(ct, GFP_ATOMIC); nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); + nf_ct_labels_ext_add(ct); ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, @@ -1352,6 +1355,7 @@ static void nf_conntrack_cleanup_net(struct net *net) } nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); + nf_conntrack_labels_fini(net); nf_conntrack_helper_fini(net); nf_conntrack_timeout_fini(net); nf_conntrack_ecache_fini(net); @@ -1583,7 +1587,15 @@ static int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_helper_init(net); if (ret < 0) goto err_helper; + + ret = nf_conntrack_labels_init(net); + if (ret < 0) + goto err_labels; + return 0; + +err_labels: + nf_conntrack_helper_fini(net); err_helper: nf_conntrack_timeout_fini(net); err_timeout: diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c new file mode 100644 index 000000000000..0c542f41f338 --- /dev/null +++ b/net/netfilter/nf_conntrack_labels.c @@ -0,0 +1,72 @@ +/* + * test/set flag bits stored in conntrack extension area. + * + * (C) 2013 Astaro GmbH & Co KG + * + * 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 + +static unsigned int label_bits(const struct nf_conn_labels *l) +{ + unsigned int longs = l->words; + return longs * BITS_PER_LONG; +} + +bool nf_connlabel_match(const struct nf_conn *ct, u16 bit) +{ + struct nf_conn_labels *labels = nf_ct_labels_find(ct); + + if (!labels) + return false; + + return bit < label_bits(labels) && test_bit(bit, labels->bits); +} +EXPORT_SYMBOL_GPL(nf_connlabel_match); + +int nf_connlabel_set(struct nf_conn *ct, u16 bit) +{ + struct nf_conn_labels *labels = nf_ct_labels_find(ct); + + if (!labels || bit >= label_bits(labels)) + return -ENOSPC; + + if (test_bit(bit, labels->bits)) + return 0; + + if (test_and_set_bit(bit, labels->bits)) + return 0; + + return 0; +} +EXPORT_SYMBOL_GPL(nf_connlabel_set); + +static struct nf_ct_ext_type labels_extend __read_mostly = { + .len = sizeof(struct nf_conn_labels), + .align = __alignof__(struct nf_conn_labels), + .id = NF_CT_EXT_LABELS, +}; + +int nf_conntrack_labels_init(struct net *net) +{ + if (net_eq(net, &init_net)) + return nf_ct_extend_register(&labels_extend); + return 0; +} + +void nf_conntrack_labels_fini(struct net *net) +{ + if (net_eq(net, &init_net)) + nf_ct_extend_unregister(&labels_extend); +} diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 627b0e50b238..e0b10ee180ef 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef CONFIG_NF_NAT_NEEDED #include #include @@ -1598,6 +1599,8 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, nf_ct_acct_ext_add(ct, GFP_ATOMIC); nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); + nf_ct_labels_ext_add(ct); + /* we must add conntrack extensions before confirmation. */ ct->status |= IPS_CONFIRMED; diff --git a/net/netfilter/xt_connlabel.c b/net/netfilter/xt_connlabel.c new file mode 100644 index 000000000000..9f8719df2001 --- /dev/null +++ b/net/netfilter/xt_connlabel.c @@ -0,0 +1,99 @@ +/* + * (C) 2013 Astaro GmbH & Co KG + * + * 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 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Westphal "); +MODULE_DESCRIPTION("Xtables: add/match connection trackling labels"); +MODULE_ALIAS("ipt_connlabel"); +MODULE_ALIAS("ip6t_connlabel"); + +static bool +connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_connlabel_mtinfo *info = par->matchinfo; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + bool invert = info->options & XT_CONNLABEL_OP_INVERT; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL || nf_ct_is_untracked(ct)) + return invert; + + if (info->options & XT_CONNLABEL_OP_SET) + return (nf_connlabel_set(ct, info->bit) == 0) ^ invert; + + return nf_connlabel_match(ct, info->bit) ^ invert; +} + +static int connlabel_mt_check(const struct xt_mtchk_param *par) +{ + const int options = XT_CONNLABEL_OP_INVERT | + XT_CONNLABEL_OP_SET; + struct xt_connlabel_mtinfo *info = par->matchinfo; + int ret; + size_t words; + + if (info->bit > XT_CONNLABEL_MAXBIT) + return -ERANGE; + + if (info->options & ~options) { + pr_err("Unknown options in mask %x\n", info->options); + return -EINVAL; + } + + ret = nf_ct_l3proto_try_module_get(par->family); + if (ret < 0) { + pr_info("cannot load conntrack support for proto=%u\n", + par->family); + return ret; + } + + par->net->ct.labels_used++; + words = BITS_TO_LONGS(info->bit+1); + if (words > par->net->ct.label_words) + par->net->ct.label_words = words; + + return ret; +} + +static void connlabel_mt_destroy(const struct xt_mtdtor_param *par) +{ + par->net->ct.labels_used--; + if (par->net->ct.labels_used == 0) + par->net->ct.label_words = 0; + nf_ct_l3proto_module_put(par->family); +} + +static struct xt_match connlabels_mt_reg __read_mostly = { + .name = "connlabel", + .family = NFPROTO_UNSPEC, + .checkentry = connlabel_mt_check, + .match = connlabel_mt, + .matchsize = sizeof(struct xt_connlabel_mtinfo), + .destroy = connlabel_mt_destroy, + .me = THIS_MODULE, +}; + +static int __init connlabel_mt_init(void) +{ + return xt_register_match(&connlabels_mt_reg); +} + +static void __exit connlabel_mt_exit(void) +{ + xt_unregister_match(&connlabels_mt_reg); +} + +module_init(connlabel_mt_init); +module_exit(connlabel_mt_exit); -- cgit v1.2.3 From 0ceabd83875b72a29f33db4ab703d6ba40ea4c58 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 11 Jan 2013 06:30:45 +0000 Subject: netfilter: ctnetlink: deliver labels to userspace Introduce CTA_LABELS attribute to send a bit-vector of currently active labels to userspace. Future patch will permit userspace to also set/delete active labels. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_conntrack_common.h | 1 + include/uapi/linux/netfilter/nfnetlink_conntrack.h | 1 + net/netfilter/nf_conntrack_labels.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 41 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 1644cdd8be91..d69483fb3825 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -101,6 +101,7 @@ enum ip_conntrack_events { IPCT_MARK, /* new mark has been set */ IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ IPCT_SECMARK, /* new security mark has been set */ + IPCT_LABEL, /* new connlabel has been set */ }; enum ip_conntrack_expect_events { diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 86e930cf3dfb..9e71e0c081fd 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -49,6 +49,7 @@ enum ctattr_type { CTA_SECCTX, CTA_TIMESTAMP, CTA_MARK_MASK, + CTA_LABELS, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c index 0c542f41f338..ac5d0807d681 100644 --- a/net/netfilter/nf_conntrack_labels.c +++ b/net/netfilter/nf_conntrack_labels.c @@ -46,7 +46,7 @@ int nf_connlabel_set(struct nf_conn *ct, u16 bit) return 0; if (test_and_set_bit(bit, labels->bits)) - return 0; + nf_conntrack_event_cache(IPCT_LABEL, ct); return 0; } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index e0b10ee180ef..5f5386382f13 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -324,6 +324,40 @@ nla_put_failure: #define ctnetlink_dump_secctx(a, b) (0) #endif +#ifdef CONFIG_NF_CONNTRACK_LABELS +static int ctnetlink_label_size(const struct nf_conn *ct) +{ + struct nf_conn_labels *labels = nf_ct_labels_find(ct); + + if (!labels) + return 0; + return nla_total_size(labels->words * sizeof(long)); +} + +static int +ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct) +{ + struct nf_conn_labels *labels = nf_ct_labels_find(ct); + unsigned int len, i; + + if (!labels) + return 0; + + len = labels->words * sizeof(long); + i = 0; + do { + if (labels->bits[i] != 0) + return nla_put(skb, CTA_LABELS, len, labels->bits); + i++; + } while (i < labels->words); + + return 0; +} +#else +#define ctnetlink_dump_labels(a, b) (0) +#define ctnetlink_label_size(a) (0) +#endif + #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple) static inline int @@ -464,6 +498,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_secctx(skb, ct) < 0 || + ctnetlink_dump_labels(skb, ct) < 0 || ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 || @@ -562,6 +597,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */ #endif + ctnetlink_proto_size(ct) + + ctnetlink_label_size(ct) ; } @@ -663,6 +699,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) && ctnetlink_dump_secctx(skb, ct) < 0) goto nla_put_failure; #endif + if (events & (1 << IPCT_LABEL) && + ctnetlink_dump_labels(skb, ct) < 0) + goto nla_put_failure; if (events & (1 << IPCT_RELATED) && ctnetlink_dump_master(skb, ct) < 0) @@ -1986,6 +2025,8 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0) goto nla_put_failure; #endif + if (ctnetlink_dump_labels(skb, ct) < 0) + goto nla_put_failure; rcu_read_unlock(); return 0; -- cgit v1.2.3 From 9b21f6a90924dfe8e5e686c314ddb441fb06501e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 11 Jan 2013 06:30:46 +0000 Subject: netfilter: ctnetlink: allow userspace to modify labels Add the ability to set/clear labels assigned to a conntrack via ctnetlink. To allow userspace to only alter specific bits, Pablo suggested to add a new CTA_LABELS_MASK attribute: The new set of active labels is then determined via active = (active & ~mask) ^ changeset i.e., the mask selects those bits in the existing set that should be changed. This follows the same method already used by MARK and CONNMARK targets. Omitting CTA_LABELS_MASK is the same as setting all bits in CTA_LABELS_MASK to 1: The existing set is replaced by the one from userspace. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_labels.h | 3 ++ include/uapi/linux/netfilter/nfnetlink_conntrack.h | 1 + net/netfilter/nf_conntrack_labels.c | 43 +++++++++++++++++++++ net/netfilter/nf_conntrack_netlink.c | 44 ++++++++++++++++++++++ 4 files changed, 91 insertions(+) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h index b94fe31c7b39..a3ce5d076fca 100644 --- a/include/net/netfilter/nf_conntrack_labels.h +++ b/include/net/netfilter/nf_conntrack_labels.h @@ -46,6 +46,9 @@ static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct) bool nf_connlabel_match(const struct nf_conn *ct, u16 bit); int nf_connlabel_set(struct nf_conn *ct, u16 bit); +int nf_connlabels_replace(struct nf_conn *ct, + const u32 *data, const u32 *mask, unsigned int words); + #ifdef CONFIG_NF_CONNTRACK_LABELS int nf_conntrack_labels_init(struct net *net); void nf_conntrack_labels_fini(struct net *net); diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 9e71e0c081fd..08fabc6c93f3 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -50,6 +50,7 @@ enum ctattr_type { CTA_TIMESTAMP, CTA_MARK_MASK, CTA_LABELS, + CTA_LABELS_MASK, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c index ac5d0807d681..e1d1eb850e7f 100644 --- a/net/netfilter/nf_conntrack_labels.c +++ b/net/netfilter/nf_conntrack_labels.c @@ -52,6 +52,49 @@ int nf_connlabel_set(struct nf_conn *ct, u16 bit) } EXPORT_SYMBOL_GPL(nf_connlabel_set); +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) +static void replace_u32(u32 *address, u32 mask, u32 new) +{ + u32 old, tmp; + + do { + old = *address; + tmp = (old & mask) ^ new; + } while (cmpxchg(address, old, tmp) != old); +} + +int nf_connlabels_replace(struct nf_conn *ct, + const u32 *data, + const u32 *mask, unsigned int words32) +{ + struct nf_conn_labels *labels; + unsigned int size, i; + u32 *dst; + + labels = nf_ct_labels_find(ct); + if (!labels) + return -ENOSPC; + + size = labels->words * sizeof(long); + if (size < (words32 * sizeof(u32))) + words32 = size / sizeof(u32); + + dst = (u32 *) labels->bits; + if (words32) { + for (i = 0; i < words32; i++) + replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]); + } + + size /= sizeof(u32); + for (i = words32; i < size; i++) /* pad */ + replace_u32(&dst[i], 0, 0); + + nf_conntrack_event_cache(IPCT_LABEL, ct); + return 0; +} +EXPORT_SYMBOL_GPL(nf_connlabels_replace); +#endif + static struct nf_ct_ext_type labels_extend __read_mostly = { .len = sizeof(struct nf_conn_labels), .align = __alignof__(struct nf_conn_labels), diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 5f5386382f13..2334cc5d2b16 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -961,6 +961,7 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name, return 0; } +#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE) static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { [CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, [CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, @@ -977,6 +978,10 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED }, [CTA_ZONE] = { .type = NLA_U16 }, [CTA_MARK_MASK] = { .type = NLA_U32 }, + [CTA_LABELS] = { .type = NLA_BINARY, + .len = __CTA_LABELS_MAX_LENGTH }, + [CTA_LABELS_MASK] = { .type = NLA_BINARY, + .len = __CTA_LABELS_MAX_LENGTH }, }; static int @@ -1504,6 +1509,31 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, } #endif +static int +ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[]) +{ +#ifdef CONFIG_NF_CONNTRACK_LABELS + size_t len = nla_len(cda[CTA_LABELS]); + const void *mask = cda[CTA_LABELS_MASK]; + + if (len & (sizeof(u32)-1)) /* must be multiple of u32 */ + return -EINVAL; + + if (mask) { + if (nla_len(cda[CTA_LABELS_MASK]) == 0 || + nla_len(cda[CTA_LABELS_MASK]) != len) + return -EINVAL; + mask = nla_data(cda[CTA_LABELS_MASK]); + } + + len /= sizeof(u32); + + return nf_connlabels_replace(ct, nla_data(cda[CTA_LABELS]), mask, len); +#else + return -EOPNOTSUPP; +#endif +} + static int ctnetlink_change_conntrack(struct nf_conn *ct, const struct nlattr * const cda[]) @@ -1550,6 +1580,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct, return err; } #endif + if (cda[CTA_LABELS]) { + err = ctnetlink_attach_labels(ct, cda); + if (err < 0) + return err; + } return 0; } @@ -1758,6 +1793,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, else events = IPCT_NEW; + if (cda[CTA_LABELS] && + ctnetlink_attach_labels(ct, cda) == 0) + events |= (1 << IPCT_LABEL); + nf_conntrack_eventmask_report((1 << IPCT_REPLY) | (1 << IPCT_ASSURED) | (1 << IPCT_HELPER) | @@ -2055,6 +2094,11 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) if (err < 0) return err; } + if (cda[CTA_LABELS]) { + err = ctnetlink_attach_labels(ct, cda); + if (err < 0) + return err; + } #if defined(CONFIG_NF_CONNTRACK_MARK) if (cda[CTA_MARK]) ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); -- cgit v1.2.3 From 5a406b0cdfa948c7d949b270374737b17ee1679f Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 28 Dec 2012 18:17:08 +0000 Subject: netfilter: nf_ct_snmp: add include file Prototype for nf_nat_snmp_hook is in nf_conntrack_snmp.h therefore it should be included to get type checking. Found by sparse. Signed-off-by: Stephen Hemminger Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_snmp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index 6e545e26289e..87b95a2c270c 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -16,6 +16,7 @@ #include #include #include +#include #define SNMP_PORT 161 -- cgit v1.2.3 From 7ff74a596b6aa47ccc71f6b9a26006af69d0e33f Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:02 +0000 Subject: ndisc: Update neigh->updated with write lock. neigh->nud_state and neigh->updated are under protection of neigh->lock. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 7c34c01b515b..1341f68e8009 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -499,22 +499,26 @@ static void rt6_probe(struct rt6_info *rt) * to no more than one per minute. */ neigh = rt ? rt->n : NULL; - if (!neigh || (neigh->nud_state & NUD_VALID)) + if (!neigh) + return; + write_lock_bh(&neigh->lock); + if (neigh->nud_state & NUD_VALID) { + write_unlock_bh(&neigh->lock); return; - read_lock_bh(&neigh->lock); + } if (!(neigh->nud_state & NUD_VALID) && time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { struct in6_addr mcaddr; struct in6_addr *target; neigh->updated = jiffies; - read_unlock_bh(&neigh->lock); + write_unlock_bh(&neigh->lock); target = (struct in6_addr *)&neigh->primary_key; addrconf_addr_solict_mult(target, &mcaddr); ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); } else { - read_unlock_bh(&neigh->lock); + write_unlock_bh(&neigh->lock); } } #else -- cgit v1.2.3 From 8e022ee63f344e0dd12e60f5cdbacabcd312c4c7 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:09 +0000 Subject: ndisc: Remove tbl argument for __ipv6_neigh_lookup(). We can refer to nd_tbl directly. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ndisc.h | 4 ++-- net/ipv6/route.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 23b3a7c58783..bbc938edf41b 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -148,7 +148,7 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _ (p32[3] * hash_rnd[3])); } -static inline struct neighbour *__ipv6_neigh_lookup(struct neigh_table *tbl, struct net_device *dev, const void *pkey) +static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, const void *pkey) { struct neigh_hash_table *nht; const u32 *p32 = pkey; @@ -156,7 +156,7 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct neigh_table *tbl, str u32 hash_val; rcu_read_lock_bh(); - nht = rcu_dereference_bh(tbl->nht); + nht = rcu_dereference_bh(nd_tbl.nht); hash_val = ndisc_hashfn(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 1341f68e8009..5d9ca274d149 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -145,7 +145,7 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, struct neighbour *n; daddr = choose_neigh_daddr(rt, skb, daddr); - n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); + n = __ipv6_neigh_lookup(dst->dev, daddr); if (n) return n; return neigh_create(&nd_tbl, daddr, dst->dev); @@ -153,7 +153,7 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) { - struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway); + struct neighbour *n = __ipv6_neigh_lookup(dev, &rt->rt6i_gateway); if (!n) { n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev); if (IS_ERR(n)) -- cgit v1.2.3 From dd0cbf29b12bf876abf2880bcc0db8983537b87a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:15 +0000 Subject: ipv6 route: Dump gateway based on RTF_GATEWAY flag and rt->rt6i_gateway. Do not depend on rt->n. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5d9ca274d149..d82eb1f24b25 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2484,7 +2484,6 @@ static int rt6_fill_node(struct net *net, struct nlmsghdr *nlh; long expires; u32 table; - struct neighbour *n; if (prefix) { /* user wants prefix routes only */ if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { @@ -2597,9 +2596,8 @@ static int rt6_fill_node(struct net *net, if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) goto nla_put_failure; - n = rt->n; - if (n) { - if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) + if (rt->rt6i_flags & RTF_GATEWAY) { + if (nla_put(skb, RTA_GATEWAY, 16, &rt->rt6i_gateway) < 0) goto nla_put_failure; } @@ -2794,7 +2792,6 @@ struct rt6_proc_arg static int rt6_info_route(struct rt6_info *rt, void *p_arg) { struct seq_file *m = p_arg; - struct neighbour *n; seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); @@ -2803,9 +2800,8 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg) #else seq_puts(m, "00000000000000000000000000000000 00 "); #endif - n = rt->n; - if (n) { - seq_printf(m, "%pi6", n->primary_key); + if (rt->rt6i_flags & RTF_GATEWAY) { + seq_printf(m, "%pi6", &rt->rt6i_gateway); } else { seq_puts(m, "00000000000000000000000000000000"); } -- cgit v1.2.3 From c440f1609b6516029ac5286e00fb86b3a11acd8e Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:32 +0000 Subject: ipv6: Do not depend on rt->n in ip6_pol_route(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d82eb1f24b25..2839381116ea 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -925,7 +925,7 @@ restart: dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY))) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr); -- cgit v1.2.3 From 145a36217adf91cdc6922c5b9cc985b87f8e0c73 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:38 +0000 Subject: ipv6: Do not depend on rt->n in rt6_check_neigh(). CC: Cong Wang Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 2839381116ea..c7bcd777db23 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -546,20 +546,24 @@ static inline bool rt6_check_neigh(struct rt6_info *rt) struct neighbour *neigh; bool ret = false; - neigh = rt->n; if (rt->rt6i_flags & RTF_NONEXTHOP || !(rt->rt6i_flags & RTF_GATEWAY)) - ret = true; - else if (neigh) { - read_lock_bh(&neigh->lock); + return true; + + rcu_read_lock_bh(); + neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); + if (neigh) { + read_lock(&neigh->lock); if (neigh->nud_state & NUD_VALID) ret = true; #ifdef CONFIG_IPV6_ROUTER_PREF else if (!(neigh->nud_state & NUD_FAILED)) ret = true; #endif - read_unlock_bh(&neigh->lock); + read_unlock(&neigh->lock); } + rcu_read_unlock_bh(); + return ret; } -- cgit v1.2.3 From 2152caea719657579daa3a1a57a5425a3db6d612 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:43 +0000 Subject: ipv6: Do not depend on rt->n in rt6_probe(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c7bcd777db23..afc8386e3d9f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -498,28 +498,34 @@ static void rt6_probe(struct rt6_info *rt) * Router Reachability Probe MUST be rate-limited * to no more than one per minute. */ - neigh = rt ? rt->n : NULL; - if (!neigh) - return; - write_lock_bh(&neigh->lock); - if (neigh->nud_state & NUD_VALID) { - write_unlock_bh(&neigh->lock); + if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) return; + rcu_read_lock_bh(); + neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); + if (neigh) { + write_lock(&neigh->lock); + if (neigh->nud_state & NUD_VALID) + goto out; } - if (!(neigh->nud_state & NUD_VALID) && + + if (!neigh || time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { struct in6_addr mcaddr; struct in6_addr *target; neigh->updated = jiffies; - write_unlock_bh(&neigh->lock); - target = (struct in6_addr *)&neigh->primary_key; + if (neigh) + write_unlock(&neigh->lock); + + target = (struct in6_addr *)&rt->rt6i_gateway; addrconf_addr_solict_mult(target, &mcaddr); ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); } else { - write_unlock_bh(&neigh->lock); +out: + write_unlock(&neigh->lock); } + rcu_read_unlock_bh(); } #else static inline void rt6_probe(struct rt6_info *rt) -- cgit v1.2.3 From 707be1ff3d9309f810e33790e6ae3f15e7ac967e Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:53:55 +0000 Subject: ipv6: Do not depend on rt->n in ip6_dst_lookup_tail(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 9250c696de9d..7c00edb343a0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -913,8 +913,12 @@ static int ip6_dst_lookup_tail(struct sock *sk, * dst entry of the nexthop router */ rt = (struct rt6_info *) *dst; - n = rt->n; - if (n && !(n->nud_state & NUD_VALID)) { + rcu_read_lock_bh(); + n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr)); + err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0; + rcu_read_unlock_bh(); + + if (err) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6; int redirect; -- cgit v1.2.3 From 6fd6ce2056de27090f4723138e0ce0a816f4ba0d Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:54:00 +0000 Subject: ipv6: Do not depend on rt->n in ip6_finish_output2(). If neigh is not found, create new one. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7c00edb343a0..b0895f5d5fc6 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -88,7 +88,8 @@ static int ip6_finish_output2(struct sk_buff *skb) struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; struct neighbour *neigh; - struct rt6_info *rt; + struct in6_addr *nexthop; + int ret; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -123,10 +124,17 @@ static int ip6_finish_output2(struct sk_buff *skb) skb->len); } - rt = (struct rt6_info *) dst; - neigh = rt->n; - if (neigh) - return dst_neigh_output(dst, neigh, skb); + rcu_read_lock_bh(); + nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr); + neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop); + if (unlikely(!neigh)) + neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false); + if (!IS_ERR(neigh)) { + ret = dst_neigh_output(dst, neigh, skb); + rcu_read_unlock_bh(); + return ret; + } + rcu_read_unlock_bh(); IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); -- cgit v1.2.3 From 887c95cc1da53f66a5890fdeab13414613010097 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 17 Jan 2013 12:54:05 +0000 Subject: ipv6: Complete neighbour entry removal from dst_entry. CC: Cong Wang Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 -- net/ipv6/route.c | 84 +------------------------------------------------ net/ipv6/xfrm6_policy.c | 1 - 3 files changed, 1 insertion(+), 86 deletions(-) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index fdc48a94a063..6919a501f99e 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -89,8 +89,6 @@ struct fib6_table; struct rt6_info { struct dst_entry dst; - struct neighbour *n; - /* * Tail elements of dst_entry (__refcnt etc.) * and these elements (rarely used in hot path) are in diff --git a/net/ipv6/route.c b/net/ipv6/route.c index afc8386e3d9f..3a562a103312 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -151,19 +151,6 @@ static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, return neigh_create(&nd_tbl, daddr, dst->dev); } -static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) -{ - struct neighbour *n = __ipv6_neigh_lookup(dev, &rt->rt6i_gateway); - if (!n) { - n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev); - if (IS_ERR(n)) - return PTR_ERR(n); - } - rt->n = n; - - return 0; -} - static struct dst_ops ip6_dst_ops_template = { .family = AF_INET6, .protocol = cpu_to_be16(ETH_P_IPV6), @@ -301,9 +288,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; - if (rt->n) - neigh_release(rt->n); - if (!(rt->dst.flags & DST_HOST)) dst_destroy_metrics_generic(dst); @@ -354,11 +338,6 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, in6_dev_put(idev); } } - if (rt->n && rt->n->dev == dev) { - rt->n->dev = loopback_dev; - dev_hold(loopback_dev); - dev_put(dev); - } } } @@ -845,8 +824,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, rt = ip6_rt_copy(ort, daddr); if (rt) { - int attempts = !in_softirq(); - if (!(rt->rt6i_flags & RTF_GATEWAY)) { if (ort->rt6i_dst.plen != 128 && ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) @@ -862,32 +839,6 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, rt->rt6i_src.plen = 128; } #endif - - retry: - if (rt6_bind_neighbour(rt, rt->dst.dev)) { - struct net *net = dev_net(rt->dst.dev); - int saved_rt_min_interval = - net->ipv6.sysctl.ip6_rt_gc_min_interval; - int saved_rt_elasticity = - net->ipv6.sysctl.ip6_rt_gc_elasticity; - - if (attempts-- > 0) { - net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; - net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; - - ip6_dst_gc(&net->ipv6.ip6_dst_ops); - - net->ipv6.sysctl.ip6_rt_gc_elasticity = - saved_rt_elasticity; - net->ipv6.sysctl.ip6_rt_gc_min_interval = - saved_rt_min_interval; - goto retry; - } - - net_warn_ratelimited("Neighbour table overflow\n"); - dst_free(&rt->dst); - return NULL; - } } return rt; @@ -898,10 +849,8 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, { struct rt6_info *rt = ip6_rt_copy(ort, daddr); - if (rt) { + if (rt) rt->rt6i_flags |= RTF_CACHE; - rt->n = neigh_clone(ort->n); - } return rt; } @@ -1272,20 +1221,8 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, goto out; } - if (neigh) - neigh_hold(neigh); - else { - neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); - if (IS_ERR(neigh)) { - in6_dev_put(idev); - dst_free(&rt->dst); - return ERR_CAST(neigh); - } - } - rt->dst.flags |= DST_HOST; rt->dst.output = ip6_output; - rt->n = neigh; atomic_set(&rt->dst.__refcnt, 1); rt->rt6i_dst.addr = fl6->daddr; rt->rt6i_dst.plen = 128; @@ -1594,12 +1531,6 @@ int ip6_route_add(struct fib6_config *cfg) } else rt->rt6i_prefsrc.plen = 0; - if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { - err = rt6_bind_neighbour(rt, dev); - if (err) - goto out; - } - rt->rt6i_flags = cfg->fc_flags; install_route: @@ -1713,7 +1644,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu struct netevent_redirect netevent; struct rt6_info *rt, *nrt = NULL; struct ndisc_options ndopts; - struct neighbour *old_neigh; struct inet6_dev *in6_dev; struct neighbour *neigh; struct rd_msg *msg; @@ -1786,11 +1716,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu if (!neigh) return; - /* Duplicate redirect: silently ignore. */ - old_neigh = rt->n; - if (neigh == old_neigh) - goto out; - /* * We have finally decided to accept it. */ @@ -1811,7 +1736,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu nrt->rt6i_flags &= ~RTF_GATEWAY; nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; - nrt->n = neigh_clone(neigh); if (ip6_ins_rt(nrt)) goto out; @@ -2125,7 +2049,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, { struct net *net = dev_net(idev->dev); struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL); - int err; if (!rt) { net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n"); @@ -2144,11 +2067,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, rt->rt6i_flags |= RTF_ANYCAST; else rt->rt6i_flags |= RTF_LOCAL; - err = rt6_bind_neighbour(rt, rt->dst.dev); - if (err) { - dst_free(&rt->dst); - return ERR_PTR(err); - } rt->rt6i_dst.addr = *addr; rt->rt6i_dst.plen = 128; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c9844135c9ca..128273744332 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -110,7 +110,6 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ - xdst->u.rt6.n = neigh_clone(rt->n); xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric; -- cgit v1.2.3 From 6ead1bbc381a674c20f227dbe6f3a8c6f67ce7a2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:50 +0200 Subject: Bluetooth: Add a new workqueue for hci_request operations The hci_request function is blocking and cannot be called through the usual per-HCI device workqueue (hdev->workqueue). While hci_request is in progress any other work from the queue, including sending HCI commands to the controller would be blocked and eventually cause the hci_request call to time out. This patch adds a second workqueue to be used by operations needing hci_request and thereby avoiding issues with blocking other workqueue users. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 014a2eaa5389..769a740c104c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -216,6 +216,7 @@ struct hci_dev { unsigned long le_last_tx; struct workqueue_struct *workqueue; + struct workqueue_struct *req_workqueue; struct work_struct power_on; struct delayed_work power_off; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 596660d37c5e..f73907aad79f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1799,6 +1799,15 @@ int hci_register_dev(struct hci_dev *hdev) goto err; } + hdev->req_workqueue = alloc_workqueue(hdev->name, + WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1); + if (!hdev->req_workqueue) { + destroy_workqueue(hdev->workqueue); + error = -ENOMEM; + goto err; + } + error = hci_add_sysfs(hdev); if (error < 0) goto err_wqueue; @@ -1827,6 +1836,7 @@ int hci_register_dev(struct hci_dev *hdev) err_wqueue: destroy_workqueue(hdev->workqueue); + destroy_workqueue(hdev->req_workqueue); err: ida_simple_remove(&hci_index_ida, hdev->id); write_lock(&hci_dev_list_lock); @@ -1880,6 +1890,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_del_sysfs(hdev); destroy_workqueue(hdev->workqueue); + destroy_workqueue(hdev->req_workqueue); hci_dev_lock(hdev); hci_blacklist_clear(hdev); -- cgit v1.2.3 From 1920257316615676387794cc5fb838183b3bae7f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:51 +0200 Subject: Bluetooth: Use req_workqueue for hci_request operations This patch converts work assignment relying on hci_request() from the system-global work queue to the per-HCI device specific work queue (hdev->req_workqueue) intended for hci_request() related tasks. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 5 +++-- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f73907aad79f..545553b82295 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work) return; if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT); + queue_delayed_work(hdev->req_workqueue, &hdev->power_off, + HCI_AUTO_OFF_TIMEOUT); if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); @@ -1830,7 +1831,7 @@ int hci_register_dev(struct hci_dev *hdev) hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); - schedule_work(&hdev->power_on); + queue_work(hdev->req_workqueue, &hdev->power_on); return id; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 37add53ce613..54114ff4090f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -812,9 +812,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, } if (cp->val) - schedule_work(&hdev->power_on); + queue_work(hdev->req_workqueue, &hdev->power_on); else - schedule_work(&hdev->power_off.work); + queue_work(hdev->req_workqueue, &hdev->power_off.work); err = 0; -- cgit v1.2.3 From 46818ed514102c8d251d4aff5c99ad3ff6805432 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jan 2013 22:33:52 +0200 Subject: Bluetooth: Fix using system-global workqueue when not necessary There's a per-HCI device workqueue (hdev->workqueue) that should be used for general per-HCI device work (except hdev->req_workqueue that's for hci_request() related work). This patch fixes places using the system-global work queue and makes them use the hdev->workqueue instead. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 545553b82295..e061b354d2c4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1622,8 +1622,8 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, if (err < 0) return err; - schedule_delayed_work(&hdev->le_scan_disable, - msecs_to_jiffies(timeout)); + queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, + msecs_to_jiffies(timeout)); return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 54114ff4090f..fc171f222ba4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1361,7 +1361,8 @@ static bool enable_service_cache(struct hci_dev *hdev) return false; if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT); + queue_delayed_work(hdev->workqueue, &hdev->service_cache, + CACHE_TIMEOUT); return true; } -- cgit v1.2.3 From de5fad815703b5b24bc4726cd71422929537d259 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Wed, 30 May 2012 11:36:39 +0300 Subject: mac80211: add op to configure default key id There are hardwares which support offload of data packets for example when auto ARP is enabled the hw will send the ARP response. In such cases if WEP encryption is configured the hw must know the default WEP key in order to encrypt the packets correctly. When hw_accel is enabled and encryption type is set to WEP, the driver should get the default key index from mac80211. Signed-off-by: Yoni Divinsky [cleanups, fixes, documentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 10 ++++++++++ net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/key.c | 5 ++++- net/mac80211/trace.h | 23 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3037f49e51c8..e0825a9dbfea 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1630,6 +1630,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is * provided by update_tkip_key only. The trigger that makes mac80211 call this * handler is software decryption with wrap around of iv16. + * + * The set_default_unicast_key() call updates the default WEP key index + * configured to the hardware for WEP encryption type. This is required + * for devices that support offload of data packets (e.g. ARP responses). */ /** @@ -2208,6 +2212,10 @@ enum ieee80211_rate_control_changed { * After rekeying was done it should (for example during resume) notify * userspace of the new replay counter using ieee80211_gtk_rekey_notify(). * + * @set_default_unicast_key: Set the default (unicast) key index, useful for + * WEP when the device sends data packets autonomously, e.g. for ARP + * offloading. The index can be 0-3, or -1 for unsetting it. + * * @hw_scan: Ask the hardware to service the scan request, no need to start * the scan state machine in stack. The scan must honour the channel * configuration done by the regulatory agent in the wiphy's @@ -2539,6 +2547,8 @@ struct ieee80211_ops { void (*set_rekey_data)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_gtk_rekey_data *data); + void (*set_default_unicast_key)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int idx); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req); void (*cancel_hw_scan)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0c07f94c5378..e6033b06caba 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1020,4 +1020,20 @@ static inline void drv_restart_complete(struct ieee80211_local *local) trace_drv_return_void(local); } +static inline void +drv_set_default_unicast_key(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int key_idx) +{ + check_sdata_in_driver(sdata); + + WARN_ON_ONCE(key_idx < -1 || key_idx > 3); + + trace_drv_set_default_unicast_key(local, sdata, key_idx); + if (local->ops->set_default_unicast_key) + local->ops->set_default_unicast_key(&local->hw, &sdata->vif, + key_idx); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 619c5d697999..ef252eb58c36 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -204,8 +204,11 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, if (idx >= 0 && idx < NUM_DEFAULT_KEYS) key = key_mtx_dereference(sdata->local, sdata->keys[idx]); - if (uni) + if (uni) { rcu_assign_pointer(sdata->default_unicast_key, key); + drv_set_default_unicast_key(sdata->local, sdata, idx); + } + if (multi) rcu_assign_pointer(sdata->default_multicast_key, key); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 41861b91daa3..9341b9359b66 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1821,6 +1821,29 @@ TRACE_EVENT(stop_queue, ) ); +TRACE_EVENT(drv_set_default_unicast_key, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int key_idx), + + TP_ARGS(local, sdata, key_idx), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(int, key_idx) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->key_idx = key_idx; + ), + + TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg -- cgit v1.2.3 From 0c446212c4f68e51ba03a9b172599437d6660a9d Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Wed, 16 Jan 2013 19:09:47 +0000 Subject: 6lowpan: Refactor packet delivery into a function Refactor the handing of the skb's to the individual lowpan devices into a function. Signed-off-by: Alan Ott Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index f651da60f161..1714cfab1220 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -594,10 +594,32 @@ static int lowpan_header_create(struct sk_buff *skb, } } +static int lowpan_give_skb_to_devices(struct sk_buff *skb) +{ + struct lowpan_dev_record *entry; + struct sk_buff *skb_cp; + int stat = NET_RX_SUCCESS; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &lowpan_devices, list) + if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { + skb_cp = skb_copy(skb, GFP_ATOMIC); + if (!skb_cp) { + stat = -ENOMEM; + break; + } + + skb_cp->dev = entry->ldev; + stat = netif_rx(skb_cp); + } + rcu_read_unlock(); + + return stat; +} + static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr) { struct sk_buff *new; - struct lowpan_dev_record *entry; int stat = NET_RX_SUCCESS; new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), @@ -614,19 +636,7 @@ static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr) new->protocol = htons(ETH_P_IPV6); new->pkt_type = PACKET_HOST; - rcu_read_lock(); - list_for_each_entry_rcu(entry, &lowpan_devices, list) - if (lowpan_dev_info(entry->ldev)->real_dev == new->dev) { - skb = skb_copy(new, GFP_ATOMIC); - if (!skb) { - stat = -ENOMEM; - break; - } - - skb->dev = entry->ldev; - stat = netif_rx(skb); - } - rcu_read_unlock(); + stat = lowpan_give_skb_to_devices(new); kfree_skb(new); -- cgit v1.2.3 From ee21c7e0d1f6e742e9f441f580a3bac316a39a21 Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Wed, 16 Jan 2013 19:09:48 +0000 Subject: 6lowpan: Handle uncompressed IPv6 packets over 6LoWPAN Handle the reception of uncompressed packets (dispatch type = IPv6). Signed-off-by: Alan Ott Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 1714cfab1220..09cba81d2c4a 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1147,19 +1147,42 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, goto drop; /* check that it's our buffer */ - switch (skb->data[0] & 0xe0) { - case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ - case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ - local_skb = skb_clone(skb, GFP_ATOMIC); + if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { + /* Copy the packet so that the IPv6 header is + * properly aligned. + */ + local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1, + skb_tailroom(skb), GFP_ATOMIC); if (!local_skb) goto drop; - lowpan_process_data(local_skb); + local_skb->protocol = htons(ETH_P_IPV6); + local_skb->pkt_type = PACKET_HOST; + + /* Pull off the 1-byte of 6lowpan header. */ + skb_pull(local_skb, 1); + skb_reset_network_header(local_skb); + skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); + + lowpan_give_skb_to_devices(local_skb); + + kfree_skb(local_skb); kfree_skb(skb); - break; - default: - break; + } else { + switch (skb->data[0] & 0xe0) { + case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ + case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ + case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ + local_skb = skb_clone(skb, GFP_ATOMIC); + if (!local_skb) + goto drop; + lowpan_process_data(local_skb); + + kfree_skb(skb); + break; + default: + break; + } } return NET_RX_SUCCESS; -- cgit v1.2.3 From 12fd84f4383b15b0a12cfd50b7c527cd55d6f101 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Fri, 18 Jan 2013 02:00:24 +0000 Subject: ipv6: Remove unused neigh argument for icmp6_dst_alloc() and its callers. Because of rt->n removal, we do not need neigh argument any more. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ip6_route.h | 1 - net/ipv6/mcast.c | 4 ++-- net/ipv6/ndisc.c | 15 +++++---------- net/ipv6/route.c | 1 - 4 files changed, 7 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 30cbb15f6279..260f83f16bcf 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -103,7 +103,6 @@ extern struct rt6_info *rt6_lookup(struct net *net, int oif, int flags); extern struct dst_entry *icmp6_dst_alloc(struct net_device *dev, - struct neighbour *neigh, struct flowi6 *fl6); extern int icmp6_dst_gc(void); diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 8237ee15eafd..587a84530a57 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1391,7 +1391,7 @@ static void mld_sendpack(struct sk_buff *skb) icmpv6_flow_init(net->ipv6.igmp_sk, &fl6, ICMPV6_MLD2_REPORT, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - dst = icmp6_dst_alloc(skb->dev, NULL, &fl6); + dst = icmp6_dst_alloc(skb->dev, &fl6); err = 0; if (IS_ERR(dst)) { @@ -1759,7 +1759,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) icmpv6_flow_init(sk, &fl6, type, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - dst = icmp6_dst_alloc(skb->dev, NULL, &fl6); + dst = icmp6_dst_alloc(skb->dev, &fl6); if (IS_ERR(dst)) { err = PTR_ERR(dst); goto err_out; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 5733cd27f4a3..429622d74705 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -432,7 +432,6 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, } static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, - struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *saddr, struct icmp6hdr *icmp6h) @@ -448,7 +447,7 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, type = icmp6h->icmp6_type; icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); - dst = icmp6_dst_alloc(dev, neigh, &fl6); + dst = icmp6_dst_alloc(dev, &fl6); if (IS_ERR(dst)) { kfree_skb(skb); return; @@ -474,7 +473,6 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, * Send a Neighbour Discover packet */ static void __ndisc_send(struct net_device *dev, - struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *saddr, struct icmp6hdr *icmp6h, const struct in6_addr *target, @@ -486,7 +484,7 @@ static void __ndisc_send(struct net_device *dev, if (!skb) return; - ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h); + ndisc_send_skb(skb, dev, daddr, saddr, icmp6h); } static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, @@ -521,8 +519,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, icmp6h.icmp6_solicited = solicited; icmp6h.icmp6_override = override; - __ndisc_send(dev, neigh, daddr, src_addr, - &icmp6h, solicited_addr, + __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr, inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); } @@ -563,8 +560,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, saddr = &addr_buf; } - __ndisc_send(dev, neigh, daddr, saddr, - &icmp6h, solicit, + __ndisc_send(dev, daddr, saddr, &icmp6h, solicit, !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); } @@ -598,8 +594,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, } } #endif - __ndisc_send(dev, NULL, daddr, saddr, - &icmp6h, NULL, + __ndisc_send(dev, daddr, saddr, &icmp6h, NULL, send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 3a562a103312..2d94d5a7a051 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1203,7 +1203,6 @@ static struct dst_entry *icmp6_dst_gc_list; static DEFINE_SPINLOCK(icmp6_dst_lock); struct dst_entry *icmp6_dst_alloc(struct net_device *dev, - struct neighbour *neigh, struct flowi6 *fl6) { struct dst_entry *dst; -- cgit v1.2.3 From 115b0aa6b444e8dd89b7f67b77b8c472763fbc1a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Fri, 18 Jan 2013 02:05:03 +0000 Subject: ndisc: Check NS message length before access. Check message length before accessing "target" field, as we do for other types. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 429622d74705..350f86005c13 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -671,6 +671,11 @@ static void ndisc_recv_ns(struct sk_buff *skb) bool inc; int is_router = -1; + if (skb->len < sizeof(struct nd_msg)) { + ND_PRINTK(2, warn, "NS: packet too short\n"); + return; + } + if (ipv6_addr_is_multicast(&msg->target)) { ND_PRINTK(2, warn, "NS: multicast target address\n"); return; -- cgit v1.2.3 From 1ad759d8479b4b28f2a6c874d380066cf987b341 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Fri, 18 Jan 2013 09:18:17 +0000 Subject: ipv6: remove unneeded check to pskb_may_pull in ipip6_rcv This is already checked by the caller (tunnel64_rcv) and brings ipip6_rcv in line with ipip_rcv. Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/sit.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index cfba99b2c2a4..98fe53694a65 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -592,15 +592,10 @@ out: static int ipip6_rcv(struct sk_buff *skb) { - const struct iphdr *iph; + const struct iphdr *iph = ip_hdr(skb); struct ip_tunnel *tunnel; int err; - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) - goto out; - - iph = ip_hdr(skb); - tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, iph->saddr, iph->daddr); if (tunnel != NULL) { -- cgit v1.2.3 From 0f19b41e223d787251c59137e61fc5145c13d1c4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Jan 2013 16:39:07 +0100 Subject: mac80211: remove ARP filter enable/disable logic Depending on the driver, having ARP filtering for some addresses may be possible. Remove the logic that tracks whether ARP filter is enabled or not and give the driver the total number of addresses instead of the length of the list so it can make its own decision. Reviewed-by: Luciano Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 5 ++--- drivers/net/wireless/ti/wlcore/main.c | 3 +-- include/net/mac80211.h | 11 ++++------- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/iface.c | 3 --- net/mac80211/main.c | 16 ++++------------ net/mac80211/mlme.c | 8 ++------ net/mac80211/trace.h | 13 +++++++++---- 8 files changed, 22 insertions(+), 39 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index f0fc8cd4d5df..e4b42f7e659c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -539,9 +539,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ARP_FILTER) { /* Hardware ARP filter address list or state changed */ - brcms_err(core, "%s: arp filtering: enabled %s, count %d" - " (implement)\n", __func__, info->arp_filter_enabled ? - "true" : "false", info->arp_addr_cnt); + brcms_err(core, "%s: arp filtering: %d addresses" + " (implement)\n", __func__, info->arp_addr_cnt); } if (changed & BSS_CHANGED_QOS) { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ce6e62a37e14..919ad70cc520 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4113,8 +4113,7 @@ sta_not_found: wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); - if (bss_conf->arp_addr_cnt == 1 && - bss_conf->arp_filter_enabled) { + if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) { wlvif->ip_addr = addr; /* * The template should have been configured only upon diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e0825a9dbfea..679ad4bb222b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -297,11 +297,9 @@ enum ieee80211_rssi_event { * may filter ARP queries targeted for other addresses than listed here. * The driver must allow ARP queries targeted for all address listed here * to pass through. An empty list implies no ARP queries need to pass. - * @arp_addr_cnt: Number of addresses currently on the list. - * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may - * filter ARP queries based on the @arp_addr_list, if disabled, the - * hardware must not perform any ARP filtering. Note, that the filter will - * be enabled also in promiscuous mode. + * @arp_addr_cnt: Number of addresses currently on the list. Note that this + * may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list + * array size), it's up to the driver what to do in that case. * @qos: This is a QoS-enabled BSS. * @idle: This interface is idle. There's also a global idle flag in the * hardware config which may be more appropriate depending on what @@ -338,8 +336,7 @@ struct ieee80211_bss_conf { u32 cqm_rssi_hyst; struct cfg80211_chan_def chandef; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; - u8 arp_addr_cnt; - bool arp_filter_enabled; + int arp_addr_cnt; bool qos; bool idle; bool ps; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0fa44a965ad9..b1fed5491ed6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -747,8 +747,6 @@ struct ieee80211_sub_if_data { struct work_struct work; struct sk_buff_head skb_queue; - bool arp_filter_state; - u8 needed_rx_chains; enum ieee80211_smps_mode smps_mode; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 06fac2991d40..0a36dc6346bb 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1574,9 +1574,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, /* initialise type-independent data */ sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; -#ifdef CONFIG_INET - sdata->arp_filter_state = true; -#endif for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) skb_queue_head_init(&sdata->fragments[i].skb_list); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 39cfe8f10ad2..baf9720c1876 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -349,27 +349,19 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, /* Copy the addresses to the bss_conf list */ ifa = idev->ifa_list; - while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) { - bss_conf->arp_addr_list[c] = ifa->ifa_address; + while (ifa) { + if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN) + bss_conf->arp_addr_list[c] = ifa->ifa_address; ifa = ifa->ifa_next; c++; } - /* If not all addresses fit the list, disable filtering */ - if (ifa) { - sdata->arp_filter_state = false; - c = 0; - } else { - sdata->arp_filter_state = true; - } bss_conf->arp_addr_cnt = c; /* Configure driver only if associated (which also implies it is up) */ - if (ifmgd->associated) { - bss_conf->arp_filter_enabled = sdata->arp_filter_state; + if (ifmgd->associated) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_ARP_FILTER); - } mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e930175771ff..2d9ef20cd38d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1465,10 +1465,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, bss_info_changed |= BSS_CHANGED_CQM; /* Enable ARP filtering */ - if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) { - bss_conf->arp_filter_enabled = sdata->arp_filter_state; + if (bss_conf->arp_addr_cnt) bss_info_changed |= BSS_CHANGED_ARP_FILTER; - } ieee80211_bss_info_change_notify(sdata, bss_info_changed); @@ -1582,10 +1580,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&local->dynamic_ps_enable_work); /* Disable ARP filtering */ - if (sdata->vif.bss_conf.arp_filter_enabled) { - sdata->vif.bss_conf.arp_filter_enabled = false; + if (sdata->vif.bss_conf.arp_addr_cnt) changed |= BSS_CHANGED_ARP_FILTER; - } sdata->vif.bss_conf.qos = false; changed |= BSS_CHANGED_QOS; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 9341b9359b66..e9f95913c6f0 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -347,8 +347,11 @@ TRACE_EVENT(drv_bss_info_changed, __field(s32, cqm_rssi_hyst); __field(u32, channel_width); __field(u32, channel_cfreq1); - __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt); - __field(bool, arp_filter_enabled); + __dynamic_array(u32, arp_addr_list, + info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? + IEEE80211_BSS_ARP_ADDR_LIST_LEN : + info->arp_addr_cnt); + __field(int, arp_addr_cnt); __field(bool, qos); __field(bool, idle); __field(bool, ps); @@ -384,9 +387,11 @@ TRACE_EVENT(drv_bss_info_changed, __entry->cqm_rssi_hyst = info->cqm_rssi_hyst; __entry->channel_width = info->chandef.width; __entry->channel_cfreq1 = info->chandef.center_freq1; + __entry->arp_addr_cnt = info->arp_addr_cnt; memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list, - sizeof(u32) * info->arp_addr_cnt); - __entry->arp_filter_enabled = info->arp_filter_enabled; + sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ? + IEEE80211_BSS_ARP_ADDR_LIST_LEN : + info->arp_addr_cnt)); __entry->qos = info->qos; __entry->idle = info->idle; __entry->ps = info->ps; -- cgit v1.2.3 From 0a214d3f7e5041685ebff054628a4354bedd78c3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 Jan 2013 10:39:44 +0100 Subject: mac80211: improve aggregation debug messages A lot of the aggregation messages don't indicate the station so they're hard to understand if there are multiple sessions in progress. Make that easier by adding the MAC address to most messages. Also add the TID if it wasn't already there. Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 14 +++++++++----- net/mac80211/agg-tx.c | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 808338a1bce5..31bf2586fb84 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -83,8 +83,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, &sta->sta, tid, NULL, 0)) sdata_info(sta->sdata, - "HW problem - can not stop rx aggregation for tid %d\n", - tid); + "HW problem - can not stop rx aggregation for %pM tid %d\n", + sta->sta.addr, tid); /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) @@ -159,7 +159,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) } rcu_read_unlock(); - ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n", + sta->sta.addr, (u16)*ptid); set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); @@ -247,7 +248,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { - ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n"); + ht_dbg(sta->sdata, + "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", + sta->sta.addr, tid); goto end_no_lock; } @@ -317,7 +320,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); - ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret); + ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", + sta->sta.addr, tid, ret); if (ret) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 2f0ccbc5f13e..e90911819ed4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -387,12 +387,13 @@ static void sta_addba_resp_timer_expired(unsigned long data) test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { rcu_read_unlock(); ht_dbg(sta->sdata, - "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", - tid); + "timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n", + sta->sta.addr, tid); return; } - ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid); + ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n", + sta->sta.addr, tid); ieee80211_stop_tx_ba_session(&sta->sta, tid); rcu_read_unlock(); @@ -429,7 +430,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) &sta->sta, tid, &start_seq_num, 0); if (ret) { ht_dbg(sdata, - "BA request denied - HW unavailable for tid %d\n", tid); + "BA request denied - HW unavailable for %pM tid %d\n", + sta->sta.addr, tid); spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sdata, tid_tx, tid); ieee80211_assign_tid_tx(sta, tid, NULL); @@ -442,7 +444,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); - ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid); + ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n", + sta->sta.addr, tid); spin_lock_bh(&sta->lock); sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; @@ -489,7 +492,8 @@ static void sta_tx_agg_session_timer_expired(unsigned long data) rcu_read_unlock(); - ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n", + sta->sta.addr, (u16)*ptid); ieee80211_stop_tx_ba_session(&sta->sta, *ptid); } @@ -525,7 +529,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sdata, - "BA sessions blocked - Denying BA session request\n"); + "BA sessions blocked - Denying BA session request %pM tid %d\n", + sta->sta.addr, tid); return -EINVAL; } @@ -566,8 +571,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + HT_AGG_RETRIES_PERIOD)) { ht_dbg(sdata, - "BA request denied - waiting a grace period after %d failed requests on tid %u\n", - sta->ampdu_mlme.addba_req_num[tid], tid); + "BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n", + sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid); ret = -EBUSY; goto err_unlock_sta; } @@ -576,8 +581,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { ht_dbg(sdata, - "BA request denied - session is not idle on tid %u\n", - tid); + "BA request denied - session is not idle on %pM tid %u\n", + sta->sta.addr, tid); ret = -EAGAIN; goto err_unlock_sta; } @@ -632,7 +637,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); - ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid); + ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n", + sta->sta.addr, tid); drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, @@ -802,7 +808,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { - ht_dbg(sdata, "unexpected callback to A-MPDU stop\n"); + ht_dbg(sdata, + "unexpected callback to A-MPDU stop for %pM tid %d\n", + sta->sta.addr, tid); goto unlock_sta; } @@ -861,13 +869,15 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, goto out; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { - ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid); + ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n", + sta->sta.addr, tid); goto out; } del_timer_sync(&tid_tx->addba_resp_timer); - ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid); + ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n", + sta->sta.addr, tid); /* * addba_resp_timer may have fired before we got here, and @@ -877,8 +887,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { ht_dbg(sta->sdata, - "got addBA resp for tid %d but we already gave up\n", - tid); + "got addBA resp for %pM tid %d but we already gave up\n", + sta->sta.addr, tid); goto out; } -- cgit v1.2.3 From a65240c1013222dbf41166c8b2c5ed2720c807c3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 Jan 2013 15:14:34 +0100 Subject: mac80211: allow drivers to access IPv6 information To be able to implement NS response offloading (in regular operation or while in WoWLAN) drivers need to know the IPv6 addresses assigned to interfaces. Implement an IPv6 notifier in mac80211 to call the driver when addresses change. Unlike for IPv4, implement it as a callback rather than as a list in the BSS configuration, that is more flexible. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 9 +++++++++ net/mac80211/driver-ops.h | 12 ++++++++++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/main.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/trace.h | 8 ++++++++ 5 files changed, 78 insertions(+) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 679ad4bb222b..ece5733d113d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2497,6 +2497,9 @@ enum ieee80211_rate_control_changed { * driver's resume function returned 1, as this is just like an "inline" * hardware restart. This callback may sleep. * + * @ipv6_addr_change: IPv6 address assignment on the given interface changed. + * Currently, this is only called for managed or P2P client interfaces. + * This callback is optional; it must not sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2672,6 +2675,12 @@ struct ieee80211_ops { struct ieee80211_chanctx_conf *ctx); void (*restart_complete)(struct ieee80211_hw *hw); + +#if IS_ENABLED(CONFIG_IPV6) + void (*ipv6_addr_change)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev); +#endif }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index e6033b06caba..d51afbd614d3 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1036,4 +1036,16 @@ drv_set_default_unicast_key(struct ieee80211_local *local, trace_drv_return_void(local); } +#if IS_ENABLED(CONFIG_IPV6) +static inline void drv_ipv6_addr_change(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct inet6_dev *idev) +{ + trace_drv_ipv6_addr_change(local, sdata); + if (local->ops->ipv6_addr_change) + local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev); + trace_drv_return_void(local); +} +#endif + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b1fed5491ed6..aec1b332aeb7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1127,6 +1127,7 @@ struct ieee80211_local { struct timer_list dynamic_ps_timer; struct notifier_block network_latency_notifier; struct notifier_block ifa_notifier; + struct notifier_block ifa6_notifier; /* * The dynamic ps timeout configured from user space via WEXT - diff --git a/net/mac80211/main.c b/net/mac80211/main.c index baf9720c1876..2bdd454e8bcf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "ieee80211_i.h" #include "driver-ops.h" @@ -369,6 +370,37 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, } #endif +#if IS_ENABLED(CONFIG_IPV6) +static int ieee80211_ifa6_changed(struct notifier_block *nb, + unsigned long data, void *arg) +{ + struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg; + struct inet6_dev *idev = ifa->idev; + struct net_device *ndev = ifa->idev->dev; + struct ieee80211_local *local = + container_of(nb, struct ieee80211_local, ifa6_notifier); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + struct ieee80211_sub_if_data *sdata; + + /* Make sure it's our interface that got changed */ + if (!wdev || wdev->wiphy != local->hw.wiphy) + return NOTIFY_DONE; + + sdata = IEEE80211_DEV_TO_SUB_IF(ndev); + + /* + * For now only support station mode. This is mostly because + * doing AP would have to handle AP_VLAN in some way ... + */ + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return NOTIFY_DONE; + + drv_ipv6_addr_change(local, sdata, idev); + + return NOTIFY_DONE; +} +#endif + static int ieee80211_napi_poll(struct napi_struct *napi, int budget) { struct ieee80211_local *local = @@ -977,12 +1009,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) goto fail_ifa; #endif +#if IS_ENABLED(CONFIG_IPV6) + local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; + result = register_inet6addr_notifier(&local->ifa6_notifier); + if (result) + goto fail_ifa6; +#endif + netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll, local->hw.napi_weight); return 0; +#if IS_ENABLED(CONFIG_IPV6) + fail_ifa6: #ifdef CONFIG_INET + unregister_inetaddr_notifier(&local->ifa_notifier); +#endif +#endif +#if defined(CONFIG_INET) || defined(CONFIG_IPV6) fail_ifa: pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, &local->network_latency_notifier); @@ -1018,6 +1063,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) #ifdef CONFIG_INET unregister_inetaddr_notifier(&local->ifa_notifier); #endif +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&local->ifa6_notifier); +#endif rtnl_lock(); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9f95913c6f0..2a2c2e20307d 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1437,6 +1437,14 @@ DEFINE_EVENT(local_only_evt, drv_restart_complete, TP_ARGS(local) ); +#if IS_ENABLED(CONFIG_IPV6) +DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); +#endif + /* * Tracing for API calls that drivers call. */ -- cgit v1.2.3 From 7241444209f88f804ea33483394a601c1afb1d64 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 25 Dec 2012 13:14:37 +0100 Subject: batman-adv: a delayed_work has to be initialised once A delayed_work struct does not need to be initialized each every time before being enqueued. Therefore the INIT_DELAYED_WORK() macro should be used during the initialization process only. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bridge_loop_avoidance.c | 18 ++++++------------ net/batman-adv/main.h | 2 ++ net/batman-adv/originator.c | 17 ++++++++--------- net/batman-adv/send.c | 5 +++-- net/batman-adv/translation-table.c | 14 +++++--------- net/batman-adv/vis.c | 20 ++++++++------------ 6 files changed, 32 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 5e834c1df469..bb5fbd64dc28 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1100,16 +1100,6 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv, } } - - -/* (re)start the timer */ -static void batadv_bla_start_timer(struct batadv_priv *bat_priv) -{ - INIT_DELAYED_WORK(&bat_priv->bla.work, batadv_bla_periodic_work); - queue_delayed_work(batadv_event_workqueue, &bat_priv->bla.work, - msecs_to_jiffies(BATADV_BLA_PERIOD_LENGTH)); -} - /* periodic work to do: * * purge structures when they are too old * * send announcements @@ -1180,7 +1170,8 @@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - batadv_bla_start_timer(bat_priv); + queue_delayed_work(batadv_event_workqueue, &bat_priv->bla.work, + msecs_to_jiffies(BATADV_BLA_PERIOD_LENGTH)); } /* The hash for claim and backbone hash receive the same key because they @@ -1238,7 +1229,10 @@ int batadv_bla_init(struct batadv_priv *bat_priv) batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla hashes initialized\n"); - batadv_bla_start_timer(bat_priv); + INIT_DELAYED_WORK(&bat_priv->bla.work, batadv_bla_periodic_work); + + queue_delayed_work(batadv_event_workqueue, &bat_priv->bla.work, + msecs_to_jiffies(BATADV_BLA_PERIOD_LENGTH)); return 0; } diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index d04b209a8295..af73d603455d 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -44,6 +44,8 @@ #define BATADV_TT_LOCAL_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */ #define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */ +#define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */ #define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ /* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index fa88b2bec986..92a55fddf44b 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -34,13 +34,6 @@ static struct lock_class_key batadv_orig_hash_lock_class_key; static void batadv_purge_orig(struct work_struct *work); -static void batadv_start_purge_timer(struct batadv_priv *bat_priv) -{ - INIT_DELAYED_WORK(&bat_priv->orig_work, batadv_purge_orig); - queue_delayed_work(batadv_event_workqueue, - &bat_priv->orig_work, msecs_to_jiffies(1000)); -} - /* returns 1 if they are the same originator */ static int batadv_compare_orig(const struct hlist_node *node, const void *data2) { @@ -63,7 +56,11 @@ int batadv_originator_init(struct batadv_priv *bat_priv) batadv_hash_set_lock_class(bat_priv->orig_hash, &batadv_orig_hash_lock_class_key); - batadv_start_purge_timer(bat_priv); + INIT_DELAYED_WORK(&bat_priv->orig_work, batadv_purge_orig); + queue_delayed_work(batadv_event_workqueue, + &bat_priv->orig_work, + msecs_to_jiffies(BATADV_ORIG_WORK_PERIOD)); + return 0; err: @@ -396,7 +393,9 @@ static void batadv_purge_orig(struct work_struct *work) delayed_work = container_of(work, struct delayed_work, work); bat_priv = container_of(delayed_work, struct batadv_priv, orig_work); _batadv_purge_orig(bat_priv); - batadv_start_purge_timer(bat_priv); + queue_delayed_work(batadv_event_workqueue, + &bat_priv->orig_work, + msecs_to_jiffies(BATADV_ORIG_WORK_PERIOD)); } void batadv_purge_orig_ref(struct batadv_priv *bat_priv) diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 89810cea0530..0b6f65bc4611 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -155,8 +155,6 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, spin_unlock_bh(&bat_priv->forw_bcast_list_lock); /* start timer for this packet */ - INIT_DELAYED_WORK(&forw_packet->delayed_work, - batadv_send_outstanding_bcast_packet); queue_delayed_work(batadv_event_workqueue, &forw_packet->delayed_work, send_time); } @@ -210,6 +208,9 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, /* how often did we send the bcast packet ? */ forw_packet->num_packets = 0; + INIT_DELAYED_WORK(&forw_packet->delayed_work, + batadv_send_outstanding_bcast_packet); + _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, delay); return NETDEV_TX_OK; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index d4b27b65d6e9..877acd149178 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -52,13 +52,6 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2) return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } -static void batadv_tt_start_timer(struct batadv_priv *bat_priv) -{ - INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge); - queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work, - msecs_to_jiffies(5000)); -} - static struct batadv_tt_common_entry * batadv_tt_hash_find(struct batadv_hashtable *hash, const void *data) { @@ -2136,7 +2129,9 @@ int batadv_tt_init(struct batadv_priv *bat_priv) if (ret < 0) return ret; - batadv_tt_start_timer(bat_priv); + INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge); + queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work, + msecs_to_jiffies(BATADV_TT_WORK_PERIOD)); return 1; } @@ -2286,7 +2281,8 @@ static void batadv_tt_purge(struct work_struct *work) batadv_tt_req_purge(bat_priv); batadv_tt_roam_purge(bat_priv); - batadv_tt_start_timer(bat_priv); + queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work, + msecs_to_jiffies(BATADV_TT_WORK_PERIOD)); } void batadv_tt_free(struct batadv_priv *bat_priv) diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 60eb9b7ca8d1..51e2bf01c919 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -31,8 +31,6 @@ /* hash class keys */ static struct lock_class_key batadv_vis_hash_lock_class_key; -static void batadv_start_vis_timer(struct batadv_priv *bat_priv); - /* free the info */ static void batadv_free_info(struct kref *ref) { @@ -830,7 +828,9 @@ static void batadv_send_vis_packets(struct work_struct *work) kref_put(&info->refcount, batadv_free_info); } spin_unlock_bh(&bat_priv->vis.hash_lock); - batadv_start_vis_timer(bat_priv); + + queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work, + msecs_to_jiffies(BATADV_VIS_INTERVAL)); } /* init the vis server. this may only be called when if_list is already @@ -900,7 +900,11 @@ int batadv_vis_init(struct batadv_priv *bat_priv) } spin_unlock_bh(&bat_priv->vis.hash_lock); - batadv_start_vis_timer(bat_priv); + + INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets); + queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work, + msecs_to_jiffies(BATADV_VIS_INTERVAL)); + return 0; free_info: @@ -937,11 +941,3 @@ void batadv_vis_quit(struct batadv_priv *bat_priv) bat_priv->vis.my_info = NULL; spin_unlock_bh(&bat_priv->vis.hash_lock); } - -/* schedule packets for (re)transmission */ -static void batadv_start_vis_timer(struct batadv_priv *bat_priv) -{ - INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets); - queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work, - msecs_to_jiffies(BATADV_VIS_INTERVAL)); -} -- cgit v1.2.3 From b6d0ab7ca306f3363a5996570ae8772d57d61846 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:19 +0800 Subject: batman-adv: align kernel doc properly Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index d8061ac28409..dffbdffc79a4 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -66,19 +66,18 @@ struct batadv_hard_iface { }; /** - * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh - * @primary_addr: hosts primary interface address - * @last_seen: when last packet from this node was received - * @bcast_seqno_reset: time when the broadcast seqno window was reset - * @batman_seqno_reset: time when the batman seqno window was reset - * @gw_flags: flags related to gateway class - * @flags: for now only VIS_SERVER flag - * @last_real_seqno: last and best known sequence number - * @last_ttl: ttl of last received packet - * @last_bcast_seqno: last broadcast sequence number received by this host - * - * @candidates: how many candidates are available - * @selected: next bonding candidate + * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh + * @primary_addr: hosts primary interface address + * @last_seen: when last packet from this node was received + * @bcast_seqno_reset: time when the broadcast seqno window was reset + * @batman_seqno_reset: time when the batman seqno window was reset + * @gw_flags: flags related to gateway class + * @flags: for now only VIS_SERVER flag + * @last_real_seqno: last and best known sequence number + * @last_ttl: ttl of last received packet + * @last_bcast_seqno: last broadcast sequence number received by this host + * @candidates: how many candidates are available + * @selected: next bonding candidate */ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; -- cgit v1.2.3 From 0abf5d8117dc864135eda5adda12640e16ed0cb8 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:20 +0800 Subject: batman-adv: mark debug_log struct as bat_priv only struct Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/debugfs.c | 13 +++++++------ net/batman-adv/types.h | 25 +++++++++++++------------ 2 files changed, 20 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index 55a90078dc66..e47555b4280e 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -40,13 +40,14 @@ static struct dentry *batadv_debugfs; static const int batadv_log_buff_len = BATADV_LOG_BUF_LEN; -static char *batadv_log_char_addr(struct batadv_debug_log *debug_log, +static char *batadv_log_char_addr(struct batadv_priv_debug_log *debug_log, size_t idx) { return &debug_log->log_buff[idx & BATADV_LOG_BUFF_MASK]; } -static void batadv_emit_log_char(struct batadv_debug_log *debug_log, char c) +static void batadv_emit_log_char(struct batadv_priv_debug_log *debug_log, + char c) { char *char_addr; @@ -59,7 +60,7 @@ static void batadv_emit_log_char(struct batadv_debug_log *debug_log, char c) } __printf(2, 3) -static int batadv_fdebug_log(struct batadv_debug_log *debug_log, +static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log, const char *fmt, ...) { va_list args; @@ -114,7 +115,7 @@ static int batadv_log_release(struct inode *inode, struct file *file) return 0; } -static int batadv_log_empty(struct batadv_debug_log *debug_log) +static int batadv_log_empty(struct batadv_priv_debug_log *debug_log) { return !(debug_log->log_start - debug_log->log_end); } @@ -123,7 +124,7 @@ static ssize_t batadv_log_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct batadv_priv *bat_priv = file->private_data; - struct batadv_debug_log *debug_log = bat_priv->debug_log; + struct batadv_priv_debug_log *debug_log = bat_priv->debug_log; int error, i = 0; char *char_addr; char c; @@ -177,7 +178,7 @@ static ssize_t batadv_log_read(struct file *file, char __user *buf, static unsigned int batadv_log_poll(struct file *file, poll_table *wait) { struct batadv_priv *bat_priv = file->private_data; - struct batadv_debug_log *debug_log = bat_priv->debug_log; + struct batadv_priv_debug_log *debug_log = bat_priv->debug_log; poll_wait(file, &debug_log->queue_wait, wait); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index dffbdffc79a4..db3fb252432d 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -233,6 +233,16 @@ struct batadv_priv_bla { }; #endif +#ifdef CONFIG_BATMAN_ADV_DEBUG +struct batadv_priv_debug_log { + char log_buff[BATADV_LOG_BUF_LEN]; + unsigned long log_start; + unsigned long log_end; + spinlock_t lock; /* protects log_buff, log_start and log_end */ + wait_queue_head_t queue_wait; +}; +#endif + struct batadv_priv_gw { struct hlist_head list; spinlock_t list_lock; /* protects gw_list and curr_gw */ @@ -290,9 +300,6 @@ struct batadv_priv { atomic_t bcast_queue_left; atomic_t batman_queue_left; char num_ifaces; -#ifdef CONFIG_BATMAN_ADV_DEBUG - struct batadv_debug_log *debug_log; -#endif struct kobject *mesh_obj; struct dentry *debug_dir; struct hlist_head forw_bat_list; @@ -305,6 +312,9 @@ struct batadv_priv { struct batadv_algo_ops *bat_algo_ops; #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_priv_bla bla; +#endif +#ifdef CONFIG_BATMAN_ADV_DEBUG + struct batadv_priv_debug_log *debug_log; #endif struct batadv_priv_gw gw; struct batadv_priv_tt tt; @@ -425,15 +435,6 @@ struct batadv_if_list_entry { bool primary; struct hlist_node list; }; - -struct batadv_debug_log { - char log_buff[BATADV_LOG_BUF_LEN]; - unsigned long log_start; - unsigned long log_end; - spinlock_t lock; /* protects log_buff, log_start and log_end */ - wait_queue_head_t queue_wait; -}; - struct batadv_frag_packet_list_entry { struct list_head list; uint16_t seqno; -- cgit v1.2.3 From 2006fea82085cfd9ddc92b23ef1ea8d57e065c61 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:21 +0800 Subject: batman-adv: group tt type definitions together Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index db3fb252432d..9a66115bd9c5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -339,6 +339,31 @@ struct batadv_socket_packet { struct batadv_icmp_packet_rr icmp_packet; }; +#ifdef CONFIG_BATMAN_ADV_BLA +struct batadv_backbone_gw { + uint8_t orig[ETH_ALEN]; + short vid; /* used VLAN ID */ + struct hlist_node hash_entry; + struct batadv_priv *bat_priv; + unsigned long lasttime; /* last time we heard of this backbone gw */ + atomic_t wait_periods; + atomic_t request_sent; + atomic_t refcount; + struct rcu_head rcu; + uint16_t crc; /* crc checksum over all claims */ +}; + +struct batadv_claim { + uint8_t addr[ETH_ALEN]; + short vid; + struct batadv_backbone_gw *backbone_gw; + unsigned long lasttime; /* last time we heard of claim (locals only) */ + struct rcu_head rcu; + atomic_t refcount; + struct hlist_node hash_entry; +}; +#endif + struct batadv_tt_common_entry { uint8_t addr[ETH_ALEN]; struct hlist_node hash_entry; @@ -368,31 +393,6 @@ struct batadv_tt_orig_list_entry { struct hlist_node list; }; -#ifdef CONFIG_BATMAN_ADV_BLA -struct batadv_backbone_gw { - uint8_t orig[ETH_ALEN]; - short vid; /* used VLAN ID */ - struct hlist_node hash_entry; - struct batadv_priv *bat_priv; - unsigned long lasttime; /* last time we heard of this backbone gw */ - atomic_t wait_periods; - atomic_t request_sent; - atomic_t refcount; - struct rcu_head rcu; - uint16_t crc; /* crc checksum over all claims */ -}; - -struct batadv_claim { - uint8_t addr[ETH_ALEN]; - short vid; - struct batadv_backbone_gw *backbone_gw; - unsigned long lasttime; /* last time we heard of claim (locals only) */ - struct rcu_head rcu; - atomic_t refcount; - struct hlist_node hash_entry; -}; -#endif - struct batadv_tt_change_node { struct list_head list; struct batadv_tt_change change; -- cgit v1.2.3 From 015b4ae4a36bb5ee62187ac6e2f193e87d225516 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:22 +0800 Subject: batman-adv: rename batadv_if_list_entry struct to make clear it is used by vis Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 19 ++++++++++--------- net/batman-adv/vis.c | 8 ++++---- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 9a66115bd9c5..d17517c79d3c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -426,15 +426,6 @@ struct batadv_forw_packet { struct batadv_hard_iface *if_incoming; }; -/* While scanning for vis-entries of a particular vis-originator - * this list collects its interfaces to create a subgraph/cluster - * out of them later - */ -struct batadv_if_list_entry { - uint8_t addr[ETH_ALEN]; - bool primary; - struct hlist_node list; -}; struct batadv_frag_packet_list_entry { struct list_head list; uint16_t seqno; @@ -467,6 +458,16 @@ struct batadv_recvlist_node { uint8_t mac[ETH_ALEN]; }; +/* While scanning for vis-entries of a particular vis-originator + * this list collects its interfaces to create a subgraph/cluster + * out of them later + */ +struct batadv_vis_if_list_entry { + uint8_t addr[ETH_ALEN]; + bool primary; + struct hlist_node list; +}; + struct batadv_algo_ops { struct hlist_node list; char *name; diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 51e2bf01c919..3d42b17ede08 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -127,7 +127,7 @@ static void batadv_vis_data_insert_interface(const uint8_t *interface, struct hlist_head *if_list, bool primary) { - struct batadv_if_list_entry *entry; + struct batadv_vis_if_list_entry *entry; struct hlist_node *pos; hlist_for_each_entry(entry, pos, if_list, list) { @@ -147,7 +147,7 @@ static void batadv_vis_data_insert_interface(const uint8_t *interface, static void batadv_vis_data_read_prim_sec(struct seq_file *seq, const struct hlist_head *if_list) { - struct batadv_if_list_entry *entry; + struct batadv_vis_if_list_entry *entry; struct hlist_node *pos; hlist_for_each_entry(entry, pos, if_list, list) { @@ -197,7 +197,7 @@ static void batadv_vis_data_read_entries(struct seq_file *seq, struct batadv_vis_info_entry *entries) { int i; - struct batadv_if_list_entry *entry; + struct batadv_vis_if_list_entry *entry; struct hlist_node *pos; hlist_for_each_entry(entry, pos, list, list) { @@ -223,7 +223,7 @@ static void batadv_vis_seq_print_text_bucket(struct seq_file *seq, struct batadv_vis_packet *packet; uint8_t *entries_pos; struct batadv_vis_info_entry *entries; - struct batadv_if_list_entry *entry; + struct batadv_vis_if_list_entry *entry; struct hlist_node *pos, *n; HLIST_HEAD(vis_if_list); -- cgit v1.2.3 From 28500f07abf120e5609a68d6eb2f78350562381d Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:23 +0800 Subject: batman-adv: rename batadv_recvlist_node struct to make clear it is used by vis Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 2 +- net/batman-adv/vis.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index d17517c79d3c..c56e0d904dac 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -453,7 +453,7 @@ struct batadv_vis_info_entry { uint8_t quality; /* quality = 0 client */ } __packed; -struct batadv_recvlist_node { +struct batadv_vis_recvlist_node { struct list_head list; uint8_t mac[ETH_ALEN]; }; diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 3d42b17ede08..2e70ec113205 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -36,7 +36,7 @@ static void batadv_free_info(struct kref *ref) { struct batadv_vis_info *info; struct batadv_priv *bat_priv; - struct batadv_recvlist_node *entry, *tmp; + struct batadv_vis_recvlist_node *entry, *tmp; info = container_of(ref, struct batadv_vis_info, refcount); bat_priv = info->bat_priv; @@ -305,7 +305,7 @@ static void batadv_send_list_del(struct batadv_vis_info *info) static void batadv_recv_list_add(struct batadv_priv *bat_priv, struct list_head *recv_list, const char *mac) { - struct batadv_recvlist_node *entry; + struct batadv_vis_recvlist_node *entry; entry = kmalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) @@ -322,7 +322,7 @@ static int batadv_recv_list_is_in(struct batadv_priv *bat_priv, const struct list_head *recv_list, const char *mac) { - const struct batadv_recvlist_node *entry; + const struct batadv_vis_recvlist_node *entry; spin_lock_bh(&bat_priv->vis.list_lock); list_for_each_entry(entry, recv_list, list) { -- cgit v1.2.3 From bae987747150d8939bf385863f49e2647db15e2a Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:24 +0800 Subject: batman-adv: rename batadv_backbone_gw struct to make clear it is used by bla Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/bridge_loop_avoidance.c | 52 ++++++++++++++++++---------------- net/batman-adv/types.h | 4 +-- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index bb5fbd64dc28..5f9a206ca8a0 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -34,8 +34,9 @@ static const uint8_t batadv_announce_mac[4] = {0x43, 0x05, 0x43, 0x05}; static void batadv_bla_periodic_work(struct work_struct *work); -static void batadv_bla_send_announce(struct batadv_priv *bat_priv, - struct batadv_backbone_gw *backbone_gw); +static void +batadv_bla_send_announce(struct batadv_priv *bat_priv, + struct batadv_bla_backbone_gw *backbone_gw); /* return the index of the claim */ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) @@ -75,9 +76,9 @@ static inline uint32_t batadv_choose_backbone_gw(const void *data, static int batadv_compare_backbone_gw(const struct hlist_node *node, const void *data2) { - const void *data1 = container_of(node, struct batadv_backbone_gw, + const void *data1 = container_of(node, struct batadv_bla_backbone_gw, hash_entry); - const struct batadv_backbone_gw *gw1 = data1, *gw2 = data2; + const struct batadv_bla_backbone_gw *gw1 = data1, *gw2 = data2; if (!batadv_compare_eth(gw1->orig, gw2->orig)) return 0; @@ -106,7 +107,8 @@ static int batadv_compare_claim(const struct hlist_node *node, } /* free a backbone gw */ -static void batadv_backbone_gw_free_ref(struct batadv_backbone_gw *backbone_gw) +static void +batadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw) { if (atomic_dec_and_test(&backbone_gw->refcount)) kfree_rcu(backbone_gw, rcu); @@ -176,15 +178,15 @@ static struct batadv_claim *batadv_claim_hash_find(struct batadv_priv *bat_priv, * * Returns claim if found or NULL otherwise. */ -static struct batadv_backbone_gw * +static struct batadv_bla_backbone_gw * batadv_backbone_hash_find(struct batadv_priv *bat_priv, uint8_t *addr, short vid) { struct batadv_hashtable *hash = bat_priv->bla.backbone_hash; struct hlist_head *head; struct hlist_node *node; - struct batadv_backbone_gw search_entry, *backbone_gw; - struct batadv_backbone_gw *backbone_gw_tmp = NULL; + struct batadv_bla_backbone_gw search_entry, *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw_tmp = NULL; int index; if (!hash) @@ -215,7 +217,7 @@ batadv_backbone_hash_find(struct batadv_priv *bat_priv, /* delete all claims for a backbone */ static void -batadv_bla_del_backbone_claims(struct batadv_backbone_gw *backbone_gw) +batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) { struct batadv_hashtable *hash; struct hlist_node *node, *node_tmp; @@ -364,11 +366,11 @@ out: * searches for the backbone gw or creates a new one if it could not * be found. */ -static struct batadv_backbone_gw * +static struct batadv_bla_backbone_gw * batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, short vid, bool own_backbone) { - struct batadv_backbone_gw *entry; + struct batadv_bla_backbone_gw *entry; struct batadv_orig_node *orig_node; int hash_added; @@ -435,7 +437,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, short vid) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; backbone_gw = batadv_bla_get_backbone_gw(bat_priv, primary_if->net_dev->dev_addr, @@ -461,7 +463,7 @@ static void batadv_bla_answer_request(struct batadv_priv *bat_priv, struct hlist_head *head; struct batadv_hashtable *hash; struct batadv_claim *claim; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; int i; batadv_dbg(BATADV_DBG_BLA, bat_priv, @@ -500,7 +502,7 @@ static void batadv_bla_answer_request(struct batadv_priv *bat_priv, * After the request, it will repeat all of his own claims and finally * send an announcement claim with which we can check again. */ -static void batadv_bla_send_request(struct batadv_backbone_gw *backbone_gw) +static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) { /* first, remove all old entries */ batadv_bla_del_backbone_claims(backbone_gw); @@ -526,7 +528,7 @@ static void batadv_bla_send_request(struct batadv_backbone_gw *backbone_gw) * places. */ static void batadv_bla_send_announce(struct batadv_priv *bat_priv, - struct batadv_backbone_gw *backbone_gw) + struct batadv_bla_backbone_gw *backbone_gw) { uint8_t mac[ETH_ALEN]; __be16 crc; @@ -548,7 +550,7 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv, */ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, const uint8_t *mac, const short vid, - struct batadv_backbone_gw *backbone_gw) + struct batadv_bla_backbone_gw *backbone_gw) { struct batadv_claim *claim; struct batadv_claim search_claim; @@ -639,7 +641,7 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, uint8_t *an_addr, uint8_t *backbone_addr, short vid) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; uint16_t crc; if (memcmp(an_addr, batadv_announce_mac, 4) != 0) @@ -711,7 +713,7 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv, uint8_t *backbone_addr, uint8_t *claim_addr, short vid) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; /* unclaim in any case if it is our own */ if (primary_if && batadv_compare_eth(backbone_addr, @@ -740,7 +742,7 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv, uint8_t *backbone_addr, uint8_t *claim_addr, short vid) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; /* register the gateway if not yet available, and add the claim. */ @@ -954,7 +956,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, */ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; struct hlist_node *node, *node_tmp; struct hlist_head *head; struct batadv_hashtable *hash; @@ -1058,7 +1060,7 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, struct batadv_hard_iface *oldif) { - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; struct hlist_node *node; struct hlist_head *head; struct batadv_hashtable *hash; @@ -1111,7 +1113,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) struct batadv_priv_bla *priv_bla; struct hlist_node *node; struct hlist_head *head; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; struct batadv_hashtable *hash; struct batadv_hard_iface *primary_if; int i; @@ -1320,7 +1322,7 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig) struct batadv_hashtable *hash = bat_priv->bla.backbone_hash; struct hlist_head *head; struct hlist_node *node; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; int i; if (!atomic_read(&bat_priv->bridge_loop_avoidance)) @@ -1361,7 +1363,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb, { struct ethhdr *ethhdr; struct vlan_ethhdr *vhdr; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; short vid = -1; if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance)) @@ -1647,7 +1649,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) struct net_device *net_dev = (struct net_device *)seq->private; struct batadv_priv *bat_priv = netdev_priv(net_dev); struct batadv_hashtable *hash = bat_priv->bla.backbone_hash; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; struct batadv_hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c56e0d904dac..eb3d12914043 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -340,7 +340,7 @@ struct batadv_socket_packet { }; #ifdef CONFIG_BATMAN_ADV_BLA -struct batadv_backbone_gw { +struct batadv_bla_backbone_gw { uint8_t orig[ETH_ALEN]; short vid; /* used VLAN ID */ struct hlist_node hash_entry; @@ -356,7 +356,7 @@ struct batadv_backbone_gw { struct batadv_claim { uint8_t addr[ETH_ALEN]; short vid; - struct batadv_backbone_gw *backbone_gw; + struct batadv_bla_backbone_gw *backbone_gw; unsigned long lasttime; /* last time we heard of claim (locals only) */ struct rcu_head rcu; atomic_t refcount; -- cgit v1.2.3 From 712bbfe46b3004603762adb506baff99be0f8d8c Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 17:03:25 +0800 Subject: batman-adv: rename batadv_claim struct to make clear it is used by bla Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/bridge_loop_avoidance.c | 41 +++++++++++++++++----------------- net/batman-adv/types.h | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 5f9a206ca8a0..9971892bcbf8 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -41,7 +41,7 @@ batadv_bla_send_announce(struct batadv_priv *bat_priv, /* return the index of the claim */ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) { - struct batadv_claim *claim = (struct batadv_claim *)data; + struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data; uint32_t hash = 0; hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr)); @@ -58,7 +58,7 @@ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) static inline uint32_t batadv_choose_backbone_gw(const void *data, uint32_t size) { - const struct batadv_claim *claim = (struct batadv_claim *)data; + const struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data; uint32_t hash = 0; hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr)); @@ -93,9 +93,9 @@ static int batadv_compare_backbone_gw(const struct hlist_node *node, static int batadv_compare_claim(const struct hlist_node *node, const void *data2) { - const void *data1 = container_of(node, struct batadv_claim, + const void *data1 = container_of(node, struct batadv_bla_claim, hash_entry); - const struct batadv_claim *cl1 = data1, *cl2 = data2; + const struct batadv_bla_claim *cl1 = data1, *cl2 = data2; if (!batadv_compare_eth(cl1->addr, cl2->addr)) return 0; @@ -117,16 +117,16 @@ batadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw) /* finally deinitialize the claim */ static void batadv_claim_free_rcu(struct rcu_head *rcu) { - struct batadv_claim *claim; + struct batadv_bla_claim *claim; - claim = container_of(rcu, struct batadv_claim, rcu); + claim = container_of(rcu, struct batadv_bla_claim, rcu); batadv_backbone_gw_free_ref(claim->backbone_gw); kfree(claim); } /* free a claim, call claim_free_rcu if its the last reference */ -static void batadv_claim_free_ref(struct batadv_claim *claim) +static void batadv_claim_free_ref(struct batadv_bla_claim *claim) { if (atomic_dec_and_test(&claim->refcount)) call_rcu(&claim->rcu, batadv_claim_free_rcu); @@ -138,14 +138,15 @@ static void batadv_claim_free_ref(struct batadv_claim *claim) * looks for a claim in the hash, and returns it if found * or NULL otherwise. */ -static struct batadv_claim *batadv_claim_hash_find(struct batadv_priv *bat_priv, - struct batadv_claim *data) +static struct batadv_bla_claim +*batadv_claim_hash_find(struct batadv_priv *bat_priv, + struct batadv_bla_claim *data) { struct batadv_hashtable *hash = bat_priv->bla.claim_hash; struct hlist_head *head; struct hlist_node *node; - struct batadv_claim *claim; - struct batadv_claim *claim_tmp = NULL; + struct batadv_bla_claim *claim; + struct batadv_bla_claim *claim_tmp = NULL; int index; if (!hash) @@ -222,7 +223,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) struct batadv_hashtable *hash; struct hlist_node *node, *node_tmp; struct hlist_head *head; - struct batadv_claim *claim; + struct batadv_bla_claim *claim; int i; spinlock_t *list_lock; /* protects write access to the hash lists */ @@ -462,7 +463,7 @@ static void batadv_bla_answer_request(struct batadv_priv *bat_priv, struct hlist_node *node; struct hlist_head *head; struct batadv_hashtable *hash; - struct batadv_claim *claim; + struct batadv_bla_claim *claim; struct batadv_bla_backbone_gw *backbone_gw; int i; @@ -552,8 +553,8 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, const uint8_t *mac, const short vid, struct batadv_bla_backbone_gw *backbone_gw) { - struct batadv_claim *claim; - struct batadv_claim search_claim; + struct batadv_bla_claim *claim; + struct batadv_bla_claim search_claim; int hash_added; memcpy(search_claim.addr, mac, ETH_ALEN); @@ -615,7 +616,7 @@ claim_free_ref: static void batadv_bla_del_claim(struct batadv_priv *bat_priv, const uint8_t *mac, const short vid) { - struct batadv_claim search_claim, *claim; + struct batadv_bla_claim search_claim, *claim; memcpy(search_claim.addr, mac, ETH_ALEN); search_claim.vid = vid; @@ -1011,7 +1012,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, int now) { - struct batadv_claim *claim; + struct batadv_bla_claim *claim; struct hlist_node *node; struct hlist_head *head; struct batadv_hashtable *hash; @@ -1434,7 +1435,7 @@ int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid, bool is_bcast) { struct ethhdr *ethhdr; - struct batadv_claim search_claim, *claim = NULL; + struct batadv_bla_claim search_claim, *claim = NULL; struct batadv_hard_iface *primary_if; int ret; @@ -1528,7 +1529,7 @@ out: int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid) { struct ethhdr *ethhdr; - struct batadv_claim search_claim, *claim = NULL; + struct batadv_bla_claim search_claim, *claim = NULL; struct batadv_hard_iface *primary_if; int ret = 0; @@ -1604,7 +1605,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) struct net_device *net_dev = (struct net_device *)seq->private; struct batadv_priv *bat_priv = netdev_priv(net_dev); struct batadv_hashtable *hash = bat_priv->bla.claim_hash; - struct batadv_claim *claim; + struct batadv_bla_claim *claim; struct batadv_hard_iface *primary_if; struct hlist_node *node; struct hlist_head *head; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index eb3d12914043..549ba5618071 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -353,7 +353,7 @@ struct batadv_bla_backbone_gw { uint16_t crc; /* crc checksum over all claims */ }; -struct batadv_claim { +struct batadv_bla_claim { uint8_t addr[ETH_ALEN]; short vid; struct batadv_bla_backbone_gw *backbone_gw; -- cgit v1.2.3 From 88a32c9a8a3f0217430f0b67fc90e425c1ceaa7f Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Tue, 25 Dec 2012 21:45:12 +0800 Subject: batman-adv: kernel doc for types.h Thanks to Sven Eckelmann and Simon Wunderlich for their support. Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/types.h | 461 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 395 insertions(+), 66 deletions(-) (limited to 'net') diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 549ba5618071..03197f5ebf6c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -24,6 +24,9 @@ #include "bitarray.h" #include +/** + * Maximum overhead for the encapsulation for a payload packet + */ #define BATADV_HEADER_LEN \ (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \ sizeof(struct batadv_bcast_packet))) @@ -51,6 +54,21 @@ struct batadv_hard_iface_bat_iv { atomic_t ogm_seqno; }; +/** + * struct batadv_hard_iface - network device known to batman-adv + * @list: list node for batadv_hardif_list + * @if_num: identificator of the interface + * @if_status: status of the interface for batman-adv + * @net_dev: pointer to the net_device + * @frag_seqno: last fragment sequence number sent by this interface + * @hardif_obj: kobject of the per interface sysfs "mesh" directory + * @refcount: number of contexts the object is used + * @batman_adv_ptype: packet type describing packets that should be processed by + * batman-adv for this interface + * @soft_iface: the batman-adv interface which uses this network interface + * @rcu: struct used for freeing in an RCU-safe manner + * @bat_iv: BATMAN IV specific per hard interface data + */ struct batadv_hard_iface { struct list_head list; int16_t if_num; @@ -67,17 +85,47 @@ struct batadv_hard_iface { /** * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh + * @orig: originator ethernet address * @primary_addr: hosts primary interface address - * @last_seen: when last packet from this node was received + * @router: router that should be used to reach this originator + * @batadv_dat_addr_t: address of the orig node in the distributed hash + * @bcast_own: bitfield containing the number of our OGMs this orig_node + * rebroadcasted "back" to us (relative to last_real_seqno) + * @bcast_own_sum: counted result of bcast_own + * @last_seen: time when last packet from this node was received * @bcast_seqno_reset: time when the broadcast seqno window was reset * @batman_seqno_reset: time when the batman seqno window was reset * @gw_flags: flags related to gateway class * @flags: for now only VIS_SERVER flag + * @last_ttvn: last seen translation table version number + * @tt_crc: CRC of the translation table + * @tt_buff: last tt changeset this node received from the orig node + * @tt_buff_len: length of the last tt changeset this node received from the + * orig node + * @tt_buff_lock: lock that protects tt_buff and tt_buff_len + * @tt_size: number of global TT entries announced by the orig node + * @tt_initialised: bool keeping track of whether or not this node have received + * any translation table information from the orig node yet * @last_real_seqno: last and best known sequence number * @last_ttl: ttl of last received packet + * @bcast_bits: bitfield containing the info which payload broadcast originated + * from this orig node this host already has seen (relative to + * last_bcast_seqno) * @last_bcast_seqno: last broadcast sequence number received by this host - * @candidates: how many candidates are available - * @selected: next bonding candidate + * @neigh_list: list of potential next hop neighbor towards this orig node + * @frag_list: fragmentation buffer list for fragment re-assembly + * @last_frag_packet: time when last fragmented packet from this node was + * received + * @neigh_list_lock: lock protecting neigh_list, router and bonding_list + * @hash_entry: hlist node for batadv_priv::orig_hash + * @bat_priv: pointer to soft_iface this orig node belongs to + * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum, + * neigh_node->real_bits & neigh_node->real_packet_count + * @bcast_seqno_lock: lock protecting bcast_bits & last_bcast_seqno + * @bond_candidates: how many candidates are available + * @bond_list: list of bonding candidates + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner */ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; @@ -93,11 +141,11 @@ struct batadv_orig_node { unsigned long batman_seqno_reset; uint8_t gw_flags; uint8_t flags; - atomic_t last_ttvn; /* last seen translation table version number */ + atomic_t last_ttvn; uint16_t tt_crc; unsigned char *tt_buff; int16_t tt_buff_len; - spinlock_t tt_buff_lock; /* protects tt_buff */ + spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */ atomic_t tt_size; bool tt_initialised; uint32_t last_real_seqno; @@ -106,22 +154,31 @@ struct batadv_orig_node { uint32_t last_bcast_seqno; struct hlist_head neigh_list; struct list_head frag_list; - spinlock_t neigh_list_lock; /* protects neigh_list and router */ - atomic_t refcount; - struct rcu_head rcu; + unsigned long last_frag_packet; + /* neigh_list_lock protects: neigh_list, router & bonding_list */ + spinlock_t neigh_list_lock; struct hlist_node hash_entry; struct batadv_priv *bat_priv; - unsigned long last_frag_packet; /* ogm_cnt_lock protects: bcast_own, bcast_own_sum, - * neigh_node->real_bits, neigh_node->real_packet_count + * neigh_node->real_bits & neigh_node->real_packet_count */ spinlock_t ogm_cnt_lock; - /* bcast_seqno_lock protects bcast_bits, last_bcast_seqno */ + /* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */ spinlock_t bcast_seqno_lock; atomic_t bond_candidates; struct list_head bond_list; + atomic_t refcount; + struct rcu_head rcu; }; +/** + * struct batadv_gw_node - structure for orig nodes announcing gw capabilities + * @list: list node for batadv_priv_gw::list + * @orig_node: pointer to corresponding orig node + * @deleted: this struct is scheduled for deletion + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ struct batadv_gw_node { struct hlist_node list; struct batadv_orig_node *orig_node; @@ -130,13 +187,28 @@ struct batadv_gw_node { struct rcu_head rcu; }; -/* batadv_neigh_node - * @last_seen: when last packet via this neighbor was received +/** + * struct batadv_neigh_node - structure for single hop neighbors + * @list: list node for batadv_orig_node::neigh_list + * @addr: mac address of neigh node + * @tq_recv: ring buffer of received TQ values from this neigh node + * @tq_index: ring buffer index + * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) + * @last_ttl: last received ttl from this neigh node + * @bonding_list: list node for batadv_orig_node::bond_list + * @last_seen: when last packet via this neighbor was received + * @real_bits: bitfield containing the number of OGMs received from this neigh + * node (relative to orig_node->last_real_seqno) + * @real_packet_count: counted result of real_bits + * @orig_node: pointer to corresponding orig_node + * @if_incoming: pointer to incoming hard interface + * @lq_update_lock: lock protecting tq_recv & tq_index + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner */ struct batadv_neigh_node { struct hlist_node list; uint8_t addr[ETH_ALEN]; - uint8_t real_packet_count; uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE]; uint8_t tq_index; uint8_t tq_avg; @@ -144,13 +216,20 @@ struct batadv_neigh_node { struct list_head bonding_list; unsigned long last_seen; DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - atomic_t refcount; - struct rcu_head rcu; + uint8_t real_packet_count; struct batadv_orig_node *orig_node; struct batadv_hard_iface *if_incoming; - spinlock_t lq_update_lock; /* protects: tq_recv, tq_index */ + spinlock_t lq_update_lock; /* protects tq_recv & tq_index */ + atomic_t refcount; + struct rcu_head rcu; }; +/** + * struct batadv_bcast_duplist_entry - structure for LAN broadcast suppression + * @orig[ETH_ALEN]: mac address of orig node orginating the broadcast + * @crc: crc32 checksum of broadcast payload + * @entrytime: time when the broadcast packet was received + */ #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_bcast_duplist_entry { uint8_t orig[ETH_ALEN]; @@ -159,6 +238,33 @@ struct batadv_bcast_duplist_entry { }; #endif +/** + * enum batadv_counters - indices for traffic counters + * @BATADV_CNT_TX: transmitted payload traffic packet counter + * @BATADV_CNT_TX_BYTES: transmitted payload traffic bytes counter + * @BATADV_CNT_TX_DROPPED: dropped transmission payload traffic packet counter + * @BATADV_CNT_RX: received payload traffic packet counter + * @BATADV_CNT_RX_BYTES: received payload traffic bytes counter + * @BATADV_CNT_FORWARD: forwarded payload traffic packet counter + * @BATADV_CNT_FORWARD_BYTES: forwarded payload traffic bytes counter + * @BATADV_CNT_MGMT_TX: transmitted routing protocol traffic packet counter + * @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter + * @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter + * @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter + * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter + * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter + * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter + * @BATADV_CNT_TT_RESPONSE_RX: received tt resp traffic packet counter + * @BATADV_CNT_TT_ROAM_ADV_TX: transmitted tt roam traffic packet counter + * @BATADV_CNT_TT_ROAM_ADV_RX: received tt roam traffic packet counter + * @BATADV_CNT_DAT_GET_TX: transmitted dht GET traffic packet counter + * @BATADV_CNT_DAT_GET_RX: received dht GET traffic packet counter + * @BATADV_CNT_DAT_PUT_TX: transmitted dht PUT traffic packet counter + * @BATADV_CNT_DAT_PUT_RX: received dht PUT traffic packet counter + * @BATADV_CNT_DAT_CACHED_REPLY_TX: transmitted dat cache reply traffic packet + * counter + * @BATADV_CNT_NUM: number of traffic counters + */ enum batadv_counters { BATADV_CNT_TX, BATADV_CNT_TX_BYTES, @@ -190,14 +296,23 @@ enum batadv_counters { /** * struct batadv_priv_tt - per mesh interface translation table data * @vn: translation table version number + * @ogm_append_cnt: counter of number of OGMs containing the local tt diff * @local_changes: changes registered in an originator interval - * @poss_change: Detect an ongoing roaming phase. If true, then this node - * received a roaming_adv and has to inspect every packet directed to it to - * check whether it still is the true destination or not. This flag will be - * reset to false as soon as the this node's ttvn is increased * @changes_list: tracks tt local changes within an originator interval - * @req_list: list of pending tt_requests + * @local_hash: local translation table hash table + * @global_hash: global translation table hash table + * @req_list: list of pending & unanswered tt_requests + * @roam_list: list of the last roaming events of each client limiting the + * number of roaming events to avoid route flapping + * @changes_list_lock: lock protecting changes_list + * @req_list_lock: lock protecting req_list + * @roam_list_lock: lock protecting roam_list + * @local_entry_num: number of entries in the local hash table * @local_crc: Checksum of the local table, recomputed before sending a new OGM + * @last_changeset: last tt changeset this host has generated + * @last_changeset_len: length of last tt changeset this host has generated + * @last_changeset_lock: lock protecting last_changeset & last_changeset_len + * @work: work queue callback item for translation table purging */ struct batadv_priv_tt { atomic_t vn; @@ -215,24 +330,45 @@ struct batadv_priv_tt { uint16_t local_crc; unsigned char *last_changeset; int16_t last_changeset_len; - spinlock_t last_changeset_lock; /* protects last_changeset */ + /* protects last_changeset & last_changeset_len */ + spinlock_t last_changeset_lock; struct delayed_work work; }; +/** + * struct batadv_priv_bla - per mesh interface bridge loope avoidance data + * @num_requests; number of bla requests in flight + * @claim_hash: hash table containing mesh nodes this host has claimed + * @backbone_hash: hash table containing all detected backbone gateways + * @bcast_duplist: recently received broadcast packets array (for broadcast + * duplicate suppression) + * @bcast_duplist_curr: index of last broadcast packet added to bcast_duplist + * @bcast_duplist_lock: lock protecting bcast_duplist & bcast_duplist_curr + * @claim_dest: local claim data (e.g. claim group) + * @work: work queue callback item for cleanups & bla announcements + */ #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_priv_bla { - atomic_t num_requests; /* number of bla requests in flight */ + atomic_t num_requests; struct batadv_hashtable *claim_hash; struct batadv_hashtable *backbone_hash; struct batadv_bcast_duplist_entry bcast_duplist[BATADV_DUPLIST_SIZE]; int bcast_duplist_curr; - /* protects bcast_duplist and bcast_duplist_curr */ + /* protects bcast_duplist & bcast_duplist_curr */ spinlock_t bcast_duplist_lock; struct batadv_bla_claim_dst claim_dest; struct delayed_work work; }; #endif +/** + * struct batadv_debug_log - debug logging data + * @log_buff: buffer holding the logs (ring bufer) + * @log_start: index of next character to read + * @log_end: index of next character to write + * @lock: lock protecting log_buff, log_start & log_end + * @queue_wait: log reader's wait queue + */ #ifdef CONFIG_BATMAN_ADV_DEBUG struct batadv_priv_debug_log { char log_buff[BATADV_LOG_BUF_LEN]; @@ -243,18 +379,34 @@ struct batadv_priv_debug_log { }; #endif +/** + * struct batadv_priv_gw - per mesh interface gateway data + * @list: list of available gateway nodes + * @list_lock: lock protecting gw_list & curr_gw + * @curr_gw: pointer to currently selected gateway node + * @reselect: bool indicating a gateway re-selection is in progress + */ struct batadv_priv_gw { struct hlist_head list; - spinlock_t list_lock; /* protects gw_list and curr_gw */ + spinlock_t list_lock; /* protects gw_list & curr_gw */ struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */ atomic_t reselect; }; +/** + * struct batadv_priv_vis - per mesh interface vis data + * @send_list: list of batadv_vis_info packets to sent + * @hash: hash table containing vis data from other nodes in the network + * @hash_lock: lock protecting the hash table + * @list_lock: lock protecting my_info::recv_list + * @work: work queue callback item for vis packet sending + * @my_info: holds this node's vis data sent on a regular basis + */ struct batadv_priv_vis { struct list_head send_list; struct batadv_hashtable *hash; spinlock_t hash_lock; /* protects hash */ - spinlock_t list_lock; /* protects info::recv_list */ + spinlock_t list_lock; /* protects my_info::recv_list */ struct delayed_work work; struct batadv_vis_info *my_info; }; @@ -273,28 +425,70 @@ struct batadv_priv_dat { }; #endif +/** + * struct batadv_priv - per mesh interface data + * @mesh_state: current status of the mesh (inactive/active/deactivating) + * @stats: structure holding the data for the ndo_get_stats() call + * @bat_counters: mesh internal traffic statistic counters (see batadv_counters) + * @aggregated_ogms: bool indicating whether OGM aggregation is enabled + * @bonding: bool indicating whether traffic bonding is enabled + * @fragmentation: bool indicating whether traffic fragmentation is enabled + * @ap_isolation: bool indicating whether ap isolation is enabled + * @bridge_loop_avoidance: bool indicating whether bridge loop avoidance is + * enabled + * @distributed_arp_table: bool indicating whether distributed ARP table is + * enabled + * @vis_mode: vis operation: client or server (see batadv_vis_packettype) + * @gw_mode: gateway operation: off, client or server (see batadv_gw_modes) + * @gw_sel_class: gateway selection class (applies if gw_mode client) + * @gw_bandwidth: gateway announced bandwidth (applies if gw_mode server) + * @orig_interval: OGM broadcast interval in milliseconds + * @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop + * @log_level: configured log level (see batadv_dbg_level) + * @bcast_seqno: last sent broadcast packet sequence number + * @bcast_queue_left: number of remaining buffered broadcast packet slots + * @batman_queue_left: number of remaining OGM packet slots + * @num_ifaces: number of interfaces assigned to this mesh interface + * @mesh_obj: kobject for sysfs mesh subdirectory + * @debug_dir: dentry for debugfs batman-adv subdirectory + * @forw_bat_list: list of aggregated OGMs that will be forwarded + * @forw_bcast_list: list of broadcast packets that will be rebroadcasted + * @orig_hash: hash table containing mesh participants (orig nodes) + * @forw_bat_list_lock: lock protecting forw_bat_list + * @forw_bcast_list_lock: lock protecting forw_bcast_list + * @orig_work: work queue callback item for orig node purging + * @primary_if: one of the hard interfaces assigned to this mesh interface + * becomes the primary interface + * @bat_algo_ops: routing algorithm used by this mesh interface + * @bla: bridge loope avoidance data + * @debug_log: holding debug logging relevant data + * @gw: gateway data + * @tt: translation table data + * @vis: vis data + * @dat: distributed arp table data + */ struct batadv_priv { atomic_t mesh_state; struct net_device_stats stats; uint64_t __percpu *bat_counters; /* Per cpu counters */ - atomic_t aggregated_ogms; /* boolean */ - atomic_t bonding; /* boolean */ - atomic_t fragmentation; /* boolean */ - atomic_t ap_isolation; /* boolean */ + atomic_t aggregated_ogms; + atomic_t bonding; + atomic_t fragmentation; + atomic_t ap_isolation; #ifdef CONFIG_BATMAN_ADV_BLA - atomic_t bridge_loop_avoidance; /* boolean */ + atomic_t bridge_loop_avoidance; #endif #ifdef CONFIG_BATMAN_ADV_DAT - atomic_t distributed_arp_table; /* boolean */ + atomic_t distributed_arp_table; #endif - atomic_t vis_mode; /* VIS_TYPE_* */ - atomic_t gw_mode; /* GW_MODE_* */ - atomic_t gw_sel_class; /* uint */ - atomic_t gw_bandwidth; /* gw bandwidth */ - atomic_t orig_interval; /* uint */ - atomic_t hop_penalty; /* uint */ + atomic_t vis_mode; + atomic_t gw_mode; + atomic_t gw_sel_class; + atomic_t gw_bandwidth; + atomic_t orig_interval; + atomic_t hop_penalty; #ifdef CONFIG_BATMAN_ADV_DEBUG - atomic_t log_level; /* uint */ + atomic_t log_level; #endif atomic_t bcast_seqno; atomic_t bcast_queue_left; @@ -324,46 +518,97 @@ struct batadv_priv { #endif }; +/** + * struct batadv_socket_client - layer2 icmp socket client data + * @queue_list: packet queue for packets destined for this socket client + * @queue_len: number of packets in the packet queue (queue_list) + * @index: socket client's index in the batadv_socket_client_hash + * @lock: lock protecting queue_list, queue_len & index + * @queue_wait: socket client's wait queue + * @bat_priv: pointer to soft_iface this client belongs to + */ struct batadv_socket_client { struct list_head queue_list; unsigned int queue_len; unsigned char index; - spinlock_t lock; /* protects queue_list, queue_len, index */ + spinlock_t lock; /* protects queue_list, queue_len & index */ wait_queue_head_t queue_wait; struct batadv_priv *bat_priv; }; +/** + * struct batadv_socket_packet - layer2 icmp packet for socket client + * @list: list node for batadv_socket_client::queue_list + * @icmp_len: size of the layer2 icmp packet + * @icmp_packet: layer2 icmp packet + */ struct batadv_socket_packet { struct list_head list; size_t icmp_len; struct batadv_icmp_packet_rr icmp_packet; }; +/** + * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN + * @orig: originator address of backbone node (mac address of primary iface) + * @vid: vlan id this gateway was detected on + * @hash_entry: hlist node for batadv_priv_bla::backbone_hash + * @bat_priv: pointer to soft_iface this backbone gateway belongs to + * @lasttime: last time we heard of this backbone gw + * @wait_periods: grace time for bridge forward delays and bla group forming at + * bootup phase - no bcast traffic is formwared until it has elapsed + * @request_sent: if this bool is set to true we are out of sync with this + * backbone gateway - no bcast traffic is formwared until the situation was + * resolved + * @crc: crc16 checksum over all claims + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_bla_backbone_gw { uint8_t orig[ETH_ALEN]; - short vid; /* used VLAN ID */ + short vid; struct hlist_node hash_entry; struct batadv_priv *bat_priv; - unsigned long lasttime; /* last time we heard of this backbone gw */ + unsigned long lasttime; atomic_t wait_periods; atomic_t request_sent; + uint16_t crc; atomic_t refcount; struct rcu_head rcu; - uint16_t crc; /* crc checksum over all claims */ }; +/** + * struct batadv_bla_claim - claimed non-mesh client structure + * @addr: mac address of claimed non-mesh client + * @vid: vlan id this client was detected on + * @batadv_bla_backbone_gw: pointer to backbone gw claiming this client + * @lasttime: last time we heard of claim (locals only) + * @hash_entry: hlist node for batadv_priv_bla::claim_hash + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ struct batadv_bla_claim { uint8_t addr[ETH_ALEN]; short vid; struct batadv_bla_backbone_gw *backbone_gw; - unsigned long lasttime; /* last time we heard of claim (locals only) */ + unsigned long lasttime; + struct hlist_node hash_entry; struct rcu_head rcu; atomic_t refcount; - struct hlist_node hash_entry; }; #endif +/** + * struct batadv_tt_common_entry - tt local & tt global common data + * @addr: mac address of non-mesh client + * @hash_entry: hlist node for batadv_priv_tt::local_hash or for + * batadv_priv_tt::global_hash + * @flags: various state handling flags (see batadv_tt_client_flags) + * @added_at: timestamp used for purging stale tt common entries + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ struct batadv_tt_common_entry { uint8_t addr[ETH_ALEN]; struct hlist_node hash_entry; @@ -373,37 +618,76 @@ struct batadv_tt_common_entry { struct rcu_head rcu; }; +/** + * struct batadv_tt_local_entry - translation table local entry data + * @common: general translation table data + * @last_seen: timestamp used for purging stale tt local entries + */ struct batadv_tt_local_entry { struct batadv_tt_common_entry common; unsigned long last_seen; }; +/** + * struct batadv_tt_global_entry - translation table global entry data + * @common: general translation table data + * @orig_list: list of orig nodes announcing this non-mesh client + * @list_lock: lock protecting orig_list + * @roam_at: time at which TT_GLOBAL_ROAM was set + */ struct batadv_tt_global_entry { struct batadv_tt_common_entry common; struct hlist_head orig_list; - spinlock_t list_lock; /* protects the list */ - unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */ + spinlock_t list_lock; /* protects orig_list */ + unsigned long roam_at; }; +/** + * struct batadv_tt_orig_list_entry - orig node announcing a non-mesh client + * @orig_node: pointer to orig node announcing this non-mesh client + * @ttvn: translation table version number which added the non-mesh client + * @list: list node for batadv_tt_global_entry::orig_list + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ struct batadv_tt_orig_list_entry { struct batadv_orig_node *orig_node; uint8_t ttvn; + struct hlist_node list; atomic_t refcount; struct rcu_head rcu; - struct hlist_node list; }; +/** + * struct batadv_tt_change_node - structure for tt changes occured + * @list: list node for batadv_priv_tt::changes_list + * @change: holds the actual translation table diff data + */ struct batadv_tt_change_node { struct list_head list; struct batadv_tt_change change; }; +/** + * struct batadv_tt_req_node - data to keep track of the tt requests in flight + * @addr: mac address address of the originator this request was sent to + * @issued_at: timestamp used for purging stale tt requests + * @list: list node for batadv_priv_tt::req_list + */ struct batadv_tt_req_node { uint8_t addr[ETH_ALEN]; unsigned long issued_at; struct list_head list; }; +/** + * struct batadv_tt_roam_node - roaming client data + * @addr: mac address of the client in the roaming phase + * @counter: number of allowed roaming events per client within a single + * OGM interval (changes are committed with each OGM) + * @first_time: timestamp used for purging stale roaming node entries + * @list: list node for batadv_priv_tt::roam_list + */ struct batadv_tt_roam_node { uint8_t addr[ETH_ALEN]; atomic_t counter; @@ -411,8 +695,19 @@ struct batadv_tt_roam_node { struct list_head list; }; -/* forw_packet - structure for forw_list maintaining packets to be - * send/forwarded +/** + * struct batadv_forw_packet - structure for bcast packets to be sent/forwarded + * @list: list node for batadv_socket_client::queue_list + * @send_time: execution time for delayed_work (packet sending) + * @own: bool for locally generated packets (local OGMs are re-scheduled after + * sending) + * @skb: bcast packet's skb buffer + * @packet_len: size of aggregated OGM packet inside the skb buffer + * @direct_link_flags: direct link flags for aggregated OGM packets + * @num_packets: counter for bcast packet retransmission + * @delayed_work: work queue callback item for packet sending + * @if_incoming: pointer incoming hard-iface or primary iface if locally + * generated packet */ struct batadv_forw_packet { struct hlist_node list; @@ -426,39 +721,69 @@ struct batadv_forw_packet { struct batadv_hard_iface *if_incoming; }; +/** + * struct batadv_frag_packet_list_entry - storage for fragment packet + * @list: list node for orig_node::frag_list + * @seqno: sequence number of the fragment + * @skb: fragment's skb buffer + */ struct batadv_frag_packet_list_entry { struct list_head list; uint16_t seqno; struct sk_buff *skb; }; +/** + * struct batadv_vis_info - local data for vis information + * @first_seen: timestamp used for purging stale vis info entries + * @recv_list: List of server-neighbors we have received this packet from. This + * packet should not be re-forward to them again. List elements are struct + * batadv_vis_recvlist_node + * @send_list: list of packets to be forwarded + * @refcount: number of contexts the object is used + * @hash_entry: hlist node for batadv_priv_vis::hash + * @bat_priv: pointer to soft_iface this orig node belongs to + * @skb_packet: contains the vis packet + */ struct batadv_vis_info { unsigned long first_seen; - /* list of server-neighbors we received a vis-packet - * from. we should not reply to them. - */ struct list_head recv_list; struct list_head send_list; struct kref refcount; struct hlist_node hash_entry; struct batadv_priv *bat_priv; - /* this packet might be part of the vis send queue. */ struct sk_buff *skb_packet; - /* vis_info may follow here */ } __packed; +/** + * struct batadv_vis_info_entry - contains link information for vis + * @src: source MAC of the link, all zero for local TT entry + * @dst: destination MAC of the link, client mac address for local TT entry + * @quality: transmission quality of the link, or 0 for local TT entry + */ struct batadv_vis_info_entry { uint8_t src[ETH_ALEN]; uint8_t dest[ETH_ALEN]; - uint8_t quality; /* quality = 0 client */ + uint8_t quality; } __packed; +/** + * struct batadv_vis_recvlist_node - list entry for batadv_vis_info::recv_list + * @list: list node for batadv_vis_info::recv_list + * @mac: MAC address of the originator from where the vis_info was received + */ struct batadv_vis_recvlist_node { struct list_head list; uint8_t mac[ETH_ALEN]; }; -/* While scanning for vis-entries of a particular vis-originator +/** + * struct batadv_vis_if_list_entry - auxiliary data for vis data generation + * @addr: MAC address of the interface + * @primary: true if this interface is the primary interface + * @list: list node the interface list + * + * While scanning for vis-entries of a particular vis-originator * this list collects its interfaces to create a subgraph/cluster * out of them later */ @@ -468,22 +793,26 @@ struct batadv_vis_if_list_entry { struct hlist_node list; }; +/** + * struct batadv_algo_ops - mesh algorithm callbacks + * @list: list node for the batadv_algo_list + * @name: name of the algorithm + * @bat_iface_enable: init routing info when hard-interface is enabled + * @bat_iface_disable: de-init routing info when hard-interface is disabled + * @bat_iface_update_mac: (re-)init mac addresses of the protocol information + * belonging to this hard-interface + * @bat_primary_iface_set: called when primary interface is selected / changed + * @bat_ogm_schedule: prepare a new outgoing OGM for the send queue + * @bat_ogm_emit: send scheduled OGM + */ struct batadv_algo_ops { struct hlist_node list; char *name; - /* init routing info when hard-interface is enabled */ int (*bat_iface_enable)(struct batadv_hard_iface *hard_iface); - /* de-init routing info when hard-interface is disabled */ void (*bat_iface_disable)(struct batadv_hard_iface *hard_iface); - /* (re-)init mac addresses of the protocol information - * belonging to this hard-interface - */ void (*bat_iface_update_mac)(struct batadv_hard_iface *hard_iface); - /* called when primary interface is selected / changed */ void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface); - /* prepare a new outgoing OGM for the send queue */ void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface); - /* send scheduled OGM */ void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet); }; -- cgit v1.2.3 From e76e4320a26ba6301dfd3e9a28a1e3faccd302ab Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 24 Dec 2012 11:14:07 +0900 Subject: batman-adv: rename random32() to prandom_u32() Use more preferable function name which implies using a pseudo-random number generator. Signed-off-by: Akinobu Mita Acked-by: Antonio Quartulli Signed-off-by: Marek Lindner Cc: Simon Wunderlich Cc: Antonio Quartulli Cc: b.a.t.m.a.n@lists.open-mesh.org Cc: "David S. Miller" Cc: netdev@vger.kernel.org Signed-off-by: Antonio Quartulli --- net/batman-adv/bat_iv_ogm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 8f2de43b7597..36b97278c118 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -123,7 +123,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv) unsigned int msecs; msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER; - msecs += random32() % (2 * BATADV_JITTER); + msecs += prandom_u32() % (2 * BATADV_JITTER); return jiffies + msecs_to_jiffies(msecs); } @@ -131,7 +131,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv) /* when do we schedule a ogm packet to be sent */ static unsigned long batadv_iv_ogm_fwd_send_time(void) { - return jiffies + msecs_to_jiffies(random32() % (BATADV_JITTER / 2)); + return jiffies + msecs_to_jiffies(prandom_u32() % (BATADV_JITTER / 2)); } /* apply hop penalty for a normal link */ -- cgit v1.2.3 From 5bc44dc8458cbac08c664f2f337326bf87cc924b Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 11 Jan 2013 10:19:51 +0100 Subject: batman-adv: postpone sysfs removal when unregistering When processing the unregister notify for a hard interface, removing the sysfs files may lead to a circular deadlock (rtnl mutex <-> s_active). To overcome this problem, postpone the sysfs removal in a worker. Reported-by: Sasha Levin Reported-by: Sven Eckelmann Signed-off-by: Simon Wunderlich Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/hard-interface.c | 24 ++++++++++++++++++++++-- net/batman-adv/soft-interface.c | 32 +++++++++++++++++++++++++++++--- net/batman-adv/types.h | 6 ++++++ 3 files changed, 57 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index f1d37cd81815..cb3191fe60fc 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -457,6 +457,24 @@ out: batadv_hardif_free_ref(primary_if); } +/** + * batadv_hardif_remove_interface_finish - cleans up the remains of a hardif + * @work: work queue item + * + * Free the parts of the hard interface which can not be removed under + * rtnl lock (to prevent deadlock situations). + */ +static void batadv_hardif_remove_interface_finish(struct work_struct *work) +{ + struct batadv_hard_iface *hard_iface; + + hard_iface = container_of(work, struct batadv_hard_iface, + cleanup_work); + + batadv_sysfs_del_hardif(&hard_iface->hardif_obj); + batadv_hardif_free_ref(hard_iface); +} + static struct batadv_hard_iface * batadv_hardif_add_interface(struct net_device *net_dev) { @@ -484,6 +502,9 @@ batadv_hardif_add_interface(struct net_device *net_dev) hard_iface->soft_iface = NULL; hard_iface->if_status = BATADV_IF_NOT_IN_USE; INIT_LIST_HEAD(&hard_iface->list); + INIT_WORK(&hard_iface->cleanup_work, + batadv_hardif_remove_interface_finish); + /* extra reference for return */ atomic_set(&hard_iface->refcount, 2); @@ -518,8 +539,7 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface) return; hard_iface->if_status = BATADV_IF_TO_BE_REMOVED; - batadv_sysfs_del_hardif(&hard_iface->hardif_obj); - batadv_hardif_free_ref(hard_iface); + queue_work(batadv_event_workqueue, &hard_iface->cleanup_work); } void batadv_hardif_remove_interfaces(void) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 3d6816667bfc..c9962fee44b1 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -449,6 +449,30 @@ static void batadv_interface_setup(struct net_device *dev) memset(priv, 0, sizeof(*priv)); } +/** + * batadv_softif_destroy_finish - cleans up the remains of a softif + * @work: work queue item + * + * Free the parts of the soft interface which can not be removed under + * rtnl lock (to prevent deadlock situations). + */ +static void batadv_softif_destroy_finish(struct work_struct *work) +{ + struct batadv_priv *bat_priv; + struct net_device *soft_iface; + + bat_priv = container_of(work, struct batadv_priv, + cleanup_work); + soft_iface = bat_priv->soft_iface; + + batadv_debugfs_del_meshif(soft_iface); + batadv_sysfs_del_meshif(soft_iface); + + rtnl_lock(); + unregister_netdevice(soft_iface); + rtnl_unlock(); +} + struct net_device *batadv_softif_create(const char *name) { struct net_device *soft_iface; @@ -463,6 +487,8 @@ struct net_device *batadv_softif_create(const char *name) goto out; bat_priv = netdev_priv(soft_iface); + bat_priv->soft_iface = soft_iface; + INIT_WORK(&bat_priv->cleanup_work, batadv_softif_destroy_finish); /* batadv_interface_stats() needs to be available as soon as * register_netdevice() has been called @@ -551,10 +577,10 @@ out: void batadv_softif_destroy(struct net_device *soft_iface) { - batadv_debugfs_del_meshif(soft_iface); - batadv_sysfs_del_meshif(soft_iface); + struct batadv_priv *bat_priv = netdev_priv(soft_iface); + batadv_mesh_free(soft_iface); - unregister_netdevice(soft_iface); + queue_work(batadv_event_workqueue, &bat_priv->cleanup_work); } int batadv_softif_is_valid(const struct net_device *net_dev) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 03197f5ebf6c..8d4a16c0ed50 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -68,6 +68,7 @@ struct batadv_hard_iface_bat_iv { * @soft_iface: the batman-adv interface which uses this network interface * @rcu: struct used for freeing in an RCU-safe manner * @bat_iv: BATMAN IV specific per hard interface data + * @cleanup_work: work queue callback item for hard interface deinit */ struct batadv_hard_iface { struct list_head list; @@ -81,6 +82,7 @@ struct batadv_hard_iface { struct net_device *soft_iface; struct rcu_head rcu; struct batadv_hard_iface_bat_iv bat_iv; + struct work_struct cleanup_work; }; /** @@ -428,6 +430,7 @@ struct batadv_priv_dat { /** * struct batadv_priv - per mesh interface data * @mesh_state: current status of the mesh (inactive/active/deactivating) + * @soft_iface: net device which holds this struct as private data * @stats: structure holding the data for the ndo_get_stats() call * @bat_counters: mesh internal traffic statistic counters (see batadv_counters) * @aggregated_ogms: bool indicating whether OGM aggregation is enabled @@ -457,6 +460,7 @@ struct batadv_priv_dat { * @forw_bat_list_lock: lock protecting forw_bat_list * @forw_bcast_list_lock: lock protecting forw_bcast_list * @orig_work: work queue callback item for orig node purging + * @cleanup_work: work queue callback item for soft interface deinit * @primary_if: one of the hard interfaces assigned to this mesh interface * becomes the primary interface * @bat_algo_ops: routing algorithm used by this mesh interface @@ -469,6 +473,7 @@ struct batadv_priv_dat { */ struct batadv_priv { atomic_t mesh_state; + struct net_device *soft_iface; struct net_device_stats stats; uint64_t __percpu *bat_counters; /* Per cpu counters */ atomic_t aggregated_ogms; @@ -502,6 +507,7 @@ struct batadv_priv { spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */ struct delayed_work orig_work; + struct work_struct cleanup_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops; #ifdef CONFIG_BATMAN_ADV_BLA -- cgit v1.2.3 From 0b87393146244c2dc31cb3c69c45f4948ca94ee1 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Fri, 4 Jan 2013 03:05:31 +0100 Subject: batman-adv: update copyright years Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_algo.h | 2 +- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bitarray.c | 2 +- net/batman-adv/bitarray.h | 2 +- net/batman-adv/bridge_loop_avoidance.c | 2 +- net/batman-adv/bridge_loop_avoidance.h | 2 +- net/batman-adv/debugfs.c | 2 +- net/batman-adv/debugfs.h | 2 +- net/batman-adv/distributed-arp-table.c | 2 +- net/batman-adv/distributed-arp-table.h | 2 +- net/batman-adv/gateway_client.c | 2 +- net/batman-adv/gateway_client.h | 2 +- net/batman-adv/gateway_common.c | 2 +- net/batman-adv/gateway_common.h | 2 +- net/batman-adv/hard-interface.c | 2 +- net/batman-adv/hard-interface.h | 2 +- net/batman-adv/hash.c | 2 +- net/batman-adv/hash.h | 2 +- net/batman-adv/icmp_socket.c | 2 +- net/batman-adv/icmp_socket.h | 2 +- net/batman-adv/main.c | 2 +- net/batman-adv/main.h | 2 +- net/batman-adv/originator.c | 2 +- net/batman-adv/originator.h | 2 +- net/batman-adv/packet.h | 2 +- net/batman-adv/ring_buffer.c | 2 +- net/batman-adv/ring_buffer.h | 2 +- net/batman-adv/routing.c | 2 +- net/batman-adv/routing.h | 2 +- net/batman-adv/send.c | 2 +- net/batman-adv/send.h | 2 +- net/batman-adv/soft-interface.c | 2 +- net/batman-adv/soft-interface.h | 2 +- net/batman-adv/sysfs.c | 2 +- net/batman-adv/sysfs.h | 2 +- net/batman-adv/translation-table.c | 2 +- net/batman-adv/translation-table.h | 2 +- net/batman-adv/types.h | 2 +- net/batman-adv/unicast.c | 2 +- net/batman-adv/unicast.h | 2 +- net/batman-adv/vis.c | 2 +- net/batman-adv/vis.h | 2 +- 42 files changed, 42 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_algo.h b/net/batman-adv/bat_algo.h index a0ba3bff9b36..a4808c29ea3d 100644 --- a/net/batman-adv/bat_algo.h +++ b/net/batman-adv/bat_algo.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 36b97278c118..72fe1bbf7721 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c index 5453b17d8df2..973982414d58 100644 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h index cebaae7e148b..a81b9322e382 100644 --- a/net/batman-adv/bitarray.h +++ b/net/batman-adv/bitarray.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 9971892bcbf8..30f46526cbbd 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich * diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index 196d9a0254bc..dea2fbc5d98d 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich * diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index e47555b4280e..6ae86516db4d 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/debugfs.h b/net/batman-adv/debugfs.h index 3319e1f21f55..f8c3849edff4 100644 --- a/net/batman-adv/debugfs.h +++ b/net/batman-adv/debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 8e1d89d2b1c1..7485a78484ff 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index d060c033e7de..125c8c6fcfad 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Antonio Quartulli * diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index dd07c7e3654f..074107f2cfaa 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index f0d129e323c8..039902dca4a6 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 9001208d1752..84bb2b18d711 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h index 13697f6e7113..509b2bf8c2f4 100644 --- a/net/batman-adv/gateway_common.h +++ b/net/batman-adv/gateway_common.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index cb3191fe60fc..368219e026a9 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 3732366e7445..308437d52e22 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index 15a849c2d414..7198dafd3bf3 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index ea0214824327..1b4da72f2093 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2006-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 87ca8095b011..0ba6c899b2d3 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h index 29443a1dbb5c..1fcca37b6223 100644 --- a/net/batman-adv/icmp_socket.h +++ b/net/batman-adv/icmp_socket.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index f65a222b7b83..21fe6987733b 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index af73d603455d..74eb960c58fb 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 92a55fddf44b..457ea445217c 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 9778e656dec7..286bf743e76a 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index cb6405bf755c..ed0aa89bbf8b 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c index c8f61e395b74..ccab0bbdbb59 100644 --- a/net/batman-adv/ring_buffer.c +++ b/net/batman-adv/ring_buffer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h index fda8c17df273..3f92ae248e83 100644 --- a/net/batman-adv/ring_buffer.h +++ b/net/batman-adv/ring_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index db89238d6767..60ba03fc8390 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 9262279ea667..99eeafaba407 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 0b6f65bc4611..80ca65fc89a1 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 0078dece1abc..38e662f619ac 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index c9962fee44b1..2711e870f557 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 07a08fed28b9..43182e5e603a 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 84a55cb19b0b..afbba319d73a 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h index 3fd1412b0620..479acf4c16f4 100644 --- a/net/batman-adv/sysfs.h +++ b/net/batman-adv/sysfs.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Marek Lindner * diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 877acd149178..fb15b4c076f7 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 46d4451a59ee..ab8e683b402f 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich, Antonio Quartulli * diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 8d4a16c0ed50..4cd87a0b5b80 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index f56bccf4229e..50e079f00be6 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Andreas Langer * diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h index 61abba58bd8f..429cf8a4a31e 100644 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: * * Andreas Langer * diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 2e70ec113205..22d2785177d1 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich * diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h index 873282fa86da..ad92b0e3c230 100644 --- a/net/batman-adv/vis.h +++ b/net/batman-adv/vis.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2012 B.A.T.M.A.N. contributors: +/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * -- cgit v1.2.3 From 5ac2a177298e1f245aa54cce4030842f3abe5aea Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 3 Jan 2013 12:22:10 +0100 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich Signed-off-by: Antonio Quartulli --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 74eb960c58fb..ced08b936a96 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -26,7 +26,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2012.5.0" +#define BATADV_SOURCE_VERSION "2013.1.0" #endif /* B.A.T.M.A.N. parameters */ -- cgit v1.2.3 From ca97a644d752b46e5e08526e36705c3b0dd03f5f Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 20 Jan 2013 07:39:00 +0000 Subject: ipv6: Introduce ipv6_addr_is_solict_mult() to check Solicited Node Multicast Addresses. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/addrconf.h | 8 ++++++++ net/ipv6/ndisc.c | 6 +----- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index c6a44213fc74..3a3eeb407f4b 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -305,6 +305,14 @@ static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); } +static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) +{ + return (addr->s6_addr32[0] == htonl(0xff020000) && + addr->s6_addr32[1] == htonl(0x00000000) && + addr->s6_addr32[2] == htonl(0x00000001) && + addr->s6_addr[12] == 0xff); +} + #ifdef CONFIG_PROC_FS extern int if6_proc_init(void); extern void if6_proc_exit(void); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 350f86005c13..903191adb5ca 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -685,11 +685,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) * RFC2461 7.1.1: * DAD has to be destined for solicited node multicast address. */ - if (dad && - !(daddr->s6_addr32[0] == htonl(0xff020000) && - daddr->s6_addr32[1] == htonl(0x00000000) && - daddr->s6_addr32[2] == htonl(0x00000001) && - daddr->s6_addr [12] == 0xff )) { + if (dad && !ipv6_addr_is_solict_mult(daddr)) { ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n"); return; } -- cgit v1.2.3 From fb568637e58a8459edd551af1160f27ea53e8367 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sun, 20 Jan 2013 07:39:18 +0000 Subject: ndisc: Make several arguments for ndisc_send_na() boolean. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 903191adb5ca..067a0d2eb557 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -490,7 +490,7 @@ static void __ndisc_send(struct net_device *dev, static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, - int router, int solicited, int override, int inc_opt) + bool router, bool solicited, bool override, bool inc_opt) { struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; @@ -776,11 +776,11 @@ static void ndisc_recv_ns(struct sk_buff *skb) } if (is_router < 0) - is_router = !!idev->cnf.forwarding; + is_router = idev->cnf.forwarding; if (dad) { ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target, - is_router, 0, (ifp != NULL), 1); + !!is_router, false, (ifp != NULL), true); goto out; } @@ -801,8 +801,8 @@ static void ndisc_recv_ns(struct sk_buff *skb) NEIGH_UPDATE_F_OVERRIDE); if (neigh || !dev->header_ops) { ndisc_send_na(dev, neigh, saddr, &msg->target, - is_router, - 1, (ifp != NULL && inc), inc); + !!is_router, + true, (ifp != NULL && inc), inc); if (neigh) neigh_release(neigh); } -- cgit v1.2.3 From e6f30c731718db45cec380964dfee210307cfc4a Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 18 Jan 2013 07:17:30 +0000 Subject: netfilter: x_tables: add xt_bpf match Support arbitrary linux socket filter (BPF) programs as x_tables match rules. This allows for very expressive filters, and on platforms with BPF JIT appears competitive with traditional hardcoded iptables rules using the u32 match. The size of the filter has been artificially limited to 64 instructions maximum to avoid bloating the size of each rule using this new match. Signed-off-by: Willem de Bruijn Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/xt_bpf.h | 17 ++++++++ net/netfilter/Kconfig | 9 +++++ net/netfilter/Makefile | 1 + net/netfilter/xt_bpf.c | 73 +++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 include/uapi/linux/netfilter/xt_bpf.h create mode 100644 net/netfilter/xt_bpf.c (limited to 'net') diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h new file mode 100644 index 000000000000..5dda450eb55b --- /dev/null +++ b/include/uapi/linux/netfilter/xt_bpf.h @@ -0,0 +1,17 @@ +#ifndef _XT_BPF_H +#define _XT_BPF_H + +#include +#include + +#define XT_BPF_MAX_NUM_INSTR 64 + +struct xt_bpf_info { + __u16 bpf_program_num_elem; + struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR]; + + /* only used in the kernel */ + struct sk_filter *filter __attribute__((aligned(8))); +}; + +#endif /*_XT_BPF_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index bb48607d4ee4..eb2c8ebf6d99 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -811,6 +811,15 @@ config NETFILTER_XT_MATCH_ADDRTYPE If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_BPF + tristate '"bpf" match support' + depends on NETFILTER_ADVANCED + help + BPF matching applies a linux socket filter to each packet and + accepts those for which the filter returns non-zero. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_MATCH_CLUSTER tristate '"cluster" match support' depends on NF_CONNTRACK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index b3bbda60945e..a1abf87d43bf 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o # matches obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o +obj-$(CONFIG_NETFILTER_XT_MATCH_BPF) += xt_bpf.o 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 diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c new file mode 100644 index 000000000000..12d4da8e6c77 --- /dev/null +++ b/net/netfilter/xt_bpf.c @@ -0,0 +1,73 @@ +/* Xtables module to match packets using a BPF filter. + * Copyright 2013 Google Inc. + * Written by Willem de Bruijn + * + * 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 + +MODULE_AUTHOR("Willem de Bruijn "); +MODULE_DESCRIPTION("Xtables: BPF filter match"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_bpf"); +MODULE_ALIAS("ip6t_bpf"); + +static int bpf_mt_check(const struct xt_mtchk_param *par) +{ + struct xt_bpf_info *info = par->matchinfo; + struct sock_fprog program; + + program.len = info->bpf_program_num_elem; + program.filter = (struct sock_filter __user *) info->bpf_program; + if (sk_unattached_filter_create(&info->filter, &program)) { + pr_info("bpf: check failed: parse error\n"); + return -EINVAL; + } + + return 0; +} + +static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_bpf_info *info = par->matchinfo; + + return SK_RUN_FILTER(info->filter, skb); +} + +static void bpf_mt_destroy(const struct xt_mtdtor_param *par) +{ + const struct xt_bpf_info *info = par->matchinfo; + sk_unattached_filter_destroy(info->filter); +} + +static struct xt_match bpf_mt_reg __read_mostly = { + .name = "bpf", + .revision = 0, + .family = NFPROTO_UNSPEC, + .checkentry = bpf_mt_check, + .match = bpf_mt, + .destroy = bpf_mt_destroy, + .matchsize = sizeof(struct xt_bpf_info), + .me = THIS_MODULE, +}; + +static int __init bpf_mt_init(void) +{ + return xt_register_match(&bpf_mt_reg); +} + +static void __exit bpf_mt_exit(void) +{ + xt_unregister_match(&bpf_mt_reg); +} + +module_init(bpf_mt_init); +module_exit(bpf_mt_exit); -- cgit v1.2.3 From 315ff09dbaebc6061948102d7ed8781d4ee46f36 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:47:50 +0000 Subject: ndisc: Reduce number of arguments for ndisc_fill_addr_option(). Add pointer to struct net_device (dev) and remove data_len (= dev->addr_len) and addr_type (= dev->type). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 067a0d2eb557..49dfc2a73ee0 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -148,10 +148,11 @@ static inline int ndisc_opt_addr_space(struct net_device *dev) return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); } -static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, - unsigned short addr_type) +static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, + struct net_device *dev) { - int pad = ndisc_addr_option_pad(addr_type); + int pad = ndisc_addr_option_pad(dev->type); + int data_len = dev->addr_len; int space = NDISC_OPT_SPACE(data_len + pad); opt[0] = type; @@ -420,8 +421,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, } if (llinfo) - ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, - dev->addr_len, dev->type); + ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev); hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, @@ -1469,8 +1469,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) */ if (ha) - opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, - dev->addr_len, dev->type); + opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev); /* * build redirect option and copy skb over to the new packet. -- cgit v1.2.3 From c558e9fca876d6d0463a7c337d2291774b9c6f96 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:47:56 +0000 Subject: ndisc: Move ndisc_opt_addr_space() to include/net/ndisc.h. This also makes ndisc_opt_addr_data() and ndisc_fill_addr_option() use ndisc_opt_addr_space(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ndisc.h | 8 +++++++- net/ipv6/ndisc.c | 7 +------ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/ndisc.h b/include/net/ndisc.h index ec48f42db5ed..745bf741e029 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -127,13 +127,19 @@ static int ndisc_addr_option_pad(unsigned short type) } } +static inline int ndisc_opt_addr_space(struct net_device *dev) +{ + return NDISC_OPT_SPACE(dev->addr_len + + ndisc_addr_option_pad(dev->type)); +} + static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, struct net_device *dev) { u8 *lladdr = (u8 *)(p + 1); int lladdrlen = p->nd_opt_len << 3; int prepad = ndisc_addr_option_pad(dev->type); - if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) + if (lladdrlen != ndisc_opt_addr_space(dev)) return NULL; return lladdr + prepad; } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 49dfc2a73ee0..82ccf0a43063 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -143,17 +143,12 @@ struct neigh_table nd_tbl = { .gc_thresh3 = 1024, }; -static inline int ndisc_opt_addr_space(struct net_device *dev) -{ - return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); -} - static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, struct net_device *dev) { int pad = ndisc_addr_option_pad(dev->type); int data_len = dev->addr_len; - int space = NDISC_OPT_SPACE(data_len + pad); + int space = ndisc_opt_addr_space(dev); opt[0] = type; opt[1] = space>>3; -- cgit v1.2.3 From 6bce6b4e16e46cc860175b9e10a283194ef9f004 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:03 +0000 Subject: ndisc: Use skb_linearize() instead of pskb_may_pull(skb, skb->len). Suggested by Eric Dumazet . Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 82ccf0a43063..539b2ec37d3a 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1509,7 +1509,7 @@ int ndisc_rcv(struct sk_buff *skb) { struct nd_msg *msg; - if (!pskb_may_pull(skb, skb->len)) + if (skb_linearize(skb)) return 0; msg = (struct nd_msg *)skb_transport_header(skb); -- cgit v1.2.3 From 9c86dafe94f03679b77d85915e65da1304005a7c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:09 +0000 Subject: ndisc: Introduce ndisc_fill_redirect_hdr_option(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 539b2ec37d3a..53a545f32625 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1341,6 +1341,19 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); } +static u8 *ndisc_fill_redirect_hdr_option(u8 *opt, struct sk_buff *orig_skb, + int rd_len) +{ + memset(opt, 0, 8); + *(opt++) = ND_OPT_REDIRECT_HDR; + *(opt++) = (rd_len >> 3); + opt += 6; + + memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8); + + return opt + rd_len - 8; +} + void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) { struct net_device *dev = skb->dev; @@ -1470,12 +1483,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) * build redirect option and copy skb over to the new packet. */ - memset(opt, 0, 8); - *(opt++) = ND_OPT_REDIRECT_HDR; - *(opt++) = (rd_len >> 3); - opt += 6; - - memcpy(opt, ipv6_hdr(skb), rd_len - 8); + if (rd_len) + opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len); msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, len, IPPROTO_ICMPV6, -- cgit v1.2.3 From de09334b9326632bbf1a74bfd8b01866cbbf2f61 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:14 +0000 Subject: ndisc: Introduce ndisc_alloc_skb() helper. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 53a545f32625..1776a0deee7f 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -366,6 +366,29 @@ static void pndisc_destructor(struct pneigh_entry *n) ipv6_dev_mc_dec(dev, &maddr); } +static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, + int len) +{ + int hlen = LL_RESERVED_SPACE(dev); + int tlen = dev->needed_tailroom; + struct sock *sk = dev_net(dev)->ipv6.ndisc_sk; + struct sk_buff *skb; + int err; + + skb = sock_alloc_send_skb(sk, + hlen + sizeof(struct ipv6hdr) + len + tlen, + 1, &err); + if (!skb) { + ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n", + __func__, err); + return NULL; + } + + skb_reserve(skb, hlen); + + return skb; +} + static struct sk_buff *ndisc_build_skb(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *saddr, @@ -377,10 +400,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, struct sock *sk = net->ipv6.ndisc_sk; struct sk_buff *skb; struct icmp6hdr *hdr; - int hlen = LL_RESERVED_SPACE(dev); - int tlen = dev->needed_tailroom; int len; - int err; u8 *opt; if (!dev->addr_len) @@ -390,17 +410,10 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (llinfo) len += ndisc_opt_addr_space(dev); - skb = sock_alloc_send_skb(sk, - (sizeof(struct ipv6hdr) + - len + hlen + tlen), - 1, &err); - if (!skb) { - ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n", - __func__, err); + skb = ndisc_alloc_skb(dev, len); + if (!skb) return NULL; - } - skb_reserve(skb, hlen); ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); skb->transport_header = skb->tail; @@ -1369,7 +1382,6 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct inet6_dev *idev; struct flowi6 fl6; u8 *opt; - int hlen, tlen; int rd_len; int err; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; @@ -1439,20 +1451,10 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) rd_len &= ~0x7; len += rd_len; - hlen = LL_RESERVED_SPACE(dev); - tlen = dev->needed_tailroom; - buff = sock_alloc_send_skb(sk, - (sizeof(struct ipv6hdr) + - len + hlen + tlen), - 1, &err); - if (buff == NULL) { - ND_PRINTK(0, err, - "Redirect: %s failed to allocate an skb, err=%d\n", - __func__, err); + buff = ndisc_alloc_skb(dev, len); + if (!buff) goto release; - } - skb_reserve(buff, hlen); ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, IPPROTO_ICMPV6, len); -- cgit v1.2.3 From 2576f17dfad402e2446244238ed22dddf35c2e53 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:19 +0000 Subject: ipv6: Unshare ip6_nd_hdr() and change return type to void. - move ip6_nd_hdr() to its users' source files. In net/ipv6/mcast.c, it will be called ip6_mc_hdr(). - make return type to void since this function never fails. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 7 ------- net/ipv6/ip6_output.c | 33 --------------------------------- net/ipv6/mcast.c | 29 +++++++++++++++++++++++++++-- net/ipv6/ndisc.c | 25 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 464c6f70eca1..c1878f7049c8 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -661,13 +661,6 @@ extern int ip6_xmit(struct sock *sk, struct ipv6_txoptions *opt, int tclass); -extern int ip6_nd_hdr(struct sock *sk, - struct sk_buff *skb, - struct net_device *dev, - const struct in6_addr *saddr, - const struct in6_addr *daddr, - int proto, int len); - extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); extern int ip6_append_data(struct sock *sk, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index b0895f5d5fc6..7eee94c27f8d 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -254,39 +254,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, EXPORT_SYMBOL(ip6_xmit); -/* - * To avoid extra problems ND packets are send through this - * routine. It's code duplication but I really want to avoid - * extra checks since ipv6_build_header is used by TCP (which - * is for us performance critical) - */ - -int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev, - const struct in6_addr *saddr, const struct in6_addr *daddr, - int proto, int len) -{ - struct ipv6_pinfo *np = inet6_sk(sk); - struct ipv6hdr *hdr; - - skb->protocol = htons(ETH_P_IPV6); - skb->dev = dev; - - skb_reset_network_header(skb); - skb_put(skb, sizeof(struct ipv6hdr)); - hdr = ipv6_hdr(skb); - - ip6_flow_hdr(hdr, 0, 0); - - hdr->payload_len = htons(len); - hdr->nexthdr = proto; - hdr->hop_limit = np->hop_limit; - - hdr->saddr = *saddr; - hdr->daddr = *daddr; - - return 0; -} - static int ip6_call_ra_chain(struct sk_buff *skb, int sel) { struct ip6_ra_chain *ra; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 587a84530a57..f25002aaf624 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1313,6 +1313,31 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted) return scount; } +static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, + struct net_device *dev, + const struct in6_addr *saddr, + const struct in6_addr *daddr, + int proto, int len) +{ + struct ipv6hdr *hdr; + + skb->protocol = htons(ETH_P_IPV6); + skb->dev = dev; + + skb_reset_network_header(skb); + skb_put(skb, sizeof(struct ipv6hdr)); + hdr = ipv6_hdr(skb); + + ip6_flow_hdr(hdr, 0, 0); + + hdr->payload_len = htons(len); + hdr->nexthdr = proto; + hdr->hop_limit = inet6_sk(sk)->hop_limit; + + hdr->saddr = *saddr; + hdr->daddr = *daddr; +} + static struct sk_buff *mld_newpack(struct net_device *dev, int size) { struct net *net = dev_net(dev); @@ -1348,7 +1373,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) } else saddr = &addr_buf; - ip6_nd_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); + ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); @@ -1740,7 +1765,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) } else saddr = &addr_buf; - ip6_nd_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); + ip6_mc_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 1776a0deee7f..7ce266f34cc7 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -389,6 +389,31 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, return skb; } +static void ip6_nd_hdr(struct sock *sk, + struct sk_buff *skb, struct net_device *dev, + const struct in6_addr *saddr, + const struct in6_addr *daddr, + int proto, int len) +{ + struct ipv6hdr *hdr; + + skb->protocol = htons(ETH_P_IPV6); + skb->dev = dev; + + skb_reset_network_header(skb); + skb_put(skb, sizeof(struct ipv6hdr)); + hdr = ipv6_hdr(skb); + + ip6_flow_hdr(hdr, 0, 0); + + hdr->payload_len = htons(len); + hdr->nexthdr = proto; + hdr->hop_limit = inet6_sk(sk)->hop_limit; + + hdr->saddr = *saddr; + hdr->daddr = *daddr; +} + static struct sk_buff *ndisc_build_skb(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *saddr, -- cgit v1.2.3 From c8d6c380d9463e1405d821db7071e39f63acfb28 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:24 +0000 Subject: ndisc: Simplify arguments for ip6_nd_hdr(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7ce266f34cc7..05c69964f356 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -389,11 +389,10 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, return skb; } -static void ip6_nd_hdr(struct sock *sk, - struct sk_buff *skb, struct net_device *dev, +static void ip6_nd_hdr(struct sk_buff *skb, struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr, - int proto, int len) + int hop_limit, int len) { struct ipv6hdr *hdr; @@ -407,8 +406,8 @@ static void ip6_nd_hdr(struct sock *sk, ip6_flow_hdr(hdr, 0, 0); hdr->payload_len = htons(len); - hdr->nexthdr = proto; - hdr->hop_limit = inet6_sk(sk)->hop_limit; + hdr->nexthdr = IPPROTO_ICMPV6; + hdr->hop_limit = hop_limit; hdr->saddr = *saddr; hdr->daddr = *daddr; @@ -439,7 +438,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (!skb) return NULL; - ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); + ip6_nd_hdr(skb, dev, saddr, daddr, inet6_sk(sk)->hop_limit, len); skb->transport_header = skb->tail; skb_put(skb, len); @@ -1480,8 +1479,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, - IPPROTO_ICMPV6, len); + ip6_nd_hdr(buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, + inet6_sk(sk)->hop_limit, len); skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); skb_put(buff, len); -- cgit v1.2.3 From f382d03ad003815be6dc268711659d4475fb7d28 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:29 +0000 Subject: ndisc: Set skb->dev and skb->protocol inside ndisc_alloc_skb(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 05c69964f356..ed3ba8eb9505 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -384,21 +384,21 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, return NULL; } + skb->protocol = htons(ETH_P_IPV6); + skb->dev = dev; + skb_reserve(skb, hlen); return skb; } -static void ip6_nd_hdr(struct sk_buff *skb, struct net_device *dev, +static void ip6_nd_hdr(struct sk_buff *skb, const struct in6_addr *saddr, const struct in6_addr *daddr, int hop_limit, int len) { struct ipv6hdr *hdr; - skb->protocol = htons(ETH_P_IPV6); - skb->dev = dev; - skb_reset_network_header(skb); skb_put(skb, sizeof(struct ipv6hdr)); hdr = ipv6_hdr(skb); @@ -438,7 +438,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (!skb) return NULL; - ip6_nd_hdr(skb, dev, saddr, daddr, inet6_sk(sk)->hop_limit, len); + ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, len); skb->transport_header = skb->tail; skb_put(skb, len); @@ -1479,7 +1479,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - ip6_nd_hdr(buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr, + ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr, inet6_sk(sk)->hop_limit, len); skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); -- cgit v1.2.3 From af9a997629488ba5f92dc3d76e1ec51fcd759c5d Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:34 +0000 Subject: ndisc: Remove dev argument for ndisc_send_skb(). Since we have skb->dev, use it. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ed3ba8eb9505..200b2b22b9ba 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -463,14 +463,14 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, return skb; } -static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, +static void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, const struct in6_addr *saddr, struct icmp6hdr *icmp6h) { struct flowi6 fl6; struct dst_entry *dst; - struct net *net = dev_net(dev); + struct net *net = dev_net(skb->dev); struct sock *sk = net->ipv6.ndisc_sk; struct inet6_dev *idev; int err; @@ -478,8 +478,8 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, type = icmp6h->icmp6_type; - icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); - dst = icmp6_dst_alloc(dev, &fl6); + icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); + dst = icmp6_dst_alloc(skb->dev, &fl6); if (IS_ERR(dst)) { kfree_skb(skb); return; @@ -516,7 +516,7 @@ static void __ndisc_send(struct net_device *dev, if (!skb) return; - ndisc_send_skb(skb, dev, daddr, saddr, icmp6h); + ndisc_send_skb(skb, daddr, saddr, icmp6h); } static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, -- cgit v1.2.3 From 527a150fb2292a59ca0545dace8d482581253532 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:39 +0000 Subject: ndisc: Defer building IPv6 header. Build ICMPv6 message first and make buffer management easier; we can use skb->len when filling checksum in ICMPv6 header, and then build IP header with length field. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 200b2b22b9ba..09171fdbc312 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -387,7 +387,7 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; - skb_reserve(skb, hlen); + skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); return skb; } @@ -399,8 +399,8 @@ static void ip6_nd_hdr(struct sk_buff *skb, { struct ipv6hdr *hdr; + skb_push(skb, sizeof(*hdr)); skb_reset_network_header(skb); - skb_put(skb, sizeof(struct ipv6hdr)); hdr = ipv6_hdr(skb); ip6_flow_hdr(hdr, 0, 0); @@ -438,8 +438,6 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (!skb) return NULL; - ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, len); - skb->transport_header = skb->tail; skb_put(skb, len); @@ -455,10 +453,12 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (llinfo) ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev); - hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, + hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, csum_partial(hdr, - len, 0)); + skb->len, 0)); + + ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); return skb; } @@ -1479,9 +1479,6 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr, - inet6_sk(sk)->hop_limit, len); - skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); skb_put(buff, len); msg = (struct rd_msg *)icmp6_hdr(buff); @@ -1513,8 +1510,11 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len); msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, - len, IPPROTO_ICMPV6, - csum_partial(msg, len, 0)); + buff->len, IPPROTO_ICMPV6, + csum_partial(msg, buff->len, 0)); + + ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr, + inet6_sk(sk)->hop_limit, buff->len); skb_dst_set(buff, dst); rcu_read_lock(); -- cgit v1.2.3 From 5135e633f92ab4deb3600a30cbbec6e0929fc8a4 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:44 +0000 Subject: ndisc: Reset skb->trasport_headner inside ndisc_alloc_send_skb(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 09171fdbc312..62b1415bec25 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -388,6 +388,7 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, skb->dev = dev; skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); + skb_reset_transport_header(skb); return skb; } @@ -438,7 +439,6 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (!skb) return NULL; - skb->transport_header = skb->tail; skb_put(skb, len); hdr = (struct icmp6hdr *)skb_transport_header(skb); @@ -1479,7 +1479,6 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); skb_put(buff, len); msg = (struct rd_msg *)icmp6_hdr(buff); -- cgit v1.2.3 From 2ce13576144ade3e9340efce9620a6dd338aedc7 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:49 +0000 Subject: ndisc: Calculate message body length and option length separately. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 62b1415bec25..c2b16ffeb1a8 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -426,6 +426,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, struct sk_buff *skb; struct icmp6hdr *hdr; int len; + int optlen = 0; u8 *opt; if (!dev->addr_len) @@ -433,13 +434,13 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); if (llinfo) - len += ndisc_opt_addr_space(dev); + optlen += ndisc_opt_addr_space(dev); - skb = ndisc_alloc_skb(dev, len); + skb = ndisc_alloc_skb(dev, len + optlen); if (!skb) return NULL; - skb_put(skb, len); + skb_put(skb, len + optlen); hdr = (struct icmp6hdr *)skb_transport_header(skb); memcpy(hdr, icmp6h, sizeof(*hdr)); @@ -1396,7 +1397,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct net_device *dev = skb->dev; struct net *net = dev_net(dev); struct sock *sk = net->ipv6.ndisc_sk; - int len = sizeof(struct rd_msg); + int optlen = 0; struct inet_peer *peer; struct sk_buff *buff; struct rd_msg *msg; @@ -1463,7 +1464,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) memcpy(ha_buf, neigh->ha, dev->addr_len); read_unlock_bh(&neigh->lock); ha = ha_buf; - len += ndisc_opt_addr_space(dev); + optlen += ndisc_opt_addr_space(dev); } else read_unlock_bh(&neigh->lock); @@ -1471,15 +1472,16 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) } rd_len = min_t(unsigned int, - IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8); + IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen, + skb->len + 8); rd_len &= ~0x7; - len += rd_len; + optlen += rd_len; - buff = ndisc_alloc_skb(dev, len); + buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!buff) goto release; - skb_put(buff, len); + skb_put(buff, sizeof(*msg) + optlen); msg = (struct rd_msg *)icmp6_hdr(buff); memset(&msg->icmph, 0, sizeof(struct icmp6hdr)); -- cgit v1.2.3 From 5f5a0115635bf678d267cbc72169a718e8724d03 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:53 +0000 Subject: ndisc: Make ndisc_fill_xxx_option() for sk_buff. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index c2b16ffeb1a8..ed786441bc51 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -143,12 +143,12 @@ struct neigh_table nd_tbl = { .gc_thresh3 = 1024, }; -static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, - struct net_device *dev) +static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data) { - int pad = ndisc_addr_option_pad(dev->type); - int data_len = dev->addr_len; - int space = ndisc_opt_addr_space(dev); + int pad = ndisc_addr_option_pad(skb->dev->type); + int data_len = skb->dev->addr_len; + int space = ndisc_opt_addr_space(skb->dev); + u8 *opt = skb_put(skb, space); opt[0] = type; opt[1] = space>>3; @@ -162,7 +162,6 @@ static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, opt += data_len; if ((space -= data_len) > 0) memset(opt, 0, space); - return opt + space; } static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, @@ -440,7 +439,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (!skb) return NULL; - skb_put(skb, len + optlen); + skb_put(skb, len); hdr = (struct icmp6hdr *)skb_transport_header(skb); memcpy(hdr, icmp6h, sizeof(*hdr)); @@ -452,7 +451,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, } if (llinfo) - ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev); + ndisc_fill_addr_option(skb, llinfo, dev->dev_addr); hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, @@ -1379,17 +1378,18 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); } -static u8 *ndisc_fill_redirect_hdr_option(u8 *opt, struct sk_buff *orig_skb, - int rd_len) +static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb, + struct sk_buff *orig_skb, + int rd_len) { + u8 *opt = skb_put(skb, rd_len); + memset(opt, 0, 8); *(opt++) = ND_OPT_REDIRECT_HDR; *(opt++) = (rd_len >> 3); opt += 6; memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8); - - return opt + rd_len - 8; } void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) @@ -1406,7 +1406,6 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct dst_entry *dst; struct inet6_dev *idev; struct flowi6 fl6; - u8 *opt; int rd_len; int err; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; @@ -1481,7 +1480,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - skb_put(buff, sizeof(*msg) + optlen); + skb_put(buff, sizeof(*msg)); msg = (struct rd_msg *)icmp6_hdr(buff); memset(&msg->icmph, 0, sizeof(struct icmp6hdr)); @@ -1494,21 +1493,19 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) msg->target = *target; msg->dest = ipv6_hdr(skb)->daddr; - opt = msg->opt; - /* * include target_address option */ if (ha) - opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev); + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha); /* * build redirect option and copy skb over to the new packet. */ if (rd_len) - opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len); + ndisc_fill_redirect_hdr_option(buff, skb, rd_len); msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, buff->len, IPPROTO_ICMPV6, -- cgit v1.2.3 From aa4bdd4b3f43274252c1280f6464850b0f004fc8 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:48:58 +0000 Subject: ndisc: Remove icmp6h argument from ndisc_send_skb(). skb_transport_header() (thus icmp6_hdr()) is available here, use it. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ed786441bc51..f0c436b49255 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -465,8 +465,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, static void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, - const struct in6_addr *saddr, - struct icmp6hdr *icmp6h) + const struct in6_addr *saddr) { struct flowi6 fl6; struct dst_entry *dst; @@ -474,6 +473,7 @@ static void ndisc_send_skb(struct sk_buff *skb, struct sock *sk = net->ipv6.ndisc_sk; struct inet6_dev *idev; int err; + struct icmp6hdr *icmp6h = icmp6_hdr(skb); u8 type; type = icmp6h->icmp6_type; @@ -516,7 +516,7 @@ static void __ndisc_send(struct net_device *dev, if (!skb) return; - ndisc_send_skb(skb, daddr, saddr, icmp6h); + ndisc_send_skb(skb, daddr, saddr); } static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, -- cgit v1.2.3 From f4de84c64e3d801965ee4c6dadd19b76b9424bbf Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:49:03 +0000 Subject: ndisc: Use ndisc_send_skb() for redirect. Reuse dst if one is attached with skb. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f0c436b49255..437781c2af41 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -467,10 +467,8 @@ static void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, const struct in6_addr *saddr) { - struct flowi6 fl6; - struct dst_entry *dst; + struct dst_entry *dst = skb_dst(skb); struct net *net = dev_net(skb->dev); - struct sock *sk = net->ipv6.ndisc_sk; struct inet6_dev *idev; int err; struct icmp6hdr *icmp6h = icmp6_hdr(skb); @@ -478,14 +476,19 @@ static void ndisc_send_skb(struct sk_buff *skb, type = icmp6h->icmp6_type; - icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); - dst = icmp6_dst_alloc(skb->dev, &fl6); - if (IS_ERR(dst)) { - kfree_skb(skb); - return; - } + if (!dst) { + struct sock *sk = net->ipv6.ndisc_sk; + struct flowi6 fl6; - skb_dst_set(skb, dst); + icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); + dst = icmp6_dst_alloc(skb->dev, &fl6); + if (IS_ERR(dst)) { + kfree_skb(skb); + return; + } + + skb_dst_set(skb, dst); + } rcu_read_lock(); idev = __in6_dev_get(dst->dev); @@ -1404,10 +1407,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct in6_addr saddr_buf; struct rt6_info *rt; struct dst_entry *dst; - struct inet6_dev *idev; struct flowi6 fl6; int rd_len; - int err; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; bool ret; @@ -1515,17 +1516,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) inet6_sk(sk)->hop_limit, buff->len); skb_dst_set(buff, dst); - rcu_read_lock(); - idev = __in6_dev_get(dst->dev); - IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); - err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev, - dst_output); - if (!err) { - ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT); - ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); - } - - rcu_read_unlock(); + ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf); return; release: -- cgit v1.2.3 From 7b3d9b06d8b6407fe25e00e627b48ec0d4a2a076 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:49:08 +0000 Subject: ndisc: Fill in ICMPv6 checksum and IPv6 header in ndisc_send_skb(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 437781c2af41..dda4addf2919 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -420,8 +420,6 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, const struct in6_addr *target, int llinfo) { - struct net *net = dev_net(dev); - struct sock *sk = net->ipv6.ndisc_sk; struct sk_buff *skb; struct icmp6hdr *hdr; int len; @@ -453,13 +451,6 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev, if (llinfo) ndisc_fill_addr_option(skb, llinfo, dev->dev_addr); - hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, - IPPROTO_ICMPV6, - csum_partial(hdr, - skb->len, 0)); - - ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); - return skb; } @@ -469,6 +460,7 @@ static void ndisc_send_skb(struct sk_buff *skb, { struct dst_entry *dst = skb_dst(skb); struct net *net = dev_net(skb->dev); + struct sock *sk = net->ipv6.ndisc_sk; struct inet6_dev *idev; int err; struct icmp6hdr *icmp6h = icmp6_hdr(skb); @@ -490,6 +482,13 @@ static void ndisc_send_skb(struct sk_buff *skb, skb_dst_set(skb, dst); } + icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, + IPPROTO_ICMPV6, + csum_partial(icmp6h, + skb->len, 0)); + + ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); + rcu_read_lock(); idev = __in6_dev_get(dst->dev); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); @@ -1508,13 +1507,6 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (rd_len) ndisc_fill_redirect_hdr_option(buff, skb, rd_len); - msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr, - buff->len, IPPROTO_ICMPV6, - csum_partial(msg, buff->len, 0)); - - ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr, - inet6_sk(sk)->hop_limit, buff->len); - skb_dst_set(buff, dst); ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf); return; -- cgit v1.2.3 From b44b5f4ae96de87e458ef73fc01e7ec0dbac10c0 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:49:13 +0000 Subject: ndisc: Break down __ndisc_send(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index dda4addf2919..79a56a7663b7 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -503,29 +503,12 @@ static void ndisc_send_skb(struct sk_buff *skb, rcu_read_unlock(); } -/* - * Send a Neighbour Discover packet - */ -static void __ndisc_send(struct net_device *dev, - const struct in6_addr *daddr, - const struct in6_addr *saddr, - struct icmp6hdr *icmp6h, const struct in6_addr *target, - int llinfo) -{ - struct sk_buff *skb; - - skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo); - if (!skb) - return; - - ndisc_send_skb(skb, daddr, saddr); -} - static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, bool router, bool solicited, bool override, bool inc_opt) { + struct sk_buff *skb; struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; const struct in6_addr *src_addr; @@ -553,8 +536,12 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, icmp6h.icmp6_solicited = solicited; icmp6h.icmp6_override = override; - __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr, - inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); + skb = ndisc_build_skb(dev, daddr, src_addr, &icmp6h, solicited_addr, + inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); + if (!skb) + return; + + ndisc_send_skb(skb, daddr, src_addr); } static void ndisc_send_unsol_na(struct net_device *dev) @@ -582,6 +569,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, const struct in6_addr *solicit, const struct in6_addr *daddr, const struct in6_addr *saddr) { + struct sk_buff *skb; struct in6_addr addr_buf; struct icmp6hdr icmp6h = { .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, @@ -594,13 +582,18 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, saddr = &addr_buf; } - __ndisc_send(dev, daddr, saddr, &icmp6h, solicit, - !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); + skb = ndisc_build_skb(dev, daddr, saddr, &icmp6h, solicit, + !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); + if (!skb) + return; + + ndisc_send_skb(skb, daddr, saddr); } void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr) { + struct sk_buff *skb; struct icmp6hdr icmp6h = { .icmp6_type = NDISC_ROUTER_SOLICITATION, }; @@ -628,8 +621,12 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, } } #endif - __ndisc_send(dev, daddr, saddr, &icmp6h, NULL, - send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); + skb = ndisc_build_skb(dev, daddr, saddr, &icmp6h, NULL, + send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); + if (!skb) + return; + + ndisc_send_skb(skb, daddr, saddr); } -- cgit v1.2.3 From 1cb3fe513f62ca7c5963d551e01d75aa4f6ca72a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:49:17 +0000 Subject: ndisc: Break down ndisc_build_skb() and build message directly. Construct NS/NA/RS message directly using C99 compound literals. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 122 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 59 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 79a56a7663b7..1c015665ce71 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -413,47 +413,6 @@ static void ip6_nd_hdr(struct sk_buff *skb, hdr->daddr = *daddr; } -static struct sk_buff *ndisc_build_skb(struct net_device *dev, - const struct in6_addr *daddr, - const struct in6_addr *saddr, - struct icmp6hdr *icmp6h, - const struct in6_addr *target, - int llinfo) -{ - struct sk_buff *skb; - struct icmp6hdr *hdr; - int len; - int optlen = 0; - u8 *opt; - - if (!dev->addr_len) - llinfo = 0; - - len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0); - if (llinfo) - optlen += ndisc_opt_addr_space(dev); - - skb = ndisc_alloc_skb(dev, len + optlen); - if (!skb) - return NULL; - - skb_put(skb, len); - - hdr = (struct icmp6hdr *)skb_transport_header(skb); - memcpy(hdr, icmp6h, sizeof(*hdr)); - - opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); - if (target) { - *(struct in6_addr *)opt = *target; - opt += sizeof(*target); - } - - if (llinfo) - ndisc_fill_addr_option(skb, llinfo, dev->dev_addr); - - return skb; -} - static void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, const struct in6_addr *saddr) @@ -512,9 +471,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; const struct in6_addr *src_addr; - struct icmp6hdr icmp6h = { - .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, - }; + struct nd_msg *msg; + int optlen = 0; /* for anycast or proxy, solicited_addr != src_addr */ ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); @@ -532,15 +490,31 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, src_addr = &tmpaddr; } - icmp6h.icmp6_router = router; - icmp6h.icmp6_solicited = solicited; - icmp6h.icmp6_override = override; + if (!dev->addr_len) + inc_opt = 0; + if (inc_opt) + optlen += ndisc_opt_addr_space(dev); - skb = ndisc_build_skb(dev, daddr, src_addr, &icmp6h, solicited_addr, - inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); + skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!skb) return; + msg = (struct nd_msg *)skb_put(skb, sizeof(*msg)); + *msg = (struct nd_msg) { + .icmph = { + .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, + .icmp6_router = router, + .icmp6_solicited = solicited, + .icmp6_override = override, + }, + .target = *solicited_addr, + }; + + if (inc_opt) + ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, + dev->dev_addr); + + ndisc_send_skb(skb, daddr, src_addr); } @@ -571,9 +545,9 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, { struct sk_buff *skb; struct in6_addr addr_buf; - struct icmp6hdr icmp6h = { - .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, - }; + int inc_opt = dev->addr_len; + int optlen = 0; + struct nd_msg *msg; if (saddr == NULL) { if (ipv6_get_lladdr(dev, &addr_buf, @@ -582,11 +556,27 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, saddr = &addr_buf; } - skb = ndisc_build_skb(dev, daddr, saddr, &icmp6h, solicit, - !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); + if (ipv6_addr_any(saddr)) + inc_opt = 0; + if (inc_opt) + optlen += ndisc_opt_addr_space(dev); + + skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!skb) return; + msg = (struct nd_msg *)skb_put(skb, sizeof(*msg)); + *msg = (struct nd_msg) { + .icmph = { + .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, + }, + .target = *solicit, + }; + + if (inc_opt) + ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, + dev->dev_addr); + ndisc_send_skb(skb, daddr, saddr); } @@ -594,10 +584,9 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr) { struct sk_buff *skb; - struct icmp6hdr icmp6h = { - .icmp6_type = NDISC_ROUTER_SOLICITATION, - }; + struct rs_msg *msg; int send_sllao = dev->addr_len; + int optlen = 0; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD /* @@ -621,11 +610,26 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, } } #endif - skb = ndisc_build_skb(dev, daddr, saddr, &icmp6h, NULL, - send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); + if (!dev->addr_len) + send_sllao = 0; + if (send_sllao) + optlen += ndisc_opt_addr_space(dev); + + skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen); if (!skb) return; + msg = (struct rs_msg *)skb_put(skb, sizeof(*msg)); + *msg = (struct rs_msg) { + .icmph = { + .icmp6_type = NDISC_ROUTER_SOLICITATION, + }, + }; + + if (send_sllao) + ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, + dev->dev_addr); + ndisc_send_skb(skb, daddr, saddr); } -- cgit v1.2.3 From 4d5c152e8694b57094e320060be4621dee5779dd Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 06:49:25 +0000 Subject: ndisc: Use compound literals to build redirect message. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 1c015665ce71..76ef4353d518 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1481,18 +1481,14 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) if (!buff) goto release; - skb_put(buff, sizeof(*msg)); - msg = (struct rd_msg *)icmp6_hdr(buff); - - memset(&msg->icmph, 0, sizeof(struct icmp6hdr)); - msg->icmph.icmp6_type = NDISC_REDIRECT; - - /* - * copy target and destination addresses - */ - - msg->target = *target; - msg->dest = ipv6_hdr(skb)->daddr; + msg = (struct rd_msg *)skb_put(buff, sizeof(*msg)); + *msg = (struct rd_msg) { + .icmph = { + .icmp6_type = NDISC_REDIRECT, + }, + .target = *target, + .dest = ipv6_hdr(skb)->daddr, + }; /* * include target_address option -- cgit v1.2.3 From 660b26dc1a8aeb33c2a2246ebf1b3684449a74b7 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Mon, 21 Jan 2013 06:00:26 +0000 Subject: mcast: add multicast proxy support (IPv4 and IPv6) This patch add the support of proxy multicast, ie being able to build a static multicast tree. It adds the support of (*,*) and (*,G) entries. The user should define an (*,*) entry which is not used for real forwarding. This entry defines the upstream in iif and contains all interfaces from the static tree in its oifs. It will be used to forward packet upstream when they come from an interface belonging to the static tree. Hence, the user should define (*,G) entries to build its static tree. Note that upstream interface must be part of oifs: packets are sent to all oifs interfaces except the input interface. This ensures to always join the whole static tree, even if the packet is not coming from the upstream interface. Signed-off-by: Nicolas Dichtel Acked-by: David L Stevens Signed-off-by: David S. Miller --- include/uapi/linux/mroute.h | 4 +- include/uapi/linux/mroute6.h | 4 +- net/ipv4/ipmr.c | 119 +++++++++++++++++++++++++++++++++++----- net/ipv6/ip6mr.c | 126 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 225 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h index 1c11004af5db..a382d2c04a42 100644 --- a/include/uapi/linux/mroute.h +++ b/include/uapi/linux/mroute.h @@ -26,7 +26,9 @@ #define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ #define MRT_PIM (MRT_BASE+8) /* enable PIM code */ #define MRT_TABLE (MRT_BASE+9) /* Specify mroute table ID */ -#define MRT_MAX (MRT_BASE+9) +#define MRT_ADD_MFC_PROXY (MRT_BASE+10) /* Add a (*,*|G) mfc entry */ +#define MRT_DEL_MFC_PROXY (MRT_BASE+11) /* Del a (*,*|G) mfc entry */ +#define MRT_MAX (MRT_BASE+11) #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h index c206ae3a2327..ce91215cf7e6 100644 --- a/include/uapi/linux/mroute6.h +++ b/include/uapi/linux/mroute6.h @@ -26,7 +26,9 @@ #define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */ #define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */ #define MRT6_TABLE (MRT6_BASE+9) /* Specify mroute table ID */ -#define MRT6_MAX (MRT6_BASE+9) +#define MRT6_ADD_MFC_PROXY (MRT6_BASE+10) /* Add a (*,*|G) mfc entry */ +#define MRT6_DEL_MFC_PROXY (MRT6_BASE+11) /* Del a (*,*|G) mfc entry */ +#define MRT6_MAX (MRT6_BASE+11) #define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a9454cbd953c..4b5e22670d44 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, return NULL; } +/* Look for a (*,*,oif) entry */ +static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, + int vifi) +{ + int line = MFC_HASH(INADDR_ANY, INADDR_ANY); + struct mfc_cache *c; + + list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) + if (c->mfc_origin == INADDR_ANY && + c->mfc_mcastgrp == INADDR_ANY && + c->mfc_un.res.ttls[vifi] < 255) + return c; + + return NULL; +} + +/* Look for a (*,G) entry */ +static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, + __be32 mcastgrp, int vifi) +{ + int line = MFC_HASH(mcastgrp, INADDR_ANY); + struct mfc_cache *c, *proxy; + + if (mcastgrp == INADDR_ANY) + goto skip; + + list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) + if (c->mfc_origin == INADDR_ANY && + c->mfc_mcastgrp == mcastgrp) { + if (c->mfc_un.res.ttls[vifi] < 255) + return c; + + /* It's ok if the vifi is part of the static tree */ + proxy = ipmr_cache_find_any_parent(mrt, + c->mfc_parent); + if (proxy && proxy->mfc_un.res.ttls[vifi] < 255) + return c; + } + +skip: + return ipmr_cache_find_any_parent(mrt, vifi); +} + /* * Allocate a multicast cache entry */ @@ -1053,7 +1096,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb) * MFC cache manipulation by user space mroute daemon */ -static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) +static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) { int line; struct mfc_cache *c, *next; @@ -1062,7 +1105,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) list_for_each_entry_safe(c, next, &mrt->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 && + (parent == -1 || parent == c->mfc_parent)) { list_del_rcu(&c->list); mroute_netlink_event(mrt, c, RTM_DELROUTE); ipmr_cache_free(c); @@ -1073,7 +1117,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) } static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, - struct mfcctl *mfc, int mrtsock) + struct mfcctl *mfc, int mrtsock, int parent) { bool found = false; int line; @@ -1086,7 +1130,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, list_for_each_entry(c, &mrt->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 && + (parent == -1 || parent == c->mfc_parent)) { found = true; break; } @@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, return 0; } - if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) + if (mfc->mfcc_mcastgrp.s_addr != INADDR_ANY && + !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; c = ipmr_cache_alloc(); @@ -1218,7 +1264,7 @@ static void mrtsock_destruct(struct sock *sk) int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) { - int ret; + int ret, parent = 0; struct vifctl vif; struct mfcctl mfc; struct net *net = sock_net(sk); @@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi */ case MRT_ADD_MFC: case MRT_DEL_MFC: + parent = -1; + case MRT_ADD_MFC_PROXY: + case MRT_DEL_MFC_PROXY: if (optlen != sizeof(mfc)) return -EINVAL; if (copy_from_user(&mfc, optval, sizeof(mfc))) return -EFAULT; + if (parent == 0) + parent = mfc.mfcc_parent; rtnl_lock(); - if (optname == MRT_DEL_MFC) - ret = ipmr_mfc_delete(mrt, &mfc); + if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) + ret = ipmr_mfc_delete(mrt, &mfc, parent); else ret = ipmr_mfc_add(net, mrt, &mfc, - sk == rtnl_dereference(mrt->mroute_sk)); + sk == rtnl_dereference(mrt->mroute_sk), + parent); rtnl_unlock(); return ret; /* @@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, { int psend = -1; int vif, ct; + int true_vifi = ipmr_find_vif(mrt, skb->dev); vif = cache->mfc_parent; cache->mfc_un.res.pkt++; cache->mfc_un.res.bytes += skb->len; + if (cache->mfc_origin == INADDR_ANY && true_vifi >= 0) { + struct mfc_cache *cache_proxy; + + /* For an (*,G) entry, we only check that the incomming + * interface is part of the static tree. + */ + cache_proxy = ipmr_cache_find_any_parent(mrt, vif); + if (cache_proxy && + cache_proxy->mfc_un.res.ttls[true_vifi] < 255) + goto forward; + } + /* * Wrong interface: drop packet and (maybe) send PIM assert. */ if (mrt->vif_table[vif].dev != skb->dev) { - int true_vifi; - if (rt_is_output_route(skb_rtable(skb))) { /* It is our own packet, looped back. * Very complicated situation... @@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, } cache->mfc_un.res.wrong_if++; - true_vifi = ipmr_find_vif(mrt, skb->dev); if (true_vifi >= 0 && mrt->mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, @@ -1794,15 +1856,33 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, goto dont_forward; } +forward: mrt->vif_table[vif].pkt_in++; mrt->vif_table[vif].bytes_in += skb->len; /* * Forward the frame */ + if (cache->mfc_origin == INADDR_ANY && + cache->mfc_mcastgrp == INADDR_ANY) { + if (true_vifi >= 0 && + true_vifi != cache->mfc_parent && + ip_hdr(skb)->ttl > + cache->mfc_un.res.ttls[cache->mfc_parent]) { + /* It's an (*,*) entry and the packet is not coming from + * the upstream: forward the packet to the upstream + * only. + */ + psend = cache->mfc_parent; + goto last_forward; + } + goto dont_forward; + } for (ct = cache->mfc_un.res.maxvif - 1; ct >= cache->mfc_un.res.minvif; ct--) { - if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { + /* For (*,G) entry, don't forward to the incoming interface */ + if ((cache->mfc_origin != INADDR_ANY || ct != true_vifi) && + ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); @@ -1813,6 +1893,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, psend = ct; } } +last_forward: if (psend != -1) { if (local) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); @@ -1902,6 +1983,13 @@ int ip_mr_input(struct sk_buff *skb) /* already under rcu_read_lock() */ cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); + if (cache == NULL) { + int vif = ipmr_find_vif(mrt, skb->dev); + + if (vif >= 0) + cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, + vif); + } /* * No usable cache entry @@ -2107,7 +2195,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, rcu_read_lock(); cache = ipmr_cache_find(mrt, saddr, daddr); + if (cache == NULL && skb->dev) { + int vif = ipmr_find_vif(mrt, skb->dev); + if (vif >= 0) + cache = ipmr_cache_find_any(mrt, daddr, vif); + } if (cache == NULL) { struct sk_buff *skb2; struct iphdr *iph; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 26dcdec9e3a5..acc32494006a 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1017,6 +1017,50 @@ static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt, return NULL; } +/* Look for a (*,*,oif) entry */ +static struct mfc6_cache *ip6mr_cache_find_any_parent(struct mr6_table *mrt, + mifi_t mifi) +{ + int line = MFC6_HASH(&in6addr_any, &in6addr_any); + struct mfc6_cache *c; + + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) + if (ipv6_addr_any(&c->mf6c_origin) && + ipv6_addr_any(&c->mf6c_mcastgrp) && + (c->mfc_un.res.ttls[mifi] < 255)) + return c; + + return NULL; +} + +/* Look for a (*,G) entry */ +static struct mfc6_cache *ip6mr_cache_find_any(struct mr6_table *mrt, + struct in6_addr *mcastgrp, + mifi_t mifi) +{ + int line = MFC6_HASH(mcastgrp, &in6addr_any); + struct mfc6_cache *c, *proxy; + + if (ipv6_addr_any(mcastgrp)) + goto skip; + + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) + if (ipv6_addr_any(&c->mf6c_origin) && + ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp)) { + if (c->mfc_un.res.ttls[mifi] < 255) + return c; + + /* It's ok if the mifi is part of the static tree */ + proxy = ip6mr_cache_find_any_parent(mrt, + c->mf6c_parent); + if (proxy && proxy->mfc_un.res.ttls[mifi] < 255) + return c; + } + +skip: + return ip6mr_cache_find_any_parent(mrt, mifi); +} + /* * Allocate a multicast cache entry */ @@ -1247,7 +1291,8 @@ ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb) * MFC6 cache manipulation by user space */ -static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc) +static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc, + int parent) { int line; struct mfc6_cache *c, *next; @@ -1256,7 +1301,9 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc) 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)) { + ipv6_addr_equal(&c->mf6c_mcastgrp, + &mfc->mf6cc_mcastgrp.sin6_addr) && + (parent == -1 || parent == c->mf6c_parent)) { write_lock_bh(&mrt_lock); list_del(&c->list); write_unlock_bh(&mrt_lock); @@ -1391,7 +1438,7 @@ void ip6_mr_cleanup(void) } static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, - struct mf6cctl *mfc, int mrtsock) + struct mf6cctl *mfc, int mrtsock, int parent) { bool found = false; int line; @@ -1413,7 +1460,9 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, 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)) { + ipv6_addr_equal(&c->mf6c_mcastgrp, + &mfc->mf6cc_mcastgrp.sin6_addr) && + (parent == -1 || parent == mfc->mf6cc_parent)) { found = true; break; } @@ -1430,7 +1479,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, return 0; } - if (!ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr)) + if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) && + !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr)) return -EINVAL; c = ip6mr_cache_alloc(); @@ -1596,7 +1646,7 @@ struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) { - int ret; + int ret, parent = 0; struct mif6ctl vif; struct mf6cctl mfc; mifi_t mifi; @@ -1653,15 +1703,21 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns */ case MRT6_ADD_MFC: case MRT6_DEL_MFC: + parent = -1; + case MRT6_ADD_MFC_PROXY: + case MRT6_DEL_MFC_PROXY: if (optlen < sizeof(mfc)) return -EINVAL; if (copy_from_user(&mfc, optval, sizeof(mfc))) return -EFAULT; + if (parent == 0) + parent = mfc.mf6cc_parent; rtnl_lock(); - if (optname == MRT6_DEL_MFC) - ret = ip6mr_mfc_delete(mrt, &mfc); + if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY) + ret = ip6mr_mfc_delete(mrt, &mfc, parent); else - ret = ip6mr_mfc_add(net, mrt, &mfc, sk == mrt->mroute6_sk); + ret = ip6mr_mfc_add(net, mrt, &mfc, + sk == mrt->mroute6_sk, parent); rtnl_unlock(); return ret; @@ -2015,19 +2071,29 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, { int psend = -1; int vif, ct; + int true_vifi = ip6mr_find_vif(mrt, skb->dev); vif = cache->mf6c_parent; cache->mfc_un.res.pkt++; cache->mfc_un.res.bytes += skb->len; + if (ipv6_addr_any(&cache->mf6c_origin) && true_vifi >= 0) { + struct mfc6_cache *cache_proxy; + + /* For an (*,G) entry, we only check that the incomming + * interface is part of the static tree. + */ + cache_proxy = ip6mr_cache_find_any_parent(mrt, vif); + if (cache_proxy && + cache_proxy->mfc_un.res.ttls[true_vifi] < 255) + goto forward; + } + /* * Wrong interface: drop packet and (maybe) send PIM assert. */ if (mrt->vif6_table[vif].dev != skb->dev) { - int true_vifi; - cache->mfc_un.res.wrong_if++; - true_vifi = ip6mr_find_vif(mrt, skb->dev); if (true_vifi >= 0 && mrt->mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, @@ -2045,14 +2111,32 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, goto dont_forward; } +forward: mrt->vif6_table[vif].pkt_in++; mrt->vif6_table[vif].bytes_in += skb->len; /* * Forward the frame */ + if (ipv6_addr_any(&cache->mf6c_origin) && + ipv6_addr_any(&cache->mf6c_mcastgrp)) { + if (true_vifi >= 0 && + true_vifi != cache->mf6c_parent && + ipv6_hdr(skb)->hop_limit > + cache->mfc_un.res.ttls[cache->mf6c_parent]) { + /* It's an (*,*) entry and the packet is not coming from + * the upstream: forward the packet to the upstream + * only. + */ + psend = cache->mf6c_parent; + goto last_forward; + } + goto dont_forward; + } for (ct = cache->mfc_un.res.maxvif - 1; ct >= cache->mfc_un.res.minvif; ct--) { - if (ipv6_hdr(skb)->hop_limit > cache->mfc_un.res.ttls[ct]) { + /* For (*,G) entry, don't forward to the incoming interface */ + if ((!ipv6_addr_any(&cache->mf6c_origin) || ct != true_vifi) && + ipv6_hdr(skb)->hop_limit > cache->mfc_un.res.ttls[ct]) { if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) @@ -2061,6 +2145,7 @@ static int ip6_mr_forward(struct net *net, struct mr6_table *mrt, psend = ct; } } +last_forward: if (psend != -1) { ip6mr_forward2(net, mrt, skb, cache, psend); return 0; @@ -2096,6 +2181,14 @@ int ip6_mr_input(struct sk_buff *skb) read_lock(&mrt_lock); cache = ip6mr_cache_find(mrt, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); + if (cache == NULL) { + int vif = ip6mr_find_vif(mrt, skb->dev); + + if (vif >= 0) + cache = ip6mr_cache_find_any(mrt, + &ipv6_hdr(skb)->daddr, + vif); + } /* * No usable cache entry @@ -2183,6 +2276,13 @@ int ip6mr_get_route(struct net *net, read_lock(&mrt_lock); cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); + if (!cache && skb->dev) { + int vif = ip6mr_find_vif(mrt, skb->dev); + + if (vif >= 0) + cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr, + vif); + } if (!cache) { struct sk_buff *skb2; -- cgit v1.2.3 From fa0879e37b59e8e3f130a30a9e6fa515717c5bdd Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 21 Jan 2013 01:17:22 +0000 Subject: net: split eth_mac_addr for better error handling When we set mac address, software mac address in system and hardware mac address all need to be updated. Current eth_mac_addr() doesn't allow callers to implement error handling nicely. This patch split eth_mac_addr() to prepare part and real commit part, then we can prepare first, and try to change hardware address, then do the real commit if hardware address is set successfully. Signed-off-by: Stefan Hajnoczi Signed-off-by: Amos Kong Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 2 ++ net/ethernet/eth.c | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 1a43e1b4f7ad..c623861964e4 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -40,6 +40,8 @@ extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, extern void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev, const unsigned char *haddr); +extern int eth_prepare_mac_addr_change(struct net_device *dev, void *p); +extern void eth_commit_mac_addr_change(struct net_device *dev, void *p); extern int eth_mac_addr(struct net_device *dev, void *p); extern int eth_change_mtu(struct net_device *dev, int new_mtu); extern int eth_validate_addr(struct net_device *dev); diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index bc39c8c8f589..a36c85eab5b4 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -271,6 +271,36 @@ void eth_header_cache_update(struct hh_cache *hh, } EXPORT_SYMBOL(eth_header_cache_update); +/** + * eth_prepare_mac_addr_change - prepare for mac change + * @dev: network device + * @p: socket address + */ +int eth_prepare_mac_addr_change(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + return 0; +} +EXPORT_SYMBOL(eth_prepare_mac_addr_change); + +/** + * eth_commit_mac_addr_change - commit mac change + * @dev: network device + * @p: socket address + */ +void eth_commit_mac_addr_change(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); +} +EXPORT_SYMBOL(eth_commit_mac_addr_change); + /** * eth_mac_addr - set new Ethernet hardware address * @dev: network device @@ -283,13 +313,12 @@ EXPORT_SYMBOL(eth_header_cache_update); */ int eth_mac_addr(struct net_device *dev, void *p) { - struct sockaddr *addr = p; + int ret; - if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev)) - return -EBUSY; - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + ret = eth_prepare_mac_addr_change(dev, p); + if (ret < 0) + return ret; + eth_commit_mac_addr_change(dev, p); return 0; } EXPORT_SYMBOL(eth_mac_addr); -- cgit v1.2.3 From 441d9d327f1e770f5aa76fd91735851ac6e1e236 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 21 Jan 2013 00:39:24 +0000 Subject: net: move rx and tx hash functions to net/core/flow_dissector.c __skb_tx_hash() and __skb_get_rxhash() are all for calculating hash value based by some fields in skb, mostly used for selecting queues by device drivers. Meanwhile, net/core/dev.c is bloating. Cc: "David S. Miller" Cc: Eric Dumazet Cc: Ben Hutchings Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/dev.c | 175 ---------------------------------------------- net/core/flow_dissector.c | 173 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 175 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index b6d2b32933ba..c69cd8721b28 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -134,7 +134,6 @@ #include #include #include -#include #include "net-sysfs.h" @@ -2636,136 +2635,6 @@ out: return rc; } -static u32 hashrnd __read_mostly; - -/* - * Returns a Tx hash based on the given packet descriptor a Tx queues' number - * to be used as a distribution range. - */ -u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, - unsigned int num_tx_queues) -{ - u32 hash; - u16 qoffset = 0; - u16 qcount = num_tx_queues; - - if (skb_rx_queue_recorded(skb)) { - hash = skb_get_rx_queue(skb); - while (unlikely(hash >= num_tx_queues)) - hash -= num_tx_queues; - return hash; - } - - if (dev->num_tc) { - u8 tc = netdev_get_prio_tc_map(dev, skb->priority); - qoffset = dev->tc_to_txq[tc].offset; - qcount = dev->tc_to_txq[tc].count; - } - - if (skb->sk && skb->sk->sk_hash) - hash = skb->sk->sk_hash; - else - hash = (__force u16) skb->protocol; - hash = jhash_1word(hash, hashrnd); - - return (u16) (((u64) hash * qcount) >> 32) + qoffset; -} -EXPORT_SYMBOL(__skb_tx_hash); - -static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index) -{ - if (unlikely(queue_index >= dev->real_num_tx_queues)) { - net_warn_ratelimited("%s selects TX queue %d, but real number of TX queues is %d\n", - dev->name, queue_index, - dev->real_num_tx_queues); - return 0; - } - return queue_index; -} - -static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) -{ -#ifdef CONFIG_XPS - struct xps_dev_maps *dev_maps; - struct xps_map *map; - int queue_index = -1; - - rcu_read_lock(); - dev_maps = rcu_dereference(dev->xps_maps); - if (dev_maps) { - map = rcu_dereference( - dev_maps->cpu_map[raw_smp_processor_id()]); - if (map) { - if (map->len == 1) - queue_index = map->queues[0]; - else { - u32 hash; - if (skb->sk && skb->sk->sk_hash) - hash = skb->sk->sk_hash; - else - hash = (__force u16) skb->protocol ^ - skb->rxhash; - hash = jhash_1word(hash, hashrnd); - queue_index = map->queues[ - ((u64)hash * map->len) >> 32]; - } - if (unlikely(queue_index >= dev->real_num_tx_queues)) - queue_index = -1; - } - } - rcu_read_unlock(); - - return queue_index; -#else - return -1; -#endif -} - -u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) -{ - struct sock *sk = skb->sk; - int queue_index = sk_tx_queue_get(sk); - - if (queue_index < 0 || skb->ooo_okay || - queue_index >= dev->real_num_tx_queues) { - int new_index = get_xps_queue(dev, skb); - if (new_index < 0) - new_index = skb_tx_hash(dev, skb); - - if (queue_index != new_index && sk) { - struct dst_entry *dst = - rcu_dereference_check(sk->sk_dst_cache, 1); - - if (dst && skb_dst(skb) == dst) - sk_tx_queue_set(sk, queue_index); - - } - - queue_index = new_index; - } - - return queue_index; -} -EXPORT_SYMBOL(__netdev_pick_tx); - -struct netdev_queue *netdev_pick_tx(struct net_device *dev, - struct sk_buff *skb) -{ - int queue_index = 0; - - if (dev->real_num_tx_queues != 1) { - const struct net_device_ops *ops = dev->netdev_ops; - if (ops->ndo_select_queue) - queue_index = ops->ndo_select_queue(dev, skb); - else - queue_index = __netdev_pick_tx(dev, skb); - queue_index = dev_cap_txqueue(dev, queue_index); - } - - skb_set_queue_mapping(skb, queue_index); - return netdev_get_tx_queue(dev, queue_index); -} - static void qdisc_pkt_len_init(struct sk_buff *skb) { const struct skb_shared_info *shinfo = skb_shinfo(skb); @@ -3015,41 +2884,6 @@ static inline void ____napi_schedule(struct softnet_data *sd, __raise_softirq_irqoff(NET_RX_SOFTIRQ); } -/* - * __skb_get_rxhash: calculate a flow hash based on src/dst addresses - * and src/dst port numbers. Sets rxhash in skb to non-zero hash value - * on success, zero indicates no valid hash. Also, sets l4_rxhash in skb - * if hash is a canonical 4-tuple hash over transport ports. - */ -void __skb_get_rxhash(struct sk_buff *skb) -{ - struct flow_keys keys; - u32 hash; - - if (!skb_flow_dissect(skb, &keys)) - return; - - if (keys.ports) - skb->l4_rxhash = 1; - - /* get a consistent hash (same value on both flow directions) */ - if (((__force u32)keys.dst < (__force u32)keys.src) || - (((__force u32)keys.dst == (__force u32)keys.src) && - ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) { - swap(keys.dst, keys.src); - swap(keys.port16[0], keys.port16[1]); - } - - hash = jhash_3words((__force u32)keys.dst, - (__force u32)keys.src, - (__force u32)keys.ports, hashrnd); - if (!hash) - hash = 1; - - skb->rxhash = hash; -} -EXPORT_SYMBOL(__skb_get_rxhash); - #ifdef CONFIG_RPS /* One global table that all flow-based protocols share. */ @@ -7308,12 +7142,3 @@ out: } subsys_initcall(net_dev_init); - -static int __init initialize_hashrnd(void) -{ - get_random_bytes(&hashrnd, sizeof(hashrnd)); - return 0; -} - -late_initcall_sync(initialize_hashrnd); - diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 466820b6e344..9d4c7201400d 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -143,3 +143,176 @@ ipv6: return true; } EXPORT_SYMBOL(skb_flow_dissect); + +static u32 hashrnd __read_mostly; + +/* + * __skb_get_rxhash: calculate a flow hash based on src/dst addresses + * and src/dst port numbers. Sets rxhash in skb to non-zero hash value + * on success, zero indicates no valid hash. Also, sets l4_rxhash in skb + * if hash is a canonical 4-tuple hash over transport ports. + */ +void __skb_get_rxhash(struct sk_buff *skb) +{ + struct flow_keys keys; + u32 hash; + + if (!skb_flow_dissect(skb, &keys)) + return; + + if (keys.ports) + skb->l4_rxhash = 1; + + /* get a consistent hash (same value on both flow directions) */ + if (((__force u32)keys.dst < (__force u32)keys.src) || + (((__force u32)keys.dst == (__force u32)keys.src) && + ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) { + swap(keys.dst, keys.src); + swap(keys.port16[0], keys.port16[1]); + } + + hash = jhash_3words((__force u32)keys.dst, + (__force u32)keys.src, + (__force u32)keys.ports, hashrnd); + if (!hash) + hash = 1; + + skb->rxhash = hash; +} +EXPORT_SYMBOL(__skb_get_rxhash); + +/* + * Returns a Tx hash based on the given packet descriptor a Tx queues' number + * to be used as a distribution range. + */ +u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, + unsigned int num_tx_queues) +{ + u32 hash; + u16 qoffset = 0; + u16 qcount = num_tx_queues; + + if (skb_rx_queue_recorded(skb)) { + hash = skb_get_rx_queue(skb); + while (unlikely(hash >= num_tx_queues)) + hash -= num_tx_queues; + return hash; + } + + if (dev->num_tc) { + u8 tc = netdev_get_prio_tc_map(dev, skb->priority); + qoffset = dev->tc_to_txq[tc].offset; + qcount = dev->tc_to_txq[tc].count; + } + + if (skb->sk && skb->sk->sk_hash) + hash = skb->sk->sk_hash; + else + hash = (__force u16) skb->protocol; + hash = jhash_1word(hash, hashrnd); + + return (u16) (((u64) hash * qcount) >> 32) + qoffset; +} +EXPORT_SYMBOL(__skb_tx_hash); + +static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index) +{ + if (unlikely(queue_index >= dev->real_num_tx_queues)) { + net_warn_ratelimited("%s selects TX queue %d, but real number of TX queues is %d\n", + dev->name, queue_index, + dev->real_num_tx_queues); + return 0; + } + return queue_index; +} + +static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) +{ +#ifdef CONFIG_XPS + struct xps_dev_maps *dev_maps; + struct xps_map *map; + int queue_index = -1; + + rcu_read_lock(); + dev_maps = rcu_dereference(dev->xps_maps); + if (dev_maps) { + map = rcu_dereference( + dev_maps->cpu_map[raw_smp_processor_id()]); + if (map) { + if (map->len == 1) + queue_index = map->queues[0]; + else { + u32 hash; + if (skb->sk && skb->sk->sk_hash) + hash = skb->sk->sk_hash; + else + hash = (__force u16) skb->protocol ^ + skb->rxhash; + hash = jhash_1word(hash, hashrnd); + queue_index = map->queues[ + ((u64)hash * map->len) >> 32]; + } + if (unlikely(queue_index >= dev->real_num_tx_queues)) + queue_index = -1; + } + } + rcu_read_unlock(); + + return queue_index; +#else + return -1; +#endif +} + +u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + int queue_index = sk_tx_queue_get(sk); + + if (queue_index < 0 || skb->ooo_okay || + queue_index >= dev->real_num_tx_queues) { + int new_index = get_xps_queue(dev, skb); + if (new_index < 0) + new_index = skb_tx_hash(dev, skb); + + if (queue_index != new_index && sk) { + struct dst_entry *dst = + rcu_dereference_check(sk->sk_dst_cache, 1); + + if (dst && skb_dst(skb) == dst) + sk_tx_queue_set(sk, queue_index); + + } + + queue_index = new_index; + } + + return queue_index; +} +EXPORT_SYMBOL(__netdev_pick_tx); + +struct netdev_queue *netdev_pick_tx(struct net_device *dev, + struct sk_buff *skb) +{ + int queue_index = 0; + + if (dev->real_num_tx_queues != 1) { + const struct net_device_ops *ops = dev->netdev_ops; + if (ops->ndo_select_queue) + queue_index = ops->ndo_select_queue(dev, skb); + else + queue_index = __netdev_pick_tx(dev, skb); + queue_index = dev_cap_txqueue(dev, queue_index); + } + + skb_set_queue_mapping(skb, queue_index); + return netdev_get_tx_queue(dev, queue_index); +} + +static int __init initialize_hashrnd(void) +{ + get_random_bytes(&hashrnd, sizeof(hashrnd)); + return 0; +} + +late_initcall_sync(initialize_hashrnd); -- cgit v1.2.3 From f490be049ba0a3b194bed3a0dfbdcab1ef0ae063 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 21 Jan 2013 09:58:50 +0000 Subject: dsa: use an unique and non conflicting bus name for the slave MII bus The slave MII bus registered by the DSA code is using the parent MII bus as part of its name (ds->master_mii_bus_id), in case the parent MII bus name is already 16 characters long (such as d0072004.mdio-mi) we will get the following WARN_ON in dsa_switch_setup() when calling mdiobus_register(): [ 79.088782] ------------[ cut here ]------------ [ 79.093448] WARNING: at fs/sysfs/dir.c:536 sysfs_add_one+0x80/0xa0() [ 79.099831] sysfs: cannot create duplicate filename '/class/mdio_bus/d0072004.mdio-mi' This is a genuine warning, because the DSA slave MII bus will also be named d0072004.mdio-mi, and since MII_BUS_ID_SIZE is 17 characters long (with null-terminator) the following will truncate the slave MII bus id: snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s-%d:%.2x", ds->master_mii_bus->id, ds->pd->sw_addr); Fix this by using dsa-: which is guaranteed to be unique. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f4345582a6b9..6ebd8fbd9285 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -41,8 +41,8 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) ds->slave_mii_bus->name = "dsa slave smi"; ds->slave_mii_bus->read = dsa_slave_phy_read; ds->slave_mii_bus->write = dsa_slave_phy_write; - snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x", - ds->master_mii_bus->id, ds->pd->sw_addr); + snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d:%.2x", + ds->index, ds->pd->sw_addr); ds->slave_mii_bus->parent = &ds->master_mii_bus->dev; } -- cgit v1.2.3 From f9bf5a2ca6cc331f32e3dd9cf16ced7215d0e6e8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 21 Jan 2013 09:58:51 +0000 Subject: dsa: make dsa_switch_setup check for valid port names This patch changes dsa_switch_setup() to ensure that at least one valid valid port name is specified and will bail out with an error in case we walked the maximum number of port with a valid port name found. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 45295ca09571..2bc62ea857c8 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -80,6 +80,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, int ret; char *name; int i; + bool valid_name_found = false; /* * Probe for switch model. @@ -131,8 +132,13 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, } else { ds->phys_port_mask |= 1 << i; } + valid_name_found = true; } + if (!valid_name_found && i == DSA_MAX_PORTS) { + ret = -EINVAL; + goto out; + } /* * If the CPU connects to this switch, set the switch tree -- cgit v1.2.3 From b820bb6b996966d93a5442b5fbf803ea58e7f487 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Mon, 21 Jan 2013 09:58:50 +0000 Subject: ndisc: Do not try to update "updated" time if neighbour has already gone. Commit 2152caea ("ipv6: Do not depend on rt->n in rt6_probe().") introduce a bug to try to update "updated" time in neighbour structure. Update the "updated" time only if neighbour is available. Bug was found by Dan Carpenter Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/route.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 2d94d5a7a051..f3328bc1174f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -492,10 +492,10 @@ static void rt6_probe(struct rt6_info *rt) struct in6_addr mcaddr; struct in6_addr *target; - neigh->updated = jiffies; - - if (neigh) + if (neigh) { + neigh->updated = jiffies; write_unlock(&neigh->lock); + } target = (struct in6_addr *)&rt->rt6i_gateway; addrconf_addr_solict_mult(target, &mcaddr); -- cgit v1.2.3 From 360eb5da665566a110993c58ed2a63e98f6720bf Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 22 Jan 2013 11:18:03 +0100 Subject: ipmr: fix sparse warning when testing origin or group mfc_mcastgrp and mfc_origin are __be32, thus we need to convert INADDR_ANY. Because INADDR_ANY is 0, this patch just fix sparse warnings. Reported-by: Fengguang Wu Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/ipv4/ipmr.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 4b5e22670d44..7085b9b51e7f 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -832,12 +832,12 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, int vifi) { - int line = MFC_HASH(INADDR_ANY, INADDR_ANY); + int line = MFC_HASH(htonl(INADDR_ANY), htonl(INADDR_ANY)); struct mfc_cache *c; list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) - if (c->mfc_origin == INADDR_ANY && - c->mfc_mcastgrp == INADDR_ANY && + if (c->mfc_origin == htonl(INADDR_ANY) && + c->mfc_mcastgrp == htonl(INADDR_ANY) && c->mfc_un.res.ttls[vifi] < 255) return c; @@ -848,14 +848,14 @@ static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, __be32 mcastgrp, int vifi) { - int line = MFC_HASH(mcastgrp, INADDR_ANY); + int line = MFC_HASH(mcastgrp, htonl(INADDR_ANY)); struct mfc_cache *c, *proxy; - if (mcastgrp == INADDR_ANY) + if (mcastgrp == htonl(INADDR_ANY)) goto skip; list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) - if (c->mfc_origin == INADDR_ANY && + if (c->mfc_origin == htonl(INADDR_ANY) && c->mfc_mcastgrp == mcastgrp) { if (c->mfc_un.res.ttls[vifi] < 255) return c; @@ -1148,7 +1148,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, return 0; } - if (mfc->mfcc_mcastgrp.s_addr != INADDR_ANY && + if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; @@ -1807,7 +1807,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, cache->mfc_un.res.pkt++; cache->mfc_un.res.bytes += skb->len; - if (cache->mfc_origin == INADDR_ANY && true_vifi >= 0) { + if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { struct mfc_cache *cache_proxy; /* For an (*,G) entry, we only check that the incomming @@ -1863,8 +1863,8 @@ forward: /* * Forward the frame */ - if (cache->mfc_origin == INADDR_ANY && - cache->mfc_mcastgrp == INADDR_ANY) { + if (cache->mfc_origin == htonl(INADDR_ANY) && + cache->mfc_mcastgrp == htonl(INADDR_ANY)) { if (true_vifi >= 0 && true_vifi != cache->mfc_parent && ip_hdr(skb)->ttl > @@ -1881,7 +1881,8 @@ forward: for (ct = cache->mfc_un.res.maxvif - 1; ct >= cache->mfc_un.res.minvif; ct--) { /* For (*,G) entry, don't forward to the incoming interface */ - if ((cache->mfc_origin != INADDR_ANY || ct != true_vifi) && + if ((cache->mfc_origin != htonl(INADDR_ANY) || + ct != true_vifi) && ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); -- cgit v1.2.3 From 2724680bceee94eac391552863771af105a7355c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 05:20:05 +0000 Subject: neigh: Keep neighbour cache entries if number of them is small enough. Since we have removed NCE (Neighbour Cache Entry) reference from routing entries, the only refcnt holders of an NCE are its timer (if running) and its owner table, in usual cases. As a result, neigh_periodic_work() purges NCEs over and over again even for gateways. It does not make sense to purge entries, if number of them is very small, so keep them. The minimum number of entries to keep is specified by gc_thresh1. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 +++++ net/core/neighbour.c | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 497656473b1c..19ac1802bfd4 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -26,6 +26,11 @@ route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. +neigh/default/gc_thresh1 - INTEGER + Minimum number of entries to keep. Garbage collector will not + purge entries if there are fewer than this number. + Default: 256 + neigh/default/gc_thresh3 - INTEGER Maximum number of neighbor entries allowed. Increase this when using large numbers of interfaces and when communicating diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c815f285e5ab..7bd0eedb357f 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -778,6 +778,9 @@ static void neigh_periodic_work(struct work_struct *work) nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); + if (atomic_read(&tbl->entries) < tbl->gc_thresh1) + goto out; + /* * periodically recompute ReachableTime from random function */ @@ -832,6 +835,7 @@ next_elt: nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); } +out: /* Cycle through all hash buckets every base_reachable_time/2 ticks. * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 * base_reachable_time. -- cgit v1.2.3 From 8fbcec241df21d1ba2aba09974ea9017832b69b0 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 06:32:44 +0000 Subject: net: Use IS_ERR_OR_NULL(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/core/flow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/flow.c b/net/core/flow.c index b0901ee5a002..43f7495df27a 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -286,7 +286,7 @@ nocache: else fle->genid--; } else { - if (flo && !IS_ERR(flo)) + if (!IS_ERR_OR_NULL(flo)) flo->ops->delete(flo); } ret_object: -- cgit v1.2.3 From 50c3a487d507568359ccbce1b15bcdfc01edf7ef Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 06:32:49 +0000 Subject: ipv4: Use IS_ERR_OR_NULL(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 2 +- net/ipv4/tcp.c | 2 +- net/ipv4/udp.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 4fdf96710d45..4b7053919976 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1333,7 +1333,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, segs = ops->callbacks.gso_segment(skb, features); rcu_read_unlock(); - if (!segs || IS_ERR(segs)) + if (IS_ERR_OR_NULL(segs)) goto out; skb = segs; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2aa69c8ae60c..52271947a471 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3243,7 +3243,7 @@ __tcp_alloc_md5sig_pool(struct sock *sk) struct crypto_hash *hash; hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); - if (!hash || IS_ERR(hash)) + if (IS_ERR_OR_NULL(hash)) goto out_free; per_cpu_ptr(pool, cpu)->md5_desc.tfm = hash; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 79c8dbe59b54..cf6158f1f46b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -971,7 +971,7 @@ back_from_confirm: sizeof(struct udphdr), &ipc, &rt, msg->msg_flags); err = PTR_ERR(skb); - if (skb && !IS_ERR(skb)) + if (!IS_ERR_OR_NULL(skb)) err = udp_send_skb(skb, fl4); goto out; } -- cgit v1.2.3 From 3f0d2ba0bd7d7dc7b461bc7d2cd3c297b843a3a8 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 06:32:54 +0000 Subject: ipv6: Use IS_ERR_OR_NULL(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 420e56326384..80d59802d964 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1051,7 +1051,7 @@ retry: ipv6_add_addr(idev, &addr, tmp_plen, ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, addr_flags) : NULL; - if (!ift || IS_ERR(ift)) { + if (IS_ERR_OR_NULL(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); pr_info("%s: retry temporary address regeneration\n", __func__); @@ -2079,7 +2079,7 @@ ok: addr_type&IPV6_ADDR_SCOPE_MASK, addr_flags); - if (!ifp || IS_ERR(ifp)) { + if (IS_ERR_OR_NULL(ifp)) { in6_dev_put(in6_dev); return; } -- cgit v1.2.3 From 0cc8d8df9bb931f1d4ab376f59d8ab8a49f9d4d4 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 06:33:09 +0000 Subject: netfilter: Use IS_ERR_OR_NULL(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv4/netfilter/arp_tables.c | 10 +++++----- net/ipv4/netfilter/ip_tables.c | 10 +++++----- net/ipv6/netfilter/ip6_tables.c | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 3ea4127404d6..7dc6a9743592 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -901,7 +901,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, NFPROTO_ARP, name), "arptable_%s", name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { struct arpt_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -958,7 +958,7 @@ static int get_entries(struct net *net, struct arpt_get_entries __user *uptr, } t = xt_find_table_lock(net, NFPROTO_ARP, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { const struct xt_table_info *private = t->private; duprintf("t->private->number = %u\n", @@ -1001,7 +1001,7 @@ static int __do_replace(struct net *net, const char *name, t = try_then_request_module(xt_find_table_lock(net, NFPROTO_ARP, name), "arptable_%s", name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free_newinfo_counters_untrans; } @@ -1158,7 +1158,7 @@ static int do_add_counters(struct net *net, const void __user *user, } t = xt_find_table_lock(net, NFPROTO_ARP, name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free; } @@ -1646,7 +1646,7 @@ static int compat_get_entries(struct net *net, xt_compat_lock(NFPROTO_ARP); t = xt_find_table_lock(net, NFPROTO_ARP, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { const struct xt_table_info *private = t->private; struct xt_table_info info; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 17c5e06da662..3efcf87400c3 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1090,7 +1090,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { struct ipt_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -1149,7 +1149,7 @@ get_entries(struct net *net, struct ipt_get_entries __user *uptr, } t = xt_find_table_lock(net, AF_INET, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { const struct xt_table_info *private = t->private; duprintf("t->private->number = %u\n", private->number); if (get.size == private->size) @@ -1189,7 +1189,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free_newinfo_counters_untrans; } @@ -1347,7 +1347,7 @@ do_add_counters(struct net *net, const void __user *user, } t = xt_find_table_lock(net, AF_INET, name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free; } @@ -1931,7 +1931,7 @@ compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr, xt_compat_lock(AF_INET); t = xt_find_table_lock(net, AF_INET, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { const struct xt_table_info *private = t->private; struct xt_table_info info; duprintf("t->private->number = %u\n", private->number); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 125a90d6a795..341b54ade72c 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1098,7 +1098,7 @@ static int get_info(struct net *net, void __user *user, #endif t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name), "ip6table_%s", name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { struct ip6t_getinfo info; const struct xt_table_info *private = t->private; #ifdef CONFIG_COMPAT @@ -1157,7 +1157,7 @@ get_entries(struct net *net, struct ip6t_get_entries __user *uptr, } t = xt_find_table_lock(net, AF_INET6, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { struct xt_table_info *private = t->private; duprintf("t->private->number = %u\n", private->number); if (get.size == private->size) @@ -1197,7 +1197,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks, t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name), "ip6table_%s", name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free_newinfo_counters_untrans; } @@ -1355,7 +1355,7 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len, } t = xt_find_table_lock(net, AF_INET6, name); - if (!t || IS_ERR(t)) { + if (IS_ERR_OR_NULL(t)) { ret = t ? PTR_ERR(t) : -ENOENT; goto free; } @@ -1939,7 +1939,7 @@ compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr, xt_compat_lock(AF_INET6); t = xt_find_table_lock(net, AF_INET6, get.name); - if (t && !IS_ERR(t)) { + if (!IS_ERR_OR_NULL(t)) { const struct xt_table_info *private = t->private; struct xt_table_info info; duprintf("t->private->number = %u\n", private->number); -- cgit v1.2.3 From fe038884a83b85f2bb61c77609eacb5cf613d3fb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jan 2013 16:15:34 +0200 Subject: Bluetooth: Fix Class of Device indication when powering off When a HCI device is powered off the Management interface specification dictates that the class of device value is indicated as zero. This patch fixes sending of the appropriate class of device changed event when a HCI device is powered off. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fc171f222ba4..54f3ddba9139 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2987,7 +2987,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) } } else { u8 status = MGMT_STATUS_NOT_POWERED; + u8 zero_cod[] = { 0, 0, 0 }; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + + if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) + mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + zero_cod, sizeof(zero_cod), NULL); } err = new_settings(hdev, match.sk); -- cgit v1.2.3 From f950a30e2433f049c17fc47caced1397d25373a6 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Fri, 18 Jan 2013 12:48:07 +0100 Subject: Bluetooth: Fix pair device command reply if adapter is powered off According to Bluetooth Management API specification Pair Device Command should generate command complete event on both success and failure. This fix replying with command status (which lacks address info) when adapter is powered off. Signed-off-by: Szymon Janc Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 54f3ddba9139..36b23101d651 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1939,11 +1939,15 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto unlock; } @@ -1960,10 +1964,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, cp->addr.type, sec_level, auth_type); - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - if (IS_ERR(conn)) { int status; -- cgit v1.2.3 From 4ae14301c3b180adaf6b72285499e7404819a023 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:13 +0200 Subject: Bluetooth: Fix checking for correct mgmt_load_link_keys parameters The debug_keys parameter is only allowed to have the values 0x00 and 0x01. Any other value should result in a proper command status with MGMT_STATUS_INVALID_PARAMS. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 36b23101d651..d9b042efbf42 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1519,6 +1519,10 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS); } + if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); + BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, key_count); -- cgit v1.2.3 From e57e619f463e7841940ef1b98969e23f71f5ee8a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:14 +0200 Subject: Bluetooth: Fix returning proper mgmt status for Load LTKs Failures of mgmt commands should be indicated with valid mgmt status codes, and EINVAL is not one of them. Instead MGMT_STATUS_INVALID_PARAMS should be returned. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d9b042efbf42..a050eee61623 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2716,7 +2716,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, BT_ERR("load_keys: expected %u bytes, got %u bytes", len, expected_len); return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); } BT_DBG("%s key_count %u", hdev->name, key_count); -- cgit v1.2.3 From 54ad6d8a5afe1a6d162d8d229a3d8fc48b254d24 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:15 +0200 Subject: Bluetooth: Fix checking for proper key->master value in Load LTKs The allowed values for the key->master parameter in the Load LTKs command are 0x00 and 0x01. If there is a key in the list with some other value the command should fail with a proper invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a050eee61623..c7ec47ce94e5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2721,6 +2721,15 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s key_count %u", hdev->name, key_count); + for (i = 0; i < key_count; i++) { + struct mgmt_ltk_info *key = &cp->keys[i]; + + if (key->master != 0x00 && key->master != 0x01) + return cmd_status(sk, hdev->id, + MGMT_OP_LOAD_LONG_TERM_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } + hci_dev_lock(hdev); hci_smp_ltks_clear(hdev); -- cgit v1.2.3 From 3f706b7205456c90cdc91e21eab36e2fcf4a8bce Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:16 +0200 Subject: Bluetooth: Refactor valid LTK data testing into its own function This patch refactors valid LTK data testing into its own function. This will help keep the code readable since there are several tests still missing that need to be done on the LTK data. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c7ec47ce94e5..cd758994b02f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2701,6 +2701,13 @@ done: return err; } +static bool ltk_is_valid(struct mgmt_ltk_info *key) +{ + if (key->master != 0x00 && key->master != 0x01) + return false; + return true; +} + static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, void *cp_data, u16 len) { @@ -2724,7 +2731,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; - if (key->master != 0x00 && key->master != 0x01) + if (!ltk_is_valid(key)) return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, MGMT_STATUS_INVALID_PARAMS); -- cgit v1.2.3 From 44b20d33962a73ca14b934540e9168e0da1b49ab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:17 +0200 Subject: Bluetooth: Check for valid key->authenticated value for LTKs This patch adds necessary checks for the two allowed values of the authenticated parameter of each Long Term Key, i.e. 0x00 and 0x01. If any other value is encountered the valid response is to return invalid params to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cd758994b02f..bc04c444c98e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2703,6 +2703,8 @@ done: static bool ltk_is_valid(struct mgmt_ltk_info *key) { + if (key->authenticated != 0x00 && key->authenticated != 0x01) + return false; if (key->master != 0x00 && key->master != 0x01) return false; return true; -- cgit v1.2.3 From 4ee71b2017336f68128515bdbe7c946a39aa9250 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:19 +0200 Subject: Bluetooth: Fix checking for valid address type values in mgmt commands This patch adds checks for valid address type values passed to mgmt commands. If an invalid address type is encountered the code will return a proper invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bc04c444c98e..7dd2de1c2152 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1526,6 +1526,14 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, key_count); + for (i = 0; i < key_count; i++) { + struct mgmt_link_key_info *key = &cp->keys[i]; + + if (key->addr.type != BDADDR_BREDR) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } + hci_dev_lock(hdev); hci_link_keys_clear(hdev); @@ -1573,12 +1581,17 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, struct hci_conn *conn; int err; - hci_dev_lock(hdev); - memset(&rp, 0, sizeof(rp)); bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + if (!hdev_is_powered(hdev)) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); @@ -1643,6 +1656,10 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { @@ -1947,6 +1964,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -2564,6 +2586,10 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); @@ -2589,6 +2615,10 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); + if (!bdaddr_type_is_valid(cp->addr.type)) + return cmd_status(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); @@ -2707,6 +2737,8 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key) return false; if (key->master != 0x00 && key->master != 0x01) return false; + if (!bdaddr_type_is_le(key->addr.type)) + return false; return true; } -- cgit v1.2.3 From 118da70b760f04bb2b8130ced97a9f9cc173440a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:20 +0200 Subject: Bluetooth: Fix checking for valid disconnect parameters in unpair_device The valid values for the Disconnect parameter in the Unpair Device command are 0x00 and 0x01. If any other value is encountered the command should fail with the appropriate invalid params response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7dd2de1c2152..e5e865d8afa8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1590,6 +1590,11 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS, &rp, sizeof(rp)); + if (cp->disconnect != 0x00 && cp->disconnect != 0x01) + return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { -- cgit v1.2.3 From 06a63b19e9eb90402e465d60d4c2564afd3ca211 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:21 +0200 Subject: Bluetooth: Fix returning proper cmd_complete for mgmt_disconnect The Disconnect Management command should return Command Complete instead of Command Status whenever possible so that user space can distinguish exactly which command failed in the case of multiple commands. This patch does the necessary changes in the disconnect command handler to return the right event to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e5e865d8afa8..7b8bc7c658b2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1654,6 +1654,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_disconnect *cp = data; + struct mgmt_rp_disconnect rp; struct hci_cp_disconnect dc; struct pending_cmd *cmd; struct hci_conn *conn; @@ -1661,21 +1662,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); hci_dev_lock(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); goto failed; } if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_BUSY); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_BUSY, &rp, sizeof(rp)); goto failed; } @@ -1686,8 +1692,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_CONNECTED); + err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp)); goto failed; } -- cgit v1.2.3 From 5d0846d416a6c8b7fda1b24aa7369818a7dfa00e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Jan 2013 14:27:22 +0200 Subject: Bluetooth: Fix returning proper cmd_complete for mgmt_block/unblock The Block/Unblock Device Management commands should return Command Complete instead of Command Status whenever possible so that user space can distinguish exactly which command failed in the case of multiple commands. This patch does the necessary changes in the command handler to return the right event to user space. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7b8bc7c658b2..e7f944f52ff2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2598,8 +2598,9 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); hci_dev_lock(hdev); @@ -2627,8 +2628,9 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); if (!bdaddr_type_is_valid(cp->addr.type)) - return cmd_status(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, - MGMT_STATUS_INVALID_PARAMS); + return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); hci_dev_lock(hdev); -- cgit v1.2.3 From 60e77321985ab599fac010afdc465c3e30281a06 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:01:59 +0200 Subject: Bluetooth: Add LE Local Features reading support To be able to make the appropriate decisions for some LE procedures we need to know the LE features that the local controller supports. Therefore, it's important to have the LE Read Local Supported Features HCI comand as part of the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 45eee08157bb..521eefa033e7 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_READ_LOCAL_FEATURES 0x2003 +struct hci_rp_le_read_local_features { + __u8 status; + __u8 features[8]; +} __packed; + #define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 struct hci_rp_le_read_adv_tx_power { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 769a740c104c..3f607c94e213 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -152,6 +152,7 @@ struct hci_dev { __u8 minor_class; __u8 features[8]; __u8 host_features[8]; + __u8 le_features[8]; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 705078a0cc39..07c8c79a9fd1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -609,6 +609,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE Buffer Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + /* Read LE Local Supported Features */ + hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); + /* Read LE Advertising Channel TX Power */ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); } @@ -1090,6 +1093,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_le_read_local_features(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_local_features *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) + memcpy(hdev->le_features, rp->features, 8); + + hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status); +} + static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2628,6 +2644,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_LE_READ_LOCAL_FEATURES: + hci_cc_le_read_local_features(hdev, skb); + break; + case HCI_OP_LE_READ_ADV_TX_POWER: hci_cc_le_read_adv_tx_power(hdev, skb); break; -- cgit v1.2.3 From cf1d081f6597a45e5ff63f55c893494a8ae1cdaf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:02:00 +0200 Subject: Bluetooth: Add support for reading LE White List Size The LE White List Size is necessary to be known before attempting to feed the controller with any addresses intended for the white list. This patch adds the necessary HCI command sending to the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 521eefa033e7..f1766a6f4954 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1001,6 +1001,12 @@ struct hci_cp_le_create_conn { #define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e +#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f +struct hci_rp_le_read_white_list_size { + __u8 status; + __u8 size; +} __packed; + #define HCI_OP_LE_CONN_UPDATE 0x2013 struct hci_cp_le_conn_update { __le16 handle; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3f607c94e213..d6ed4ac18d83 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -153,6 +153,7 @@ struct hci_dev { __u8 features[8]; __u8 host_features[8]; __u8 le_features[8]; + __u8 le_white_list_size; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 07c8c79a9fd1..d2fee64b728c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -614,6 +614,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE Advertising Channel TX Power */ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + + /* Read LE White List Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -1306,6 +1309,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, } } +static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_white_list_size *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size); + + if (!rp->status) + hdev->le_white_list_size = rp->size; + + hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status); +} + static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_le_ltk_reply *rp = (void *) skb->data; @@ -2684,6 +2700,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_set_scan_enable(hdev, skb); break; + case HCI_OP_LE_READ_WHITE_LIST_SIZE: + hci_cc_le_read_white_list_size(hdev, skb); + break; + case HCI_OP_LE_LTK_REPLY: hci_cc_le_ltk_reply(hdev, skb); break; -- cgit v1.2.3 From 9b008c0457e583e10e62d1215bed6ab26ee54906 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2013 14:02:01 +0200 Subject: Bluetooth: Add support for reading LE supported states The LE supported states indicate the states and state combinations that the link layer supports. This is important information for knowing what operations are possible when dealing with multiple connected devices. This patch adds reading of the supported states to the HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f1766a6f4954..7f12c25f1fca 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1045,6 +1045,12 @@ struct hci_rp_le_ltk_neg_reply { __le16 handle; } __packed; +#define HCI_OP_LE_READ_SUPPORTED_STATES 0x201c +struct hci_rp_le_read_supported_states { + __u8 status; + __u8 le_states[8]; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d6ed4ac18d83..bcf8ffe2a843 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -154,6 +154,7 @@ struct hci_dev { __u8 host_features[8]; __u8 le_features[8]; __u8 le_white_list_size; + __u8 le_states[8]; __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d2fee64b728c..d4fcba6ec23e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -617,6 +617,9 @@ static void le_setup(struct hci_dev *hdev) /* Read LE White List Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); + + /* Read LE Supported States */ + hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -1346,6 +1349,19 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } +static void hci_cc_le_read_supported_states(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_supported_states *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) + memcpy(hdev->le_states, rp->le_states, 8); + + hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status); +} + static void hci_cc_write_le_host_supported(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2712,6 +2728,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_ltk_neg_reply(hdev, skb); break; + case HCI_OP_LE_READ_SUPPORTED_STATES: + hci_cc_le_read_supported_states(hdev, skb); + break; + case HCI_OP_WRITE_LE_HOST_SUPPORTED: hci_cc_write_le_host_supported(hdev, skb); break; -- cgit v1.2.3 From 9647bb80a501a34f06132a04e240156950570005 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 22 Jan 2013 17:22:07 +0000 Subject: ipv6: remove duplicated declaration of ip6_fragment() It is declared in: include/net/ip6_route.h:187:int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); and net/ip6_route.h is already included. Cc: YOSHIFUJI Hideaki Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 7eee94c27f8d..7dea45af66d3 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -56,8 +56,6 @@ #include #include -int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); - int __ip6_local_out(struct sk_buff *skb) { int len; -- cgit v1.2.3 From e39363a9def53dd4086be107dc8b3ebca09f045d Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 22 Jan 2013 17:39:11 +0000 Subject: netpoll: fix an uninitialized variable Fengguang reported: net/core/netpoll.c: In function 'netpoll_setup': net/core/netpoll.c:1049:6: warning: 'err' may be used uninitialized in this function [-Wmaybe-uninitialized] in !CONFIG_IPV6 case, we may error out without initializing 'err'. Reported-by: Fengguang Wu Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/netpoll.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index a9b10048388a..e2f79a14625c 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -1140,6 +1140,7 @@ int netpoll_setup(struct netpoll *np) #else np_err(np, "IPv6 is not supported %s, aborting\n", np->dev_name); + err = -EINVAL; goto put; #endif } -- cgit v1.2.3 From f94161c1bbdf7af11729cf106b4452f2432448e0 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:24 +0000 Subject: netfilter: nf_conntrack: move initialization out of pernet operations nf_conntrack initialization and cleanup codes happens in pernet operations function. This task should be done in module_init/exit. We can't use init_net to identify if it's the right time to initialize or cleanup since we cannot make assumption on the order netns are created/destroyed. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 8 ++- net/netfilter/nf_conntrack_core.c | 96 ++++++++++++------------------- net/netfilter/nf_conntrack_standalone.c | 56 +++++++++++------- 3 files changed, 77 insertions(+), 83 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index e98aeb3da033..e05c1f67055c 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -25,12 +25,16 @@ extern unsigned int nf_conntrack_in(struct net *net, unsigned int hooknum, struct sk_buff *skb); -extern int nf_conntrack_init(struct net *net); -extern void nf_conntrack_cleanup(struct net *net); +extern int nf_conntrack_init_net(struct net *net); +extern void nf_conntrack_cleanup_net(struct net *net); extern int nf_conntrack_proto_init(struct net *net); extern void nf_conntrack_proto_fini(struct net *net); +extern int nf_conntrack_init_start(void); +extern void nf_conntrack_cleanup_start(void); + +extern void nf_conntrack_init_end(void); extern void nf_conntrack_cleanup_end(void); extern bool diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 85aa4b7149c5..fb3e514c461e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1334,8 +1334,14 @@ static int untrack_refs(void) return cnt; } -static void nf_conntrack_cleanup_init_net(void) +void nf_conntrack_cleanup_start(void) { + RCU_INIT_POINTER(ip_ct_attach, NULL); +} + +void nf_conntrack_cleanup_end(void) +{ + RCU_INIT_POINTER(nf_ct_destroy, NULL); while (untrack_refs() > 0) schedule(); @@ -1344,8 +1350,18 @@ static void nf_conntrack_cleanup_init_net(void) #endif } -static void nf_conntrack_cleanup_net(struct net *net) +/* + * Mishearing the voices in his head, our hero wonders how he's + * supposed to kill the mall. + */ +void nf_conntrack_cleanup_net(struct net *net) { + /* + * This makes sure all current packets have passed through + * netfilter framework. Roll on, two-stage module + * delete... + */ + synchronize_net(); i_see_dead_people: nf_ct_iterate_cleanup(net, kill_all, NULL); nf_ct_release_dying_list(net); @@ -1355,6 +1371,7 @@ static void nf_conntrack_cleanup_net(struct net *net) } nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); + nf_conntrack_proto_fini(net); nf_conntrack_labels_fini(net); nf_conntrack_helper_fini(net); nf_conntrack_timeout_fini(net); @@ -1367,27 +1384,6 @@ static void nf_conntrack_cleanup_net(struct net *net) free_percpu(net->ct.stat); } -/* Mishearing the voices in his head, our hero wonders how he's - supposed to kill the mall. */ -void nf_conntrack_cleanup(struct net *net) -{ - if (net_eq(net, &init_net)) - RCU_INIT_POINTER(ip_ct_attach, NULL); - - /* This makes sure all current packets have passed through - netfilter framework. Roll on, two-stage module - delete... */ - synchronize_net(); - nf_conntrack_proto_fini(net); - nf_conntrack_cleanup_net(net); -} - -void nf_conntrack_cleanup_end(void) -{ - RCU_INIT_POINTER(nf_ct_destroy, NULL); - nf_conntrack_cleanup_init_net(); -} - void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) { struct hlist_nulls_head *hash; @@ -1478,7 +1474,7 @@ void nf_ct_untracked_status_or(unsigned long bits) } EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); -static int nf_conntrack_init_init_net(void) +int nf_conntrack_init_start(void) { int max_factor = 8; int ret, cpu; @@ -1526,6 +1522,16 @@ err_extend: return ret; } +void nf_conntrack_init_end(void) +{ + /* For use by REJECT target */ + RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); + RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); + + /* Howto get NAT offsets */ + RCU_INIT_POINTER(nf_ct_nat_offset, NULL); +} + /* * We need to use special "null" values, not used in hash table */ @@ -1533,7 +1539,7 @@ err_extend: #define DYING_NULLS_VAL ((1<<30)+1) #define TEMPLATE_NULLS_VAL ((1<<30)+2) -static int nf_conntrack_init_net(struct net *net) +int nf_conntrack_init_net(struct net *net) { int ret; @@ -1592,8 +1598,13 @@ static int nf_conntrack_init_net(struct net *net) if (ret < 0) goto err_labels; + ret = nf_conntrack_proto_init(net); + if (ret < 0) + goto err_proto; return 0; +err_proto: + nf_conntrack_labels_fini(net); err_labels: nf_conntrack_helper_fini(net); err_helper: @@ -1622,38 +1633,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); EXPORT_SYMBOL_GPL(nf_ct_nat_offset); - -int nf_conntrack_init(struct net *net) -{ - int ret; - - if (net_eq(net, &init_net)) { - ret = nf_conntrack_init_init_net(); - if (ret < 0) - goto out_init_net; - } - ret = nf_conntrack_proto_init(net); - if (ret < 0) - goto out_proto; - ret = nf_conntrack_init_net(net); - if (ret < 0) - goto out_net; - - if (net_eq(net, &init_net)) { - /* For use by REJECT target */ - RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); - RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); - - /* Howto get NAT offsets */ - RCU_INIT_POINTER(nf_ct_nat_offset, NULL); - } - return 0; - -out_net: - nf_conntrack_proto_fini(net); -out_proto: - if (net_eq(net, &init_net)) - nf_conntrack_cleanup_init_net(); -out_init_net: - return ret; -} diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index e7185c684816..725bf04a2fb9 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -472,13 +472,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net) { struct ctl_table *table; - if (net_eq(net, &init_net)) { - nf_ct_netfilter_header = - register_net_sysctl(&init_net, "net", nf_ct_netfilter_table); - if (!nf_ct_netfilter_header) - goto out; - } - table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table), GFP_KERNEL); if (!table) @@ -502,10 +495,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net) out_unregister_netfilter: kfree(table); out_kmemdup: - if (net_eq(net, &init_net)) - unregister_net_sysctl_table(nf_ct_netfilter_header); -out: - printk(KERN_ERR "nf_conntrack: can't register to sysctl.\n"); return -ENOMEM; } @@ -513,8 +502,6 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net) { struct ctl_table *table; - if (net_eq(net, &init_net)) - unregister_net_sysctl_table(nf_ct_netfilter_header); table = net->ct.sysctl_header->ctl_table_arg; unregister_net_sysctl_table(net->ct.sysctl_header); kfree(table); @@ -530,51 +517,78 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net) } #endif /* CONFIG_SYSCTL */ -static int nf_conntrack_net_init(struct net *net) +static int nf_conntrack_pernet_init(struct net *net) { int ret; - ret = nf_conntrack_init(net); + ret = nf_conntrack_init_net(net); if (ret < 0) goto out_init; + ret = nf_conntrack_standalone_init_proc(net); if (ret < 0) goto out_proc; + net->ct.sysctl_checksum = 1; net->ct.sysctl_log_invalid = 0; ret = nf_conntrack_standalone_init_sysctl(net); if (ret < 0) goto out_sysctl; + return 0; out_sysctl: nf_conntrack_standalone_fini_proc(net); out_proc: - nf_conntrack_cleanup(net); + nf_conntrack_cleanup_net(net); out_init: return ret; } -static void nf_conntrack_net_exit(struct net *net) +static void nf_conntrack_pernet_exit(struct net *net) { nf_conntrack_standalone_fini_sysctl(net); nf_conntrack_standalone_fini_proc(net); - nf_conntrack_cleanup(net); + nf_conntrack_cleanup_net(net); } static struct pernet_operations nf_conntrack_net_ops = { - .init = nf_conntrack_net_init, - .exit = nf_conntrack_net_exit, + .init = nf_conntrack_pernet_init, + .exit = nf_conntrack_pernet_exit, }; static int __init nf_conntrack_standalone_init(void) { - return register_pernet_subsys(&nf_conntrack_net_ops); + int ret = nf_conntrack_init_start(); + if (ret < 0) + goto out_start; + + nf_ct_netfilter_header = + register_net_sysctl(&init_net, "net", nf_ct_netfilter_table); + if (!nf_ct_netfilter_header) + goto out_sysctl; + + ret = register_pernet_subsys(&nf_conntrack_net_ops); + if (ret < 0) + goto out_pernet; + + nf_conntrack_init_end(); + return 0; + +out_pernet: + unregister_net_sysctl_table(nf_ct_netfilter_header); +out_sysctl: + pr_err("nf_conntrack: can't register to sysctl.\n"); + nf_conntrack_cleanup_end(); +out_start: + return ret; } static void __exit nf_conntrack_standalone_fini(void) { + nf_conntrack_cleanup_start(); unregister_pernet_subsys(&nf_conntrack_net_ops); + unregister_net_sysctl_table(nf_ct_netfilter_header); nf_conntrack_cleanup_end(); } -- cgit v1.2.3 From 83b4dbe19844b5472a8f44b6cf1d88693c080ef7 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:25 +0000 Subject: netfilter: nf_ct_expect: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 7 ++-- net/netfilter/nf_conntrack_core.c | 14 ++++++-- net/netfilter/nf_conntrack_expect.c | 53 ++++++++++++++--------------- 3 files changed, 41 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index cc13f377a705..cbbae7621e22 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -69,8 +69,11 @@ struct nf_conntrack_expect_policy { #define NF_CT_EXPECT_CLASS_DEFAULT 0 -int nf_conntrack_expect_init(struct net *net); -void nf_conntrack_expect_fini(struct net *net); +int nf_conntrack_expect_pernet_init(struct net *net); +void nf_conntrack_expect_pernet_fini(struct net *net); + +int nf_conntrack_expect_init(void); +void nf_conntrack_expect_fini(void); struct nf_conntrack_expect * __nf_ct_expect_find(struct net *net, u16 zone, diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index fb3e514c461e..a3cca572412c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_expect_fini(); } /* @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) nf_conntrack_ecache_fini(net); nf_conntrack_tstamp_fini(net); nf_conntrack_acct_fini(net); - nf_conntrack_expect_fini(net); + nf_conntrack_expect_pernet_fini(net); kmem_cache_destroy(net->ct.nf_conntrack_cachep); kfree(net->ct.slabname); free_percpu(net->ct.stat); @@ -1501,6 +1502,11 @@ int nf_conntrack_init_start(void) printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n", NF_CONNTRACK_VERSION, nf_conntrack_htable_size, nf_conntrack_max); + + ret = nf_conntrack_expect_init(); + if (ret < 0) + goto err_expect; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1518,7 +1524,9 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: + nf_conntrack_expect_fini(); #endif +err_expect: return ret; } @@ -1575,7 +1583,7 @@ int nf_conntrack_init_net(struct net *net) printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); goto err_hash; } - ret = nf_conntrack_expect_init(net); + ret = nf_conntrack_expect_pernet_init(net); if (ret < 0) goto err_expect; ret = nf_conntrack_acct_init(net); @@ -1616,7 +1624,7 @@ err_ecache: err_tstamp: nf_conntrack_acct_fini(net); err_acct: - nf_conntrack_expect_fini(net); + nf_conntrack_expect_pernet_fini(net); err_expect: nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); err_hash: diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 527651a53a45..bdd341899ed3 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -587,53 +587,50 @@ static void exp_proc_remove(struct net *net) module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400); -int nf_conntrack_expect_init(struct net *net) +int nf_conntrack_expect_pernet_init(struct net *net) { int err = -ENOMEM; - if (net_eq(net, &init_net)) { - if (!nf_ct_expect_hsize) { - nf_ct_expect_hsize = net->ct.htable_size / 256; - if (!nf_ct_expect_hsize) - nf_ct_expect_hsize = 1; - } - nf_ct_expect_max = nf_ct_expect_hsize * 4; - } - net->ct.expect_count = 0; net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0); if (net->ct.expect_hash == NULL) goto err1; - if (net_eq(net, &init_net)) { - nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect", - sizeof(struct nf_conntrack_expect), - 0, 0, NULL); - if (!nf_ct_expect_cachep) - goto err2; - } - err = exp_proc_init(net); if (err < 0) - goto err3; + goto err2; return 0; - -err3: - if (net_eq(net, &init_net)) - kmem_cache_destroy(nf_ct_expect_cachep); err2: nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize); err1: return err; } -void nf_conntrack_expect_fini(struct net *net) +void nf_conntrack_expect_pernet_fini(struct net *net) { exp_proc_remove(net); - if (net_eq(net, &init_net)) { - rcu_barrier(); /* Wait for call_rcu() before destroy */ - kmem_cache_destroy(nf_ct_expect_cachep); - } nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize); } + +int nf_conntrack_expect_init(void) +{ + if (!nf_ct_expect_hsize) { + nf_ct_expect_hsize = nf_conntrack_htable_size / 256; + if (!nf_ct_expect_hsize) + nf_ct_expect_hsize = 1; + } + nf_ct_expect_max = nf_ct_expect_hsize * 4; + nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect", + sizeof(struct nf_conntrack_expect), + 0, 0, NULL); + if (!nf_ct_expect_cachep) + return -ENOMEM; + return 0; +} + +void nf_conntrack_expect_fini(void) +{ + rcu_barrier(); /* Wait for call_rcu() before destroy */ + kmem_cache_destroy(nf_ct_expect_cachep); +} -- cgit v1.2.3 From b7ff3a1fae78783e0ab1ef82f5978aeb89ddd16b Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:26 +0000 Subject: netfilter: nf_ct_acct: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_acct.h | 6 ++++-- net/netfilter/nf_conntrack_acct.c | 36 +++++++++++-------------------- net/netfilter/nf_conntrack_core.c | 15 +++++++++---- 3 files changed, 28 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h index 463ae8e16696..2bdb7a15fe06 100644 --- a/include/net/netfilter/nf_conntrack_acct.h +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -57,7 +57,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable) net->ct.sysctl_acct = enable; } -extern int nf_conntrack_acct_init(struct net *net); -extern void nf_conntrack_acct_fini(struct net *net); +extern int nf_conntrack_acct_pernet_init(struct net *net); +extern void nf_conntrack_acct_pernet_fini(struct net *net); +extern int nf_conntrack_acct_init(void); +extern void nf_conntrack_acct_fini(void); #endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 7df424e2d10c..2d3030ab5b61 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -106,36 +106,26 @@ static void nf_conntrack_acct_fini_sysctl(struct net *net) } #endif -int nf_conntrack_acct_init(struct net *net) +int nf_conntrack_acct_pernet_init(struct net *net) { - int ret; - net->ct.sysctl_acct = nf_ct_acct; + return nf_conntrack_acct_init_sysctl(net); +} - if (net_eq(net, &init_net)) { - ret = nf_ct_extend_register(&acct_extend); - if (ret < 0) { - printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); - goto out_extend_register; - } - } +void nf_conntrack_acct_pernet_fini(struct net *net) +{ + nf_conntrack_acct_fini_sysctl(net); +} - ret = nf_conntrack_acct_init_sysctl(net); +int nf_conntrack_acct_init(void) +{ + int ret = nf_ct_extend_register(&acct_extend); if (ret < 0) - goto out_sysctl; - - return 0; - -out_sysctl: - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&acct_extend); -out_extend_register: + pr_err("nf_conntrack_acct: Unable to register extension\n"); return ret; } -void nf_conntrack_acct_fini(struct net *net) +void nf_conntrack_acct_fini(void) { - nf_conntrack_acct_fini_sysctl(net); - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&acct_extend); + nf_ct_extend_unregister(&acct_extend); } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a3cca572412c..f4c6d4a06a1a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_acct_fini(); nf_conntrack_expect_fini(); } @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) nf_conntrack_timeout_fini(net); nf_conntrack_ecache_fini(net); nf_conntrack_tstamp_fini(net); - nf_conntrack_acct_fini(net); + nf_conntrack_acct_pernet_fini(net); nf_conntrack_expect_pernet_fini(net); kmem_cache_destroy(net->ct.nf_conntrack_cachep); kfree(net->ct.slabname); @@ -1507,6 +1508,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_expect; + ret = nf_conntrack_acct_init(); + if (ret < 0) + goto err_acct; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1524,8 +1529,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_expect_fini(); + nf_conntrack_acct_fini(); #endif +err_acct: + nf_conntrack_expect_fini(); err_expect: return ret; } @@ -1586,7 +1593,7 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_expect_pernet_init(net); if (ret < 0) goto err_expect; - ret = nf_conntrack_acct_init(net); + ret = nf_conntrack_acct_pernet_init(net); if (ret < 0) goto err_acct; ret = nf_conntrack_tstamp_init(net); @@ -1622,7 +1629,7 @@ err_timeout: err_ecache: nf_conntrack_tstamp_fini(net); err_tstamp: - nf_conntrack_acct_fini(net); + nf_conntrack_acct_pernet_fini(net); err_acct: nf_conntrack_expect_pernet_fini(net); err_expect: -- cgit v1.2.3 From 73f4001a52c986114f540504d70b21e52eb0d92a Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:27 +0000 Subject: netfilter: nf_ct_tstamp: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_timestamp.h | 21 +++++++++++--- net/netfilter/nf_conntrack_core.c | 15 +++++++--- net/netfilter/nf_conntrack_timestamp.c | 39 ++++++++++---------------- 3 files changed, 43 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h index fc9c82b1f06b..b00461413efd 100644 --- a/include/net/netfilter/nf_conntrack_timestamp.h +++ b/include/net/netfilter/nf_conntrack_timestamp.h @@ -48,15 +48,28 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable) } #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP -extern int nf_conntrack_tstamp_init(struct net *net); -extern void nf_conntrack_tstamp_fini(struct net *net); +extern int nf_conntrack_tstamp_pernet_init(struct net *net); +extern void nf_conntrack_tstamp_pernet_fini(struct net *net); + +extern int nf_conntrack_tstamp_init(void); +extern void nf_conntrack_tstamp_fini(void); #else -static inline int nf_conntrack_tstamp_init(struct net *net) +static inline int nf_conntrack_tstamp_pernet_init(struct net *net) +{ + return 0; +} + +static inline void nf_conntrack_tstamp_pernet_fini(struct net *net) +{ + return; +} + +static inline int nf_conntrack_tstamp_init(void) { return 0; } -static inline void nf_conntrack_tstamp_fini(struct net *net) +static inline void nf_conntrack_tstamp_fini(void) { return; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f4c6d4a06a1a..20ebfff0a47e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_tstamp_fini(); nf_conntrack_acct_fini(); nf_conntrack_expect_fini(); } @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) nf_conntrack_helper_fini(net); nf_conntrack_timeout_fini(net); nf_conntrack_ecache_fini(net); - nf_conntrack_tstamp_fini(net); + nf_conntrack_tstamp_pernet_fini(net); nf_conntrack_acct_pernet_fini(net); nf_conntrack_expect_pernet_fini(net); kmem_cache_destroy(net->ct.nf_conntrack_cachep); @@ -1512,6 +1513,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_acct; + ret = nf_conntrack_tstamp_init(); + if (ret < 0) + goto err_tstamp; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1529,8 +1534,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_acct_fini(); + nf_conntrack_tstamp_fini(); #endif +err_tstamp: + nf_conntrack_acct_fini(); err_acct: nf_conntrack_expect_fini(); err_expect: @@ -1596,7 +1603,7 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_acct_pernet_init(net); if (ret < 0) goto err_acct; - ret = nf_conntrack_tstamp_init(net); + ret = nf_conntrack_tstamp_pernet_init(net); if (ret < 0) goto err_tstamp; ret = nf_conntrack_ecache_init(net); @@ -1627,7 +1634,7 @@ err_helper: err_timeout: nf_conntrack_ecache_fini(net); err_ecache: - nf_conntrack_tstamp_fini(net); + nf_conntrack_tstamp_pernet_fini(net); err_tstamp: nf_conntrack_acct_pernet_fini(net); err_acct: diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c index 7ea8026f07c9..902fb0a6b38a 100644 --- a/net/netfilter/nf_conntrack_timestamp.c +++ b/net/netfilter/nf_conntrack_timestamp.c @@ -88,37 +88,28 @@ static void nf_conntrack_tstamp_fini_sysctl(struct net *net) } #endif -int nf_conntrack_tstamp_init(struct net *net) +int nf_conntrack_tstamp_pernet_init(struct net *net) { - int ret; - net->ct.sysctl_tstamp = nf_ct_tstamp; + return nf_conntrack_tstamp_init_sysctl(net); +} - if (net_eq(net, &init_net)) { - ret = nf_ct_extend_register(&tstamp_extend); - if (ret < 0) { - printk(KERN_ERR "nf_ct_tstamp: Unable to register " - "extension\n"); - goto out_extend_register; - } - } +void nf_conntrack_tstamp_pernet_fini(struct net *net) +{ + nf_conntrack_tstamp_fini_sysctl(net); + nf_ct_extend_unregister(&tstamp_extend); +} - ret = nf_conntrack_tstamp_init_sysctl(net); +int nf_conntrack_tstamp_init(void) +{ + int ret; + ret = nf_ct_extend_register(&tstamp_extend); if (ret < 0) - goto out_sysctl; - - return 0; - -out_sysctl: - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&tstamp_extend); -out_extend_register: + pr_err("nf_ct_tstamp: Unable to register extension\n"); return ret; } -void nf_conntrack_tstamp_fini(struct net *net) +void nf_conntrack_tstamp_fini(void) { - nf_conntrack_tstamp_fini_sysctl(net); - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&tstamp_extend); + nf_ct_extend_unregister(&tstamp_extend); } -- cgit v1.2.3 From 3fe0f943d4f52f875f0fdf8dbe472c8a9b852891 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:28 +0000 Subject: netfilter: nf_ct_ecache: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_ecache.h | 19 +++++++++++---- net/netfilter/nf_conntrack_core.c | 15 ++++++++---- net/netfilter/nf_conntrack_ecache.c | 37 ++++++++++------------------- 3 files changed, 39 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 5654d292efd4..092dc651689f 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -207,9 +207,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event, nf_ct_expect_event_report(event, exp, 0, 0); } -extern int nf_conntrack_ecache_init(struct net *net); -extern void nf_conntrack_ecache_fini(struct net *net); +extern int nf_conntrack_ecache_pernet_init(struct net *net); +extern void nf_conntrack_ecache_pernet_fini(struct net *net); +extern int nf_conntrack_ecache_init(void); +extern void nf_conntrack_ecache_fini(void); #else /* CONFIG_NF_CONNTRACK_EVENTS */ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, @@ -232,12 +234,21 @@ static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e, u32 portid, int report) {} -static inline int nf_conntrack_ecache_init(struct net *net) +static inline int nf_conntrack_ecache_pernet_init(struct net *net) { return 0; } -static inline void nf_conntrack_ecache_fini(struct net *net) +static inline void nf_conntrack_ecache_pernet_fini(struct net *net) +{ +} + +static inline int nf_conntrack_ecache_init(void) +{ + return 0; +} + +static inline void nf_conntrack_ecache_fini(void) { } #endif /* CONFIG_NF_CONNTRACK_EVENTS */ diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 20ebfff0a47e..048fe77bf20c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_ecache_fini(); nf_conntrack_tstamp_fini(); nf_conntrack_acct_fini(); nf_conntrack_expect_fini(); @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) nf_conntrack_labels_fini(net); nf_conntrack_helper_fini(net); nf_conntrack_timeout_fini(net); - nf_conntrack_ecache_fini(net); + nf_conntrack_ecache_pernet_fini(net); nf_conntrack_tstamp_pernet_fini(net); nf_conntrack_acct_pernet_fini(net); nf_conntrack_expect_pernet_fini(net); @@ -1517,6 +1518,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_tstamp; + ret = nf_conntrack_ecache_init(); + if (ret < 0) + goto err_ecache; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1534,8 +1539,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_tstamp_fini(); + nf_conntrack_ecache_fini(); #endif +err_ecache: + nf_conntrack_tstamp_fini(); err_tstamp: nf_conntrack_acct_fini(); err_acct: @@ -1606,7 +1613,7 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_tstamp_pernet_init(net); if (ret < 0) goto err_tstamp; - ret = nf_conntrack_ecache_init(net); + ret = nf_conntrack_ecache_pernet_init(net); if (ret < 0) goto err_ecache; ret = nf_conntrack_timeout_init(net); @@ -1632,7 +1639,7 @@ err_labels: err_helper: nf_conntrack_timeout_fini(net); err_timeout: - nf_conntrack_ecache_fini(net); + nf_conntrack_ecache_pernet_fini(net); err_ecache: nf_conntrack_tstamp_pernet_fini(net); err_tstamp: diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index faa978f1714b..b5d2eb8bf0d5 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -233,38 +233,27 @@ static void nf_conntrack_event_fini_sysctl(struct net *net) } #endif /* CONFIG_SYSCTL */ -int nf_conntrack_ecache_init(struct net *net) +int nf_conntrack_ecache_pernet_init(struct net *net) { - int ret; - net->ct.sysctl_events = nf_ct_events; net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout; + return nf_conntrack_event_init_sysctl(net); +} - if (net_eq(net, &init_net)) { - ret = nf_ct_extend_register(&event_extend); - if (ret < 0) { - printk(KERN_ERR "nf_ct_event: Unable to register " - "event extension.\n"); - goto out_extend_register; - } - } +void nf_conntrack_ecache_pernet_fini(struct net *net) +{ + nf_conntrack_event_fini_sysctl(net); +} - ret = nf_conntrack_event_init_sysctl(net); +int nf_conntrack_ecache_init(void) +{ + int ret = nf_ct_extend_register(&event_extend); if (ret < 0) - goto out_sysctl; - - return 0; - -out_sysctl: - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&event_extend); -out_extend_register: + pr_err("nf_ct_event: Unable to register event extension.\n"); return ret; } -void nf_conntrack_ecache_fini(struct net *net) +void nf_conntrack_ecache_fini(void) { - nf_conntrack_event_fini_sysctl(net); - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&event_extend); + nf_ct_extend_unregister(&event_extend); } -- cgit v1.2.3 From 8684094cf17d8ce96e0a8c63003f331aa017e22d Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:29 +0000 Subject: netfilter: nf_ct_timeout: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_timeout.h | 8 ++++---- net/netfilter/nf_conntrack_core.c | 15 ++++++++------- net/netfilter/nf_conntrack_timeout.c | 23 +++++++---------------- 3 files changed, 19 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index e41e472d08f2..d23aceb16d94 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -76,15 +76,15 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct, } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT -extern int nf_conntrack_timeout_init(struct net *net); -extern void nf_conntrack_timeout_fini(struct net *net); +extern int nf_conntrack_timeout_init(void); +extern void nf_conntrack_timeout_fini(void); #else -static inline int nf_conntrack_timeout_init(struct net *net) +static inline int nf_conntrack_timeout_init(void) { return 0; } -static inline void nf_conntrack_timeout_fini(struct net *net) +static inline void nf_conntrack_timeout_fini(void) { return; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 048fe77bf20c..4f4d1075644e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_timeout_fini(); nf_conntrack_ecache_fini(); nf_conntrack_tstamp_fini(); nf_conntrack_acct_fini(); @@ -1378,7 +1379,6 @@ void nf_conntrack_cleanup_net(struct net *net) nf_conntrack_proto_fini(net); nf_conntrack_labels_fini(net); nf_conntrack_helper_fini(net); - nf_conntrack_timeout_fini(net); nf_conntrack_ecache_pernet_fini(net); nf_conntrack_tstamp_pernet_fini(net); nf_conntrack_acct_pernet_fini(net); @@ -1522,6 +1522,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_ecache; + ret = nf_conntrack_timeout_init(); + if (ret < 0) + goto err_timeout; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1539,8 +1543,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_ecache_fini(); + nf_conntrack_timeout_fini(); #endif +err_timeout: + nf_conntrack_ecache_fini(); err_ecache: nf_conntrack_tstamp_fini(); err_tstamp: @@ -1616,9 +1622,6 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_ecache_pernet_init(net); if (ret < 0) goto err_ecache; - ret = nf_conntrack_timeout_init(net); - if (ret < 0) - goto err_timeout; ret = nf_conntrack_helper_init(net); if (ret < 0) goto err_helper; @@ -1637,8 +1640,6 @@ err_proto: err_labels: nf_conntrack_helper_fini(net); err_helper: - nf_conntrack_timeout_fini(net); -err_timeout: nf_conntrack_ecache_pernet_fini(net); err_ecache: nf_conntrack_tstamp_pernet_fini(net); diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c index a878ce5b252c..93da609d9d29 100644 --- a/net/netfilter/nf_conntrack_timeout.c +++ b/net/netfilter/nf_conntrack_timeout.c @@ -37,24 +37,15 @@ static struct nf_ct_ext_type timeout_extend __read_mostly = { .id = NF_CT_EXT_TIMEOUT, }; -int nf_conntrack_timeout_init(struct net *net) +int nf_conntrack_timeout_init(void) { - int ret = 0; - - if (net_eq(net, &init_net)) { - ret = nf_ct_extend_register(&timeout_extend); - if (ret < 0) { - printk(KERN_ERR "nf_ct_timeout: Unable to register " - "timeout extension.\n"); - return ret; - } - } - - return 0; + int ret = nf_ct_extend_register(&timeout_extend); + if (ret < 0) + pr_err("nf_ct_timeout: Unable to register timeout extension.\n"); + return ret; } -void nf_conntrack_timeout_fini(struct net *net) +void nf_conntrack_timeout_fini(void) { - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&timeout_extend); + nf_ct_extend_unregister(&timeout_extend); } -- cgit v1.2.3 From 5e615b220087c5551f486c967831cecdfd338dbe Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:30 +0000 Subject: netfilter: nf_ct_helper: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 7 ++-- net/netfilter/nf_conntrack_core.c | 15 +++++--- net/netfilter/nf_conntrack_helper.c | 53 ++++++++++++++--------------- 3 files changed, 41 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 9aad956d1008..ce27edf57570 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -82,8 +82,11 @@ static inline void *nfct_help_data(const struct nf_conn *ct) return (void *)help->data; } -extern int nf_conntrack_helper_init(struct net *net); -extern void nf_conntrack_helper_fini(struct net *net); +extern int nf_conntrack_helper_pernet_init(struct net *net); +extern void nf_conntrack_helper_pernet_fini(struct net *net); + +extern int nf_conntrack_helper_init(void); +extern void nf_conntrack_helper_fini(void); extern int nf_conntrack_broadcast_help(struct sk_buff *skb, unsigned int protoff, diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 4f4d1075644e..06b8cdb5e620 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_helper_fini(); nf_conntrack_timeout_fini(); nf_conntrack_ecache_fini(); nf_conntrack_tstamp_fini(); @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_conntrack_proto_fini(net); nf_conntrack_labels_fini(net); - nf_conntrack_helper_fini(net); + nf_conntrack_helper_pernet_fini(net); nf_conntrack_ecache_pernet_fini(net); nf_conntrack_tstamp_pernet_fini(net); nf_conntrack_acct_pernet_fini(net); @@ -1526,6 +1527,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_timeout; + ret = nf_conntrack_helper_init(); + if (ret < 0) + goto err_helper; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1543,8 +1548,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_timeout_fini(); + nf_conntrack_helper_fini(); #endif +err_helper: + nf_conntrack_timeout_fini(); err_timeout: nf_conntrack_ecache_fini(); err_ecache: @@ -1622,7 +1629,7 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_ecache_pernet_init(net); if (ret < 0) goto err_ecache; - ret = nf_conntrack_helper_init(net); + ret = nf_conntrack_helper_pernet_init(net); if (ret < 0) goto err_helper; @@ -1638,7 +1645,7 @@ int nf_conntrack_init_net(struct net *net) err_proto: nf_conntrack_labels_fini(net); err_labels: - nf_conntrack_helper_fini(net); + nf_conntrack_helper_pernet_fini(net); err_helper: nf_conntrack_ecache_pernet_fini(net); err_ecache: diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 884f2b39319a..2f380f73c4c0 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -423,44 +423,41 @@ static struct nf_ct_ext_type helper_extend __read_mostly = { .id = NF_CT_EXT_HELPER, }; -int nf_conntrack_helper_init(struct net *net) +int nf_conntrack_helper_pernet_init(struct net *net) { - int err; - net->ct.auto_assign_helper_warned = false; net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper; + return nf_conntrack_helper_init_sysctl(net); +} - if (net_eq(net, &init_net)) { - nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ - nf_ct_helper_hash = - nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); - if (!nf_ct_helper_hash) - return -ENOMEM; +void nf_conntrack_helper_pernet_fini(struct net *net) +{ + nf_conntrack_helper_fini_sysctl(net); +} - err = nf_ct_extend_register(&helper_extend); - if (err < 0) - goto err1; +int nf_conntrack_helper_init(void) +{ + int ret; + nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ + nf_ct_helper_hash = + nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); + if (!nf_ct_helper_hash) + return -ENOMEM; + + ret = nf_ct_extend_register(&helper_extend); + if (ret < 0) { + pr_err("nf_ct_helper: Unable to register helper extension.\n"); + goto out_extend; } - err = nf_conntrack_helper_init_sysctl(net); - if (err < 0) - goto out_sysctl; - return 0; - -out_sysctl: - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&helper_extend); -err1: +out_extend: nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); - return err; + return ret; } -void nf_conntrack_helper_fini(struct net *net) +void nf_conntrack_helper_fini(void) { - nf_conntrack_helper_fini_sysctl(net); - if (net_eq(net, &init_net)) { - nf_ct_extend_unregister(&helper_extend); - nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); - } + nf_ct_extend_unregister(&helper_extend); + nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); } -- cgit v1.2.3 From 5f69b8f5218dc303cbcb6f71d221c27d3cd17ebb Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:31 +0000 Subject: netfilter: nf_ct_labels: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_labels.h | 8 ++++---- net/netfilter/nf_conntrack_core.c | 17 ++++++++--------- net/netfilter/nf_conntrack_labels.c | 11 ++++------- 3 files changed, 16 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h index a3ce5d076fca..c985695283b3 100644 --- a/include/net/netfilter/nf_conntrack_labels.h +++ b/include/net/netfilter/nf_conntrack_labels.h @@ -50,9 +50,9 @@ int nf_connlabels_replace(struct nf_conn *ct, const u32 *data, const u32 *mask, unsigned int words); #ifdef CONFIG_NF_CONNTRACK_LABELS -int nf_conntrack_labels_init(struct net *net); -void nf_conntrack_labels_fini(struct net *net); +int nf_conntrack_labels_init(void); +void nf_conntrack_labels_fini(void); #else -static inline int nf_conntrack_labels_init(struct net *n) { return 0; } -static inline void nf_conntrack_labels_fini(struct net *net) {} +static inline int nf_conntrack_labels_init(void) { return 0; } +static inline void nf_conntrack_labels_fini(void) {} #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 06b8cdb5e620..a4a3bcf77627 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_labels_fini(); nf_conntrack_helper_fini(); nf_conntrack_timeout_fini(); nf_conntrack_ecache_fini(); @@ -1378,7 +1379,6 @@ void nf_conntrack_cleanup_net(struct net *net) nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_conntrack_proto_fini(net); - nf_conntrack_labels_fini(net); nf_conntrack_helper_pernet_fini(net); nf_conntrack_ecache_pernet_fini(net); nf_conntrack_tstamp_pernet_fini(net); @@ -1531,6 +1531,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_helper; + ret = nf_conntrack_labels_init(); + if (ret < 0) + goto err_labels; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1548,8 +1552,10 @@ int nf_conntrack_init_start(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_helper_fini(); + nf_conntrack_labels_fini(); #endif +err_labels: + nf_conntrack_helper_fini(); err_helper: nf_conntrack_timeout_fini(); err_timeout: @@ -1632,19 +1638,12 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_helper_pernet_init(net); if (ret < 0) goto err_helper; - - ret = nf_conntrack_labels_init(net); - if (ret < 0) - goto err_labels; - ret = nf_conntrack_proto_init(net); if (ret < 0) goto err_proto; return 0; err_proto: - nf_conntrack_labels_fini(net); -err_labels: nf_conntrack_helper_pernet_fini(net); err_helper: nf_conntrack_ecache_pernet_fini(net); diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c index e1d1eb850e7f..8fe2e99428b7 100644 --- a/net/netfilter/nf_conntrack_labels.c +++ b/net/netfilter/nf_conntrack_labels.c @@ -101,15 +101,12 @@ static struct nf_ct_ext_type labels_extend __read_mostly = { .id = NF_CT_EXT_LABELS, }; -int nf_conntrack_labels_init(struct net *net) +int nf_conntrack_labels_init(void) { - if (net_eq(net, &init_net)) - return nf_ct_extend_register(&labels_extend); - return 0; + return nf_ct_extend_register(&labels_extend); } -void nf_conntrack_labels_fini(struct net *net) +void nf_conntrack_labels_fini(void) { - if (net_eq(net, &init_net)) - nf_ct_extend_unregister(&labels_extend); + nf_ct_extend_unregister(&labels_extend); } -- cgit v1.2.3 From 04d870017908f40bbb1c51910acc030ae4979db4 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:32 +0000 Subject: netfilter: nf_ct_proto: move initialization out of pernet_operations Move the global initial codes to the module_init/exit context. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 7 +++++-- net/netfilter/nf_conntrack_core.c | 13 +++++++++--- net/netfilter/nf_conntrack_proto.c | 34 +++++++++++++++++-------------- 3 files changed, 34 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index e05c1f67055c..930275fa2ea6 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -28,8 +28,11 @@ extern unsigned int nf_conntrack_in(struct net *net, extern int nf_conntrack_init_net(struct net *net); extern void nf_conntrack_cleanup_net(struct net *net); -extern int nf_conntrack_proto_init(struct net *net); -extern void nf_conntrack_proto_fini(struct net *net); +extern int nf_conntrack_proto_pernet_init(struct net *net); +extern void nf_conntrack_proto_pernet_fini(struct net *net); + +extern int nf_conntrack_proto_init(void); +extern void nf_conntrack_proto_fini(void); extern int nf_conntrack_init_start(void); extern void nf_conntrack_cleanup_start(void); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index a4a3bcf77627..c8e001a9c45b 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1348,6 +1348,7 @@ void nf_conntrack_cleanup_end(void) #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif + nf_conntrack_proto_fini(); nf_conntrack_labels_fini(); nf_conntrack_helper_fini(); nf_conntrack_timeout_fini(); @@ -1378,7 +1379,7 @@ void nf_conntrack_cleanup_net(struct net *net) } nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); - nf_conntrack_proto_fini(net); + nf_conntrack_proto_pernet_fini(net); nf_conntrack_helper_pernet_fini(net); nf_conntrack_ecache_pernet_fini(net); nf_conntrack_tstamp_pernet_fini(net); @@ -1540,6 +1541,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_extend; #endif + ret = nf_conntrack_proto_init(); + if (ret < 0) + goto err_proto; + /* Set up fake conntrack: to never be deleted, not in any hashes */ for_each_possible_cpu(cpu) { struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu); @@ -1550,10 +1555,12 @@ int nf_conntrack_init_start(void) nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED); return 0; +err_proto: #ifdef CONFIG_NF_CONNTRACK_ZONES + nf_ct_extend_unregister(&nf_ct_zone_extend); err_extend: - nf_conntrack_labels_fini(); #endif + nf_conntrack_labels_fini(); err_labels: nf_conntrack_helper_fini(); err_helper: @@ -1638,7 +1645,7 @@ int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_helper_pernet_init(net); if (ret < 0) goto err_helper; - ret = nf_conntrack_proto_init(net); + ret = nf_conntrack_proto_pernet_init(net); if (ret < 0) goto err_proto; return 0; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 51e928db48c8..f0ec07c3fb6c 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -503,9 +503,8 @@ void nf_conntrack_l4proto_unregister(struct net *net, } EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); -int nf_conntrack_proto_init(struct net *net) +int nf_conntrack_proto_pernet_init(struct net *net) { - unsigned int i; int err; struct nf_proto_net *pn = nf_ct_l4proto_net(net, &nf_conntrack_l4proto_generic); @@ -520,19 +519,12 @@ int nf_conntrack_proto_init(struct net *net) if (err < 0) return err; - if (net == &init_net) { - for (i = 0; i < AF_MAX; i++) - rcu_assign_pointer(nf_ct_l3protos[i], - &nf_conntrack_l3proto_generic); - } - pn->users++; return 0; } -void nf_conntrack_proto_fini(struct net *net) +void nf_conntrack_proto_pernet_fini(struct net *net) { - unsigned int i; struct nf_proto_net *pn = nf_ct_l4proto_net(net, &nf_conntrack_l4proto_generic); @@ -540,9 +532,21 @@ void nf_conntrack_proto_fini(struct net *net) nf_ct_l4proto_unregister_sysctl(net, pn, &nf_conntrack_l4proto_generic); - if (net == &init_net) { - /* free l3proto protocol tables */ - for (i = 0; i < PF_MAX; i++) - kfree(nf_ct_protos[i]); - } +} + +int nf_conntrack_proto_init(void) +{ + unsigned int i; + for (i = 0; i < AF_MAX; i++) + rcu_assign_pointer(nf_ct_l3protos[i], + &nf_conntrack_l3proto_generic); + return 0; +} + +void nf_conntrack_proto_fini(void) +{ + unsigned int i; + /* free l3proto protocol tables */ + for (i = 0; i < PF_MAX; i++) + kfree(nf_ct_protos[i]); } -- cgit v1.2.3 From 6330750d566d764ce4916d8fe2bcdcad28fc7a42 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 21 Jan 2013 22:10:33 +0000 Subject: netfilter: nf_conntrack: refactor l3proto support for netns Move the code that register/unregister l3proto to the module_init/exit context. Given that we have to modify some interfaces to accomodate these changes, it is a good time to use shorter function names for this using the nf_ct_* prefix instead of nf_conntrack_*, that is: nf_ct_l3proto_register nf_ct_l3proto_pernet_register nf_ct_l3proto_unregister nf_ct_l3proto_pernet_unregister We same many line breaks with it. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 11 ++++++--- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 22 ++++++++++++------ net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 26 +++++++++++++-------- net/netfilter/nf_conntrack_proto.c | 31 ++++++++------------------ 4 files changed, 49 insertions(+), 41 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 6f7c13f4ac03..3bb89eac3fa1 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -76,11 +76,16 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; -/* Protocol registration. */ -extern int nf_conntrack_l3proto_register(struct net *net, +/* Protocol pernet registration. */ +extern int nf_ct_l3proto_pernet_register(struct net *net, struct nf_conntrack_l3proto *proto); -extern void nf_conntrack_l3proto_unregister(struct net *net, +extern void nf_ct_l3proto_pernet_unregister(struct net *net, struct nf_conntrack_l3proto *proto); + +/* Protocol global registration. */ +extern int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); +extern void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); + extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index fcdd0c2406e6..76b84fc9acfc 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -438,10 +438,9 @@ static int ipv4_net_init(struct net *net) pr_err("nf_conntrack_l4proto_icmp4 :protocol register failed\n"); goto out_icmp; } - ret = nf_conntrack_l3proto_register(net, - &nf_conntrack_l3proto_ipv4); + ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4); if (ret < 0) { - pr_err("nf_conntrack_l3proto_ipv4 :protocol register failed\n"); + pr_err("nf_conntrack_ipv4: pernet registration failed\n"); goto out_ipv4; } return 0; @@ -460,8 +459,7 @@ out_tcp: static void ipv4_net_exit(struct net *net) { - nf_conntrack_l3proto_unregister(net, - &nf_conntrack_l3proto_ipv4); + nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4); nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_icmp); nf_conntrack_l4proto_unregister(net, @@ -500,16 +498,25 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) pr_err("nf_conntrack_ipv4: can't register hooks.\n"); goto cleanup_pernet; } + + ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); + if (ret < 0) { + pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n"); + goto cleanup_hooks; + } + #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) ret = nf_conntrack_ipv4_compat_init(); if (ret < 0) - goto cleanup_hooks; + goto cleanup_proto; #endif return ret; #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) + cleanup_proto: + nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); +#endif cleanup_hooks: nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); -#endif cleanup_pernet: unregister_pernet_subsys(&ipv4_net_ops); cleanup_sockopt: @@ -523,6 +530,7 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) nf_conntrack_ipv4_compat_fini(); #endif + nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); unregister_pernet_subsys(&ipv4_net_ops); nf_unregister_sockopt(&so_getorigdst); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 137e245860ab..3eaf6baf8f61 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -439,10 +439,9 @@ static int ipv6_net_init(struct net *net) printk(KERN_ERR "nf_conntrack_l4proto_icmp6: protocol register failed\n"); goto cleanup_udp6; } - ret = nf_conntrack_l3proto_register(net, - &nf_conntrack_l3proto_ipv6); + ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6); if (ret < 0) { - printk(KERN_ERR "nf_conntrack_l3proto_ipv6: protocol register failed\n"); + pr_err("nf_conntrack_ipv6: pernet registration failed.\n"); goto cleanup_icmpv6; } return 0; @@ -461,8 +460,7 @@ static int ipv6_net_init(struct net *net) static void ipv6_net_exit(struct net *net) { - nf_conntrack_l3proto_unregister(net, - &nf_conntrack_l3proto_ipv6); + nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6); nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_icmpv6); nf_conntrack_l4proto_unregister(net, @@ -491,19 +489,28 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) ret = register_pernet_subsys(&ipv6_net_ops); if (ret < 0) - goto cleanup_pernet; + goto cleanup_sockopt; + ret = nf_register_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register pre-routing defrag " "hook.\n"); - goto cleanup_ipv6; + goto cleanup_pernet; + } + + ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6); + if (ret < 0) { + pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n"); + goto cleanup_hooks; } return ret; - cleanup_ipv6: - unregister_pernet_subsys(&ipv6_net_ops); + cleanup_hooks: + nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); cleanup_pernet: + unregister_pernet_subsys(&ipv6_net_ops); + cleanup_sockopt: nf_unregister_sockopt(&so_getorigdst6); return ret; } @@ -511,6 +518,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) static void __exit nf_conntrack_l3proto_ipv6_fini(void) { synchronize_net(); + nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index f0ec07c3fb6c..076d82707226 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -212,8 +212,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct net *net, #endif } -static int -nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto) +int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto) { int ret = 0; struct nf_conntrack_l3proto *old; @@ -242,8 +241,9 @@ out_unlock: return ret; } +EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); -int nf_conntrack_l3proto_register(struct net *net, +int nf_ct_l3proto_pernet_register(struct net *net, struct nf_conntrack_l3proto *proto) { int ret = 0; @@ -254,22 +254,11 @@ int nf_conntrack_l3proto_register(struct net *net, return ret; } - ret = nf_ct_l3proto_register_sysctl(net, proto); - if (ret < 0) - return ret; - - if (net == &init_net) { - ret = nf_conntrack_l3proto_register_net(proto); - if (ret < 0) - nf_ct_l3proto_unregister_sysctl(net, proto); - } - - return ret; + return nf_ct_l3proto_register_sysctl(net, proto); } -EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); +EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register); -static void -nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto) +void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) { BUG_ON(proto->l3proto >= AF_MAX); @@ -283,19 +272,17 @@ nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto) synchronize_rcu(); } +EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); -void nf_conntrack_l3proto_unregister(struct net *net, +void nf_ct_l3proto_pernet_unregister(struct net *net, struct nf_conntrack_l3proto *proto) { - if (net == &init_net) - nf_conntrack_l3proto_unregister_net(proto); - nf_ct_l3proto_unregister_sysctl(net, proto); /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(net, kill_l3proto, proto); } -EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); +EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister); static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, struct nf_conntrack_l4proto *l4proto) -- cgit v1.2.3 From c296bb4d5d417d466c9bcc8afef68a3db5449a64 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Wed, 23 Jan 2013 12:51:10 +0100 Subject: netfilter: nf_conntrack: refactor l4proto support for netns Move the code that register/unregister l4proto to the module_init/exit context. Given that we have to modify some interfaces to accomodate these changes, it is a good time to use shorter function names for this using the nf_ct_* prefix instead of nf_conntrack_*, that is: nf_ct_l4proto_register nf_ct_l4proto_pernet_register nf_ct_l4proto_unregister nf_ct_l4proto_pernet_unregister We same many line breaks with it. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 10 +++-- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 62 +++++++++++++++++--------- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 62 +++++++++++++++++--------- net/netfilter/nf_conntrack_proto.c | 27 ++++------- net/netfilter/nf_conntrack_proto_dccp.c | 43 ++++++++++++------ net/netfilter/nf_conntrack_proto_gre.c | 23 ++++++++-- net/netfilter/nf_conntrack_proto_sctp.c | 43 ++++++++++++------ net/netfilter/nf_conntrack_proto_udplite.c | 40 ++++++++++++----- 8 files changed, 204 insertions(+), 106 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index c3be4aef6bf7..914d8d900798 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -121,12 +121,16 @@ extern struct nf_conntrack_l4proto * nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); -/* Protocol registration. */ -extern int nf_conntrack_l4proto_register(struct net *net, +/* Protocol pernet registration. */ +extern int nf_ct_l4proto_pernet_register(struct net *net, struct nf_conntrack_l4proto *proto); -extern void nf_conntrack_l4proto_unregister(struct net *net, +extern void nf_ct_l4proto_pernet_unregister(struct net *net, struct nf_conntrack_l4proto *proto); +/* Protocol global registration. */ +extern int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto); +extern void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto); + static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn) { #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 76b84fc9acfc..48990ada0e1e 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -420,22 +420,19 @@ static int ipv4_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_tcp4); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4); if (ret < 0) { - pr_err("nf_conntrack_l4proto_tcp4 :protocol register failed\n"); + pr_err("nf_conntrack_tcp4: pernet registration failed\n"); goto out_tcp; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_udp4); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4); if (ret < 0) { - pr_err("nf_conntrack_l4proto_udp4 :protocol register failed\n"); + pr_err("nf_conntrack_udp4: pernet registration failed\n"); goto out_udp; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_icmp); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp); if (ret < 0) { - pr_err("nf_conntrack_l4proto_icmp4 :protocol register failed\n"); + pr_err("nf_conntrack_icmp4: pernet registration failed\n"); goto out_icmp; } ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4); @@ -445,14 +442,11 @@ static int ipv4_net_init(struct net *net) } return 0; out_ipv4: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_icmp); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp); out_icmp: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_udp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4); out_udp: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_tcp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4); out_tcp: return ret; } @@ -460,12 +454,9 @@ out_tcp: static void ipv4_net_exit(struct net *net) { nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_icmp); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_udp4); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_tcp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4); } static struct pernet_operations ipv4_net_ops = { @@ -499,10 +490,28 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) goto cleanup_pernet; } + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4); + if (ret < 0) { + pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n"); + goto cleanup_hooks; + } + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4); + if (ret < 0) { + pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n"); + goto cleanup_tcp4; + } + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp); + if (ret < 0) { + pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n"); + goto cleanup_udp4; + } + ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n"); - goto cleanup_hooks; + goto cleanup_icmpv4; } #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) @@ -515,6 +524,12 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) cleanup_proto: nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); #endif + cleanup_icmpv4: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp); + cleanup_udp4: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4); + cleanup_tcp4: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4); cleanup_hooks: nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); cleanup_pernet: @@ -531,6 +546,9 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) nf_conntrack_ipv4_compat_fini(); #endif nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4); nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); unregister_pernet_subsys(&ipv4_net_ops); nf_unregister_sockopt(&so_getorigdst); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 3eaf6baf8f61..8a45bb20bedb 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -421,22 +421,19 @@ static int ipv6_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_tcp6); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6); if (ret < 0) { - printk(KERN_ERR "nf_conntrack_l4proto_tcp6: protocol register failed\n"); + pr_err("nf_conntrack_tcp6: pernet registration failed\n"); goto out; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_udp6); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6); if (ret < 0) { - printk(KERN_ERR "nf_conntrack_l4proto_udp6: protocol register failed\n"); + pr_err("nf_conntrack_udp6: pernet registration failed\n"); goto cleanup_tcp6; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_icmpv6); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6); if (ret < 0) { - printk(KERN_ERR "nf_conntrack_l4proto_icmp6: protocol register failed\n"); + pr_err("nf_conntrack_icmp6: pernet registration failed\n"); goto cleanup_udp6; } ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6); @@ -446,14 +443,11 @@ static int ipv6_net_init(struct net *net) } return 0; cleanup_icmpv6: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_icmpv6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6); cleanup_udp6: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_udp6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6); cleanup_tcp6: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_tcp6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6); out: return ret; } @@ -461,12 +455,9 @@ static int ipv6_net_init(struct net *net) static void ipv6_net_exit(struct net *net) { nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_icmpv6); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_udp6); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_tcp6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6); } static struct pernet_operations ipv6_net_ops = { @@ -499,13 +490,37 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) goto cleanup_pernet; } + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6); + if (ret < 0) { + pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n"); + goto cleanup_hooks; + } + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6); + if (ret < 0) { + pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n"); + goto cleanup_tcp6; + } + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6); + if (ret < 0) { + pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n"); + goto cleanup_udp6; + } + ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n"); - goto cleanup_hooks; + goto cleanup_icmpv6; } return ret; + cleanup_icmpv6: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); + cleanup_udp6: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6); + cleanup_tcp6: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6); cleanup_hooks: nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); cleanup_pernet: @@ -519,6 +534,9 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) { synchronize_net(); nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); unregister_pernet_subsys(&ipv6_net_ops); nf_unregister_sockopt(&so_getorigdst6); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 076d82707226..58ab4050830c 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -363,8 +363,7 @@ void nf_ct_l4proto_unregister_sysctl(struct net *net, /* FIXME: Allow NULL functions and sub in pointers to generic for them. --RR */ -static int -nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto) +int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto) { int ret = 0; @@ -418,8 +417,9 @@ out_unlock: mutex_unlock(&nf_ct_proto_mutex); return ret; } +EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); -int nf_conntrack_l4proto_register(struct net *net, +int nf_ct_l4proto_pernet_register(struct net *net, struct nf_conntrack_l4proto *l4proto) { int ret = 0; @@ -439,22 +439,13 @@ int nf_conntrack_l4proto_register(struct net *net, if (ret < 0) goto out; - if (net == &init_net) { - ret = nf_conntrack_l4proto_register_net(l4proto); - if (ret < 0) { - nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); - goto out; - } - } - pn->users++; out: return ret; } -EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); +EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register); -static void -nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) +void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) { BUG_ON(l4proto->l3proto >= PF_MAX); @@ -469,15 +460,13 @@ nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) synchronize_rcu(); } +EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); -void nf_conntrack_l4proto_unregister(struct net *net, +void nf_ct_l4proto_pernet_unregister(struct net *net, struct nf_conntrack_l4proto *l4proto) { struct nf_proto_net *pn = NULL; - if (net == &init_net) - nf_conntrack_l4proto_unregister_net(l4proto); - pn = nf_ct_l4proto_net(net, l4proto); if (pn == NULL) return; @@ -488,7 +477,7 @@ void nf_conntrack_l4proto_unregister(struct net *net, /* Remove all contrack entries for this protocol */ nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); } -EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); +EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister); int nf_conntrack_proto_pernet_init(struct net *net) { diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index a8ae287bc7af..432f95780003 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -935,32 +935,27 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { static __net_init int dccp_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, - &dccp_proto4); + ret = nf_ct_l4proto_pernet_register(net, &dccp_proto4); if (ret < 0) { - pr_err("nf_conntrack_l4proto_dccp4 :protocol register failed.\n"); + pr_err("nf_conntrack_dccp4: pernet registration failed.\n"); goto out; } - ret = nf_conntrack_l4proto_register(net, - &dccp_proto6); + ret = nf_ct_l4proto_pernet_register(net, &dccp_proto6); if (ret < 0) { - pr_err("nf_conntrack_l4proto_dccp6 :protocol register failed.\n"); + pr_err("nf_conntrack_dccp6: pernet registration failed.\n"); goto cleanup_dccp4; } return 0; cleanup_dccp4: - nf_conntrack_l4proto_unregister(net, - &dccp_proto4); + nf_ct_l4proto_pernet_unregister(net, &dccp_proto4); out: return ret; } static __net_exit void dccp_net_exit(struct net *net) { - nf_conntrack_l4proto_unregister(net, - &dccp_proto6); - nf_conntrack_l4proto_unregister(net, - &dccp_proto4); + nf_ct_l4proto_pernet_unregister(net, &dccp_proto6); + nf_ct_l4proto_pernet_unregister(net, &dccp_proto4); } static struct pernet_operations dccp_net_ops = { @@ -972,11 +967,33 @@ static struct pernet_operations dccp_net_ops = { static int __init nf_conntrack_proto_dccp_init(void) { - return register_pernet_subsys(&dccp_net_ops); + int ret; + + ret = nf_ct_l4proto_register(&dccp_proto4); + if (ret < 0) + goto out_dccp4; + + ret = nf_ct_l4proto_register(&dccp_proto6); + if (ret < 0) + goto out_dccp6; + + ret = register_pernet_subsys(&dccp_net_ops); + if (ret < 0) + goto out_pernet; + + return 0; +out_pernet: + nf_ct_l4proto_unregister(&dccp_proto6); +out_dccp6: + nf_ct_l4proto_unregister(&dccp_proto4); +out_dccp4: + return ret; } static void __exit nf_conntrack_proto_dccp_fini(void) { + nf_ct_l4proto_unregister(&dccp_proto6); + nf_ct_l4proto_unregister(&dccp_proto4); unregister_pernet_subsys(&dccp_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index b09b7af7f6f8..bd7d01d9c7e7 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -397,15 +397,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { static int proto_gre_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, &nf_conntrack_l4proto_gre4); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_gre4); if (ret < 0) - pr_err("nf_conntrack_l4proto_gre4 :protocol register failed.\n"); + pr_err("nf_conntrack_gre4: pernet registration failed.\n"); return ret; } static void proto_gre_net_exit(struct net *net) { - nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_gre4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_gre4); nf_ct_gre_keymap_flush(net); } @@ -418,11 +418,26 @@ static struct pernet_operations proto_gre_net_ops = { static int __init nf_ct_proto_gre_init(void) { - return register_pernet_subsys(&proto_gre_net_ops); + int ret; + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_gre4); + if (ret < 0) + goto out_gre4; + + ret = register_pernet_subsys(&proto_gre_net_ops); + if (ret < 0) + goto out_pernet; + + return 0; +out_pernet: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4); +out_gre4: + return ret; } static void __exit nf_ct_proto_gre_fini(void) { + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4); unregister_pernet_subsys(&proto_gre_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index c746d61f83ed..480f616d5936 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -853,33 +853,28 @@ static int sctp_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_sctp4); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp4); if (ret < 0) { - pr_err("nf_conntrack_l4proto_sctp4 :protocol register failed.\n"); + pr_err("nf_conntrack_sctp4: pernet registration failed.\n"); goto out; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_sctp6); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp6); if (ret < 0) { - pr_err("nf_conntrack_l4proto_sctp6 :protocol register failed.\n"); + pr_err("nf_conntrack_sctp6: pernet registration failed.\n"); goto cleanup_sctp4; } return 0; cleanup_sctp4: - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_sctp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4); out: return ret; } static void sctp_net_exit(struct net *net) { - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_sctp6); - nf_conntrack_l4proto_unregister(net, - &nf_conntrack_l4proto_sctp4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4); } static struct pernet_operations sctp_net_ops = { @@ -891,11 +886,33 @@ static struct pernet_operations sctp_net_ops = { static int __init nf_conntrack_proto_sctp_init(void) { - return register_pernet_subsys(&sctp_net_ops); + int ret; + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp4); + if (ret < 0) + goto out_sctp4; + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp6); + if (ret < 0) + goto out_sctp6; + + ret = register_pernet_subsys(&sctp_net_ops); + if (ret < 0) + goto out_pernet; + + return 0; +out_pernet: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6); +out_sctp6: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4); +out_sctp4: + return ret; } static void __exit nf_conntrack_proto_sctp_fini(void) { + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4); unregister_pernet_subsys(&sctp_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 4b66df209286..157489581c31 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -336,30 +336,28 @@ static int udplite_net_init(struct net *net) { int ret = 0; - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_udplite4); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite4); if (ret < 0) { - pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n"); + pr_err("nf_conntrack_udplite4: pernet registration failed.\n"); goto out; } - ret = nf_conntrack_l4proto_register(net, - &nf_conntrack_l4proto_udplite6); + ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite6); if (ret < 0) { - pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n"); + pr_err("nf_conntrack_udplite6: pernet registration failed.\n"); goto cleanup_udplite4; } return 0; cleanup_udplite4: - nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4); out: return ret; } static void udplite_net_exit(struct net *net) { - nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite6); - nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite6); + nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4); } static struct pernet_operations udplite_net_ops = { @@ -371,11 +369,33 @@ static struct pernet_operations udplite_net_ops = { static int __init nf_conntrack_proto_udplite_init(void) { - return register_pernet_subsys(&udplite_net_ops); + int ret; + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite4); + if (ret < 0) + goto out_udplite4; + + ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite6); + if (ret < 0) + goto out_udplite6; + + ret = register_pernet_subsys(&udplite_net_ops); + if (ret < 0) + goto out_pernet; + + return 0; +out_pernet: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6); +out_udplite6: + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4); +out_udplite4: + return ret; } static void __exit nf_conntrack_proto_udplite_exit(void) { + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6); + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4); unregister_pernet_subsys(&udplite_net_ops); } -- cgit v1.2.3 From 5f9f946b39bff31bc9a4a8be18cef3354d3382ae Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 23 Jan 2013 15:12:25 +0100 Subject: netfilter: nf_conntrack: fix compilation if sysctl are disabled In (f94161c netfilter: nf_conntrack: move initialization out of pernet operations), some ifdefs were missing for sysctl dependent code. Reported-by: kbuild test robot Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_standalone.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 725bf04a2fb9..7936bf7f90ba 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -563,10 +563,14 @@ static int __init nf_conntrack_standalone_init(void) if (ret < 0) goto out_start; +#ifdef CONFIG_SYSCTL nf_ct_netfilter_header = register_net_sysctl(&init_net, "net", nf_ct_netfilter_table); - if (!nf_ct_netfilter_header) + if (!nf_ct_netfilter_header) { + pr_err("nf_conntrack: can't register to sysctl.\n"); goto out_sysctl; + } +#endif ret = register_pernet_subsys(&nf_conntrack_net_ops); if (ret < 0) @@ -576,9 +580,10 @@ static int __init nf_conntrack_standalone_init(void) return 0; out_pernet: +#ifdef CONFIG_SYSCTL unregister_net_sysctl_table(nf_ct_netfilter_header); out_sysctl: - pr_err("nf_conntrack: can't register to sysctl.\n"); +#endif nf_conntrack_cleanup_end(); out_start: return ret; @@ -588,7 +593,9 @@ static void __exit nf_conntrack_standalone_fini(void) { nf_conntrack_cleanup_start(); unregister_pernet_subsys(&nf_conntrack_net_ops); +#ifdef CONFIG_SYSCTL unregister_net_sysctl_table(nf_ct_netfilter_header); +#endif nf_conntrack_cleanup_end(); } -- cgit v1.2.3 From 055dc21a1d1d219608cd4baac7d0683fb2cbbe8a Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 22 Jan 2013 09:49:50 +0000 Subject: soreuseport: infrastructure Definitions and macros for implementing soreusport. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- arch/alpha/include/uapi/asm/socket.h | 2 +- arch/avr32/include/uapi/asm/socket.h | 2 +- arch/cris/include/uapi/asm/socket.h | 2 +- arch/frv/include/uapi/asm/socket.h | 2 +- arch/h8300/include/uapi/asm/socket.h | 2 +- arch/ia64/include/uapi/asm/socket.h | 2 +- arch/m32r/include/uapi/asm/socket.h | 2 +- arch/mips/include/uapi/asm/socket.h | 3 +-- arch/mn10300/include/uapi/asm/socket.h | 2 +- arch/parisc/include/uapi/asm/socket.h | 2 +- arch/powerpc/include/uapi/asm/socket.h | 2 +- arch/s390/include/uapi/asm/socket.h | 2 +- arch/sparc/include/uapi/asm/socket.h | 2 +- arch/xtensa/include/uapi/asm/socket.h | 2 +- include/linux/random.h | 6 ++++++ include/net/sock.h | 5 ++++- include/uapi/asm-generic/socket.h | 3 +-- net/core/sock.c | 7 +++++++ 18 files changed, 32 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 755702eefd9c..c5195524d1ef 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -19,7 +19,7 @@ #define SO_BROADCAST 0x0020 #define SO_LINGER 0x0080 #define SO_OOBINLINE 0x0100 -/* To add :#define SO_REUSEPORT 0x0200 */ +#define SO_REUSEPORT 0x0200 #define SO_TYPE 0x1008 #define SO_ERROR 0x1007 diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index f3f38a0e2ef9..51c6401582ea 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -22,7 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h index 406b5838defd..50692b738c75 100644 --- a/arch/cris/include/uapi/asm/socket.h +++ b/arch/cris/include/uapi/asm/socket.h @@ -24,7 +24,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index d8e1132a1ab6..595391f0f98c 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -22,7 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/h8300/include/uapi/asm/socket.h b/arch/h8300/include/uapi/asm/socket.h index c8b87a828206..43e32621da7d 100644 --- a/arch/h8300/include/uapi/asm/socket.h +++ b/arch/h8300/include/uapi/asm/socket.h @@ -22,7 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index f390896c3104..c567adc8bea5 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -31,7 +31,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 6a895155e7a3..519afa2755db 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -22,7 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 9d11a7713923..7e2723637b35 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -28,8 +28,7 @@ #define SO_LINGER 0x0080 /* Block on close of a reliable socket to transmit pending data. */ #define SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. */ -#if 0 -To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ +#define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #endif #define SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index ab702c40b30e..5c7c7c988544 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -22,7 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index da2c8d3c209e..526e4b9aece0 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -13,7 +13,7 @@ #define SO_BROADCAST 0x0020 #define SO_LINGER 0x0080 #define SO_OOBINLINE 0x0100 -/* To add :#define SO_REUSEPORT 0x0200 */ +#define SO_REUSEPORT 0x0200 #define SO_SNDBUF 0x1001 #define SO_RCVBUF 0x1002 #define SO_SNDBUFFORCE 0x100a diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index e6ca31816cc9..a26dcaece509 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -29,7 +29,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_RCVLOWAT 16 #define SO_SNDLOWAT 17 #define SO_RCVTIMEO 18 diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 9ce60b68f070..f99eea7fff0f 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -28,7 +28,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index fbbba57547d1..cbbad74b2e06 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -15,7 +15,7 @@ #define SO_PEERCRED 0x0040 #define SO_LINGER 0x0080 #define SO_OOBINLINE 0x0100 -/* To add :#define SO_REUSEPORT 0x0200 */ +#define SO_REUSEPORT 0x0200 #define SO_BSDCOMPAT 0x0400 #define SO_RCVLOWAT 0x0800 #define SO_SNDLOWAT 0x1000 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index dbf316487b51..35905cb6e419 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -32,7 +32,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ +#define SO_REUSEPORT 15 #define SO_PASSCRED 16 #define SO_PEERCRED 17 #define SO_RCVLOWAT 18 diff --git a/include/linux/random.h b/include/linux/random.h index d9846088c2c5..347ce553a306 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -74,4 +74,10 @@ static inline int arch_get_random_int(unsigned int *v) } #endif +/* Pseudo random number generator from numerical recipes. */ +static inline u32 next_pseudo_random32(u32 seed) +{ + return seed * 1664525 + 1013904223; +} + #endif /* _LINUX_RANDOM_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 5a34e2f03657..581dc6bd7dc6 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -140,6 +140,7 @@ typedef __u64 __bitwise __addrpair; * @skc_family: network address family * @skc_state: Connection state * @skc_reuse: %SO_REUSEADDR setting + * @skc_reuseport: %SO_REUSEPORT setting * @skc_bound_dev_if: bound device index if != 0 * @skc_bind_node: bind hash linkage for various protocol lookup tables * @skc_portaddr_node: second hash linkage for UDP/UDP-Lite protocol @@ -179,7 +180,8 @@ struct sock_common { unsigned short skc_family; volatile unsigned char skc_state; - unsigned char skc_reuse; + unsigned char skc_reuse:4; + unsigned char skc_reuseport:4; int skc_bound_dev_if; union { struct hlist_node skc_bind_node; @@ -297,6 +299,7 @@ struct sock { #define sk_family __sk_common.skc_family #define sk_state __sk_common.skc_state #define sk_reuse __sk_common.skc_reuse +#define sk_reuseport __sk_common.skc_reuseport #define sk_bound_dev_if __sk_common.skc_bound_dev_if #define sk_bind_node __sk_common.skc_bind_node #define sk_prot __sk_common.skc_prot diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 3f6a99201410..4ef3acbba5da 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -22,8 +22,7 @@ #define SO_PRIORITY 12 #define SO_LINGER 13 #define SO_BSDCOMPAT 14 -/* To add :#define SO_REUSEPORT 15 */ - +#define SO_REUSEPORT 15 #ifndef SO_PASSCRED /* powerpc only differs in these */ #define SO_PASSCRED 16 #define SO_PEERCRED 17 diff --git a/net/core/sock.c b/net/core/sock.c index 8258fb741e9a..235fb89e8973 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -665,6 +665,9 @@ int sock_setsockopt(struct socket *sock, int level, int optname, case SO_REUSEADDR: sk->sk_reuse = (valbool ? SK_CAN_REUSE : SK_NO_REUSE); break; + case SO_REUSEPORT: + sk->sk_reuseport = valbool; + break; case SO_TYPE: case SO_PROTOCOL: case SO_DOMAIN: @@ -972,6 +975,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_reuse; break; + case SO_REUSEPORT: + v.val = sk->sk_reuseport; + break; + case SO_KEEPALIVE: v.val = sock_flag(sk, SOCK_KEEPOPEN); break; -- cgit v1.2.3 From da5e36308d9f7151845018369148201a5d28b46d Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 22 Jan 2013 09:50:24 +0000 Subject: soreuseport: TCP/IPv4 implementation Allow multiple listener sockets to bind to the same port. Motivation for soresuseport would be something like a web server binding to port 80 running with multiple threads, where each thread might have it's own listener socket. This could be done as an alternative to other models: 1) have one listener thread which dispatches completed connections to workers. 2) accept on a single listener socket from multiple threads. In case #1 the listener thread can easily become the bottleneck with high connection turn-over rate. In case #2, the proportion of connections accepted per thread tends to be uneven under high connection load (assuming simple event loop: while (1) { accept(); process() }, wakeup does not promote fairness among the sockets. We have seen the disproportion to be as high as 3:1 ratio between thread accepting most connections and the one accepting the fewest. With so_reusport the distribution is uniform. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 13 ++++++--- include/net/netfilter/nf_tproxy_core.h | 1 + net/ipv4/inet_connection_sock.c | 48 ++++++++++++++++++++++++++-------- net/ipv4/inet_hashtables.c | 28 +++++++++++++++----- net/ipv4/tcp_ipv4.c | 4 ++- 5 files changed, 73 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 67a8fa098e3a..7b2ae9d37076 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -81,7 +81,9 @@ struct inet_bind_bucket { struct net *ib_net; #endif unsigned short port; - signed short fastreuse; + signed char fastreuse; + signed char fastreuseport; + kuid_t fastuid; int num_owners; struct hlist_node node; struct hlist_head owners; @@ -257,15 +259,19 @@ extern void inet_unhash(struct sock *sk); extern struct sock *__inet_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, + const __be32 saddr, + const __be16 sport, const __be32 daddr, const unsigned short hnum, const int dif); static inline struct sock *inet_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, + __be32 saddr, __be16 sport, __be32 daddr, __be16 dport, int dif) { - return __inet_lookup_listener(net, hashinfo, daddr, ntohs(dport), dif); + return __inet_lookup_listener(net, hashinfo, saddr, sport, + daddr, ntohs(dport), dif); } /* Socket demux engine toys. */ @@ -358,7 +364,8 @@ static inline struct sock *__inet_lookup(struct net *net, struct sock *sk = __inet_lookup_established(net, hashinfo, saddr, sport, daddr, hnum, dif); - return sk ? : __inet_lookup_listener(net, hashinfo, daddr, hnum, dif); + return sk ? : __inet_lookup_listener(net, hashinfo, saddr, sport, + daddr, hnum, dif); } static inline struct sock *inet_lookup(struct net *net, diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h index 75ca9291cf2c..193796445642 100644 --- a/include/net/netfilter/nf_tproxy_core.h +++ b/include/net/netfilter/nf_tproxy_core.h @@ -82,6 +82,7 @@ nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, break; case NFT_LOOKUP_LISTENER: sk = inet_lookup_listener(net, &tcp_hashinfo, + saddr, sport, daddr, dport, in->ifindex); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index d0670f00d524..8bb623d357ad 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -59,6 +59,8 @@ int inet_csk_bind_conflict(const struct sock *sk, struct sock *sk2; struct hlist_node *node; int reuse = sk->sk_reuse; + int reuseport = sk->sk_reuseport; + kuid_t uid = sock_i_uid((struct sock *)sk); /* * Unlike other sk lookup places we do not check @@ -73,8 +75,11 @@ int inet_csk_bind_conflict(const struct sock *sk, (!sk->sk_bound_dev_if || !sk2->sk_bound_dev_if || sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) { - if (!reuse || !sk2->sk_reuse || - sk2->sk_state == TCP_LISTEN) { + if ((!reuse || !sk2->sk_reuse || + sk2->sk_state == TCP_LISTEN) && + (!reuseport || !sk2->sk_reuseport || + (sk2->sk_state != TCP_TIME_WAIT && + !uid_eq(uid, sock_i_uid(sk2))))) { const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2); if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) || sk2_rcv_saddr == sk_rcv_saddr(sk)) @@ -106,6 +111,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) int ret, attempts = 5; struct net *net = sock_net(sk); int smallest_size = -1, smallest_rover; + kuid_t uid = sock_i_uid(sk); local_bh_disable(); if (!snum) { @@ -125,9 +131,12 @@ again: spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) if (net_eq(ib_net(tb), net) && tb->port == rover) { - if (tb->fastreuse > 0 && - sk->sk_reuse && - sk->sk_state != TCP_LISTEN && + if (((tb->fastreuse > 0 && + sk->sk_reuse && + sk->sk_state != TCP_LISTEN) || + (tb->fastreuseport > 0 && + sk->sk_reuseport && + uid_eq(tb->fastuid, uid))) && (tb->num_owners < smallest_size || smallest_size == -1)) { smallest_size = tb->num_owners; smallest_rover = rover; @@ -185,14 +194,17 @@ tb_found: if (sk->sk_reuse == SK_FORCE_REUSE) goto success; - if (tb->fastreuse > 0 && - sk->sk_reuse && sk->sk_state != TCP_LISTEN && + if (((tb->fastreuse > 0 && + sk->sk_reuse && sk->sk_state != TCP_LISTEN) || + (tb->fastreuseport > 0 && + sk->sk_reuseport && uid_eq(tb->fastuid, uid))) && smallest_size == -1) { goto success; } else { ret = 1; if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true)) { - if (sk->sk_reuse && sk->sk_state != TCP_LISTEN && + if (((sk->sk_reuse && sk->sk_state != TCP_LISTEN) || + (sk->sk_reuseport && uid_eq(tb->fastuid, uid))) && smallest_size != -1 && --attempts >= 0) { spin_unlock(&head->lock); goto again; @@ -212,9 +224,23 @@ tb_not_found: tb->fastreuse = 1; else tb->fastreuse = 0; - } else if (tb->fastreuse && - (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) - tb->fastreuse = 0; + if (sk->sk_reuseport) { + tb->fastreuseport = 1; + tb->fastuid = uid; + } else { + tb->fastreuseport = 0; + tb->fastuid = 0; + } + } else { + if (tb->fastreuse && + (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) + tb->fastreuse = 0; + if (tb->fastreuseport && + (!sk->sk_reuseport || !uid_eq(tb->fastuid, uid))) { + tb->fastreuseport = 0; + tb->fastuid = 0; + } + } success: if (!inet_csk(sk)->icsk_bind_hash) inet_bind_hash(sk, tb, snum); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index fa3ae8148710..0ce0595d9861 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -39,6 +39,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep, write_pnet(&tb->ib_net, hold_net(net)); tb->port = snum; tb->fastreuse = 0; + tb->fastreuseport = 0; tb->num_owners = 0; INIT_HLIST_HEAD(&tb->owners); hlist_add_head(&tb->node, &head->chain); @@ -151,16 +152,16 @@ static inline int compute_score(struct sock *sk, struct net *net, if (net_eq(sock_net(sk), net) && inet->inet_num == hnum && !ipv6_only_sock(sk)) { __be32 rcv_saddr = inet->inet_rcv_saddr; - score = sk->sk_family == PF_INET ? 1 : 0; + score = sk->sk_family == PF_INET ? 2 : 1; if (rcv_saddr) { if (rcv_saddr != daddr) return -1; - score += 2; + score += 4; } if (sk->sk_bound_dev_if) { if (sk->sk_bound_dev_if != dif) return -1; - score += 2; + score += 4; } } return score; @@ -176,6 +177,7 @@ static inline int compute_score(struct sock *sk, struct net *net, struct sock *__inet_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, + const __be32 saddr, __be16 sport, const __be32 daddr, const unsigned short hnum, const int dif) { @@ -183,17 +185,29 @@ struct sock *__inet_lookup_listener(struct net *net, struct hlist_nulls_node *node; unsigned int hash = inet_lhashfn(net, hnum); struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; - int score, hiscore; + int score, hiscore, matches = 0, reuseport = 0; + u32 phash = 0; rcu_read_lock(); begin: result = NULL; - hiscore = -1; + hiscore = 0; sk_nulls_for_each_rcu(sk, node, &ilb->head) { score = compute_score(sk, net, hnum, daddr, dif); if (score > hiscore) { result = sk; hiscore = score; + reuseport = sk->sk_reuseport; + if (reuseport) { + phash = inet_ehashfn(net, daddr, hnum, + saddr, sport); + matches = 1; + } + } else if (score == hiscore && reuseport) { + matches++; + if (((u64)phash * matches) >> 32 == 0) + result = sk; + phash = next_pseudo_random32(phash); } } /* @@ -501,7 +515,8 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, inet_bind_bucket_for_each(tb, node, &head->chain) { if (net_eq(ib_net(tb), net) && tb->port == port) { - if (tb->fastreuse >= 0) + if (tb->fastreuse >= 0 || + tb->fastreuseport >= 0) goto next_port; WARN_ON(hlist_empty(&tb->owners)); if (!check_established(death_row, sk, @@ -518,6 +533,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, break; } tb->fastreuse = -1; + tb->fastreuseport = -1; goto ok; next_port: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c6ce9ca98d23..bbbdcc5c1973 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -657,7 +657,8 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) * no RST generated if md5 hash doesn't match. */ sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev), - &tcp_hashinfo, ip_hdr(skb)->daddr, + &tcp_hashinfo, ip_hdr(skb)->saddr, + th->source, ip_hdr(skb)->daddr, ntohs(th->source), inet_iif(skb)); /* don't send rst if it can't find key */ if (!sk1) @@ -2074,6 +2075,7 @@ do_time_wait: case TCP_TW_SYN: { struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev), &tcp_hashinfo, + iph->saddr, th->source, iph->daddr, th->dest, inet_iif(skb)); if (sk2) { -- cgit v1.2.3 From ba418fa357a7b3c9d477f4706c6c7c96ddbd1360 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 22 Jan 2013 09:50:32 +0000 Subject: soreuseport: UDP/IPv4 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow multiple UDP sockets to bind to the same port. Motivation soreuseport would be something like a DNS server.  An alternative would be to recv on the same socket from multiple threads. As in the case of TCP, the load across these threads tends to be disproportionate and we also see a lot of contection on the socketlock. Note that SO_REUSEADDR already allows multiple UDP sockets to bind to the same port, however there is no provision to prevent hijacking and nothing to distribute packets across all the sockets sharing the same bound port.  This patch does not change the semantics of SO_REUSEADDR, but provides usable functionality of it for unicast. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv4/udp.c | 61 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index cf6158f1f46b..e0610e4b5158 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -139,6 +139,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, { struct sock *sk2; struct hlist_nulls_node *node; + kuid_t uid = sock_i_uid(sk); sk_nulls_for_each(sk2, node, &hslot->head) if (net_eq(sock_net(sk2), net) && @@ -147,6 +148,8 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, (!sk2->sk_reuse || !sk->sk_reuse) && (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && + (!sk2->sk_reuseport || !sk->sk_reuseport || + !uid_eq(uid, sock_i_uid(sk2))) && (*saddr_comp)(sk, sk2)) { if (bitmap) __set_bit(udp_sk(sk2)->udp_port_hash >> log, @@ -169,6 +172,7 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num, { struct sock *sk2; struct hlist_nulls_node *node; + kuid_t uid = sock_i_uid(sk); int res = 0; spin_lock(&hslot2->lock); @@ -179,6 +183,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num, (!sk2->sk_reuse || !sk->sk_reuse) && (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && + (!sk2->sk_reuseport || !sk->sk_reuseport || + !uid_eq(uid, sock_i_uid(sk2))) && (*saddr_comp)(sk, sk2)) { res = 1; break; @@ -337,26 +343,26 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, !ipv6_only_sock(sk)) { struct inet_sock *inet = inet_sk(sk); - score = (sk->sk_family == PF_INET ? 1 : 0); + score = (sk->sk_family == PF_INET ? 2 : 1); if (inet->inet_rcv_saddr) { if (inet->inet_rcv_saddr != daddr) return -1; - score += 2; + score += 4; } if (inet->inet_daddr) { if (inet->inet_daddr != saddr) return -1; - score += 2; + score += 4; } if (inet->inet_dport) { if (inet->inet_dport != sport) return -1; - score += 2; + score += 4; } if (sk->sk_bound_dev_if) { if (sk->sk_bound_dev_if != dif) return -1; - score += 2; + score += 4; } } return score; @@ -365,7 +371,6 @@ static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, /* * In this second variant, we check (daddr, dport) matches (inet_rcv_sadd, inet_num) */ -#define SCORE2_MAX (1 + 2 + 2 + 2) static inline int compute_score2(struct sock *sk, struct net *net, __be32 saddr, __be16 sport, __be32 daddr, unsigned int hnum, int dif) @@ -380,21 +385,21 @@ static inline int compute_score2(struct sock *sk, struct net *net, if (inet->inet_num != hnum) return -1; - score = (sk->sk_family == PF_INET ? 1 : 0); + score = (sk->sk_family == PF_INET ? 2 : 1); if (inet->inet_daddr) { if (inet->inet_daddr != saddr) return -1; - score += 2; + score += 4; } if (inet->inet_dport) { if (inet->inet_dport != sport) return -1; - score += 2; + score += 4; } if (sk->sk_bound_dev_if) { if (sk->sk_bound_dev_if != dif) return -1; - score += 2; + score += 4; } } return score; @@ -409,19 +414,29 @@ static struct sock *udp4_lib_lookup2(struct net *net, { struct sock *sk, *result; struct hlist_nulls_node *node; - int score, badness; + int score, badness, matches = 0, reuseport = 0; + u32 hash = 0; begin: result = NULL; - badness = -1; + badness = 0; udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) { score = compute_score2(sk, net, saddr, sport, daddr, hnum, dif); if (score > badness) { result = sk; badness = score; - if (score == SCORE2_MAX) - goto exact_match; + reuseport = sk->sk_reuseport; + if (reuseport) { + hash = inet_ehashfn(net, daddr, hnum, + saddr, htons(sport)); + matches = 1; + } + } else if (score == badness && reuseport) { + matches++; + if (((u64)hash * matches) >> 32 == 0) + result = sk; + hash = next_pseudo_random32(hash); } } /* @@ -431,9 +446,7 @@ begin: */ if (get_nulls_value(node) != slot2) goto begin; - if (result) { -exact_match: if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2))) result = NULL; else if (unlikely(compute_score2(result, net, saddr, sport, @@ -457,7 +470,8 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, unsigned short hnum = ntohs(dport); unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; - int score, badness; + int score, badness, matches = 0, reuseport = 0; + u32 hash = 0; rcu_read_lock(); if (hslot->count > 10) { @@ -486,13 +500,24 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, } begin: result = NULL; - badness = -1; + badness = 0; sk_nulls_for_each_rcu(sk, node, &hslot->head) { score = compute_score(sk, net, saddr, hnum, sport, daddr, dport, dif); if (score > badness) { result = sk; badness = score; + reuseport = sk->sk_reuseport; + if (reuseport) { + hash = inet_ehashfn(net, daddr, hnum, + saddr, htons(sport)); + matches = 1; + } + } else if (score == badness && reuseport) { + matches++; + if (((u64)hash * matches) >> 32 == 0) + result = sk; + hash = next_pseudo_random32(hash); } } /* -- cgit v1.2.3 From 5ba24953e9707387cce87b07f0d5fbdd03c5c11b Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 22 Jan 2013 09:50:39 +0000 Subject: soreuseport: TCP/IPv6 implementation Motivation for soreuseport would be something like a web server binding to port 80 running with multiple threads, where each thread might have it's own listener socket. This could be done as an alternative to other models: 1) have one listener thread which dispatches completed connections to workers. 2) accept on a single listener socket from multiple threads. In case #1 the listener thread can easily become the bottleneck with high connection turn-over rate. In case #2, the proportion of connections accepted per thread tends to be uneven under high connection load (assuming simple event loop: while (1) { accept(); process() }, wakeup does not promote fairness among the sockets. We have seen the disproportion to be as high as 3:1 ratio between thread accepting most connections and the one accepting the fewest. With so_reusport the distribution is uniform. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/inet6_hashtables.h | 5 ++++- include/net/netfilter/nf_tproxy_core.h | 1 + net/ipv6/inet6_connection_sock.c | 19 ++++++++++++++----- net/ipv6/inet6_hashtables.c | 19 ++++++++++++++++--- net/ipv6/tcp_ipv6.c | 4 +++- 5 files changed, 38 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 9e34c877a770..7ca75cbbf75e 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -71,6 +71,8 @@ extern struct sock *__inet6_lookup_established(struct net *net, extern struct sock *inet6_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, + const struct in6_addr *saddr, + const __be16 sport, const struct in6_addr *daddr, const unsigned short hnum, const int dif); @@ -88,7 +90,8 @@ static inline struct sock *__inet6_lookup(struct net *net, if (sk) return sk; - return inet6_lookup_listener(net, hashinfo, daddr, hnum, dif); + return inet6_lookup_listener(net, hashinfo, saddr, sport, + daddr, hnum, dif); } static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h index 193796445642..36d9379d4c4b 100644 --- a/include/net/netfilter/nf_tproxy_core.h +++ b/include/net/netfilter/nf_tproxy_core.h @@ -152,6 +152,7 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, break; case NFT_LOOKUP_LISTENER: sk = inet6_lookup_listener(net, &tcp_hashinfo, + saddr, sport, daddr, ntohs(dport), in->ifindex); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 30647857a375..e4297a393678 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -32,6 +32,9 @@ int inet6_csk_bind_conflict(const struct sock *sk, { const struct sock *sk2; const struct hlist_node *node; + int reuse = sk->sk_reuse; + int reuseport = sk->sk_reuseport; + int uid = sock_i_uid((struct sock *)sk); /* We must walk the whole port owner list in this case. -DaveM */ /* @@ -42,11 +45,17 @@ int inet6_csk_bind_conflict(const struct sock *sk, if (sk != sk2 && (!sk->sk_bound_dev_if || !sk2->sk_bound_dev_if || - sk->sk_bound_dev_if == sk2->sk_bound_dev_if) && - (!sk->sk_reuse || !sk2->sk_reuse || - sk2->sk_state == TCP_LISTEN) && - ipv6_rcv_saddr_equal(sk, sk2)) - break; + sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) { + if ((!reuse || !sk2->sk_reuse || + sk2->sk_state == TCP_LISTEN) && + (!reuseport || !sk2->sk_reuseport || + (sk2->sk_state != TCP_TIME_WAIT && + !uid_eq(uid, + sock_i_uid((struct sock *)sk2))))) { + if (ipv6_rcv_saddr_equal(sk, sk2)) + break; + } + } } return node != NULL; diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index dea17fd28e50..32b4a1675d82 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -158,25 +158,38 @@ static inline int compute_score(struct sock *sk, struct net *net, } struct sock *inet6_lookup_listener(struct net *net, - struct inet_hashinfo *hashinfo, const struct in6_addr *daddr, + struct inet_hashinfo *hashinfo, const struct in6_addr *saddr, + const __be16 sport, const struct in6_addr *daddr, const unsigned short hnum, const int dif) { struct sock *sk; const struct hlist_nulls_node *node; struct sock *result; - int score, hiscore; + int score, hiscore, matches = 0, reuseport = 0; + u32 phash = 0; unsigned int hash = inet_lhashfn(net, hnum); struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; rcu_read_lock(); begin: result = NULL; - hiscore = -1; + hiscore = 0; sk_nulls_for_each(sk, node, &ilb->head) { score = compute_score(sk, net, hnum, daddr, dif); if (score > hiscore) { hiscore = score; result = sk; + reuseport = sk->sk_reuseport; + if (reuseport) { + phash = inet6_ehashfn(net, daddr, hnum, + saddr, sport); + matches = 1; + } + } else if (score == hiscore && reuseport) { + matches++; + if (((u64)phash * matches) >> 32 == 0) + result = sk; + phash = next_pseudo_random32(phash); } } /* diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3701c3c6e2eb..06087e58738a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -834,7 +834,8 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) * no RST generated if md5 hash doesn't match. */ sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev), - &tcp_hashinfo, &ipv6h->daddr, + &tcp_hashinfo, &ipv6h->saddr, + th->source, &ipv6h->daddr, ntohs(th->source), inet6_iif(skb)); if (!sk1) return; @@ -1598,6 +1599,7 @@ do_time_wait: struct sock *sk2; sk2 = inet6_lookup_listener(dev_net(skb->dev), &tcp_hashinfo, + &ipv6_hdr(skb)->saddr, th->source, &ipv6_hdr(skb)->daddr, ntohs(th->dest), inet6_iif(skb)); if (sk2 != NULL) { -- cgit v1.2.3 From 72289b96c943757220ccc681fe2e22b46e21aced Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 22 Jan 2013 09:50:44 +0000 Subject: soreuseport: UDP/IPv6 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation for soreuseport would be something like a DNS server.  An alternative would be to recv on the same socket from multiple threads. As in the case of TCP, the load across these threads tends to be disproportionate and we also see a lot of contection on the socket lock. Note that SO_REUSEADDR already allows multiple UDP sockets to bind to the same port, however there is no provision to prevent hijacking and nothing to distribute packets across all the sockets sharing the same bound port.  This patch does not change the semantics of SO_REUSEADDR, but provides usable functionality of it for unicast. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv6/udp.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 1afb635d9b57..cb5bf497c09c 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -203,7 +204,8 @@ static struct sock *udp6_lib_lookup2(struct net *net, { struct sock *sk, *result; struct hlist_nulls_node *node; - int score, badness; + int score, badness, matches = 0, reuseport = 0; + u32 hash = 0; begin: result = NULL; @@ -214,8 +216,18 @@ begin: if (score > badness) { result = sk; badness = score; - if (score == SCORE2_MAX) + reuseport = sk->sk_reuseport; + if (reuseport) { + hash = inet6_ehashfn(net, daddr, hnum, + saddr, sport); + matches = 1; + } else if (score == SCORE2_MAX) goto exact_match; + } else if (score == badness && reuseport) { + matches++; + if (((u64)hash * matches) >> 32 == 0) + result = sk; + hash = next_pseudo_random32(hash); } } /* @@ -249,7 +261,8 @@ struct sock *__udp6_lib_lookup(struct net *net, unsigned short hnum = ntohs(dport); unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask); struct udp_hslot *hslot2, *hslot = &udptable->hash[slot]; - int score, badness; + int score, badness, matches = 0, reuseport = 0; + u32 hash = 0; rcu_read_lock(); if (hslot->count > 10) { @@ -284,6 +297,17 @@ begin: if (score > badness) { result = sk; badness = score; + reuseport = sk->sk_reuseport; + if (reuseport) { + hash = inet6_ehashfn(net, daddr, hnum, + saddr, sport); + matches = 1; + } + } else if (score == badness && reuseport) { + matches++; + if (((u64)hash * matches) >> 32 == 0) + result = sk; + hash = next_pseudo_random32(hash); } } /* -- cgit v1.2.3 From 887da9176e011a044b12ec0deff62df5faadd67c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 20 Jan 2013 17:32:41 +0200 Subject: mac80211: provide the vif in rssi_callback Since drivers can support several BSS / P2P Client interfaces, the rssi callback needs to inform the driver about the interface teh rssi event relates to. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 1 + include/net/mac80211.h | 1 + net/mac80211/driver-ops.h | 5 +++-- net/mac80211/mlme.c | 4 ++-- net/mac80211/trace.h | 9 ++++++--- 5 files changed, 13 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 02fdcea76b21..f16b81d625ad 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1156,6 +1156,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) } static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ece5733d113d..5c98d654fc75 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2633,6 +2633,7 @@ struct ieee80211_ops { int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); void (*rssi_callback)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void (*allow_buffered_frames)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index d51afbd614d3..e75f5b9eb24f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -845,11 +845,12 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, } static inline void drv_rssi_callback(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, const enum ieee80211_rssi_event event) { - trace_drv_rssi_callback(local, event); + trace_drv_rssi_callback(local, sdata, event); if (local->ops->rssi_callback) - local->ops->rssi_callback(&local->hw, event); + local->ops->rssi_callback(&local->hw, &sdata->vif, event); trace_drv_return_void(local); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2d9ef20cd38d..344ef7d47c1a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2604,12 +2604,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (sig > ifmgd->rssi_max_thold && (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, RSSI_EVENT_HIGH); + drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); } else if (sig < ifmgd->rssi_min_thold && (last_sig >= ifmgd->rssi_max_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, RSSI_EVENT_LOW); + drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 2a2c2e20307d..6ca53d64cb28 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1189,23 +1189,26 @@ TRACE_EVENT(drv_set_rekey_data, TRACE_EVENT(drv_rssi_callback, TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, enum ieee80211_rssi_event rssi_event), - TP_ARGS(local, rssi_event), + TP_ARGS(local, sdata, rssi_event), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u32, rssi_event) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->rssi_event = rssi_event; ), TP_printk( - LOCAL_PR_FMT " rssi_event:%d", - LOCAL_PR_ARG, __entry->rssi_event + LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event ) ); -- cgit v1.2.3 From 8147dc7f54f0f2e13c3b65fd9628994ad576610b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Jan 2013 23:47:05 +0100 Subject: mac80211: fix aggregation state with current drivers For drivers that don't actually flush their queues when aggregation stop with the IEEE80211_AMPDU_TX_STOP_FLUSH or IEEE80211_AMPDU_TX_STOP_FLUSH_CONT reasons is done, like iwlwifi or iwlegacy, mac80211 can then transmit on a TID that the driver still considers busy. This happens in the following way: - IEEE80211_AMPDU_TX_STOP_FLUSH requested - driver marks TID as emptying - mac80211 removes tid_tx data, this can copy packets to the TX pending queues and also let new packets through to the driver - driver gets unexpected TX as it wasn't completely converted to the new API In iwlwifi, this lead to the following warning: WARNING: at drivers/net/wireless/iwlwifi/dvm/tx.c:442 iwlagn_tx_skb+0xc47/0xce0 Tx while agg.state = 4 Modules linked in: [...] Pid: 0, comm: kworker/0:0 Tainted: G W 3.1.0 #1 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] warn_slowpath_fmt+0x33/0x40 [] iwlagn_tx_skb+0xc47/0xce0 [iwldvm] [] iwlagn_mac_tx+0x23/0x40 [iwldvm] [] __ieee80211_tx+0xf6/0x3c0 [mac80211] [] ieee80211_tx+0xd0/0x100 [mac80211] [] ieee80211_xmit+0x96/0xe0 [mac80211] [] ieee80211_subif_start_xmit+0x348/0xc80 [mac80211] [] dev_hard_start_xmit+0x337/0x6d0 [] sch_direct_xmit+0xa9/0x210 [] dev_queue_xmit+0x1b0/0x8e0 Fortunately, solving this problem is easy as the station is being destroyed, so such transmit packets can only happen due to races. Instead of trying to close the race just let the race not reach the drivers by making two changes: 1) remove the explicit aggregation session teardown in the managed mode code, the same thing will be done when the station is removed, in __sta_info_destroy. 2) When aggregation stop with AGG_STOP_DESTROY_STA is requested, leave the tid_tx data around as stopped. It will be cleared and freed in cleanup_single_sta later, but until then any racy packets will be put onto the tid_tx pending queue instead of transmitted which is fine since the station is being removed. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 17 ++++++++++------- net/mac80211/mlme.c | 9 --------- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index e90911819ed4..13b7683de5a4 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -296,7 +296,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, &sta->sta, tid, NULL, 0); WARN_ON_ONCE(ret); - goto remove_tid_tx; + return 0; } if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { @@ -354,12 +354,15 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, */ } - if (reason == AGG_STOP_DESTROY_STA) { - remove_tid_tx: - spin_lock_bh(&sta->lock); - ieee80211_remove_tid_tx(sta, tid); - spin_unlock_bh(&sta->lock); - } + /* + * In the case of AGG_STOP_DESTROY_STA, the driver won't + * necessarily call ieee80211_stop_tx_ba_cb(), so this may + * seem like we can leave the tid_tx data pending forever. + * This is true, in a way, but "forever" is only until the + * station struct is actually destroyed. In the meantime, + * leaving it around ensures that we don't transmit packets + * to the driver on this TID which might confuse it. + */ return 0; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 344ef7d47c1a..62e8f890fed6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1487,7 +1487,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - struct sta_info *sta; u32 changed = 0; ASSERT_MGD_MTX(ifmgd); @@ -1519,14 +1518,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); - mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ifmgd->bssid); - if (sta) { - set_sta_flag(sta, WLAN_STA_BLOCK_BA); - ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); - } - mutex_unlock(&local->sta_mtx); - /* * if we want to get out of ps before disassoc (why?) we have * to do it before sending disassoc, as otherwise the null-packet -- cgit v1.2.3 From 296fcba3ba1b8888aa8f5211de1e25a78b47aeee Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 23 Jan 2013 12:18:12 -0800 Subject: mac80211: clean up mesh sta allocation warning This refactoring fixes a "scheduling while atomic" warning when allocating a mesh station entry while holding the RCU read lock. Fix this by creating a new function mesh_sta_info_get(), which correctly handles the locking and returns under RCU. Also move some unnecessarily #ifdefed mesh station init code from sta_info_alloc() to __mesh_sta_info_alloc(). Signed-off-by: Thomas Pedersen [change code flow to make sparse happy] Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 158 ++++++++++++++++++++++++++++------------------ net/mac80211/sta_info.c | 5 -- 2 files changed, 95 insertions(+), 68 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9e0416696a83..ef92d2705851 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) sta->plink_retries = 0; } -/* - * Allocate mesh sta entry and insert into station table - */ -static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr) -{ - struct sta_info *sta; - - if (sdata->local->num_sta >= MESH_MAX_PLINKS) - return NULL; - - sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); - if (!sta) - return NULL; - - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); - sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); - sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); - - set_sta_flag(sta, WLAN_STA_WME); - - return sta; -} - /** * mesh_set_ht_prot_mode - set correct HT protection mode * @@ -309,52 +285,24 @@ free: return err; } -/** - * mesh_peer_init - initialize new mesh peer and return resulting sta_info - * - * @sdata: local meshif - * @addr: peer's address - * @elems: IEs from beacon or mesh peering frame - * - * call under RCU - */ -static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, - u8 *addr, - struct ieee802_11_elems *elems) +static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee802_11_elems *elems, bool insert) { struct ieee80211_local *local = sdata->local; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0; - struct sta_info *sta; - bool insert = false; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); - sta = sta_info_get(sdata, addr); - if (!sta) { - /* Userspace handles peer allocation when security is enabled */ - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { - cfg80211_notify_new_peer_candidate(sdata->dev, addr, - elems->ie_start, - elems->total_len, - GFP_ATOMIC); - return NULL; - } - - sta = mesh_plink_alloc(sdata, addr); - if (!sta) - return NULL; - insert = true; - } - spin_lock_bh(&sta->lock); sta->last_rx = jiffies; - if (sta->plink_state == NL80211_PLINK_ESTAB) { - spin_unlock_bh(&sta->lock); - return sta; - } + + /* rates and capabilities don't change during peering */ + if (sta->plink_state == NL80211_PLINK_ESTAB) + goto out; sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && @@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, if (insert) rate_control_rate_init(sta); +out: spin_unlock_bh(&sta->lock); +} + +static struct sta_info * +__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) +{ + struct sta_info *sta; - if (insert && sta_info_insert(sta)) + if (sdata->local->num_sta >= MESH_MAX_PLINKS) return NULL; + sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); + if (!sta) + return NULL; + + sta->plink_state = NL80211_PLINK_LISTEN; + init_timer(&sta->plink_timer); + + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); + + set_sta_flag(sta, WLAN_STA_WME); + + return sta; +} + +static struct sta_info * +mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, + struct ieee802_11_elems *elems) +{ + struct sta_info *sta = NULL; + + /* Userspace handles peer allocation when security is enabled */ + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) + cfg80211_notify_new_peer_candidate(sdata->dev, addr, + elems->ie_start, + elems->total_len, + GFP_KERNEL); + else + sta = __mesh_sta_info_alloc(sdata, addr); + return sta; } +/* + * mesh_sta_info_get - return mesh sta info entry for @addr. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame. + * + * Return existing or newly allocated sta_info under RCU read lock. + * (re)initialize with given IEs. + */ +static struct sta_info * +mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, + u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) +{ + struct sta_info *sta = NULL; + + rcu_read_lock(); + sta = sta_info_get(sdata, addr); + if (sta) { + mesh_sta_info_init(sdata, sta, elems, false); + } else { + rcu_read_unlock(); + /* can't run atomic */ + sta = mesh_sta_info_alloc(sdata, addr, elems); + if (!sta) { + rcu_read_lock(); + return NULL; + } + + if (sta_info_insert_rcu(sta)) + return NULL; + } + + return sta; +} + +/* + * mesh_neighbour_update - update or initialize new mesh neighbor. + * + * @sdata: local meshif + * @addr: peer's address + * @elems: IEs from beacon or mesh peering frame + * + * Initiates peering if appropriate. + */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, u8 *hw_addr, struct ieee802_11_elems *elems) { struct sta_info *sta; - rcu_read_lock(); - sta = mesh_peer_init(sdata, hw_addr, elems); + sta = mesh_sta_info_get(sdata, hw_addr, elems); if (!sta) goto out; @@ -632,6 +662,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); + /* WARNING: Only for sta pointer, is dropped & re-acquired */ rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); @@ -735,8 +766,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } if (event == OPN_ACPT) { + rcu_read_unlock(); /* allocate sta entry if necessary and update info */ - sta = mesh_peer_init(sdata, mgmt->sa, &elems); + sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); if (!sta) { mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); rcu_read_unlock(); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 9d864ed5f3da..227233c3ff7f 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); -#ifdef CONFIG_MAC80211_MESH - sta->plink_state = NL80211_PLINK_LISTEN; - init_timer(&sta->plink_timer); -#endif - return sta; } -- cgit v1.2.3 From f68d776a04bfcebc426f605c4440ffe38c583a4b Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 23 Jan 2013 12:18:13 -0800 Subject: mac80211: support mesh rate updates An existing mesh station entry may change its rate capabilities, so call rate_control_rate_update() to notify the rate control. Signed-off-by: Thomas Pedersen [fix compilation] Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 3 ++- net/mac80211/mesh_plink.c | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index e75f5b9eb24f..434b3c4f31b5 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -569,7 +569,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, check_sdata_in_driver(sdata); WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && - sdata->vif.type != NL80211_IFTYPE_ADHOC); + (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); trace_drv_sta_rc_update(local, sdata, sta, changed); if (local->ops->sta_rc_update) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index ef92d2705851..4e1d406fd9b1 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -292,7 +292,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; - u32 rates, basic_rates = 0; + u32 rates, basic_rates = 0, changed = 0; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); @@ -304,6 +304,8 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, if (sta->plink_state == NL80211_PLINK_ESTAB) goto out; + if (sta->sta.supp_rates[band] != rates) + changed |= IEEE80211_RC_SUPP_RATES_CHANGED; sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) @@ -322,11 +324,15 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, elems->ht_operation, &chandef); + if (sta->ch_width != chandef.width) + changed |= IEEE80211_RC_BW_CHANGED; sta->ch_width = chandef.width; } if (insert) rate_control_rate_init(sta); + else + rate_control_rate_update(local, sband, sta, changed); out: spin_unlock_bh(&sta->lock); } -- cgit v1.2.3 From 683d41ae6755e6ae297ec09603c229795ab9566e Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Wed, 23 Jan 2013 15:15:57 +0100 Subject: cfg80211: fix channel check in cfg80211_can_use_iftype_chan In commit "cfg80211: check radar interface combinations" a regression was introduced which might lead to NULL dereference if the argument chan = NULL, which might happen in IBSS/wext case (and probably others). Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/wireless/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/util.c b/net/wireless/util.c index 1c2795d52db0..d7873c7ae0ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1212,7 +1212,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_WDS: - radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); + radar_required = !!(chan && + (chan->flags & IEEE80211_CHAN_RADAR)); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: -- cgit v1.2.3 From 782d2673655c05a1c0335fe0ee4cb62c23bcc231 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Jan 2013 21:21:31 +0100 Subject: mac80211: remove redundant check There's no need to have two checks for "associated" in ieee80211_sta_restart(), make the first one locked to not race (unlikely at this point during resume) and remove the second check. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 62e8f890fed6..5913fb924b12 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3156,23 +3156,22 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - if (!ifmgd->associated) + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) { + mutex_unlock(&ifmgd->mtx); return; + } if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; - mutex_lock(&ifmgd->mtx); - if (ifmgd->associated) { - mlme_dbg(sdata, - "driver requested disconnect after resume\n"); - ieee80211_sta_connection_lost(sdata, - ifmgd->associated->bssid, - WLAN_REASON_UNSPECIFIED); - mutex_unlock(&ifmgd->mtx); - return; - } + mlme_dbg(sdata, "driver requested disconnect after resume\n"); + ieee80211_sta_connection_lost(sdata, + ifmgd->associated->bssid, + WLAN_REASON_UNSPECIFIED); mutex_unlock(&ifmgd->mtx); + return; } + mutex_unlock(&ifmgd->mtx); if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) add_timer(&ifmgd->timer); -- cgit v1.2.3 From fe80123d83d1f9abca426914f768048e601f54db Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 Jan 2013 14:14:33 +0000 Subject: mac80211: avoid a build warning gcc cannot prove that the value of sdata->vif.type does not change between the switch() statement and the second comparison to NL80211_IFTYPE_AP, causing a harmless warning. Slightly reordering the code makes the warning go away with no functional change. Without this patch, building ARM at91sam9g45_defconfig with gcc-4.6 results in: net/mac80211/tx.c: In function 'ieee80211_subif_start_xmit': net/mac80211/tx.c:1797:22: warning: 'chanctx_conf' may be used uninitialized in this function [-Wuninitialized] Signed-off-by: Arnd Bergmann Cc: Johannes Berg Cc: "John W. Linville" Cc: "David S. Miller" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0d7381679a5c..b4a84146de0c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1784,16 +1784,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, break; /* fall through */ case NL80211_IFTYPE_AP: + if (sdata->vif.type == NL80211_IFTYPE_AP) + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); hdrlen = 24; - if (sdata->vif.type == NL80211_IFTYPE_AP) - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; band = chanctx_conf->def.chan->band; break; case NL80211_IFTYPE_WDS: -- cgit v1.2.3 From 77765eaf5cfb6b8dd98ec8b54b411d74ff6095f1 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 18 Jan 2013 11:18:45 +0530 Subject: cfg80211/nl80211: add API for MAC address ACLs Add API to enable drivers to implement MAC address based access control in AP/P2P GO mode. Capable drivers advertise this capability by setting the maximum number of MAC addresses in such a list in wiphy->max_acl_mac_addrs. An initial ACL may be given to the NL80211_CMD_START_AP command and/or changed later with NL80211_CMD_SET_MAC_ACL. Black- and whitelists are supported, but not simultaneously. Signed-off-by: Vasanthakumar Thiagarajan [rewrite commit log, many cleanups] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 34 +++++++++++++ include/uapi/linux/nl80211.h | 51 ++++++++++++++++++- net/wireless/core.c | 5 ++ net/wireless/nl80211.c | 116 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 12 +++++ net/wireless/trace.h | 18 +++++++ 6 files changed, 234 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 183033789e69..36e076e374d2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -531,6 +531,22 @@ struct mac_address { u8 addr[ETH_ALEN]; }; +/** + * struct cfg80211_acl_data - Access control list data + * + * @acl_policy: ACL policy to be applied on the station's + entry specified by mac_addr + * @n_acl_entries: Number of MAC address entries passed + * @mac_addrs: List of MAC addresses of stations to be used for ACL + */ +struct cfg80211_acl_data { + enum nl80211_acl_policy acl_policy; + int n_acl_entries; + + /* Keep it last */ + struct mac_address mac_addrs[]; +}; + /** * struct cfg80211_ap_settings - AP configuration * @@ -550,6 +566,8 @@ struct mac_address { * @inactivity_timeout: time in seconds to determine station's inactivity. * @p2p_ctwindow: P2P CT Window * @p2p_opp_ps: P2P opportunistic PS + * @acl: ACL configuration used by the drivers which has support for + * MAC address based access control */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -566,6 +584,7 @@ struct cfg80211_ap_settings { int inactivity_timeout; u8 p2p_ctwindow; bool p2p_opp_ps; + const struct cfg80211_acl_data *acl; }; /** @@ -1800,6 +1819,13 @@ struct cfg80211_gtk_rekey_data { * * @start_p2p_device: Start the given P2P device. * @stop_p2p_device: Stop the given P2P device. + * + * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode. + * Parameters include ACL policy, an array of MAC address of stations + * and the number of MAC addresses. If there is already a list in driver + * this new list replaces the existing one. Driver has to clear its ACL + * when number of MAC addresses entries is passed as 0. Drivers which + * advertise the support for MAC based ACL have to implement this callback. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2020,6 +2046,9 @@ struct cfg80211_ops { struct wireless_dev *wdev); void (*stop_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); + + int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params); }; /* @@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support { * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. * If null, then none can be over-ridden. + * + * @max_acl_mac_addrs: Maximum number of MAC addresses that the device + * supports for ACL. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2346,6 +2378,8 @@ struct wiphy { /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ u16 interface_modes; + u16 max_acl_mac_addrs; + u32 flags, features; u32 ap_sme_capa; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index e6eeb4ba5dc5..5b7dbc1ea966 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -170,7 +170,8 @@ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. * The channel to use can be set on the interface or be given using the * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP @@ -586,6 +587,16 @@ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames * for IBSS or MESH vif. * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -736,6 +747,8 @@ enum nl80211_commands { NL80211_CMD_SET_MCAST_RATE, + NL80211_CMD_SET_MAC_ACL, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1313,6 +1326,16 @@ enum nl80211_commands { * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode * defined in &enum nl80211_mesh_power_mode. * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1585,6 +1608,12 @@ enum nl80211_attrs { NL80211_ATTR_LOCAL_MESH_POWER_MODE, + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr { * enum nl80211_connect_failed_reason - connection request failed reasons * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be * handled by the AP is reached. - * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. */ enum nl80211_connect_failed_reason { NL80211_CONN_FAIL_MAX_CLIENTS, @@ -3276,4 +3305,22 @@ enum nl80211_scan_flags { NL80211_SCAN_FLAG_AP = 1<<2, }; +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 0e702cdc6043..ce827242f390 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy) ETH_ALEN))) return -EINVAL; + if (WARN_ON(wiphy->max_acl_mac_addrs && + (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) || + !rdev->ops->set_mac_acl))) + return -EINVAL; + if (wiphy->addresses) memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 33de80364c5c..b5978ab4ad7a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 }, [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, + [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, + [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag dev->wiphy.ht_capa_mod_mask)) goto nla_put_failure; + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && + dev->wiphy.max_acl_mac_addrs && + nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, + dev->wiphy.max_acl_mac_addrs)) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: @@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) return err; } +/* This function returns an error or the number of nested attributes */ +static int validate_acl_mac_addrs(struct nlattr *nl_attr) +{ + struct nlattr *attr; + int n_entries = 0, tmp; + + nla_for_each_nested(attr, nl_attr, tmp) { + if (nla_len(attr) != ETH_ALEN) + return -EINVAL; + + n_entries++; + } + + return n_entries; +} + +/* + * This function parses ACL information and allocates memory for ACL data. + * On successful return, the calling function is responsible to free the + * ACL buffer returned by this function. + */ +static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy, + struct genl_info *info) +{ + enum nl80211_acl_policy acl_policy; + struct nlattr *attr; + struct cfg80211_acl_data *acl; + int i = 0, n_entries, tmp; + + if (!wiphy->max_acl_mac_addrs) + return ERR_PTR(-EOPNOTSUPP); + + if (!info->attrs[NL80211_ATTR_ACL_POLICY]) + return ERR_PTR(-EINVAL); + + acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]); + if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED && + acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED) + return ERR_PTR(-EINVAL); + + if (!info->attrs[NL80211_ATTR_MAC_ADDRS]) + return ERR_PTR(-EINVAL); + + n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]); + if (n_entries < 0) + return ERR_PTR(n_entries); + + if (n_entries > wiphy->max_acl_mac_addrs) + return ERR_PTR(-ENOTSUPP); + + acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries), + GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { + memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN); + i++; + } + + acl->n_acl_entries = n_entries; + acl->acl_policy = acl_policy; + + return acl; +} + +static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_acl_data *acl; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!dev->ieee80211_ptr->beacon_interval) + return -EINVAL; + + acl = parse_acl_data(&rdev->wiphy, info); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + err = rdev_set_mac_acl(rdev, dev, acl); + + kfree(acl); + + return err; +} + static int nl80211_parse_beacon(struct genl_info *info, struct cfg80211_beacon_data *bcn) { @@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) return err; + if (info->attrs[NL80211_ATTR_ACL_POLICY]) { + params.acl = parse_acl_data(&rdev->wiphy, info); + if (IS_ERR(params.acl)) + return PTR_ERR(params.acl); + } + err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { wdev->preset_chandef = params.chandef; @@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->ssid_len = params.ssid_len; memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } + + kfree(params.acl); + return err; } @@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_MAC_ACL, + .doit = nl80211_set_mac_acl, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 6c0c8191f837..422d38291d66 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev, rdev->ops->stop_p2p_device(&rdev->wiphy, wdev); trace_rdev_return_void(&rdev->wiphy); } + +static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_acl_data *params) +{ + int ret; + + trace_rdev_set_mac_acl(&rdev->wiphy, dev, params); + ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 2134576f426e..8bc553199686 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device, TP_ARGS(wiphy, wdev) ); +TRACE_EVENT(rdev_set_mac_acl, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_acl_data *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u32, acl_policy) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WIPHY_ASSIGN; + __entry->acl_policy = params->acl_policy; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ -- cgit v1.2.3 From 77136836aef2c7ce9745e765c1e51a14e3933931 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 20 Jul 2012 21:00:00 +0200 Subject: can: Kconfig: convert 'depends on CAN' into 'if CAN...endif' block This patch adds an 'if CAN...endif' Block around all CAN symbols in net/can/Kconfig. So the 'depends on CAN' dependencies can be removed. Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 5 ----- net/can/Kconfig | 7 ++++--- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index b56bd9e80957..8088d8fa5611 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -1,9 +1,7 @@ menu "CAN Device Drivers" - depends on CAN config CAN_VCAN tristate "Virtual Local CAN Interface (vcan)" - depends on CAN ---help--- Similar to the network loopback devices, vcan offers a virtual local CAN interface. @@ -13,7 +11,6 @@ config CAN_VCAN config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" - depends on CAN ---help--- CAN driver for several 'low cost' CAN interfaces that are attached via serial lines or via USB-to-serial adapters using the LAWICEL @@ -33,7 +30,6 @@ config CAN_SLCAN config CAN_DEV tristate "Platform CAN drivers with Netlink support" - depends on CAN default y ---help--- Enables the common framework for platform CAN drivers with Netlink @@ -133,7 +129,6 @@ source "drivers/net/can/softing/Kconfig" config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" - depends on CAN ---help--- Say Y here if you want the CAN device drivers to produce a bunch of debug messages to the system log. Select this if you are having diff --git a/net/can/Kconfig b/net/can/Kconfig index 03200699d274..0435fc42ab04 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -16,9 +16,10 @@ menuconfig CAN If you want CAN support you should say Y here and also to the specific driver for your controller(s) below. +if CAN + config CAN_RAW tristate "Raw CAN Protocol (raw access with CAN-ID filtering)" - depends on CAN default N ---help--- The raw CAN protocol option offers access to the CAN bus via @@ -29,7 +30,6 @@ config CAN_RAW config CAN_BCM tristate "Broadcast Manager CAN Protocol (with content filtering)" - depends on CAN default N ---help--- The Broadcast Manager offers content filtering, timeout monitoring, @@ -42,7 +42,6 @@ config CAN_BCM config CAN_GW tristate "CAN Gateway/Router (with netlink configuration)" - depends on CAN default N ---help--- The CAN Gateway/Router is used to route (and modify) CAN frames. @@ -53,3 +52,5 @@ config CAN_GW by the netlink configuration interface known e.g. from iptables. source "drivers/net/can/Kconfig" + +endif -- cgit v1.2.3 From f66b0301fcab28f90c5c9249590a52239a005c5a Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 20 Jul 2012 21:22:27 +0200 Subject: can: Kconfig: switch on all CAN protocolls by default This patch enables all basic CAN protocol by default. Signed-off-by: Marc Kleine-Budde --- net/can/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/can/Kconfig b/net/can/Kconfig index 0435fc42ab04..a15c0e0d1fc7 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -20,7 +20,7 @@ if CAN config CAN_RAW tristate "Raw CAN Protocol (raw access with CAN-ID filtering)" - default N + default y ---help--- The raw CAN protocol option offers access to the CAN bus via the BSD socket API. You probably want to use the raw socket in @@ -30,7 +30,7 @@ config CAN_RAW config CAN_BCM tristate "Broadcast Manager CAN Protocol (with content filtering)" - default N + default y ---help--- The Broadcast Manager offers content filtering, timeout monitoring, sending of RTR frames, and cyclic CAN messages without permanent user @@ -42,7 +42,7 @@ config CAN_BCM config CAN_GW tristate "CAN Gateway/Router (with netlink configuration)" - default N + default y ---help--- The CAN Gateway/Router is used to route (and modify) CAN frames. It is based on the PF_CAN core infrastructure for msg filtering and -- cgit v1.2.3 From 156c2bb9f88065c8da78814f98fde665a5cbb527 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 17 Jan 2013 18:43:39 +0100 Subject: can: add private data space for CAN sk_buffs The struct can_skb_priv is used to transport additional information along with the stored struct can(fd)_frame that can not be contained in existing struct sk_buff elements. can_skb_priv is located in the skb headroom, which does not touch the existing CAN sk_buff usage with skb->data and skb->len, so that even out-of-tree CAN drivers can be used without changes. Btw. out-of-tree CAN drivers without can_skb_priv in the sk_buff headroom would not support features based on can_skb_priv. The can_skb_priv->ifindex contains the first interface where the CAN frame appeared on the local host. Unfortunately skb->skb_iif can not be used as this value is overwritten in every netif_receive_skb() call. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev.c | 8 +++++++- drivers/net/can/slcan.c | 8 +++++++- include/linux/can/skb.h | 35 +++++++++++++++++++++++++++++++++++ net/can/bcm.c | 12 +++++++++--- net/can/raw.c | 8 ++++++-- 5 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 include/linux/can/skb.h (limited to 'net') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 6abc6e59778e..59ada082a994 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -502,13 +503,18 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) { struct sk_buff *skb; - skb = netdev_alloc_skb(dev, sizeof(struct can_frame)); + skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) + + sizeof(struct can_frame)); if (unlikely(!skb)) return NULL; skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reserve(skb, sizeof(struct can_skb_priv)); + ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); memset(*cf, 0, sizeof(struct can_frame)); diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index adc3708d8829..e79a8d10e0fc 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -55,6 +55,7 @@ #include #include #include +#include static __initconst const char banner[] = KERN_INFO "slcan: serial line CAN interface driver\n"; @@ -184,7 +185,8 @@ static void slc_bump(struct slcan *sl) cf.data[i] |= tmp; } - skb = dev_alloc_skb(sizeof(struct can_frame)); + skb = dev_alloc_skb(sizeof(struct can_frame) + + sizeof(struct can_skb_priv)); if (!skb) return; @@ -192,6 +194,10 @@ static void slc_bump(struct slcan *sl) skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reserve(skb, sizeof(struct can_skb_priv)); + ((struct can_skb_priv *)(skb->head))->ifindex = sl->dev->ifindex; + memcpy(skb_put(skb, sizeof(struct can_frame)), &cf, sizeof(struct can_frame)); netif_rx_ni(skb); diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h new file mode 100644 index 000000000000..4b0f24d3a878 --- /dev/null +++ b/include/linux/can/skb.h @@ -0,0 +1,35 @@ +/* + * linux/can/skb.h + * + * Definitions for the CAN network socket buffer + * + * Copyright (C) 2012 Oliver Hartkopp + * + */ + +#ifndef CAN_SKB_H +#define CAN_SKB_H + +#include +#include + +/* + * The struct can_skb_priv is used to transport additional information along + * with the stored struct can(fd)_frame that can not be contained in existing + * struct sk_buff elements. + * N.B. that this information must not be modified in cloned CAN sk_buffs. + * To modify the CAN frame content or the struct can_skb_priv content + * skb_copy() needs to be used instead of skb_clone(). + */ + +/** + * struct can_skb_priv - private additional data inside CAN sk_buffs + * @ifindex: ifindex of the first interface the CAN frame appeared on + * @cf: align to the following CAN frame at skb->data + */ +struct can_skb_priv { + int ifindex; + struct can_frame cf[0]; +}; + +#endif /* CAN_SKB_H */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 969b7cdff59d..ccc27b9e8384 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -256,10 +257,13 @@ static void bcm_can_tx(struct bcm_op *op) return; } - skb = alloc_skb(CFSIZ, gfp_any()); + skb = alloc_skb(CFSIZ + sizeof(struct can_skb_priv), gfp_any()); if (!skb) goto out; + skb_reserve(skb, sizeof(struct can_skb_priv)); + ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + memcpy(skb_put(skb, CFSIZ), cf, CFSIZ); /* send with loopback */ @@ -1199,11 +1203,12 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) if (!ifindex) return -ENODEV; - skb = alloc_skb(CFSIZ, GFP_KERNEL); - + skb = alloc_skb(CFSIZ + sizeof(struct can_skb_priv), GFP_KERNEL); if (!skb) return -ENOMEM; + skb_reserve(skb, sizeof(struct can_skb_priv)); + err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ); if (err < 0) { kfree_skb(skb); @@ -1216,6 +1221,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) return -ENODEV; } + ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; skb->dev = dev; skb->sk = sk; err = can_send(skb, 1); /* send with loopback */ diff --git a/net/can/raw.c b/net/can/raw.c index 5b0e3e330d97..5d860e8dcc52 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -699,11 +700,14 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, if (!dev) return -ENXIO; - skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, - &err); + skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv), + msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) goto put_dev; + skb_reserve(skb, sizeof(struct can_skb_priv)); + ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err < 0) goto free_skb; -- cgit v1.2.3 From d904d3edcbb26efc86ea3575bb4265559801a94b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 17 Jan 2013 18:43:41 +0100 Subject: can: gw: make routing to the incoming CAN interface configurable Introduce new configuration flag CGW_FLAGS_CAN_IIF_TX_OK to configure if a CAN sk_buff that has been routed with can-gw is allowed to be send back to the originating CAN interface. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/uapi/linux/can/gw.h | 1 + net/can/gw.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index 8e1db18c3cb6..0505c7f86213 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h @@ -51,6 +51,7 @@ enum { #define CGW_FLAGS_CAN_ECHO 0x01 #define CGW_FLAGS_CAN_SRC_TSTAMP 0x02 +#define CGW_FLAGS_CAN_IIF_TX_OK 0x04 #define CGW_MOD_FUNCS 4 /* AND OR XOR SET */ diff --git a/net/can/gw.c b/net/can/gw.c index 574dda78eb0f..37a3efb7cc9d 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -347,6 +348,13 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) return; } + /* is sending the skb back to the incoming interface not allowed? */ + if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) && + skb_headroom(skb) == sizeof(struct can_skb_priv) && + (((struct can_skb_priv *)(skb->head))->ifindex == + gwj->dst.dev->ifindex)) + return; + /* * clone the given skb, which has not been done in can_rcv() * -- cgit v1.2.3 From be286bafe1f4069094865264f29805854c5788bf Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 17 Jan 2013 18:43:44 +0100 Subject: can: gw: add a variable limit for CAN frame routings To prevent a possible misconfiguration (e.g. circular CAN frame routings) limit the number of routings of a single CAN frame to a small variable value. The limit can be specified by the module parameter 'max_hops' (1..6). The default value is 1 (one hop), according to the original can-gw behaviour. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/gw.c | 56 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/can/gw.c b/net/can/gw.c index 37a3efb7cc9d..4216a80618cb 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -58,14 +59,25 @@ #include #include -#define CAN_GW_VERSION "20101209" -static __initconst const char banner[] = - KERN_INFO "can: netlink gateway (rev " CAN_GW_VERSION ")\n"; +#define CAN_GW_VERSION "20130117" +#define CAN_GW_NAME "can-gw" MODULE_DESCRIPTION("PF_CAN netlink gateway"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Oliver Hartkopp "); -MODULE_ALIAS("can-gw"); +MODULE_ALIAS(CAN_GW_NAME); + +#define CGW_MIN_HOPS 1 +#define CGW_MAX_HOPS 6 +#define CGW_DEFAULT_HOPS 1 + +static unsigned int max_hops __read_mostly = CGW_DEFAULT_HOPS; +module_param(max_hops, uint, S_IRUGO); +MODULE_PARM_DESC(max_hops, + "maximum " CAN_GW_NAME " routing hops for CAN frames " + "(valid values: " __stringify(CGW_MIN_HOPS) "-" + __stringify(CGW_MAX_HOPS) " hops, " + "default: " __stringify(CGW_DEFAULT_HOPS) ")"); static HLIST_HEAD(cgw_list); static struct notifier_block notifier; @@ -339,8 +351,23 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) struct sk_buff *nskb; int modidx = 0; - /* do not handle already routed frames - see comment below */ - if (skb_mac_header_was_set(skb)) + /* + * Do not handle CAN frames routed more than 'max_hops' times. + * In general we should never catch this delimiter which is intended + * to cover a misconfiguration protection (e.g. circular CAN routes). + * + * The Controller Area Network controllers only accept CAN frames with + * correct CRCs - which are not visible in the controller registers. + * According to skbuff.h documentation the csum_start element for IP + * checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY. + * Only CAN skbs can be processed here which already have this property. + */ + +#define cgw_hops(skb) ((skb)->csum_start) + + BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY); + + if (cgw_hops(skb) >= max_hops) return; if (!(gwj->dst.dev->flags & IFF_UP)) { @@ -371,15 +398,8 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) return; } - /* - * Mark routed frames by setting some mac header length which is - * not relevant for the CAN frames located in the skb->data section. - * - * As dev->header_ops is not set in CAN netdevices no one is ever - * accessing the various header offsets in the CAN skbuffs anyway. - * E.g. using the packet socket to read CAN frames is still working. - */ - skb_set_mac_header(nskb, 8); + /* put the incremented hop counter in the cloned skb */ + cgw_hops(nskb) = cgw_hops(skb) + 1; nskb->dev = gwj->dst.dev; /* pointer to modifiable CAN frame */ @@ -903,7 +923,11 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) static __init int cgw_module_init(void) { - printk(banner); + /* sanitize given module parameter */ + max_hops = clamp_t(unsigned int, max_hops, CGW_MIN_HOPS, CGW_MAX_HOPS); + + pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", + max_hops); cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), 0, 0, NULL); -- cgit v1.2.3 From e6afa00a1409bc3bceed9ccb33111519463dfe7b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Thu, 17 Jan 2013 18:43:46 +0100 Subject: can: gw: indicate and count deleted frames due to misconfiguration Add a statistic counter to detect deleted frames due to misconfiguration with a new read-only CGW_DELETED netlink attribute for the CAN gateway. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/uapi/linux/can/gw.h | 1 + net/can/gw.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index 0505c7f86213..ae07bec74f4b 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h @@ -44,6 +44,7 @@ enum { CGW_SRC_IF, /* ifindex of source network interface */ CGW_DST_IF, /* ifindex of destination network interface */ CGW_FILTER, /* specify struct can_filter on source CAN device */ + CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */ __CGW_MAX }; diff --git a/net/can/gw.c b/net/can/gw.c index 4216a80618cb..acdd4656cc3b 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -131,6 +131,7 @@ struct cgw_job { struct rcu_head rcu; u32 handled_frames; u32 dropped_frames; + u32 deleted_frames; struct cf_mod mod; union { /* CAN frame data source */ @@ -367,8 +368,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) BUG_ON(skb->ip_summed != CHECKSUM_UNNECESSARY); - if (cgw_hops(skb) >= max_hops) + if (cgw_hops(skb) >= max_hops) { + /* indicate deleted frames due to misconfiguration */ + gwj->deleted_frames++; return; + } if (!(gwj->dst.dev->flags & IFF_UP)) { gwj->dropped_frames++; @@ -500,6 +504,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type, goto cancel; } + if (gwj->deleted_frames) { + if (nla_put_u32(skb, CGW_DELETED, gwj->deleted_frames) < 0) + goto cancel; + } + /* check non default settings of attributes */ if (gwj->mod.modtype.and) { @@ -799,6 +808,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, gwj->handled_frames = 0; gwj->dropped_frames = 0; + gwj->deleted_frames = 0; gwj->flags = r->flags; gwj->gwtype = r->gwtype; -- cgit v1.2.3 From 9c5e0c0bbc5f683ada546af3c39a5a90b156a6f0 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sat, 26 Jan 2013 07:50:54 +0000 Subject: soreuseport: fix use of uid in tb->fastuid Fix a reported compilation error where ia variable of type kuid_t was being set to zero. Eliminate two instances of setting tb->fastuid to zero. tb->fastuid is only used if tb->fastreuseport is set, so there should be no problem if tb->fastuid is not initialized (when tb->fastreuesport is zero). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/ipv4/inet_connection_sock.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 8bb623d357ad..11cb4979a465 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -204,7 +204,8 @@ tb_found: ret = 1; if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true)) { if (((sk->sk_reuse && sk->sk_state != TCP_LISTEN) || - (sk->sk_reuseport && uid_eq(tb->fastuid, uid))) && + (tb->fastreuseport > 0 && + sk->sk_reuseport && uid_eq(tb->fastuid, uid))) && smallest_size != -1 && --attempts >= 0) { spin_unlock(&head->lock); goto again; @@ -227,19 +228,15 @@ tb_not_found: if (sk->sk_reuseport) { tb->fastreuseport = 1; tb->fastuid = uid; - } else { + } else tb->fastreuseport = 0; - tb->fastuid = 0; - } } else { if (tb->fastreuse && (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) tb->fastreuse = 0; if (tb->fastreuseport && - (!sk->sk_reuseport || !uid_eq(tb->fastuid, uid))) { + (!sk->sk_reuseport || !uid_eq(tb->fastuid, uid))) tb->fastreuseport = 0; - tb->fastuid = 0; - } } success: if (!inet_csk(sk)->icsk_bind_hash) -- cgit v1.2.3 From 4bf613c6d87399e3c8a4d5e14281456b6959d8fd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Jan 2013 20:40:56 +0000 Subject: irda: buffer overflow in irnet_ctrl_read() The comments here say that the /* Max event is 61 char */ but in 2003 we changed the event format and now the max event size is 75. The longest event is: "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", 12345678901 23 456789012 34567890 1 2 3 +8 +21 +8 +2 +2 +1 = 75 characters. There was a check to return -EOVERFLOW if the user gave us a "count" value that was less than 64. Raising it to 75 might break backwards compatability. Instead I removed the check and now it returns a truncated string if "count" is too low. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- net/irda/irnet/irnet_ppp.c | 117 ++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 59 deletions(-) (limited to 'net') diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c index 2bb2beb6a373..3c83a1e5ab03 100644 --- a/net/irda/irnet/irnet_ppp.c +++ b/net/irda/irnet/irnet_ppp.c @@ -214,8 +214,7 @@ irnet_get_discovery_log(irnet_socket * ap) * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1 */ static inline int -irnet_read_discovery_log(irnet_socket * ap, - char * event) +irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size) { int done_event = 0; @@ -237,12 +236,13 @@ irnet_read_discovery_log(irnet_socket * ap, if(ap->disco_index < ap->disco_number) { /* Write an event */ - sprintf(event, "Found %08x (%s) behind %08x {hints %02X-%02X}\n", - ap->discoveries[ap->disco_index].daddr, - ap->discoveries[ap->disco_index].info, - ap->discoveries[ap->disco_index].saddr, - ap->discoveries[ap->disco_index].hints[0], - ap->discoveries[ap->disco_index].hints[1]); + snprintf(event, buf_size, + "Found %08x (%s) behind %08x {hints %02X-%02X}\n", + ap->discoveries[ap->disco_index].daddr, + ap->discoveries[ap->disco_index].info, + ap->discoveries[ap->disco_index].saddr, + ap->discoveries[ap->disco_index].hints[0], + ap->discoveries[ap->disco_index].hints[1]); DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", ap->disco_index, ap->discoveries[ap->disco_index].info); @@ -282,27 +282,24 @@ irnet_ctrl_read(irnet_socket * ap, size_t count) { DECLARE_WAITQUEUE(wait, current); - char event[64]; /* Max event is 61 char */ + char event[75]; ssize_t ret = 0; DENTER(CTRL_TRACE, "(ap=0x%p, count=%Zd)\n", ap, count); - /* Check if we can write an event out in one go */ - DABORT(count < sizeof(event), -EOVERFLOW, CTRL_ERROR, "Buffer to small.\n"); - #ifdef INITIAL_DISCOVERY /* Check if we have read the log */ - if(irnet_read_discovery_log(ap, event)) + if (irnet_read_discovery_log(ap, event, sizeof(event))) { - /* We have an event !!! Copy it to the user */ - if(copy_to_user(buf, event, strlen(event))) + count = min(strlen(event), count); + if (copy_to_user(buf, event, count)) { DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); return -EFAULT; } DEXIT(CTRL_TRACE, "\n"); - return strlen(event); + return count; } #endif /* INITIAL_DISCOVERY */ @@ -339,79 +336,81 @@ irnet_ctrl_read(irnet_socket * ap, switch(irnet_events.log[ap->event_index].event) { case IRNET_DISCOVER: - sprintf(event, "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); + snprintf(event, sizeof(event), + "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr, + irnet_events.log[ap->event_index].hints.byte[0], + irnet_events.log[ap->event_index].hints.byte[1]); break; case IRNET_EXPIRE: - sprintf(event, "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); + snprintf(event, sizeof(event), + "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr, + irnet_events.log[ap->event_index].hints.byte[0], + irnet_events.log[ap->event_index].hints.byte[1]); break; case IRNET_CONNECT_TO: - sprintf(event, "Connected to %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); + snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); break; case IRNET_CONNECT_FROM: - sprintf(event, "Connection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); + snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); break; case IRNET_REQUEST_FROM: - sprintf(event, "Request from %08x (%s) behind %08x\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr); + snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr); break; case IRNET_NOANSWER_FROM: - sprintf(event, "No-answer from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); + snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); break; case IRNET_BLOCKED_LINK: - sprintf(event, "Blocked link with %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); + snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); break; case IRNET_DISCONNECT_FROM: - sprintf(event, "Disconnection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); + snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); break; case IRNET_DISCONNECT_TO: - sprintf(event, "Disconnected to %08x (%s)\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name); + snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name); break; default: - sprintf(event, "Bug\n"); + snprintf(event, sizeof(event), "Bug\n"); } /* Increment our event index */ ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; DEBUG(CTRL_INFO, "Event is :%s", event); - /* Copy it to the user */ - if(copy_to_user(buf, event, strlen(event))) + count = min(strlen(event), count); + if (copy_to_user(buf, event, count)) { DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); return -EFAULT; } DEXIT(CTRL_TRACE, "\n"); - return strlen(event); + return count; } /*------------------------------------------------------------------*/ -- cgit v1.2.3 From cec771d646d6c9a6f123b50e132ab142342dcb97 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 22 Jan 2013 21:09:50 +0000 Subject: decnet: use correct RCU API to deref sk_dst_cache field sock->sk_dst_cache is protected by RCU, therefore we should use __sk_dst_get() to deref it once we lock the sock. This fixes several sparse warnings. Cc: linux-decnet-user@lists.sourceforge.net Cc: Eric Dumazet Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/dn_route.h | 2 +- net/decnet/af_decnet.c | 6 ++++-- net/decnet/dn_nsp_out.c | 2 +- net/decnet/dn_route.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/dn_route.h b/include/net/dn_route.h index 4f7d6a182381..2e9d317c82dc 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -16,7 +16,7 @@ *******************************************************************************/ extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); -extern int dn_route_output_sock(struct dst_entry **pprt, struct flowidn *, struct sock *sk, int flags); +extern int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *, struct sock *sk, int flags); extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); extern void dn_rt_cache_flush(int delay); diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 307c322d53bb..64d9843f9e04 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -909,6 +909,7 @@ static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen, struct dn_scp *scp = DN_SK(sk); int err = -EISCONN; struct flowidn fld; + struct dst_entry *dst; if (sock->state == SS_CONNECTED) goto out; @@ -955,10 +956,11 @@ static int __dn_connect(struct sock *sk, struct sockaddr_dn *addr, int addrlen, fld.flowidn_proto = DNPROTO_NSP; if (dn_route_output_sock(&sk->sk_dst_cache, &fld, sk, flags) < 0) goto out; - sk->sk_route_caps = sk->sk_dst_cache->dev->features; + dst = __sk_dst_get(sk); + sk->sk_route_caps = dst->dev->features; sock->state = SS_CONNECTING; scp->state = DN_CI; - scp->segsize_loc = dst_metric_advmss(sk->sk_dst_cache); + scp->segsize_loc = dst_metric_advmss(dst); dn_nsp_send_conninit(sk, NSP_CI); err = -EINPROGRESS; diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 8a96047c7c94..1aaa51ebbda6 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -598,7 +598,7 @@ void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg, if (reason == 0) reason = le16_to_cpu(scp->discdata_out.opt_status); - dn_nsp_do_disc(sk, msgflg, reason, gfp, sk->sk_dst_cache, ddl, + dn_nsp_do_disc(sk, msgflg, reason, gfp, __sk_dst_get(sk), ddl, scp->discdata_out.opt_data, scp->addrrem, scp->addrloc); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index b57419cc41a4..1550028fcd8e 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1282,7 +1282,7 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowidn *flp, int return err; } -int dn_route_output_sock(struct dst_entry **pprt, struct flowidn *fl, struct sock *sk, int flags) +int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *fl, struct sock *sk, int flags) { int err; -- cgit v1.2.3 From cef401de7be8c4e155c6746bfccf721a4fa5fab9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 25 Jan 2013 20:34:37 +0000 Subject: net: fix possible wrong checksum generation Pravin Shelar mentioned that GSO could potentially generate wrong TX checksum if skb has fragments that are overwritten by the user between the checksum computation and transmit. He suggested to linearize skbs but this extra copy can be avoided for normal tcp skbs cooked by tcp_sendmsg(). This patch introduces a new SKB_GSO_SHARED_FRAG flag, set in skb_shinfo(skb)->gso_type if at least one frag can be modified by the user. Typical sources of such possible overwrites are {vm}splice(), sendfile(), and macvtap/tun/virtio_net drivers. Tested: $ netperf -H 7.7.8.84 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3959.52 $ netperf -H 7.7.8.84 -t TCP_SENDFILE TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3216.80 Performance of the SENDFILE is impacted by the extra allocation and copy, and because we use order-0 pages, while the TCP_STREAM uses bigger pages. Reported-by: Pravin Shelar Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 3 ++- drivers/net/tun.c | 12 ++++++++---- drivers/net/virtio_net.c | 12 ++++++++---- include/linux/skbuff.h | 19 +++++++++++++++++++ net/core/dev.c | 9 +++++++++ net/core/skbuff.c | 4 ++++ net/ipv4/af_inet.c | 1 + net/ipv4/ip_gre.c | 4 +++- net/ipv4/ipip.c | 4 +++- net/ipv4/tcp.c | 3 +++ net/ipv4/tcp_input.c | 4 ++-- net/ipv4/tcp_output.c | 4 ++-- net/ipv6/ip6_offload.c | 1 + 13 files changed, 65 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 0f0f9ce3a776..b181dfb3d6d6 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -543,6 +543,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, skb->data_len += len; skb->len += len; skb->truesize += truesize; + skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; atomic_add(truesize, &skb->sk->sk_wmem_alloc); while (len) { int off = base & ~PAGE_MASK; @@ -598,7 +599,7 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { skb_shinfo(skb)->gso_size = vnet_hdr->gso_size; - skb_shinfo(skb)->gso_type = gso_type; + skb_shinfo(skb)->gso_type |= gso_type; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index c81680dc10eb..293ce8dfc9e6 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1005,6 +1005,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, skb->data_len += len; skb->len += len; skb->truesize += truesize; + skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; atomic_add(truesize, &skb->sk->sk_wmem_alloc); while (len) { int off = base & ~PAGE_MASK; @@ -1150,16 +1151,18 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { + unsigned short gso_type = 0; + pr_debug("GSO!\n"); switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + gso_type = SKB_GSO_TCPV4; break; case VIRTIO_NET_HDR_GSO_TCPV6: - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + gso_type = SKB_GSO_TCPV6; break; case VIRTIO_NET_HDR_GSO_UDP: - skb_shinfo(skb)->gso_type = SKB_GSO_UDP; + gso_type = SKB_GSO_UDP; break; default: tun->dev->stats.rx_frame_errors++; @@ -1168,9 +1171,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + gso_type |= SKB_GSO_TCP_ECN; skb_shinfo(skb)->gso_size = gso.gso_size; + skb_shinfo(skb)->gso_type |= gso_type; if (skb_shinfo(skb)->gso_size == 0) { tun->dev->stats.rx_frame_errors++; kfree_skb(skb); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 701408a1ded6..58914c8ea68f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -220,6 +220,7 @@ static void set_skb_frag(struct sk_buff *skb, struct page *page, skb->len += size; skb->truesize += PAGE_SIZE; skb_shinfo(skb)->nr_frags++; + skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; *len -= size; } @@ -379,16 +380,18 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) ntohs(skb->protocol), skb->len, skb->pkt_type); if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { + unsigned short gso_type = 0; + pr_debug("GSO!\n"); switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + gso_type = SKB_GSO_TCPV4; break; case VIRTIO_NET_HDR_GSO_UDP: - skb_shinfo(skb)->gso_type = SKB_GSO_UDP; + gso_type = SKB_GSO_UDP; break; case VIRTIO_NET_HDR_GSO_TCPV6: - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + gso_type = SKB_GSO_TCPV6; break; default: net_warn_ratelimited("%s: bad gso type %u.\n", @@ -397,7 +400,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) } if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + gso_type |= SKB_GSO_TCP_ECN; skb_shinfo(skb)->gso_size = hdr->hdr.gso_size; if (skb_shinfo(skb)->gso_size == 0) { @@ -405,6 +408,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) goto frame_err; } + skb_shinfo(skb)->gso_type |= gso_type; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; skb_shinfo(skb)->gso_segs = 0; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8b2256e880e0..0259b719bebf 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -307,6 +307,13 @@ enum { SKB_GSO_TCPV6 = 1 << 4, SKB_GSO_FCOE = 1 << 5, + + /* This indicates at least one fragment might be overwritten + * (as in vmsplice(), sendfile() ...) + * If we need to compute a TX checksum, we'll need to copy + * all frags to avoid possible bad checksum + */ + SKB_GSO_SHARED_FRAG = 1 << 6, }; #if BITS_PER_LONG > 32 @@ -2200,6 +2207,18 @@ static inline int skb_linearize(struct sk_buff *skb) return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; } +/** + * skb_has_shared_frag - can any frag be overwritten + * @skb: buffer to test + * + * Return true if the skb has at least one frag that might be modified + * by an external entity (as in vmsplice()/sendfile()) + */ +static inline bool skb_has_shared_frag(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->gso_type & SKB_GSO_SHARED_FRAG; +} + /** * skb_linearize_cow - make sure skb is linear and writable * @skb: buffer to process diff --git a/net/core/dev.c b/net/core/dev.c index c69cd8721b28..a83375d3af72 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2271,6 +2271,15 @@ int skb_checksum_help(struct sk_buff *skb) return -EINVAL; } + /* Before computing a checksum, we should make sure no frag could + * be modified by an external entity : checksum could be wrong. + */ + if (skb_has_shared_frag(skb)) { + ret = __skb_linearize(skb); + if (ret) + goto out; + } + offset = skb_checksum_start_offset(skb); BUG_ON(offset >= skb_headlen(skb)); csum = skb_checksum(skb, offset, skb->len - offset, 0); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2568c449fe36..bddc1dd2e7f2 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2340,6 +2340,8 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) { int pos = skb_headlen(skb); + skb_shinfo(skb1)->gso_type = skb_shinfo(skb)->gso_type; + if (len < pos) /* Split line is inside header. */ skb_split_inside_header(skb, skb1, len, pos); else /* Second chunk has no header, nothing to copy. */ @@ -2845,6 +2847,8 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) skb_copy_from_linear_data_offset(skb, offset, skb_put(nskb, hsize), hsize); + skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type; + while (pos < offset + len && i < nfrags) { *frag = skb_shinfo(skb)->frags[i]; __skb_frag_ref(frag); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 4b7053919976..49ddca31c4da 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1306,6 +1306,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | + SKB_GSO_SHARED_FRAG | 0))) goto out; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 303012adf9e6..af6be70821c4 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -738,7 +738,7 @@ drop: static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); - const struct iphdr *old_iph = ip_hdr(skb); + const struct iphdr *old_iph; const struct iphdr *tiph; struct flowi4 fl4; u8 tos; @@ -756,6 +756,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev skb_checksum_help(skb)) goto tx_error; + old_iph = ip_hdr(skb); + if (dev->type == ARPHRD_ETHER) IPCB(skb)->flags = 0; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 191fc24a745a..8f024d41eefa 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -472,7 +472,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) __be16 df = tiph->frag_off; struct rtable *rt; /* Route to the other host */ struct net_device *tdev; /* Device to other host */ - const struct iphdr *old_iph = ip_hdr(skb); + const struct iphdr *old_iph; struct iphdr *iph; /* Our new IP header */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst = tiph->daddr; @@ -486,6 +486,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) skb_checksum_help(skb)) goto tx_error; + old_iph = ip_hdr(skb); + if (tos & 1) tos = old_iph->tos; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 52271947a471..3ec1f69c5ceb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -896,6 +896,8 @@ new_segment: skb_fill_page_desc(skb, i, page, offset, copy); } + skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; + skb->len += copy; skb->data_len += copy; skb->truesize += copy; @@ -3032,6 +3034,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | + SKB_GSO_SHARED_FRAG | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) goto out; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0905997e5873..492c7cfe1453 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1240,13 +1240,13 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, */ if (!skb_shinfo(prev)->gso_size) { skb_shinfo(prev)->gso_size = mss; - skb_shinfo(prev)->gso_type = sk->sk_gso_type; + skb_shinfo(prev)->gso_type |= sk->sk_gso_type; } /* CHECKME: To clear or not to clear? Mimics normal skb currently */ if (skb_shinfo(skb)->gso_segs <= 1) { skb_shinfo(skb)->gso_size = 0; - skb_shinfo(skb)->gso_type = 0; + skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG; } /* Difference in this won't matter, both ACKed by the same cumul. ACK */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 667a6adfccf8..367e2ec01da1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1133,6 +1133,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, unsigned int mss_now) { + skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG; if (skb->len <= mss_now || !sk_can_gso(sk) || skb->ip_summed == CHECKSUM_NONE) { /* Avoid the costly divide in the normal @@ -1140,11 +1141,10 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, */ skb_shinfo(skb)->gso_segs = 1; skb_shinfo(skb)->gso_size = 0; - skb_shinfo(skb)->gso_type = 0; } else { skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); skb_shinfo(skb)->gso_size = mss_now; - skb_shinfo(skb)->gso_type = sk->sk_gso_type; + skb_shinfo(skb)->gso_type |= sk->sk_gso_type; } } diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index f26f0da7f095..d141fc32a2ea 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | + SKB_GSO_SHARED_FRAG | 0))) goto out; -- cgit v1.2.3 From cdfa1fe5c0d4f2815acfe8cd7730a0b3d8d0a3e9 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 23 Jan 2013 22:52:27 +0800 Subject: af_key: remove a duplicated skb_orphan() skb_set_owner_r() will call skb_orphan(), I don't see any reason to call it twice. Cc: Steffen Klassert Cc: Herbert Xu Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: Steffen Klassert --- net/key/af_key.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/key/af_key.c b/net/key/af_key.c index 5b426a646544..6a6e68479b97 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -203,7 +203,6 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2, } if (*skb2 != NULL) { if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) { - skb_orphan(*skb2); skb_set_owner_r(*skb2, sk); skb_queue_tail(&sk->sk_receive_queue, *skb2); sk->sk_data_ready(sk, (*skb2)->len); -- cgit v1.2.3 From 3b4797bce0050570e84bedd10e1b14e9320a3551 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 26 Jan 2013 17:00:02 -0800 Subject: mac80211: fix mesh_sta_info_get() reshuffle damage Before "mac80211: clean up mesh sta allocation warning" was applied, mesh_sta_info_get() was reshuffled to please sparse. As a result we neglect to initialize newly created STAs. Fix this. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4e1d406fd9b1..81e612682bc3 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -408,6 +408,8 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, return NULL; } + mesh_sta_info_init(sdata, sta, elems, true); + if (sta_info_insert_rcu(sta)) return NULL; } -- cgit v1.2.3 From a7966d908ae8472e8183496916b7baec5f65b3a6 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 24 Jan 2013 11:41:39 +0100 Subject: batman-adv: fix local translation table output The last-seen field has to be printed for all the local entries but the one marked with the no-purge flag Introduced by 15727323d9f8864b2d41930940acc38de987045a ("batman-adv: don't print the last_seen time for bat0 TT local entry") Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index fb15b4c076f7..d44672f4a349 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -517,8 +517,8 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) BATADV_TT_CLIENT_PENDING ? 'X' : '.'), (tt_common_entry->flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), - no_purge ? last_seen_secs : 0, - no_purge ? last_seen_msecs : 0); + no_purge ? 0 : last_seen_secs, + no_purge ? 0 : last_seen_msecs); } rcu_read_unlock(); } -- cgit v1.2.3 From 2bf3440d7b8755f2627232e6a4c37efbbe053685 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Mon, 28 Jan 2013 08:33:33 +0000 Subject: can: rework skb reserved data handling Added accessor and skb_reserve helpers for struct can_skb_priv. Removed pointless skb_headroom() check. Signed-off-by: Oliver Hartkopp CC: Marc Kleine-Budde Signed-off-by: David S. Miller --- drivers/net/can/dev.c | 4 ++-- drivers/net/can/slcan.c | 4 ++-- include/linux/can/skb.h | 10 ++++++++++ net/can/bcm.c | 8 ++++---- net/can/gw.c | 4 +--- net/can/raw.c | 4 ++-- 6 files changed, 21 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 59ada082a994..f9cba4123c66 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -512,8 +512,8 @@ struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf) skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; - skb_reserve(skb, sizeof(struct can_skb_priv)); - ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); memset(*cf, 0, sizeof(struct can_frame)); diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index e79a8d10e0fc..06b7e097d36e 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -195,8 +195,8 @@ static void slc_bump(struct slcan *sl) skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; - skb_reserve(skb, sizeof(struct can_skb_priv)); - ((struct can_skb_priv *)(skb->head))->ifindex = sl->dev->ifindex; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = sl->dev->ifindex; memcpy(skb_put(skb, sizeof(struct can_frame)), &cf, sizeof(struct can_frame)); diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 4b0f24d3a878..2f0543f7510c 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -32,4 +32,14 @@ struct can_skb_priv { struct can_frame cf[0]; }; +static inline struct can_skb_priv *can_skb_prv(struct sk_buff *skb) +{ + return (struct can_skb_priv *)(skb->head); +} + +static inline void can_skb_reserve(struct sk_buff *skb) +{ + skb_reserve(skb, sizeof(struct can_skb_priv)); +} + #endif /* CAN_SKB_H */ diff --git a/net/can/bcm.c b/net/can/bcm.c index ccc27b9e8384..28e12d18f0f1 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -261,8 +261,8 @@ static void bcm_can_tx(struct bcm_op *op) if (!skb) goto out; - skb_reserve(skb, sizeof(struct can_skb_priv)); - ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; memcpy(skb_put(skb, CFSIZ), cf, CFSIZ); @@ -1207,7 +1207,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) if (!skb) return -ENOMEM; - skb_reserve(skb, sizeof(struct can_skb_priv)); + can_skb_reserve(skb); err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ); if (err < 0) { @@ -1221,7 +1221,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) return -ENODEV; } - ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + can_skb_prv(skb)->ifindex = dev->ifindex; skb->dev = dev; skb->sk = sk; err = can_send(skb, 1); /* send with loopback */ diff --git a/net/can/gw.c b/net/can/gw.c index acdd4656cc3b..c185fcd5e828 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -381,9 +381,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) /* is sending the skb back to the incoming interface not allowed? */ if (!(gwj->flags & CGW_FLAGS_CAN_IIF_TX_OK) && - skb_headroom(skb) == sizeof(struct can_skb_priv) && - (((struct can_skb_priv *)(skb->head))->ifindex == - gwj->dst.dev->ifindex)) + can_skb_prv(skb)->ifindex == gwj->dst.dev->ifindex) return; /* diff --git a/net/can/raw.c b/net/can/raw.c index 5d860e8dcc52..c1764e41ddaf 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -705,8 +705,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, if (!skb) goto put_dev; - skb_reserve(skb, sizeof(struct can_skb_priv)); - ((struct can_skb_priv *)(skb->head))->ifindex = dev->ifindex; + can_skb_reserve(skb); + can_skb_prv(skb)->ifindex = dev->ifindex; err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err < 0) -- cgit v1.2.3 From faeed828f9607923e9dc22182e819908e95c8852 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sun, 27 Jan 2013 15:55:20 +0000 Subject: netpoll: use ipv6_addr_equal() to compare ipv6 addr ipv6_addr_equal() is faster. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/netpoll.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index e2f79a14625c..a6f39b6aeec0 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -635,7 +635,7 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { - if (memcmp(daddr, &np->local_ip, sizeof(*daddr))) + if (!ipv6_addr_equal(daddr, &np->local_ip.in6)) continue; hlen = LL_RESERVED_SPACE(np->dev); @@ -828,9 +828,9 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) if (udp6_csum_init(skb, uh, IPPROTO_UDP)) goto out; list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { - if (memcmp(&np->local_ip.in6, &ip6h->daddr, sizeof(struct in6_addr)) != 0) + if (!ipv6_addr_equal(&np->local_ip.in6, &ip6h->daddr)) continue; - if (memcmp(&np->remote_ip.in6, &ip6h->saddr, sizeof(struct in6_addr)) != 0) + if (!ipv6_addr_equal(&np->remote_ip.in6, &ip6h->saddr)) continue; if (np->local_port && np->local_port != ntohs(uh->dest)) continue; -- cgit v1.2.3 From 556e6256c813063b95a9f4e55109f77c1a7d3fdd Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sun, 27 Jan 2013 15:55:21 +0000 Subject: netpoll: use the net namespace of current process instead of init_net This will allow us to setup netconsole in a different namespace rather than where init_net is. Cc: Eric W. Biederman Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/netpoll.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index a6f39b6aeec0..331ccb90f915 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -1049,8 +1049,10 @@ int netpoll_setup(struct netpoll *np) int err; rtnl_lock(); - if (np->dev_name) - ndev = __dev_get_by_name(&init_net, np->dev_name); + if (np->dev_name) { + struct net *net = current->nsproxy->net_ns; + ndev = __dev_get_by_name(net, np->dev_name); + } if (!ndev) { np_err(np, "%s doesn't exist, aborting\n", np->dev_name); err = -ENODEV; -- cgit v1.2.3 From cdda88912d62f9603d27433338a18be83ef23ac1 Mon Sep 17 00:00:00 2001 From: "bingtian.ly@taobao.com" Date: Wed, 23 Jan 2013 20:35:28 +0000 Subject: net: avoid to hang up on sending due to sysctl configuration overflow. I found if we write a larger than 4GB value to some sysctl variables, the sending syscall will hang up forever, because these variables are 32 bits, such large values make them overflow to 0 or negative. This patch try to fix overflow or prevent from zero value setup of below sysctl variables: net.core.wmem_default net.core.rmem_default net.core.rmem_max net.core.wmem_max net.ipv4.udp_rmem_min net.ipv4.udp_wmem_min net.ipv4.tcp_wmem net.ipv4.tcp_rmem Signed-off-by: Eric Dumazet Signed-off-by: Li Yu Signed-off-by: David S. Miller --- net/core/sysctl_net_core.c | 14 ++++++++++---- net/ipv4/sysctl_net_ipv4.c | 11 +++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index d1b08045a9df..cfdb46ab3a7f 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -20,6 +20,8 @@ #include #include +static int one = 1; + #ifdef CONFIG_RPS static int rps_sock_flow_sysctl(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -92,28 +94,32 @@ static struct ctl_table net_core_table[] = { .data = &sysctl_wmem_max, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "rmem_max", .data = &sysctl_rmem_max, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "wmem_default", .data = &sysctl_wmem_default, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "rmem_default", .data = &sysctl_rmem_default, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "dev_weight", diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a25e1d286b99..2622707602d1 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -27,6 +27,7 @@ #include static int zero; +static int one = 1; static int two = 2; static int tcp_retr1_max = 255; static int ip_local_port_range_min[] = { 1, 1 }; @@ -549,14 +550,16 @@ static struct ctl_table ipv4_table[] = { .data = &sysctl_tcp_wmem, .maxlen = sizeof(sysctl_tcp_wmem), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "tcp_rmem", .data = &sysctl_tcp_rmem, .maxlen = sizeof(sysctl_tcp_rmem), .mode = 0644, - .proc_handler = proc_dointvec + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, }, { .procname = "tcp_app_win", @@ -779,7 +782,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_udp_rmem_min), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &zero + .extra1 = &one }, { .procname = "udp_wmem_min", @@ -787,7 +790,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_udp_wmem_min), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &zero + .extra1 = &one }, { } }; -- cgit v1.2.3 From 08433eff2d041b263c68306f6a6ccb4e1f75e196 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Thu, 24 Jan 2013 00:44:23 +0000 Subject: net neigh: Optimize neighbor entry size calculation. When allocating memory for neighbour cache entry, if tbl->entry_size is not set, we always calculate sizeof(struct neighbour) + tbl->key_len, which is common in the same table. With this change, set tbl->entry_size during the table initialization phase, if it was not set, and use it in neigh_alloc() and neighbour_priv(). This change also allow us to have both of protocol private data and device priate data at tha same time. Note that the only user of prototcol private is DECnet and the only user of device private is ATM CLIP. Since those are exclusive, we have not been facing issues here. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/neighbour.h | 2 +- net/core/neighbour.c | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 0dab173e27da..629ee573c6d0 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -184,7 +184,7 @@ struct neigh_table { static inline void *neighbour_priv(const struct neighbour *n) { - return (char *)n + ALIGN(sizeof(*n) + n->tbl->key_len, NEIGH_PRIV_ALIGN); + return (char *)n + n->tbl->entry_size; } /* flags for neigh_update() */ diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 7bd0eedb357f..3863b8f639c5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -290,15 +290,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device goto out_entries; } - if (tbl->entry_size) - n = kzalloc(tbl->entry_size, GFP_ATOMIC); - else { - int sz = sizeof(*n) + tbl->key_len; - - sz = ALIGN(sz, NEIGH_PRIV_ALIGN); - sz += dev->neigh_priv_len; - n = kzalloc(sz, GFP_ATOMIC); - } + n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); if (!n) goto out_entries; @@ -1546,6 +1538,12 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl) if (!tbl->nht || !tbl->phash_buckets) panic("cannot allocate neighbour cache hashes"); + if (!tbl->entry_size) + tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + + tbl->key_len, NEIGH_PRIV_ALIGN); + else + WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); + rwlock_init(&tbl->lock); INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); schedule_delayed_work(&tbl->gc_work, tbl->parms.reachable_time); -- cgit v1.2.3 From 8df6b7b11a5e4200484e9356073d288f08bdefb0 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 28 Jan 2013 14:42:30 +0100 Subject: mac80211: remove IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL This is basically a revert of: commit 5b632fe85ec82e5c43740b52e74c66df50a37db3 Author: Stanislaw Gruszka Date: Mon Dec 3 12:56:33 2012 +0100 mac80211: introduce IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL We do not need this flag any longer, rt2x00 BAR/BA problem was fixed correctly by wireless-testing commit: commit 84e9e8ebd369679a958200a8baca96aafb2393bb Author: Helmut Schaa Date: Thu Jan 17 17:34:32 2013 +0100 rt2x00: Improve TX status handling for BlockAckReq frames Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 ----- net/mac80211/status.c | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5c98d654fc75..21831ee57e3c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1366,10 +1366,6 @@ struct ieee80211_tx_control { * @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any * P2P Interface. This will be honoured even if more than one interface * is supported. - * - * @IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL: On this hardware TX BA session - * should be tear down once BAR frame will not be acked. - * */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1398,7 +1394,6 @@ enum ieee80211_hw_flags { IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, - IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL = 1<<26, }; /** diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 07d99578a2b1..ab50285fcbab 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -502,11 +502,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) IEEE80211_BAR_CTRL_TID_INFO_MASK) >> IEEE80211_BAR_CTRL_TID_INFO_SHIFT; - if (local->hw.flags & - IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL) - ieee80211_stop_tx_ba_session(&sta->sta, tid); - else - ieee80211_set_bar_pending(sta, tid, ssn); + ieee80211_set_bar_pending(sta, tid, ssn); } } -- cgit v1.2.3 From 8dcf011ab05bf1fdd9323a9c460bdd0e325b99e1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 26 Jan 2013 22:16:08 +0100 Subject: cfg80211: add SME state to warning in __cfg80211_mlme_disassoc The warning here occasionally triggers but we haven't found the cause yet. It's a valid warning since if it triggers the SME state got confused, so add the SME state to it to help narrow it down in the future. Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 461e692cdfec..fee9bf70efcf 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -514,7 +514,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, if (wdev->sme_state != CFG80211_SME_CONNECTED) return -ENOTCONN; - if (WARN_ON(!wdev->current_bss)) + if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state)) return -ENOTCONN; memset(&req, 0, sizeof(req)); -- cgit v1.2.3 From d433673e5f9180e05a770c4b2ab18c08ad51cc21 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 28 Jan 2013 23:45:12 +0000 Subject: net: frag helper functions for mem limit tracking This change is primarily a preparation to ease the extension of memory limit tracking. The change does reduce the number atomic operation, during freeing of a frag queue. This does introduce a some performance improvement, as these atomic operations are at the core of the performance problems seen on NUMA systems. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/inet_frag.h | 27 +++++++++++++++++++++++++++ include/net/ipv6.h | 2 +- net/ipv4/inet_fragment.c | 25 ++++++++++++------------- net/ipv4/ip_fragment.c | 24 +++++++++++------------- net/ipv6/netfilter/nf_conntrack_reasm.c | 6 +++--- net/ipv6/reassembly.c | 6 +++--- 6 files changed, 57 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 8e4c42523f59..f2fabc2a79de 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -79,4 +79,31 @@ static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f inet_frag_destroy(q, f, NULL); } +/* Memory Tracking Functions. */ + +static inline int frag_mem_limit(struct netns_frags *nf) +{ + return atomic_read(&nf->mem); +} + +static inline void sub_frag_mem_limit(struct inet_frag_queue *q, int i) +{ + atomic_sub(i, &q->net->mem); +} + +static inline void add_frag_mem_limit(struct inet_frag_queue *q, int i) +{ + atomic_add(i, &q->net->mem); +} + +static inline void init_frag_mem_limit(struct netns_frags *nf) +{ + atomic_set(&nf->mem, 0); +} + +static inline int sum_frag_mem_limit(struct netns_frags *nf) +{ + return atomic_read(&nf->mem); +} + #endif diff --git a/include/net/ipv6.h b/include/net/ipv6.h index c1878f7049c8..dc30b60975ef 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -288,7 +288,7 @@ static inline int ip6_frag_nqueues(struct net *net) static inline int ip6_frag_mem(struct net *net) { - return atomic_read(&net->ipv6.frags.mem); + return sum_frag_mem_limit(&net->ipv6.frags); } #endif diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 4750d2b74d79..e348c849c5a3 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -73,7 +73,7 @@ EXPORT_SYMBOL(inet_frags_init); void inet_frags_init_net(struct netns_frags *nf) { nf->nqueues = 0; - atomic_set(&nf->mem, 0); + init_frag_mem_limit(nf); INIT_LIST_HEAD(&nf->lru_list); } EXPORT_SYMBOL(inet_frags_init_net); @@ -117,12 +117,8 @@ void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) EXPORT_SYMBOL(inet_frag_kill); static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f, - struct sk_buff *skb, int *work) + struct sk_buff *skb) { - if (work) - *work -= skb->truesize; - - atomic_sub(skb->truesize, &nf->mem); if (f->skb_free) f->skb_free(skb); kfree_skb(skb); @@ -133,6 +129,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, { struct sk_buff *fp; struct netns_frags *nf; + unsigned int sum, sum_truesize = 0; WARN_ON(!(q->last_in & INET_FRAG_COMPLETE)); WARN_ON(del_timer(&q->timer) != 0); @@ -143,13 +140,14 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, while (fp) { struct sk_buff *xp = fp->next; - frag_kfree_skb(nf, f, fp, work); + sum_truesize += fp->truesize; + frag_kfree_skb(nf, f, fp); fp = xp; } - + sum = sum_truesize + f->qsize; if (work) - *work -= f->qsize; - atomic_sub(f->qsize, &nf->mem); + *work -= sum; + sub_frag_mem_limit(q, sum); if (f->destructor) f->destructor(q); @@ -164,11 +162,11 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) int work, evicted = 0; if (!force) { - if (atomic_read(&nf->mem) <= nf->high_thresh) + if (frag_mem_limit(nf) <= nf->high_thresh) return 0; } - work = atomic_read(&nf->mem) - nf->low_thresh; + work = frag_mem_limit(nf) - nf->low_thresh; while (work > 0) { read_lock(&f->lock); if (list_empty(&nf->lru_list)) { @@ -250,7 +248,8 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, q->net = nf; f->constructor(q, arg); - atomic_add(f->qsize, &nf->mem); + add_frag_mem_limit(q, f->qsize); + setup_timer(&q->timer, f->frag_expire, (unsigned long)q); spin_lock_init(&q->lock); atomic_set(&q->refcnt, 1); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index f55a4e61bfb8..927fe58caf46 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -122,7 +122,7 @@ int ip_frag_nqueues(struct net *net) int ip_frag_mem(struct net *net) { - return atomic_read(&net->ipv4.frags.mem); + return sum_frag_mem_limit(&net->ipv4.frags); } static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, @@ -161,13 +161,6 @@ static bool ip4_frag_match(struct inet_frag_queue *q, void *a) qp->user == arg->user; } -/* Memory Tracking Functions. */ -static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb) -{ - atomic_sub(skb->truesize, &nf->mem); - kfree_skb(skb); -} - static void ip4_frag_init(struct inet_frag_queue *q, void *a) { struct ipq *qp = container_of(q, struct ipq, q); @@ -340,6 +333,7 @@ static inline int ip_frag_too_far(struct ipq *qp) static int ip_frag_reinit(struct ipq *qp) { struct sk_buff *fp; + unsigned int sum_truesize = 0; if (!mod_timer(&qp->q.timer, jiffies + qp->q.net->timeout)) { atomic_inc(&qp->q.refcnt); @@ -349,9 +343,12 @@ static int ip_frag_reinit(struct ipq *qp) fp = qp->q.fragments; do { struct sk_buff *xp = fp->next; - frag_kfree_skb(qp->q.net, fp); + + sum_truesize += fp->truesize; + kfree_skb(fp); fp = xp; } while (fp); + sub_frag_mem_limit(&qp->q, sum_truesize); qp->q.last_in = 0; qp->q.len = 0; @@ -496,7 +493,8 @@ found: qp->q.fragments = next; qp->q.meat -= free_it->len; - frag_kfree_skb(qp->q.net, free_it); + sub_frag_mem_limit(&qp->q, free_it->truesize); + kfree_skb(free_it); } } @@ -519,7 +517,7 @@ found: qp->q.stamp = skb->tstamp; qp->q.meat += skb->len; qp->ecn |= ecn; - atomic_add(skb->truesize, &qp->q.net->mem); + add_frag_mem_limit(&qp->q, skb->truesize); if (offset == 0) qp->q.last_in |= INET_FRAG_FIRST_IN; @@ -617,7 +615,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, head->len -= clone->len; clone->csum = 0; clone->ip_summed = head->ip_summed; - atomic_add(clone->truesize, &qp->q.net->mem); + add_frag_mem_limit(&qp->q, clone->truesize); } skb_push(head, head->data - skb_network_header(head)); @@ -645,7 +643,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, } fp = next; } - atomic_sub(sum_truesize, &qp->q.net->mem); + sub_frag_mem_limit(&qp->q, sum_truesize); head->next = NULL; head->dev = dev; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3dacecc99065..07ef2949c946 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -319,7 +319,7 @@ found: fq->q.meat += skb->len; if (payload_len > fq->q.max_size) fq->q.max_size = payload_len; - atomic_add(skb->truesize, &fq->q.net->mem); + add_frag_mem_limit(&fq->q, skb->truesize); /* The first fragment. * nhoffset is obtained from the first fragment, of course. @@ -398,7 +398,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) clone->ip_summed = head->ip_summed; NFCT_FRAG6_CB(clone)->orig = NULL; - atomic_add(clone->truesize, &fq->q.net->mem); + add_frag_mem_limit(&fq->q, clone->truesize); } /* We have to remove fragment header from datagram and to relocate @@ -422,7 +422,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) head->csum = csum_add(head->csum, fp->csum); head->truesize += fp->truesize; } - atomic_sub(head->truesize, &fq->q.net->mem); + sub_frag_mem_limit(&fq->q, head->truesize); head->local_df = 1; head->next = NULL; diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index e5253ec9e0fc..18cb8defcdb2 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -327,7 +327,7 @@ found: } fq->q.stamp = skb->tstamp; fq->q.meat += skb->len; - atomic_add(skb->truesize, &fq->q.net->mem); + add_frag_mem_limit(&fq->q, skb->truesize); /* The first fragment. * nhoffset is obtained from the first fragment, of course. @@ -429,7 +429,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, head->len -= clone->len; clone->csum = 0; clone->ip_summed = head->ip_summed; - atomic_add(clone->truesize, &fq->q.net->mem); + add_frag_mem_limit(&fq->q, clone->truesize); } /* We have to remove fragment header from datagram and to relocate @@ -467,7 +467,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, } fp = next; } - atomic_sub(sum_truesize, &fq->q.net->mem); + sub_frag_mem_limit(&fq->q, sum_truesize); head->next = NULL; head->dev = dev; -- cgit v1.2.3 From 6d7b857d541ecd1d9bd997c97242d4ef94b19de2 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 28 Jan 2013 23:45:33 +0000 Subject: net: use lib/percpu_counter API for fragmentation mem accounting Replace the per network namespace shared atomic "mem" accounting variable, in the fragmentation code, with a lib/percpu_counter. Getting percpu_counter to scale to the fragmentation code usage requires some tweaks. At first view, percpu_counter looks superfast, but it does not scale on multi-CPU/NUMA machines, because the default batch size is too small, for frag code usage. Thus, I have adjusted the batch size by using __percpu_counter_add() directly, instead of percpu_counter_sub() and percpu_counter_add(). The batch size is increased to 130.000, based on the largest 64K fragment memory usage. This does introduce some imprecise memory accounting, but its does not need to be strict for this use-case. It is also essential, that the percpu_counter, does not share cacheline with other writers, to make this scale. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/inet_frag.h | 26 ++++++++++++++++++-------- net/ipv4/inet_fragment.c | 2 ++ 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index f2fabc2a79de..e0eec7450f15 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -1,14 +1,17 @@ #ifndef __NET_FRAG_H__ #define __NET_FRAG_H__ +#include + struct netns_frags { int nqueues; struct list_head lru_list; - /* Its important for performance to keep lru_list and mem on - * separate cachelines + /* The percpu_counter "mem" need to be cacheline aligned. + * mem.count must not share cacheline with other writers */ - atomic_t mem ____cacheline_aligned_in_smp; + struct percpu_counter mem ____cacheline_aligned_in_smp; + /* sysctls */ int timeout; int high_thresh; @@ -81,29 +84,36 @@ static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f /* Memory Tracking Functions. */ +/* The default percpu_counter batch size is not big enough to scale to + * fragmentation mem acct sizes. + * The mem size of a 64K fragment is approx: + * (44 fragments * 2944 truesize) + frag_queue struct(200) = 129736 bytes + */ +static unsigned int frag_percpu_counter_batch = 130000; + static inline int frag_mem_limit(struct netns_frags *nf) { - return atomic_read(&nf->mem); + return percpu_counter_read(&nf->mem); } static inline void sub_frag_mem_limit(struct inet_frag_queue *q, int i) { - atomic_sub(i, &q->net->mem); + __percpu_counter_add(&q->net->mem, -i, frag_percpu_counter_batch); } static inline void add_frag_mem_limit(struct inet_frag_queue *q, int i) { - atomic_add(i, &q->net->mem); + __percpu_counter_add(&q->net->mem, i, frag_percpu_counter_batch); } static inline void init_frag_mem_limit(struct netns_frags *nf) { - atomic_set(&nf->mem, 0); + percpu_counter_init(&nf->mem, 0); } static inline int sum_frag_mem_limit(struct netns_frags *nf) { - return atomic_read(&nf->mem); + return percpu_counter_sum_positive(&nf->mem); } #endif diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index e348c849c5a3..b82520565098 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -91,6 +91,8 @@ void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) local_bh_disable(); inet_frag_evictor(nf, f, true); local_bh_enable(); + + percpu_counter_destroy(&nf->mem); } EXPORT_SYMBOL(inet_frags_exit_net); -- cgit v1.2.3 From 3ef0eb0db4bf92c6d2510fe5c4dc51852746f206 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 28 Jan 2013 23:45:51 +0000 Subject: net: frag, move LRU list maintenance outside of rwlock Updating the fragmentation queues LRU (Least-Recently-Used) list, required taking the hash writer lock. However, the LRU list isn't tied to the hash at all, so we can use a separate lock for it. Original-idea-by: Florian Westphal Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/inet_frag.h | 22 ++++++++++++++++++++++ net/ipv4/inet_fragment.c | 12 +++++++----- net/ipv4/ip_fragment.c | 4 +--- net/ipv6/netfilter/nf_conntrack_reasm.c | 5 ++--- net/ipv6/reassembly.c | 4 +--- 5 files changed, 33 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index e0eec7450f15..3f237db0a426 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -6,6 +6,7 @@ struct netns_frags { int nqueues; struct list_head lru_list; + spinlock_t lru_lock; /* The percpu_counter "mem" need to be cacheline aligned. * mem.count must not share cacheline with other writers @@ -116,4 +117,25 @@ static inline int sum_frag_mem_limit(struct netns_frags *nf) return percpu_counter_sum_positive(&nf->mem); } +static inline void inet_frag_lru_move(struct inet_frag_queue *q) +{ + spin_lock(&q->net->lru_lock); + list_move_tail(&q->lru_list, &q->net->lru_list); + spin_unlock(&q->net->lru_lock); +} + +static inline void inet_frag_lru_del(struct inet_frag_queue *q) +{ + spin_lock(&q->net->lru_lock); + list_del(&q->lru_list); + spin_unlock(&q->net->lru_lock); +} + +static inline void inet_frag_lru_add(struct netns_frags *nf, + struct inet_frag_queue *q) +{ + spin_lock(&nf->lru_lock); + list_add_tail(&q->lru_list, &nf->lru_list); + spin_unlock(&nf->lru_lock); +} #endif diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index b82520565098..2e453bde6992 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -75,6 +75,7 @@ void inet_frags_init_net(struct netns_frags *nf) nf->nqueues = 0; init_frag_mem_limit(nf); INIT_LIST_HEAD(&nf->lru_list); + spin_lock_init(&nf->lru_lock); } EXPORT_SYMBOL(inet_frags_init_net); @@ -100,9 +101,9 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) { write_lock(&f->lock); hlist_del(&fq->list); - list_del(&fq->lru_list); fq->net->nqueues--; write_unlock(&f->lock); + inet_frag_lru_del(fq); } void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) @@ -170,16 +171,17 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) work = frag_mem_limit(nf) - nf->low_thresh; while (work > 0) { - read_lock(&f->lock); + spin_lock(&nf->lru_lock); + if (list_empty(&nf->lru_list)) { - read_unlock(&f->lock); + spin_unlock(&nf->lru_lock); break; } q = list_first_entry(&nf->lru_list, struct inet_frag_queue, lru_list); atomic_inc(&q->refcnt); - read_unlock(&f->lock); + spin_unlock(&nf->lru_lock); spin_lock(&q->lock); if (!(q->last_in & INET_FRAG_COMPLETE)) @@ -233,9 +235,9 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, atomic_inc(&qp->refcnt); hlist_add_head(&qp->list, &f->hash[hash]); - list_add_tail(&qp->lru_list, &nf->lru_list); nf->nqueues++; write_unlock(&f->lock); + inet_frag_lru_add(nf, qp); return qp; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 927fe58caf46..1211613c6c34 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -529,9 +529,7 @@ found: qp->q.meat == qp->q.len) return ip_frag_reasm(qp, prev, dev); - write_lock(&ip4_frags.lock); - list_move_tail(&qp->q.lru_list, &qp->q.net->lru_list); - write_unlock(&ip4_frags.lock); + inet_frag_lru_move(&qp->q); return -EINPROGRESS; err: diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 07ef2949c946..c674f158efa8 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -328,9 +328,8 @@ found: fq->nhoffset = nhoff; fq->q.last_in |= INET_FRAG_FIRST_IN; } - write_lock(&nf_frags.lock); - list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list); - write_unlock(&nf_frags.lock); + + inet_frag_lru_move(&fq->q); return 0; discard_fq: diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 18cb8defcdb2..bab2c270f292 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -341,9 +341,7 @@ found: fq->q.meat == fq->q.len) return ip6_frag_reasm(fq, prev, dev); - write_lock(&ip6_frags.lock); - list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list); - write_unlock(&ip6_frags.lock); + inet_frag_lru_move(&fq->q); return -1; discard_fq: -- cgit v1.2.3 From 5c766d642bcaffd0c2a5b354db2068515b3846cf Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 24 Jan 2013 09:41:41 +0000 Subject: ipv4: introduce address lifetime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some usecase when lifetime of ipv4 addresses might be helpful. For example: 1) initramfs networkmanager uses a DHCP daemon to learn network configuration parameters 2) initramfs networkmanager addresses, routes and DNS configuration 3) initramfs networkmanager is requested to stop 4) initramfs networkmanager stops all daemons including dhclient 5) there are addresses and routes configured but no daemon running. If the system doesn't start networkmanager for some reason, addresses and routes will be used forever, which violates RFC 2131. This patch is essentially a backport of ivp6 address lifetime mechanism for ipv4 addresses. Current "ip" tool supports this without any patch (since it does not distinguish between ipv4 and ipv6 addresses in this perspective. Also, this should be back-compatible with all current netlink users. Reported-by: Pavel Šimerda Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 6 ++ include/net/addrconf.h | 4 + net/ipv4/devinet.c | 215 +++++++++++++++++++++++++++++++++++++++++++-- net/ipv6/addrconf.c | 4 - 4 files changed, 220 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index a9d828976a77..ea1e3b863890 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -166,6 +166,12 @@ struct in_ifaddr { unsigned char ifa_flags; unsigned char ifa_prefixlen; char ifa_label[IFNAMSIZ]; + + /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ + __u32 ifa_valid_lft; + __u32 ifa_preferred_lft; + unsigned long ifa_cstamp; /* created timestamp */ + unsigned long ifa_tstamp; /* updated timestamp */ }; extern int register_inetaddr_notifier(struct notifier_block *nb); diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 6c58d507123f..40be2a0d8ae1 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -15,6 +15,10 @@ #define IPV6_MAX_ADDRESSES 16 +#define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ / 50 : 1) +#define ADDRCONF_TIMER_FUZZ (HZ / 4) +#define ADDRCONF_TIMER_FUZZ_MAX (HZ) + #include #include diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index a8e4f2665d5e..5281314886c1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "fib_lookup.h" @@ -93,6 +94,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { [IFA_ADDRESS] = { .type = NLA_U32 }, [IFA_BROADCAST] = { .type = NLA_U32 }, [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, + [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, }; #define IN4_ADDR_HSIZE_SHIFT 8 @@ -417,6 +419,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); } +static void check_lifetime(struct work_struct *work); + +static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); + static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, u32 portid) { @@ -462,6 +468,9 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, inet_hash_insert(dev_net(in_dev->dev), ifa); + cancel_delayed_work(&check_lifetime_work); + schedule_delayed_work(&check_lifetime_work, 0); + /* Send message first, then call notifier. Notifier will trigger FIB update, so that listeners of netlink will know about new ifaddr */ @@ -573,7 +582,107 @@ errout: return err; } -static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) +#define INFINITY_LIFE_TIME 0xFFFFFFFF + +static void check_lifetime(struct work_struct *work) +{ + unsigned long now, next, next_sec, next_sched; + struct in_ifaddr *ifa; + struct hlist_node *node; + int i; + + now = jiffies; + next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); + + rcu_read_lock(); + for (i = 0; i < IN4_ADDR_HSIZE; i++) { + hlist_for_each_entry_rcu(ifa, node, + &inet_addr_lst[i], hash) { + unsigned long age; + + if (ifa->ifa_flags & IFA_F_PERMANENT) + continue; + + /* We try to batch several events at once. */ + age = (now - ifa->ifa_tstamp + + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; + + if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && + age >= ifa->ifa_valid_lft) { + struct in_ifaddr **ifap ; + + rtnl_lock(); + for (ifap = &ifa->ifa_dev->ifa_list; + *ifap != NULL; ifap = &ifa->ifa_next) { + if (*ifap == ifa) + inet_del_ifa(ifa->ifa_dev, + ifap, 1); + } + rtnl_unlock(); + } else if (ifa->ifa_preferred_lft == + INFINITY_LIFE_TIME) { + continue; + } else if (age >= ifa->ifa_preferred_lft) { + if (time_before(ifa->ifa_tstamp + + ifa->ifa_valid_lft * HZ, next)) + next = ifa->ifa_tstamp + + ifa->ifa_valid_lft * HZ; + + if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) { + ifa->ifa_flags |= IFA_F_DEPRECATED; + rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); + } + } else if (time_before(ifa->ifa_tstamp + + ifa->ifa_preferred_lft * HZ, + next)) { + next = ifa->ifa_tstamp + + ifa->ifa_preferred_lft * HZ; + } + } + } + rcu_read_unlock(); + + next_sec = round_jiffies_up(next); + next_sched = next; + + /* If rounded timeout is accurate enough, accept it. */ + if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) + next_sched = next_sec; + + now = jiffies; + /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ + if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) + next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; + + schedule_delayed_work(&check_lifetime_work, next_sched - now); +} + +static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, + __u32 prefered_lft) +{ + unsigned long timeout; + + ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); + + timeout = addrconf_timeout_fixup(valid_lft, HZ); + if (addrconf_finite_timeout(timeout)) + ifa->ifa_valid_lft = timeout; + else + ifa->ifa_flags |= IFA_F_PERMANENT; + + timeout = addrconf_timeout_fixup(prefered_lft, HZ); + if (addrconf_finite_timeout(timeout)) { + if (timeout == 0) + ifa->ifa_flags |= IFA_F_DEPRECATED; + ifa->ifa_preferred_lft = timeout; + } + ifa->ifa_tstamp = jiffies; + if (!ifa->ifa_cstamp) + ifa->ifa_cstamp = ifa->ifa_tstamp; +} + +static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, + __u32 *pvalid_lft, __u32 *pprefered_lft) { struct nlattr *tb[IFA_MAX+1]; struct in_ifaddr *ifa; @@ -633,24 +742,73 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) else memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); + if (tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci; + + ci = nla_data(tb[IFA_CACHEINFO]); + if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { + err = -EINVAL; + goto errout; + } + *pvalid_lft = ci->ifa_valid; + *pprefered_lft = ci->ifa_prefered; + } + return ifa; errout: return ERR_PTR(err); } +static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) +{ + struct in_device *in_dev = ifa->ifa_dev; + struct in_ifaddr *ifa1, **ifap; + + if (!ifa->ifa_local) + return NULL; + + for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; + ifap = &ifa1->ifa_next) { + if (ifa1->ifa_mask == ifa->ifa_mask && + inet_ifa_match(ifa1->ifa_address, ifa) && + ifa1->ifa_local == ifa->ifa_local) + return ifa1; + } + return NULL; +} + static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); struct in_ifaddr *ifa; + struct in_ifaddr *ifa_existing; + __u32 valid_lft = INFINITY_LIFE_TIME; + __u32 prefered_lft = INFINITY_LIFE_TIME; ASSERT_RTNL(); - ifa = rtm_to_ifaddr(net, nlh); + ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); if (IS_ERR(ifa)) return PTR_ERR(ifa); - return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); + ifa_existing = find_matching_ifa(ifa); + if (!ifa_existing) { + /* It would be best to check for !NLM_F_CREATE here but + * userspace alreay relies on not having to provide this. + */ + set_ifa_lifetime(ifa, valid_lft, prefered_lft); + return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); + } else { + inet_free_ifa(ifa); + + if (nlh->nlmsg_flags & NLM_F_EXCL || + !(nlh->nlmsg_flags & NLM_F_REPLACE)) + return -EEXIST; + + set_ifa_lifetime(ifa_existing, valid_lft, prefered_lft); + } + return 0; } /* @@ -852,6 +1010,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) ifa->ifa_prefixlen = 32; ifa->ifa_mask = inet_make_mask(32); } + set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); ret = inet_set_ifa(dev, ifa); break; @@ -1190,6 +1349,8 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, ifa->ifa_dev = in_dev; ifa->ifa_scope = RT_SCOPE_HOST; memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); + set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, + INFINITY_LIFE_TIME); inet_insert_ifa(ifa); } } @@ -1246,11 +1407,30 @@ static size_t inet_nlmsg_size(void) + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ } +static inline u32 cstamp_delta(unsigned long cstamp) +{ + return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; +} + +static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, + unsigned long tstamp, u32 preferred, u32 valid) +{ + struct ifa_cacheinfo ci; + + ci.cstamp = cstamp_delta(cstamp); + ci.tstamp = cstamp_delta(tstamp); + ci.ifa_prefered = preferred; + ci.ifa_valid = valid; + + return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); +} + static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, u32 portid, u32 seq, int event, unsigned int flags) { struct ifaddrmsg *ifm; struct nlmsghdr *nlh; + u32 preferred, valid; nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); if (nlh == NULL) @@ -1259,10 +1439,31 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, ifm = nlmsg_data(nlh); ifm->ifa_family = AF_INET; ifm->ifa_prefixlen = ifa->ifa_prefixlen; - ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; + ifm->ifa_flags = ifa->ifa_flags; ifm->ifa_scope = ifa->ifa_scope; ifm->ifa_index = ifa->ifa_dev->dev->ifindex; + if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { + preferred = ifa->ifa_preferred_lft; + valid = ifa->ifa_valid_lft; + if (preferred != INFINITY_LIFE_TIME) { + long tval = (jiffies - ifa->ifa_tstamp) / HZ; + + if (preferred > tval) + preferred -= tval; + else + preferred = 0; + if (valid != INFINITY_LIFE_TIME) { + if (valid > tval) + valid -= tval; + else + valid = 0; + } + } + } else { + preferred = INFINITY_LIFE_TIME; + valid = INFINITY_LIFE_TIME; + } if ((ifa->ifa_address && nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || (ifa->ifa_local && @@ -1270,7 +1471,9 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, (ifa->ifa_broadcast && nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || (ifa->ifa_label[0] && - nla_put_string(skb, IFA_LABEL, ifa->ifa_label))) + nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || + put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, + preferred, valid)) goto nla_put_failure; return nlmsg_end(skb, nlh); @@ -1988,6 +2191,8 @@ void __init devinet_init(void) register_gifconf(PF_INET, inet_gifconf); register_netdevice_notifier(&ip_netdev_notifier); + schedule_delayed_work(&check_lifetime_work, 0); + rtnl_af_register(&inet_af_ops); rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 80d59802d964..7f7332b44699 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -110,10 +110,6 @@ static inline u32 cstamp_delta(unsigned long cstamp) return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; } -#define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ/50 : 1) -#define ADDRCONF_TIMER_FUZZ (HZ / 4) -#define ADDRCONF_TIMER_FUZZ_MAX (HZ) - #ifdef CONFIG_SYSCTL static void addrconf_sysctl_register(struct inet6_dev *idev); static void addrconf_sysctl_unregister(struct inet6_dev *idev); -- cgit v1.2.3 From 040468a0a7b3f9c7966126d98151bc64f1423657 Mon Sep 17 00:00:00 2001 From: David Ward Date: Sun, 27 Jan 2013 13:04:58 +0000 Subject: ip_gre: When TOS is inherited, use configured TOS value for non-IP packets A GRE tunnel can be configured so that outgoing tunnel packets inherit the value of the TOS field from the inner IP header. In doing so, when a non-IP packet is transmitted through the tunnel, the TOS field will always be set to 0. Instead, the user should be able to configure a different TOS value as the fallback to use for non-IP packets. This is helpful when the non-IP packets are all control packets and should be handled by routers outside the tunnel as having Internet Control precedence. One example of this is the NHRP packets that control a DMVPN-compatible mGRE tunnel; they are encapsulated directly by GRE and do not contain an inner IP header. Under the existing behavior, the IFLA_GRE_TOS parameter must be set to '1' for the TOS value to be inherited. Now, only the least significant bit of this parameter must be set to '1', and when a non-IP packet is sent through the tunnel, the upper 6 bits of this same parameter will be copied into the TOS field. (The ECN bits get masked off as before.) This behavior is backwards-compatible with existing configurations and iproute2 versions. Signed-off-by: David Ward Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index af6be70821c4..801e02355ec4 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -820,8 +820,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev ttl = tiph->ttl; tos = tiph->tos; - if (tos == 1) { - tos = 0; + if (tos & 0x1) { + tos &= ~0x1; if (skb->protocol == htons(ETH_P_IP)) tos = old_iph->tos; else if (skb->protocol == htons(ETH_P_IPV6)) -- cgit v1.2.3 From 3b144658bc7be5f7fa68d13ba24afb4c24489965 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 26 Jan 2013 16:39:55 -0800 Subject: mac80211: dynamic short slot time for MBSSs The standard mandates mesh STAs to set the ERP Short Slot Time capability info bit in beacons to 0. Even though this is their way of disallowing short slot time for mesh STAs, there should be no harm in enabling it if we determine all STAs in the current MBSS support ERP rates. Increases throughput about 20% for legacy rates when enabled. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 5 ---- net/mac80211/mesh_plink.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 694e27376afa..f920da1201ab 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -629,11 +629,6 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); - if (band == IEEE80211_BAND_5GHZ) { - sdata->vif.bss_conf.use_short_slot = true; - changed |= BSS_CHANGED_ERP_SLOT; - } - ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 81e612682bc3..6787d696d94c 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -55,6 +55,66 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) sta->plink_retries = 0; } +/* + * mesh_set_short_slot_time - enable / disable ERP short slot time. + * + * The standard indirectly mandates mesh STAs to turn off short slot time by + * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we + * can't be sneaky about it. Enable short slot time if all mesh STAs in the + * MBSS support ERP rates. + * + * Returns BSS_CHANGED_ERP_SLOT or 0 for no change. + */ +static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + struct sta_info *sta; + u32 erp_rates = 0, changed = 0; + int i; + bool short_slot = false; + + if (band == IEEE80211_BAND_5GHZ) { + /* (IEEE 802.11-2012 19.4.5) */ + short_slot = true; + goto out; + } else if (band != IEEE80211_BAND_2GHZ || + (band == IEEE80211_BAND_2GHZ && + local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) + goto out; + + for (i = 0; i < sband->n_bitrates; i++) + if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) + erp_rates |= BIT(i); + + if (!erp_rates) + goto out; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sdata != sta->sdata || + sta->plink_state != NL80211_PLINK_ESTAB) + continue; + + short_slot = false; + if (erp_rates & sta->sta.supp_rates[band]) + short_slot = true; + else + break; + } + rcu_read_unlock(); + +out: + if (sdata->vif.bss_conf.use_short_slot != short_slot) { + sdata->vif.bss_conf.use_short_slot = short_slot; + changed = BSS_CHANGED_ERP_SLOT; + mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n", + sdata->vif.addr, short_slot); + } + return changed; +} + /** * mesh_set_ht_prot_mode - set correct HT protection mode * @@ -896,6 +956,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m spin_unlock_bh(&sta->lock); changed |= mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); + changed |= mesh_set_short_slot_time(sdata); mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; @@ -931,6 +992,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m spin_unlock_bh(&sta->lock); changed |= mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); + changed |= mesh_set_short_slot_time(sdata); mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, @@ -954,6 +1016,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); spin_unlock_bh(&sta->lock); changed |= mesh_set_ht_prot_mode(sdata); + changed |= mesh_set_short_slot_time(sdata); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; -- cgit v1.2.3 From 4e58a0275015e5c8988bda9cd0635e2ef405c985 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 28 Jan 2013 19:55:53 +0000 Subject: pktgen: support net namespace v3: make pktgen_threads list per-namespace v2: remove a useless check This patch add net namespace to pktgen, so that we can use pktgen in different namespaces. Cc: Eric W. Biederman Cc: David S. Miller Signed-off-by: Cong Wang Acked-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- net/core/pktgen.c | 196 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 79 deletions(-) (limited to 'net') diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b29dacf900f9..797769551b91 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -164,6 +164,7 @@ #ifdef CONFIG_XFRM #include #endif +#include #include #include #include @@ -212,7 +213,6 @@ #define PKTGEN_MAGIC 0xbe9be955 #define PG_PROC_DIR "pktgen" #define PGCTRL "pgctrl" -static struct proc_dir_entry *pg_proc_dir; #define MAX_CFLOWS 65536 @@ -397,7 +397,15 @@ struct pktgen_hdr { __be32 tv_usec; }; -static bool pktgen_exiting __read_mostly; + +static int pg_net_id __read_mostly; + +struct pktgen_net { + struct net *net; + struct proc_dir_entry *proc_dir; + struct list_head pktgen_threads; + bool pktgen_exiting; +}; struct pktgen_thread { spinlock_t if_lock; /* for list of devices */ @@ -414,6 +422,7 @@ struct pktgen_thread { wait_queue_head_t queue; struct completion start_done; + struct pktgen_net *net; }; #define REMOVE 1 @@ -428,9 +437,9 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname); static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, const char *ifname, bool exact); static int pktgen_device_event(struct notifier_block *, unsigned long, void *); -static void pktgen_run_all_threads(void); -static void pktgen_reset_all_threads(void); -static void pktgen_stop_all_threads_ifs(void); +static void pktgen_run_all_threads(struct pktgen_net *pn); +static void pktgen_reset_all_threads(struct pktgen_net *pn); +static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn); static void pktgen_stop(struct pktgen_thread *t); static void pktgen_clear_counters(struct pktgen_dev *pkt_dev); @@ -442,7 +451,6 @@ static int pg_clone_skb_d __read_mostly; static int debug __read_mostly; static DEFINE_MUTEX(pktgen_thread_lock); -static LIST_HEAD(pktgen_threads); static struct notifier_block pktgen_notifier_block = { .notifier_call = pktgen_device_event, @@ -464,6 +472,7 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf, { int err = 0; char data[128]; + struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id); if (!capable(CAP_NET_ADMIN)) { err = -EPERM; @@ -480,13 +489,13 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf, data[count - 1] = 0; /* Make string */ if (!strcmp(data, "stop")) - pktgen_stop_all_threads_ifs(); + pktgen_stop_all_threads_ifs(pn); else if (!strcmp(data, "start")) - pktgen_run_all_threads(); + pktgen_run_all_threads(pn); else if (!strcmp(data, "reset")) - pktgen_reset_all_threads(); + pktgen_reset_all_threads(pn); else pr_warning("Unknown command: %s\n", data); @@ -1824,13 +1833,14 @@ static const struct file_operations pktgen_thread_fops = { }; /* Think find or remove for NN */ -static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove) +static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn, + const char *ifname, int remove) { struct pktgen_thread *t; struct pktgen_dev *pkt_dev = NULL; bool exact = (remove == FIND); - list_for_each_entry(t, &pktgen_threads, th_list) { + list_for_each_entry(t, &pn->pktgen_threads, th_list) { pkt_dev = pktgen_find_dev(t, ifname, exact); if (pkt_dev) { if (remove) { @@ -1848,7 +1858,7 @@ static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove) /* * mark a device for removal */ -static void pktgen_mark_device(const char *ifname) +static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname) { struct pktgen_dev *pkt_dev = NULL; const int max_tries = 10, msec_per_try = 125; @@ -1859,7 +1869,7 @@ static void pktgen_mark_device(const char *ifname) while (1) { - pkt_dev = __pktgen_NN_threads(ifname, REMOVE); + pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE); if (pkt_dev == NULL) break; /* success */ @@ -1880,21 +1890,21 @@ static void pktgen_mark_device(const char *ifname) mutex_unlock(&pktgen_thread_lock); } -static void pktgen_change_name(struct net_device *dev) +static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev) { struct pktgen_thread *t; - list_for_each_entry(t, &pktgen_threads, th_list) { + list_for_each_entry(t, &pn->pktgen_threads, th_list) { struct pktgen_dev *pkt_dev; list_for_each_entry(pkt_dev, &t->if_list, list) { if (pkt_dev->odev != dev) continue; - remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); + remove_proc_entry(pkt_dev->entry->name, pn->proc_dir); pkt_dev->entry = proc_create_data(dev->name, 0600, - pg_proc_dir, + pn->proc_dir, &pktgen_if_fops, pkt_dev); if (!pkt_dev->entry) @@ -1909,8 +1919,9 @@ static int pktgen_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *dev = ptr; + struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id); - if (!net_eq(dev_net(dev), &init_net) || pktgen_exiting) + if (pn->pktgen_exiting) return NOTIFY_DONE; /* It is OK that we do not hold the group lock right now, @@ -1919,18 +1930,19 @@ static int pktgen_device_event(struct notifier_block *unused, switch (event) { case NETDEV_CHANGENAME: - pktgen_change_name(dev); + pktgen_change_name(pn, dev); break; case NETDEV_UNREGISTER: - pktgen_mark_device(dev->name); + pktgen_mark_device(pn, dev->name); break; } return NOTIFY_DONE; } -static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, +static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn, + struct pktgen_dev *pkt_dev, const char *ifname) { char b[IFNAMSIZ+5]; @@ -1944,13 +1956,14 @@ static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, } b[i] = 0; - return dev_get_by_name(&init_net, b); + return dev_get_by_name(pn->net, b); } /* Associate pktgen_dev with a device. */ -static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname) +static int pktgen_setup_dev(const struct pktgen_net *pn, + struct pktgen_dev *pkt_dev, const char *ifname) { struct net_device *odev; int err; @@ -1961,7 +1974,7 @@ static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname) pkt_dev->odev = NULL; } - odev = pktgen_dev_get_by_name(pkt_dev, ifname); + odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname); if (!odev) { pr_err("no such netdevice: \"%s\"\n", ifname); return -ENODEV; @@ -2203,9 +2216,10 @@ static inline int f_pick(struct pktgen_dev *pkt_dev) static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) { struct xfrm_state *x = pkt_dev->flows[flow].x; + struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id); if (!x) { /*slow path: we dont already have xfrm_state*/ - x = xfrm_stateonly_find(&init_net, DUMMY_MARK, + x = xfrm_stateonly_find(pn->net, DUMMY_MARK, (xfrm_address_t *)&pkt_dev->cur_daddr, (xfrm_address_t *)&pkt_dev->cur_saddr, AF_INET, @@ -2912,7 +2926,7 @@ static void pktgen_run(struct pktgen_thread *t) t->control &= ~(T_STOP); } -static void pktgen_stop_all_threads_ifs(void) +static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn) { struct pktgen_thread *t; @@ -2920,7 +2934,7 @@ static void pktgen_stop_all_threads_ifs(void) mutex_lock(&pktgen_thread_lock); - list_for_each_entry(t, &pktgen_threads, th_list) + list_for_each_entry(t, &pn->pktgen_threads, th_list) t->control |= T_STOP; mutex_unlock(&pktgen_thread_lock); @@ -2956,28 +2970,28 @@ signal: return 0; } -static int pktgen_wait_all_threads_run(void) +static int pktgen_wait_all_threads_run(struct pktgen_net *pn) { struct pktgen_thread *t; int sig = 1; mutex_lock(&pktgen_thread_lock); - list_for_each_entry(t, &pktgen_threads, th_list) { + list_for_each_entry(t, &pn->pktgen_threads, th_list) { sig = pktgen_wait_thread_run(t); if (sig == 0) break; } if (sig == 0) - list_for_each_entry(t, &pktgen_threads, th_list) + list_for_each_entry(t, &pn->pktgen_threads, th_list) t->control |= (T_STOP); mutex_unlock(&pktgen_thread_lock); return sig; } -static void pktgen_run_all_threads(void) +static void pktgen_run_all_threads(struct pktgen_net *pn) { struct pktgen_thread *t; @@ -2985,7 +2999,7 @@ static void pktgen_run_all_threads(void) mutex_lock(&pktgen_thread_lock); - list_for_each_entry(t, &pktgen_threads, th_list) + list_for_each_entry(t, &pn->pktgen_threads, th_list) t->control |= (T_RUN); mutex_unlock(&pktgen_thread_lock); @@ -2993,10 +3007,10 @@ static void pktgen_run_all_threads(void) /* Propagate thread->control */ schedule_timeout_interruptible(msecs_to_jiffies(125)); - pktgen_wait_all_threads_run(); + pktgen_wait_all_threads_run(pn); } -static void pktgen_reset_all_threads(void) +static void pktgen_reset_all_threads(struct pktgen_net *pn) { struct pktgen_thread *t; @@ -3004,7 +3018,7 @@ static void pktgen_reset_all_threads(void) mutex_lock(&pktgen_thread_lock); - list_for_each_entry(t, &pktgen_threads, th_list) + list_for_each_entry(t, &pn->pktgen_threads, th_list) t->control |= (T_REMDEVALL); mutex_unlock(&pktgen_thread_lock); @@ -3012,7 +3026,7 @@ static void pktgen_reset_all_threads(void) /* Propagate thread->control */ schedule_timeout_interruptible(msecs_to_jiffies(125)); - pktgen_wait_all_threads_run(); + pktgen_wait_all_threads_run(pn); } static void show_results(struct pktgen_dev *pkt_dev, int nr_frags) @@ -3154,9 +3168,7 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t) static void pktgen_rem_thread(struct pktgen_thread *t) { /* Remove from the thread list */ - - remove_proc_entry(t->tsk->comm, pg_proc_dir); - + remove_proc_entry(t->tsk->comm, t->net->proc_dir); } static void pktgen_resched(struct pktgen_dev *pkt_dev) @@ -3302,7 +3314,7 @@ static int pktgen_thread_worker(void *arg) pkt_dev = next_to_run(t); if (unlikely(!pkt_dev && t->control == 0)) { - if (pktgen_exiting) + if (t->net->pktgen_exiting) break; wait_event_interruptible_timeout(t->queue, t->control != 0, @@ -3424,7 +3436,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) /* We don't allow a device to be on several threads */ - pkt_dev = __pktgen_NN_threads(ifname, FIND); + pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND); if (pkt_dev) { pr_err("ERROR: interface already used\n"); return -EBUSY; @@ -3459,13 +3471,13 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) pkt_dev->svlan_id = 0xffff; pkt_dev->node = -1; - err = pktgen_setup_dev(pkt_dev, ifname); + err = pktgen_setup_dev(t->net, pkt_dev, ifname); if (err) goto out1; if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING) pkt_dev->clone_skb = pg_clone_skb_d; - pkt_dev->entry = proc_create_data(ifname, 0600, pg_proc_dir, + pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir, &pktgen_if_fops, pkt_dev); if (!pkt_dev->entry) { pr_err("cannot create %s/%s procfs entry\n", @@ -3490,7 +3502,7 @@ out1: return err; } -static int __init pktgen_create_thread(int cpu) +static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn) { struct pktgen_thread *t; struct proc_dir_entry *pe; @@ -3508,7 +3520,7 @@ static int __init pktgen_create_thread(int cpu) INIT_LIST_HEAD(&t->if_list); - list_add_tail(&t->th_list, &pktgen_threads); + list_add_tail(&t->th_list, &pn->pktgen_threads); init_completion(&t->start_done); p = kthread_create_on_node(pktgen_thread_worker, @@ -3524,7 +3536,7 @@ static int __init pktgen_create_thread(int cpu) kthread_bind(p, cpu); t->tsk = p; - pe = proc_create_data(t->tsk->comm, 0600, pg_proc_dir, + pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir, &pktgen_thread_fops, t); if (!pe) { pr_err("cannot create %s/%s procfs entry\n", @@ -3535,6 +3547,7 @@ static int __init pktgen_create_thread(int cpu) return -EINVAL; } + t->net = pn; wake_up_process(p); wait_for_completion(&t->start_done); @@ -3560,6 +3573,7 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *pkt_dev) { + struct pktgen_net *pn = t->net; pr_debug("remove_device pkt_dev=%p\n", pkt_dev); @@ -3580,7 +3594,7 @@ static int pktgen_remove_device(struct pktgen_thread *t, _rem_dev_from_if_list(t, pkt_dev); if (pkt_dev->entry) - remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); + remove_proc_entry(pkt_dev->entry->name, pn->proc_dir); #ifdef CONFIG_XFRM free_SAs(pkt_dev); @@ -3592,63 +3606,63 @@ static int pktgen_remove_device(struct pktgen_thread *t, return 0; } -static int __init pg_init(void) +static int __net_init pg_net_init(struct net *net) { - int cpu; + struct pktgen_net *pn = net_generic(net, pg_net_id); struct proc_dir_entry *pe; - int ret = 0; - - pr_info("%s", version); - - pg_proc_dir = proc_mkdir(PG_PROC_DIR, init_net.proc_net); - if (!pg_proc_dir) + int cpu, ret = 0; + + pn->net = net; + INIT_LIST_HEAD(&pn->pktgen_threads); + pn->pktgen_exiting = false; + pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net); + if (!pn->proc_dir) { + pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR); return -ENODEV; - - pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops); + } + pe = proc_create(PGCTRL, 0600, pn->proc_dir, &pktgen_fops); if (pe == NULL) { - pr_err("ERROR: cannot create %s procfs entry\n", PGCTRL); + pr_err("cannot create %s procfs entry\n", PGCTRL); ret = -EINVAL; - goto remove_dir; + goto remove; } - register_netdevice_notifier(&pktgen_notifier_block); - for_each_online_cpu(cpu) { int err; - err = pktgen_create_thread(cpu); + err = pktgen_create_thread(cpu, pn); if (err) - pr_warning("WARNING: Cannot create thread for cpu %d (%d)\n", + pr_warn("Cannot create thread for cpu %d (%d)\n", cpu, err); } - if (list_empty(&pktgen_threads)) { - pr_err("ERROR: Initialization failed for all threads\n"); + if (list_empty(&pn->pktgen_threads)) { + pr_err("Initialization failed for all threads\n"); ret = -ENODEV; - goto unregister; + goto remove_entry; } return 0; - unregister: - unregister_netdevice_notifier(&pktgen_notifier_block); - remove_proc_entry(PGCTRL, pg_proc_dir); - remove_dir: - proc_net_remove(&init_net, PG_PROC_DIR); +remove_entry: + remove_proc_entry(PGCTRL, pn->proc_dir); +remove: + proc_net_remove(pn->net, PG_PROC_DIR); return ret; } -static void __exit pg_cleanup(void) +static void __net_exit pg_net_exit(struct net *net) { + struct pktgen_net *pn = net_generic(net, pg_net_id); struct pktgen_thread *t; struct list_head *q, *n; LIST_HEAD(list); /* Stop all interfaces & threads */ - pktgen_exiting = true; + pn->pktgen_exiting = true; mutex_lock(&pktgen_thread_lock); - list_splice_init(&pktgen_threads, &list); + list_splice_init(&pn->pktgen_threads, &list); mutex_unlock(&pktgen_thread_lock); list_for_each_safe(q, n, &list) { @@ -3658,12 +3672,36 @@ static void __exit pg_cleanup(void) kfree(t); } - /* Un-register us from receiving netdevice events */ - unregister_netdevice_notifier(&pktgen_notifier_block); + remove_proc_entry(PGCTRL, pn->proc_dir); + proc_net_remove(pn->net, PG_PROC_DIR); +} + +static struct pernet_operations pg_net_ops = { + .init = pg_net_init, + .exit = pg_net_exit, + .id = &pg_net_id, + .size = sizeof(struct pktgen_net), +}; + +static int __init pg_init(void) +{ + int ret = 0; - /* Clean up proc file system */ - remove_proc_entry(PGCTRL, pg_proc_dir); - proc_net_remove(&init_net, PG_PROC_DIR); + pr_info("%s", version); + ret = register_pernet_subsys(&pg_net_ops); + if (ret) + return ret; + ret = register_netdevice_notifier(&pktgen_notifier_block); + if (ret) + unregister_pernet_subsys(&pg_net_ops); + + return ret; +} + +static void __exit pg_cleanup(void) +{ + unregister_netdevice_notifier(&pktgen_notifier_block); + unregister_pernet_subsys(&pg_net_ops); } module_init(pg_init); -- cgit v1.2.3 From 243bb4c6d731f886ac7a99b986e46625f0d5ba07 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 02:16:18 +0000 Subject: ipv6: Fix inet6_csk_bind_conflict so it builds with user namespaces enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When attempting to build linux-next with user namespaces enabled I ran into this fun build error. CC net/ipv6/inet6_connection_sock.o .../net/ipv6/inet6_connection_sock.c: In function ‘inet6_csk_bind_conflict’: .../net/ipv6/inet6_connection_sock.c:37:12: error: incompatible types when initializing type ‘int’ using type ‘kuid_t’ .../net/ipv6/inet6_connection_sock.c:54:30: error: incompatible type for argument 1 of ‘uid_eq’ .../include/linux/uidgid.h:48:20: note: expected ‘kuid_t’ but argument is of type ‘int’ make[3]: *** [net/ipv6/inet6_connection_sock.o] Error 1 make[2]: *** [net/ipv6] Error 2 make[2]: *** Waiting for unfinished jobs.... Using kuid_t instead of int to hold the uid fixes this. Cc: Tom Herbert Signed-off-by: "Eric W. Biederman" Signed-off-by: David S. Miller --- net/ipv6/inet6_connection_sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index e4297a393678..b386a2ce4c6f 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -34,7 +34,7 @@ int inet6_csk_bind_conflict(const struct sock *sk, const struct hlist_node *node; int reuse = sk->sk_reuse; int reuseport = sk->sk_reuseport; - int uid = sock_i_uid((struct sock *)sk); + kuid_t uid = sock_i_uid((struct sock *)sk); /* We must walk the whole port owner list in this case. -DaveM */ /* -- cgit v1.2.3 From 218774dc341f219bfcf940304a081b121a0e8099 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Tue, 29 Jan 2013 08:24:25 +0000 Subject: ipv6: add anti-spoofing checks for 6to4 and 6rd This patch adds anti-spoofing checks in sit.c as specified in RFC3964 section 5.2 for 6to4 and RFC5969 section 12 for 6rd. I left out the checks which could easily be implemented with netfilter. Specifically this patch adds following logic (based loosely on the pseudocode in RFC3964 section 5.2): if prefix (inner_src_v6) == rd6_prefix (2002::/16 is the default) and outer_src_v4 != embedded_ipv4 (inner_src_v6) drop if prefix (inner_dst_v6) == rd6_prefix (or 2002::/16 is the default) and outer_dst_v4 != embedded_ipv4 (inner_dst_v6) drop accept To accomplish the specified security checks proposed by above RFCs, it is still necessary to employ uRPF filters with netfilter. These new checks only kick in if the employed addresses are within the 2002::/16 or another range specified by the 6rd-prefix (which defaults to 2002::/16). Cc: YOSHIFUJI Hideaki Cc: David Miller Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/sit.c | 54 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 98fe53694a65..02f96dcbcf02 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -72,6 +72,8 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); static int ipip6_tunnel_init(struct net_device *dev); static void ipip6_tunnel_setup(struct net_device *dev); static void ipip6_dev_free(struct net_device *dev); +static bool check_6rd(struct ip_tunnel *tunnel, const struct in6_addr *v6dst, + __be32 *v4dst); static struct rtnl_link_ops sit_link_ops __read_mostly; static int sit_net_id __read_mostly; @@ -590,6 +592,15 @@ out: return err; } +static inline bool is_spoofed_6rd(struct ip_tunnel *tunnel, const __be32 v4addr, + const struct in6_addr *v6addr) +{ + __be32 v4embed = 0; + if (check_6rd(tunnel, v6addr, &v4embed) && v4addr != v4embed) + return true; + return false; +} + static int ipip6_rcv(struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); @@ -608,10 +619,19 @@ static int ipip6_rcv(struct sk_buff *skb) skb->protocol = htons(ETH_P_IPV6); skb->pkt_type = PACKET_HOST; - if ((tunnel->dev->priv_flags & IFF_ISATAP) && - !isatap_chksrc(skb, iph, tunnel)) { - tunnel->dev->stats.rx_errors++; - goto out; + if (tunnel->dev->priv_flags & IFF_ISATAP) { + if (!isatap_chksrc(skb, iph, tunnel)) { + tunnel->dev->stats.rx_errors++; + goto out; + } + } else { + if (is_spoofed_6rd(tunnel, iph->saddr, + &ipv6_hdr(skb)->saddr) || + is_spoofed_6rd(tunnel, iph->daddr, + &ipv6_hdr(skb)->daddr)) { + tunnel->dev->stats.rx_errors++; + goto out; + } } __skb_tunnel_rx(skb, tunnel->dev); @@ -645,14 +665,12 @@ out: } /* - * Returns the embedded IPv4 address if the IPv6 address - * comes from 6rd / 6to4 (RFC 3056) addr space. + * If the IPv6 address comes from 6rd / 6to4 (RFC 3056) addr space this function + * stores the embedded IPv4 address in v4dst and returns true. */ -static inline -__be32 try_6rd(const struct in6_addr *v6dst, struct ip_tunnel *tunnel) +static bool check_6rd(struct ip_tunnel *tunnel, const struct in6_addr *v6dst, + __be32 *v4dst) { - __be32 dst = 0; - #ifdef CONFIG_IPV6_SIT_6RD if (ipv6_prefix_equal(v6dst, &tunnel->ip6rd.prefix, tunnel->ip6rd.prefixlen)) { @@ -671,14 +689,24 @@ __be32 try_6rd(const struct in6_addr *v6dst, struct ip_tunnel *tunnel) d |= ntohl(v6dst->s6_addr32[pbw0 + 1]) >> (32 - pbi1); - dst = tunnel->ip6rd.relay_prefix | htonl(d); + *v4dst = tunnel->ip6rd.relay_prefix | htonl(d); + return true; } #else if (v6dst->s6_addr16[0] == htons(0x2002)) { /* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */ - memcpy(&dst, &v6dst->s6_addr16[1], 4); + memcpy(v4dst, &v6dst->s6_addr16[1], 4); + return true; } #endif + return false; +} + +static inline __be32 try_6rd(struct ip_tunnel *tunnel, + const struct in6_addr *v6dst) +{ + __be32 dst = 0; + check_6rd(tunnel, v6dst, &dst); return dst; } @@ -739,7 +767,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, } if (!dst) - dst = try_6rd(&iph6->daddr, tunnel); + dst = try_6rd(tunnel, &iph6->daddr); if (!dst) { struct neighbour *neigh = NULL; -- cgit v1.2.3 From 07c2fecc363c8f94a0bd2fdf9b7209ec6f5a0ab5 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 29 Jan 2013 12:48:23 +0000 Subject: ipv6 mcast: Use ipv6_addr_equal() in ip6_mc_source(). Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/mcast.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index f25002aaf624..8460a1f54a76 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -376,8 +376,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, goto done; /* err = -EADDRNOTAVAIL */ rv = !0; for (i=0; isl_count; i++) { - rv = memcmp(&psl->sl_addr[i], source, - sizeof(struct in6_addr)); + rv = !ipv6_addr_equal(&psl->sl_addr[i], source); if (rv == 0) break; } @@ -427,7 +426,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk, } rv = 1; /* > 0 for insert logic below if sl_count is 0 */ for (i=0; isl_count; i++) { - rv = memcmp(&psl->sl_addr[i], source, sizeof(struct in6_addr)); + rv = !ipv6_addr_equal(&psl->sl_addr[i], source); if (rv == 0) break; } -- cgit v1.2.3 From ff88b30c717f21dffba6784cae4d3287da16f6ef Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 29 Jan 2013 12:48:31 +0000 Subject: xfrm: Use ipv6_addr_equal() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/xfrm.h | 13 ++++++++++--- net/ipv6/xfrm6_tunnel.c | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 421f764794d5..0d8a797f0441 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1032,7 +1032,7 @@ static inline int __xfrm6_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x) { return (!ipv6_addr_any((struct in6_addr*)&tmpl->saddr) && - ipv6_addr_cmp((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr)); + !ipv6_addr_equal((struct in6_addr *)&tmpl->saddr, (struct in6_addr*)&x->props.saddr)); } static inline int @@ -1243,8 +1243,8 @@ static __inline__ int __xfrm6_state_addr_check(const struct xfrm_state *x, const xfrm_address_t *daddr, const xfrm_address_t *saddr) { - if (!ipv6_addr_cmp((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) && - (!ipv6_addr_cmp((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr)|| + if (ipv6_addr_equal((struct in6_addr *)daddr, (struct in6_addr *)&x->id.daddr) && + (ipv6_addr_equal((struct in6_addr *)saddr, (struct in6_addr *)&x->props.saddr) || ipv6_addr_any((struct in6_addr *)saddr) || ipv6_addr_any((struct in6_addr *)&x->props.saddr))) return 1; @@ -1588,6 +1588,13 @@ static inline int xfrm_addr_cmp(const xfrm_address_t *a, } } +static inline bool xfrm6_addr_equal(const xfrm_address_t *a, + const xfrm_address_t *b) +{ + return ipv6_addr_equal((const struct in6_addr *)a, + (const struct in6_addr *)b); +} + static inline int xfrm_policy_id2dir(u32 index) { return index & 7; diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index babd1674388a..6cc48012b730 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -94,7 +94,7 @@ static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, const hlist_for_each_entry_rcu(x6spi, pos, &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], list_byaddr) { - if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) + if (xfrm6_addr_equal(&x6spi->addr, saddr)) return x6spi; } @@ -211,7 +211,7 @@ static void xfrm6_tunnel_free_spi(struct net *net, xfrm_address_t *saddr) &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], list_byaddr) { - if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { + if (xfrm6_addr_equal(&x6spi->addr, saddr)) { if (atomic_dec_and_test(&x6spi->refcnt)) { hlist_del_rcu(&x6spi->list_byaddr); hlist_del_rcu(&x6spi->list_byspi); -- cgit v1.2.3 From 70e94e66aec255aff276397f5ed3f3626c548f1c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 29 Jan 2013 12:48:50 +0000 Subject: xfrm: Convert xfrm_addr_cmp() to boolean xfrm_addr_equal(). All users of xfrm_addr_cmp() use its result as boolean. Introduce xfrm_addr_equal() (which is equal to !xfrm_addr_cmp()) and convert all users. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/xfrm.h | 25 ++++++++++++------------- net/key/af_key.c | 8 ++++---- net/xfrm/xfrm_policy.c | 24 ++++++++++++------------ net/xfrm/xfrm_state.c | 34 +++++++++++++++++----------------- net/xfrm/xfrm_user.c | 2 +- 5 files changed, 46 insertions(+), 47 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 0d8a797f0441..de34883e8b16 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1574,27 +1574,26 @@ extern struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe); extern struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, int probe); -static inline int xfrm_addr_cmp(const xfrm_address_t *a, - const xfrm_address_t *b, - int family) +static inline bool xfrm6_addr_equal(const xfrm_address_t *a, + const xfrm_address_t *b) +{ + return ipv6_addr_equal((const struct in6_addr *)a, + (const struct in6_addr *)b); +} + +static inline bool xfrm_addr_equal(const xfrm_address_t *a, + const xfrm_address_t *b, + sa_family_t family) { switch (family) { default: case AF_INET: - return (__force u32)a->a4 - (__force u32)b->a4; + return ((__force u32)a->a4 ^ (__force u32)b->a4) == 0; case AF_INET6: - return ipv6_addr_cmp((const struct in6_addr *)a, - (const struct in6_addr *)b); + return xfrm6_addr_equal(a, b); } } -static inline bool xfrm6_addr_equal(const xfrm_address_t *a, - const xfrm_address_t *b) -{ - return ipv6_addr_equal((const struct in6_addr *)a, - (const struct in6_addr *)b); -} - static inline int xfrm_policy_id2dir(u32 index) { return index & 7; diff --git a/net/key/af_key.c b/net/key/af_key.c index 5b426a646544..cc2630ac8061 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -762,7 +762,7 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, } /* identity & sensitivity */ - if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr, x->props.family)) + if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, x->props.family)) size += sizeof(struct sadb_address) + sockaddr_size; if (add_keys) { @@ -909,8 +909,8 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, if (!addr->sadb_address_prefixlen) BUG(); - if (xfrm_addr_cmp(&x->sel.saddr, &x->props.saddr, - x->props.family)) { + if (!xfrm_addr_equal(&x->sel.saddr, &x->props.saddr, + x->props.family)) { addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = @@ -1321,7 +1321,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_ if (hdr->sadb_msg_seq) { x = xfrm_find_acq_byseq(net, DUMMY_MARK, hdr->sadb_msg_seq); - if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) { + if (x && !xfrm_addr_equal(&x->id.daddr, xdaddr, family)) { xfrm_state_put(x); x = NULL; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 07c585756d2a..6c9aa642a2ba 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2786,10 +2786,10 @@ static bool xfrm_migrate_selector_match(const struct xfrm_selector *sel_cmp, { if (sel_cmp->proto == IPSEC_ULPROTO_ANY) { if (sel_tgt->family == sel_cmp->family && - xfrm_addr_cmp(&sel_tgt->daddr, &sel_cmp->daddr, - sel_cmp->family) == 0 && - xfrm_addr_cmp(&sel_tgt->saddr, &sel_cmp->saddr, - sel_cmp->family) == 0 && + xfrm_addr_equal(&sel_tgt->daddr, &sel_cmp->daddr, + sel_cmp->family) && + xfrm_addr_equal(&sel_tgt->saddr, &sel_cmp->saddr, + sel_cmp->family) && sel_tgt->prefixlen_d == sel_cmp->prefixlen_d && sel_tgt->prefixlen_s == sel_cmp->prefixlen_s) { return true; @@ -2847,10 +2847,10 @@ static int migrate_tmpl_match(const struct xfrm_migrate *m, const struct xfrm_tm switch (t->mode) { case XFRM_MODE_TUNNEL: case XFRM_MODE_BEET: - if (xfrm_addr_cmp(&t->id.daddr, &m->old_daddr, - m->old_family) == 0 && - xfrm_addr_cmp(&t->saddr, &m->old_saddr, - m->old_family) == 0) { + if (xfrm_addr_equal(&t->id.daddr, &m->old_daddr, + m->old_family) && + xfrm_addr_equal(&t->saddr, &m->old_saddr, + m->old_family)) { match = 1; } break; @@ -2916,10 +2916,10 @@ static int xfrm_migrate_check(const struct xfrm_migrate *m, int num_migrate) return -EINVAL; for (i = 0; i < num_migrate; i++) { - if ((xfrm_addr_cmp(&m[i].old_daddr, &m[i].new_daddr, - m[i].old_family) == 0) && - (xfrm_addr_cmp(&m[i].old_saddr, &m[i].new_saddr, - m[i].old_family) == 0)) + if (xfrm_addr_equal(&m[i].old_daddr, &m[i].new_daddr, + m[i].old_family) && + xfrm_addr_equal(&m[i].old_saddr, &m[i].new_saddr, + m[i].old_family)) return -EINVAL; if (xfrm_addr_any(&m[i].new_daddr, m[i].new_family) || xfrm_addr_any(&m[i].new_saddr, m[i].new_family)) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 0adae918a7a2..ae01bdbcb294 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -691,7 +691,7 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, if (x->props.family != family || x->id.spi != spi || x->id.proto != proto || - xfrm_addr_cmp(&x->id.daddr, daddr, family)) + !xfrm_addr_equal(&x->id.daddr, daddr, family)) continue; if ((mark & x->mark.m) != x->mark.v) @@ -715,8 +715,8 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark, hlist_for_each_entry(x, entry, net->xfrm.state_bysrc+h, bysrc) { if (x->props.family != family || x->id.proto != proto || - xfrm_addr_cmp(&x->id.daddr, daddr, family) || - xfrm_addr_cmp(&x->props.saddr, saddr, family)) + !xfrm_addr_equal(&x->id.daddr, daddr, family) || + !xfrm_addr_equal(&x->props.saddr, saddr, family)) continue; if ((mark & x->mark.m) != x->mark.v) @@ -981,8 +981,8 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew) if (x->props.family == family && x->props.reqid == reqid && (mark & x->mark.m) == x->mark.v && - !xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) && - !xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family)) + xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) && + xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family)) x->genid++; } } @@ -1016,8 +1016,8 @@ static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m, x->id.spi != 0 || x->id.proto != proto || (mark & x->mark.m) != x->mark.v || - xfrm_addr_cmp(&x->id.daddr, daddr, family) || - xfrm_addr_cmp(&x->props.saddr, saddr, family)) + !xfrm_addr_equal(&x->id.daddr, daddr, family) || + !xfrm_addr_equal(&x->props.saddr, saddr, family)) continue; xfrm_state_hold(x); @@ -1100,7 +1100,7 @@ int xfrm_state_add(struct xfrm_state *x) if (use_spi && x->km.seq) { x1 = __xfrm_find_acq_byseq(net, mark, x->km.seq); if (x1 && ((x1->id.proto != x->id.proto) || - xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) { + !xfrm_addr_equal(&x1->id.daddr, &x->id.daddr, family))) { to_put = x1; x1 = NULL; } @@ -1226,10 +1226,10 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) continue; if (m->reqid && x->props.reqid != m->reqid) continue; - if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr, - m->old_family) || - xfrm_addr_cmp(&x->props.saddr, &m->old_saddr, - m->old_family)) + if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, + m->old_family) || + !xfrm_addr_equal(&x->props.saddr, &m->old_saddr, + m->old_family)) continue; xfrm_state_hold(x); return x; @@ -1241,10 +1241,10 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m) if (x->props.mode != m->mode || x->id.proto != m->proto) continue; - if (xfrm_addr_cmp(&x->id.daddr, &m->old_daddr, - m->old_family) || - xfrm_addr_cmp(&x->props.saddr, &m->old_saddr, - m->old_family)) + if (!xfrm_addr_equal(&x->id.daddr, &m->old_daddr, + m->old_family) || + !xfrm_addr_equal(&x->props.saddr, &m->old_saddr, + m->old_family)) continue; xfrm_state_hold(x); return x; @@ -1269,7 +1269,7 @@ struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, memcpy(&xc->props.saddr, &m->new_saddr, sizeof(xc->props.saddr)); /* add state */ - if (!xfrm_addr_cmp(&x->id.daddr, &m->new_daddr, m->new_family)) { + if (xfrm_addr_equal(&x->id.daddr, &m->new_daddr, m->new_family)) { /* a care is needed when the destination address of the state is to be updated as it is a part of triplet */ xfrm_state_insert(xc); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index eb872b2e366e..fbd9e6cd0fd7 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1112,7 +1112,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, mark = xfrm_mark_get(attrs, &m); if (p->info.seq) { x = xfrm_find_acq_byseq(net, mark, p->info.seq); - if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { + if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) { xfrm_state_put(x); x = NULL; } -- cgit v1.2.3 From d9e85655b5c9fcebf81aedc69182efbfdf4fe740 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 29 Jan 2013 12:48:58 +0000 Subject: netfilter ip6table_mangle: Use ipv6_addr_equal() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/netfilter/ip6table_mangle.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 7431121b87de..6134a1ebfb1b 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -11,6 +11,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); @@ -60,8 +61,8 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out) dev_net(out)->ipv6.ip6table_mangle); if (ret != NF_DROP && ret != NF_STOLEN && - (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || - memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || + (!ipv6_addr_equal(&ipv6_hdr(skb)->saddr, &saddr) || + !ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) || skb->mark != mark || ipv6_hdr(skb)->hop_limit != hop_limit || flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) -- cgit v1.2.3 From 29e3b1608c8dca3ae4224a26862d18ea003ccee6 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 29 Jan 2013 12:49:03 +0000 Subject: netfilter ipset: Use ipv6_addr_equal() where appropriate. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/netfilter/ipset/ip_set_hash_ip.c | 2 +- net/netfilter/ipset/ip_set_hash_ipport.c | 2 +- net/netfilter/ipset/ip_set_hash_ipportip.c | 4 ++-- net/netfilter/ipset/ip_set_hash_ipportnet.c | 4 ++-- net/netfilter/ipset/ip_set_hash_net.c | 2 +- net/netfilter/ipset/ip_set_hash_netiface.c | 2 +- net/netfilter/ipset/ip_set_hash_netport.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 5c0b78528e55..b7d4cb475ae6 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -234,7 +234,7 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1, const struct hash_ip6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6); } static inline bool diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 6283351f4eeb..d8f77bacae86 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -284,7 +284,7 @@ hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, const struct hash_ipport6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && ip1->port == ip2->port && ip1->proto == ip2->proto; } diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 6a21271c8d5a..1da1e955f38b 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -294,8 +294,8 @@ hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, const struct hash_ipportip6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && - ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && + ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && ip1->port == ip2->port && ip1->proto == ip2->proto; } diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 2d5cd4ee30eb..f2627226a087 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -388,8 +388,8 @@ hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, const struct hash_ipportnet6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && - ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && + ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && ip1->cidr == ip2->cidr && ip1->port == ip2->port && ip1->proto == ip2->proto; diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 29e94b981f3f..4b677cf6bf7d 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -286,7 +286,7 @@ hash_net6_data_equal(const struct hash_net6_elem *ip1, const struct hash_net6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && ip1->cidr == ip2->cidr; } diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 45a101439bc5..6ba985f1c96f 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -471,7 +471,7 @@ hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, const struct hash_netiface6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && ip1->cidr == ip2->cidr && (++*multi) && ip1->physdev == ip2->physdev && diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 7ef700de596c..af20c0c5ced2 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -350,7 +350,7 @@ hash_netport6_data_equal(const struct hash_netport6_elem *ip1, const struct hash_netport6_elem *ip2, u32 *multi) { - return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && + return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && ip1->port == ip2->port && ip1->proto == ip2->proto && ip1->cidr == ip2->cidr; -- cgit v1.2.3 From d2ed273d30c5ffd14f6b5ec7ecc751d960f832fc Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Tue, 29 Jan 2013 15:14:16 +0000 Subject: net: disallow drivers with buggy VLAN accel to register_netdevice() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of jumping aroung bugs that are easily fixed just don't let them in: affected drivers should be either fixed or have NETIF_F_HW_VLAN_FILTER removed from advertised features. Quick grep in drivers/net shows two drivers that have NETIF_F_HW_VLAN_FILTER but not ndo_vlan_rx_add/kill_vid(), but those are false-positives (features are commented out). OTOH two drivers have ndo_vlan_rx_add/kill_vid() implemented but don't advertise NETIF_F_HW_VLAN_FILTER. Those are: +ethernet/cisco/enic/enic_main.c +ethernet/qlogic/qlcnic/qlcnic_main.c Signed-off-by: Michał Mirosław Signed-off-by: David S. Miller --- net/8021q/vlan.c | 7 ------- net/8021q/vlan_core.c | 6 ++---- net/core/dev.c | 8 ++++++++ 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index babfde9f734c..addc578d5443 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -117,19 +117,12 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) { const char *name = real_dev->name; - const struct net_device_ops *ops = real_dev->netdev_ops; if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { pr_info("VLANs not supported on %s\n", name); return -EOPNOTSUPP; } - if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) && - (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid)) { - pr_info("Device %s has buggy VLAN hw accel\n", name); - return -EOPNOTSUPP; - } - if (vlan_find_dev(real_dev, vlan_id) != NULL) return -EEXIST; diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 380440b8ea89..71b64fde8dc9 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -224,8 +224,7 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid, if (!vid_info) return -ENOMEM; - if ((dev->features & NETIF_F_HW_VLAN_FILTER) && - ops->ndo_vlan_rx_add_vid) { + if (dev->features & NETIF_F_HW_VLAN_FILTER) { err = ops->ndo_vlan_rx_add_vid(dev, vid); if (err) { kfree(vid_info); @@ -282,8 +281,7 @@ static void __vlan_vid_del(struct vlan_info *vlan_info, unsigned short vid = vid_info->vid; int err; - if ((dev->features & NETIF_F_HW_VLAN_FILTER) && - ops->ndo_vlan_rx_kill_vid) { + if (dev->features & NETIF_F_HW_VLAN_FILTER) { err = ops->ndo_vlan_rx_kill_vid(dev, vid); if (err) { pr_warn("failed to kill vid %d for device %s\n", diff --git a/net/core/dev.c b/net/core/dev.c index a83375d3af72..a87bc74e9fd0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6054,6 +6054,14 @@ int register_netdevice(struct net_device *dev) } } + if (((dev->hw_features | dev->features) & NETIF_F_HW_VLAN_FILTER) && + (!dev->netdev_ops->ndo_vlan_rx_add_vid || + !dev->netdev_ops->ndo_vlan_rx_kill_vid)) { + netdev_WARN(dev, "Buggy VLAN acceleration in driver!\n"); + ret = -EINVAL; + goto err_uninit; + } + ret = -EBUSY; if (!dev->ifindex) dev->ifindex = dev_new_index(net); -- cgit v1.2.3 From f256dc59d0729cf7d371b93062375d9bc79c1e44 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 30 Jan 2013 09:26:42 +0000 Subject: ipv6 flowlabel: Ensure to take lock when modifying np->ip6_sk_fl_list. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ip6_flowlabel.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 29124b7a04c8..5d767f1b8780 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -255,10 +255,19 @@ void fl6_free_socklist(struct sock *sk) struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; - while ((sfl = np->ipv6_fl_list) != NULL) { - np->ipv6_fl_list = sfl->next; + if (!np->ipv6_fl_list) + return; + + write_lock_bh(&ipv6_sk_fl_lock); + sfl = np->ipv6_fl_list; + np->ipv6_fl_list = NULL; + write_unlock_bh(&ipv6_sk_fl_lock); + + while (sfl) { + struct ipv6_fl_socklist *next = sfl->next; fl_release(sfl->fl); kfree(sfl); + sfl = next; } } -- cgit v1.2.3 From d3aedd5ebd4b0b925b0bcda548066803e1318499 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 30 Jan 2013 09:27:47 +0000 Subject: ipv6 flowlabel: Convert hash list to RCU. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 1 + net/ipv6/ip6_flowlabel.c | 94 +++++++++++++++++++++++++++--------------------- 2 files changed, 55 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index dc30b60975ef..1d457161def2 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -222,6 +222,7 @@ struct ip6_flowlabel { struct in6_addr dst; struct ipv6_txoptions *opt; unsigned long linger; + struct rcu_head rcu; u8 share; union { struct pid *pid; diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 5d767f1b8780..da156015d827 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -51,25 +51,33 @@ #define FL_HASH(l) (ntohl(l)&FL_HASH_MASK) static atomic_t fl_size = ATOMIC_INIT(0); -static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1]; +static struct ip6_flowlabel __rcu *fl_ht[FL_HASH_MASK+1]; static void ip6_fl_gc(unsigned long dummy); static DEFINE_TIMER(ip6_fl_gc_timer, ip6_fl_gc, 0, 0); /* FL hash table lock: it protects only of GC */ -static DEFINE_RWLOCK(ip6_fl_lock); +static DEFINE_SPINLOCK(ip6_fl_lock); /* Big socket sock */ static DEFINE_RWLOCK(ip6_sk_fl_lock); +#define for_each_fl_rcu(hash, fl) \ + for (fl = rcu_dereference(fl_ht[(hash)]); \ + fl != NULL; \ + fl = rcu_dereference(fl->next)) +#define for_each_fl_continue_rcu(fl) \ + for (fl = rcu_dereference(fl->next); \ + fl != NULL; \ + fl = rcu_dereference(fl->next)) static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label) { struct ip6_flowlabel *fl; - for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) { + for_each_fl_rcu(FL_HASH(label), fl) { if (fl->label == label && net_eq(fl->fl_net, net)) return fl; } @@ -80,11 +88,11 @@ static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) { struct ip6_flowlabel *fl; - read_lock_bh(&ip6_fl_lock); + rcu_read_lock_bh(); fl = __fl_lookup(net, label); - if (fl) - atomic_inc(&fl->users); - read_unlock_bh(&ip6_fl_lock); + if (fl && !atomic_inc_not_zero(&fl->users)) + fl = NULL; + rcu_read_unlock_bh(); return fl; } @@ -96,13 +104,13 @@ static void fl_free(struct ip6_flowlabel *fl) put_pid(fl->owner.pid); release_net(fl->fl_net); kfree(fl->opt); + kfree_rcu(fl, rcu); } - kfree(fl); } static void fl_release(struct ip6_flowlabel *fl) { - write_lock_bh(&ip6_fl_lock); + spin_lock_bh(&ip6_fl_lock); fl->lastuse = jiffies; if (atomic_dec_and_test(&fl->users)) { @@ -119,7 +127,7 @@ static void fl_release(struct ip6_flowlabel *fl) time_after(ip6_fl_gc_timer.expires, ttd)) mod_timer(&ip6_fl_gc_timer, ttd); } - write_unlock_bh(&ip6_fl_lock); + spin_unlock_bh(&ip6_fl_lock); } static void ip6_fl_gc(unsigned long dummy) @@ -128,12 +136,13 @@ static void ip6_fl_gc(unsigned long dummy) unsigned long now = jiffies; unsigned long sched = 0; - write_lock(&ip6_fl_lock); + spin_lock(&ip6_fl_lock); for (i=0; i<=FL_HASH_MASK; i++) { struct ip6_flowlabel *fl, **flp; flp = &fl_ht[i]; - while ((fl=*flp) != NULL) { + while ((fl = rcu_dereference_protected(*flp, + lockdep_is_held(&ip6_fl_lock))) != NULL) { if (atomic_read(&fl->users) == 0) { unsigned long ttd = fl->lastuse + fl->linger; if (time_after(ttd, fl->expires)) @@ -156,18 +165,19 @@ static void ip6_fl_gc(unsigned long dummy) if (sched) { mod_timer(&ip6_fl_gc_timer, sched); } - write_unlock(&ip6_fl_lock); + spin_unlock(&ip6_fl_lock); } static void __net_exit ip6_fl_purge(struct net *net) { int i; - write_lock(&ip6_fl_lock); + spin_lock(&ip6_fl_lock); for (i = 0; i <= FL_HASH_MASK; i++) { struct ip6_flowlabel *fl, **flp; flp = &fl_ht[i]; - while ((fl = *flp) != NULL) { + while ((fl = rcu_dereference_protected(*flp, + lockdep_is_held(&ip6_fl_lock))) != NULL) { if (net_eq(fl->fl_net, net) && atomic_read(&fl->users) == 0) { *flp = fl->next; @@ -178,7 +188,7 @@ static void __net_exit ip6_fl_purge(struct net *net) flp = &fl->next; } } - write_unlock(&ip6_fl_lock); + spin_unlock(&ip6_fl_lock); } static struct ip6_flowlabel *fl_intern(struct net *net, @@ -188,7 +198,7 @@ static struct ip6_flowlabel *fl_intern(struct net *net, fl->label = label & IPV6_FLOWLABEL_MASK; - write_lock_bh(&ip6_fl_lock); + spin_lock_bh(&ip6_fl_lock); if (label == 0) { for (;;) { fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; @@ -210,16 +220,16 @@ static struct ip6_flowlabel *fl_intern(struct net *net, lfl = __fl_lookup(net, fl->label); if (lfl != NULL) { atomic_inc(&lfl->users); - write_unlock_bh(&ip6_fl_lock); + spin_unlock_bh(&ip6_fl_lock); return lfl; } } fl->lastuse = jiffies; fl->next = fl_ht[FL_HASH(fl->label)]; - fl_ht[FL_HASH(fl->label)] = fl; + rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl); atomic_inc(&fl_size); - write_unlock_bh(&ip6_fl_lock); + spin_unlock_bh(&ip6_fl_lock); return NULL; } @@ -650,13 +660,13 @@ static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) struct net *net = seq_file_net(seq); for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { - fl = fl_ht[state->bucket]; - - while (fl && !net_eq(fl->fl_net, net)) - fl = fl->next; - if (fl) - break; + for_each_fl_rcu(state->bucket, fl) { + if (net_eq(fl->fl_net, net)) + goto out; + } } + fl = NULL; +out: return fl; } @@ -665,18 +675,22 @@ static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flo struct ip6fl_iter_state *state = ip6fl_seq_private(seq); struct net *net = seq_file_net(seq); - fl = fl->next; + for_each_fl_continue_rcu(fl) { + if (net_eq(fl->fl_net, net)) + goto out; + } + try_again: - while (fl && !net_eq(fl->fl_net, net)) - fl = fl->next; - - while (!fl) { - if (++state->bucket <= FL_HASH_MASK) { - fl = fl_ht[state->bucket]; - goto try_again; - } else - break; + if (++state->bucket <= FL_HASH_MASK) { + for_each_fl_rcu(state->bucket, fl) { + if (net_eq(fl->fl_net, net)) + goto out; + } + goto try_again; } + fl = NULL; + +out: return fl; } @@ -690,9 +704,9 @@ static struct ip6_flowlabel *ip6fl_get_idx(struct seq_file *seq, loff_t pos) } static void *ip6fl_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(ip6_fl_lock) + __acquires(RCU) { - read_lock_bh(&ip6_fl_lock); + rcu_read_lock_bh(); return *pos ? ip6fl_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; } @@ -709,9 +723,9 @@ static void *ip6fl_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void ip6fl_seq_stop(struct seq_file *seq, void *v) - __releases(ip6_fl_lock) + __releases(RCU) { - read_unlock_bh(&ip6_fl_lock); + rcu_read_unlock_bh(); } static int ip6fl_seq_show(struct seq_file *seq, void *v) -- cgit v1.2.3 From 18367681a10bd29c3f2305e6b7b984de5b33d548 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 30 Jan 2013 09:27:52 +0000 Subject: ipv6 flowlabel: Convert np->ipv6_fl_list to RCU. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/linux/ipv6.h | 2 +- include/net/ipv6.h | 1 + net/ipv6/ip6_flowlabel.c | 72 +++++++++++++++++++++++++++--------------------- 3 files changed, 42 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index e971e3742172..850e95bc766c 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -214,7 +214,7 @@ struct ipv6_pinfo { struct ipv6_mc_socklist __rcu *ipv6_mc_list; struct ipv6_ac_socklist *ipv6_ac_list; - struct ipv6_fl_socklist *ipv6_fl_list; + struct ipv6_fl_socklist __rcu *ipv6_fl_list; struct ipv6_txoptions *opt; struct sk_buff *pktoptions; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 1d457161def2..851d5412a299 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -239,6 +239,7 @@ struct ip6_flowlabel { struct ipv6_fl_socklist { struct ipv6_fl_socklist *next; struct ip6_flowlabel *fl; + struct rcu_head rcu; }; extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index da156015d827..22494afd981c 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -62,7 +62,7 @@ static DEFINE_SPINLOCK(ip6_fl_lock); /* Big socket sock */ -static DEFINE_RWLOCK(ip6_sk_fl_lock); +static DEFINE_SPINLOCK(ip6_sk_fl_lock); #define for_each_fl_rcu(hash, fl) \ for (fl = rcu_dereference(fl_ht[(hash)]); \ @@ -73,6 +73,11 @@ static DEFINE_RWLOCK(ip6_sk_fl_lock); fl != NULL; \ fl = rcu_dereference(fl->next)) +#define for_each_sk_fl_rcu(np, sfl) \ + for (sfl = rcu_dereference_bh(np->ipv6_fl_list); \ + sfl != NULL; \ + sfl = rcu_dereference_bh(sfl->next)) + static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label) { struct ip6_flowlabel *fl; @@ -244,17 +249,17 @@ struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label) label &= IPV6_FLOWLABEL_MASK; - read_lock_bh(&ip6_sk_fl_lock); - for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) { + rcu_read_lock_bh(); + for_each_sk_fl_rcu(np, sfl) { struct ip6_flowlabel *fl = sfl->fl; if (fl->label == label) { fl->lastuse = jiffies; atomic_inc(&fl->users); - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); return fl; } } - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); return NULL; } @@ -265,20 +270,21 @@ void fl6_free_socklist(struct sock *sk) struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_fl_socklist *sfl; - if (!np->ipv6_fl_list) + if (!rcu_access_pointer(np->ipv6_fl_list)) return; - write_lock_bh(&ipv6_sk_fl_lock); - sfl = np->ipv6_fl_list; - np->ipv6_fl_list = NULL; - write_unlock_bh(&ipv6_sk_fl_lock); + spin_lock_bh(&ip6_sk_fl_lock); + while ((sfl = rcu_dereference_protected(np->ipv6_fl_list, + lockdep_is_held(&ip6_sk_fl_lock))) != NULL) { + np->ipv6_fl_list = sfl->next; + spin_unlock_bh(&ip6_sk_fl_lock); - while (sfl) { - struct ipv6_fl_socklist *next = sfl->next; fl_release(sfl->fl); - kfree(sfl); - sfl = next; + kfree_rcu(sfl, rcu); + + spin_lock_bh(&ip6_sk_fl_lock); } + spin_unlock_bh(&ip6_sk_fl_lock); } /* Service routines */ @@ -443,7 +449,7 @@ static int mem_check(struct sock *sk) if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) return 0; - for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) + for_each_sk_fl_rcu(np, sfl) count++; if (room <= 0 || @@ -486,11 +492,11 @@ static bool ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2) static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, struct ip6_flowlabel *fl) { - write_lock_bh(&ip6_sk_fl_lock); + spin_lock_bh(&ip6_sk_fl_lock); sfl->fl = fl; sfl->next = np->ipv6_fl_list; - np->ipv6_fl_list = sfl; - write_unlock_bh(&ip6_sk_fl_lock); + rcu_assign_pointer(np->ipv6_fl_list, sfl); + spin_unlock_bh(&ip6_sk_fl_lock); } int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) @@ -512,31 +518,33 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) switch (freq.flr_action) { case IPV6_FL_A_PUT: - write_lock_bh(&ip6_sk_fl_lock); - for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) { + spin_lock_bh(&ip6_sk_fl_lock); + for (sflp = &np->ipv6_fl_list; + (sfl = rcu_dereference(*sflp))!=NULL; + sflp = &sfl->next) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) np->flow_label &= ~IPV6_FLOWLABEL_MASK; - *sflp = sfl->next; - write_unlock_bh(&ip6_sk_fl_lock); + *sflp = rcu_dereference(sfl->next); + spin_unlock_bh(&ip6_sk_fl_lock); fl_release(sfl->fl); - kfree(sfl); + kfree_rcu(sfl, rcu); return 0; } } - write_unlock_bh(&ip6_sk_fl_lock); + spin_unlock_bh(&ip6_sk_fl_lock); return -ESRCH; case IPV6_FL_A_RENEW: - read_lock_bh(&ip6_sk_fl_lock); - for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { + rcu_read_lock_bh(); + for_each_sk_fl_rcu(np, sfl) { if (sfl->fl->label == freq.flr_label) { err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires); - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); return err; } } - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); if (freq.flr_share == IPV6_FL_S_NONE && ns_capable(net->user_ns, CAP_NET_ADMIN)) { @@ -560,11 +568,11 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (freq.flr_label) { err = -EEXIST; - read_lock_bh(&ip6_sk_fl_lock); - for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { + rcu_read_lock_bh(); + for_each_sk_fl_rcu(np, sfl) { if (sfl->fl->label == freq.flr_label) { if (freq.flr_flags&IPV6_FL_F_EXCL) { - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); goto done; } fl1 = sfl->fl; @@ -572,7 +580,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) break; } } - read_unlock_bh(&ip6_sk_fl_lock); + rcu_read_unlock_bh(); if (fl1 == NULL) fl1 = fl_lookup(net, freq.flr_label); -- cgit v1.2.3 From c33e7b05f1411b97cd0260ae682fc1cdb27bfaa4 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 30 Jan 2013 09:27:58 +0000 Subject: ipv6 anycast: Convert ipv6_sk_ac_lock to spinlock. Since all users are write-lock, it does not make sense to use rwlock here. Use simple spinlock. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/anycast.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 757a810d8f15..921b8b398a8c 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -47,7 +47,7 @@ static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); /* Big ac list lock for all the sockets */ -static DEFINE_RWLOCK(ipv6_sk_ac_lock); +static DEFINE_SPINLOCK(ipv6_sk_ac_lock); /* @@ -128,10 +128,10 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) err = ipv6_dev_ac_inc(dev, addr); if (!err) { - write_lock_bh(&ipv6_sk_ac_lock); + spin_lock_bh(&ipv6_sk_ac_lock); pac->acl_next = np->ipv6_ac_list; np->ipv6_ac_list = pac; - write_unlock_bh(&ipv6_sk_ac_lock); + spin_unlock_bh(&ipv6_sk_ac_lock); pac = NULL; } @@ -152,7 +152,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) struct ipv6_ac_socklist *pac, *prev_pac; struct net *net = sock_net(sk); - write_lock_bh(&ipv6_sk_ac_lock); + spin_lock_bh(&ipv6_sk_ac_lock); prev_pac = NULL; for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) { if ((ifindex == 0 || pac->acl_ifindex == ifindex) && @@ -161,7 +161,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) prev_pac = pac; } if (!pac) { - write_unlock_bh(&ipv6_sk_ac_lock); + spin_unlock_bh(&ipv6_sk_ac_lock); return -ENOENT; } if (prev_pac) @@ -169,7 +169,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) else np->ipv6_ac_list = pac->acl_next; - write_unlock_bh(&ipv6_sk_ac_lock); + spin_unlock_bh(&ipv6_sk_ac_lock); rcu_read_lock(); dev = dev_get_by_index_rcu(net, pac->acl_ifindex); @@ -192,10 +192,10 @@ void ipv6_sock_ac_close(struct sock *sk) if (!np->ipv6_ac_list) return; - write_lock_bh(&ipv6_sk_ac_lock); + spin_lock_bh(&ipv6_sk_ac_lock); pac = np->ipv6_ac_list; np->ipv6_ac_list = NULL; - write_unlock_bh(&ipv6_sk_ac_lock); + spin_unlock_bh(&ipv6_sk_ac_lock); prev_index = 0; rcu_read_lock(); -- cgit v1.2.3 From cd8f7cb4e6dfa4ea08fc250a814240b883ef7911 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Jan 2013 12:34:29 +0100 Subject: cfg80211/mac80211: support reporting wakeup reason When waking up from WoWLAN, it is useful to know what triggered the wakeup. Support reporting the wakeup reason(s) in cfg80211 (and a pass-through in mac80211) to allow userspace to know. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 41 +++++++++++++++++++ include/net/mac80211.h | 12 ++++++ include/uapi/linux/nl80211.h | 31 ++++++++++++++ net/mac80211/pm.c | 10 +++++ net/wireless/nl80211.c | 97 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/trace.h | 35 ++++++++++++++++ 6 files changed, 226 insertions(+) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 36e076e374d2..48add7e3ba1d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1596,6 +1596,32 @@ struct cfg80211_wowlan { int n_patterns; }; +/** + * struct cfg80211_wowlan_wakeup - wakeup report + * @disconnect: woke up by getting disconnected + * @magic_pkt: woke up by receiving magic packet + * @gtk_rekey_failure: woke up by GTK rekey failure + * @eap_identity_req: woke up by EAP identity request packet + * @four_way_handshake: woke up by 4-way handshake + * @rfkill_release: woke up by rfkill being released + * @pattern_idx: pattern that caused wakeup, -1 if not due to pattern + * @packet_present_len: copied wakeup packet data + * @packet_len: original wakeup packet length + * @packet: The packet causing the wakeup, if any. + * @packet_80211: For pattern match, magic packet and other data + * frame triggers an 802.3 frame should be reported, for + * disconnect due to deauth 802.11 frame. This indicates which + * it is. + */ +struct cfg80211_wowlan_wakeup { + bool disconnect, magic_pkt, gtk_rekey_failure, + eap_identity_req, four_way_handshake, + rfkill_release, packet_80211; + s32 pattern_idx; + u32 packet_present_len, packet_len; + const void *packet; +}; + /** * struct cfg80211_gtk_rekey_data - rekey data * @kek: key encryption key @@ -3852,6 +3878,21 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, enum ieee80211_p2p_attr_id attr, u8 *buf, unsigned int bufsize); +/** + * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN + * @wdev: the wireless device reporting the wakeup + * @wakeup: the wakeup report + * @gfp: allocation flags + * + * This function reports that the given device woke up. If it + * caused the wakeup, report the reason(s), otherwise you may + * pass %NULL as the @wakeup parameter to advertise that something + * else caused the wakeup. + */ +void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, + struct cfg80211_wowlan_wakeup *wakeup, + gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 21831ee57e3c..7a27e00c513a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4206,4 +4206,16 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); */ int ieee80211_ave_rssi(struct ieee80211_vif *vif); +/** + * ieee80211_report_wowlan_wakeup - report WoWLAN wakeup + * @vif: virtual interface + * @wakeup: wakeup reason(s) + * @gfp: allocation flags + * + * See cfg80211_report_wowlan_wakeup(). + */ +void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, + struct cfg80211_wowlan_wakeup *wakeup, + gfp_t gfp); + #endif /* MAC80211_H */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5b7dbc1ea966..225a65e72219 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -513,6 +513,12 @@ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification + * from the driver reporting the wakeup reason. In this case, the + * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason + * for the wakeup, if it was caused by wireless. If it is not present + * in the wakeup notification, the wireless device didn't cause the + * wakeup but reports that it was woken up. * * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver * the necessary information for supporting GTK rekey offload. This @@ -2947,6 +2953,10 @@ struct nl80211_wowlan_pattern_support { * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * carrying a &struct nl80211_wowlan_pattern_support. + * + * When reporting wakeup. it is a u32 attribute containing the 0-based + * index of the pattern that caused the wakeup, in the patterns passed + * to the kernel when configuring. * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be * used when setting, used only to indicate that GTK rekeying is supported * by the device (flag) @@ -2957,8 +2967,25 @@ struct nl80211_wowlan_pattern_support { * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released * (on devices that have rfkill in the device) (flag) + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains + * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame + * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN + * attribute contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the + * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may + * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute + * contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 + * attribute if the packet was truncated somewhere. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + * + * These nested attributes are used to configure the wakeup triggers and + * to report the wakeup reason(s). */ enum nl80211_wowlan_triggers { __NL80211_WOWLAN_TRIG_INVALID, @@ -2971,6 +2998,10 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, /* keep last */ NUM_NL80211_WOWLAN_TRIG, diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index e45b83610e85..53801d20176d 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -228,3 +228,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) * ieee80211_reconfig(), which is also needed for hardware * hang/firmware failure/etc. recovery. */ + +void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, + struct cfg80211_wowlan_wakeup *wakeup, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp); +} +EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b5978ab4ad7a..d359734b6972 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9323,6 +9323,103 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_report_obss_beacon); +#ifdef CONFIG_PM +void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, + struct cfg80211_wowlan_wakeup *wakeup, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err, size = 200; + + trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup); + + if (wakeup) + size += wakeup->packet_present_len; + + msg = nlmsg_new(size, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) + goto free_msg; + + if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) + goto free_msg; + + if (wakeup) { + struct nlattr *reasons; + + reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + + if (wakeup->disconnect && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) + goto free_msg; + if (wakeup->magic_pkt && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) + goto free_msg; + if (wakeup->gtk_rekey_failure && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) + goto free_msg; + if (wakeup->eap_identity_req && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) + goto free_msg; + if (wakeup->four_way_handshake && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) + goto free_msg; + if (wakeup->rfkill_release && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)) + goto free_msg; + + if (wakeup->pattern_idx >= 0 && + nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, + wakeup->pattern_idx)) + goto free_msg; + + if (wakeup->packet) { + u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; + u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; + + if (!wakeup->packet_80211) { + pkt_attr = + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023; + len_attr = + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN; + } + + if (wakeup->packet_len && + nla_put_u32(msg, len_attr, wakeup->packet_len)) + goto free_msg; + + if (nla_put(msg, pkt_attr, wakeup->packet_present_len, + wakeup->packet)) + goto free_msg; + } + + nla_nest_end(msg, reasons); + } + + err = genlmsg_end(msg, hdr); + if (err < 0) + goto free_msg; + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + free_msg: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup); +#endif + void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper, u16 reason_code, gfp_t gfp) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 8bc553199686..c9cafb0ea95f 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2333,6 +2333,41 @@ TRACE_EVENT(cfg80211_return_u32, TP_printk("ret: %u", __entry->ret) ); +TRACE_EVENT(cfg80211_report_wowlan_wakeup, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_wowlan_wakeup *wakeup), + TP_ARGS(wiphy, wdev, wakeup), + TP_STRUCT__entry( + WIPHY_ENTRY + WDEV_ENTRY + __field(bool, disconnect) + __field(bool, magic_pkt) + __field(bool, gtk_rekey_failure) + __field(bool, eap_identity_req) + __field(bool, four_way_handshake) + __field(bool, rfkill_release) + __field(s32, pattern_idx) + __field(u32, packet_len) + __dynamic_array(u8, packet, wakeup->packet_present_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + WDEV_ASSIGN; + __entry->disconnect = wakeup->disconnect; + __entry->magic_pkt = wakeup->magic_pkt; + __entry->gtk_rekey_failure = wakeup->gtk_rekey_failure; + __entry->eap_identity_req = wakeup->eap_identity_req; + __entry->four_way_handshake = wakeup->four_way_handshake; + __entry->rfkill_release = wakeup->rfkill_release; + __entry->pattern_idx = wakeup->pattern_idx; + __entry->packet_len = wakeup->packet_len; + if (wakeup->packet && wakeup->packet_present_len) + memcpy(__get_dynamic_array(packet), wakeup->packet, + wakeup->packet_present_len); + ), + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) +); + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From fdcb786930777231c81f487ab2526d33971a3438 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 25 Jan 2013 00:44:12 +0100 Subject: mac80211: remove assoc data "sent_assoc" The field is never used, so remove it. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 - net/mac80211/mlme.c | 3 --- 2 files changed, 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5fba867d9e2e..2f50be7eea09 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -400,7 +400,6 @@ struct ieee80211_mgd_assoc_data { u8 supp_rates_len; bool wmm, uapsd; bool have_beacon; - bool sent_assoc; bool synced; u8 ap_ht_param; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5913fb924b12..d79b9cd72fc5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2555,7 +2555,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; - ifmgd->assoc_data->sent_assoc = false; /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; run_again(ifmgd, ifmgd->assoc_data->timeout); @@ -3967,13 +3966,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ifmgd->dtim_period = tim->dtim_period; } assoc_data->have_beacon = true; - assoc_data->sent_assoc = false; assoc_data->timeout = jiffies; } rcu_read_unlock(); } else { assoc_data->have_beacon = true; - assoc_data->sent_assoc = false; assoc_data->timeout = jiffies; } run_again(ifmgd, assoc_data->timeout); -- cgit v1.2.3 From c65dd1477b6fe5971489dd8b6e28a07ec277fdd6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 12 Dec 2012 10:12:24 +0200 Subject: mac80211: inform the driver about update of dtim_period Currently, when the driver requires the DTIM period, mac80211 will wait to hear a beacon before association. This behavior is suboptimal since some drivers may be able to deal with knowing the DTIM period after the association, if they get it at all. To address this, notify the drivers with bss_info_changed with the new BSS_CHANGED_DTIM_PERIOD flag when the DTIM becomes known. This might be when changing to associated, or later when the entire association was done with only probe response information. Rename the hardware flag for the current behaviour to IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC to more accurately reflect its behaviour. IEEE80211_HW_NEED_DTIM_PERIOD is no longer accurate as all drivers get the DTIM period now, just not before association. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/carl9170/main.c | 2 +- drivers/net/wireless/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +- include/net/mac80211.h | 16 ++++--- net/mac80211/debugfs.c | 4 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mlme.c | 74 +++++++++++++++++------------ net/mac80211/util.c | 4 ++ 8 files changed, 63 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ef82751722e0..f293b3ff4756 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1853,7 +1853,7 @@ void *carl9170_alloc(size_t priv_size) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | - IEEE80211_HW_NEED_DTIM_PERIOD | + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SIGNAL_DBM; if (!modparam_noht) { diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index f1dc04006564..d3cc776d2d34 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5711,7 +5711,7 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; if (il->cfg->sku & IL_SKU_N) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index fa43c0d6abe2..8e5c5c5ce06f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -145,7 +145,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_NEED_DTIM_PERIOD | + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_QUEUE_CONTROL | diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7a27e00c513a..398b6ca4a9c5 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -208,6 +208,8 @@ struct ieee80211_chanctx_conf { * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS) * changed (currently only in P2P client mode, GO mode will be later) + * @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when + * it becomes valid, managed mode only) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -230,6 +232,7 @@ enum ieee80211_bss_change { BSS_CHANGED_PS = 1<<17, BSS_CHANGED_TXPOWER = 1<<18, BSS_CHANGED_P2P_PS = 1<<19, + BSS_CHANGED_DTIM_PERIOD = 1<<20, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -271,9 +274,8 @@ enum ieee80211_rssi_event { * if the hardware cannot handle this it must set the * IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag * @dtim_period: num of beacons before the next DTIM, for beaconing, - * valid in station mode only while @assoc is true and if also - * requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf - * @ps_dtim_period) + * valid in station mode only if after the driver was notified + * with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then. * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old * as it may have been received during scanning long ago) * @sync_device_ts: the device timestamp corresponding to the sync_tsf, @@ -1328,9 +1330,9 @@ struct ieee80211_tx_control { * When this flag is set, signaling beacon-loss will cause an immediate * change to disassociated state. * - * @IEEE80211_HW_NEED_DTIM_PERIOD: - * This device needs to know the DTIM period for the BSS before - * associating. + * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC: + * This device needs to get data from beacon before association (i.e. + * dtim_period). * * @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports * per-station GTKs as used by IBSS RSN or during fast transition. If @@ -1375,7 +1377,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = 1<<4, IEEE80211_HW_SIGNAL_UNSPEC = 1<<5, IEEE80211_HW_SIGNAL_DBM = 1<<6, - IEEE80211_HW_NEED_DTIM_PERIOD = 1<<7, + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC = 1<<7, IEEE80211_HW_SPECTRUM_MGMT = 1<<8, IEEE80211_HW_AMPDU_AGGREGATION = 1<<9, IEEE80211_HW_SUPPORTS_PS = 1<<10, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 466f4b45dd94..8e6040998ba6 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -121,8 +121,8 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); - if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) - sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n"); + if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC) + sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n"); if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2f50be7eea09..5ce0b6b6e182 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -399,7 +399,7 @@ struct ieee80211_mgd_assoc_data { u8 ssid_len; u8 supp_rates_len; bool wmm, uapsd; - bool have_beacon; + bool have_beacon, need_beacon; bool synced; u8 ap_ht_param; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d79b9cd72fc5..a5dba67bbe0b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1445,7 +1445,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_led_assoc(local, 1); - if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { + if (sdata->u.mgd.assoc_data->have_beacon) { /* * If the AP is buggy we may get here with no DTIM period * known, so assume it's 1 which is the only safe assumption @@ -1453,6 +1453,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, * probably just won't work at all. */ bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1; + bss_info_changed |= BSS_CHANGED_DTIM_PERIOD; } else { bss_conf->dtim_period = 0; } @@ -2548,13 +2549,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, chan = chanctx_conf->def.chan; rcu_read_unlock(); - if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && + if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; + ifmgd->assoc_data->need_beacon = false; /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; run_again(ifmgd, ifmgd->assoc_data->timeout); @@ -2711,6 +2713,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, elems.wmm_param_len)) changed |= BSS_CHANGED_QOS; + /* + * If we haven't had a beacon before, tell the driver about the + * DTIM period now. + */ + if (!bss_conf->dtim_period) { + /* a few bogus AP send dtim_period = 0 or no TIM IE */ + if (elems.tim) + bss_conf->dtim_period = elems.tim->dtim_period ?: 1; + else + bss_conf->dtim_period = 1; + changed |= BSS_CHANGED_DTIM_PERIOD; + } + if (elems.erp_info && elems.erp_info_len >= 1) { erp_valid = true; erp_value = elems.erp_info[0]; @@ -2989,7 +3004,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if (ifmgd->assoc_data && time_after(jiffies, ifmgd->assoc_data->timeout)) { - if (!ifmgd->assoc_data->have_beacon || + if ((ifmgd->assoc_data->need_beacon && + !ifmgd->assoc_data->have_beacon) || ieee80211_do_assoc(sdata)) { u8 bssid[ETH_ALEN]; @@ -3776,6 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_mgd_assoc_data *assoc_data; + const struct cfg80211_bss_ies *beacon_ies; struct ieee80211_supported_band *sband; const u8 *ssidie, *ht_ie, *vht_ie; int i, err; @@ -3941,38 +3958,35 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (err) goto err_clear; - if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) { - const struct cfg80211_bss_ies *beacon_ies; + rcu_read_lock(); + beacon_ies = rcu_dereference(req->bss->beacon_ies); - rcu_read_lock(); - beacon_ies = rcu_dereference(req->bss->beacon_ies); - if (!beacon_ies) { - /* - * Wait up to one beacon interval ... - * should this be more if we miss one? - */ - sdata_info(sdata, "waiting for beacon from %pM\n", - ifmgd->bssid); - assoc_data->timeout = - TU_TO_EXP_TIME(req->bss->beacon_interval); - } else { - const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, - beacon_ies->data, - beacon_ies->len); - if (tim_ie && tim_ie[1] >= - sizeof(struct ieee80211_tim_ie)) { - const struct ieee80211_tim_ie *tim; - tim = (void *)(tim_ie + 2); - ifmgd->dtim_period = tim->dtim_period; - } - assoc_data->have_beacon = true; - assoc_data->timeout = jiffies; + if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC && + !beacon_ies) { + /* + * Wait up to one beacon interval ... + * should this be more if we miss one? + */ + sdata_info(sdata, "waiting for beacon from %pM\n", + ifmgd->bssid); + assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); + assoc_data->need_beacon = true; + } else if (beacon_ies) { + const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, + beacon_ies->data, + beacon_ies->len); + if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) { + const struct ieee80211_tim_ie *tim; + tim = (void *)(tim_ie + 2); + ifmgd->dtim_period = tim->dtim_period; } - rcu_read_unlock(); - } else { assoc_data->have_beacon = true; assoc_data->timeout = jiffies; + } else { + assoc_data->timeout = jiffies; } + rcu_read_unlock(); + run_again(ifmgd, assoc_data->timeout); if (bss->corrupt_data) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7519018ff71a..1c74512697f0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1538,6 +1538,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) changed |= BSS_CHANGED_ASSOC | BSS_CHANGED_ARP_FILTER | BSS_CHANGED_PS; + + if (sdata->u.mgd.dtim_period) + changed |= BSS_CHANGED_DTIM_PERIOD; + mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); -- cgit v1.2.3 From ce9877c0cd68f52e1818f0e175fc7b2f85b0e655 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 22:33:30 +0100 Subject: mac80211: remove last_probe_resp from bss We track this, but never use it, so we can just remove it. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/scan.c | 3 --- 2 files changed, 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5ce0b6b6e182..12666c2ed49e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -95,8 +95,6 @@ struct ieee80211_bss { bool wmm_used; bool uapsd_supported; - unsigned long last_probe_resp; - #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 607684c47d55..f69a1b9cb131 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -146,9 +146,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->valid_data |= IEEE80211_BSS_VALID_WMM; } - if (!beacon) - bss->last_probe_resp = jiffies; - return bss; } -- cgit v1.2.3 From 0fe20add2c4b768ba8432ed15635caa46417ede7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 22:37:04 +0100 Subject: mac80211: remove unused mesh data from bss These pointers/values are never used, remove them. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 31 ------------------------------- net/mac80211/scan.c | 9 --------- 2 files changed, 40 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 12666c2ed49e..e4ee16839c6d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -95,12 +95,6 @@ struct ieee80211_bss { bool wmm_used; bool uapsd_supported; -#ifdef CONFIG_MAC80211_MESH - u8 *mesh_id; - size_t mesh_id_len; - u8 *mesh_cfg; -#endif - #define IEEE80211_MAX_SUPP_RATES 32 u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; @@ -151,31 +145,6 @@ enum ieee80211_bss_valid_data_flags { IEEE80211_BSS_VALID_ERP = BIT(3) }; -static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH - return bss->mesh_cfg; -#endif - return NULL; -} - -static inline u8 *bss_mesh_id(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH - return bss->mesh_id; -#endif - return NULL; -} - -static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss) -{ -#ifdef CONFIG_MAC80211_MESH - return bss->mesh_id_len; -#endif - return 0; -} - - typedef unsigned __bitwise__ ieee80211_tx_result; #define TX_CONTINUE ((__force ieee80211_tx_result) 0u) #define TX_DROP ((__force ieee80211_tx_result) 1u) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f69a1b9cb131..061595ae513b 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -29,14 +29,6 @@ #define IEEE80211_CHANNEL_TIME (HZ / 33) #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) -static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) -{ - struct ieee80211_bss *bss = (void *)cbss->priv; - - kfree(bss_mesh_id(bss)); - kfree(bss_mesh_cfg(bss)); -} - void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss) { @@ -85,7 +77,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, if (!cbss) return NULL; - cbss->free_priv = ieee80211_rx_bss_free; bss = (void *)cbss->priv; bss->device_ts = rx_status->device_timestamp; -- cgit v1.2.3 From 3ff9a827c683353b9826ef57366b0f313acc21b0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 22:37:48 +0100 Subject: cfg80211: remove free_priv BSS API Now that mac80211 no longer uses this API, remove it completely. If anyone needs it again, we can revert this patch of course, but mac80211 was the only user right now. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 --- net/wireless/scan.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 48add7e3ba1d..63599ab6005b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1289,7 +1289,6 @@ struct cfg80211_bss_ies { * @beacon_ies: the information elements from the last Beacon frame * @proberesp_ies: the information elements from the last Probe Response frame * @signal: signal strength value (type depends on the wiphy's signal_type) - * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ struct cfg80211_bss { @@ -1301,8 +1300,6 @@ struct cfg80211_bss { const struct cfg80211_bss_ies __rcu *beacon_ies; const struct cfg80211_bss_ies __rcu *proberesp_ies; - void (*free_priv)(struct cfg80211_bss *bss); - s32 signal; u16 beacon_interval; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 01592d7d4789..ca367c58a3e2 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -31,9 +31,6 @@ static void bss_release(struct kref *ref) if (WARN_ON(atomic_read(&bss->hold))) return; - if (bss->pub.free_priv) - bss->pub.free_priv(&bss->pub); - ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); if (ies) kfree_rcu(ies, rcu_head); -- cgit v1.2.3 From 1672c0e31917f49d31d30d79067103432bc20cc7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 15:02:27 +0100 Subject: mac80211: start auth/assoc timeout on frame status When sending authentication/association frames they might take a bit of time to go out because we may have to synchronise with the AP, in particular in the case where it's really a P2P GO. In this case the 200ms fixed timeout could potentially be too short if the beacon interval is relatively large. For drivers that report TX status we can do better. Instead of starting the timeout directly, start it only when the frame status arrives. Since then the frame was out on the air, we can wait shorter (the typical response time is supposed to be 30ms, wait 100ms.) Also, if the frame failed to be transmitted try again right away instead of waiting. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 ++- net/mac80211/ibss.c | 4 +-- net/mac80211/ieee80211_i.h | 11 ++++-- net/mac80211/mlme.c | 85 +++++++++++++++++++++++++++++++++++++++------- net/mac80211/scan.c | 3 +- net/mac80211/status.c | 12 ++++--- net/mac80211/util.c | 12 +++---- 7 files changed, 103 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 398b6ca4a9c5..164fd4b6503c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -408,6 +408,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted * after TX status because the destination was asleep, it must not * be modified again (no seqno assignment, crypto, etc.) + * @IEEE80211_TX_INTFL_MLME_CONN_TX: This frame was transmitted by the MLME + * code for connection establishment, this indicates that its status + * should kick the MLME state machine. * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211 * MLME command (internal to mac80211 to figure out whether to send TX * status to user space) @@ -459,7 +462,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17), IEEE80211_TX_CTL_MORE_FRAMES = BIT(18), IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19), - /* hole at 20, use later */ + IEEE80211_TX_INTFL_MLME_CONN_TX = BIT(20), IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21), IEEE80211_TX_CTL_LDPC = BIT(22), IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b4b866f41919..a54c8248e0e0 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -302,7 +302,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", sdata->vif.addr, addr, sdata->u.ibss.bssid); ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0, - addr, sdata->u.ibss.bssid, NULL, 0, 0); + addr, sdata->u.ibss.bssid, NULL, 0, 0, 0); } return sta; } @@ -422,7 +422,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, * has actually implemented this. */ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0, - mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0); + mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0); } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e4ee16839c6d..c9c66dec170a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -415,6 +415,10 @@ struct ieee80211_if_managed { bool beacon_crc_valid; u32 beacon_crc; + bool status_acked; + bool status_received; + __le16 status_fc; + enum { IEEE80211_MFP_DISABLED, IEEE80211_MFP_OPTIONAL, @@ -1284,6 +1288,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata); +void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, + __le16 fc, bool acked); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); @@ -1544,7 +1550,8 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, u8 *extra, size_t extra_len, const u8 *bssid, - const u8 *da, const u8 *key, u8 key_len, u8 key_idx); + const u8 *da, const u8 *key, u8 key_len, u8 key_idx, + u32 tx_flags); void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf); @@ -1561,7 +1568,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed, bool no_cck, + u32 ratemask, bool directed, u32 tx_flags, struct ieee80211_channel *channel, bool scan); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a5dba67bbe0b..4ff52d0aaf9c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -30,11 +30,13 @@ #include "rate.h" #include "led.h" -#define IEEE80211_AUTH_TIMEOUT (HZ / 5) -#define IEEE80211_AUTH_MAX_TRIES 3 -#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) -#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) -#define IEEE80211_ASSOC_MAX_TRIES 3 +#define IEEE80211_AUTH_TIMEOUT (HZ / 5) +#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10) +#define IEEE80211_AUTH_MAX_TRIES 3 +#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) +#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) +#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) +#define IEEE80211_ASSOC_MAX_TRIES 3 static int max_nullfunc_tries = 2; module_param(max_nullfunc_tries, int, 0644); @@ -644,6 +646,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) drv_mgd_prepare_tx(local, sdata); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_MLME_CONN_TX; ieee80211_tx_skb(sdata, skb); } @@ -1707,7 +1712,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ssid_len = ssid[1]; ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, - 0, (u32) -1, true, false, + 0, (u32) -1, true, 0, ifmgd->associated->channel, false); rcu_read_unlock(); } @@ -1937,9 +1942,11 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { + struct ieee80211_local *local = sdata->local; struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; u8 *pos; struct ieee802_11_elems elems; + u32 tx_flags = 0; pos = mgmt->u.auth.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); @@ -1947,11 +1954,14 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, return; auth_data->expected_transaction = 4; drv_mgd_prepare_tx(sdata->local, sdata); + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_MLME_CONN_TX; ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, elems.challenge - 2, elems.challenge_len + 2, auth_data->bss->bssid, auth_data->bss->bssid, auth_data->key, auth_data->key_len, - auth_data->key_idx); + auth_data->key_idx, tx_flags); } static enum rx_mgmt_action __must_check @@ -2869,12 +2879,17 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; + u32 tx_flags = 0; lockdep_assert_held(&ifmgd->mtx); if (WARN_ON_ONCE(!auth_data)) return -EINVAL; + if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) + tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_MLME_CONN_TX; + auth_data->tries++; if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { @@ -2911,7 +2926,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, auth_data->data, auth_data->data_len, auth_data->bss->bssid, - auth_data->bss->bssid, NULL, 0, 0); + auth_data->bss->bssid, NULL, 0, 0, + tx_flags); } else { const u8 *ssidie; @@ -2930,13 +2946,15 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) * will not answer to direct packet in unassociated state. */ ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], - NULL, 0, (u32) -1, true, false, + NULL, 0, (u32) -1, true, tx_flags, auth_data->bss->channel, false); rcu_read_unlock(); } - auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - run_again(ifmgd, auth_data->timeout); + if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { + auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + run_again(ifmgd, auth_data->timeout); + } return 0; } @@ -2967,12 +2985,26 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) IEEE80211_ASSOC_MAX_TRIES); ieee80211_send_assoc(sdata); - assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; - run_again(&sdata->u.mgd, assoc_data->timeout); + if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { + assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; + run_again(&sdata->u.mgd, assoc_data->timeout); + } return 0; } +void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, + __le16 fc, bool acked) +{ + struct ieee80211_local *local = sdata->local; + + sdata->u.mgd.status_fc = fc; + sdata->u.mgd.status_acked = acked; + sdata->u.mgd.status_received = true; + + ieee80211_queue_work(&local->hw, &sdata->work); +} + void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -2980,6 +3012,33 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) mutex_lock(&ifmgd->mtx); + if (ifmgd->status_received) { + __le16 fc = ifmgd->status_fc; + bool status_acked = ifmgd->status_acked; + + ifmgd->status_received = false; + if (ifmgd->auth_data && + (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) { + if (status_acked) { + ifmgd->auth_data->timeout = + jiffies + IEEE80211_AUTH_TIMEOUT_SHORT; + run_again(ifmgd, ifmgd->auth_data->timeout); + } else { + ifmgd->auth_data->timeout = jiffies - 1; + } + } else if (ifmgd->assoc_data && + (ieee80211_is_assoc_req(fc) || + ieee80211_is_reassoc_req(fc))) { + if (status_acked) { + ifmgd->assoc_data->timeout = + jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; + run_again(ifmgd, ifmgd->assoc_data->timeout); + } else { + ifmgd->assoc_data->timeout = jiffies - 1; + } + } + } + if (ifmgd->auth_data && time_after(jiffies, ifmgd->auth_data->timeout)) { if (ifmgd->auth_data->done) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 061595ae513b..85d0e5ee55a2 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -389,7 +389,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, local->scan_req->rates[band], false, - local->scan_req->no_cck, + local->scan_req->no_cck ? + IEEE80211_TX_CTL_NO_CCK_RATE : 0, local->hw.conf.channel, true); /* diff --git a/net/mac80211/status.c b/net/mac80211/status.c index ab50285fcbab..d041de056b7f 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -335,7 +335,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, if (dropped) acked = false; - if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { + if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX | + IEEE80211_TX_INTFL_MLME_CONN_TX)) { struct ieee80211_sub_if_data *sdata = NULL; struct ieee80211_sub_if_data *iter_sdata; u64 cookie = (unsigned long)skb; @@ -357,10 +358,13 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, sdata = rcu_dereference(local->p2p_sdata); } - if (!sdata) + if (!sdata) { skb->dev = NULL; - else if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) { + } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { + ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control, + acked); + } else if (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) { cfg80211_probe_status(sdata->dev, hdr->addr1, cookie, acked, GFP_ATOMIC); } else { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1c74512697f0..139ad9b66c39 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1030,7 +1030,8 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, u8 *extra, size_t extra_len, const u8 *da, - const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx) + const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, + u32 tx_flags) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -1063,7 +1064,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, WARN_ON(err); } - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | + tx_flags; ieee80211_tx_skb(sdata, skb); } @@ -1277,7 +1279,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed, bool no_cck, + u32 ratemask, bool directed, u32 tx_flags, struct ieee80211_channel *channel, bool scan) { struct sk_buff *skb; @@ -1286,9 +1288,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, ssid, ssid_len, ie, ie_len, directed); if (skb) { - if (no_cck) - IEEE80211_SKB_CB(skb)->flags |= - IEEE80211_TX_CTL_NO_CCK_RATE; + IEEE80211_SKB_CB(skb)->flags |= tx_flags; if (scan) ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band); else -- cgit v1.2.3 From 3f892b61a8cf6a85bc389c8d13209d686a4f6403 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 23 Jan 2013 12:32:45 +0100 Subject: mac80211: improve latency and throughput while software scanning Patch vastly improve latency while scanning. Slight throughput improvements were observed as well. Is intended for improve performance of voice and video applications, when scan is periodically requested by user space (i.e. default NetworkManager behaviour). Patch remove latency requirement based on PM_QOS_NETWORK_LATENCY, this value is 2000 seconds by default (i.e. approximately 0.5 hour !?!). Also remove listen interval requirement, which based on beaconing and depending on BSS parameters. It can make we stay off-channel for a second or more. Instead try to offer the best latency that we could, i.e. be off-channel no longer than PASSIVE channel scan time: 125 ms. That mean we will scan two ACTIVE channels and go back to on-channel, and one PASSIVE channel, and go back to on-channel. Patch also decrease PASSIVE channel scan time to about 110 ms. As drawback patch increase overall scan time. On my tests, when scanning both 2GHz and 5GHz bands, scanning time increase from 5 seconds up to 10 seconds. Since that increase happen only when we are associated, I think it can be acceptable. If eventually better scan time is needed for situations when we lose signal and quickly need to decide to which AP roam, additional scan flag or parameter can be introduced. I tested patch by doing: while true; do iw dev wlan0 scan; sleep 3; done > /dev/null and ping -i0.2 -c 1000 HOST on remote and local machine, results are as below: * Ping from local periodically scanning machine to AP: Unpatched: rtt min/avg/max/mdev = 0.928/24.946/182.135/36.873 ms Patched: rtt min/avg/max/mdev = 0.928/19.678/150.845/33.130 ms * Ping from remote machine to periodically scanning machine: Unpatched: rtt min/avg/max/mdev = 1.637/120.683/709.139/164.337 ms Patched: rtt min/avg/max/mdev = 1.807/26.893/201.435/40.284 ms Throughput measured by scp show following results. * Upload to periodically scanning machine: Unpatched: 3.9MB/s 03:15 Patched: 4.3MB/s 02:58 * Download from periodically scanning machine: Unpatched: 5.5MB/s 02:17 Patched: 6.2MB/s 02:02 Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- net/mac80211/scan.c | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 85d0e5ee55a2..7f80f0a5026e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -27,7 +27,7 @@ #define IEEE80211_PROBE_DELAY (HZ / 33) #define IEEE80211_CHANNEL_TIME (HZ / 33) -#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) +#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9) void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss) @@ -535,8 +535,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, bool associated = false; bool tx_empty = true; bool bad_latency; - bool listen_int_exceeded; - unsigned long min_beacon_int = 0; struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *next_chan; enum mac80211_scan_state next_scan_state; @@ -555,11 +553,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, if (sdata->u.mgd.associated) { associated = true; - if (sdata->vif.bss_conf.beacon_int < - min_beacon_int || min_beacon_int == 0) - min_beacon_int = - sdata->vif.bss_conf.beacon_int; - if (!qdisc_all_tx_empty(sdata->dev)) { tx_empty = false; break; @@ -576,34 +569,19 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, * see if we can scan another channel without interfering * with the current traffic situation. * - * Since we don't know if the AP has pending frames for us - * we can only check for our tx queues and use the current - * pm_qos requirements for rx. Hence, if no tx traffic occurs - * at all we will scan as many channels in a row as the pm_qos - * latency allows us to. Additionally we also check for the - * currently negotiated listen interval to prevent losing - * frames unnecessarily. - * - * Otherwise switch back to the operating channel. + * Keep good latency, do not stay off-channel more than 125 ms. */ bad_latency = time_after(jiffies + - ieee80211_scan_get_channel_time(next_chan), - local->leave_oper_channel_time + - usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); - - listen_int_exceeded = time_after(jiffies + - ieee80211_scan_get_channel_time(next_chan), - local->leave_oper_channel_time + - usecs_to_jiffies(min_beacon_int * 1024) * - local->hw.conf.listen_interval); + ieee80211_scan_get_channel_time(next_chan), + local->leave_oper_channel_time + HZ / 8); if (associated && !tx_empty) { if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) next_scan_state = SCAN_ABORT; else next_scan_state = SCAN_SUSPEND; - } else if (associated && (bad_latency || listen_int_exceeded)) { + } else if (associated && bad_latency) { next_scan_state = SCAN_SUSPEND; } else { next_scan_state = SCAN_SET_CHANNEL; -- cgit v1.2.3 From a786a7c0ad44985548118fd2370c792c0da36891 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 30 Jan 2013 21:49:29 -0500 Subject: wanrouter: completely decouple obsolete code from kernel. The original suggestion to delete wanrouter started earlier with the mainline commit f0d1b3c2bcc5de8a17af5f2274f7fcde8292b5fc ("net/wanrouter: Deprecate and schedule for removal") in May 2012. More importantly, Dan Carpenter found[1] that the driver had a fundamental breakage introduced back in 2008, with commit 7be6065b39c3 ("netdevice wanrouter: Convert directly reference of netdev->priv"). So we know with certainty that the code hasn't been used by anyone willing to at least take the effort to send an e-mail report of breakage for at least 4 years. This commit does a decouple of the wanrouter subsystem, by going after the Makefile/Kconfig and similar files, so that these mainline files that we are keeping do not have the big wanrouter file/driver deletion commit tied into their history. Once this commit is in place, we then can remove the obsolete cyclomx drivers and similar that have a dependency on CONFIG_WAN_ROUTER_DRIVERS. [1] http://www.spinics.net/lists/netdev/msg218670.html Originally-by: Joe Perches Cc: Dan Carpenter Cc: Arnaldo Carvalho de Melo Signed-off-by: Paul Gortmaker --- Documentation/ioctl/ioctl-number.txt | 2 +- Documentation/magic-number.txt | 2 +- Documentation/zh_CN/magic-number.txt | 2 +- drivers/isdn/i4l/isdn_x25iface.h | 1 - drivers/net/wan/Kconfig | 54 ------------------------------------ drivers/net/wan/Makefile | 5 ---- net/Kconfig | 1 - net/Makefile | 1 - net/socket.c | 1 - 9 files changed, 3 insertions(+), 66 deletions(-) (limited to 'net') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 2152b0e7237d..3210540f8bd3 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -179,7 +179,7 @@ Code Seq#(hex) Include File Comments 'V' C0 media/davinci/vpfe_capture.h conflict! 'V' C0 media/si4713.h conflict! 'W' 00-1F linux/watchdog.h conflict! -'W' 00-1F linux/wanrouter.h conflict! +'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) 'W' 00-3F sound/asound.h conflict! 'X' all fs/xfs/xfs_fs.h conflict! and fs/xfs/linux-2.6/xfs_ioctl32.h diff --git a/Documentation/magic-number.txt b/Documentation/magic-number.txt index 82761a31d64d..76d80a64bbe1 100644 --- a/Documentation/magic-number.txt +++ b/Documentation/magic-number.txt @@ -122,7 +122,7 @@ SLAB_C_MAGIC 0x4f17a36d kmem_cache mm/slab.c COW_MAGIC 0x4f4f4f4d cow_header_v1 arch/um/drivers/ubd_user.c I810_CARD_MAGIC 0x5072696E i810_card sound/oss/i810_audio.c TRIDENT_CARD_MAGIC 0x5072696E trident_card sound/oss/trident.c -ROUTER_MAGIC 0x524d4157 wan_device include/linux/wanrouter.h +ROUTER_MAGIC 0x524d4157 wan_device [in wanrouter.h pre 3.9] SCC_MAGIC 0x52696368 gs_port drivers/char/scc.h SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c GDA_MAGIC 0x58464552 gda arch/mips/include/asm/sn/gda.h diff --git a/Documentation/zh_CN/magic-number.txt b/Documentation/zh_CN/magic-number.txt index 4263022f5002..2ebe539f5450 100644 --- a/Documentation/zh_CN/magic-number.txt +++ b/Documentation/zh_CN/magic-number.txt @@ -122,7 +122,7 @@ SLAB_C_MAGIC 0x4f17a36d kmem_cache mm/slab.c COW_MAGIC 0x4f4f4f4d cow_header_v1 arch/um/drivers/ubd_user.c I810_CARD_MAGIC 0x5072696E i810_card sound/oss/i810_audio.c TRIDENT_CARD_MAGIC 0x5072696E trident_card sound/oss/trident.c -ROUTER_MAGIC 0x524d4157 wan_device include/linux/wanrouter.h +ROUTER_MAGIC 0x524d4157 wan_device [in wanrouter.h pre 3.9] SCC_MAGIC 0x52696368 gs_port drivers/char/scc.h SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c GDA_MAGIC 0x58464552 gda arch/mips/include/asm/sn/gda.h diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h index 0b26e3b336e7..ca08e082cf7c 100644 --- a/drivers/isdn/i4l/isdn_x25iface.h +++ b/drivers/isdn/i4l/isdn_x25iface.h @@ -19,7 +19,6 @@ #endif #include -#include #include #include diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index d58431e99f73..0c077b0f7a2b 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -356,60 +356,6 @@ config SDLA To compile this driver as a module, choose M here: the module will be called sdla. -# Wan router core. -config WAN_ROUTER_DRIVERS - tristate "WAN router drivers" - depends on WAN_ROUTER - ---help--- - Connect LAN to WAN via Linux box. - - Select driver your card and remember to say Y to "Wan Router." - You will need the wan-tools package which is available from - . - - Note that the answer to this question won't directly affect the - kernel except for how subordinate drivers may be built: - saying N will just cause the configurator to skip all - the questions about WAN router drivers. - - If unsure, say N. - -config CYCLADES_SYNC - tristate "Cyclom 2X(tm) cards (EXPERIMENTAL)" - depends on WAN_ROUTER_DRIVERS && (PCI || ISA) - ---help--- - Cyclom 2X from Cyclades Corporation is an - intelligent multiprotocol WAN adapter with data transfer rates up to - 512 Kbps. These cards support the X.25 and SNA related protocols. - - While no documentation is available at this time please grab the - wanconfig tarball in - (with minor changes - to make it compile with the current wanrouter include files; efforts - are being made to use the original package available at - ). - - Feel free to contact me or the cycsyn-devel mailing list at - and for - additional details, I hope to have documentation available as soon as - possible. (Cyclades Brazil is writing the Documentation). - - The next questions will ask you about the protocols you want the - driver to support (for now only X.25 is supported). - - If you have one or more of these cards, say Y to this option. - - To compile this driver as a module, choose M here: the - module will be called cyclomx. - -config CYCLOMX_X25 - bool "Cyclom 2X X.25 support (EXPERIMENTAL)" - depends on CYCLADES_SYNC - help - Connect a Cyclom 2X card to an X.25 network. - - Enabling X.25 support will enlarge your kernel by about 11 kB. - # X.25 network drivers config LAPBETHER tristate "LAPB over Ethernet driver (EXPERIMENTAL)" diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index df70248e2fda..c135ef47cbca 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -5,10 +5,6 @@ # Rewritten to use lists instead of if-statements. # -cyclomx-y := cycx_main.o -cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o -cyclomx-objs := $(cyclomx-y) - obj-$(CONFIG_HDLC) += hdlc.o obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o @@ -28,7 +24,6 @@ obj-$(CONFIG_LANMEDIA) += lmc/ obj-$(CONFIG_DLCI) += dlci.o obj-$(CONFIG_SDLA) += sdla.o -obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o obj-$(CONFIG_N2) += n2.o diff --git a/net/Kconfig b/net/Kconfig index 3cc5be0fe420..c31348e70aad 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -209,7 +209,6 @@ source "net/ipx/Kconfig" source "drivers/net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" -source "net/wanrouter/Kconfig" source "net/phonet/Kconfig" source "net/ieee802154/Kconfig" source "net/mac802154/Kconfig" diff --git a/net/Makefile b/net/Makefile index 4f4ee083064c..c5aa8b3b49dc 100644 --- a/net/Makefile +++ b/net/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ obj-$(CONFIG_ATALK) += appletalk/ -obj-$(CONFIG_WAN_ROUTER) += wanrouter/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ obj-$(CONFIG_NETROM) += netrom/ diff --git a/net/socket.c b/net/socket.c index 2ca51c719ef9..5c4d82c05293 100644 --- a/net/socket.c +++ b/net/socket.c @@ -69,7 +69,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 6fcdf4facb85e7d54ff6195378dd2ba8e0baccc4 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 30 Jan 2013 21:50:08 -0500 Subject: wanrouter: delete now orphaned header content, files/drivers The wanrouter support was identified earlier as unused for years, and so the previous commit totally decoupled it from the kernel, leaving the related wanrouter files present, but totally inert. Here we take the final step in that cleanup, by doing a wholesale removal of these files. The two step process is used so that the large deletion is decoupled from the git history of files that we still care about. The drivers deleted here all were dependent on the Kconfig setting CONFIG_WAN_ROUTER_DRIVERS. A stub wanrouter.h header (kernel & uapi) are left behind so that drivers/isdn/i4l/isdn_x25iface.c continues to compile, and so that we don't accidentally break userspace that expected these defines. Cc: Joe Perches Cc: Dan Carpenter Cc: Arnaldo Carvalho de Melo Signed-off-by: Paul Gortmaker --- drivers/net/wan/cycx_drv.c | 569 -------------- drivers/net/wan/cycx_main.c | 346 --------- drivers/net/wan/cycx_x25.c | 1602 ---------------------------------------- include/linux/cyclomx.h | 77 -- include/linux/cycx_drv.h | 64 -- include/linux/wanrouter.h | 127 +--- include/uapi/linux/wanrouter.h | 443 +---------- net/wanrouter/Kconfig | 27 - net/wanrouter/Makefile | 7 - net/wanrouter/patchlevel | 1 - net/wanrouter/wanmain.c | 782 -------------------- net/wanrouter/wanproc.c | 380 ---------- 12 files changed, 8 insertions(+), 4417 deletions(-) delete mode 100644 drivers/net/wan/cycx_drv.c delete mode 100644 drivers/net/wan/cycx_main.c delete mode 100644 drivers/net/wan/cycx_x25.c delete mode 100644 include/linux/cyclomx.h delete mode 100644 include/linux/cycx_drv.h delete mode 100644 net/wanrouter/Kconfig delete mode 100644 net/wanrouter/Makefile delete mode 100644 net/wanrouter/patchlevel delete mode 100644 net/wanrouter/wanmain.c delete mode 100644 net/wanrouter/wanproc.c (limited to 'net') diff --git a/drivers/net/wan/cycx_drv.c b/drivers/net/wan/cycx_drv.c deleted file mode 100644 index 2a3ecae67a90..000000000000 --- a/drivers/net/wan/cycx_drv.c +++ /dev/null @@ -1,569 +0,0 @@ -/* -* cycx_drv.c Cyclom 2X Support Module. -* -* This module is a library of common hardware specific -* functions used by the Cyclades Cyclom 2X sync card. -* -* Author: Arnaldo Carvalho de Melo -* -* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo -* -* Based on sdladrv.c by Gene Kozin -* -* 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. -* ============================================================================ -* 1999/11/11 acme set_current_state(TASK_INTERRUPTIBLE), code -* cleanup -* 1999/11/08 acme init_cyc2x deleted, doing nothing -* 1999/11/06 acme back to read[bw], write[bw] and memcpy_to and -* fromio to use dpmbase ioremaped -* 1999/10/26 acme use isa_read[bw], isa_write[bw] & isa_memcpy_to -* & fromio -* 1999/10/23 acme cleanup to only supports cyclom2x: all the other -* boards are no longer manufactured by cyclades, -* if someone wants to support them... be my guest! -* 1999/05/28 acme cycx_intack & cycx_intde gone for good -* 1999/05/18 acme lots of unlogged work, submitting to Linus... -* 1999/01/03 acme more judicious use of data types -* 1999/01/03 acme judicious use of data types :> -* cycx_inten trying to reset pending interrupts -* from cyclom 2x - I think this isn't the way to -* go, but for now... -* 1999/01/02 acme cycx_intack ok, I think there's nothing to do -* to ack an int in cycx_drv.c, only handle it in -* cyx_isr (or in the other protocols: cyp_isr, -* cyf_isr, when they get implemented. -* Dec 31, 1998 acme cycx_data_boot & cycx_code_boot fixed, crossing -* fingers to see x25_configure in cycx_x25.c -* work... :) -* Dec 26, 1998 acme load implementation fixed, seems to work! :) -* cycx_2x_dpmbase_options with all the possible -* DPM addresses (20). -* cycx_intr implemented (test this!) -* general code cleanup -* Dec 8, 1998 Ivan Passos Cyclom-2X firmware load implementation. -* Aug 8, 1998 acme Initial version. -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include /* __init */ -#include -#include /* printk(), and other useful stuff */ -#include /* offsetof(), etc. */ -#include /* return codes */ -#include /* API definitions */ -#include /* CYCX firmware module definitions */ -#include /* udelay, msleep_interruptible */ -#include /* read[wl], write[wl], ioremap, iounmap */ - -#define MOD_VERSION 0 -#define MOD_RELEASE 6 - -MODULE_AUTHOR("Arnaldo Carvalho de Melo"); -MODULE_DESCRIPTION("Cyclom 2x Sync Card Driver"); -MODULE_LICENSE("GPL"); - -/* Hardware-specific functions */ -static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len); -static void cycx_bootcfg(struct cycx_hw *hw); - -static int reset_cyc2x(void __iomem *addr); -static int detect_cyc2x(void __iomem *addr); - -/* Miscellaneous functions */ -static int get_option_index(const long *optlist, long optval); -static u16 checksum(u8 *buf, u32 len); - -#define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET) - -/* Global Data */ - -/* private data */ -static const char fullname[] = "Cyclom 2X Support Module"; -static const char copyright[] = - "(c) 1998-2003 Arnaldo Carvalho de Melo "; - -/* Hardware configuration options. - * These are arrays of configuration options used by verification routines. - * The first element of each array is its size (i.e. number of options). - */ -static const long cyc2x_dpmbase_options[] = { - 20, - 0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000, - 0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000, - 0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000 -}; - -static const long cycx_2x_irq_options[] = { 7, 3, 5, 9, 10, 11, 12, 15 }; - -/* Kernel Loadable Module Entry Points */ -/* Module 'insert' entry point. - * o print announcement - * o initialize static data - * - * Return: 0 Ok - * < 0 error. - * Context: process */ - -static int __init cycx_drv_init(void) -{ - pr_info("%s v%u.%u %s\n", - fullname, MOD_VERSION, MOD_RELEASE, copyright); - - return 0; -} - -/* Module 'remove' entry point. - * o release all remaining system resources */ -static void cycx_drv_cleanup(void) -{ -} - -/* Kernel APIs */ -/* Set up adapter. - * o detect adapter type - * o verify hardware configuration options - * o check for hardware conflicts - * o set up adapter shared memory - * o test adapter memory - * o load firmware - * Return: 0 ok. - * < 0 error */ -EXPORT_SYMBOL(cycx_setup); -int cycx_setup(struct cycx_hw *hw, void *cfm, u32 len, unsigned long dpmbase) -{ - int err; - - /* Verify IRQ configuration options */ - if (!get_option_index(cycx_2x_irq_options, hw->irq)) { - pr_err("IRQ %d is invalid!\n", hw->irq); - return -EINVAL; - } - - /* Setup adapter dual-port memory window and test memory */ - if (!dpmbase) { - pr_err("you must specify the dpm address!\n"); - return -EINVAL; - } else if (!get_option_index(cyc2x_dpmbase_options, dpmbase)) { - pr_err("memory address 0x%lX is invalid!\n", dpmbase); - return -EINVAL; - } - - hw->dpmbase = ioremap(dpmbase, CYCX_WINDOWSIZE); - hw->dpmsize = CYCX_WINDOWSIZE; - - if (!detect_cyc2x(hw->dpmbase)) { - pr_err("adapter Cyclom 2X not found at address 0x%lX!\n", - dpmbase); - return -EINVAL; - } - - pr_info("found Cyclom 2X card at address 0x%lX\n", dpmbase); - - /* Load firmware. If loader fails then shut down adapter */ - err = load_cyc2x(hw, cfm, len); - - if (err) - cycx_down(hw); /* shutdown adapter */ - - return err; -} - -EXPORT_SYMBOL(cycx_down); -int cycx_down(struct cycx_hw *hw) -{ - iounmap(hw->dpmbase); - return 0; -} - -/* Enable interrupt generation. */ -static void cycx_inten(struct cycx_hw *hw) -{ - writeb(0, hw->dpmbase); -} - -/* Generate an interrupt to adapter's CPU. */ -EXPORT_SYMBOL(cycx_intr); -void cycx_intr(struct cycx_hw *hw) -{ - writew(0, hw->dpmbase + GEN_CYCX_INTR); -} - -/* Execute Adapter Command. - * o Set exec flag. - * o Busy-wait until flag is reset. */ -EXPORT_SYMBOL(cycx_exec); -int cycx_exec(void __iomem *addr) -{ - u16 i = 0; - /* wait till addr content is zeroed */ - - while (readw(addr)) { - udelay(1000); - - if (++i > 50) - return -1; - } - - return 0; -} - -/* Read absolute adapter memory. - * Transfer data from adapter's memory to data buffer. */ -EXPORT_SYMBOL(cycx_peek); -int cycx_peek(struct cycx_hw *hw, u32 addr, void *buf, u32 len) -{ - if (len == 1) - *(u8*)buf = readb(hw->dpmbase + addr); - else - memcpy_fromio(buf, hw->dpmbase + addr, len); - - return 0; -} - -/* Write Absolute Adapter Memory. - * Transfer data from data buffer to adapter's memory. */ -EXPORT_SYMBOL(cycx_poke); -int cycx_poke(struct cycx_hw *hw, u32 addr, void *buf, u32 len) -{ - if (len == 1) - writeb(*(u8*)buf, hw->dpmbase + addr); - else - memcpy_toio(hw->dpmbase + addr, buf, len); - - return 0; -} - -/* Hardware-Specific Functions */ - -/* Load Aux Routines */ -/* Reset board hardware. - return 1 if memory exists at addr and 0 if not. */ -static int memory_exists(void __iomem *addr) -{ - int tries = 0; - - for (; tries < 3 ; tries++) { - writew(TEST_PATTERN, addr + 0x10); - - if (readw(addr + 0x10) == TEST_PATTERN) - if (readw(addr + 0x10) == TEST_PATTERN) - return 1; - - msleep_interruptible(1 * 1000); - } - - return 0; -} - -/* Load reset code. */ -static void reset_load(void __iomem *addr, u8 *buffer, u32 cnt) -{ - void __iomem *pt_code = addr + RESET_OFFSET; - u16 i; /*, j; */ - - for (i = 0 ; i < cnt ; i++) { -/* for (j = 0 ; j < 50 ; j++); Delay - FIXME busy waiting... */ - writeb(*buffer++, pt_code++); - } -} - -/* Load buffer using boot interface. - * o copy data from buffer to Cyclom-X memory - * o wait for reset code to copy it to right portion of memory */ -static int buffer_load(void __iomem *addr, u8 *buffer, u32 cnt) -{ - memcpy_toio(addr + DATA_OFFSET, buffer, cnt); - writew(GEN_BOOT_DAT, addr + CMD_OFFSET); - - return wait_cyc(addr); -} - -/* Set up entry point and kick start Cyclom-X CPU. */ -static void cycx_start(void __iomem *addr) -{ - /* put in 0x30 offset the jump instruction to the code entry point */ - writeb(0xea, addr + 0x30); - writeb(0x00, addr + 0x31); - writeb(0xc4, addr + 0x32); - writeb(0x00, addr + 0x33); - writeb(0x00, addr + 0x34); - - /* cmd to start executing code */ - writew(GEN_START, addr + CMD_OFFSET); -} - -/* Load and boot reset code. */ -static void cycx_reset_boot(void __iomem *addr, u8 *code, u32 len) -{ - void __iomem *pt_start = addr + START_OFFSET; - - writeb(0xea, pt_start++); /* jmp to f000:3f00 */ - writeb(0x00, pt_start++); - writeb(0xfc, pt_start++); - writeb(0x00, pt_start++); - writeb(0xf0, pt_start); - reset_load(addr, code, len); - - /* 80186 was in hold, go */ - writeb(0, addr + START_CPU); - msleep_interruptible(1 * 1000); -} - -/* Load data.bin file through boot (reset) interface. */ -static int cycx_data_boot(void __iomem *addr, u8 *code, u32 len) -{ - void __iomem *pt_boot_cmd = addr + CMD_OFFSET; - u32 i; - - /* boot buffer length */ - writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); - writew(GEN_DEFPAR, pt_boot_cmd); - - if (wait_cyc(addr) < 0) - return -1; - - writew(0, pt_boot_cmd + sizeof(u16)); - writew(0x4000, pt_boot_cmd + 2 * sizeof(u16)); - writew(GEN_SET_SEG, pt_boot_cmd); - - if (wait_cyc(addr) < 0) - return -1; - - for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) - if (buffer_load(addr, code + i, - min_t(u32, CFM_LOAD_BUFSZ, (len - i))) < 0) { - pr_err("Error !!\n"); - return -1; - } - - return 0; -} - - -/* Load code.bin file through boot (reset) interface. */ -static int cycx_code_boot(void __iomem *addr, u8 *code, u32 len) -{ - void __iomem *pt_boot_cmd = addr + CMD_OFFSET; - u32 i; - - /* boot buffer length */ - writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16)); - writew(GEN_DEFPAR, pt_boot_cmd); - - if (wait_cyc(addr) < 0) - return -1; - - writew(0x0000, pt_boot_cmd + sizeof(u16)); - writew(0xc400, pt_boot_cmd + 2 * sizeof(u16)); - writew(GEN_SET_SEG, pt_boot_cmd); - - if (wait_cyc(addr) < 0) - return -1; - - for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ) - if (buffer_load(addr, code + i, - min_t(u32, CFM_LOAD_BUFSZ, (len - i)))) { - pr_err("Error !!\n"); - return -1; - } - - return 0; -} - -/* Load adapter from the memory image of the CYCX firmware module. - * o verify firmware integrity and compatibility - * o start adapter up */ -static int load_cyc2x(struct cycx_hw *hw, struct cycx_firmware *cfm, u32 len) -{ - int i, j; - struct cycx_fw_header *img_hdr; - u8 *reset_image, - *data_image, - *code_image; - void __iomem *pt_cycld = hw->dpmbase + 0x400; - u16 cksum; - - /* Announce */ - pr_info("firmware signature=\"%s\"\n", cfm->signature); - - /* Verify firmware signature */ - if (strcmp(cfm->signature, CFM_SIGNATURE)) { - pr_err("load_cyc2x: not Cyclom-2X firmware!\n"); - return -EINVAL; - } - - pr_info("firmware version=%u\n", cfm->version); - - /* Verify firmware module format version */ - if (cfm->version != CFM_VERSION) { - pr_err("%s: firmware format %u rejected! Expecting %u.\n", - __func__, cfm->version, CFM_VERSION); - return -EINVAL; - } - - /* Verify firmware module length and checksum */ - cksum = checksum((u8*)&cfm->info, sizeof(struct cycx_fw_info) + - cfm->info.codesize); -/* - FIXME cfm->info.codesize is off by 2 - if (((len - sizeof(struct cycx_firmware) - 1) != cfm->info.codesize) || -*/ - if (cksum != cfm->checksum) { - pr_err("%s: firmware corrupted!\n", __func__); - pr_err(" cdsize = 0x%x (expected 0x%lx)\n", - len - (int)sizeof(struct cycx_firmware) - 1, - cfm->info.codesize); - pr_err(" chksum = 0x%x (expected 0x%x)\n", - cksum, cfm->checksum); - return -EINVAL; - } - - /* If everything is ok, set reset, data and code pointers */ - img_hdr = (struct cycx_fw_header *)&cfm->image; -#ifdef FIRMWARE_DEBUG - pr_info("%s: image sizes\n", __func__); - pr_info(" reset=%lu\n", img_hdr->reset_size); - pr_info(" data=%lu\n", img_hdr->data_size); - pr_info(" code=%lu\n", img_hdr->code_size); -#endif - reset_image = ((u8 *)img_hdr) + sizeof(struct cycx_fw_header); - data_image = reset_image + img_hdr->reset_size; - code_image = data_image + img_hdr->data_size; - - /*---- Start load ----*/ - /* Announce */ - pr_info("loading firmware %s (ID=%u)...\n", - cfm->descr[0] ? cfm->descr : "unknown firmware", - cfm->info.codeid); - - for (i = 0 ; i < 5 ; i++) { - /* Reset Cyclom hardware */ - if (!reset_cyc2x(hw->dpmbase)) { - pr_err("dpm problem or board not found\n"); - return -EINVAL; - } - - /* Load reset.bin */ - cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size); - /* reset is waiting for boot */ - writew(GEN_POWER_ON, pt_cycld); - msleep_interruptible(1 * 1000); - - for (j = 0 ; j < 3 ; j++) - if (!readw(pt_cycld)) - goto reset_loaded; - else - msleep_interruptible(1 * 1000); - } - - pr_err("reset not started\n"); - return -EINVAL; - -reset_loaded: - /* Load data.bin */ - if (cycx_data_boot(hw->dpmbase, data_image, img_hdr->data_size)) { - pr_err("cannot load data file\n"); - return -EINVAL; - } - - /* Load code.bin */ - if (cycx_code_boot(hw->dpmbase, code_image, img_hdr->code_size)) { - pr_err("cannot load code file\n"); - return -EINVAL; - } - - /* Prepare boot-time configuration data */ - cycx_bootcfg(hw); - - /* kick-off CPU */ - cycx_start(hw->dpmbase); - - /* Arthur Ganzert's tip: wait a while after the firmware loading... - seg abr 26 17:17:12 EST 1999 - acme */ - msleep_interruptible(7 * 1000); - pr_info("firmware loaded!\n"); - - /* enable interrupts */ - cycx_inten(hw); - - return 0; -} - -/* Prepare boot-time firmware configuration data. - * o initialize configuration data area - From async.doc - V_3.4.0 - 07/18/1994 - - As of now, only static buffers are available to the user. - So, the bit VD_RXDIRC must be set in 'valid'. That means that user - wants to use the static transmission and reception buffers. */ -static void cycx_bootcfg(struct cycx_hw *hw) -{ - /* use fixed buffers */ - writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET); -} - -/* Detect Cyclom 2x adapter. - * Following tests are used to detect Cyclom 2x adapter: - * to be completed based on the tests done below - * Return 1 if detected o.k. or 0 if failed. - * Note: This test is destructive! Adapter will be left in shutdown - * state after the test. */ -static int detect_cyc2x(void __iomem *addr) -{ - reset_cyc2x(addr); - - return memory_exists(addr); -} - -/* Miscellaneous */ -/* Get option's index into the options list. - * Return option's index (1 .. N) or zero if option is invalid. */ -static int get_option_index(const long *optlist, long optval) -{ - int i = 1; - - for (; i <= optlist[0]; ++i) - if (optlist[i] == optval) - return i; - - return 0; -} - -/* Reset adapter's CPU. */ -static int reset_cyc2x(void __iomem *addr) -{ - writeb(0, addr + RST_ENABLE); - msleep_interruptible(2 * 1000); - writeb(0, addr + RST_DISABLE); - msleep_interruptible(2 * 1000); - - return memory_exists(addr); -} - -/* Calculate 16-bit CRC using CCITT polynomial. */ -static u16 checksum(u8 *buf, u32 len) -{ - u16 crc = 0; - u16 mask, flag; - - for (; len; --len, ++buf) - for (mask = 0x80; mask; mask >>= 1) { - flag = (crc & 0x8000); - crc <<= 1; - crc |= ((*buf & mask) ? 1 : 0); - - if (flag) - crc ^= 0x1021; - } - - return crc; -} - -module_init(cycx_drv_init); -module_exit(cycx_drv_cleanup); - -/* End */ diff --git a/drivers/net/wan/cycx_main.c b/drivers/net/wan/cycx_main.c deleted file mode 100644 index 81fbbad406be..000000000000 --- a/drivers/net/wan/cycx_main.c +++ /dev/null @@ -1,346 +0,0 @@ -/* -* cycx_main.c Cyclades Cyclom 2X WAN Link Driver. Main module. -* -* Author: Arnaldo Carvalho de Melo -* -* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo -* -* Based on sdlamain.c by Gene Kozin & -* Jaspreet Singh -* -* 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. -* ============================================================================ -* Please look at the bitkeeper changelog (or any other scm tool that ends up -* importing bitkeeper changelog or that replaces bitkeeper in the future as -* main tool for linux development). -* -* 2001/05/09 acme Fix MODULE_DESC for debug, .bss nitpicks, -* some cleanups -* 2000/07/13 acme remove useless #ifdef MODULE and crap -* #if KERNEL_VERSION > blah -* 2000/07/06 acme __exit at cyclomx_cleanup -* 2000/04/02 acme dprintk and cycx_debug -* module_init/module_exit -* 2000/01/21 acme rename cyclomx_open to cyclomx_mod_inc_use_count -* and cyclomx_close to cyclomx_mod_dec_use_count -* 2000/01/08 acme cleanup -* 1999/11/06 acme cycx_down back to life (it needs to be -* called to iounmap the dpmbase) -* 1999/08/09 acme removed references to enable_tx_int -* use spinlocks instead of cli/sti in -* cyclomx_set_state -* 1999/05/19 acme works directly linked into the kernel -* init_waitqueue_head for 2.3.* kernel -* 1999/05/18 acme major cleanup (polling not needed), etc -* 1998/08/28 acme minor cleanup (ioctls for firmware deleted) -* queue_task activated -* 1998/08/08 acme Initial version. -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include /* offsetof(), etc. */ -#include /* return codes */ -#include /* inline memset(), etc. */ -#include /* kmalloc(), kfree() */ -#include /* printk(), and other useful stuff */ -#include /* support for loadable modules */ -#include /* request_region(), release_region() */ -#include /* WAN router definitions */ -#include /* cyclomx common user API definitions */ -#include /* __init (when not using as a module) */ -#include - -unsigned int cycx_debug; - -MODULE_AUTHOR("Arnaldo Carvalho de Melo"); -MODULE_DESCRIPTION("Cyclom 2X Sync Card Driver."); -MODULE_LICENSE("GPL"); -module_param(cycx_debug, int, 0); -MODULE_PARM_DESC(cycx_debug, "cyclomx debug level"); - -/* Defines & Macros */ - -#define CYCX_DRV_VERSION 0 /* version number */ -#define CYCX_DRV_RELEASE 11 /* release (minor version) number */ -#define CYCX_MAX_CARDS 1 /* max number of adapters */ - -#define CONFIG_CYCX_CARDS 1 - -/* Function Prototypes */ - -/* WAN link driver entry points */ -static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf); -static int cycx_wan_shutdown(struct wan_device *wandev); - -/* Miscellaneous functions */ -static irqreturn_t cycx_isr(int irq, void *dev_id); - -/* Global Data - * Note: All data must be explicitly initialized!!! - */ - -/* private data */ -static const char cycx_drvname[] = "cyclomx"; -static const char cycx_fullname[] = "CYCLOM 2X(tm) Sync Card Driver"; -static const char cycx_copyright[] = "(c) 1998-2003 Arnaldo Carvalho de Melo " - ""; -static int cycx_ncards = CONFIG_CYCX_CARDS; -static struct cycx_device *cycx_card_array; /* adapter data space */ - -/* Kernel Loadable Module Entry Points */ - -/* - * Module 'insert' entry point. - * o print announcement - * o allocate adapter data space - * o initialize static data - * o register all cards with WAN router - * o calibrate Cyclom 2X shared memory access delay. - * - * Return: 0 Ok - * < 0 error. - * Context: process - */ -static int __init cycx_init(void) -{ - int cnt, err = -ENOMEM; - - pr_info("%s v%u.%u %s\n", - cycx_fullname, CYCX_DRV_VERSION, CYCX_DRV_RELEASE, - cycx_copyright); - - /* Verify number of cards and allocate adapter data space */ - cycx_ncards = min_t(int, cycx_ncards, CYCX_MAX_CARDS); - cycx_ncards = max_t(int, cycx_ncards, 1); - cycx_card_array = kcalloc(cycx_ncards, sizeof(struct cycx_device), GFP_KERNEL); - if (!cycx_card_array) - goto out; - - - /* Register adapters with WAN router */ - for (cnt = 0; cnt < cycx_ncards; ++cnt) { - struct cycx_device *card = &cycx_card_array[cnt]; - struct wan_device *wandev = &card->wandev; - - sprintf(card->devname, "%s%d", cycx_drvname, cnt + 1); - wandev->magic = ROUTER_MAGIC; - wandev->name = card->devname; - wandev->private = card; - wandev->setup = cycx_wan_setup; - wandev->shutdown = cycx_wan_shutdown; - err = register_wan_device(wandev); - - if (err) { - pr_err("%s registration failed with error %d!\n", - card->devname, err); - break; - } - } - - err = -ENODEV; - if (!cnt) { - kfree(cycx_card_array); - goto out; - } - err = 0; - cycx_ncards = cnt; /* adjust actual number of cards */ -out: return err; -} - -/* - * Module 'remove' entry point. - * o unregister all adapters from the WAN router - * o release all remaining system resources - */ -static void __exit cycx_exit(void) -{ - int i = 0; - - for (; i < cycx_ncards; ++i) { - struct cycx_device *card = &cycx_card_array[i]; - unregister_wan_device(card->devname); - } - - kfree(cycx_card_array); -} - -/* WAN Device Driver Entry Points */ -/* - * Setup/configure WAN link driver. - * o check adapter state - * o make sure firmware is present in configuration - * o allocate interrupt vector - * o setup Cyclom 2X hardware - * o call appropriate routine to perform protocol-specific initialization - * - * This function is called when router handles ROUTER_SETUP IOCTL. The - * configuration structure is in kernel memory (including extended data, if - * any). - */ -static int cycx_wan_setup(struct wan_device *wandev, wandev_conf_t *conf) -{ - int rc = -EFAULT; - struct cycx_device *card; - int irq; - - /* Sanity checks */ - - if (!wandev || !wandev->private || !conf) - goto out; - - card = wandev->private; - rc = -EBUSY; - if (wandev->state != WAN_UNCONFIGURED) - goto out; - - rc = -EINVAL; - if (!conf->data_size || !conf->data) { - pr_err("%s: firmware not found in configuration data!\n", - wandev->name); - goto out; - } - - if (conf->irq <= 0) { - pr_err("%s: can't configure without IRQ!\n", wandev->name); - goto out; - } - - /* Allocate IRQ */ - irq = conf->irq == 2 ? 9 : conf->irq; /* IRQ2 -> IRQ9 */ - - if (request_irq(irq, cycx_isr, 0, wandev->name, card)) { - pr_err("%s: can't reserve IRQ %d!\n", wandev->name, irq); - goto out; - } - - /* Configure hardware, load firmware, etc. */ - memset(&card->hw, 0, sizeof(card->hw)); - card->hw.irq = irq; - card->hw.dpmsize = CYCX_WINDOWSIZE; - card->hw.fwid = CFID_X25_2X; - spin_lock_init(&card->lock); - init_waitqueue_head(&card->wait_stats); - - rc = cycx_setup(&card->hw, conf->data, conf->data_size, conf->maddr); - if (rc) - goto out_irq; - - /* Initialize WAN device data space */ - wandev->irq = irq; - wandev->dma = wandev->ioport = 0; - wandev->maddr = (unsigned long)card->hw.dpmbase; - wandev->msize = card->hw.dpmsize; - wandev->hw_opt[2] = 0; - wandev->hw_opt[3] = card->hw.fwid; - - /* Protocol-specific initialization */ - switch (card->hw.fwid) { -#ifdef CONFIG_CYCLOMX_X25 - case CFID_X25_2X: - rc = cycx_x25_wan_init(card, conf); - break; -#endif - default: - pr_err("%s: this firmware is not supported!\n", wandev->name); - rc = -EINVAL; - } - - if (rc) { - cycx_down(&card->hw); - goto out_irq; - } - - rc = 0; -out: - return rc; -out_irq: - free_irq(irq, card); - goto out; -} - -/* - * Shut down WAN link driver. - * o shut down adapter hardware - * o release system resources. - * - * This function is called by the router when device is being unregistered or - * when it handles ROUTER_DOWN IOCTL. - */ -static int cycx_wan_shutdown(struct wan_device *wandev) -{ - int ret = -EFAULT; - struct cycx_device *card; - - /* sanity checks */ - if (!wandev || !wandev->private) - goto out; - - ret = 0; - if (wandev->state == WAN_UNCONFIGURED) - goto out; - - card = wandev->private; - wandev->state = WAN_UNCONFIGURED; - cycx_down(&card->hw); - pr_info("%s: irq %d being freed!\n", wandev->name, wandev->irq); - free_irq(wandev->irq, card); -out: return ret; -} - -/* Miscellaneous */ -/* - * Cyclom 2X Interrupt Service Routine. - * o acknowledge Cyclom 2X hardware interrupt. - * o call protocol-specific interrupt service routine, if any. - */ -static irqreturn_t cycx_isr(int irq, void *dev_id) -{ - struct cycx_device *card = dev_id; - - if (card->wandev.state == WAN_UNCONFIGURED) - goto out; - - if (card->in_isr) { - pr_warn("%s: interrupt re-entrancy on IRQ %d!\n", - card->devname, card->wandev.irq); - goto out; - } - - if (card->isr) - card->isr(card); - return IRQ_HANDLED; -out: - return IRQ_NONE; -} - -/* Set WAN device state. */ -void cycx_set_state(struct cycx_device *card, int state) -{ - unsigned long flags; - char *string_state = NULL; - - spin_lock_irqsave(&card->lock, flags); - - if (card->wandev.state != state) { - switch (state) { - case WAN_CONNECTED: - string_state = "connected!"; - break; - case WAN_DISCONNECTED: - string_state = "disconnected!"; - break; - } - pr_info("%s: link %s\n", card->devname, string_state); - card->wandev.state = state; - } - - card->state_tick = jiffies; - spin_unlock_irqrestore(&card->lock, flags); -} - -module_init(cycx_init); -module_exit(cycx_exit); diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c deleted file mode 100644 index 06f3f6309e4b..000000000000 --- a/drivers/net/wan/cycx_x25.c +++ /dev/null @@ -1,1602 +0,0 @@ -/* -* cycx_x25.c Cyclom 2X WAN Link Driver. X.25 module. -* -* Author: Arnaldo Carvalho de Melo -* -* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo -* -* Based on sdla_x25.c by Gene Kozin -* -* 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. -* ============================================================================ -* 2001/01/12 acme use dev_kfree_skb_irq on interrupt context -* 2000/04/02 acme dprintk, cycx_debug -* fixed the bug introduced in get_dev_by_lcn and -* get_dev_by_dte_addr by the anonymous hacker -* that converted this driver to softnet -* 2000/01/08 acme cleanup -* 1999/10/27 acme use ARPHRD_HWX25 so that the X.25 stack know -* that we have a X.25 stack implemented in -* firmware onboard -* 1999/10/18 acme support for X.25 sockets in if_send, -* beware: socket(AF_X25...) IS WORK IN PROGRESS, -* TCP/IP over X.25 via wanrouter not affected, -* working. -* 1999/10/09 acme chan_disc renamed to chan_disconnect, -* began adding support for X.25 sockets: -* conf->protocol in new_if -* 1999/10/05 acme fixed return E... to return -E... -* 1999/08/10 acme serialized access to the card thru a spinlock -* in x25_exec -* 1999/08/09 acme removed per channel spinlocks -* removed references to enable_tx_int -* 1999/05/28 acme fixed nibble_to_byte, ackvc now properly treated -* if_send simplified -* 1999/05/25 acme fixed t1, t2, t21 & t23 configuration -* use spinlocks instead of cli/sti in some points -* 1999/05/24 acme finished the x25_get_stat function -* 1999/05/23 acme dev->type = ARPHRD_X25 (tcpdump only works, -* AFAIT, with ARPHRD_ETHER). This seems to be -* needed to use socket(AF_X25)... -* Now the config file must specify a peer media -* address for svc channels over a crossover cable. -* Removed hold_timeout from x25_channel_t, -* not used. -* A little enhancement in the DEBUG processing -* 1999/05/22 acme go to DISCONNECTED in disconnect_confirm_intr, -* instead of chan_disc. -* 1999/05/16 marcelo fixed timer initialization in SVCs -* 1999/01/05 acme x25_configure now get (most of) all -* parameters... -* 1999/01/05 acme pktlen now (correctly) uses log2 (value -* configured) -* 1999/01/03 acme judicious use of data types (u8, u16, u32, etc) -* 1999/01/03 acme cyx_isr: reset dpmbase to acknowledge -* indication (interrupt from cyclom 2x) -* 1999/01/02 acme cyx_isr: first hackings... -* 1999/01/0203 acme when initializing an array don't give less -* elements than declared... -* example: char send_cmd[6] = "?\xFF\x10"; -* you'll gonna lose a couple hours, 'cause your -* brain won't admit that there's an error in the -* above declaration... the side effect is that -* memset is put into the unresolved symbols -* instead of using the inline memset functions... -* 1999/01/02 acme began chan_connect, chan_send, x25_send -* 1998/12/31 acme x25_configure -* this code can be compiled as non module -* 1998/12/27 acme code cleanup -* IPX code wiped out! let's decrease code -* complexity for now, remember: I'm learning! :) -* bps_to_speed_code OK -* 1998/12/26 acme Minimal debug code cleanup -* 1998/08/08 acme Initial version. -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define CYCLOMX_X25_DEBUG 1 - -#include /* isdigit() */ -#include /* return codes */ -#include /* ARPHRD_HWX25 */ -#include /* printk(), and other useful stuff */ -#include -#include /* inline memset(), etc. */ -#include -#include /* kmalloc(), kfree() */ -#include /* offsetof(), etc. */ -#include /* WAN router definitions */ - -#include /* htons(), etc. */ - -#include /* Cyclom 2X common user API definitions */ -#include /* X.25 firmware API definitions */ - -#include - -/* Defines & Macros */ -#define CYCX_X25_MAX_CMD_RETRY 5 -#define CYCX_X25_CHAN_MTU 2048 /* unfragmented logical channel MTU */ - -/* Data Structures */ -/* This is an extension of the 'struct net_device' we create for each network - interface to keep the rest of X.25 channel-specific data. */ -struct cycx_x25_channel { - /* This member must be first. */ - struct net_device *slave; /* WAN slave */ - - char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ - char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */ - char *local_addr; /* local media address, ASCIIZ - - svc thru crossover cable */ - s16 lcn; /* logical channel number/conn.req.key*/ - u8 link; - struct timer_list timer; /* timer used for svc channel disc. */ - u16 protocol; /* ethertype, 0 - multiplexed */ - u8 svc; /* 0 - permanent, 1 - switched */ - u8 state; /* channel state */ - u8 drop_sequence; /* mark sequence for dropping */ - u32 idle_tmout; /* sec, before disconnecting */ - struct sk_buff *rx_skb; /* receive socket buffer */ - struct cycx_device *card; /* -> owner */ - struct net_device_stats ifstats;/* interface statistics */ -}; - -/* Function Prototypes */ -/* WAN link driver entry points. These are called by the WAN router module. */ -static int cycx_wan_update(struct wan_device *wandev), - cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev, - wanif_conf_t *conf), - cycx_wan_del_if(struct wan_device *wandev, struct net_device *dev); - -/* Network device interface */ -static int cycx_netdevice_init(struct net_device *dev); -static int cycx_netdevice_open(struct net_device *dev); -static int cycx_netdevice_stop(struct net_device *dev); -static int cycx_netdevice_hard_header(struct sk_buff *skb, - struct net_device *dev, u16 type, - const void *daddr, const void *saddr, - unsigned len); -static int cycx_netdevice_rebuild_header(struct sk_buff *skb); -static netdev_tx_t cycx_netdevice_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev); - -static struct net_device_stats * - cycx_netdevice_get_stats(struct net_device *dev); - -/* Interrupt handlers */ -static void cycx_x25_irq_handler(struct cycx_device *card), - cycx_x25_irq_tx(struct cycx_device *card, struct cycx_x25_cmd *cmd), - cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd), - cycx_x25_irq_log(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_stat(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_connect_confirm(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_disconnect_confirm(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_connect(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_disconnect(struct cycx_device *card, - struct cycx_x25_cmd *cmd), - cycx_x25_irq_spurious(struct cycx_device *card, - struct cycx_x25_cmd *cmd); - -/* X.25 firmware interface functions */ -static int cycx_x25_configure(struct cycx_device *card, - struct cycx_x25_config *conf), - cycx_x25_get_stats(struct cycx_device *card), - cycx_x25_send(struct cycx_device *card, u8 link, u8 lcn, u8 bitm, - int len, void *buf), - cycx_x25_connect_response(struct cycx_device *card, - struct cycx_x25_channel *chan), - cycx_x25_disconnect_response(struct cycx_device *card, u8 link, - u8 lcn); - -/* channel functions */ -static int cycx_x25_chan_connect(struct net_device *dev), - cycx_x25_chan_send(struct net_device *dev, struct sk_buff *skb); - -static void cycx_x25_chan_disconnect(struct net_device *dev), - cycx_x25_chan_send_event(struct net_device *dev, u8 event); - -/* Miscellaneous functions */ -static void cycx_x25_set_chan_state(struct net_device *dev, u8 state), - cycx_x25_chan_timer(unsigned long d); - -static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble), - reset_timer(struct net_device *dev); - -static u8 bps_to_speed_code(u32 bps); -static u8 cycx_log2(u32 n); - -static unsigned dec_to_uint(u8 *str, int len); - -static struct net_device *cycx_x25_get_dev_by_lcn(struct wan_device *wandev, - s16 lcn); -static struct net_device * - cycx_x25_get_dev_by_dte_addr(struct wan_device *wandev, char *dte); - -static void cycx_x25_chan_setup(struct net_device *dev); - -#ifdef CYCLOMX_X25_DEBUG -static void hex_dump(char *msg, unsigned char *p, int len); -static void cycx_x25_dump_config(struct cycx_x25_config *conf); -static void cycx_x25_dump_stats(struct cycx_x25_stats *stats); -static void cycx_x25_dump_devs(struct wan_device *wandev); -#else -#define hex_dump(msg, p, len) -#define cycx_x25_dump_config(conf) -#define cycx_x25_dump_stats(stats) -#define cycx_x25_dump_devs(wandev) -#endif -/* Public Functions */ - -/* X.25 Protocol Initialization routine. - * - * This routine is called by the main Cyclom 2X module during setup. At this - * point adapter is completely initialized and X.25 firmware is running. - * o configure adapter - * o initialize protocol-specific fields of the adapter data space. - * - * Return: 0 o.k. - * < 0 failure. */ -int cycx_x25_wan_init(struct cycx_device *card, wandev_conf_t *conf) -{ - struct cycx_x25_config cfg; - - /* Verify configuration ID */ - if (conf->config_id != WANCONFIG_X25) { - pr_info("%s: invalid configuration ID %u!\n", - card->devname, conf->config_id); - return -EINVAL; - } - - /* Initialize protocol-specific fields */ - card->mbox = card->hw.dpmbase + X25_MBOX_OFFS; - card->u.x.connection_keys = 0; - spin_lock_init(&card->u.x.lock); - - /* Configure adapter. Here we set reasonable defaults, then parse - * device configuration structure and set configuration options. - * Most configuration options are verified and corrected (if - * necessary) since we can't rely on the adapter to do so and don't - * want it to fail either. */ - memset(&cfg, 0, sizeof(cfg)); - cfg.link = 0; - cfg.clock = conf->clocking == WANOPT_EXTERNAL ? 8 : 55; - cfg.speed = bps_to_speed_code(conf->bps); - cfg.n3win = 7; - cfg.n2win = 2; - cfg.n2 = 5; - cfg.nvc = 1; - cfg.npvc = 1; - cfg.flags = 0x02; /* default = V35 */ - cfg.t1 = 10; /* line carrier timeout */ - cfg.t2 = 29; /* tx timeout */ - cfg.t21 = 180; /* CALL timeout */ - cfg.t23 = 180; /* CLEAR timeout */ - - /* adjust MTU */ - if (!conf->mtu || conf->mtu >= 512) - card->wandev.mtu = 512; - else if (conf->mtu >= 256) - card->wandev.mtu = 256; - else if (conf->mtu >= 128) - card->wandev.mtu = 128; - else - card->wandev.mtu = 64; - - cfg.pktlen = cycx_log2(card->wandev.mtu); - - if (conf->station == WANOPT_DTE) { - cfg.locaddr = 3; /* DTE */ - cfg.remaddr = 1; /* DCE */ - } else { - cfg.locaddr = 1; /* DCE */ - cfg.remaddr = 3; /* DTE */ - } - - if (conf->interface == WANOPT_RS232) - cfg.flags = 0; /* FIXME just reset the 2nd bit */ - - if (conf->u.x25.hi_pvc) { - card->u.x.hi_pvc = min_t(unsigned int, conf->u.x25.hi_pvc, 4095); - card->u.x.lo_pvc = min_t(unsigned int, conf->u.x25.lo_pvc, card->u.x.hi_pvc); - } - - if (conf->u.x25.hi_svc) { - card->u.x.hi_svc = min_t(unsigned int, conf->u.x25.hi_svc, 4095); - card->u.x.lo_svc = min_t(unsigned int, conf->u.x25.lo_svc, card->u.x.hi_svc); - } - - if (card->u.x.lo_pvc == 255) - cfg.npvc = 0; - else - cfg.npvc = card->u.x.hi_pvc - card->u.x.lo_pvc + 1; - - cfg.nvc = card->u.x.hi_svc - card->u.x.lo_svc + 1 + cfg.npvc; - - if (conf->u.x25.hdlc_window) - cfg.n2win = min_t(unsigned int, conf->u.x25.hdlc_window, 7); - - if (conf->u.x25.pkt_window) - cfg.n3win = min_t(unsigned int, conf->u.x25.pkt_window, 7); - - if (conf->u.x25.t1) - cfg.t1 = min_t(unsigned int, conf->u.x25.t1, 30); - - if (conf->u.x25.t2) - cfg.t2 = min_t(unsigned int, conf->u.x25.t2, 30); - - if (conf->u.x25.t11_t21) - cfg.t21 = min_t(unsigned int, conf->u.x25.t11_t21, 30); - - if (conf->u.x25.t13_t23) - cfg.t23 = min_t(unsigned int, conf->u.x25.t13_t23, 30); - - if (conf->u.x25.n2) - cfg.n2 = min_t(unsigned int, conf->u.x25.n2, 30); - - /* initialize adapter */ - if (cycx_x25_configure(card, &cfg)) - return -EIO; - - /* Initialize protocol-specific fields of adapter data space */ - card->wandev.bps = conf->bps; - card->wandev.interface = conf->interface; - card->wandev.clocking = conf->clocking; - card->wandev.station = conf->station; - card->isr = cycx_x25_irq_handler; - card->exec = NULL; - card->wandev.update = cycx_wan_update; - card->wandev.new_if = cycx_wan_new_if; - card->wandev.del_if = cycx_wan_del_if; - card->wandev.state = WAN_DISCONNECTED; - - return 0; -} - -/* WAN Device Driver Entry Points */ -/* Update device status & statistics. */ -static int cycx_wan_update(struct wan_device *wandev) -{ - /* sanity checks */ - if (!wandev || !wandev->private) - return -EFAULT; - - if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV; - - cycx_x25_get_stats(wandev->private); - - return 0; -} - -/* Create new logical channel. - * This routine is called by the router when ROUTER_IFNEW IOCTL is being - * handled. - * o parse media- and hardware-specific configuration - * o make sure that a new channel can be created - * o allocate resources, if necessary - * o prepare network device structure for registration. - * - * Return: 0 o.k. - * < 0 failure (channel will not be created) */ -static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev, - wanif_conf_t *conf) -{ - struct cycx_device *card = wandev->private; - struct cycx_x25_channel *chan; - int err = 0; - - if (!conf->name[0] || strlen(conf->name) > WAN_IFNAME_SZ) { - pr_info("%s: invalid interface name!\n", card->devname); - return -EINVAL; - } - - dev = alloc_netdev(sizeof(struct cycx_x25_channel), conf->name, - cycx_x25_chan_setup); - if (!dev) - return -ENOMEM; - - chan = netdev_priv(dev); - strcpy(chan->name, conf->name); - chan->card = card; - chan->link = conf->port; - chan->protocol = conf->protocol ? ETH_P_X25 : ETH_P_IP; - chan->rx_skb = NULL; - /* only used in svc connected thru crossover cable */ - chan->local_addr = NULL; - - if (conf->addr[0] == '@') { /* SVC */ - int len = strlen(conf->local_addr); - - if (len) { - if (len > WAN_ADDRESS_SZ) { - pr_err("%s: %s local addr too long!\n", - wandev->name, chan->name); - err = -EINVAL; - goto error; - } else { - chan->local_addr = kmalloc(len + 1, GFP_KERNEL); - - if (!chan->local_addr) { - err = -ENOMEM; - goto error; - } - } - - strncpy(chan->local_addr, conf->local_addr, - WAN_ADDRESS_SZ); - } - - chan->svc = 1; - strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ); - init_timer(&chan->timer); - chan->timer.function = cycx_x25_chan_timer; - chan->timer.data = (unsigned long)dev; - - /* Set channel timeouts (default if not specified) */ - chan->idle_tmout = conf->idle_timeout ? conf->idle_timeout : 90; - } else if (isdigit(conf->addr[0])) { /* PVC */ - s16 lcn = dec_to_uint(conf->addr, 0); - - if (lcn >= card->u.x.lo_pvc && lcn <= card->u.x.hi_pvc) - chan->lcn = lcn; - else { - pr_err("%s: PVC %u is out of range on interface %s!\n", - wandev->name, lcn, chan->name); - err = -EINVAL; - goto error; - } - } else { - pr_err("%s: invalid media address on interface %s!\n", - wandev->name, chan->name); - err = -EINVAL; - goto error; - } - - return 0; - -error: - free_netdev(dev); - return err; -} - -/* Delete logical channel. */ -static int cycx_wan_del_if(struct wan_device *wandev, struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - - if (chan->svc) { - kfree(chan->local_addr); - if (chan->state == WAN_CONNECTED) - del_timer(&chan->timer); - } - - return 0; -} - - -/* Network Device Interface */ - -static const struct header_ops cycx_header_ops = { - .create = cycx_netdevice_hard_header, - .rebuild = cycx_netdevice_rebuild_header, -}; - -static const struct net_device_ops cycx_netdev_ops = { - .ndo_init = cycx_netdevice_init, - .ndo_open = cycx_netdevice_open, - .ndo_stop = cycx_netdevice_stop, - .ndo_start_xmit = cycx_netdevice_hard_start_xmit, - .ndo_get_stats = cycx_netdevice_get_stats, -}; - -static void cycx_x25_chan_setup(struct net_device *dev) -{ - /* Initialize device driver entry points */ - dev->netdev_ops = &cycx_netdev_ops; - dev->header_ops = &cycx_header_ops; - - /* Initialize media-specific parameters */ - dev->mtu = CYCX_X25_CHAN_MTU; - dev->type = ARPHRD_HWX25; /* ARP h/w type */ - dev->hard_header_len = 0; /* media header length */ - dev->addr_len = 0; /* hardware address length */ -} - -/* Initialize Linux network interface. - * - * This routine is called only once for each interface, during Linux network - * interface registration. Returning anything but zero will fail interface - * registration. */ -static int cycx_netdevice_init(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - struct cycx_device *card = chan->card; - struct wan_device *wandev = &card->wandev; - - if (!chan->svc) - *(__be16*)dev->dev_addr = htons(chan->lcn); - - /* Initialize hardware parameters (just for reference) */ - dev->irq = wandev->irq; - dev->dma = wandev->dma; - dev->base_addr = wandev->ioport; - dev->mem_start = (unsigned long)wandev->maddr; - dev->mem_end = (unsigned long)(wandev->maddr + - wandev->msize - 1); - dev->flags |= IFF_NOARP; - - /* Set transmit buffer queue length */ - dev->tx_queue_len = 10; - - /* Initialize socket buffers */ - cycx_x25_set_chan_state(dev, WAN_DISCONNECTED); - - return 0; -} - -/* Open network interface. - * o prevent module from unloading by incrementing use count - * o if link is disconnected then initiate connection - * - * Return 0 if O.k. or errno. */ -static int cycx_netdevice_open(struct net_device *dev) -{ - if (netif_running(dev)) - return -EBUSY; /* only one open is allowed */ - - netif_start_queue(dev); - return 0; -} - -/* Close network interface. - * o reset flags. - * o if there's no more open channels then disconnect physical link. */ -static int cycx_netdevice_stop(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - - netif_stop_queue(dev); - - if (chan->state == WAN_CONNECTED || chan->state == WAN_CONNECTING) - cycx_x25_chan_disconnect(dev); - - return 0; -} - -/* Build media header. - * o encapsulate packet according to encapsulation type. - * - * The trick here is to put packet type (Ethertype) into 'protocol' field of - * the socket buffer, so that we don't forget it. If encapsulation fails, - * set skb->protocol to 0 and discard packet later. - * - * Return: media header length. */ -static int cycx_netdevice_hard_header(struct sk_buff *skb, - struct net_device *dev, u16 type, - const void *daddr, const void *saddr, - unsigned len) -{ - skb->protocol = htons(type); - - return dev->hard_header_len; -} - -/* * Re-build media header. - * Return: 1 physical address resolved. - * 0 physical address not resolved */ -static int cycx_netdevice_rebuild_header(struct sk_buff *skb) -{ - return 1; -} - -/* Send a packet on a network interface. - * o set busy flag (marks start of the transmission). - * o check link state. If link is not up, then drop the packet. - * o check channel status. If it's down then initiate a call. - * o pass a packet to corresponding WAN device. - * o free socket buffer - * - * Return: 0 complete (socket buffer must be freed) - * non-0 packet may be re-transmitted (tbusy must be set) - * - * Notes: - * 1. This routine is called either by the protocol stack or by the "net - * bottom half" (with interrupts enabled). - * 2. Setting tbusy flag will inhibit further transmit requests from the - * protocol stack and can be used for flow control with protocol layer. */ -static netdev_tx_t cycx_netdevice_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - struct cycx_device *card = chan->card; - - if (!chan->svc) - chan->protocol = ntohs(skb->protocol); - - if (card->wandev.state != WAN_CONNECTED) - ++chan->ifstats.tx_dropped; - else if (chan->svc && chan->protocol && - chan->protocol != ntohs(skb->protocol)) { - pr_info("%s: unsupported Ethertype 0x%04X on interface %s!\n", - card->devname, ntohs(skb->protocol), dev->name); - ++chan->ifstats.tx_errors; - } else if (chan->protocol == ETH_P_IP) { - switch (chan->state) { - case WAN_DISCONNECTED: - if (cycx_x25_chan_connect(dev)) { - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - } - /* fall thru */ - case WAN_CONNECTED: - reset_timer(dev); - dev->trans_start = jiffies; - netif_stop_queue(dev); - - if (cycx_x25_chan_send(dev, skb)) - return NETDEV_TX_BUSY; - - break; - default: - ++chan->ifstats.tx_dropped; - ++card->wandev.stats.tx_dropped; - } - } else { /* chan->protocol == ETH_P_X25 */ - switch (skb->data[0]) { - case X25_IFACE_DATA: - break; - case X25_IFACE_CONNECT: - cycx_x25_chan_connect(dev); - goto free_packet; - case X25_IFACE_DISCONNECT: - cycx_x25_chan_disconnect(dev); - goto free_packet; - default: - pr_info("%s: unknown %d x25-iface request on %s!\n", - card->devname, skb->data[0], dev->name); - ++chan->ifstats.tx_errors; - goto free_packet; - } - - skb_pull(skb, 1); /* Remove control byte */ - reset_timer(dev); - dev->trans_start = jiffies; - netif_stop_queue(dev); - - if (cycx_x25_chan_send(dev, skb)) { - /* prepare for future retransmissions */ - skb_push(skb, 1); - return NETDEV_TX_BUSY; - } - } - -free_packet: - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* Get Ethernet-style interface statistics. - * Return a pointer to struct net_device_stats */ -static struct net_device_stats *cycx_netdevice_get_stats(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - - return chan ? &chan->ifstats : NULL; -} - -/* Interrupt Handlers */ -/* X.25 Interrupt Service Routine. */ -static void cycx_x25_irq_handler(struct cycx_device *card) -{ - struct cycx_x25_cmd cmd; - u16 z = 0; - - card->in_isr = 1; - card->buff_int_mode_unbusy = 0; - cycx_peek(&card->hw, X25_RXMBOX_OFFS, &cmd, sizeof(cmd)); - - switch (cmd.command) { - case X25_DATA_INDICATION: - cycx_x25_irq_rx(card, &cmd); - break; - case X25_ACK_FROM_VC: - cycx_x25_irq_tx(card, &cmd); - break; - case X25_LOG: - cycx_x25_irq_log(card, &cmd); - break; - case X25_STATISTIC: - cycx_x25_irq_stat(card, &cmd); - break; - case X25_CONNECT_CONFIRM: - cycx_x25_irq_connect_confirm(card, &cmd); - break; - case X25_CONNECT_INDICATION: - cycx_x25_irq_connect(card, &cmd); - break; - case X25_DISCONNECT_INDICATION: - cycx_x25_irq_disconnect(card, &cmd); - break; - case X25_DISCONNECT_CONFIRM: - cycx_x25_irq_disconnect_confirm(card, &cmd); - break; - case X25_LINE_ON: - cycx_set_state(card, WAN_CONNECTED); - break; - case X25_LINE_OFF: - cycx_set_state(card, WAN_DISCONNECTED); - break; - default: - cycx_x25_irq_spurious(card, &cmd); - break; - } - - cycx_poke(&card->hw, 0, &z, sizeof(z)); - cycx_poke(&card->hw, X25_RXMBOX_OFFS, &z, sizeof(z)); - card->in_isr = 0; -} - -/* Transmit interrupt handler. - * o Release socket buffer - * o Clear 'tbusy' flag */ -static void cycx_x25_irq_tx(struct cycx_device *card, struct cycx_x25_cmd *cmd) -{ - struct net_device *dev; - struct wan_device *wandev = &card->wandev; - u8 lcn; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - - /* unbusy device and then dev_tint(); */ - dev = cycx_x25_get_dev_by_lcn(wandev, lcn); - if (dev) { - card->buff_int_mode_unbusy = 1; - netif_wake_queue(dev); - } else - pr_err("%s:ackvc for inexistent lcn %d\n", card->devname, lcn); -} - -/* Receive interrupt handler. - * This routine handles fragmented IP packets using M-bit according to the - * RFC1356. - * o map logical channel number to network interface. - * o allocate socket buffer or append received packet to the existing one. - * o if M-bit is reset (i.e. it's the last packet in a sequence) then - * decapsulate packet and pass socket buffer to the protocol stack. - * - * Notes: - * 1. When allocating a socket buffer, if M-bit is set then more data is - * coming and we have to allocate buffer for the maximum IP packet size - * expected on this channel. - * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no - * socket buffers available) the whole packet sequence must be discarded. */ -static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd) -{ - struct wan_device *wandev = &card->wandev; - struct net_device *dev; - struct cycx_x25_channel *chan; - struct sk_buff *skb; - u8 bitm, lcn; - int pktlen = cmd->len - 5; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - cycx_peek(&card->hw, cmd->buf + 4, &bitm, sizeof(bitm)); - bitm &= 0x10; - - dev = cycx_x25_get_dev_by_lcn(wandev, lcn); - if (!dev) { - /* Invalid channel, discard packet */ - pr_info("%s: receiving on orphaned LCN %d!\n", - card->devname, lcn); - return; - } - - chan = netdev_priv(dev); - reset_timer(dev); - - if (chan->drop_sequence) { - if (!bitm) - chan->drop_sequence = 0; - else - return; - } - - if ((skb = chan->rx_skb) == NULL) { - /* Allocate new socket buffer */ - int bufsize = bitm ? dev->mtu : pktlen; - - if ((skb = dev_alloc_skb((chan->protocol == ETH_P_X25 ? 1 : 0) + - bufsize + - dev->hard_header_len)) == NULL) { - pr_info("%s: no socket buffers available!\n", - card->devname); - chan->drop_sequence = 1; - ++chan->ifstats.rx_dropped; - return; - } - - if (chan->protocol == ETH_P_X25) /* X.25 socket layer control */ - /* 0 = data packet (dev_alloc_skb zeroed skb->data) */ - skb_put(skb, 1); - - skb->dev = dev; - skb->protocol = htons(chan->protocol); - chan->rx_skb = skb; - } - - if (skb_tailroom(skb) < pktlen) { - /* No room for the packet. Call off the whole thing! */ - dev_kfree_skb_irq(skb); - chan->rx_skb = NULL; - - if (bitm) - chan->drop_sequence = 1; - - pr_info("%s: unexpectedly long packet sequence on interface %s!\n", - card->devname, dev->name); - ++chan->ifstats.rx_length_errors; - return; - } - - /* Append packet to the socket buffer */ - cycx_peek(&card->hw, cmd->buf + 5, skb_put(skb, pktlen), pktlen); - - if (bitm) - return; /* more data is coming */ - - chan->rx_skb = NULL; /* dequeue packet */ - - ++chan->ifstats.rx_packets; - chan->ifstats.rx_bytes += pktlen; - - skb_reset_mac_header(skb); - netif_rx(skb); -} - -/* Connect interrupt handler. */ -static void cycx_x25_irq_connect(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - struct wan_device *wandev = &card->wandev; - struct net_device *dev = NULL; - struct cycx_x25_channel *chan; - u8 d[32], - loc[24], - rem[24]; - u8 lcn, sizeloc, sizerem; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - cycx_peek(&card->hw, cmd->buf + 5, &sizeloc, sizeof(sizeloc)); - cycx_peek(&card->hw, cmd->buf + 6, d, cmd->len - 6); - - sizerem = sizeloc >> 4; - sizeloc &= 0x0F; - - loc[0] = rem[0] = '\0'; - - if (sizeloc) - nibble_to_byte(d, loc, sizeloc, 0); - - if (sizerem) - nibble_to_byte(d + (sizeloc >> 1), rem, sizerem, sizeloc & 1); - - dprintk(1, KERN_INFO "%s:lcn=%d, local=%s, remote=%s\n", - __func__, lcn, loc, rem); - - dev = cycx_x25_get_dev_by_dte_addr(wandev, rem); - if (!dev) { - /* Invalid channel, discard packet */ - pr_info("%s: connect not expected: remote %s!\n", - card->devname, rem); - return; - } - - chan = netdev_priv(dev); - chan->lcn = lcn; - cycx_x25_connect_response(card, chan); - cycx_x25_set_chan_state(dev, WAN_CONNECTED); -} - -/* Connect confirm interrupt handler. */ -static void cycx_x25_irq_connect_confirm(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - struct wan_device *wandev = &card->wandev; - struct net_device *dev; - struct cycx_x25_channel *chan; - u8 lcn, key; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - cycx_peek(&card->hw, cmd->buf + 1, &key, sizeof(key)); - dprintk(1, KERN_INFO "%s: %s:lcn=%d, key=%d\n", - card->devname, __func__, lcn, key); - - dev = cycx_x25_get_dev_by_lcn(wandev, -key); - if (!dev) { - /* Invalid channel, discard packet */ - clear_bit(--key, (void*)&card->u.x.connection_keys); - pr_info("%s: connect confirm not expected: lcn %d, key=%d!\n", - card->devname, lcn, key); - return; - } - - clear_bit(--key, (void*)&card->u.x.connection_keys); - chan = netdev_priv(dev); - chan->lcn = lcn; - cycx_x25_set_chan_state(dev, WAN_CONNECTED); -} - -/* Disconnect confirm interrupt handler. */ -static void cycx_x25_irq_disconnect_confirm(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - struct wan_device *wandev = &card->wandev; - struct net_device *dev; - u8 lcn; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - dprintk(1, KERN_INFO "%s: %s:lcn=%d\n", - card->devname, __func__, lcn); - dev = cycx_x25_get_dev_by_lcn(wandev, lcn); - if (!dev) { - /* Invalid channel, discard packet */ - pr_info("%s:disconnect confirm not expected!:lcn %d\n", - card->devname, lcn); - return; - } - - cycx_x25_set_chan_state(dev, WAN_DISCONNECTED); -} - -/* disconnect interrupt handler. */ -static void cycx_x25_irq_disconnect(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - struct wan_device *wandev = &card->wandev; - struct net_device *dev; - u8 lcn; - - cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); - dprintk(1, KERN_INFO "%s:lcn=%d\n", __func__, lcn); - - dev = cycx_x25_get_dev_by_lcn(wandev, lcn); - if (dev) { - struct cycx_x25_channel *chan = netdev_priv(dev); - - cycx_x25_disconnect_response(card, chan->link, lcn); - cycx_x25_set_chan_state(dev, WAN_DISCONNECTED); - } else - cycx_x25_disconnect_response(card, 0, lcn); -} - -/* LOG interrupt handler. */ -static void cycx_x25_irq_log(struct cycx_device *card, struct cycx_x25_cmd *cmd) -{ -#if CYCLOMX_X25_DEBUG - char bf[20]; - u16 size, toread, link, msg_code; - u8 code, routine; - - cycx_peek(&card->hw, cmd->buf, &msg_code, sizeof(msg_code)); - cycx_peek(&card->hw, cmd->buf + 2, &link, sizeof(link)); - cycx_peek(&card->hw, cmd->buf + 4, &size, sizeof(size)); - /* at most 20 bytes are available... thanks to Daniela :) */ - toread = size < 20 ? size : 20; - cycx_peek(&card->hw, cmd->buf + 10, &bf, toread); - cycx_peek(&card->hw, cmd->buf + 10 + toread, &code, 1); - cycx_peek(&card->hw, cmd->buf + 10 + toread + 1, &routine, 1); - - pr_info("cycx_x25_irq_handler: X25_LOG (0x4500) indic.:\n"); - pr_info("cmd->buf=0x%X\n", cmd->buf); - pr_info("Log message code=0x%X\n", msg_code); - pr_info("Link=%d\n", link); - pr_info("log code=0x%X\n", code); - pr_info("log routine=0x%X\n", routine); - pr_info("Message size=%d\n", size); - hex_dump("Message", bf, toread); -#endif -} - -/* STATISTIC interrupt handler. */ -static void cycx_x25_irq_stat(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - cycx_peek(&card->hw, cmd->buf, &card->u.x.stats, - sizeof(card->u.x.stats)); - hex_dump("cycx_x25_irq_stat", (unsigned char*)&card->u.x.stats, - sizeof(card->u.x.stats)); - cycx_x25_dump_stats(&card->u.x.stats); - wake_up_interruptible(&card->wait_stats); -} - -/* Spurious interrupt handler. - * o print a warning - * If number of spurious interrupts exceeded some limit, then ??? */ -static void cycx_x25_irq_spurious(struct cycx_device *card, - struct cycx_x25_cmd *cmd) -{ - pr_info("%s: spurious interrupt (0x%X)!\n", - card->devname, cmd->command); -} -#ifdef CYCLOMX_X25_DEBUG -static void hex_dump(char *msg, unsigned char *p, int len) -{ - print_hex_dump(KERN_INFO, msg, DUMP_PREFIX_OFFSET, 16, 1, - p, len, true); -} -#endif - -/* Cyclom 2X Firmware-Specific Functions */ -/* Exec X.25 command. */ -static int x25_exec(struct cycx_device *card, int command, int link, - void *d1, int len1, void *d2, int len2) -{ - struct cycx_x25_cmd c; - unsigned long flags; - u32 addr = 0x1200 + 0x2E0 * link + 0x1E2; - u8 retry = CYCX_X25_MAX_CMD_RETRY; - int err = 0; - - c.command = command; - c.link = link; - c.len = len1 + len2; - - spin_lock_irqsave(&card->u.x.lock, flags); - - /* write command */ - cycx_poke(&card->hw, X25_MBOX_OFFS, &c, sizeof(c) - sizeof(c.buf)); - - /* write X.25 data */ - if (d1) { - cycx_poke(&card->hw, addr, d1, len1); - - if (d2) { - if (len2 > 254) { - u32 addr1 = 0xA00 + 0x400 * link; - - cycx_poke(&card->hw, addr + len1, d2, 249); - cycx_poke(&card->hw, addr1, ((u8*)d2) + 249, - len2 - 249); - } else - cycx_poke(&card->hw, addr + len1, d2, len2); - } - } - - /* generate interruption, executing command */ - cycx_intr(&card->hw); - - /* wait till card->mbox == 0 */ - do { - err = cycx_exec(card->mbox); - } while (retry-- && err); - - spin_unlock_irqrestore(&card->u.x.lock, flags); - - return err; -} - -/* Configure adapter. */ -static int cycx_x25_configure(struct cycx_device *card, - struct cycx_x25_config *conf) -{ - struct { - u16 nlinks; - struct cycx_x25_config conf[2]; - } x25_cmd_conf; - - memset(&x25_cmd_conf, 0, sizeof(x25_cmd_conf)); - x25_cmd_conf.nlinks = 2; - x25_cmd_conf.conf[0] = *conf; - /* FIXME: we need to find a way in the wanrouter framework - to configure the second link, for now lets use it - with the same config from the first link, fixing - the interface type to RS232, the speed in 38400 and - the clock to external */ - x25_cmd_conf.conf[1] = *conf; - x25_cmd_conf.conf[1].link = 1; - x25_cmd_conf.conf[1].speed = 5; /* 38400 */ - x25_cmd_conf.conf[1].clock = 8; - x25_cmd_conf.conf[1].flags = 0; /* default = RS232 */ - - cycx_x25_dump_config(&x25_cmd_conf.conf[0]); - cycx_x25_dump_config(&x25_cmd_conf.conf[1]); - - return x25_exec(card, X25_CONFIG, 0, - &x25_cmd_conf, sizeof(x25_cmd_conf), NULL, 0); -} - -/* Get protocol statistics. */ -static int cycx_x25_get_stats(struct cycx_device *card) -{ - /* the firmware expects 20 in the size field!!! - thanks to Daniela */ - int err = x25_exec(card, X25_STATISTIC, 0, NULL, 20, NULL, 0); - - if (err) - return err; - - interruptible_sleep_on(&card->wait_stats); - - if (signal_pending(current)) - return -EINTR; - - card->wandev.stats.rx_packets = card->u.x.stats.n2_rx_frames; - card->wandev.stats.rx_over_errors = card->u.x.stats.rx_over_errors; - card->wandev.stats.rx_crc_errors = card->u.x.stats.rx_crc_errors; - card->wandev.stats.rx_length_errors = 0; /* not available from fw */ - card->wandev.stats.rx_frame_errors = 0; /* not available from fw */ - card->wandev.stats.rx_missed_errors = card->u.x.stats.rx_aborts; - card->wandev.stats.rx_dropped = 0; /* not available from fw */ - card->wandev.stats.rx_errors = 0; /* not available from fw */ - card->wandev.stats.tx_packets = card->u.x.stats.n2_tx_frames; - card->wandev.stats.tx_aborted_errors = card->u.x.stats.tx_aborts; - card->wandev.stats.tx_dropped = 0; /* not available from fw */ - card->wandev.stats.collisions = 0; /* not available from fw */ - card->wandev.stats.tx_errors = 0; /* not available from fw */ - - cycx_x25_dump_devs(&card->wandev); - - return 0; -} - -/* return the number of nibbles */ -static int byte_to_nibble(u8 *s, u8 *d, char *nibble) -{ - int i = 0; - - if (*nibble && *s) { - d[i] |= *s++ - '0'; - *nibble = 0; - ++i; - } - - while (*s) { - d[i] = (*s - '0') << 4; - if (*(s + 1)) - d[i] |= *(s + 1) - '0'; - else { - *nibble = 1; - break; - } - ++i; - s += 2; - } - - return i; -} - -static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble) -{ - if (nibble) { - *d++ = '0' + (*s++ & 0x0F); - --len; - } - - while (len) { - *d++ = '0' + (*s >> 4); - - if (--len) { - *d++ = '0' + (*s & 0x0F); - --len; - } else break; - - ++s; - } - - *d = '\0'; -} - -/* Place X.25 call. */ -static int x25_place_call(struct cycx_device *card, - struct cycx_x25_channel *chan) -{ - int err = 0, - len; - char d[64], - nibble = 0, - mylen = chan->local_addr ? strlen(chan->local_addr) : 0, - remotelen = strlen(chan->addr); - u8 key; - - if (card->u.x.connection_keys == ~0U) { - pr_info("%s: too many simultaneous connection requests!\n", - card->devname); - return -EAGAIN; - } - - key = ffz(card->u.x.connection_keys); - set_bit(key, (void*)&card->u.x.connection_keys); - ++key; - dprintk(1, KERN_INFO "%s:x25_place_call:key=%d\n", card->devname, key); - memset(d, 0, sizeof(d)); - d[1] = key; /* user key */ - d[2] = 0x10; - d[4] = 0x0B; - - len = byte_to_nibble(chan->addr, d + 6, &nibble); - - if (chan->local_addr) - len += byte_to_nibble(chan->local_addr, d + 6 + len, &nibble); - - if (nibble) - ++len; - - d[5] = mylen << 4 | remotelen; - d[6 + len + 1] = 0xCC; /* TCP/IP over X.25, thanks to Daniela :) */ - - if ((err = x25_exec(card, X25_CONNECT_REQUEST, chan->link, - &d, 7 + len + 1, NULL, 0)) != 0) - clear_bit(--key, (void*)&card->u.x.connection_keys); - else - chan->lcn = -key; - - return err; -} - -/* Place X.25 CONNECT RESPONSE. */ -static int cycx_x25_connect_response(struct cycx_device *card, - struct cycx_x25_channel *chan) -{ - u8 d[8]; - - memset(d, 0, sizeof(d)); - d[0] = d[3] = chan->lcn; - d[2] = 0x10; - d[4] = 0x0F; - d[7] = 0xCC; /* TCP/IP over X.25, thanks Daniela */ - - return x25_exec(card, X25_CONNECT_RESPONSE, chan->link, &d, 8, NULL, 0); -} - -/* Place X.25 DISCONNECT RESPONSE. */ -static int cycx_x25_disconnect_response(struct cycx_device *card, u8 link, - u8 lcn) -{ - char d[5]; - - memset(d, 0, sizeof(d)); - d[0] = d[3] = lcn; - d[2] = 0x10; - d[4] = 0x17; - - return x25_exec(card, X25_DISCONNECT_RESPONSE, link, &d, 5, NULL, 0); -} - -/* Clear X.25 call. */ -static int x25_clear_call(struct cycx_device *card, u8 link, u8 lcn, u8 cause, - u8 diagn) -{ - u8 d[7]; - - memset(d, 0, sizeof(d)); - d[0] = d[3] = lcn; - d[2] = 0x10; - d[4] = 0x13; - d[5] = cause; - d[6] = diagn; - - return x25_exec(card, X25_DISCONNECT_REQUEST, link, d, 7, NULL, 0); -} - -/* Send X.25 data packet. */ -static int cycx_x25_send(struct cycx_device *card, u8 link, u8 lcn, u8 bitm, - int len, void *buf) -{ - u8 d[] = "?\xFF\x10??"; - - d[0] = d[3] = lcn; - d[4] = bitm; - - return x25_exec(card, X25_DATA_REQUEST, link, &d, 5, buf, len); -} - -/* Miscellaneous */ -/* Find network device by its channel number. */ -static struct net_device *cycx_x25_get_dev_by_lcn(struct wan_device *wandev, - s16 lcn) -{ - struct net_device *dev = wandev->dev; - struct cycx_x25_channel *chan; - - while (dev) { - chan = netdev_priv(dev); - - if (chan->lcn == lcn) - break; - dev = chan->slave; - } - return dev; -} - -/* Find network device by its remote dte address. */ -static struct net_device * - cycx_x25_get_dev_by_dte_addr(struct wan_device *wandev, char *dte) -{ - struct net_device *dev = wandev->dev; - struct cycx_x25_channel *chan; - - while (dev) { - chan = netdev_priv(dev); - - if (!strcmp(chan->addr, dte)) - break; - dev = chan->slave; - } - return dev; -} - -/* Initiate connection on the logical channel. - * o for PVC we just get channel configuration - * o for SVCs place an X.25 call - * - * Return: 0 connected - * >0 connection in progress - * <0 failure */ -static int cycx_x25_chan_connect(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - struct cycx_device *card = chan->card; - - if (chan->svc) { - if (!chan->addr[0]) - return -EINVAL; /* no destination address */ - - dprintk(1, KERN_INFO "%s: placing X.25 call to %s...\n", - card->devname, chan->addr); - - if (x25_place_call(card, chan)) - return -EIO; - - cycx_x25_set_chan_state(dev, WAN_CONNECTING); - return 1; - } else - cycx_x25_set_chan_state(dev, WAN_CONNECTED); - - return 0; -} - -/* Disconnect logical channel. - * o if SVC then clear X.25 call */ -static void cycx_x25_chan_disconnect(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - - if (chan->svc) { - x25_clear_call(chan->card, chan->link, chan->lcn, 0, 0); - cycx_x25_set_chan_state(dev, WAN_DISCONNECTING); - } else - cycx_x25_set_chan_state(dev, WAN_DISCONNECTED); -} - -/* Called by kernel timer */ -static void cycx_x25_chan_timer(unsigned long d) -{ - struct net_device *dev = (struct net_device *)d; - struct cycx_x25_channel *chan = netdev_priv(dev); - - if (chan->state == WAN_CONNECTED) - cycx_x25_chan_disconnect(dev); - else - pr_err("%s: %s for svc (%s) not connected!\n", - chan->card->devname, __func__, dev->name); -} - -/* Set logical channel state. */ -static void cycx_x25_set_chan_state(struct net_device *dev, u8 state) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - struct cycx_device *card = chan->card; - unsigned long flags; - char *string_state = NULL; - - spin_lock_irqsave(&card->lock, flags); - - if (chan->state != state) { - if (chan->svc && chan->state == WAN_CONNECTED) - del_timer(&chan->timer); - - switch (state) { - case WAN_CONNECTED: - string_state = "connected!"; - *(__be16*)dev->dev_addr = htons(chan->lcn); - netif_wake_queue(dev); - reset_timer(dev); - - if (chan->protocol == ETH_P_X25) - cycx_x25_chan_send_event(dev, - X25_IFACE_CONNECT); - - break; - case WAN_CONNECTING: - string_state = "connecting..."; - break; - case WAN_DISCONNECTING: - string_state = "disconnecting..."; - break; - case WAN_DISCONNECTED: - string_state = "disconnected!"; - - if (chan->svc) { - *(unsigned short*)dev->dev_addr = 0; - chan->lcn = 0; - } - - if (chan->protocol == ETH_P_X25) - cycx_x25_chan_send_event(dev, - X25_IFACE_DISCONNECT); - - netif_wake_queue(dev); - break; - } - - pr_info("%s: interface %s %s\n", - card->devname, dev->name, string_state); - chan->state = state; - } - - spin_unlock_irqrestore(&card->lock, flags); -} - -/* Send packet on a logical channel. - * When this function is called, tx_skb field of the channel data space - * points to the transmit socket buffer. When transmission is complete, - * release socket buffer and reset 'tbusy' flag. - * - * Return: 0 - transmission complete - * 1 - busy - * - * Notes: - * 1. If packet length is greater than MTU for this channel, we'll fragment - * the packet into 'complete sequence' using M-bit. - * 2. When transmission is complete, an event notification should be issued - * to the router. */ -static int cycx_x25_chan_send(struct net_device *dev, struct sk_buff *skb) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - struct cycx_device *card = chan->card; - int bitm = 0; /* final packet */ - unsigned len = skb->len; - - if (skb->len > card->wandev.mtu) { - len = card->wandev.mtu; - bitm = 0x10; /* set M-bit (more data) */ - } - - if (cycx_x25_send(card, chan->link, chan->lcn, bitm, len, skb->data)) - return 1; - - if (bitm) { - skb_pull(skb, len); - return 1; - } - - ++chan->ifstats.tx_packets; - chan->ifstats.tx_bytes += len; - - return 0; -} - -/* Send event (connection, disconnection, etc) to X.25 socket layer */ - -static void cycx_x25_chan_send_event(struct net_device *dev, u8 event) -{ - struct sk_buff *skb; - unsigned char *ptr; - - if ((skb = dev_alloc_skb(1)) == NULL) { - pr_err("%s: out of memory\n", __func__); - return; - } - - ptr = skb_put(skb, 1); - *ptr = event; - - skb->protocol = x25_type_trans(skb, dev); - netif_rx(skb); -} - -/* Convert line speed in bps to a number used by cyclom 2x code. */ -static u8 bps_to_speed_code(u32 bps) -{ - u8 number = 0; /* defaults to the lowest (1200) speed ;> */ - - if (bps >= 512000) number = 8; - else if (bps >= 256000) number = 7; - else if (bps >= 64000) number = 6; - else if (bps >= 38400) number = 5; - else if (bps >= 19200) number = 4; - else if (bps >= 9600) number = 3; - else if (bps >= 4800) number = 2; - else if (bps >= 2400) number = 1; - - return number; -} - -/* log base 2 */ -static u8 cycx_log2(u32 n) -{ - u8 log = 0; - - if (!n) - return 0; - - while (n > 1) { - n >>= 1; - ++log; - } - - return log; -} - -/* Convert decimal string to unsigned integer. - * If len != 0 then only 'len' characters of the string are converted. */ -static unsigned dec_to_uint(u8 *str, int len) -{ - unsigned val = 0; - - if (!len) - len = strlen(str); - - for (; len && isdigit(*str); ++str, --len) - val = (val * 10) + (*str - (unsigned) '0'); - - return val; -} - -static void reset_timer(struct net_device *dev) -{ - struct cycx_x25_channel *chan = netdev_priv(dev); - - if (chan->svc) - mod_timer(&chan->timer, jiffies+chan->idle_tmout*HZ); -} -#ifdef CYCLOMX_X25_DEBUG -static void cycx_x25_dump_config(struct cycx_x25_config *conf) -{ - pr_info("X.25 configuration\n"); - pr_info("-----------------\n"); - pr_info("link number=%d\n", conf->link); - pr_info("line speed=%d\n", conf->speed); - pr_info("clock=%sternal\n", conf->clock == 8 ? "Ex" : "In"); - pr_info("# level 2 retransm.=%d\n", conf->n2); - pr_info("level 2 window=%d\n", conf->n2win); - pr_info("level 3 window=%d\n", conf->n3win); - pr_info("# logical channels=%d\n", conf->nvc); - pr_info("level 3 pkt len=%d\n", conf->pktlen); - pr_info("my address=%d\n", conf->locaddr); - pr_info("remote address=%d\n", conf->remaddr); - pr_info("t1=%d seconds\n", conf->t1); - pr_info("t2=%d seconds\n", conf->t2); - pr_info("t21=%d seconds\n", conf->t21); - pr_info("# PVCs=%d\n", conf->npvc); - pr_info("t23=%d seconds\n", conf->t23); - pr_info("flags=0x%x\n", conf->flags); -} - -static void cycx_x25_dump_stats(struct cycx_x25_stats *stats) -{ - pr_info("X.25 statistics\n"); - pr_info("--------------\n"); - pr_info("rx_crc_errors=%d\n", stats->rx_crc_errors); - pr_info("rx_over_errors=%d\n", stats->rx_over_errors); - pr_info("n2_tx_frames=%d\n", stats->n2_tx_frames); - pr_info("n2_rx_frames=%d\n", stats->n2_rx_frames); - pr_info("tx_timeouts=%d\n", stats->tx_timeouts); - pr_info("rx_timeouts=%d\n", stats->rx_timeouts); - pr_info("n3_tx_packets=%d\n", stats->n3_tx_packets); - pr_info("n3_rx_packets=%d\n", stats->n3_rx_packets); - pr_info("tx_aborts=%d\n", stats->tx_aborts); - pr_info("rx_aborts=%d\n", stats->rx_aborts); -} - -static void cycx_x25_dump_devs(struct wan_device *wandev) -{ - struct net_device *dev = wandev->dev; - - pr_info("X.25 dev states\n"); - pr_info("name: addr: txoff: protocol:\n"); - pr_info("---------------------------------------\n"); - - while(dev) { - struct cycx_x25_channel *chan = netdev_priv(dev); - - pr_info("%-5.5s %-15.15s %d ETH_P_%s\n", - chan->name, chan->addr, netif_queue_stopped(dev), - chan->protocol == ETH_P_IP ? "IP" : "X25"); - dev = chan->slave; - } -} - -#endif /* CYCLOMX_X25_DEBUG */ -/* End */ diff --git a/include/linux/cyclomx.h b/include/linux/cyclomx.h deleted file mode 100644 index b88f7f428e58..000000000000 --- a/include/linux/cyclomx.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _CYCLOMX_H -#define _CYCLOMX_H -/* -* cyclomx.h Cyclom 2X WAN Link Driver. -* User-level API definitions. -* -* Author: Arnaldo Carvalho de Melo -* -* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo -* -* Based on wanpipe.h by Gene Kozin -* -* 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. -* ============================================================================ -* 2000/07/13 acme remove crap #if KERNEL_VERSION > blah -* 2000/01/21 acme rename cyclomx_open to cyclomx_mod_inc_use_count -* and cyclomx_close to cyclomx_mod_dec_use_count -* 1999/05/19 acme wait_queue_head_t wait_stats(support for 2.3.*) -* 1999/01/03 acme judicious use of data types -* 1998/12/27 acme cleanup: PACKED not needed -* 1998/08/08 acme Version 0.0.1 -*/ - -#include -#include - -#ifdef __KERNEL__ -/* Kernel Interface */ - -#include /* Cyclom 2X support module API definitions */ -#include /* Cyclom 2X firmware module definitions */ -#ifdef CONFIG_CYCLOMX_X25 -#include -#endif - -/* Adapter Data Space. - * This structure is needed because we handle multiple cards, otherwise - * static data would do it. - */ -struct cycx_device { - char devname[WAN_DRVNAME_SZ + 1];/* card name */ - struct cycx_hw hw; /* hardware configuration */ - struct wan_device wandev; /* WAN device data space */ - u32 state_tick; /* link state timestamp */ - spinlock_t lock; - char in_isr; /* interrupt-in-service flag */ - char buff_int_mode_unbusy; /* flag for carrying out dev_tint */ - wait_queue_head_t wait_stats; /* to wait for the STATS indication */ - void __iomem *mbox; /* -> mailbox */ - void (*isr)(struct cycx_device* card); /* interrupt service routine */ - int (*exec)(struct cycx_device* card, void* u_cmd, void* u_data); - union { -#ifdef CONFIG_CYCLOMX_X25 - struct { /* X.25 specific data */ - u32 lo_pvc; - u32 hi_pvc; - u32 lo_svc; - u32 hi_svc; - struct cycx_x25_stats stats; - spinlock_t lock; - u32 connection_keys; - } x; -#endif - } u; -}; - -/* Public Functions */ -void cycx_set_state(struct cycx_device *card, int state); - -#ifdef CONFIG_CYCLOMX_X25 -int cycx_x25_wan_init(struct cycx_device *card, wandev_conf_t *conf); -#endif -#endif /* __KERNEL__ */ -#endif /* _CYCLOMX_H */ diff --git a/include/linux/cycx_drv.h b/include/linux/cycx_drv.h deleted file mode 100644 index 12fe6b0bfcff..000000000000 --- a/include/linux/cycx_drv.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -* cycx_drv.h CYCX Support Module. Kernel API Definitions. -* -* Author: Arnaldo Carvalho de Melo -* -* Copyright: (c) 1998-2003 Arnaldo Carvalho de Melo -* -* Based on sdladrv.h by Gene Kozin -* -* 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. -* ============================================================================ -* 1999/10/23 acme cycxhw_t cleanup -* 1999/01/03 acme more judicious use of data types... -* uclong, ucchar, etc deleted, the u8, u16, u32 -* types are the portable way to go. -* 1999/01/03 acme judicious use of data types... u16, u32, etc -* 1998/12/26 acme FIXED_BUFFERS, CONF_OFFSET, -* removal of cy_read{bwl} -* 1998/08/08 acme Initial version. -*/ -#ifndef _CYCX_DRV_H -#define _CYCX_DRV_H - -#define CYCX_WINDOWSIZE 0x4000 /* default dual-port memory window size */ -#define GEN_CYCX_INTR 0x02 -#define RST_ENABLE 0x04 -#define START_CPU 0x06 -#define RST_DISABLE 0x08 -#define FIXED_BUFFERS 0x08 -#define TEST_PATTERN 0xaa55 -#define CMD_OFFSET 0x20 -#define CONF_OFFSET 0x0380 -#define RESET_OFFSET 0x3c00 /* For reset file load */ -#define DATA_OFFSET 0x0100 /* For code and data files load */ -#define START_OFFSET 0x3ff0 /* 80186 starts here */ - -/** - * struct cycx_hw - Adapter hardware configuration - * @fwid - firmware ID - * @irq - interrupt request level - * @dpmbase - dual-port memory base - * @dpmsize - dual-port memory size - * @reserved - reserved for future use - */ -struct cycx_hw { - u32 fwid; - int irq; - void __iomem *dpmbase; - u32 dpmsize; - u32 reserved[5]; -}; - -/* Function Prototypes */ -extern int cycx_setup(struct cycx_hw *hw, void *sfm, u32 len, unsigned long base); -extern int cycx_down(struct cycx_hw *hw); -extern int cycx_peek(struct cycx_hw *hw, u32 addr, void *buf, u32 len); -extern int cycx_poke(struct cycx_hw *hw, u32 addr, void *buf, u32 len); -extern int cycx_exec(void __iomem *addr); - -extern void cycx_intr(struct cycx_hw *hw); -#endif /* _CYCX_DRV_H */ diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h index cec4b4159767..8198a63cf459 100644 --- a/include/linux/wanrouter.h +++ b/include/linux/wanrouter.h @@ -1,129 +1,10 @@ -/***************************************************************************** -* wanrouter.h Definitions for the WAN Multiprotocol Router Module. -* This module provides API and common services for WAN Link -* Drivers and is completely hardware-independent. -* -* Author: Nenad Corbic -* Gideon Hack -* Additions: Arnaldo Melo -* -* Copyright: (c) 1995-2000 Sangoma Technologies Inc. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version -* 2 of the License, or (at your option) any later version. -* ============================================================================ -* Jul 21, 2000 Nenad Corbic Added WAN_FT1_READY State -* Feb 24, 2000 Nenad Corbic Added support for socket based x25api -* Jan 28, 2000 Nenad Corbic Added support for the ASYNC protocol. -* Oct 04, 1999 Nenad Corbic Updated for 2.1.0 release -* Jun 02, 1999 Gideon Hack Added support for the S514 adapter. -* May 23, 1999 Arnaldo Melo Added local_addr to wanif_conf_t -* WAN_DISCONNECTING state added -* Jul 20, 1998 David Fong Added Inverse ARP options to 'wanif_conf_t' -* Jun 12, 1998 David Fong Added Cisco HDLC support. -* Dec 16, 1997 Jaspreet Singh Moved 'enable_IPX' and 'network_number' to -* 'wanif_conf_t' -* Dec 05, 1997 Jaspreet Singh Added 'pap', 'chap' to 'wanif_conf_t' -* Added 'authenticator' to 'wan_ppp_conf_t' -* Nov 06, 1997 Jaspreet Singh Changed Router Driver version to 1.1 from 1.0 -* Oct 20, 1997 Jaspreet Singh Added 'cir','bc','be' and 'mc' to 'wanif_conf_t' -* Added 'enable_IPX' and 'network_number' to -* 'wan_device_t'. Also added defines for -* UDP PACKET TYPE, Interrupt test, critical values -* for RACE conditions. -* Oct 05, 1997 Jaspreet Singh Added 'dlci_num' and 'dlci[100]' to -* 'wan_fr_conf_t' to configure a list of dlci(s) -* for a NODE -* Jul 07, 1997 Jaspreet Singh Added 'ttl' to 'wandev_conf_t' & 'wan_device_t' -* May 29, 1997 Jaspreet Singh Added 'tx_int_enabled' to 'wan_device_t' -* May 21, 1997 Jaspreet Singh Added 'udp_port' to 'wan_device_t' -* Apr 25, 1997 Farhan Thawar Added 'udp_port' to 'wandev_conf_t' -* Jan 16, 1997 Gene Kozin router_devlist made public -* Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h). -*****************************************************************************/ +/* + * wanrouter.h Legacy declarations kept around until X25 is removed + */ + #ifndef _ROUTER_H #define _ROUTER_H #include -/****** Kernel Interface ****************************************************/ - -#include /* support for device drivers */ -#include /* proc filesystem pragmatics */ -#include /* support for network drivers */ -#include /* Support for SMP Locking */ - -/*---------------------------------------------------------------------------- - * WAN device data space. - */ -struct wan_device { - unsigned magic; /* magic number */ - char* name; /* -> WAN device name (ASCIIZ) */ - void* private; /* -> driver private data */ - unsigned config_id; /* Configuration ID */ - /****** hardware configuration ******/ - unsigned ioport; /* adapter I/O port base #1 */ - char S514_cpu_no[1]; /* PCI CPU Number */ - unsigned char S514_slot_no; /* PCI Slot Number */ - unsigned long maddr; /* dual-port memory address */ - unsigned msize; /* dual-port memory size */ - int irq; /* interrupt request level */ - int dma; /* DMA request level */ - unsigned bps; /* data transfer rate */ - unsigned mtu; /* max physical transmit unit size */ - unsigned udp_port; /* UDP port for management */ - unsigned char ttl; /* Time To Live for UDP security */ - unsigned enable_tx_int; /* Transmit Interrupt enabled or not */ - char interface; /* RS-232/V.35, etc. */ - char clocking; /* external/internal */ - char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */ - char station; /* DTE/DCE, primary/secondary, etc. */ - char connection; /* permanent/switched/on-demand */ - char signalling; /* Signalling RS232 or V35 */ - char read_mode; /* read mode: Polling or interrupt */ - char new_if_cnt; /* Number of interfaces per wanpipe */ - char del_if_cnt; /* Number of times del_if() gets called */ - unsigned char piggyback; /* Piggibacking a port */ - unsigned hw_opt[4]; /* other hardware options */ - /****** status and statistics *******/ - char state; /* device state */ - char api_status; /* device api status */ - struct net_device_stats stats; /* interface statistics */ - unsigned reserved[16]; /* reserved for future use */ - unsigned long critical; /* critical section flag */ - spinlock_t lock; /* Support for SMP Locking */ - - /****** device management methods ***/ - int (*setup) (struct wan_device *wandev, wandev_conf_t *conf); - int (*shutdown) (struct wan_device *wandev); - int (*update) (struct wan_device *wandev); - int (*ioctl) (struct wan_device *wandev, unsigned cmd, - unsigned long arg); - int (*new_if)(struct wan_device *wandev, struct net_device *dev, - wanif_conf_t *conf); - int (*del_if)(struct wan_device *wandev, struct net_device *dev); - /****** maintained by the router ****/ - struct wan_device* next; /* -> next device */ - struct net_device* dev; /* list of network interfaces */ - unsigned ndev; /* number of interfaces */ - struct proc_dir_entry *dent; /* proc filesystem entry */ -}; - -/* Public functions available for device drivers */ -extern int register_wan_device(struct wan_device *wandev); -extern int unregister_wan_device(char *name); - -/* Proc interface functions. These must not be called by the drivers! */ -extern int wanrouter_proc_init(void); -extern void wanrouter_proc_cleanup(void); -extern int wanrouter_proc_add(struct wan_device *wandev); -extern int wanrouter_proc_delete(struct wan_device *wandev); -extern long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg); - -/* Public Data */ -/* list of registered devices */ -extern struct wan_device *wanrouter_router_devlist; - #endif /* _ROUTER_H */ diff --git a/include/uapi/linux/wanrouter.h b/include/uapi/linux/wanrouter.h index 7617df2833d5..498d6c12c666 100644 --- a/include/uapi/linux/wanrouter.h +++ b/include/uapi/linux/wanrouter.h @@ -1,363 +1,9 @@ -/***************************************************************************** -* wanrouter.h Definitions for the WAN Multiprotocol Router Module. -* This module provides API and common services for WAN Link -* Drivers and is completely hardware-independent. -* -* Author: Nenad Corbic -* Gideon Hack -* Additions: Arnaldo Melo -* -* Copyright: (c) 1995-2000 Sangoma Technologies Inc. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version -* 2 of the License, or (at your option) any later version. -* ============================================================================ -* Jul 21, 2000 Nenad Corbic Added WAN_FT1_READY State -* Feb 24, 2000 Nenad Corbic Added support for socket based x25api -* Jan 28, 2000 Nenad Corbic Added support for the ASYNC protocol. -* Oct 04, 1999 Nenad Corbic Updated for 2.1.0 release -* Jun 02, 1999 Gideon Hack Added support for the S514 adapter. -* May 23, 1999 Arnaldo Melo Added local_addr to wanif_conf_t -* WAN_DISCONNECTING state added -* Jul 20, 1998 David Fong Added Inverse ARP options to 'wanif_conf_t' -* Jun 12, 1998 David Fong Added Cisco HDLC support. -* Dec 16, 1997 Jaspreet Singh Moved 'enable_IPX' and 'network_number' to -* 'wanif_conf_t' -* Dec 05, 1997 Jaspreet Singh Added 'pap', 'chap' to 'wanif_conf_t' -* Added 'authenticator' to 'wan_ppp_conf_t' -* Nov 06, 1997 Jaspreet Singh Changed Router Driver version to 1.1 from 1.0 -* Oct 20, 1997 Jaspreet Singh Added 'cir','bc','be' and 'mc' to 'wanif_conf_t' -* Added 'enable_IPX' and 'network_number' to -* 'wan_device_t'. Also added defines for -* UDP PACKET TYPE, Interrupt test, critical values -* for RACE conditions. -* Oct 05, 1997 Jaspreet Singh Added 'dlci_num' and 'dlci[100]' to -* 'wan_fr_conf_t' to configure a list of dlci(s) -* for a NODE -* Jul 07, 1997 Jaspreet Singh Added 'ttl' to 'wandev_conf_t' & 'wan_device_t' -* May 29, 1997 Jaspreet Singh Added 'tx_int_enabled' to 'wan_device_t' -* May 21, 1997 Jaspreet Singh Added 'udp_port' to 'wan_device_t' -* Apr 25, 1997 Farhan Thawar Added 'udp_port' to 'wandev_conf_t' -* Jan 16, 1997 Gene Kozin router_devlist made public -* Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h). -*****************************************************************************/ - -#ifndef _UAPI_ROUTER_H -#define _UAPI_ROUTER_H - -#define ROUTER_NAME "wanrouter" /* in case we ever change it */ -#define ROUTER_VERSION 1 /* version number */ -#define ROUTER_RELEASE 1 /* release (minor version) number */ -#define ROUTER_IOCTL 'W' /* for IOCTL calls */ -#define ROUTER_MAGIC 0x524D4157L /* signature: 'WANR' reversed */ - -/* IOCTL codes for /proc/router/ entries (up to 255) */ -enum router_ioctls -{ - ROUTER_SETUP = ROUTER_IOCTL<<8, /* configure device */ - ROUTER_DOWN, /* shut down device */ - ROUTER_STAT, /* get device status */ - ROUTER_IFNEW, /* add interface */ - ROUTER_IFDEL, /* delete interface */ - ROUTER_IFSTAT, /* get interface status */ - ROUTER_USER = (ROUTER_IOCTL<<8)+16, /* driver-specific calls */ - ROUTER_USER_MAX = (ROUTER_IOCTL<<8)+31 -}; - -/* identifiers for displaying proc file data for dual port adapters */ -#define PROC_DATA_PORT_0 0x8000 /* the data is for port 0 */ -#define PROC_DATA_PORT_1 0x8001 /* the data is for port 1 */ - -/* NLPID for packet encapsulation (ISO/IEC TR 9577) */ -#define NLPID_IP 0xCC /* Internet Protocol Datagram */ -#define NLPID_SNAP 0x80 /* IEEE Subnetwork Access Protocol */ -#define NLPID_CLNP 0x81 /* ISO/IEC 8473 */ -#define NLPID_ESIS 0x82 /* ISO/IEC 9542 */ -#define NLPID_ISIS 0x83 /* ISO/IEC ISIS */ -#define NLPID_Q933 0x08 /* CCITT Q.933 */ - -/* Miscellaneous */ -#define WAN_IFNAME_SZ 15 /* max length of the interface name */ -#define WAN_DRVNAME_SZ 15 /* max length of the link driver name */ -#define WAN_ADDRESS_SZ 31 /* max length of the WAN media address */ -#define USED_BY_FIELD 8 /* max length of the used by field */ - -/* Defines for UDP PACKET TYPE */ -#define UDP_PTPIPE_TYPE 0x01 -#define UDP_FPIPE_TYPE 0x02 -#define UDP_CPIPE_TYPE 0x03 -#define UDP_DRVSTATS_TYPE 0x04 -#define UDP_INVALID_TYPE 0x05 - -/* Command return code */ -#define CMD_OK 0 /* normal firmware return code */ -#define CMD_TIMEOUT 0xFF /* firmware command timed out */ - -/* UDP Packet Management */ -#define UDP_PKT_FRM_STACK 0x00 -#define UDP_PKT_FRM_NETWORK 0x01 - -/* Maximum interrupt test counter */ -#define MAX_INTR_TEST_COUNTER 100 - -/* Critical Values for RACE conditions*/ -#define CRITICAL_IN_ISR 0xA1 -#define CRITICAL_INTR_HANDLED 0xB1 - -/****** Data Types **********************************************************/ - -/*---------------------------------------------------------------------------- - * X.25-specific link-level configuration. - */ -typedef struct wan_x25_conf -{ - unsigned lo_pvc; /* lowest permanent circuit number */ - unsigned hi_pvc; /* highest permanent circuit number */ - unsigned lo_svc; /* lowest switched circuit number */ - unsigned hi_svc; /* highest switched circuit number */ - unsigned hdlc_window; /* HDLC window size (1..7) */ - unsigned pkt_window; /* X.25 packet window size (1..7) */ - unsigned t1; /* HDLC timer T1, sec (1..30) */ - unsigned t2; /* HDLC timer T2, sec (0..29) */ - unsigned t4; /* HDLC supervisory frame timer = T4 * T1 */ - unsigned n2; /* HDLC retransmission limit (1..30) */ - unsigned t10_t20; /* X.25 RESTART timeout, sec (1..255) */ - unsigned t11_t21; /* X.25 CALL timeout, sec (1..255) */ - unsigned t12_t22; /* X.25 RESET timeout, sec (1..255) */ - unsigned t13_t23; /* X.25 CLEAR timeout, sec (1..255) */ - unsigned t16_t26; /* X.25 INTERRUPT timeout, sec (1..255) */ - unsigned t28; /* X.25 REGISTRATION timeout, sec (1..255) */ - unsigned r10_r20; /* RESTART retransmission limit (0..250) */ - unsigned r12_r22; /* RESET retransmission limit (0..250) */ - unsigned r13_r23; /* CLEAR retransmission limit (0..250) */ - unsigned ccitt_compat; /* compatibility mode: 1988/1984/1980 */ - unsigned x25_conf_opt; /* User defined x25 config optoins */ - unsigned char LAPB_hdlc_only; /* Run in HDLC only mode */ - unsigned char logging; /* Control connection logging */ - unsigned char oob_on_modem; /* Whether to send modem status to the user app */ -} wan_x25_conf_t; - -/*---------------------------------------------------------------------------- - * Frame relay specific link-level configuration. - */ -typedef struct wan_fr_conf -{ - unsigned signalling; /* local in-channel signalling type */ - unsigned t391; /* link integrity verification timer */ - unsigned t392; /* polling verification timer */ - unsigned n391; /* full status polling cycle counter */ - unsigned n392; /* error threshold counter */ - unsigned n393; /* monitored events counter */ - unsigned dlci_num; /* number of DLCs (access node) */ - unsigned dlci[100]; /* List of all DLCIs */ -} wan_fr_conf_t; - -/*---------------------------------------------------------------------------- - * PPP-specific link-level configuration. - */ -typedef struct wan_ppp_conf -{ - unsigned restart_tmr; /* restart timer */ - unsigned auth_rsrt_tmr; /* authentication timer */ - unsigned auth_wait_tmr; /* authentication timer */ - unsigned mdm_fail_tmr; /* modem failure timer */ - unsigned dtr_drop_tmr; /* DTR drop timer */ - unsigned connect_tmout; /* connection timeout */ - unsigned conf_retry; /* max. retry */ - unsigned term_retry; /* max. retry */ - unsigned fail_retry; /* max. retry */ - unsigned auth_retry; /* max. retry */ - unsigned auth_options; /* authentication opt. */ - unsigned ip_options; /* IP options */ - char authenticator; /* AUTHENTICATOR or not */ - char ip_mode; /* Static/Host/Peer */ -} wan_ppp_conf_t; - -/*---------------------------------------------------------------------------- - * CHDLC-specific link-level configuration. - */ -typedef struct wan_chdlc_conf -{ - unsigned char ignore_dcd; /* Protocol options: */ - unsigned char ignore_cts; /* Ignore these to determine */ - unsigned char ignore_keepalive; /* link status (Yes or No) */ - unsigned char hdlc_streaming; /* hdlc_streaming mode (Y/N) */ - unsigned char receive_only; /* no transmit buffering (Y/N) */ - unsigned keepalive_tx_tmr; /* transmit keepalive timer */ - unsigned keepalive_rx_tmr; /* receive keepalive timer */ - unsigned keepalive_err_margin; /* keepalive_error_tolerance */ - unsigned slarp_timer; /* SLARP request timer */ -} wan_chdlc_conf_t; - - -/*---------------------------------------------------------------------------- - * WAN device configuration. Passed to ROUTER_SETUP IOCTL. - */ -typedef struct wandev_conf -{ - unsigned magic; /* magic number (for verification) */ - unsigned config_id; /* configuration structure identifier */ - /****** hardware configuration ******/ - unsigned ioport; /* adapter I/O port base */ - unsigned long maddr; /* dual-port memory address */ - unsigned msize; /* dual-port memory size */ - int irq; /* interrupt request level */ - int dma; /* DMA request level */ - char S514_CPU_no[1]; /* S514 PCI adapter CPU number ('A' or 'B') */ - unsigned PCI_slot_no; /* S514 PCI adapter slot number */ - char auto_pci_cfg; /* S515 PCI automatic slot detection */ - char comm_port; /* Communication Port (PRI=0, SEC=1) */ - unsigned bps; /* data transfer rate */ - unsigned mtu; /* maximum transmit unit size */ - unsigned udp_port; /* UDP port for management */ - unsigned char ttl; /* Time To Live for UDP security */ - unsigned char ft1; /* FT1 Configurator Option */ - char interface; /* RS-232/V.35, etc. */ - char clocking; /* external/internal */ - char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */ - char station; /* DTE/DCE, primary/secondary, etc. */ - char connection; /* permanent/switched/on-demand */ - char read_mode; /* read mode: Polling or interrupt */ - char receive_only; /* disable tx buffers */ - char tty; /* Create a fake tty device */ - unsigned tty_major; /* Major number for wanpipe tty device */ - unsigned tty_minor; /* Minor number for wanpipe tty device */ - unsigned tty_mode; /* TTY operation mode SYNC or ASYNC */ - char backup; /* Backup Mode */ - unsigned hw_opt[4]; /* other hardware options */ - unsigned reserved[4]; - /****** arbitrary data ***************/ - unsigned data_size; /* data buffer size */ - void* data; /* data buffer, e.g. firmware */ - union /****** protocol-specific ************/ - { - wan_x25_conf_t x25; /* X.25 configuration */ - wan_ppp_conf_t ppp; /* PPP configuration */ - wan_fr_conf_t fr; /* frame relay configuration */ - wan_chdlc_conf_t chdlc; /* Cisco HDLC configuration */ - } u; -} wandev_conf_t; - -/* 'config_id' definitions */ -#define WANCONFIG_X25 101 /* X.25 link */ -#define WANCONFIG_FR 102 /* frame relay link */ -#define WANCONFIG_PPP 103 /* synchronous PPP link */ -#define WANCONFIG_CHDLC 104 /* Cisco HDLC Link */ -#define WANCONFIG_BSC 105 /* BiSync Streaming */ -#define WANCONFIG_HDLC 106 /* HDLC Support */ -#define WANCONFIG_MPPP 107 /* Multi Port PPP over RAW CHDLC */ - /* - * Configuration options defines. + * wanrouter.h Legacy declarations kept around until X25 is removed */ -/* general options */ -#define WANOPT_OFF 0 -#define WANOPT_ON 1 -#define WANOPT_NO 0 -#define WANOPT_YES 1 - -/* intercace options */ -#define WANOPT_RS232 0 -#define WANOPT_V35 1 - -/* data encoding options */ -#define WANOPT_NRZ 0 -#define WANOPT_NRZI 1 -#define WANOPT_FM0 2 -#define WANOPT_FM1 3 - -/* link type options */ -#define WANOPT_POINTTOPOINT 0 /* RTS always active */ -#define WANOPT_MULTIDROP 1 /* RTS is active when transmitting */ - -/* clocking options */ -#define WANOPT_EXTERNAL 0 -#define WANOPT_INTERNAL 1 - -/* station options */ -#define WANOPT_DTE 0 -#define WANOPT_DCE 1 -#define WANOPT_CPE 0 -#define WANOPT_NODE 1 -#define WANOPT_SECONDARY 0 -#define WANOPT_PRIMARY 1 - -/* connection options */ -#define WANOPT_PERMANENT 0 /* DTR always active */ -#define WANOPT_SWITCHED 1 /* use DTR to setup link (dial-up) */ -#define WANOPT_ONDEMAND 2 /* activate DTR only before sending */ - -/* frame relay in-channel signalling */ -#define WANOPT_FR_ANSI 1 /* ANSI T1.617 Annex D */ -#define WANOPT_FR_Q933 2 /* ITU Q.933A */ -#define WANOPT_FR_LMI 3 /* LMI */ - -/* PPP IP Mode Options */ -#define WANOPT_PPP_STATIC 0 -#define WANOPT_PPP_HOST 1 -#define WANOPT_PPP_PEER 2 - -/* ASY Mode Options */ -#define WANOPT_ONE 1 -#define WANOPT_TWO 2 -#define WANOPT_ONE_AND_HALF 3 - -#define WANOPT_NONE 0 -#define WANOPT_ODD 1 -#define WANOPT_EVEN 2 - -/* CHDLC Protocol Options */ -/* DF Commented out for now. - -#define WANOPT_CHDLC_NO_DCD IGNORE_DCD_FOR_LINK_STAT -#define WANOPT_CHDLC_NO_CTS IGNORE_CTS_FOR_LINK_STAT -#define WANOPT_CHDLC_NO_KEEPALIVE IGNORE_KPALV_FOR_LINK_STAT -*/ - -/* Port options */ -#define WANOPT_PRI 0 -#define WANOPT_SEC 1 -/* read mode */ -#define WANOPT_INTR 0 -#define WANOPT_POLL 1 - -#define WANOPT_TTY_SYNC 0 -#define WANOPT_TTY_ASYNC 1 -/*---------------------------------------------------------------------------- - * WAN Link Status Info (for ROUTER_STAT IOCTL). - */ -typedef struct wandev_stat -{ - unsigned state; /* link state */ - unsigned ndev; /* number of configured interfaces */ - - /* link/interface configuration */ - unsigned connection; /* permanent/switched/on-demand */ - unsigned media_type; /* Frame relay/PPP/X.25/SDLC, etc. */ - unsigned mtu; /* max. transmit unit for this device */ - - /* physical level statistics */ - unsigned modem_status; /* modem status */ - unsigned rx_frames; /* received frames count */ - unsigned rx_overruns; /* receiver overrun error count */ - unsigned rx_crc_err; /* receive CRC error count */ - unsigned rx_aborts; /* received aborted frames count */ - unsigned rx_bad_length; /* unexpetedly long/short frames count */ - unsigned rx_dropped; /* frames discarded at device level */ - unsigned tx_frames; /* transmitted frames count */ - unsigned tx_underruns; /* aborted transmissions (underruns) count */ - unsigned tx_timeouts; /* transmission timeouts */ - unsigned tx_rejects; /* other transmit errors */ - - /* media level statistics */ - unsigned rx_bad_format; /* frames with invalid format */ - unsigned rx_bad_addr; /* frames with invalid media address */ - unsigned tx_retries; /* frames re-transmitted */ - unsigned reserved[16]; /* reserved for future use */ -} wandev_stat_t; +#ifndef _UAPI_ROUTER_H +#define _UAPI_ROUTER_H /* 'state' defines */ enum wan_states @@ -365,88 +11,7 @@ enum wan_states WAN_UNCONFIGURED, /* link/channel is not configured */ WAN_DISCONNECTED, /* link/channel is disconnected */ WAN_CONNECTING, /* connection is in progress */ - WAN_CONNECTED, /* link/channel is operational */ - WAN_LIMIT, /* for verification only */ - WAN_DUALPORT, /* for Dual Port cards */ - WAN_DISCONNECTING, - WAN_FT1_READY /* FT1 Configurator Ready */ + WAN_CONNECTED /* link/channel is operational */ }; -enum { - WAN_LOCAL_IP, - WAN_POINTOPOINT_IP, - WAN_NETMASK_IP, - WAN_BROADCAST_IP -}; - -/* 'modem_status' masks */ -#define WAN_MODEM_CTS 0x0001 /* CTS line active */ -#define WAN_MODEM_DCD 0x0002 /* DCD line active */ -#define WAN_MODEM_DTR 0x0010 /* DTR line active */ -#define WAN_MODEM_RTS 0x0020 /* RTS line active */ - -/*---------------------------------------------------------------------------- - * WAN interface (logical channel) configuration (for ROUTER_IFNEW IOCTL). - */ -typedef struct wanif_conf -{ - unsigned magic; /* magic number */ - unsigned config_id; /* configuration identifier */ - char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ - char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */ - char usedby[USED_BY_FIELD]; /* used by API or WANPIPE */ - unsigned idle_timeout; /* sec, before disconnecting */ - unsigned hold_timeout; /* sec, before re-connecting */ - unsigned cir; /* Committed Information Rate fwd,bwd*/ - unsigned bc; /* Committed Burst Size fwd, bwd */ - unsigned be; /* Excess Burst Size fwd, bwd */ - unsigned char enable_IPX; /* Enable or Disable IPX */ - unsigned char inarp; /* Send Inverse ARP requests Y/N */ - unsigned inarp_interval; /* sec, between InARP requests */ - unsigned long network_number; /* Network Number for IPX */ - char mc; /* Multicast on or off */ - char local_addr[WAN_ADDRESS_SZ+1];/* local media address, ASCIIZ */ - unsigned char port; /* board port */ - unsigned char protocol; /* prococol used in this channel (TCPOX25 or X25) */ - char pap; /* PAP enabled or disabled */ - char chap; /* CHAP enabled or disabled */ - unsigned char userid[511]; /* List of User Id */ - unsigned char passwd[511]; /* List of passwords */ - unsigned char sysname[31]; /* Name of the system */ - unsigned char ignore_dcd; /* Protocol options: */ - unsigned char ignore_cts; /* Ignore these to determine */ - unsigned char ignore_keepalive; /* link status (Yes or No) */ - unsigned char hdlc_streaming; /* Hdlc streaming mode (Y/N) */ - unsigned keepalive_tx_tmr; /* transmit keepalive timer */ - unsigned keepalive_rx_tmr; /* receive keepalive timer */ - unsigned keepalive_err_margin; /* keepalive_error_tolerance */ - unsigned slarp_timer; /* SLARP request timer */ - unsigned char ttl; /* Time To Live for UDP security */ - char interface; /* RS-232/V.35, etc. */ - char clocking; /* external/internal */ - unsigned bps; /* data transfer rate */ - unsigned mtu; /* maximum transmit unit size */ - unsigned char if_down; /* brind down interface when disconnected */ - unsigned char gateway; /* Is this interface a gateway */ - unsigned char true_if_encoding; /* Set the dev->type to true board protocol */ - - unsigned char asy_data_trans; /* async API options */ - unsigned char rts_hs_for_receive; /* async Protocol options */ - unsigned char xon_xoff_hs_for_receive; - unsigned char xon_xoff_hs_for_transmit; - unsigned char dcd_hs_for_transmit; - unsigned char cts_hs_for_transmit; - unsigned char async_mode; - unsigned tx_bits_per_char; - unsigned rx_bits_per_char; - unsigned stop_bits; - unsigned char parity; - unsigned break_timer; - unsigned inter_char_timer; - unsigned rx_complete_length; - unsigned xon_char; - unsigned xoff_char; - unsigned char receive_only; /* no transmit buffering (Y/N) */ -} wanif_conf_t; - #endif /* _UAPI_ROUTER_H */ diff --git a/net/wanrouter/Kconfig b/net/wanrouter/Kconfig deleted file mode 100644 index a157a2e64e18..000000000000 --- a/net/wanrouter/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -# -# Configuration for WAN router -# - -config WAN_ROUTER - tristate "WAN router (DEPRECATED)" - depends on EXPERIMENTAL - ---help--- - Wide Area Networks (WANs), such as X.25, frame relay and leased - lines, are used to interconnect Local Area Networks (LANs) over vast - distances with data transfer rates significantly higher than those - achievable with commonly used asynchronous modem connections. - Usually, a quite expensive external device called a `WAN router' is - needed to connect to a WAN. - - As an alternative, WAN routing can be built into the Linux kernel. - With relatively inexpensive WAN interface cards available on the - market, a perfectly usable router can be built for less than half - the price of an external router. If you have one of those cards and - wish to use your Linux box as a WAN router, say Y here and also to - the WAN driver for your card, below. You will then need the - wan-tools package which is available from . - - To compile WAN routing support as a module, choose M here: the - module will be called wanrouter. - - If unsure, say N. diff --git a/net/wanrouter/Makefile b/net/wanrouter/Makefile deleted file mode 100644 index 4da14bc48078..000000000000 --- a/net/wanrouter/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux WAN router layer. -# - -obj-$(CONFIG_WAN_ROUTER) += wanrouter.o - -wanrouter-y := wanproc.o wanmain.o diff --git a/net/wanrouter/patchlevel b/net/wanrouter/patchlevel deleted file mode 100644 index c043eea7767e..000000000000 --- a/net/wanrouter/patchlevel +++ /dev/null @@ -1 +0,0 @@ -2.2.1 diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c deleted file mode 100644 index 2ab785064b7e..000000000000 --- a/net/wanrouter/wanmain.c +++ /dev/null @@ -1,782 +0,0 @@ -/***************************************************************************** -* wanmain.c WAN Multiprotocol Router Module. Main code. -* -* This module is completely hardware-independent and provides -* the following common services for the WAN Link Drivers: -* o WAN device management (registering, unregistering) -* o Network interface management -* o Physical connection management (dial-up, incoming calls) -* o Logical connection management (switched virtual circuits) -* o Protocol encapsulation/decapsulation -* -* Author: Gideon Hack -* -* Copyright: (c) 1995-1999 Sangoma Technologies Inc. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version -* 2 of the License, or (at your option) any later version. -* ============================================================================ -* Nov 24, 2000 Nenad Corbic Updated for 2.4.X kernels -* Nov 07, 2000 Nenad Corbic Fixed the Mulit-Port PPP for kernels 2.2.16 and -* greater. -* Aug 2, 2000 Nenad Corbic Block the Multi-Port PPP from running on -* kernels 2.2.16 or greater. The SyncPPP -* has changed. -* Jul 13, 2000 Nenad Corbic Added SyncPPP support -* Added extra debugging in device_setup(). -* Oct 01, 1999 Gideon Hack Update for s514 PCI card -* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) -* Jan 16, 1997 Gene Kozin router_devlist made public -* Jan 31, 1997 Alan Cox Hacked it about a bit for 2.1 -* Jun 27, 1997 Alan Cox realigned with vendor code -* Oct 15, 1997 Farhan Thawar changed wan_encapsulate to add a pad byte of 0 -* Apr 20, 1998 Alan Cox Fixed 2.1 symbols -* May 17, 1998 K. Baranowski Fixed SNAP encapsulation in wan_encapsulate -* Dec 15, 1998 Arnaldo Melo support for firmwares of up to 128000 bytes -* check wandev->setup return value -* Dec 22, 1998 Arnaldo Melo vmalloc/vfree used in device_setup to allocate -* kernel memory and copy configuration data to -* kernel space (for big firmwares) -* Jun 02, 1999 Gideon Hack Updates for Linux 2.0.X and 2.2.X kernels. -*****************************************************************************/ - -#include /* offsetof(), etc. */ -#include -#include /* return codes */ -#include -#include /* support for loadable modules */ -#include /* kmalloc(), kfree() */ -#include -#include -#include /* inline mem*, str* functions */ - -#include /* htons(), etc. */ -#include /* WAN router API definitions */ - -#include /* vmalloc, vfree */ -#include /* copy_to/from_user */ -#include /* __initfunc et al. */ - -#define DEV_TO_SLAVE(dev) (*((struct net_device **)netdev_priv(dev))) - -/* - * Function Prototypes - */ - -/* - * WAN device IOCTL handlers - */ - -static DEFINE_MUTEX(wanrouter_mutex); -static int wanrouter_device_setup(struct wan_device *wandev, - wandev_conf_t __user *u_conf); -static int wanrouter_device_stat(struct wan_device *wandev, - wandev_stat_t __user *u_stat); -static int wanrouter_device_shutdown(struct wan_device *wandev); -static int wanrouter_device_new_if(struct wan_device *wandev, - wanif_conf_t __user *u_conf); -static int wanrouter_device_del_if(struct wan_device *wandev, - char __user *u_name); - -/* - * Miscellaneous - */ - -static struct wan_device *wanrouter_find_device(char *name); -static int wanrouter_delete_interface(struct wan_device *wandev, char *name); -static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) - __acquires(lock); -static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) - __releases(lock); - - - -/* - * Global Data - */ - -static char wanrouter_fullname[] = "Sangoma WANPIPE Router"; -static char wanrouter_copyright[] = "(c) 1995-2000 Sangoma Technologies Inc."; -static char wanrouter_modname[] = ROUTER_NAME; /* short module name */ -struct wan_device* wanrouter_router_devlist; /* list of registered devices */ - -/* - * Organize Unique Identifiers for encapsulation/decapsulation - */ - -#if 0 -static unsigned char wanrouter_oui_ether[] = { 0x00, 0x00, 0x00 }; -static unsigned char wanrouter_oui_802_2[] = { 0x00, 0x80, 0xC2 }; -#endif - -static int __init wanrouter_init(void) -{ - int err; - - printk(KERN_INFO "%s v%u.%u %s\n", - wanrouter_fullname, ROUTER_VERSION, ROUTER_RELEASE, - wanrouter_copyright); - - err = wanrouter_proc_init(); - if (err) - printk(KERN_INFO "%s: can't create entry in proc filesystem!\n", - wanrouter_modname); - - return err; -} - -static void __exit wanrouter_cleanup (void) -{ - wanrouter_proc_cleanup(); -} - -/* - * This is just plain dumb. We should move the bugger to drivers/net/wan, - * slap it first in directory and make it module_init(). The only reason - * for subsys_initcall() here is that net goes after drivers (why, BTW?) - */ -subsys_initcall(wanrouter_init); -module_exit(wanrouter_cleanup); - -/* - * Kernel APIs - */ - -/* - * Register WAN device. - * o verify device credentials - * o create an entry for the device in the /proc/net/router directory - * o initialize internally maintained fields of the wan_device structure - * o link device data space to a singly-linked list - * o if it's the first device, then start kernel 'thread' - * o increment module use count - * - * Return: - * 0 Ok - * < 0 error. - * - * Context: process - */ - - -int register_wan_device(struct wan_device *wandev) -{ - int err, namelen; - - if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) || - (wandev->name == NULL)) - return -EINVAL; - - namelen = strlen(wandev->name); - if (!namelen || (namelen > WAN_DRVNAME_SZ)) - return -EINVAL; - - if (wanrouter_find_device(wandev->name)) - return -EEXIST; - -#ifdef WANDEBUG - printk(KERN_INFO "%s: registering WAN device %s\n", - wanrouter_modname, wandev->name); -#endif - - /* - * Register /proc directory entry - */ - err = wanrouter_proc_add(wandev); - if (err) { - printk(KERN_INFO - "%s: can't create /proc/net/router/%s entry!\n", - wanrouter_modname, wandev->name); - return err; - } - - /* - * Initialize fields of the wan_device structure maintained by the - * router and update local data. - */ - - wandev->ndev = 0; - wandev->dev = NULL; - wandev->next = wanrouter_router_devlist; - wanrouter_router_devlist = wandev; - return 0; -} - -/* - * Unregister WAN device. - * o shut down device - * o unlink device data space from the linked list - * o delete device entry in the /proc/net/router directory - * o decrement module use count - * - * Return: 0 Ok - * <0 error. - * Context: process - */ - - -int unregister_wan_device(char *name) -{ - struct wan_device *wandev, *prev; - - if (name == NULL) - return -EINVAL; - - for (wandev = wanrouter_router_devlist, prev = NULL; - wandev && strcmp(wandev->name, name); - prev = wandev, wandev = wandev->next) - ; - if (wandev == NULL) - return -ENODEV; - -#ifdef WANDEBUG - printk(KERN_INFO "%s: unregistering WAN device %s\n", - wanrouter_modname, name); -#endif - - if (wandev->state != WAN_UNCONFIGURED) - wanrouter_device_shutdown(wandev); - - if (prev) - prev->next = wandev->next; - else - wanrouter_router_devlist = wandev->next; - - wanrouter_proc_delete(wandev); - return 0; -} - -#if 0 - -/* - * Encapsulate packet. - * - * Return: encapsulation header size - * < 0 - unsupported Ethertype - * - * Notes: - * 1. This function may be called on interrupt context. - */ - - -int wanrouter_encapsulate(struct sk_buff *skb, struct net_device *dev, - unsigned short type) -{ - int hdr_len = 0; - - switch (type) { - case ETH_P_IP: /* IP datagram encapsulation */ - hdr_len += 1; - skb_push(skb, 1); - skb->data[0] = NLPID_IP; - break; - - case ETH_P_IPX: /* SNAP encapsulation */ - case ETH_P_ARP: - hdr_len += 7; - skb_push(skb, 7); - skb->data[0] = 0; - skb->data[1] = NLPID_SNAP; - skb_copy_to_linear_data_offset(skb, 2, wanrouter_oui_ether, - sizeof(wanrouter_oui_ether)); - *((unsigned short*)&skb->data[5]) = htons(type); - break; - - default: /* Unknown packet type */ - printk(KERN_INFO - "%s: unsupported Ethertype 0x%04X on interface %s!\n", - wanrouter_modname, type, dev->name); - hdr_len = -EINVAL; - } - return hdr_len; -} - - -/* - * Decapsulate packet. - * - * Return: Ethertype (in network order) - * 0 unknown encapsulation - * - * Notes: - * 1. This function may be called on interrupt context. - */ - - -__be16 wanrouter_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - int cnt = skb->data[0] ? 0 : 1; /* there may be a pad present */ - __be16 ethertype; - - switch (skb->data[cnt]) { - case NLPID_IP: /* IP datagramm */ - ethertype = htons(ETH_P_IP); - cnt += 1; - break; - - case NLPID_SNAP: /* SNAP encapsulation */ - if (memcmp(&skb->data[cnt + 1], wanrouter_oui_ether, - sizeof(wanrouter_oui_ether))){ - printk(KERN_INFO - "%s: unsupported SNAP OUI %02X-%02X-%02X " - "on interface %s!\n", wanrouter_modname, - skb->data[cnt+1], skb->data[cnt+2], - skb->data[cnt+3], dev->name); - return 0; - } - ethertype = *((__be16*)&skb->data[cnt+4]); - cnt += 6; - break; - - /* add other protocols, e.g. CLNP, ESIS, ISIS, if needed */ - - default: - printk(KERN_INFO - "%s: unsupported NLPID 0x%02X on interface %s!\n", - wanrouter_modname, skb->data[cnt], dev->name); - return 0; - } - skb->protocol = ethertype; - skb->pkt_type = PACKET_HOST; /* Physically point to point */ - skb_pull(skb, cnt); - skb_reset_mac_header(skb); - return ethertype; -} - -#endif /* 0 */ - -/* - * WAN device IOCTL. - * o find WAN device associated with this node - * o execute requested action or pass command to the device driver - */ - -long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct inode *inode = file->f_path.dentry->d_inode; - int err = 0; - struct proc_dir_entry *dent; - struct wan_device *wandev; - void __user *data = (void __user *)arg; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if ((cmd >> 8) != ROUTER_IOCTL) - return -EINVAL; - - dent = PDE(inode); - if ((dent == NULL) || (dent->data == NULL)) - return -EINVAL; - - wandev = dent->data; - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - - mutex_lock(&wanrouter_mutex); - switch (cmd) { - case ROUTER_SETUP: - err = wanrouter_device_setup(wandev, data); - break; - - case ROUTER_DOWN: - err = wanrouter_device_shutdown(wandev); - break; - - case ROUTER_STAT: - err = wanrouter_device_stat(wandev, data); - break; - - case ROUTER_IFNEW: - err = wanrouter_device_new_if(wandev, data); - break; - - case ROUTER_IFDEL: - err = wanrouter_device_del_if(wandev, data); - break; - - case ROUTER_IFSTAT: - break; - - default: - if ((cmd >= ROUTER_USER) && - (cmd <= ROUTER_USER_MAX) && - wandev->ioctl) - err = wandev->ioctl(wandev, cmd, arg); - else err = -EINVAL; - } - mutex_unlock(&wanrouter_mutex); - return err; -} - -/* - * WAN Driver IOCTL Handlers - */ - -/* - * Setup WAN link device. - * o verify user address space - * o allocate kernel memory and copy configuration data to kernel space - * o if configuration data includes extension, copy it to kernel space too - * o call driver's setup() entry point - */ - -static int wanrouter_device_setup(struct wan_device *wandev, - wandev_conf_t __user *u_conf) -{ - void *data = NULL; - wandev_conf_t *conf; - int err = -EINVAL; - - if (wandev->setup == NULL) { /* Nothing to do ? */ - printk(KERN_INFO "%s: ERROR, No setup script: wandev->setup()\n", - wandev->name); - return 0; - } - - conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL); - if (conf == NULL){ - printk(KERN_INFO "%s: ERROR, Failed to allocate kernel memory !\n", - wandev->name); - return -ENOBUFS; - } - - if (copy_from_user(conf, u_conf, sizeof(wandev_conf_t))) { - printk(KERN_INFO "%s: Failed to copy user config data to kernel space!\n", - wandev->name); - kfree(conf); - return -EFAULT; - } - - if (conf->magic != ROUTER_MAGIC) { - kfree(conf); - printk(KERN_INFO "%s: ERROR, Invalid MAGIC Number\n", - wandev->name); - return -EINVAL; - } - - if (conf->data_size && conf->data) { - if (conf->data_size > 128000) { - printk(KERN_INFO - "%s: ERROR, Invalid firmware data size %i !\n", - wandev->name, conf->data_size); - kfree(conf); - return -EINVAL; - } - - data = vmalloc(conf->data_size); - if (!data) { - printk(KERN_INFO - "%s: ERROR, Failed allocate kernel memory !\n", - wandev->name); - kfree(conf); - return -ENOBUFS; - } - if (!copy_from_user(data, conf->data, conf->data_size)) { - conf->data = data; - err = wandev->setup(wandev, conf); - } else { - printk(KERN_INFO - "%s: ERROR, Failed to copy from user data !\n", - wandev->name); - err = -EFAULT; - } - vfree(data); - } else { - printk(KERN_INFO - "%s: ERROR, No firmware found ! Firmware size = %i !\n", - wandev->name, conf->data_size); - } - - kfree(conf); - return err; -} - -/* - * Shutdown WAN device. - * o delete all not opened logical channels for this device - * o call driver's shutdown() entry point - */ - -static int wanrouter_device_shutdown(struct wan_device *wandev) -{ - struct net_device *dev; - int err=0; - - if (wandev->state == WAN_UNCONFIGURED) - return 0; - - printk(KERN_INFO "\n%s: Shutting Down!\n",wandev->name); - - for (dev = wandev->dev; dev;) { - err = wanrouter_delete_interface(wandev, dev->name); - if (err) - return err; - /* The above function deallocates the current dev - * structure. Therefore, we cannot use netdev_priv(dev) - * as the next element: wandev->dev points to the - * next element */ - dev = wandev->dev; - } - - if (wandev->ndev) - return -EBUSY; /* there are opened interfaces */ - - if (wandev->shutdown) - err=wandev->shutdown(wandev); - - return err; -} - -/* - * Get WAN device status & statistics. - */ - -static int wanrouter_device_stat(struct wan_device *wandev, - wandev_stat_t __user *u_stat) -{ - wandev_stat_t stat; - - memset(&stat, 0, sizeof(stat)); - - /* Ask device driver to update device statistics */ - if ((wandev->state != WAN_UNCONFIGURED) && wandev->update) - wandev->update(wandev); - - /* Fill out structure */ - stat.ndev = wandev->ndev; - stat.state = wandev->state; - - if (copy_to_user(u_stat, &stat, sizeof(stat))) - return -EFAULT; - - return 0; -} - -/* - * Create new WAN interface. - * o verify user address space - * o copy configuration data to kernel address space - * o allocate network interface data space - * o call driver's new_if() entry point - * o make sure there is no interface name conflict - * o register network interface - */ - -static int wanrouter_device_new_if(struct wan_device *wandev, - wanif_conf_t __user *u_conf) -{ - wanif_conf_t *cnf; - struct net_device *dev = NULL; - int err; - - if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL)) - return -ENODEV; - - cnf = kmalloc(sizeof(wanif_conf_t), GFP_KERNEL); - if (!cnf) - return -ENOBUFS; - - err = -EFAULT; - if (copy_from_user(cnf, u_conf, sizeof(wanif_conf_t))) - goto out; - - err = -EINVAL; - if (cnf->magic != ROUTER_MAGIC) - goto out; - - if (cnf->config_id == WANCONFIG_MPPP) { - printk(KERN_INFO "%s: Wanpipe Mulit-Port PPP support has not been compiled in!\n", - wandev->name); - err = -EPROTONOSUPPORT; - goto out; - } else { - err = wandev->new_if(wandev, dev, cnf); - } - - if (!err) { - /* Register network interface. This will invoke init() - * function supplied by the driver. If device registered - * successfully, add it to the interface list. - */ - -#ifdef WANDEBUG - printk(KERN_INFO "%s: registering interface %s...\n", - wanrouter_modname, dev->name); -#endif - - err = register_netdev(dev); - if (!err) { - struct net_device *slave = NULL; - unsigned long smp_flags=0; - - lock_adapter_irq(&wandev->lock, &smp_flags); - - if (wandev->dev == NULL) { - wandev->dev = dev; - } else { - for (slave=wandev->dev; - DEV_TO_SLAVE(slave); - slave = DEV_TO_SLAVE(slave)) - DEV_TO_SLAVE(slave) = dev; - } - ++wandev->ndev; - - unlock_adapter_irq(&wandev->lock, &smp_flags); - err = 0; /* done !!! */ - goto out; - } - if (wandev->del_if) - wandev->del_if(wandev, dev); - free_netdev(dev); - } - -out: - kfree(cnf); - return err; -} - - -/* - * Delete WAN logical channel. - * o verify user address space - * o copy configuration data to kernel address space - */ - -static int wanrouter_device_del_if(struct wan_device *wandev, char __user *u_name) -{ - char name[WAN_IFNAME_SZ + 1]; - int err = 0; - - if (wandev->state == WAN_UNCONFIGURED) - return -ENODEV; - - memset(name, 0, sizeof(name)); - - if (copy_from_user(name, u_name, WAN_IFNAME_SZ)) - return -EFAULT; - - err = wanrouter_delete_interface(wandev, name); - if (err) - return err; - - /* If last interface being deleted, shutdown card - * This helps with administration at leaf nodes - * (You can tell if the person at the other end of the phone - * has an interface configured) and avoids DoS vulnerabilities - * in binary driver files - this fixes a problem with the current - * Sangoma driver going into strange states when all the network - * interfaces are deleted and the link irrecoverably disconnected. - */ - - if (!wandev->ndev && wandev->shutdown) - err = wandev->shutdown(wandev); - - return err; -} - -/* - * Miscellaneous Functions - */ - -/* - * Find WAN device by name. - * Return pointer to the WAN device data space or NULL if device not found. - */ - -static struct wan_device *wanrouter_find_device(char *name) -{ - struct wan_device *wandev; - - for (wandev = wanrouter_router_devlist; - wandev && strcmp(wandev->name, name); - wandev = wandev->next); - return wandev; -} - -/* - * Delete WAN logical channel identified by its name. - * o find logical channel by its name - * o call driver's del_if() entry point - * o unregister network interface - * o unlink channel data space from linked list of channels - * o release channel data space - * - * Return: 0 success - * -ENODEV channel not found. - * -EBUSY interface is open - * - * Note: If (force != 0), then device will be destroyed even if interface - * associated with it is open. It's caller's responsibility to make - * sure that opened interfaces are not removed! - */ - -static int wanrouter_delete_interface(struct wan_device *wandev, char *name) -{ - struct net_device *dev = NULL, *prev = NULL; - unsigned long smp_flags=0; - - lock_adapter_irq(&wandev->lock, &smp_flags); - dev = wandev->dev; - prev = NULL; - while (dev && strcmp(name, dev->name)) { - struct net_device **slave = netdev_priv(dev); - prev = dev; - dev = *slave; - } - unlock_adapter_irq(&wandev->lock, &smp_flags); - - if (dev == NULL) - return -ENODEV; /* interface not found */ - - if (netif_running(dev)) - return -EBUSY; /* interface in use */ - - if (wandev->del_if) - wandev->del_if(wandev, dev); - - lock_adapter_irq(&wandev->lock, &smp_flags); - if (prev) { - struct net_device **prev_slave = netdev_priv(prev); - struct net_device **slave = netdev_priv(dev); - - *prev_slave = *slave; - } else { - struct net_device **slave = netdev_priv(dev); - wandev->dev = *slave; - } - --wandev->ndev; - unlock_adapter_irq(&wandev->lock, &smp_flags); - - printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name); - - unregister_netdev(dev); - - free_netdev(dev); - - return 0; -} - -static void lock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) - __acquires(lock) -{ - spin_lock_irqsave(lock, *smp_flags); -} - - -static void unlock_adapter_irq(spinlock_t *lock, unsigned long *smp_flags) - __releases(lock) -{ - spin_unlock_irqrestore(lock, *smp_flags); -} - -EXPORT_SYMBOL(register_wan_device); -EXPORT_SYMBOL(unregister_wan_device); - -MODULE_LICENSE("GPL"); - -/* - * End - */ diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c deleted file mode 100644 index c43612ee96bb..000000000000 --- a/net/wanrouter/wanproc.c +++ /dev/null @@ -1,380 +0,0 @@ -/***************************************************************************** -* wanproc.c WAN Router Module. /proc filesystem interface. -* -* This module is completely hardware-independent and provides -* access to the router using Linux /proc filesystem. -* -* Author: Gideon Hack -* -* Copyright: (c) 1995-1999 Sangoma Technologies Inc. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version -* 2 of the License, or (at your option) any later version. -* ============================================================================ -* Jun 02, 1999 Gideon Hack Updates for Linux 2.2.X kernels. -* Jun 29, 1997 Alan Cox Merged with 1.0.3 vendor code -* Jan 29, 1997 Gene Kozin v1.0.1. Implemented /proc read routines -* Jan 30, 1997 Alan Cox Hacked around for 2.1 -* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) -*****************************************************************************/ - -#include /* __initfunc et al. */ -#include /* offsetof(), etc. */ -#include /* return codes */ -#include -#include -#include /* WAN router API definitions */ -#include -#include - -#include -#include - -#define PROC_STATS_FORMAT "%30s: %12lu\n" - -/****** Defines and Macros **************************************************/ - -#define PROT_DECODE(prot) ((prot == WANCONFIG_FR) ? " FR" :\ - (prot == WANCONFIG_X25) ? " X25" : \ - (prot == WANCONFIG_PPP) ? " PPP" : \ - (prot == WANCONFIG_CHDLC) ? " CHDLC": \ - (prot == WANCONFIG_MPPP) ? " MPPP" : \ - " Unknown" ) - -/****** Function Prototypes *************************************************/ - -#ifdef CONFIG_PROC_FS - -/* Miscellaneous */ - -/* - * Structures for interfacing with the /proc filesystem. - * Router creates its own directory /proc/net/router with the following - * entries: - * config device configuration - * status global device statistics - * entry for each WAN device - */ - -/* - * Generic /proc/net/router/ file and inode operations - */ - -/* - * /proc/net/router - */ - -static DEFINE_MUTEX(config_mutex); -static struct proc_dir_entry *proc_router; - -/* Strings */ - -/* - * Interface functions - */ - -/****** Proc filesystem entry points ****************************************/ - -/* - * Iterator - */ -static void *r_start(struct seq_file *m, loff_t *pos) -{ - struct wan_device *wandev; - loff_t l = *pos; - - mutex_lock(&config_mutex); - if (!l--) - return SEQ_START_TOKEN; - for (wandev = wanrouter_router_devlist; l-- && wandev; - wandev = wandev->next) - ; - return wandev; -} - -static void *r_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct wan_device *wandev = v; - (*pos)++; - return (v == SEQ_START_TOKEN) ? wanrouter_router_devlist : wandev->next; -} - -static void r_stop(struct seq_file *m, void *v) -{ - mutex_unlock(&config_mutex); -} - -static int config_show(struct seq_file *m, void *v) -{ - struct wan_device *p = v; - if (v == SEQ_START_TOKEN) { - seq_puts(m, "Device name | port |IRQ|DMA| mem.addr |" - "mem.size|option1|option2|option3|option4\n"); - return 0; - } - if (!p->state) - return 0; - seq_printf(m, "%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n", - p->name, p->ioport, p->irq, p->dma, p->maddr, p->msize, - p->hw_opt[0], p->hw_opt[1], p->hw_opt[2], p->hw_opt[3]); - return 0; -} - -static int status_show(struct seq_file *m, void *v) -{ - struct wan_device *p = v; - if (v == SEQ_START_TOKEN) { - seq_puts(m, "Device name |protocol|station|interface|" - "clocking|baud rate| MTU |ndev|link state\n"); - return 0; - } - if (!p->state) - return 0; - seq_printf(m, "%-15s|%-8s| %-7s| %-9s|%-8s|%9u|%5u|%3u |", - p->name, - PROT_DECODE(p->config_id), - p->config_id == WANCONFIG_FR ? - (p->station ? "Node" : "CPE") : - (p->config_id == WANCONFIG_X25 ? - (p->station ? "DCE" : "DTE") : - ("N/A")), - p->interface ? "V.35" : "RS-232", - p->clocking ? "internal" : "external", - p->bps, - p->mtu, - p->ndev); - - switch (p->state) { - case WAN_UNCONFIGURED: - seq_printf(m, "%-12s\n", "unconfigured"); - break; - case WAN_DISCONNECTED: - seq_printf(m, "%-12s\n", "disconnected"); - break; - case WAN_CONNECTING: - seq_printf(m, "%-12s\n", "connecting"); - break; - case WAN_CONNECTED: - seq_printf(m, "%-12s\n", "connected"); - break; - default: - seq_printf(m, "%-12s\n", "invalid"); - break; - } - return 0; -} - -static const struct seq_operations config_op = { - .start = r_start, - .next = r_next, - .stop = r_stop, - .show = config_show, -}; - -static const struct seq_operations status_op = { - .start = r_start, - .next = r_next, - .stop = r_stop, - .show = status_show, -}; - -static int config_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &config_op); -} - -static int status_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &status_op); -} - -static const struct file_operations config_fops = { - .owner = THIS_MODULE, - .open = config_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static const struct file_operations status_fops = { - .owner = THIS_MODULE, - .open = status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int wandev_show(struct seq_file *m, void *v) -{ - struct wan_device *wandev = m->private; - - if (wandev->magic != ROUTER_MAGIC) - return 0; - - if (!wandev->state) { - seq_puts(m, "device is not configured!\n"); - return 0; - } - - /* Update device statistics */ - if (wandev->update) { - int err = wandev->update(wandev); - if (err == -EAGAIN) { - seq_puts(m, "Device is busy!\n"); - return 0; - } - if (err) { - seq_puts(m, "Device is not configured!\n"); - return 0; - } - } - - seq_printf(m, PROC_STATS_FORMAT, - "total packets received", wandev->stats.rx_packets); - seq_printf(m, PROC_STATS_FORMAT, - "total packets transmitted", wandev->stats.tx_packets); - seq_printf(m, PROC_STATS_FORMAT, - "total bytes received", wandev->stats.rx_bytes); - seq_printf(m, PROC_STATS_FORMAT, - "total bytes transmitted", wandev->stats.tx_bytes); - seq_printf(m, PROC_STATS_FORMAT, - "bad packets received", wandev->stats.rx_errors); - seq_printf(m, PROC_STATS_FORMAT, - "packet transmit problems", wandev->stats.tx_errors); - seq_printf(m, PROC_STATS_FORMAT, - "received frames dropped", wandev->stats.rx_dropped); - seq_printf(m, PROC_STATS_FORMAT, - "transmit frames dropped", wandev->stats.tx_dropped); - seq_printf(m, PROC_STATS_FORMAT, - "multicast packets received", wandev->stats.multicast); - seq_printf(m, PROC_STATS_FORMAT, - "transmit collisions", wandev->stats.collisions); - seq_printf(m, PROC_STATS_FORMAT, - "receive length errors", wandev->stats.rx_length_errors); - seq_printf(m, PROC_STATS_FORMAT, - "receiver overrun errors", wandev->stats.rx_over_errors); - seq_printf(m, PROC_STATS_FORMAT, - "CRC errors", wandev->stats.rx_crc_errors); - seq_printf(m, PROC_STATS_FORMAT, - "frame format errors (aborts)", wandev->stats.rx_frame_errors); - seq_printf(m, PROC_STATS_FORMAT, - "receiver fifo overrun", wandev->stats.rx_fifo_errors); - seq_printf(m, PROC_STATS_FORMAT, - "receiver missed packet", wandev->stats.rx_missed_errors); - seq_printf(m, PROC_STATS_FORMAT, - "aborted frames transmitted", wandev->stats.tx_aborted_errors); - return 0; -} - -static int wandev_open(struct inode *inode, struct file *file) -{ - return single_open(file, wandev_show, PDE(inode)->data); -} - -static const struct file_operations wandev_fops = { - .owner = THIS_MODULE, - .open = wandev_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .unlocked_ioctl = wanrouter_ioctl, -}; - -/* - * Initialize router proc interface. - */ - -int __init wanrouter_proc_init(void) -{ - struct proc_dir_entry *p; - proc_router = proc_mkdir(ROUTER_NAME, init_net.proc_net); - if (!proc_router) - goto fail; - - p = proc_create("config", S_IRUGO, proc_router, &config_fops); - if (!p) - goto fail_config; - p = proc_create("status", S_IRUGO, proc_router, &status_fops); - if (!p) - goto fail_stat; - return 0; -fail_stat: - remove_proc_entry("config", proc_router); -fail_config: - remove_proc_entry(ROUTER_NAME, init_net.proc_net); -fail: - return -ENOMEM; -} - -/* - * Clean up router proc interface. - */ - -void wanrouter_proc_cleanup(void) -{ - remove_proc_entry("config", proc_router); - remove_proc_entry("status", proc_router); - remove_proc_entry(ROUTER_NAME, init_net.proc_net); -} - -/* - * Add directory entry for WAN device. - */ - -int wanrouter_proc_add(struct wan_device* wandev) -{ - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - - wandev->dent = proc_create(wandev->name, S_IRUGO, - proc_router, &wandev_fops); - if (!wandev->dent) - return -ENOMEM; - wandev->dent->data = wandev; - return 0; -} - -/* - * Delete directory entry for WAN device. - */ -int wanrouter_proc_delete(struct wan_device* wandev) -{ - if (wandev->magic != ROUTER_MAGIC) - return -EINVAL; - remove_proc_entry(wandev->name, proc_router); - return 0; -} - -#else - -/* - * No /proc - output stubs - */ - -int __init wanrouter_proc_init(void) -{ - return 0; -} - -void wanrouter_proc_cleanup(void) -{ -} - -int wanrouter_proc_add(struct wan_device *wandev) -{ - return 0; -} - -int wanrouter_proc_delete(struct wan_device *wandev) -{ - return 0; -} - -#endif - -/* - * End - */ - -- cgit v1.2.3 From 7e50f84c94b82c3b2d23ac8878012b3b60ea0e96 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Thu, 31 Jan 2013 12:40:38 +0200 Subject: pf_key/xfrm_algo: prepare pf_key and xfrm_algo for new algorithms without pfkey support Mark existing algorithms as pfkey supported and make pfkey only use algorithms that have pfkey_supported set. Signed-off-by: Jussi Kivilinna Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 5 ++-- net/key/af_key.c | 39 ++++++++++++++++++++++++------- net/xfrm/xfrm_algo.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 93 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 421f764794d5..814a1baa175c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1320,6 +1320,7 @@ struct xfrm_algo_desc { char *name; char *compat; u8 available:1; + u8 pfkey_supported:1; union { struct xfrm_algo_aead_info aead; struct xfrm_algo_auth_info auth; @@ -1561,8 +1562,8 @@ extern void xfrm_input_init(void); extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq); extern void xfrm_probe_algs(void); -extern int xfrm_count_auth_supported(void); -extern int xfrm_count_enc_supported(void); +extern int xfrm_count_pfkey_auth_supported(void); +extern int xfrm_count_pfkey_enc_supported(void); extern struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx); extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx); extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id); diff --git a/net/key/af_key.c b/net/key/af_key.c index 6a6e68479b97..cb75f9b3105e 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -815,18 +815,21 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, sa->sadb_sa_auth = 0; if (x->aalg) { struct xfrm_algo_desc *a = xfrm_aalg_get_byname(x->aalg->alg_name, 0); - sa->sadb_sa_auth = a ? a->desc.sadb_alg_id : 0; + sa->sadb_sa_auth = (a && a->pfkey_supported) ? + a->desc.sadb_alg_id : 0; } sa->sadb_sa_encrypt = 0; BUG_ON(x->ealg && x->calg); if (x->ealg) { struct xfrm_algo_desc *a = xfrm_ealg_get_byname(x->ealg->alg_name, 0); - sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0; + sa->sadb_sa_encrypt = (a && a->pfkey_supported) ? + a->desc.sadb_alg_id : 0; } /* KAME compatible: sadb_sa_encrypt is overloaded with calg id */ if (x->calg) { struct xfrm_algo_desc *a = xfrm_calg_get_byname(x->calg->alg_name, 0); - sa->sadb_sa_encrypt = a ? a->desc.sadb_alg_id : 0; + sa->sadb_sa_encrypt = (a && a->pfkey_supported) ? + a->desc.sadb_alg_id : 0; } sa->sadb_sa_flags = 0; @@ -1137,7 +1140,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, if (sa->sadb_sa_auth) { int keysize = 0; struct xfrm_algo_desc *a = xfrm_aalg_get_byid(sa->sadb_sa_auth); - if (!a) { + if (!a || !a->pfkey_supported) { err = -ENOSYS; goto out; } @@ -1159,7 +1162,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, if (sa->sadb_sa_encrypt) { if (hdr->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) { struct xfrm_algo_desc *a = xfrm_calg_get_byid(sa->sadb_sa_encrypt); - if (!a) { + if (!a || !a->pfkey_supported) { err = -ENOSYS; goto out; } @@ -1171,7 +1174,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, } else { int keysize = 0; struct xfrm_algo_desc *a = xfrm_ealg_get_byid(sa->sadb_sa_encrypt); - if (!a) { + if (!a || !a->pfkey_supported) { err = -ENOSYS; goto out; } @@ -1577,13 +1580,13 @@ static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig, struct sadb_msg *hdr; int len, auth_len, enc_len, i; - auth_len = xfrm_count_auth_supported(); + auth_len = xfrm_count_pfkey_auth_supported(); if (auth_len) { auth_len *= sizeof(struct sadb_alg); auth_len += sizeof(struct sadb_supported); } - enc_len = xfrm_count_enc_supported(); + enc_len = xfrm_count_pfkey_enc_supported(); if (enc_len) { enc_len *= sizeof(struct sadb_alg); enc_len += sizeof(struct sadb_supported); @@ -1614,6 +1617,8 @@ static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig, struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i); if (!aalg) break; + if (!aalg->pfkey_supported) + continue; if (aalg->available) *ap++ = aalg->desc; } @@ -1633,6 +1638,8 @@ static struct sk_buff *compose_sadb_supported(const struct sadb_msg *orig, struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i); if (!ealg) break; + if (!ealg->pfkey_supported) + continue; if (ealg->available) *ap++ = ealg->desc; } @@ -2824,6 +2831,8 @@ static int count_ah_combs(const struct xfrm_tmpl *t) const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(i); if (!aalg) break; + if (!aalg->pfkey_supported) + continue; if (aalg_tmpl_set(t, aalg) && aalg->available) sz += sizeof(struct sadb_comb); } @@ -2839,6 +2848,9 @@ static int count_esp_combs(const struct xfrm_tmpl *t) if (!ealg) break; + if (!ealg->pfkey_supported) + continue; + if (!(ealg_tmpl_set(t, ealg) && ealg->available)) continue; @@ -2847,6 +2859,9 @@ static int count_esp_combs(const struct xfrm_tmpl *t) if (!aalg) break; + if (!aalg->pfkey_supported) + continue; + if (aalg_tmpl_set(t, aalg) && aalg->available) sz += sizeof(struct sadb_comb); } @@ -2870,6 +2885,9 @@ static void dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) if (!aalg) break; + if (!aalg->pfkey_supported) + continue; + if (aalg_tmpl_set(t, aalg) && aalg->available) { struct sadb_comb *c; c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb)); @@ -2902,6 +2920,9 @@ static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) if (!ealg) break; + if (!ealg->pfkey_supported) + continue; + if (!(ealg_tmpl_set(t, ealg) && ealg->available)) continue; @@ -2910,6 +2931,8 @@ static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) const struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k); if (!aalg) break; + if (!aalg->pfkey_supported) + continue; if (!(aalg_tmpl_set(t, aalg) && aalg->available)) continue; c = (struct sadb_comb*)skb_put(skb, sizeof(struct sadb_comb)); diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index f9a549554740..6fb9d00a75dc 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -35,6 +35,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_GCM_ICV8, .sadb_alg_ivlen = 8, @@ -51,6 +53,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_GCM_ICV12, .sadb_alg_ivlen = 8, @@ -67,6 +71,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_GCM_ICV16, .sadb_alg_ivlen = 8, @@ -83,6 +89,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_CCM_ICV8, .sadb_alg_ivlen = 8, @@ -99,6 +107,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_CCM_ICV12, .sadb_alg_ivlen = 8, @@ -115,6 +125,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AES_CCM_ICV16, .sadb_alg_ivlen = 8, @@ -131,6 +143,8 @@ static struct xfrm_algo_desc aead_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_NULL_AES_GMAC, .sadb_alg_ivlen = 8, @@ -151,6 +165,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_NULL, .sadb_alg_ivlen = 0, @@ -169,6 +185,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_AALG_MD5HMAC, .sadb_alg_ivlen = 0, @@ -187,6 +205,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_AALG_SHA1HMAC, .sadb_alg_ivlen = 0, @@ -205,6 +225,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_SHA2_256HMAC, .sadb_alg_ivlen = 0, @@ -222,6 +244,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_SHA2_384HMAC, .sadb_alg_ivlen = 0, @@ -239,6 +263,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_SHA2_512HMAC, .sadb_alg_ivlen = 0, @@ -257,6 +283,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_RIPEMD160HMAC, .sadb_alg_ivlen = 0, @@ -274,6 +302,8 @@ static struct xfrm_algo_desc aalg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_AALG_AES_XCBC_MAC, .sadb_alg_ivlen = 0, @@ -295,6 +325,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_EALG_NULL, .sadb_alg_ivlen = 0, @@ -313,6 +345,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_EALG_DESCBC, .sadb_alg_ivlen = 8, @@ -331,6 +365,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_EALG_3DESCBC, .sadb_alg_ivlen = 8, @@ -349,6 +385,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_CASTCBC, .sadb_alg_ivlen = 8, @@ -367,6 +405,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_BLOWFISHCBC, .sadb_alg_ivlen = 8, @@ -385,6 +425,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AESCBC, .sadb_alg_ivlen = 8, @@ -403,6 +445,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_SERPENTCBC, .sadb_alg_ivlen = 8, @@ -421,6 +465,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_CAMELLIACBC, .sadb_alg_ivlen = 8, @@ -439,6 +485,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_TWOFISHCBC, .sadb_alg_ivlen = 8, @@ -456,6 +504,8 @@ static struct xfrm_algo_desc ealg_list[] = { } }, + .pfkey_supported = 1, + .desc = { .sadb_alg_id = SADB_X_EALG_AESCTR, .sadb_alg_ivlen = 8, @@ -473,6 +523,7 @@ static struct xfrm_algo_desc calg_list[] = { .threshold = 90, } }, + .pfkey_supported = 1, .desc = { .sadb_alg_id = SADB_X_CALG_DEFLATE } }, { @@ -482,6 +533,7 @@ static struct xfrm_algo_desc calg_list[] = { .threshold = 90, } }, + .pfkey_supported = 1, .desc = { .sadb_alg_id = SADB_X_CALG_LZS } }, { @@ -491,6 +543,7 @@ static struct xfrm_algo_desc calg_list[] = { .threshold = 50, } }, + .pfkey_supported = 1, .desc = { .sadb_alg_id = SADB_X_CALG_LZJH } }, }; @@ -714,27 +767,27 @@ void xfrm_probe_algs(void) } EXPORT_SYMBOL_GPL(xfrm_probe_algs); -int xfrm_count_auth_supported(void) +int xfrm_count_pfkey_auth_supported(void) { int i, n; for (i = 0, n = 0; i < aalg_entries(); i++) - if (aalg_list[i].available) + if (aalg_list[i].available && aalg_list[i].pfkey_supported) n++; return n; } -EXPORT_SYMBOL_GPL(xfrm_count_auth_supported); +EXPORT_SYMBOL_GPL(xfrm_count_pfkey_auth_supported); -int xfrm_count_enc_supported(void) +int xfrm_count_pfkey_enc_supported(void) { int i, n; for (i = 0, n = 0; i < ealg_entries(); i++) - if (ealg_list[i].available) + if (ealg_list[i].available && ealg_list[i].pfkey_supported) n++; return n; } -EXPORT_SYMBOL_GPL(xfrm_count_enc_supported); +EXPORT_SYMBOL_GPL(xfrm_count_pfkey_enc_supported); #if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE) -- cgit v1.2.3 From fa8599db8f222fd9d351a640074377a841979187 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 1 Feb 2013 13:17:16 +0800 Subject: xfrm: fix a unbalanced lock Signed-off-by: Li RongQing Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 3670526e70b9..bcfda8921b5b 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -64,7 +64,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err) if (unlikely(x->km.state != XFRM_STATE_VALID)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID); - goto error_nolock; + goto error; } err = xfrm_state_check_expire(x); -- cgit v1.2.3 From de66aa63054a6fe348869722221d5cd3463d74a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:27 +0200 Subject: Bluetooth: Store UUIDs in the same order that they were added We should be encoding UUIDs to the EIR data in the same order that they were added to the kernel, i.e. each UUID should be added to the end of the UUIDs list. This patch fixes the issue by using list_add_tail instead of list_add for storing the UUIDs. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e7f944f52ff2..4fd45a3271e0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1330,7 +1330,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) memcpy(uuid->uuid, cp->uuid, 16); uuid->svc_hint = cp->svc_hint; - list_add(&uuid->list, &hdev->uuids); + list_add_tail(&uuid->list, &hdev->uuids); err = update_class(hdev); if (err < 0) -- cgit v1.2.3 From 4821002ce2baa130666c2d777e0ed30bee6c7702 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:28 +0200 Subject: Bluetooth: Simplify UUIDs clearing code The code for clearing the UUIDs list can be simplified by using list_for_each_entry_safe instead of list_for_each_safe. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d13ce99b410a..22e77a786545 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1183,14 +1183,10 @@ static void hci_discov_off(struct work_struct *work) int hci_uuids_clear(struct hci_dev *hdev) { - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *uuid; + struct bt_uuid *uuid, *tmp; - uuid = list_entry(p, struct bt_uuid, list); - - list_del(p); + list_for_each_entry_safe(uuid, tmp, &hdev->uuids, list) { + list_del(&uuid->list); kfree(uuid); } -- cgit v1.2.3 From 83be8eca2e67faaec45280224b798828bbfa69aa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:29 +0200 Subject: Bluetooth: Keep track of UUID type upon addition The primary purpose of the UUIDs is to enable generation of EIR and AD data. In these data formats the UUIDs are split into separate fields based on whether they're 16, 32 or 128 bit UUIDs. To make the generation of these data fields simpler this patch adds a type member to the bt_uuid struct and assigns a value to it as soon as the UUID is added to the kernel. This way the type doesn't need to be calculated each time the UUID list is later iterated. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 48 +++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bcf8ffe2a843..90cf75afcb02 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -86,6 +86,7 @@ struct bdaddr_list { struct bt_uuid { struct list_head list; u8 uuid[16]; + u8 size; u8 svc_hint; }; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4fd45a3271e0..8de6d576dc70 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -435,28 +435,6 @@ static u32 get_current_settings(struct hci_dev *hdev) #define PNP_INFO_SVCLASS_ID 0x1200 -static u8 bluetooth_base_uuid[] = { - 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static u16 get_uuid16(u8 *uuid128) -{ - u32 val; - int i; - - for (i = 0; i < 12; i++) { - if (bluetooth_base_uuid[i] != uuid128[i]) - return 0; - } - - val = get_unaligned_le32(&uuid128[12]); - if (val > 0xffff) - return 0; - - return (u16) val; -} - static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -513,10 +491,10 @@ static void create_eir(struct hci_dev *hdev, u8 *data) list_for_each_entry(uuid, &hdev->uuids, list) { u16 uuid16; - uuid16 = get_uuid16(uuid->uuid); - if (uuid16 == 0) - return; + if (uuid->size != 16) + continue; + uuid16 = get_unaligned_le16(&uuid->uuid[12]); if (uuid16 < 0x1100) continue; @@ -1304,6 +1282,25 @@ unlock: return err; } +static const u8 bluetooth_base_uuid[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static u8 get_uuid_size(const u8 *uuid) +{ + u32 val; + + if (memcmp(uuid, bluetooth_base_uuid, 12)) + return 128; + + val = get_unaligned_le32(&uuid[12]); + if (val > 0xffff) + return 32; + + return 16; +} + static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -1329,6 +1326,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) memcpy(uuid->uuid, cp->uuid, 16); uuid->svc_hint = cp->svc_hint; + uuid->size = get_uuid_size(cp->uuid); list_add_tail(&uuid->list, &hdev->uuids); -- cgit v1.2.3 From 056341c8cb677356eb2c20a82e788ccb51c6a37b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:30 +0200 Subject: Bluetooth: Simplify UUID removal code The UUID removal code can be simplified by using list_for_each_entry_safe instead of list_for_each_safe. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8de6d576dc70..1e906d8d86ac 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1372,7 +1372,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_remove_uuid *cp = data; struct pending_cmd *cmd; - struct list_head *p, *n; + struct bt_uuid *match, *tmp; u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int err, found; @@ -1400,9 +1400,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, found = 0; - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *match = list_entry(p, struct bt_uuid, list); - + list_for_each_entry_safe(match, tmp, &hdev->uuids, list) { if (memcmp(match->uuid, cp->uuid, 16) != 0) continue; -- cgit v1.2.3 From a10f27cf4272033d148d91ff12bb8f4b67dfaca4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:31 +0200 Subject: Bluetooth: Simplify UUID16 list generation for EIR There's no need to use two separate loops to generate a UUID list for the EIR data. This patch merges the two loops previously used for the 16-bit UUID list generation into a single loop, thus simplifying the code a great deal. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1e906d8d86ac..02827af1aef0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -438,9 +438,8 @@ static u32 get_current_settings(struct hci_dev *hdev) static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; + u8 *uuids_start; u16 eir_len = 0; - u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; - int i, truncated = 0; struct bt_uuid *uuid; size_t name_len; @@ -485,7 +484,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr += 10; } - memset(uuid16_list, 0, sizeof(uuid16_list)); + uuids_start = NULL; /* Group all UUID16 types */ list_for_each_entry(uuid, &hdev->uuids, list) { @@ -501,39 +500,24 @@ static void create_eir(struct hci_dev *hdev, u8 *data) if (uuid16 == PNP_INFO_SVCLASS_ID) continue; + if (!uuids_start) { + uuids_start = ptr; + uuids_start[0] = 1; + uuids_start[1] = EIR_UUID16_ALL; + ptr += 2; + eir_len += 2; + } + /* Stop if not enough space to put next UUID */ if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { - truncated = 1; + uuids_start[1] = EIR_UUID16_SOME; break; } - /* Check for duplicates */ - for (i = 0; uuid16_list[i] != 0; i++) - if (uuid16_list[i] == uuid16) - break; - - if (uuid16_list[i] == 0) { - uuid16_list[i] = uuid16; - eir_len += sizeof(u16); - } - } - - if (uuid16_list[0] != 0) { - u8 *length = ptr; - - /* EIR Data type */ - ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; - - ptr += 2; - eir_len += 2; - - for (i = 0; uuid16_list[i] != 0; i++) { - *ptr++ = (uuid16_list[i] & 0x00ff); - *ptr++ = (uuid16_list[i] & 0xff00) >> 8; - } - - /* EIR Data length */ - *length = (i * sizeof(u16)) + 1; + *ptr++ = (uuid16 & 0x00ff); + *ptr++ = (uuid16 & 0xff00) >> 8; + eir_len += sizeof(uuid16); + uuids_start[0] += sizeof(uuid16); } } -- cgit v1.2.3 From 892bbc5794daac57bff09c584821ed271fa18046 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:32 +0200 Subject: Bluetooth: Remove useless eir_len variable from EIR creation The amount of data encoded so far in the create_eir() function can be calculated simply through the difference between the data and ptr pointer variables. The eir_len variable then becomes essentially useless. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 02827af1aef0..5e18d5a451f4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -439,7 +439,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; u8 *uuids_start; - u16 eir_len = 0; struct bt_uuid *uuid; size_t name_len; @@ -458,7 +457,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data) memcpy(ptr + 2, hdev->dev_name, name_len); - eir_len += (name_len + 2); ptr += (name_len + 2); } @@ -467,7 +465,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr[1] = EIR_TX_POWER; ptr[2] = (u8) hdev->inq_tx_power; - eir_len += 3; ptr += 3; } @@ -480,7 +477,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data) put_unaligned_le16(hdev->devid_product, ptr + 6); put_unaligned_le16(hdev->devid_version, ptr + 8); - eir_len += 10; ptr += 10; } @@ -505,18 +501,16 @@ static void create_eir(struct hci_dev *hdev, u8 *data) uuids_start[0] = 1; uuids_start[1] = EIR_UUID16_ALL; ptr += 2; - eir_len += 2; } /* Stop if not enough space to put next UUID */ - if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { + if ((ptr - data) + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { uuids_start[1] = EIR_UUID16_SOME; break; } *ptr++ = (uuid16 & 0x00ff); *ptr++ = (uuid16 & 0xff00) >> 8; - eir_len += sizeof(uuid16); uuids_start[0] += sizeof(uuid16); } } -- cgit v1.2.3 From 213202edc9b5ae60eef2a915b83b4aa19b1c3617 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:33 +0200 Subject: Bluetooth: Refactor UUID-16 list generation into its own function We will need to create three separate UUID lists in the EIR data (for 16, 32 and 128 bit UUIDs) so the code is easier to follow if each list is generated in their own function. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 78 +++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5e18d5a451f4..497928d2b257 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -435,11 +435,51 @@ static u32 get_current_settings(struct hci_dev *hdev) #define PNP_INFO_SVCLASS_ID 0x1200 +static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) +{ + u8 *ptr = data, *uuids_start = NULL; + struct bt_uuid *uuid; + + if (len < 4) + return ptr; + + list_for_each_entry(uuid, &hdev->uuids, list) { + u16 uuid16; + + if (uuid->size != 16) + continue; + + uuid16 = get_unaligned_le16(&uuid->uuid[12]); + if (uuid16 < 0x1100) + continue; + + if (uuid16 == PNP_INFO_SVCLASS_ID) + continue; + + if (!uuids_start) { + uuids_start = ptr; + uuids_start[0] = 1; + uuids_start[1] = EIR_UUID16_ALL; + ptr += 2; + } + + /* Stop if not enough space to put next UUID */ + if ((ptr - data) + sizeof(u16) > len) { + uuids_start[1] = EIR_UUID16_SOME; + break; + } + + *ptr++ = (uuid16 & 0x00ff); + *ptr++ = (uuid16 & 0xff00) >> 8; + uuids_start[0] += sizeof(uuid16); + } + + return ptr; +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; - u8 *uuids_start; - struct bt_uuid *uuid; size_t name_len; name_len = strlen(hdev->dev_name); @@ -480,39 +520,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr += 10; } - uuids_start = NULL; - - /* Group all UUID16 types */ - list_for_each_entry(uuid, &hdev->uuids, list) { - u16 uuid16; - - if (uuid->size != 16) - continue; - - uuid16 = get_unaligned_le16(&uuid->uuid[12]); - if (uuid16 < 0x1100) - continue; - - if (uuid16 == PNP_INFO_SVCLASS_ID) - continue; - - if (!uuids_start) { - uuids_start = ptr; - uuids_start[0] = 1; - uuids_start[1] = EIR_UUID16_ALL; - ptr += 2; - } - - /* Stop if not enough space to put next UUID */ - if ((ptr - data) + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { - uuids_start[1] = EIR_UUID16_SOME; - break; - } - - *ptr++ = (uuid16 & 0x00ff); - *ptr++ = (uuid16 & 0xff00) >> 8; - uuids_start[0] += sizeof(uuid16); - } + ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); } static int update_eir(struct hci_dev *hdev) -- cgit v1.2.3 From cdf1963f7ba075772b4b5f91f395ed8fb84d0e70 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:34 +0200 Subject: Bluetooth: Add support for 32-bit UUIDs in EIR data This patch adds the necessary code for inserting a list of 32-bit UUIDs into the EIR data. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 497928d2b257..0110a75661ef 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -477,6 +477,39 @@ static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) return ptr; } +static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) +{ + u8 *ptr = data, *uuids_start = NULL; + struct bt_uuid *uuid; + + if (len < 6) + return ptr; + + list_for_each_entry(uuid, &hdev->uuids, list) { + if (uuid->size != 32) + continue; + + if (!uuids_start) { + uuids_start = ptr; + uuids_start[0] = 1; + uuids_start[1] = EIR_UUID32_ALL; + ptr += 2; + } + + /* Stop if not enough space to put next UUID */ + if ((ptr - data) + sizeof(u32) > len) { + uuids_start[1] = EIR_UUID32_SOME; + break; + } + + memcpy(ptr, &uuid->uuid[12], sizeof(u32)); + ptr += sizeof(u32); + uuids_start[0] += sizeof(u32); + } + + return ptr; +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -521,6 +554,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) } ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); + ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); } static int update_eir(struct hci_dev *hdev) -- cgit v1.2.3 From c00d575bd550d3d57aeec2522defa0cea589560c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 00:31:35 +0200 Subject: Bluetooth: Add support for 128-bit UUIDs in EIR data This patch adds the necessary code for encoding a list of 128-bit UUIDs into the EIR data. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0110a75661ef..fbc8edf7dc1f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -510,6 +510,39 @@ static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) return ptr; } +static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) +{ + u8 *ptr = data, *uuids_start = NULL; + struct bt_uuid *uuid; + + if (len < 18) + return ptr; + + list_for_each_entry(uuid, &hdev->uuids, list) { + if (uuid->size != 128) + continue; + + if (!uuids_start) { + uuids_start = ptr; + uuids_start[0] = 1; + uuids_start[1] = EIR_UUID128_ALL; + ptr += 2; + } + + /* Stop if not enough space to put next UUID */ + if ((ptr - data) + 16 > len) { + uuids_start[1] = EIR_UUID128_SOME; + break; + } + + memcpy(ptr, uuid->uuid, 16); + ptr += 16; + uuids_start[0] += 16; + } + + return ptr; +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -555,6 +588,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); + ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); } static int update_eir(struct hci_dev *hdev) -- cgit v1.2.3 From f0ff92fbfa14c1cf8c0346f1dde9c3eda26d5abf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 08:32:00 -0600 Subject: Bluetooth: Fix link security setting when powering on If a controller is powered on while the HCI_AUTO_OFF flag is set the link security setting (HCI_LINK_SECURITY) might not be in sync with the actual state of the controller (HCI_AUTH). This patch fixes the issue by checking for inequality between the intended and actual settings and sends a HCI_Write_Auth_Enable command if necessary. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fbc8edf7dc1f..ae7585de9c08 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3073,6 +3073,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); if (powered) { + u8 link_sec; + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && !lmp_host_ssp_capable(hdev)) { u8 ssp = 1; @@ -3096,6 +3098,11 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) sizeof(cp), &cp); } + link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, + sizeof(link_sec), &link_sec); + if (lmp_bredr_capable(hdev)) { set_bredr_scan(hdev); update_class(hdev); -- cgit v1.2.3 From 3810285cf8cef5c3f9c4334a317b71b876125269 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 27 Jan 2013 08:32:01 -0600 Subject: Bluetooth: Increment Management interface revision This patch increments the management interface revision due to the various fixes, improvements and other changes that have gone in lately. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ae7585de9c08..3bd4c41c6a1b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -35,7 +35,7 @@ bool enable_hs; #define MGMT_VERSION 1 -#define MGMT_REVISION 2 +#define MGMT_REVISION 3 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, -- cgit v1.2.3 From 405280887f8fb4e168a1bbc865917bb2b881db95 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 29 Jan 2013 19:59:56 -0300 Subject: Bluetooth: Reduce critical section in sco_conn_ready This patch reduces the critical section protected by sco_conn_lock in sco_conn_ready function. The lock is acquired only when it is really needed. This patch fixes the following lockdep warning which is generated when the host terminates a SCO connection. Today, this warning is a false positive. There is no way those two threads reported by lockdep are running at the same time since hdev->workqueue (where rx_work is queued) is single-thread. However, if somehow this behavior is changed in future, we will have a potential deadlock. ====================================================== [ INFO: possible circular locking dependency detected ] 3.8.0-rc1+ #7 Not tainted ------------------------------------------------------- kworker/u:1H/1018 is trying to acquire lock: (&(&conn->lock)->rlock){+.+...}, at: [] sco_chan_del+0x66/0x190 [bluetooth] but task is already holding lock: (slock-AF_BLUETOOTH-BTPROTO_SCO){+.+...}, at: [] sco_conn_del+0x8a/0xe0 [bluetooth] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (slock-AF_BLUETOOTH-BTPROTO_SCO){+.+...}: [] lock_acquire+0xb1/0xe0 [] _raw_spin_lock+0x41/0x80 [] sco_connect_cfm+0xbe/0x350 [bluetooth] [] hci_event_packet+0xd3c/0x29b0 [bluetooth] [] hci_rx_work+0x133/0x870 [bluetooth] [] process_one_work+0x2bf/0x4f0 [] worker_thread+0x2b2/0x3e0 [] kthread+0xd1/0xe0 [] ret_from_fork+0x7c/0xb0 -> #0 (&(&conn->lock)->rlock){+.+...}: [] __lock_acquire+0x1465/0x1c70 [] lock_acquire+0xb1/0xe0 [] _raw_spin_lock+0x41/0x80 [] sco_chan_del+0x66/0x190 [bluetooth] [] sco_conn_del+0x9d/0xe0 [bluetooth] [] sco_disconn_cfm+0x53/0x60 [bluetooth] [] hci_disconn_complete_evt.isra.54+0x363/0x3c0 [bluetooth] [] hci_event_packet+0xc7/0x29b0 [bluetooth] [] hci_rx_work+0x133/0x870 [bluetooth] [] process_one_work+0x2bf/0x4f0 [] worker_thread+0x2b2/0x3e0 [] kthread+0xd1/0xe0 [] ret_from_fork+0x7c/0xb0 other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(slock-AF_BLUETOOTH-BTPROTO_SCO); lock(&(&conn->lock)->rlock); lock(slock-AF_BLUETOOTH-BTPROTO_SCO); lock(&(&conn->lock)->rlock); *** DEADLOCK *** 4 locks held by kworker/u:1H/1018: #0: (hdev->name#2){.+.+.+}, at: [] process_one_work+0x258/0x4f0 #1: ((&hdev->rx_work)){+.+.+.}, at: [] process_one_work+0x258/0x4f0 #2: (&hdev->lock){+.+.+.}, at: [] hci_disconn_complete_evt.isra.54+0x59/0x3c0 [bluetooth] #3: (slock-AF_BLUETOOTH-BTPROTO_SCO){+.+...}, at: [] sco_conn_del+0x8a/0xe0 [bluetooth] stack backtrace: Pid: 1018, comm: kworker/u:1H Not tainted 3.8.0-rc1+ #7 Call Trace: [] print_circular_bug+0x1fb/0x20c [] __lock_acquire+0x1465/0x1c70 [] lock_acquire+0xb1/0xe0 [] ? sco_chan_del+0x66/0x190 [bluetooth] [] _raw_spin_lock+0x41/0x80 [] ? sco_chan_del+0x66/0x190 [bluetooth] [] sco_chan_del+0x66/0x190 [bluetooth] [] sco_conn_del+0x9d/0xe0 [bluetooth] [] sco_disconn_cfm+0x53/0x60 [bluetooth] [] hci_disconn_complete_evt.isra.54+0x363/0x3c0 [bluetooth] [] ? hci_disconn_complete_evt.isra.54+0x40/0x3c0 [bluetooth] [] hci_event_packet+0xc7/0x29b0 [bluetooth] [] ? __dynamic_pr_debug+0x80/0x90 [] ? kfree_skb+0x2d/0x40 [] ? hci_send_to_monitor+0x1a4/0x1c0 [bluetooth] [] hci_rx_work+0x133/0x870 [bluetooth] [] ? process_one_work+0x258/0x4f0 [] process_one_work+0x2bf/0x4f0 [] ? process_one_work+0x258/0x4f0 [] ? worker_thread+0x51/0x3e0 [] ? hci_tx_work+0x800/0x800 [bluetooth] [] worker_thread+0x2b2/0x3e0 [] ? busy_worker_rebind_fn+0x100/0x100 [] kthread+0xd1/0xe0 [] ? flush_kthread_worker+0xc0/0xc0 [] ret_from_fork+0x7c/0xb0 [] ? flush_kthread_worker+0xc0/0xc0 Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 57f250c20e39..b5178d62064e 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -900,8 +900,6 @@ static void sco_conn_ready(struct sco_conn *conn) BT_DBG("conn %p", conn); - sco_conn_lock(conn); - if (sk) { sco_sock_clear_timer(sk); bh_lock_sock(sk); @@ -909,9 +907,13 @@ static void sco_conn_ready(struct sco_conn *conn) sk->sk_state_change(sk); bh_unlock_sock(sk); } else { + sco_conn_lock(conn); + parent = sco_get_sock_listen(conn->src); - if (!parent) - goto done; + if (!parent) { + sco_conn_unlock(conn); + return; + } bh_lock_sock(parent); @@ -919,7 +921,8 @@ static void sco_conn_ready(struct sco_conn *conn) BTPROTO_SCO, GFP_ATOMIC); if (!sk) { bh_unlock_sock(parent); - goto done; + sco_conn_unlock(conn); + return; } sco_sock_init(sk, parent); @@ -939,10 +942,9 @@ static void sco_conn_ready(struct sco_conn *conn) parent->sk_data_ready(parent, 1); bh_unlock_sock(parent); - } -done: - sco_conn_unlock(conn); + sco_conn_unlock(conn); + } } /* ----- SCO interface with lower layer (HCI) ----- */ -- cgit v1.2.3 From 2b8a9a2e6a2143928819f2fb948d3904746e1582 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 31 Jan 2013 20:12:10 -0300 Subject: Bluetooth: Remove unneeded locking This patch removes unneeded locking in hci_le_adv_report_evt. There is no need to lock hdev before calling mgmt_device_found. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0a4fd642d4b9..477726a63512 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3988,8 +3988,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) void *ptr = &skb->data[1]; s8 rssi; - hci_dev_lock(hdev); - while (num_reports--) { struct hci_ev_le_advertising_info *ev = ptr; @@ -3999,8 +3997,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) ptr += sizeof(*ev) + ev->length + 1; } - - hci_dev_unlock(hdev); } static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3 From a3d09356491d637548dbe815ddb966f52ec9e53a Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 1 Feb 2013 11:21:30 -0300 Subject: Bluetooth: Refactor mgmt_pending_foreach This patch does a trivial refactor in mgmt_pending_foreach function. It replaces list_for_each_safe by list_for_each_entry_safe, simplifying the function. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3bd4c41c6a1b..39395c7144aa 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -760,13 +760,9 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, void *data), void *data) { - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->mgmt_pending) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); + struct pending_cmd *cmd, *tmp; + list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) { if (opcode > 0 && cmd->opcode != opcode) continue; -- cgit v1.2.3 From eef9e54ce8b8dd08e281e0c399cced807c98959e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 13:09:34 +0100 Subject: mac80211: send deauth when connection is lost If the driver determines the connection is lost, send a deauth frame to the AP anyway just in case it still considers the connection alive. The frame might not go through, but at least we've tried. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4ff52d0aaf9c..756df39f1aba 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1827,8 +1827,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_ap_probereq_get); -static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata, - bool transmit_frame) +static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -1842,7 +1841,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - transmit_frame, frame_buf); + true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; mutex_unlock(&ifmgd->mtx); @@ -1876,7 +1875,7 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work) if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) { sdata_info(sdata, "Connection to AP %pM lost\n", ifmgd->bssid); - __ieee80211_disconnect(sdata, false); + __ieee80211_disconnect(sdata); } else { ieee80211_mgd_probe_ap(sdata, true); } @@ -1890,7 +1889,7 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work) ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); - __ieee80211_disconnect(sdata, true); + __ieee80211_disconnect(sdata); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) -- cgit v1.2.3 From 682bd38b8ac1fa3e84e84cddd1f1d7eeebce1212 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 13:13:50 +0100 Subject: mac80211: always allow calling ieee80211_connection_loss() With multi-channel, there's a corner case where a driver doesn't receive a beacon soon enough to be able to sync its timers with the AP. In this case, the only recovery (after trying again) is to disconnect from the AP. Allow calling ieee80211_connection_loss() for such cases. To make that possible, modify the work function to not rely on the IEEE80211_HW_CONNECTION_MONITOR flag but use new state kept in the interface instead. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 6 ++++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 164fd4b6503c..7da11211825d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3877,6 +3877,8 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); * When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver * needs to inform if the connection to the AP has been lost. + * The function may also be called if the connection needs to be terminated + * for some other reason, even if %IEEE80211_HW_CONNECTION_MONITOR isn't set. * * This function will cause immediate change to disassociated state, * without connection recovery attempts. diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c9c66dec170a..25a0647ada8f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -391,6 +391,7 @@ struct ieee80211_if_managed { unsigned long probe_timeout; int probe_send_count; bool nullfunc_failed; + bool connection_loss; struct mutex mtx; struct cfg80211_bss *associated; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 756df39f1aba..5e188a5d812f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1872,7 +1872,7 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work) rcu_read_unlock(); } - if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) { + if (ifmgd->connection_loss) { sdata_info(sdata, "Connection to AP %pM lost\n", ifmgd->bssid); __ieee80211_disconnect(sdata); @@ -1900,6 +1900,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) trace_api_beacon_loss(sdata); WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); + sdata->u.mgd.connection_loss = false; ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); @@ -1911,7 +1912,7 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif) trace_api_connection_loss(sdata); - WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); + sdata->u.mgd.connection_loss = true; ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_connection_loss); @@ -3154,6 +3155,7 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; + sdata->u.mgd.connection_loss = false; ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_connection_loss_work); } -- cgit v1.2.3 From 42745e039312ab4672c60ec584651f0c74e8264f Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 4 Feb 2013 13:53:11 +0200 Subject: cfg80211: expand per-station byte counters to 64bit In per-station statistics, present 32bit counters are too small for practical purposes - with gigabit speeds, it get overlapped every few seconds. Expand counters in the struct station_info to be 64-bit. Driver can still fill only 32-bit and indicate in @filled only bits like STATION_INFO_[TR]X_BYTES; in case driver provides full 64-bit counter, it should also set in @filled bit STATION_INFO_[TR]RX_BYTES64 Netlink sends both 32-bit and 64-bit counters, if present, to not break userspace. Signed-off-by: Vladimir Kondratiev [change to also have 32-bit counters if driver advertises 64-bit] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 ++++++++---- include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 16 +++++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 63599ab6005b..94a0810ef68e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -672,8 +672,10 @@ struct station_parameters { * @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_TX_BITRATE: @txrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) - * @STATION_INFO_RX_PACKETS: @rx_packets filled - * @STATION_INFO_TX_PACKETS: @tx_packets filled + * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value + * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value + * @STATION_INFO_RX_PACKETS64: @rx_packets filled with 64-bit value + * @STATION_INFO_TX_PACKETS64: @tx_packets filled with 64-bit value * @STATION_INFO_TX_RETRIES: @tx_retries filled * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled @@ -714,6 +716,8 @@ enum station_info_flags { STATION_INFO_LOCAL_PM = 1<<21, STATION_INFO_PEER_PM = 1<<22, STATION_INFO_NONPEER_PM = 1<<23, + STATION_INFO_RX_BYTES64 = 1<<24, + STATION_INFO_TX_BYTES64 = 1<<25, }; /** @@ -835,8 +839,8 @@ struct station_info { u32 filled; u32 connected_time; u32 inactive_time; - u32 rx_bytes; - u32 tx_bytes; + u64 rx_bytes; + u64 tx_bytes; u16 llid; u16 plid; u8 plink_state; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 225a65e72219..9a2ecdc4136c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1857,6 +1857,8 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info @@ -1909,6 +1911,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_LOCAL_PM, NL80211_STA_INFO_PEER_PM, NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d359734b6972..807d448e702e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3057,12 +3057,22 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, sinfo->inactive_time)) goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_RX_BYTES) && + if ((sinfo->filled & (STATION_INFO_RX_BYTES | + STATION_INFO_RX_BYTES64)) && nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, - sinfo->rx_bytes)) + (u32)sinfo->rx_bytes)) goto nla_put_failure; - if ((sinfo->filled & STATION_INFO_TX_BYTES) && + if ((sinfo->filled & (STATION_INFO_TX_BYTES | + NL80211_STA_INFO_TX_BYTES64)) && nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, + (u32)sinfo->tx_bytes)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_RX_BYTES64) && + nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64, + sinfo->rx_bytes)) + goto nla_put_failure; + if ((sinfo->filled & STATION_INFO_TX_BYTES64) && + nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64, sinfo->tx_bytes)) goto nla_put_failure; if ((sinfo->filled & STATION_INFO_LLID) && -- cgit v1.2.3 From 5622f5bb8dd3cafe0e56014ed03e31957d1b4a03 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Jan 2013 00:26:45 +0100 Subject: cfg80211: refactor hidden SSID finding Instead of duplicating the rbtree functions, pass an argument to the compare function. This removes the code duplication for the two searches. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 82 +++++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 62 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ca367c58a3e2..cfc4e1aa8dad 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -425,34 +425,13 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) } static int cmp_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - const struct cfg80211_bss_ies *a_ies, *b_ies; - int r; - - r = cmp_bss_core(a, b); - if (r) - return r; - - a_ies = rcu_access_pointer(a->ies); - if (!a_ies) - return -1; - b_ies = rcu_access_pointer(b->ies); - if (!b_ies) - return 1; - - return cmp_ies(WLAN_EID_SSID, - a_ies->data, a_ies->len, - b_ies->data, b_ies->len); -} - -static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) + struct cfg80211_bss *b, + bool hide_ssid) { const struct cfg80211_bss_ies *a_ies, *b_ies; const u8 *ie1; const u8 *ie2; - int i; - int r; + int i, r; r = cmp_bss_core(a, b); if (r) @@ -468,15 +447,13 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); + if (!ie1 && !ie2) + return 0; + /* - * Key comparator must use same algorithm in any rb-tree - * search function (order is important), otherwise ordering - * of items in the tree is broken and search gives incorrect - * results. This code uses same order as cmp_ies() does. - * - * Note that due to the differring behaviour with hidden SSIDs - * this function only works when "b" is the tree element and - * "a" is the key we're looking for. + * Note that with "hide_ssid", the function returns a match if + * the already-present BSS ("b") is a hidden SSID beacon for + * the new BSS ("a"). */ /* sort missing IE before (left of) present IE */ @@ -485,14 +462,17 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) if (!ie2) return 1; - /* zero-size SSID is used as an indication of the hidden bss */ - if (!ie2[1]) + /* zero-length SSID is used as an indication of the hidden bss */ + if (hide_ssid && !ie2[1]) return 0; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; + if (!hide_ssid) + return memcmp(ie1 + 2, ie2 + 2, ie1[1]); + /* * zeroed SSID ie is another indication of a hidden bss; * if it isn't zeroed just return the regular sort value @@ -584,7 +564,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, parent = *p; tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); - cmp = cmp_bss(&bss->pub, &tbss->pub); + cmp = cmp_bss(&bss->pub, &tbss->pub, false); if (WARN_ON(!cmp)) { /* will sort of leak this BSS */ @@ -603,30 +583,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, static struct cfg80211_internal_bss * rb_find_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct rb_node *n = dev->bss_tree.rb_node; - struct cfg80211_internal_bss *bss; - int r; - - while (n) { - bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_bss(&res->pub, &bss->pub); - - if (r == 0) - return bss; - else if (r < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return NULL; -} - -static struct cfg80211_internal_bss * -rb_find_hidden_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) + struct cfg80211_internal_bss *res, + bool hidden) { struct rb_node *n = dev->bss_tree.rb_node; struct cfg80211_internal_bss *bss; @@ -634,7 +592,7 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev, while (n) { bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_hidden_bss(&res->pub, &bss->pub); + r = cmp_bss(&res->pub, &bss->pub, hidden); if (r == 0) return bss; @@ -684,7 +642,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, return NULL; } - found = rb_find_bss(dev, tmp); + found = rb_find_bss(dev, tmp, false); if (found) { found->pub.beacon_interval = tmp->pub.beacon_interval; @@ -739,7 +697,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, /* TODO: The code is not trying to update existing probe * response bss entries when beacon ies are * getting changed. */ - hidden = rb_find_hidden_bss(dev, tmp); + hidden = rb_find_bss(dev, tmp, true); if (hidden) copy_hidden_ies(tmp, hidden); -- cgit v1.2.3 From 4593c4cbe1c96b3995727dc42f6aa103f4ff5afc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 19:20:03 +0100 Subject: cfg80211: fix BSS list hidden SSID lookup When trying to find a hidden SSID, the lookup function is done wrong; the code is trying to combine the two lookups into one, and as a consequence doesn't always find the entry at all. To understand this, consider a case where multiple BSS entries with the same channel and BSSID exist but have different SSID length. Then comparing against the probe response SSID length is bound to cause problems since the hidden one might be either zeroed out or zero-length. To fix this we need to do two lookups for the two ways to hide SSIDs. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 80 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index cfc4e1aa8dad..d4fe065eeb11 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -424,9 +424,21 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); } +/** + * enum bss_compare_mode - BSS compare mode + * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) + * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode + * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode + */ +enum bss_compare_mode { + BSS_CMP_REGULAR, + BSS_CMP_HIDE_ZLEN, + BSS_CMP_HIDE_NUL, +}; + static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *b, - bool hide_ssid) + enum bss_compare_mode mode) { const struct cfg80211_bss_ies *a_ies, *b_ies; const u8 *ie1; @@ -462,27 +474,36 @@ static int cmp_bss(struct cfg80211_bss *a, if (!ie2) return 1; - /* zero-length SSID is used as an indication of the hidden bss */ - if (hide_ssid && !ie2[1]) - return 0; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - - if (!hide_ssid) + switch (mode) { + case BSS_CMP_HIDE_ZLEN: + /* + * In ZLEN mode we assume the BSS entry we're + * looking for has a zero-length SSID. So if + * the one we're looking at right now has that, + * return 0. Otherwise, return the difference + * in length, but since we're looking for the + * 0-length it's really equivalent to returning + * the length of the one we're looking at. + * + * No content comparison is needed as we assume + * the content length is zero. + */ + return ie2[1]; + case BSS_CMP_REGULAR: + default: + /* sort by length first, then by contents */ + if (ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; return memcmp(ie1 + 2, ie2 + 2, ie1[1]); - - /* - * zeroed SSID ie is another indication of a hidden bss; - * if it isn't zeroed just return the regular sort value - * to find the next candidate - */ - for (i = 0; i < ie2[1]; i++) - if (ie2[i + 2]) - return memcmp(ie1 + 2, ie2 + 2, ie1[1]); - - return 0; + case BSS_CMP_HIDE_NUL: + if (ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */ + for (i = 0; i < ie2[1]; i++) + if (ie2[i + 2]) + return -1; + return 0; + } } struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, @@ -564,7 +585,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, parent = *p; tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); - cmp = cmp_bss(&bss->pub, &tbss->pub, false); + cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR); if (WARN_ON(!cmp)) { /* will sort of leak this BSS */ @@ -584,7 +605,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, static struct cfg80211_internal_bss * rb_find_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *res, - bool hidden) + enum bss_compare_mode mode) { struct rb_node *n = dev->bss_tree.rb_node; struct cfg80211_internal_bss *bss; @@ -592,7 +613,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, while (n) { bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_bss(&res->pub, &bss->pub, hidden); + r = cmp_bss(&res->pub, &bss->pub, mode); if (r == 0) return bss; @@ -642,7 +663,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, return NULL; } - found = rb_find_bss(dev, tmp, false); + found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR); if (found) { found->pub.beacon_interval = tmp->pub.beacon_interval; @@ -697,9 +718,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, /* TODO: The code is not trying to update existing probe * response bss entries when beacon ies are * getting changed. */ - hidden = rb_find_bss(dev, tmp, true); - if (hidden) + hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); + if (hidden) { copy_hidden_ies(tmp, hidden); + } else { + hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL); + if (hidden) + copy_hidden_ies(tmp, hidden); + } /* * create a copy -- the "res" variable that is passed in -- cgit v1.2.3 From 3af6341c5378d1ed0905614575ee9ee2e5522c81 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Jan 2013 00:40:20 +0100 Subject: cfg80211: simplify mesh BSS comparison Instead of first checking if a BSS is an MBSS and then doing the comparisons, inline it all into the BSS comparison function. This avoids doing the IE searches twice and is also a lot less code. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 120 +++++++++++++++++----------------------------------- 1 file changed, 39 insertions(+), 81 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d4fe065eeb11..1435b97b1ca2 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -288,26 +288,6 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, } EXPORT_SYMBOL(cfg80211_find_vendor_ie); -static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2) -{ - const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); - const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); - - /* equal if both missing */ - if (!ie1 && !ie2) - return 0; - /* sort missing IE before (left of) present IE */ - if (!ie1) - return -1; - if (!ie2) - return 1; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - return memcmp(ie1 + 2, ie2 + 2, ie1[1]); -} - static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) { @@ -331,29 +311,6 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, return memcmp(ssidie + 2, ssid, ssid_len) == 0; } -static bool is_mesh_bss(struct cfg80211_bss *a) -{ - const struct cfg80211_bss_ies *ies; - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ies = rcu_access_pointer(a->ies); - if (!ies) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); - if (!ie) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); - if (!ie) - return false; - - return true; -} - static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) @@ -391,39 +348,6 @@ static bool is_mesh(struct cfg80211_bss *a, sizeof(struct ieee80211_meshconf_ie) - 2) == 0; } -static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) -{ - const struct cfg80211_bss_ies *a_ies, *b_ies; - int r; - - if (a->channel != b->channel) - return b->channel->center_freq - a->channel->center_freq; - - if (is_mesh_bss(a) && is_mesh_bss(b)) { - a_ies = rcu_access_pointer(a->ies); - if (!a_ies) - return -1; - b_ies = rcu_access_pointer(b->ies); - if (!b_ies) - return 1; - - r = cmp_ies(WLAN_EID_MESH_ID, - a_ies->data, a_ies->len, - b_ies->data, b_ies->len); - if (r) - return r; - return cmp_ies(WLAN_EID_MESH_CONFIG, - a_ies->data, a_ies->len, - b_ies->data, b_ies->len); - } - - /* - * we can't use compare_ether_addr here since we need a < > operator. - * The binary return value of compare_ether_addr isn't enough - */ - return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); -} - /** * enum bss_compare_mode - BSS compare mode * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) @@ -441,13 +365,12 @@ static int cmp_bss(struct cfg80211_bss *a, enum bss_compare_mode mode) { const struct cfg80211_bss_ies *a_ies, *b_ies; - const u8 *ie1; - const u8 *ie2; + const u8 *ie1 = NULL; + const u8 *ie2 = NULL; int i, r; - r = cmp_bss_core(a, b); - if (r) - return r; + if (a->channel != b->channel) + return b->channel->center_freq - a->channel->center_freq; a_ies = rcu_access_pointer(a->ies); if (!a_ies) @@ -456,6 +379,41 @@ static int cmp_bss(struct cfg80211_bss *a, if (!b_ies) return 1; + if (WLAN_CAPABILITY_IS_STA_BSS(a->capability)) + ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID, + a_ies->data, a_ies->len); + if (WLAN_CAPABILITY_IS_STA_BSS(b->capability)) + ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID, + b_ies->data, b_ies->len); + if (ie1 && ie2) { + int mesh_id_cmp; + + if (ie1[1] == ie2[1]) + mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]); + else + mesh_id_cmp = ie2[1] - ie1[1]; + + ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + a_ies->data, a_ies->len); + ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, + b_ies->data, b_ies->len); + if (ie1 && ie2) { + if (mesh_id_cmp) + return mesh_id_cmp; + if (ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + return memcmp(ie1 + 2, ie2 + 2, ie1[1]); + } + } + + /* + * we can't use compare_ether_addr here since we need a < > operator. + * The binary return value of compare_ether_addr isn't enough + */ + r = memcmp(a->bssid, b->bssid, sizeof(a->bssid)); + if (r) + return r; + ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); -- cgit v1.2.3 From 37e0838117084eb957fdf124bf555f4b9933a5a5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Jan 2013 08:50:37 +0100 Subject: cfg80211: remove unused cfg80211_get_mesh As Thomas pointed out, cfg80211_get_mesh() is unused and can be removed. Cc: Thomas Pedersen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ---- net/wireless/scan.c | 65 -------------------------------------------------- 2 files changed, 69 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 94a0810ef68e..3ec70e1681d3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3164,10 +3164,6 @@ cfg80211_get_ibss(struct wiphy *wiphy, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); } -struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg); /** * cfg80211_ref_bss - reference BSS struct * @bss: the BSS struct to reference diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1435b97b1ca2..3772638714fa 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -311,43 +311,6 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, return memcmp(ssidie + 2, ssid, ssid_len) == 0; } -static bool is_mesh(struct cfg80211_bss *a, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - const struct cfg80211_bss_ies *ies; - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ies = rcu_access_pointer(a->ies); - if (!ies) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); - if (!ie) - return false; - if (ie[1] != meshidlen) - return false; - if (memcmp(ie + 2, meshid, meshidlen)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); - if (!ie) - return false; - if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) - return false; - - /* - * Ignore mesh capability (last two bytes of the IE) when - * comparing since that may differ between stations taking - * part in the same mesh. - */ - return memcmp(ie + 2, meshcfg, - sizeof(struct ieee80211_meshconf_ie) - 2) == 0; -} - /** * enum bss_compare_mode - BSS compare mode * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) @@ -503,34 +466,6 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_get_bss); -struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss, *res = NULL; - - spin_lock_bh(&dev->bss_lock); - - list_for_each_entry(bss, &dev->bss_list, list) { - if (channel && bss->pub.channel != channel) - continue; - if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { - res = bss; - kref_get(&res->ref); - break; - } - } - - spin_unlock_bh(&dev->bss_lock); - if (!res) - return NULL; - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_mesh); - - static void rb_insert_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { -- cgit v1.2.3 From 3146a7da83ba2dd266ef436d4ac8e7e09bd15825 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Jan 2013 09:03:08 +0100 Subject: mac80211: remove unused SSID from BSS The ssid/ssid_len fields in the private BSS struct are unused, contrary to the comment we do look up the SSID in the few cases we need it. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 25a0647ada8f..8faf360e0b4c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -86,10 +86,6 @@ struct ieee80211_fragment_entry { struct ieee80211_bss { - /* don't want to look up all the time */ - size_t ssid_len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u32 device_ts; bool wmm_used; -- cgit v1.2.3 From 50521aa8817e3f6a77eb7aaabdbbb7b5c2fe0ea3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 Jan 2013 21:33:19 +0100 Subject: cfg80211: fix BSS IE allocation comment The comment about allocating the IEs together with the BSS struct is no longer true, remove it. Also fix a typo in the same area. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 3772638714fa..34b47b30be88 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -715,11 +715,8 @@ cfg80211_inform_bss(struct wiphy *wiphy, * Response frame, we need to pick one of the options and only use it * with the driver that does not provide the full Beacon/Probe Response * frame. Use Beacon frame pointer to avoid indicating that this should - * override the iies pointer should we have received an earlier + * override the IEs pointer should we have received an earlier * indication of Probe Response data. - * - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. */ ies = kmalloc(sizeof(*ies) + ielen, gfp); if (!ies) -- cgit v1.2.3 From 2ca813ad61136896c91a40194452ff665a522711 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 01:04:14 +0100 Subject: cfg80211: move locking into cfg80211_bss_age There's no reason for it to require external locking, move it into the function. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 3 ++- net/wireless/sysfs.c | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 34b47b30be88..1494d947e8dd 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -231,15 +231,16 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, return 0; } -/* must hold dev->bss_lock! */ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs) { struct cfg80211_internal_bss *bss; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); + spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) bss->ts -= age_jiffies; + spin_unlock_bh(&dev->bss_lock); } void cfg80211_bss_expire(struct cfg80211_registered_device *dev) diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 9bf6d5e32166..73bf39f11314 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -108,9 +108,7 @@ static int wiphy_resume(struct device *dev) int ret = 0; /* Age scan results with time spent in suspend */ - spin_lock_bh(&rdev->bss_lock); cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); - spin_unlock_bh(&rdev->bss_lock); if (rdev->ops->resume) { rtnl_lock(); -- cgit v1.2.3 From e54faf29e07b6cc28a44a4f01d7fbc40905a67d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 11:41:38 +0100 Subject: mac80211: allow transmitting deauth with tainted key When we had a connection for WoWLAN and after resume it needed to be disconnected, the previous commit enabled sending a deauth frame to the AP. This frame would not go through on MFP-enabled networks as the key for it is marked tainted before the frame is transmitted. Allow a tainted key to be used for deauth frames. Worst case, we'll use a wrong key because the PTK was rekeyed while suspended, but more likely the PTK is still fine and the taint flag really only applies to the GTK(s). Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a2cb6a302cc7..7892b0a8873e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -594,7 +594,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) break; } - if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED)) + if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED && + !ieee80211_is_deauth(hdr->frame_control))) return TX_DROP; if (!skip_hw && tx->key && -- cgit v1.2.3 From 6b684db1db0df40275c7dfcc32a5d48eb72fe4e3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jan 2013 11:35:29 +0100 Subject: mac80211: send deauth if connection was lost during suspend If the driver determined the connection was lost or that it couldn't securely maintain the connection when coming out of WoWLAN, send a deauth frame to the AP to also let it know. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5e188a5d812f..353b690900e9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2851,14 +2851,14 @@ static void ieee80211_sta_timer(unsigned long data) } static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, - u8 *bssid, u8 reason) + u8 *bssid, u8 reason, bool tx) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, - false, frame_buf); + tx, frame_buf); mutex_unlock(&ifmgd->mtx); /* @@ -3107,7 +3107,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) "No ack for nullfunc frame to AP %pM, disconnecting.\n", bssid); ieee80211_sta_connection_lost(sdata, bssid, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + false); } } else if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); @@ -3116,7 +3117,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); } else if (ifmgd->probe_send_count < max_tries) { mlme_dbg(sdata, "No probe response from AP %pM after %dms, try %d/%i\n", @@ -3135,7 +3136,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); } } @@ -3242,7 +3243,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) mlme_dbg(sdata, "driver requested disconnect after resume\n"); ieee80211_sta_connection_lost(sdata, ifmgd->associated->bssid, - WLAN_REASON_UNSPECIFIED); + WLAN_REASON_UNSPECIFIED, + true); mutex_unlock(&ifmgd->mtx); return; } -- cgit v1.2.3 From 4b1af4792a2aa686483fc9bb2155f7269fa6399c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 01:05:43 +0100 Subject: cfg80211: use lockdep to assert lock is held Instead of annotating with a comment, add a lockdep annotation which also serves as documentation. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 1494d947e8dd..a56287bb550c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -41,22 +41,24 @@ static void bss_release(struct kref *ref) kfree(bss); } -/* must hold dev->bss_lock! */ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { + lockdep_assert_held(&dev->bss_lock); + list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); kref_put(&bss->ref, bss_release); } -/* must hold dev->bss_lock! */ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, unsigned long expire_time) { struct cfg80211_internal_bss *bss, *tmp; bool expired = false; + lockdep_assert_held(&dev->bss_lock); + list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { if (atomic_read(&bss->hold)) continue; -- cgit v1.2.3 From 9537f227b44762591a5598b79522789c573d34b3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 01:19:48 +0100 Subject: cfg80211: remove a local variable This local variable is only used once, so remove it. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a56287bb550c..23b7e59c6506 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -583,16 +583,15 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { - const struct cfg80211_bss_ies *old, *ies; + const struct cfg80211_bss_ies *old; old = rcu_access_pointer(found->pub.beacon_ies); - ies = rcu_access_pointer(found->pub.ies); rcu_assign_pointer(found->pub.beacon_ies, tmp->pub.beacon_ies); /* Override IEs if they were from a beacon before */ - if (old == ies) + if (old == rcu_access_pointer(found->pub.ies)) rcu_assign_pointer(found->pub.ies, tmp->pub.beacon_ies); -- cgit v1.2.3 From 0532d4f154b87da6361ab90d12f35142d5119dc1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 01:34:36 +0100 Subject: cfg80211: wrap BSS kref Add inline wrappers for the BSS struct krefs to be able to extend them easily later. Signed-off-by: Johannes Berg --- net/wireless/scan.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 23b7e59c6506..36daacb31788 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -41,6 +41,16 @@ static void bss_release(struct kref *ref) kfree(bss); } +static inline void bss_ref_get(struct cfg80211_internal_bss *bss) +{ + kref_get(&bss->ref); +} + +static inline void bss_ref_put(struct cfg80211_internal_bss *bss) +{ + kref_put(&bss->ref, bss_release); +} + static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { @@ -48,7 +58,7 @@ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); - kref_put(&bss->ref, bss_release); + bss_ref_put(bss); } static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, @@ -456,7 +466,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { res = bss; - kref_get(&res->ref); + bss_ref_get(res); break; } } @@ -649,7 +659,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, dev->bss_generation++; spin_unlock_bh(&dev->bss_lock); - kref_get(&found->ref); + bss_ref_get(found); return found; } @@ -816,7 +826,7 @@ void cfg80211_ref_bss(struct cfg80211_bss *pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); - kref_get(&bss->ref); + bss_ref_get(bss); } EXPORT_SYMBOL(cfg80211_ref_bss); @@ -828,7 +838,7 @@ void cfg80211_put_bss(struct cfg80211_bss *pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); - kref_put(&bss->ref, bss_release); + bss_ref_put(bss); } EXPORT_SYMBOL(cfg80211_put_bss); -- cgit v1.2.3 From 3f52b7e328c526fa7a592af9bf5772c591ed38a4 Mon Sep 17 00:00:00 2001 From: Marco Porsch Date: Wed, 30 Jan 2013 18:14:08 +0100 Subject: mac80211: mesh power save basics Add routines to - maintain a PS mode for each peer and a non-peer PS mode - indicate own PS mode in transmitted frames - track neighbor STAs power modes - buffer frames when neighbors are in PS mode - add TIM and Awake Window IE to beacons - release frames in Mesh Peer Service Periods Add local_pm to sta_info to represent the link-specific power mode at this station towards the remote station. When a peer link is established, use the default power mode stored in mesh config. Update the PS status if the peering status of a neighbor changes. Maintain a mesh power mode for non-peer mesh STAs. Set the non-peer power mode to active mode during peering. Authenticated mesh peering is currently not working when either node is configured to be in power save mode. Indicate the current power mode in transmitted frames. Use QoS Nulls to indicate mesh power mode transitions. For performance reasons, calls to the function setting the frame flags are placed in HWMP routing routines, as there the STA pointer is already available. Add peer_pm to sta_info to represent the peer's link-specific power mode towards the local station. Add nonpeer_pm to represent the peer's power mode towards all non-peer stations. Track power modes based on received frames. Add the ps_data structure to ieee80211_if_mesh (for TIM map, PS neighbor counter and group-addressed frame buffer). Set WLAN_STA_PS flag for STA in PS mode to use the unicast frame buffering routines in the tx path. Update num_sta_ps to buffer and release group-addressed frames after DTIM beacons. Announce the awake window duration in beacons if in light or deep sleep mode towards any peer or non-peer. Create a TIM IE similarly to AP mode and add it to mesh beacons. Parse received Awake Window IEs and check TIM IEs for buffered frames. Release frames towards peers in mesh Peer Service Periods. Use the corresponding trigger frames and monitor the MPSP status. Append a QoS Null as trigger frame if neccessary to properly end the MPSP. Currently, in HT channels MPSPs behave imperfectly and show large delay spikes and frame losses. Signed-off-by: Marco Porsch Signed-off-by: Ivan Bezyazychnyy Signed-off-by: Mike Krinkin Signed-off-by: Max Filippov Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 8 + net/mac80211/Kconfig | 11 + net/mac80211/Makefile | 3 +- net/mac80211/cfg.c | 27 +- net/mac80211/debug.h | 10 + net/mac80211/debugfs_netdev.c | 5 + net/mac80211/debugfs_sta.c | 5 +- net/mac80211/ieee80211_i.h | 6 + net/mac80211/mesh.c | 33 +++ net/mac80211/mesh.h | 17 ++ net/mac80211/mesh_hwmp.c | 7 + net/mac80211/mesh_pathtbl.c | 1 + net/mac80211/mesh_plink.c | 17 ++ net/mac80211/mesh_ps.c | 585 ++++++++++++++++++++++++++++++++++++++++++ net/mac80211/rx.c | 7 + net/mac80211/sta_info.c | 20 +- net/mac80211/sta_info.h | 11 + net/mac80211/status.c | 7 + net/mac80211/tx.c | 31 ++- net/mac80211/util.c | 4 + net/mac80211/wme.c | 13 +- 21 files changed, 811 insertions(+), 17 deletions(-) create mode 100644 net/mac80211/mesh_ps.c (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 11c8bc87fdcb..7e8a498efe6d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -151,6 +151,11 @@ /* Mesh Control 802.11s */ #define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100 +/* Mesh Power Save Level */ +#define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200 +/* Mesh Receiver Service Period Initiated */ +#define IEEE80211_QOS_CTL_RSPI 0x0400 + /* U-APSD queue for WMM IEs sent by AP */ #define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) #define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f @@ -675,11 +680,14 @@ struct ieee80211_meshconf_ie { * @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs * @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure * is ongoing + * @IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL: STA is in deep sleep mode or has + * neighbors in deep sleep mode */ enum mesh_config_capab_flags { IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01, IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08, IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20, + IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40, }; /** diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index b4ecf267a34b..0ecf947ad378 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -258,6 +258,17 @@ config MAC80211_MESH_SYNC_DEBUG Do not select this option. +config MAC80211_MESH_PS_DEBUG + bool "Verbose mesh powersave debugging" + depends on MAC80211_DEBUG_MENU + depends on MAC80211_MESH + ---help--- + Selecting this option causes mac80211 to print out very verbose mesh + powersave debugging messages (when mac80211 is taking part in a + mesh network). + + Do not select this option. + config MAC80211_TDLS_DEBUG bool "Verbose TDLS debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4911202334d9..9d7d840aac6d 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -39,7 +39,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ mesh_pathtbl.o \ mesh_plink.o \ mesh_hwmp.o \ - mesh_sync.o + mesh_sync.o \ + mesh_ps.o mac80211-$(CONFIG_PM) += pm.o diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 661b878bd19c..f4f7e7691077 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -492,7 +492,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) #ifdef CONFIG_MAC80211_MESH sinfo->filled |= STATION_INFO_LLID | STATION_INFO_PLID | - STATION_INFO_PLINK_STATE; + STATION_INFO_PLINK_STATE | + STATION_INFO_LOCAL_PM | + STATION_INFO_PEER_PM | + STATION_INFO_NONPEER_PM; sinfo->llid = le16_to_cpu(sta->llid); sinfo->plid = le16_to_cpu(sta->plid); @@ -501,6 +504,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled |= STATION_INFO_T_OFFSET; sinfo->t_offset = sta->t_offset; } + sinfo->local_pm = sta->local_pm; + sinfo->peer_pm = sta->peer_pm; + sinfo->nonpeer_pm = sta->nonpeer_pm; #endif } @@ -1262,6 +1268,10 @@ static int sta_apply_parameters(struct ieee80211_local *local, changed = mesh_plink_inc_estab_count( sdata); sta->plink_state = params->plink_state; + + ieee80211_mps_sta_status_update(sta); + ieee80211_mps_set_sta_local_pm(sta, + sdata->u.mesh.mshcfg.power_mode); break; case NL80211_PLINK_LISTEN: case NL80211_PLINK_BLOCKED: @@ -1273,6 +1283,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, changed = mesh_plink_dec_estab_count( sdata); sta->plink_state = params->plink_state; + + ieee80211_mps_sta_status_update(sta); + ieee80211_mps_local_status_update(sdata); break; default: /* nothing */ @@ -1289,6 +1302,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, break; } } + + if (params->local_pm) + ieee80211_mps_set_sta_local_pm(sta, params->local_pm); #endif } @@ -1777,6 +1793,15 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) conf->dot11MeshHWMPconfirmationInterval = nconf->dot11MeshHWMPconfirmationInterval; + if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) { + conf->power_mode = nconf->power_mode; + ieee80211_mps_local_status_update(sdata); + } + if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) { + conf->dot11MeshAwakeWindowDuration = + nconf->dot11MeshAwakeWindowDuration; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + } return 0; } diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 8f383a576016..4ccc5ed6237d 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -44,6 +44,12 @@ #define MAC80211_MESH_SYNC_DEBUG 0 #endif +#ifdef CONFIG_MAC80211_MESH_PS_DEBUG +#define MAC80211_MESH_PS_DEBUG 1 +#else +#define MAC80211_MESH_PS_DEBUG 0 +#endif + #ifdef CONFIG_MAC80211_TDLS_DEBUG #define MAC80211_TDLS_DEBUG 1 #else @@ -151,6 +157,10 @@ do { \ _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ sdata, fmt, ##__VA_ARGS__) +#define mps_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + #define tdls_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_TDLS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cbde5cc49a40..059bbb82e84f 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -515,6 +515,9 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval, u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); +IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC); +IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, + u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC); #endif #define DEBUGFS_ADD_MODE(name, mode) \ @@ -620,6 +623,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); MESHPARAMS_ADD(dot11MeshHWMProotInterval); MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); + MESHPARAMS_ADD(power_mode); + MESHPARAMS_ADD(dot11MeshAwakeWindowDuration); #undef MESHPARAMS_ADD } #endif diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 6fb1168b9f16..c7591f73dbc3 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -65,7 +65,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" int res = scnprintf(buf, sizeof(buf), - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", TEST(AUTH), TEST(ASSOC), TEST(PS_STA), TEST(PS_DRIVER), TEST(AUTHORIZED), TEST(SHORT_PREAMBLE), @@ -74,7 +74,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), TEST(INSERTED), TEST(RATE_CONTROL), - TEST(TOFFSET_KNOWN)); + TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), + TEST(MPSP_RECIPIENT)); #undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8faf360e0b4c..5fe9db707880 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -590,6 +590,11 @@ struct ieee80211_if_mesh { s64 sync_offset_clockdrift_max; spinlock_t sync_offset_lock; bool adjusting_tbtt; + /* mesh power save */ + enum nl80211_mesh_power_mode nonpeer_pm; + int ps_peers_light_sleep; + int ps_peers_deep_sleep; + struct ps_data ps; }; #ifdef CONFIG_MAC80211_MESH @@ -1185,6 +1190,7 @@ struct ieee802_11_elems { struct ieee80211_meshconf_ie *mesh_config; u8 *mesh_id; u8 *peering; + __le16 *awake_window; u8 *preq; u8 *prep; u8 *perr; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index f920da1201ab..35ac38871420 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -261,6 +261,9 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; *pos |= ifmsh->accepting_plinks ? IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; + /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ + *pos |= ifmsh->ps_peers_deep_sleep ? + IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; *pos++ |= ifmsh->adjusting_tbtt ? IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; @@ -286,6 +289,29 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } +int mesh_add_awake_window_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 *pos; + + /* see IEEE802.11-2012 13.14.6 */ + if (ifmsh->ps_peers_light_sleep == 0 && + ifmsh->ps_peers_deep_sleep == 0 && + ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE) + return 0; + + if (skb_tailroom(skb) < 4) + return -ENOMEM; + + pos = skb_put(skb, 2 + 2); + *pos++ = WLAN_EID_MESH_AWAKE_WINDOW; + *pos++ = 2; + put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos); + + return 0; +} + int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { @@ -629,6 +655,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); + ieee80211_mps_local_status_update(sdata); + ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); @@ -651,6 +679,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) sta_info_flush(sdata); mesh_path_flush_by_iface(sdata); + /* free all potentially still buffered group-addressed frames */ + local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf); + skb_queue_purge(&ifmsh->ps.bc_buf); + del_timer_sync(&sdata->u.mesh.housekeeping_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); del_timer_sync(&sdata->u.mesh.mesh_path_timer); @@ -828,6 +860,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_mesh_path_root_timer, (unsigned long) sdata); INIT_LIST_HEAD(&ifmsh->preq_queue.list); + skb_queue_head_init(&ifmsh->ps.bc_buf); spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index aff301544c7f..eb336253b6b3 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -222,6 +222,8 @@ int mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); +int mesh_add_awake_window_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ds_params_ie(struct sk_buff *skb, @@ -242,6 +244,21 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); +/* mesh power save */ +void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); +void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, + enum nl80211_mesh_power_mode pm); +void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee80211_hdr *hdr); +void ieee80211_mps_sta_status_update(struct sta_info *sta); +void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, + struct ieee80211_hdr *hdr); +void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, + bool tx, bool acked); +void ieee80211_mps_frame_release(struct sta_info *sta, + struct ieee802_11_elems *elems); + /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 6b4603a90031..f0dd8742ed42 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -205,6 +205,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); @@ -217,6 +218,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, info->control.vif = &sdata->vif; info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; ieee80211_set_qos_hdr(sdata, skb); + ieee80211_mps_set_frame_flags(sdata, NULL, hdr); } /** @@ -1080,6 +1082,10 @@ int mesh_nexthop_resolve(struct sk_buff *skb, u8 *target_addr = hdr->addr3; int err = 0; + /* Nulls are only sent to peers for PS and should be pre-addressed */ + if (ieee80211_is_qos_nullfunc(hdr->frame_control)) + return 0; + rcu_read_lock(); err = mesh_nexthop_lookup(skb, sdata); if (!err) @@ -1151,6 +1157,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, if (next_hop) { memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); + ieee80211_mps_set_frame_flags(sdata, next_hop, hdr); err = 0; } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index aa749818860e..d5786c3eaee2 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -212,6 +212,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) hdr = (struct ieee80211_hdr *) skb->data; memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN); + ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr); } spin_unlock_irqrestore(&mpath->frame_queue.lock, flags); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6787d696d94c..fe7c3334d6fe 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -201,6 +201,9 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) sta->plink_state = NL80211_PLINK_BLOCKED; mesh_path_flush_by_nexthop(sta); + ieee80211_mps_sta_status_update(sta); + ieee80211_mps_local_status_update(sdata); + return changed; } @@ -503,6 +506,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, rssi_threshold_check(sta, sdata)) mesh_plink_open(sta); + ieee80211_mps_frame_release(sta, elems); out: rcu_read_unlock(); } @@ -633,6 +637,9 @@ int mesh_plink_open(struct sta_info *sta) "Mesh plink: starting establishment with %pM\n", sta->sta.addr); + /* set the non-peer mode to active during peering */ + ieee80211_mps_local_status_update(sdata); + return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); } @@ -866,6 +873,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->llid = llid; mesh_plink_timer_set(sta, mshcfg->dot11MeshRetryTimeout); + + /* set the non-peer mode to active during peering */ + ieee80211_mps_local_status_update(sdata); + spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, @@ -959,6 +970,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m changed |= mesh_set_short_slot_time(sdata); mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); + ieee80211_mps_sta_status_update(sta); + ieee80211_mps_set_sta_local_pm(sta, + mshcfg->power_mode); break; default: spin_unlock_bh(&sta->lock); @@ -998,6 +1012,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); + ieee80211_mps_sta_status_update(sta); + ieee80211_mps_set_sta_local_pm(sta, + mshcfg->power_mode); break; default: spin_unlock_bh(&sta->lock); diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c new file mode 100644 index 000000000000..b677962525ed --- /dev/null +++ b/net/mac80211/mesh_ps.c @@ -0,0 +1,585 @@ +/* + * Copyright 2012-2013, Marco Porsch + * Copyright 2012-2013, cozybit Inc. + * + * 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 "mesh.h" +#include "wme.h" + + +/* mesh PS management */ + +/** + * mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave + */ +static struct sk_buff *mps_qos_null_get(struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + struct ieee80211_hdr *nullfunc; /* use 4addr header */ + struct sk_buff *skb; + int size = sizeof(*nullfunc); + __le16 fc; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2); + if (!skb) + return NULL; + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (struct ieee80211_hdr *) skb_put(skb, size); + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); + ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr, + sdata->vif.addr); + nullfunc->frame_control = fc; + nullfunc->duration_id = 0; + /* no address resolution for this frame -> set addr 1 immediately */ + memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); + memset(skb_put(skb, 2), 0, 2); /* append QoS control field */ + ieee80211_mps_set_frame_flags(sdata, sta, nullfunc); + + return skb; +} + +/** + * mps_qos_null_tx - send a QoS Null to indicate link-specific power mode + */ +static void mps_qos_null_tx(struct sta_info *sta) +{ + struct sk_buff *skb; + + skb = mps_qos_null_get(sta); + if (!skb) + return; + + mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n", + sta->sta.addr); + + /* don't unintentionally start a MPSP */ + if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { + u8 *qc = ieee80211_get_qos_ctl((void *) skb->data); + + qc[0] |= IEEE80211_QOS_CTL_EOSP; + } + + ieee80211_tx_skb(sta->sdata, skb); +} + +/** + * ieee80211_mps_local_status_update - track status of local link-specific PMs + * + * @sdata: local mesh subif + * + * sets the non-peer power mode and triggers the driver PS (re-)configuration + */ +void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct sta_info *sta; + bool peering = false; + int light_sleep_cnt = 0; + int deep_sleep_cnt = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { + if (sdata != sta->sdata) + continue; + + switch (sta->plink_state) { + case NL80211_PLINK_OPN_SNT: + case NL80211_PLINK_OPN_RCVD: + case NL80211_PLINK_CNF_RCVD: + peering = true; + break; + case NL80211_PLINK_ESTAB: + if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP) + light_sleep_cnt++; + else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP) + deep_sleep_cnt++; + break; + default: + break; + } + } + rcu_read_unlock(); + + /* + * Set non-peer mode to active during peering/scanning/authentication + * (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is + * deep sleep if the local STA is in light or deep sleep towards at + * least one mesh peer (see 13.14.3.1). Otherwise, set it to the + * user-configured default value. + */ + if (peering) { + mps_dbg(sdata, "setting non-peer PM to active for peering\n"); + ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; + } else if (light_sleep_cnt || deep_sleep_cnt) { + mps_dbg(sdata, "setting non-peer PM to deep sleep\n"); + ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP; + } else { + mps_dbg(sdata, "setting non-peer PM to user value\n"); + ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode; + } + + ifmsh->ps_peers_light_sleep = light_sleep_cnt; + ifmsh->ps_peers_deep_sleep = deep_sleep_cnt; +} + +/** + * ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA + * + * @sta: mesh STA + * @pm: the power mode to set + */ +void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, + enum nl80211_mesh_power_mode pm) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + + mps_dbg(sdata, "local STA operates in mode %d with %pM\n", + pm, sta->sta.addr); + + sta->local_pm = pm; + + /* + * announce peer-specific power mode transition + * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3) + */ + if (sta->plink_state == NL80211_PLINK_ESTAB) + mps_qos_null_tx(sta); + + ieee80211_mps_local_status_update(sdata); +} + +/** + * ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control) + * + * @sdata: local mesh subif + * @sta: mesh STA + * @hdr: 802.11 frame header + * + * see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11 + * + * NOTE: sta must be given when an individually-addressed QoS frame header + * is handled, for group-addressed and management frames it is not used + */ +void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee80211_hdr *hdr) +{ + enum nl80211_mesh_power_mode pm; + u8 *qc; + + if (WARN_ON(is_unicast_ether_addr(hdr->addr1) && + ieee80211_is_data_qos(hdr->frame_control) && + !sta)) + return; + + if (is_unicast_ether_addr(hdr->addr1) && + ieee80211_is_data_qos(hdr->frame_control) && + sta->plink_state == NL80211_PLINK_ESTAB) + pm = sta->local_pm; + else + pm = sdata->u.mesh.nonpeer_pm; + + if (pm == NL80211_MESH_POWER_ACTIVE) + hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM); + else + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + qc = ieee80211_get_qos_ctl(hdr); + + if ((is_unicast_ether_addr(hdr->addr1) && + pm == NL80211_MESH_POWER_DEEP_SLEEP) || + (is_multicast_ether_addr(hdr->addr1) && + sdata->u.mesh.ps_peers_deep_sleep > 0)) + qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); + else + qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8); +} + +/** + * ieee80211_mps_sta_status_update - update buffering status of neighbor STA + * + * @sta: mesh STA + * + * called after change of peering status or non-peer/peer-specific power mode + */ +void ieee80211_mps_sta_status_update(struct sta_info *sta) +{ + enum nl80211_mesh_power_mode pm; + bool do_buffer; + + /* + * use peer-specific power mode if peering is established and the + * peer's power mode is known + */ + if (sta->plink_state == NL80211_PLINK_ESTAB && + sta->peer_pm != NL80211_MESH_POWER_UNKNOWN) + pm = sta->peer_pm; + else + pm = sta->nonpeer_pm; + + do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); + + /* Don't let the same PS state be set twice */ + if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) + return; + + if (do_buffer) { + set_sta_flag(sta, WLAN_STA_PS_STA); + atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps); + mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n", + sta->sta.addr); + } else { + ieee80211_sta_ps_deliver_wakeup(sta); + } + + /* clear the MPSP flags for non-peers or active STA */ + if (sta->plink_state != NL80211_PLINK_ESTAB) { + clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); + clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + } else if (!do_buffer) { + clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); + } +} + +static void mps_set_sta_peer_pm(struct sta_info *sta, + struct ieee80211_hdr *hdr) +{ + enum nl80211_mesh_power_mode pm; + u8 *qc = ieee80211_get_qos_ctl(hdr); + + /* + * Test Power Management field of frame control (PW) and + * mesh power save level subfield of QoS control field (PSL) + * + * | PM | PSL| Mesh PM | + * +----+----+---------+ + * | 0 |Rsrv| Active | + * | 1 | 0 | Light | + * | 1 | 1 | Deep | + */ + if (ieee80211_has_pm(hdr->frame_control)) { + if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8)) + pm = NL80211_MESH_POWER_DEEP_SLEEP; + else + pm = NL80211_MESH_POWER_LIGHT_SLEEP; + } else { + pm = NL80211_MESH_POWER_ACTIVE; + } + + if (sta->peer_pm == pm) + return; + + mps_dbg(sta->sdata, "STA %pM enters mode %d\n", + sta->sta.addr, pm); + + sta->peer_pm = pm; + + ieee80211_mps_sta_status_update(sta); +} + +static void mps_set_sta_nonpeer_pm(struct sta_info *sta, + struct ieee80211_hdr *hdr) +{ + enum nl80211_mesh_power_mode pm; + + if (ieee80211_has_pm(hdr->frame_control)) + pm = NL80211_MESH_POWER_DEEP_SLEEP; + else + pm = NL80211_MESH_POWER_ACTIVE; + + if (sta->nonpeer_pm == pm) + return; + + mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n", + sta->sta.addr, pm); + + sta->nonpeer_pm = pm; + + ieee80211_mps_sta_status_update(sta); +} + +/** + * ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave + * + * @sta: STA info that transmitted the frame + * @hdr: IEEE 802.11 (QoS) Header + */ +void ieee80211_mps_rx_h_sta_process(struct sta_info *sta, + struct ieee80211_hdr *hdr) +{ + if (is_unicast_ether_addr(hdr->addr1) && + ieee80211_is_data_qos(hdr->frame_control)) { + /* + * individually addressed QoS Data/Null frames contain + * peer link-specific PS mode towards the local STA + */ + mps_set_sta_peer_pm(sta, hdr); + + /* check for mesh Peer Service Period trigger frames */ + ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr), + sta, false, false); + } else { + /* + * can only determine non-peer PS mode + * (see IEEE802.11-2012 8.2.4.1.7) + */ + mps_set_sta_nonpeer_pm(sta, hdr); + } +} + + +/* mesh PS frame release */ + +static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct sk_buff *skb; + struct ieee80211_hdr *nullfunc; + struct ieee80211_tx_info *info; + u8 *qc; + + skb = mps_qos_null_get(sta); + if (!skb) + return; + + nullfunc = (struct ieee80211_hdr *) skb->data; + if (!eosp) + nullfunc->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + /* + * | RSPI | EOSP | MPSP triggering | + * +------+------+--------------------+ + * | 0 | 0 | local STA is owner | + * | 0 | 1 | no MPSP (MPSP end) | + * | 1 | 0 | both STA are owner | + * | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2 + */ + qc = ieee80211_get_qos_ctl(nullfunc); + if (rspi) + qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8); + if (eosp) + qc[0] |= IEEE80211_QOS_CTL_EOSP; + + info = IEEE80211_SKB_CB(skb); + + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n", + rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr); + + ieee80211_tx_skb(sdata, skb); +} + +/** + * mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed + * + * To properly end a mesh MPSP the last transmitted frame has to set the EOSP + * flag in the QoS Control field. In case the current tailing frame is not a + * QoS Data frame, append a QoS Null to carry the flag. + */ +static void mpsp_qos_null_append(struct sta_info *sta, + struct sk_buff_head *frames) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct sk_buff *new_skb, *skb = skb_peek_tail(frames); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_tx_info *info; + + if (ieee80211_is_data_qos(hdr->frame_control)) + return; + + new_skb = mps_qos_null_get(sta); + if (!new_skb) + return; + + mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n", + sta->sta.addr); + /* + * This frame has to be transmitted last. Assign lowest priority to + * make sure it cannot pass other frames when releasing multiple ACs. + */ + new_skb->priority = 1; + skb_set_queue_mapping(new_skb, IEEE80211_AC_BK); + ieee80211_set_qos_hdr(sdata, new_skb); + + info = IEEE80211_SKB_CB(new_skb); + info->control.vif = &sdata->vif; + info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + + __skb_queue_tail(frames, new_skb); +} + +/** + * mps_frame_deliver - transmit frames during mesh powersave + * + * @sta: STA info to transmit to + * @n_frames: number of frames to transmit. -1 for all + */ +static void mps_frame_deliver(struct sta_info *sta, int n_frames) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + int ac; + struct sk_buff_head frames; + struct sk_buff *skb; + bool more_data = false; + + skb_queue_head_init(&frames); + + /* collect frame(s) from buffers */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + while (n_frames != 0) { + skb = skb_dequeue(&sta->tx_filtered[ac]); + if (!skb) { + skb = skb_dequeue( + &sta->ps_tx_buf[ac]); + if (skb) + local->total_ps_buffered--; + } + if (!skb) + break; + n_frames--; + __skb_queue_tail(&frames, skb); + } + + if (!skb_queue_empty(&sta->tx_filtered[ac]) || + !skb_queue_empty(&sta->ps_tx_buf[ac])) + more_data = true; + } + + /* nothing to send? -> EOSP */ + if (skb_queue_empty(&frames)) { + mpsp_trigger_send(sta, false, true); + return; + } + + /* in a MPSP make sure the last skb is a QoS Data frame */ + if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) + mpsp_qos_null_append(sta, &frames); + + mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n", + skb_queue_len(&frames), sta->sta.addr); + + /* prepare collected frames for transmission */ + skb_queue_walk(&frames, skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *) skb->data; + + /* + * Tell TX path to send this frame even though the + * STA may still remain is PS mode after this frame + * exchange. + */ + info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER; + + if (more_data || !skb_queue_is_last(&frames, skb)) + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + + if (skb_queue_is_last(&frames, skb) && + ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qoshdr = ieee80211_get_qos_ctl(hdr); + + /* MPSP trigger frame ends service period */ + *qoshdr |= IEEE80211_QOS_CTL_EOSP; + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + } + } + + ieee80211_add_pending_skbs(local, &frames); + sta_info_recalc_tim(sta); +} + +/** + * ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods + * + * @qc: QoS Control field + * @sta: peer to start a MPSP with + * @tx: frame was transmitted by the local STA + * @acked: frame has been transmitted successfully + * + * NOTE: active mode STA may only serve as MPSP owner + */ +void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, + bool tx, bool acked) +{ + u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8); + u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP; + + if (tx) { + if (rspi && acked) + set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + + if (eosp) + clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); + else if (acked && + test_sta_flag(sta, WLAN_STA_PS_STA) && + !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) + mps_frame_deliver(sta, -1); + } else { + if (eosp) + clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE) + set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + + if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) + mps_frame_deliver(sta, -1); + } +} + +/** + * ieee80211_mps_frame_release - release buffered frames in response to beacon + * + * @sta: mesh STA + * @elems: beacon IEs + * + * For peers if we have individually-addressed frames buffered or the peer + * indicates buffered frames, send a corresponding MPSP trigger frame. Since + * we do not evaluate the awake window duration, QoS Nulls are used as MPSP + * trigger frames. If the neighbour STA is not a peer, only send single frames. + */ +void ieee80211_mps_frame_release(struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + int ac, buffer_local = 0; + bool has_buffered = false; + + /* TIM map only for LLID <= IEEE80211_MAX_AID */ + if (sta->plink_state == NL80211_PLINK_ESTAB) + has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, + le16_to_cpu(sta->llid) % IEEE80211_MAX_AID); + + if (has_buffered) + mps_dbg(sta->sdata, "%pM indicates buffered frames\n", + sta->sta.addr); + + /* only transmit to PS STA with announced, non-zero awake window */ + if (test_sta_flag(sta, WLAN_STA_PS_STA) && + (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) + return; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + + skb_queue_len(&sta->tx_filtered[ac]); + + if (!has_buffered && !buffer_local) + return; + + if (sta->plink_state == NL80211_PLINK_ESTAB) + mpsp_trigger_send(sta, has_buffered, !buffer_local); + else + mps_frame_deliver(sta, 1); +} diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a19089565c4b..c98be0593756 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1452,6 +1452,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) } } + /* mesh power save support */ + if (ieee80211_vif_is_mesh(&rx->sdata->vif)) + ieee80211_mps_rx_h_sta_process(sta, hdr); + /* * Drop (qos-)data::nullfunc frames silently, since they * are used only to control station power saving mode. @@ -2090,7 +2094,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(fwd_hdr->addr1)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast); memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); + /* update power mode indication when forwarding */ + ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr); } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { + /* mesh power mode flags updated in mesh_nexthop_lookup */ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); } else { /* unable to resolve next hop */ diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 227233c3ff7f..47a0f0601768 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -120,6 +120,8 @@ static void cleanup_single_sta(struct sta_info *sta) if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ps = &sdata->bss->ps; + else if (ieee80211_vif_is_mesh(&sdata->vif)) + ps = &sdata->u.mesh.ps; else return; @@ -587,6 +589,12 @@ void sta_info_recalc_tim(struct sta_info *sta) ps = &sta->sdata->bss->ps; id = sta->sta.aid; +#ifdef CONFIG_MAC80211_MESH + } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { + ps = &sta->sdata->u.mesh.ps; + /* TIM map only for PLID <= IEEE80211_MAX_AID */ + id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID; +#endif } else { return; } @@ -745,8 +753,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, bool have_buffered = false; int ac; - /* This is only necessary for stations on BSS interfaces */ - if (!sta->sdata->bss) + /* This is only necessary for stations on BSS/MBSS interfaces */ + if (!sta->sdata->bss && + !ieee80211_vif_is_mesh(&sta->sdata->vif)) return false; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) @@ -934,6 +943,11 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, if (time_after(jiffies, sta->last_rx + exp_time)) { sta_dbg(sta->sdata, "expiring inactive STA %pM\n", sta->sta.addr); + + if (ieee80211_vif_is_mesh(&sdata->vif) && + test_sta_flag(sta, WLAN_STA_PS_STA)) + atomic_dec(&sdata->u.mesh.ps.num_sta_ps); + WARN_ON(__sta_info_destroy(sta)); } } @@ -992,6 +1006,8 @@ static void clear_sta_ps_flags(void *_sta) if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ps = &sdata->bss->ps; + else if (ieee80211_vif_is_mesh(&sdata->vif)) + ps = &sdata->u.mesh.ps; else return; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index af7d78aa5523..5a1deba2c645 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -56,6 +56,8 @@ * @WLAN_STA_INSERTED: This station is inserted into the hash table. * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. + * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period. + * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH, @@ -78,6 +80,8 @@ enum ieee80211_sta_info_flags { WLAN_STA_INSERTED, WLAN_STA_RATE_CONTROL, WLAN_STA_TOFFSET_KNOWN, + WLAN_STA_MPSP_OWNER, + WLAN_STA_MPSP_RECIPIENT, }; #define ADDBA_RESP_INTERVAL HZ @@ -282,6 +286,9 @@ struct sta_ampdu_mlme { * @t_offset_setpoint: reference timing offset of this sta to be used when * calculating clockdrift * @ch_width: peer's channel width + * @local_pm: local link-specific power save mode + * @peer_pm: peer-specific power save mode towards local STA + * @nonpeer_pm: STA power save mode towards non-peer neighbors * @debugfs: debug filesystem info * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver @@ -379,6 +386,10 @@ struct sta_info { s64 t_offset; s64 t_offset_setpoint; enum nl80211_chan_width ch_width; + /* mesh power save */ + enum nl80211_mesh_power_mode local_pm; + enum nl80211_mesh_power_mode peer_pm; + enum nl80211_mesh_power_mode nonpeer_pm; #endif #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/net/mac80211/status.c b/net/mac80211/status.c index d041de056b7f..43439203f4e4 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -472,6 +472,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) return; } + /* mesh Peer Service Period support */ + if (ieee80211_vif_is_mesh(&sta->sdata->vif) && + ieee80211_is_data_qos(fc)) + ieee80211_mpsp_trigger_process( + ieee80211_get_qos_ctl(hdr), + sta, true, acked); + if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) && (rates_idx != -1)) sta->last_tx_rate = info->status.rates[rates_idx]; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7892b0a8873e..2ef0e19b06bb 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) if (sdata->vif.type == NL80211_IFTYPE_AP) ps = &sdata->u.ap.ps; + else if (ieee80211_vif_is_mesh(&sdata->vif)) + ps = &sdata->u.mesh.ps; else continue; @@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) /* * broadcast/multicast frame * - * If any of the associated stations is in power save mode, + * If any of the associated/peer stations is in power save mode, * the frame is buffered to be sent after DTIM beacon frame. * This is done either by the hardware or us. */ - /* powersaving STAs currently only in AP/VLAN mode */ + /* powersaving STAs currently only in AP/VLAN/mesh mode */ if (tx->sdata->vif.type == NL80211_IFTYPE_AP || tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { if (!tx->sdata->bss) return TX_CONTINUE; ps = &tx->sdata->bss->ps; + } else if (ieee80211_vif_is_mesh(&tx->sdata->vif)) { + ps = &tx->sdata->u.mesh.ps; } else { return TX_CONTINUE; } @@ -1473,12 +1477,14 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, hdr = (struct ieee80211_hdr *) skb->data; info->control.vif = &sdata->vif; - if (ieee80211_vif_is_mesh(&sdata->vif) && - ieee80211_is_data(hdr->frame_control) && - !is_multicast_ether_addr(hdr->addr1) && - mesh_nexthop_resolve(skb, sdata)) { - /* skb queued: don't free */ - return; + if (ieee80211_vif_is_mesh(&sdata->vif)) { + if (ieee80211_is_data(hdr->frame_control) && + is_unicast_ether_addr(hdr->addr1)) { + if (mesh_nexthop_resolve(skb, sdata)) + return; /* skb queued: don't free */ + } else { + ieee80211_mps_set_frame_flags(sdata, NULL, hdr); + } } ieee80211_set_qos_hdr(sdata, skb); @@ -2445,12 +2451,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, 2 + /* NULL SSID */ 2 + 8 + /* supported rates */ 2 + 3 + /* DS params */ + 256 + /* TIM IE */ 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + - sdata->u.mesh.ie_len); + sdata->u.mesh.ie_len + + 2 + sizeof(__le16)); /* awake window */ if (!skb) goto out; @@ -2462,6 +2470,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, eth_broadcast_addr(mgmt->da); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt); mgmt->u.beacon.beacon_int = cpu_to_le16(sdata->vif.bss_conf.beacon_int); mgmt->u.beacon.capab_info |= cpu_to_le16( @@ -2475,12 +2484,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(skb, sdata) || + ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) || ieee80211_add_ext_srates_ie(sdata, skb, true, band) || mesh_add_rsn_ie(skb, sdata) || mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || + mesh_add_awake_window_ie(skb, sdata) || mesh_add_vendor_ies(skb, sdata)) { pr_err("o11s: couldn't add ies!\n"); goto out; @@ -2734,6 +2745,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, goto out; ps = &sdata->u.ap.ps; + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { + ps = &sdata->u.mesh.ps; } else { goto out; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 139ad9b66c39..6cb71a350edd 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -805,6 +805,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, elems->peering = pos; elems->peering_len = elen; break; + case WLAN_EID_MESH_AWAKE_WINDOW: + if (elen >= 2) + elems->awake_window = (void *)pos; + break; case WLAN_EID_PREQ: elems->preq = pos; elems->preq_len = elen; diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 906f00cd6d2f..afba19cb6f87 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, /* qos header is 2 bytes */ *p++ = ack_policy | tid; - *p = ieee80211_vif_is_mesh(&sdata->vif) ? - (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; + if (ieee80211_vif_is_mesh(&sdata->vif)) { + /* preserve RSPI and Mesh PS Level bit */ + *p &= ((IEEE80211_QOS_CTL_RSPI | + IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8); + + /* Nulls don't have a mesh header (frame body) */ + if (!ieee80211_is_qos_nullfunc(hdr->frame_control)) + *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8); + } else { + *p = 0; + } } -- cgit v1.2.3 From c5c351088ae76b46ae08dec1bb7f621e0721c78b Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 31 Jan 2013 16:30:57 +0000 Subject: netns: fdb: allow unprivileged users to add/del fdb entries Right now,only ixgdb,macvlan,vxlan and bridge implement fdb_add/fdb_del operations. these operations only operate the private data of net device. So allowing the unprivileged users who creates the userns and netns to add/del fdb entries will do no harm to other netns. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9a419b099482..c1e4db60eeca 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2058,9 +2058,6 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) u8 *addr; int err; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); if (err < 0) return err; @@ -2127,9 +2124,6 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) int err = -EINVAL; __u8 *addr; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (nlmsg_len(nlh) < sizeof(*ndm)) return -EINVAL; -- cgit v1.2.3 From bb12b8b26e197b21e3f28d5401bfee6f86a8d633 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 31 Jan 2013 16:30:58 +0000 Subject: netns: ebtable: allow unprivileged users to operate ebtables ebt_table is a private resource of netns, operating ebtables in one netns will not affect other netns, we can allow the creator user of userns and netns to change the ebtables. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- net/bridge/netfilter/ebtables.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 5fe2ff3b01ef..8d493c91a562 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1472,16 +1472,17 @@ static int do_ebt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) { int ret; + struct net *net = sock_net(sk); - if (!capable(CAP_NET_ADMIN)) + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; switch(cmd) { case EBT_SO_SET_ENTRIES: - ret = do_replace(sock_net(sk), user, len); + ret = do_replace(net, user, len); break; case EBT_SO_SET_COUNTERS: - ret = update_counters(sock_net(sk), user, len); + ret = update_counters(net, user, len); break; default: ret = -EINVAL; @@ -1494,14 +1495,15 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) int ret; struct ebt_replace tmp; struct ebt_table *t; + struct net *net = sock_net(sk); - if (!capable(CAP_NET_ADMIN)) + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&tmp, user, sizeof(tmp))) return -EFAULT; - t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex); + t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); if (!t) return ret; @@ -2279,16 +2281,17 @@ static int compat_do_ebt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) { int ret; + struct net *net = sock_net(sk); - if (!capable(CAP_NET_ADMIN)) + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; switch (cmd) { case EBT_SO_SET_ENTRIES: - ret = compat_do_replace(sock_net(sk), user, len); + ret = compat_do_replace(net, user, len); break; case EBT_SO_SET_COUNTERS: - ret = compat_update_counters(sock_net(sk), user, len); + ret = compat_update_counters(net, user, len); break; default: ret = -EINVAL; @@ -2302,8 +2305,9 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd, int ret; struct compat_ebt_replace tmp; struct ebt_table *t; + struct net *net = sock_net(sk); - if (!capable(CAP_NET_ADMIN)) + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; /* try real handler in case userland supplied needed padding */ @@ -2314,7 +2318,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd, if (copy_from_user(&tmp, user, sizeof(tmp))) return -EFAULT; - t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex); + t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); if (!t) return ret; -- cgit v1.2.3 From e4d343ea92bdce831f071d9706b2daf097e6d009 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 31 Jan 2013 16:30:59 +0000 Subject: netns: bridge: allow unprivileged users add/delete mdb entry since the mdb table is belong to bridge device,and the bridge device can only be seen in one netns. So it's safe to allow unprivileged user which is the creator of userns and netns to modify the mdb table. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- net/bridge/br_mdb.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index acc9f4cc18f7..38991e03646d 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -272,9 +272,6 @@ static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_device *dev; int err; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - err = nlmsg_parse(nlh, sizeof(*bpm), tb, MDBA_SET_ENTRY, NULL); if (err < 0) return err; -- cgit v1.2.3 From 8c98653f05534acd1cb07ea4929702a3659177d1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 1 Feb 2013 04:37:43 +0000 Subject: sctp: sctp_close: fix release of bindings for deferred call_rcu's It seems due to RCU usage, i.e. within SCTP's address binding list, a, say, ``behavioral change'' was introduced which does actually not conform to the RFC anymore. In particular consider the following (fictional) scenario to demonstrate this: do: Two SOCK_SEQPACKET-style sockets are opened (S1, S2) S1 is bound to 127.0.0.1, port 1024 [server] S2 is bound to 127.0.0.1, port 1025 [client] listen(2) is invoked on S1 From S2 we call one sendmsg(2) with msg.msg_name and msg.msg_namelen parameters set to the server's address S1, S2 are closed goto do The first pass of this loop passes successful, while the second round fails during binding of S1 (address still in use). What is happening? In the first round, the initial handshake is being done, and, at the time close(2) is called on S1, a non-graceful shutdown is performed via ABORT since in S1's receive queue an unprocessed packet is present, thus stating an error condition. This can be considered as a correct behavior. During close also all bound addresses are freed, thus nothing *must* be active anymore. In reference to RFC2960: After checking the Verification Tag, the receiving endpoint shall remove the association from its record, and shall report the termination to its upper layer. (9.1 Abort of an Association) Also, no half-open states are supported, thus after an ungraceful shutdown, we leave nothing behind. However, this seems not to be happening though. In a real-world scenario, this is exactly where it breaks the lksctp-tools functional test suite, *for instance*: ./test_sockopt test_sockopt.c 1 PASS : getsockopt(SCTP_STATUS) on a socket with no assoc test_sockopt.c 2 PASS : getsockopt(SCTP_STATUS) test_sockopt.c 3 PASS : getsockopt(SCTP_STATUS) with invalid associd test_sockopt.c 4 PASS : getsockopt(SCTP_STATUS) with NULL associd test_sockopt.c 5 BROK : bind: Address already in use The underlying problem is that sctp_endpoint_destroy() hasn't been triggered yet while the next bind attempt is being done. It will be triggered eventually (but too late) by sctp_transport_destroy_rcu() after one RCU grace period: sctp_transport_destroy() sctp_transport_destroy_rcu() ----. sctp_association_put() [*] <--+--> sctp_packet_free() sctp_association_destroy() [...] sctp_endpoint_put() skb->destructor sctp_endpoint_destroy() sctp_wfree() sctp_bind_addr_free() sctp_association_put() [*] Thus, we move out the condition with sctp_association_put() as well as the sctp_packet_free() invocation and the issue can be solved. We also better free the SCTP chunks first before putting the ref of the association. With this patch, the example above (which simulates a similar scenario as in the implementation of this test case) and therefore also the test suite run successfully through. Tested by myself. Cc: Vlad Yasevich Signed-off-by: Daniel Borkmann Acked-by: Vlad Yasevich Acked-by: Neil Horman Signed-off-by: David S. Miller --- net/sctp/transport.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 4e45bb68aef0..ca5331cc1ed2 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -168,10 +168,6 @@ static void sctp_transport_destroy_rcu(struct rcu_head *head) struct sctp_transport *transport; transport = container_of(head, struct sctp_transport, rcu); - if (transport->asoc) - sctp_association_put(transport->asoc); - - sctp_packet_free(&transport->packet); dst_release(transport->dst); kfree(transport); @@ -186,6 +182,11 @@ static void sctp_transport_destroy(struct sctp_transport *transport) SCTP_ASSERT(transport->dead, "Transport is not dead", return); call_rcu(&transport->rcu, sctp_transport_destroy_rcu); + + sctp_packet_free(&transport->packet); + + if (transport->asoc) + sctp_association_put(transport->asoc); } /* Start T3_rtx timer if it is not already running and update the heartbeat -- cgit v1.2.3 From 25cc4ae913a46bcc11b03c37bec59568f2122a36 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Sun, 3 Feb 2013 20:32:57 +0000 Subject: net: remove redundant check for timer pending state before del_timer As in del_timer() there has already placed a timer_pending() function to check whether the timer to be deleted is pending or not, it's unnecessary to check timer pending state again before del_timer() is called. Signed-off-by: Ying Xue Cc: Eric Dumazet Signed-off-by: David S. Miller --- net/bluetooth/rfcomm/core.c | 4 ++-- net/bridge/netfilter/ebt_ulog.c | 6 ++---- net/core/sock.c | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 12 ++++-------- net/irda/af_irda.c | 3 +-- net/netfilter/ipvs/ip_vs_conn.c | 3 +-- net/sctp/associola.c | 5 ++--- net/sctp/input.c | 3 +-- net/sctp/outqueue.c | 4 +--- net/sctp/sm_sideeffect.c | 6 ++---- net/sctp/transport.c | 11 ++++------- 11 files changed, 21 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 201fdf737209..b23e2713fea8 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -257,7 +257,7 @@ static void rfcomm_session_clear_timer(struct rfcomm_session *s) { BT_DBG("session %p state %ld", s, s->state); - if (timer_pending(&s->timer) && del_timer(&s->timer)) + if (del_timer(&s->timer)) rfcomm_session_put(s); } @@ -285,7 +285,7 @@ static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) { BT_DBG("dlc %p state %ld", d, d->state); - if (timer_pending(&d->timer) && del_timer(&d->timer)) + if (del_timer(&d->timer)) rfcomm_dlc_put(d); } diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 3476ec469740..3bf43f7bb9d4 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -70,8 +70,7 @@ static void ulog_send(unsigned int nlgroup) { ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup]; - if (timer_pending(&ub->timer)) - del_timer(&ub->timer); + del_timer(&ub->timer); if (!ub->skb) return; @@ -319,8 +318,7 @@ static void __exit ebt_ulog_fini(void) xt_unregister_target(&ebt_ulog_tg_reg); for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { ub = &ulog_buffers[i]; - if (timer_pending(&ub->timer)) - del_timer(&ub->timer); + del_timer(&ub->timer); spin_lock_bh(&ub->lock); if (ub->skb) { kfree_skb(ub->skb); diff --git a/net/core/sock.c b/net/core/sock.c index 235fb89e8973..f1e14e20d181 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2230,7 +2230,7 @@ EXPORT_SYMBOL(sk_reset_timer); void sk_stop_timer(struct sock *sk, struct timer_list* timer) { - if (timer_pending(timer) && del_timer(timer)) + if (del_timer(timer)) __sock_put(sk); } EXPORT_SYMBOL(sk_stop_timer); diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index b5ef3cba2250..7d168dcbd135 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -88,10 +88,8 @@ static void ulog_send(unsigned int nlgroupnum) { ulog_buff_t *ub = &ulog_buffers[nlgroupnum]; - if (timer_pending(&ub->timer)) { - pr_debug("ulog_send: timer was pending, deleting\n"); - del_timer(&ub->timer); - } + pr_debug("ulog_send: timer is deleting\n"); + del_timer(&ub->timer); if (!ub->skb) { pr_debug("ulog_send: nothing to send\n"); @@ -426,10 +424,8 @@ static void __exit ulog_tg_exit(void) /* remove pending timers and free allocated skb's */ for (i = 0; i < ULOG_MAXNLGROUPS; i++) { ub = &ulog_buffers[i]; - if (timer_pending(&ub->timer)) { - pr_debug("timer was pending, deleting\n"); - del_timer(&ub->timer); - } + pr_debug("timer is deleting\n"); + del_timer(&ub->timer); if (ub->skb) { kfree_skb(ub->skb); diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index b833677d83d6..d07e3a626446 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -2567,8 +2567,7 @@ bed: err); /* If watchdog is still activated, kill it! */ - if(timer_pending(&(self->watchdog))) - del_timer(&(self->watchdog)); + del_timer(&(self->watchdog)); IRDA_DEBUG(1, "%s(), ...waking up !\n", __func__); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 30e764ad021f..68e368a4beed 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -796,8 +796,7 @@ static void ip_vs_conn_expire(unsigned long data) */ if (likely(atomic_read(&cp->refcnt) == 1)) { /* delete the timer if it is activated by other users */ - if (timer_pending(&cp->timer)) - del_timer(&cp->timer); + del_timer(&cp->timer); /* does anybody control me? */ if (cp->control) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index b45ed1f96921..2f95f5a5145d 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -434,8 +434,7 @@ void sctp_association_free(struct sctp_association *asoc) * on our state. */ for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) { - if (timer_pending(&asoc->timers[i]) && - del_timer(&asoc->timers[i])) + if (del_timer(&asoc->timers[i])) sctp_association_put(asoc); } @@ -1497,7 +1496,7 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) /* Stop the SACK timer. */ timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK]; - if (timer_pending(timer) && del_timer(timer)) + if (del_timer(timer)) sctp_association_put(asoc); } } diff --git a/net/sctp/input.c b/net/sctp/input.c index 8bd3c279427e..965bbbbe48d4 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -468,8 +468,7 @@ void sctp_icmp_proto_unreachable(struct sock *sk, } else { struct net *net = sock_net(sk); - if (timer_pending(&t->proto_unreach_timer) && - del_timer(&t->proto_unreach_timer)) + if (del_timer(&t->proto_unreach_timer)) sctp_association_put(asoc); sctp_do_sm(net, SCTP_EVENT_T_OTHER, diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 9bcdbd02d777..01dca753db16 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1700,10 +1700,8 @@ static void sctp_check_transmitted(struct sctp_outq *q, * address. */ if (!transport->flight_size) { - if (timer_pending(&transport->T3_rtx_timer) && - del_timer(&transport->T3_rtx_timer)) { + if (del_timer(&transport->T3_rtx_timer)) sctp_transport_put(transport); - } } else if (restart_timer) { if (!mod_timer(&transport->T3_rtx_timer, jiffies + transport->rto)) diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index c9577754a708..8aab894aeabe 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -674,10 +674,8 @@ static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds, list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) { - if (timer_pending(&t->T3_rtx_timer) && - del_timer(&t->T3_rtx_timer)) { + if (del_timer(&t->T3_rtx_timer)) sctp_transport_put(t); - } } } @@ -1517,7 +1515,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_TIMER_STOP: timer = &asoc->timers[cmd->obj.to]; - if (timer_pending(timer) && del_timer(timer)) + if (del_timer(timer)) sctp_association_put(asoc); break; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index ca5331cc1ed2..fafd2a461ba0 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -151,13 +151,11 @@ void sctp_transport_free(struct sctp_transport *transport) * structure hang around in memory since we know * the tranport is going away. */ - if (timer_pending(&transport->T3_rtx_timer) && - del_timer(&transport->T3_rtx_timer)) + if (del_timer(&transport->T3_rtx_timer)) sctp_transport_put(transport); /* Delete the ICMP proto unreachable timer if it's active. */ - if (timer_pending(&transport->proto_unreach_timer) && - del_timer(&transport->proto_unreach_timer)) + if (del_timer(&transport->proto_unreach_timer)) sctp_association_put(transport->asoc); sctp_transport_put(transport); @@ -655,10 +653,9 @@ void sctp_transport_reset(struct sctp_transport *t) void sctp_transport_immediate_rtx(struct sctp_transport *t) { /* Stop pending T3_rtx_timer */ - if (timer_pending(&t->T3_rtx_timer)) { - (void)del_timer(&t->T3_rtx_timer); + if (del_timer(&t->T3_rtx_timer)) sctp_transport_put(t); - } + sctp_retransmit(&t->asoc->outqueue, t, SCTP_RTXR_T3_RTX); if (!timer_pending(&t->T3_rtx_timer)) { if (!mod_timer(&t->T3_rtx_timer, jiffies + t->rto)) -- cgit v1.2.3 From 56db1c5f41c2ed0300ca570fea279a7c189c18fb Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sun, 3 Feb 2013 21:34:10 +0000 Subject: mcast: do not check 'rv' twice in a row With the loop, don't check 'rv' twice in a row. Without the loop, 'rv' doesn't even need to be checked. Make the comment more grammar-friendly. Signed-off-by: Jean Sacren Signed-off-by: David S. Miller --- net/ipv6/mcast.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 8460a1f54a76..e5de48551864 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -427,11 +427,9 @@ int ip6_mc_source(int add, int omode, struct sock *sk, rv = 1; /* > 0 for insert logic below if sl_count is 0 */ for (i=0; isl_count; i++) { rv = !ipv6_addr_equal(&psl->sl_addr[i], source); - if (rv == 0) - break; + if (rv == 0) /* There is an error in the address. */ + goto done; } - if (rv == 0) /* address already there is an error */ - goto done; for (j=psl->sl_count-1; j>=i; j--) psl->sl_addr[j+1] = psl->sl_addr[j]; psl->sl_addr[i] = *source; -- cgit v1.2.3 From 27000929ef0f137b34ba219eb04b05e772eff2b1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 4 Feb 2013 15:46:15 -0500 Subject: ipcomp: Mark as netns_ok. This module is namespace aware, netns_ok was just disabled by default for sanity. Signed-off-by: David S. Miller --- net/ipv4/ipcomp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 9a46daed2f3c..f01d1b1aff7f 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -163,6 +163,7 @@ static const struct net_protocol ipcomp4_protocol = { .handler = xfrm4_rcv, .err_handler = ipcomp4_err, .no_policy = 1, + .netns_ok = 1, }; static int __init ipcomp4_init(void) -- cgit v1.2.3 From 35612a229c3a66aad7577d7f25e7ebb5286faf2d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 3 Feb 2013 01:24:07 +0000 Subject: netfilter: fix missing dependencies for NETFILTER_XT_MATCH_CONNLABEL It was possible to set NF_CONNTRACK=n and NF_CONNTRACK_LABELS=y via NETFILTER_XT_MATCH_CONNLABEL=y. warning: (NETFILTER_XT_MATCH_CONNLABEL) selects NF_CONNTRACK_LABELS which has unmet direct dependencies (NET && INET && NETFILTER && NF_CONNTRACK) Reported-by: Randy Dunlap Signed-off-by: Florian Westphal Acked-by: Randy Dunlap Signed-off-by: Pablo Neira Ayuso --- net/netfilter/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index eb2c8ebf6d99..d4dd702574e4 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -860,6 +860,7 @@ config NETFILTER_XT_MATCH_CONNBYTES config NETFILTER_XT_MATCH_CONNLABEL tristate '"connlabel" match support' select NF_CONNTRACK_LABELS + depends on NF_CONNTRACK depends on NETFILTER_ADVANCED ---help--- This match allows you to test and assign userspace-defined labels names -- cgit v1.2.3 From d52ed4379a8264bc9bc82c068448c424924f1fde Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 30 Jan 2013 20:20:39 +0100 Subject: netfilter: xt_CT: merge common code of revision 0 and 1 This patch merges the common code for revision 0 and 1. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_CT.c | 145 +++++++++++++++++++------------------------------- 1 file changed, 56 insertions(+), 89 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index bde009ed8d3b..d69f1c7532f7 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -20,12 +20,8 @@ #include #include -static unsigned int xt_ct_target_v0(struct sk_buff *skb, - const struct xt_action_param *par) +static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct) { - const struct xt_ct_target_info *info = par->targinfo; - struct nf_conn *ct = info->ct; - /* Previously seen (loopback)? Ignore. */ if (skb->nfct != NULL) return XT_CONTINUE; @@ -37,21 +33,22 @@ static unsigned int xt_ct_target_v0(struct sk_buff *skb, return XT_CONTINUE; } -static unsigned int xt_ct_target_v1(struct sk_buff *skb, +static unsigned int xt_ct_target_v0(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_ct_target_info_v1 *info = par->targinfo; + const struct xt_ct_target_info *info = par->targinfo; struct nf_conn *ct = info->ct; - /* Previously seen (loopback)? Ignore. */ - if (skb->nfct != NULL) - return XT_CONTINUE; + return xt_ct_target(skb, ct); +} - atomic_inc(&ct->ct_general.use); - skb->nfct = &ct->ct_general; - skb->nfctinfo = IP_CT_NEW; +static unsigned int xt_ct_target_v1(struct sk_buff *skb, + const struct xt_action_param *par) +{ + const struct xt_ct_target_info_v1 *info = par->targinfo; + struct nf_conn *ct = info->ct; - return XT_CONTINUE; + return xt_ct_target(skb, ct); } static u8 xt_ct_find_proto(const struct xt_tgchk_param *par) @@ -104,67 +101,6 @@ xt_ct_set_helper(struct nf_conn *ct, const char *helper_name, return 0; } -static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) -{ - struct xt_ct_target_info *info = par->targinfo; - struct nf_conntrack_tuple t; - struct nf_conn *ct; - int ret = -EOPNOTSUPP; - - if (info->flags & ~XT_CT_NOTRACK) - return -EINVAL; - - if (info->flags & XT_CT_NOTRACK) { - ct = nf_ct_untracked_get(); - atomic_inc(&ct->ct_general.use); - goto out; - } - -#ifndef CONFIG_NF_CONNTRACK_ZONES - if (info->zone) - goto err1; -#endif - - ret = nf_ct_l3proto_try_module_get(par->family); - if (ret < 0) - goto err1; - - memset(&t, 0, sizeof(t)); - ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); - ret = PTR_ERR(ct); - if (IS_ERR(ct)) - goto err2; - - ret = 0; - if ((info->ct_events || info->exp_events) && - !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, - GFP_KERNEL)) - goto err3; - - if (info->helper[0]) { - ret = xt_ct_set_helper(ct, info->helper, par); - if (ret < 0) - goto err3; - } - - __set_bit(IPS_TEMPLATE_BIT, &ct->status); - __set_bit(IPS_CONFIRMED_BIT, &ct->status); - - /* Overload tuple linked list to put us in template list. */ - hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, - &par->net->ct.tmpl); -out: - info->ct = ct; - return 0; - -err3: - nf_conntrack_free(ct); -err2: - nf_ct_l3proto_module_put(par->family); -err1: - return ret; -} - #ifdef CONFIG_NF_CONNTRACK_TIMEOUT static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout) { @@ -242,9 +178,9 @@ out: #endif } -static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) +static int xt_ct_tg_check(const struct xt_tgchk_param *par, + struct xt_ct_target_info_v1 *info) { - struct xt_ct_target_info_v1 *info = par->targinfo; struct nf_conntrack_tuple t; struct nf_conn *ct; int ret = -EOPNOTSUPP; @@ -309,20 +245,31 @@ err1: return ret; } -static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) +static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) { struct xt_ct_target_info *info = par->targinfo; - struct nf_conn *ct = info->ct; - struct nf_conn_help *help; + struct xt_ct_target_info_v1 info_v1 = { + .flags = info->flags, + .zone = info->zone, + .ct_events = info->ct_events, + .exp_events = info->exp_events, + }; + int ret; - if (!nf_ct_is_untracked(ct)) { - help = nfct_help(ct); - if (help) - module_put(help->helper->me); + memcpy(info_v1.helper, info->helper, sizeof(info->helper)); - nf_ct_l3proto_module_put(par->family); - } - nf_ct_put(info->ct); + ret = xt_ct_tg_check(par, &info_v1); + if (ret < 0) + return ret; + + info->ct = info_v1.ct; + + return ret; +} + +static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) +{ + return xt_ct_tg_check(par, par->targinfo); } static void xt_ct_destroy_timeout(struct nf_conn *ct) @@ -343,9 +290,9 @@ static void xt_ct_destroy_timeout(struct nf_conn *ct) #endif } -static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) +static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, + struct xt_ct_target_info_v1 *info) { - struct xt_ct_target_info_v1 *info = par->targinfo; struct nf_conn *ct = info->ct; struct nf_conn_help *help; @@ -361,6 +308,26 @@ static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) nf_ct_put(info->ct); } +static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) +{ + struct xt_ct_target_info *info = par->targinfo; + struct xt_ct_target_info_v1 info_v1 = { + .flags = info->flags, + .zone = info->zone, + .ct_events = info->ct_events, + .exp_events = info->exp_events, + .ct = info->ct, + }; + memcpy(info_v1.helper, info->helper, sizeof(info->helper)); + + xt_ct_tg_destroy(par, &info_v1); +} + +static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par) +{ + xt_ct_tg_destroy(par, par->targinfo); +} + static struct xt_target xt_ct_tg_reg[] __read_mostly = { { .name = "CT", -- cgit v1.2.3 From 5474f57f7d686ac918355419cb71496f835aaf5d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 30 Jan 2013 20:24:22 +0100 Subject: netfilter: xt_CT: add alias flag This patch adds the alias flag to support full NOTRACK target aliasing. Based on initial patch from Jozsef Kadlecsik. Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/xt_CT.h | 6 +++++- net/netfilter/xt_CT.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/netfilter/xt_CT.h b/include/uapi/linux/netfilter/xt_CT.h index a064b8af360c..5a688c1ca4d7 100644 --- a/include/uapi/linux/netfilter/xt_CT.h +++ b/include/uapi/linux/netfilter/xt_CT.h @@ -3,7 +3,11 @@ #include -#define XT_CT_NOTRACK 0x1 +enum { + XT_CT_NOTRACK = 1 << 0, + XT_CT_NOTRACK_ALIAS = 1 << 1, + XT_CT_MASK = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS, +}; struct xt_ct_target_info { __u16 flags; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index d69f1c7532f7..a60261cb0e80 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -185,9 +185,6 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, struct nf_conn *ct; int ret = -EOPNOTSUPP; - if (info->flags & ~XT_CT_NOTRACK) - return -EINVAL; - if (info->flags & XT_CT_NOTRACK) { ct = nf_ct_untracked_get(); atomic_inc(&ct->ct_general.use); @@ -256,6 +253,9 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) }; int ret; + if (info->flags & ~XT_CT_NOTRACK) + return -EINVAL; + memcpy(info_v1.helper, info->helper, sizeof(info->helper)); ret = xt_ct_tg_check(par, &info_v1); @@ -269,6 +269,21 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) { + struct xt_ct_target_info_v1 *info = par->targinfo; + + if (info->flags & ~XT_CT_NOTRACK) + return -EINVAL; + + return xt_ct_tg_check(par, par->targinfo); +} + +static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par) +{ + struct xt_ct_target_info_v1 *info = par->targinfo; + + if (info->flags & ~XT_CT_MASK) + return -EINVAL; + return xt_ct_tg_check(par, par->targinfo); } @@ -350,6 +365,17 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = { .table = "raw", .me = THIS_MODULE, }, + { + .name = "CT", + .family = NFPROTO_UNSPEC, + .revision = 2, + .targetsize = sizeof(struct xt_ct_target_info_v1), + .checkentry = xt_ct_tg_check_v2, + .destroy = xt_ct_tg_destroy_v1, + .target = xt_ct_target_v1, + .table = "raw", + .me = THIS_MODULE, + }, }; static unsigned int -- cgit v1.2.3 From c14b78e7decd0d1d5add6a4604feb8609fe920a9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 5 Feb 2013 01:50:26 +0100 Subject: netfilter: nfnetlink: add mutex per subsystem This patch replaces the global lock to one lock per subsystem. The per-subsystem lock avoids that processes operating with different subsystems are synchronized. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink.h | 4 +-- net/netfilter/ipset/ip_set_core.c | 26 +++++++++--------- net/netfilter/nf_conntrack_netlink.c | 12 ++++----- net/netfilter/nfnetlink.c | 52 ++++++++++++++++++++++-------------- 4 files changed, 53 insertions(+), 41 deletions(-) (limited to 'net') diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 4966ddec039b..ecbb8e495912 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); -extern void nfnl_lock(void); -extern void nfnl_unlock(void); +extern void nfnl_lock(__u8 subsys_id); +extern void nfnl_unlock(__u8 subsys_id); #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6d6d8f2b033e..f82b2e606cfd 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision) static bool load_settype(const char *name) { - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); pr_debug("try to load ip_set_%s\n", name); if (request_module("ip_set_%s", name) < 0) { pr_warning("Can't find ip_set type %s\n", name); - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); return false; } - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); return true; } @@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name) ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); for (i = 0; i < ip_set_max; i++) { s = nfnl_set(i); if (s != NULL && STREQ(s->name, name)) { @@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name) break; } } - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); return index; } @@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index) if (index > ip_set_max) return IPSET_INVALID_ID; - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); set = nfnl_set(index); if (set) __ip_set_get(set); else index = IPSET_INVALID_ID; - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); return index; } @@ -584,11 +584,11 @@ void ip_set_nfnl_put(ip_set_id_t index) { struct ip_set *set; - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); set = nfnl_set(index); if (set != NULL) __ip_set_put(set); - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); } EXPORT_SYMBOL_GPL(ip_set_nfnl_put); @@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) goto done; } req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); find_set_and_id(req_get->set.name, &id); req_get->set.index = id; - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); goto copy; } case IP_SET_OP_GET_BYINDEX: { @@ -1778,11 +1778,11 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) ret = -EINVAL; goto done; } - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_IPSET); set = nfnl_set(req_get->set.index); strncpy(req_get->set.name, set ? set->name : "", IPSET_MAXNAMELEN); - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_IPSET); goto copy; } default: diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 2334cc5d2b16..d490a300ce2b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, if (!parse_nat_setup) { #ifdef CONFIG_MODULES rcu_read_unlock(); - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_CTNETLINK); if (request_module("nf-nat") < 0) { - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_CTNETLINK); rcu_read_lock(); return -EOPNOTSUPP; } - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_CTNETLINK); rcu_read_lock(); if (nfnetlink_parse_nat_setup_hook) return -EAGAIN; @@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, if (err == -EAGAIN) { #ifdef CONFIG_MODULES rcu_read_unlock(); - nfnl_unlock(); + nfnl_unlock(NFNL_SUBSYS_CTNETLINK); if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_CTNETLINK); rcu_read_lock(); return -EOPNOTSUPP; } - nfnl_lock(); + nfnl_lock(NFNL_SUBSYS_CTNETLINK); rcu_read_lock(); #else err = -EOPNOTSUPP; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 58a09b7c3f6d..d578ec251712 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER); static char __initdata nfversion[] = "0.30"; -static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; -static DEFINE_MUTEX(nfnl_mutex); +static struct { + struct mutex mutex; + const struct nfnetlink_subsystem __rcu *subsys; +} table[NFNL_SUBSYS_COUNT]; static const int nfnl_group2type[NFNLGRP_MAX+1] = { [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, @@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = { [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, }; -void nfnl_lock(void) +void nfnl_lock(__u8 subsys_id) { - mutex_lock(&nfnl_mutex); + mutex_lock(&table[subsys_id].mutex); } EXPORT_SYMBOL_GPL(nfnl_lock); -void nfnl_unlock(void) +void nfnl_unlock(__u8 subsys_id) { - mutex_unlock(&nfnl_mutex); + mutex_unlock(&table[subsys_id].mutex); } EXPORT_SYMBOL_GPL(nfnl_unlock); +static struct mutex *nfnl_get_lock(__u8 subsys_id) +{ + return &table[subsys_id].mutex; +} + int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) { - nfnl_lock(); - if (subsys_table[n->subsys_id]) { - nfnl_unlock(); + nfnl_lock(n->subsys_id); + if (table[n->subsys_id].subsys) { + nfnl_unlock(n->subsys_id); return -EBUSY; } - rcu_assign_pointer(subsys_table[n->subsys_id], n); - nfnl_unlock(); + rcu_assign_pointer(table[n->subsys_id].subsys, n); + nfnl_unlock(n->subsys_id); return 0; } @@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register); int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) { - nfnl_lock(); - subsys_table[n->subsys_id] = NULL; - nfnl_unlock(); + nfnl_lock(n->subsys_id); + table[n->subsys_id].subsys = NULL; + nfnl_unlock(n->subsys_id); synchronize_rcu(); return 0; } @@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t if (subsys_id >= NFNL_SUBSYS_COUNT) return NULL; - return rcu_dereference(subsys_table[subsys_id]); + return rcu_dereference(table[subsys_id].subsys); } static inline const struct nfnl_callback * @@ -175,6 +182,7 @@ replay: struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *attr = (void *)nlh + min_len; int attrlen = nlh->nlmsg_len - min_len; + __u8 subsys_id = NFNL_SUBSYS_ID(type); err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, ss->cb[cb_id].policy); @@ -189,10 +197,9 @@ replay: rcu_read_unlock(); } else { rcu_read_unlock(); - nfnl_lock(); - if (rcu_dereference_protected( - subsys_table[NFNL_SUBSYS_ID(type)], - lockdep_is_held(&nfnl_mutex)) != ss || + nfnl_lock(subsys_id); + if (rcu_dereference_protected(table[subsys_id].subsys, + lockdep_is_held(nfnl_get_lock(subsys_id))) != ss || nfnetlink_find_client(type, ss) != nc) err = -EAGAIN; else if (nc->call) @@ -200,7 +207,7 @@ replay: (const struct nlattr **)cda); else err = -EINVAL; - nfnl_unlock(); + nfnl_unlock(subsys_id); } if (err == -EAGAIN) goto replay; @@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = { static int __init nfnetlink_init(void) { + int i; + + for (i=0; i Date: Wed, 7 Nov 2012 07:05:08 +0000 Subject: netfilter: nf_ct_pptp: Fix comment referring to incorrect RFC The header of this file cites to "RFFC2673" which is "Binary Labels in the Domain Name System". It should refer to "RFC 2637" which is "Point-to-Point Tunneling Protocol (PPTP)". This patch also corrects the typo RFFC. Signed-off-by: Reese Moore Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_pptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index cc7669ef0b95..e6678d2b624e 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -14,7 +14,7 @@ * Limitations: * - We blindly assume that control connections are always * established in PNS->PAC direction. This is a violation - * of RFFC2673 + * of RFC 2637 * - We can only support one single call within each session * TODO: * - testing of incoming PPTP calls -- cgit v1.2.3 From aa5a1b8e68c95151fd249a3b5ec444c6b0aa2f1c Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 4 Feb 2013 14:12:41 -0800 Subject: mac80211: stop plink timer only on mesh interfaces Since mesh_plink_quiesce() would unconditionally delete the plink timer, and the timer initialization was recently moved into the mesh code path, suspending with a non-mesh interface now causes a crash. Fix this by only deleting the plink timer for mesh interfaces. Reported-by: Emmanuel Grumbach Tested-by: Emmanuel Grumbach Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index fe7c3334d6fe..67524e7d2548 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -592,6 +592,9 @@ static void mesh_plink_timer(unsigned long data) #ifdef CONFIG_PM void mesh_plink_quiesce(struct sta_info *sta) { + if (!ieee80211_vif_is_mesh(&sta->sdata->vif)) + return; + if (del_timer_sync(&sta->plink_timer)) sta->plink_timer_was_running = true; } -- cgit v1.2.3 From f8ccac0e44934ff9414b31cc3167a5c828afec73 Mon Sep 17 00:00:00 2001 From: Tom Parkin Date: Thu, 31 Jan 2013 23:43:00 +0000 Subject: l2tp: put tunnel socket release on a workqueue To allow l2tp_tunnel_delete to be called from an atomic context, place the tunnel socket release calls on a workqueue for asynchronous execution. Tunnel memory is eventually freed in the tunnel socket destructor. Signed-off-by: Tom Parkin Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 103 +++++++++++++++++++++++++++++---------------------- net/l2tp/l2tp_core.h | 2 + 2 files changed, 61 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 06389d5ff120..73988c070561 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -101,6 +101,7 @@ struct l2tp_skb_cb { static atomic_t l2tp_tunnel_count; static atomic_t l2tp_session_count; +static struct workqueue_struct *l2tp_wq; /* per-net private data for this module */ static unsigned int l2tp_net_id; @@ -122,7 +123,6 @@ static inline struct l2tp_net *l2tp_pernet(struct net *net) return net_generic(net, l2tp_net_id); } - /* Tunnel reference counts. Incremented per session that is added to * the tunnel. */ @@ -1277,6 +1277,7 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb); static void l2tp_tunnel_destruct(struct sock *sk) { struct l2tp_tunnel *tunnel; + struct l2tp_net *pn; tunnel = sk->sk_user_data; if (tunnel == NULL) @@ -1284,9 +1285,8 @@ static void l2tp_tunnel_destruct(struct sock *sk) l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: closing...\n", tunnel->name); - /* Close all sessions */ - l2tp_tunnel_closeall(tunnel); + /* Disable udp encapsulation */ switch (tunnel->encap) { case L2TP_ENCAPTYPE_UDP: /* No longer an encapsulation socket. See net/ipv4/udp.c */ @@ -1298,17 +1298,23 @@ static void l2tp_tunnel_destruct(struct sock *sk) } /* Remove hooks into tunnel socket */ - tunnel->sock = NULL; sk->sk_destruct = tunnel->old_sk_destruct; sk->sk_user_data = NULL; + tunnel->sock = NULL; - /* Call the original destructor */ - if (sk->sk_destruct) - (*sk->sk_destruct)(sk); + /* Remove the tunnel struct from the tunnel list */ + pn = l2tp_pernet(tunnel->l2tp_net); + spin_lock_bh(&pn->l2tp_tunnel_list_lock); + list_del_rcu(&tunnel->list); + spin_unlock_bh(&pn->l2tp_tunnel_list_lock); + atomic_dec(&l2tp_tunnel_count); - /* We're finished with the socket */ + l2tp_tunnel_closeall(tunnel); l2tp_tunnel_dec_refcount(tunnel); + /* Call the original destructor */ + if (sk->sk_destruct) + (*sk->sk_destruct)(sk); end: return; } @@ -1382,20 +1388,41 @@ again: */ static 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); - l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name); - - /* Remove from tunnel list */ - spin_lock_bh(&pn->l2tp_tunnel_list_lock); - list_del_rcu(&tunnel->list); kfree_rcu(tunnel, rcu); - spin_unlock_bh(&pn->l2tp_tunnel_list_lock); +} - atomic_dec(&l2tp_tunnel_count); +/* Workqueue tunnel deletion function */ +static void l2tp_tunnel_del_work(struct work_struct *work) +{ + struct l2tp_tunnel *tunnel = NULL; + struct socket *sock = NULL; + struct sock *sk = NULL; + + tunnel = container_of(work, struct l2tp_tunnel, del_work); + sk = l2tp_tunnel_sock_lookup(tunnel); + if (!sk) + return; + + sock = sk->sk_socket; + BUG_ON(!sock); + + /* 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. + */ + inet_shutdown(sock, 2); + + /* If the tunnel's socket was created by the kernel, + * close the socket here since the socket was not + * created by userspace. + */ + if (sock->file == NULL) + inet_release(sock); + + l2tp_tunnel_sock_put(sk); } /* Create a socket for the tunnel, if one isn't set up by @@ -1657,6 +1684,9 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 sk->sk_allocation = GFP_ATOMIC; + /* Init delete workqueue struct */ + INIT_WORK(&tunnel->del_work, l2tp_tunnel_del_work); + /* Add tunnel to our list */ INIT_LIST_HEAD(&tunnel->list); atomic_inc(&l2tp_tunnel_count); @@ -1688,33 +1718,7 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); */ int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) { - int err = -EBADF; - struct socket *sock = NULL; - struct sock *sk = NULL; - - sk = l2tp_tunnel_sock_lookup(tunnel); - if (!sk) - goto out; - - sock = sk->sk_socket; - BUG_ON(!sock); - - /* 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. - */ - err = inet_shutdown(sock, 2); - - /* If the tunnel's socket was created by the kernel, - * close the socket here since the socket was not - * created by userspace. - */ - if (sock->file == NULL) - err = inet_release(sock); - - l2tp_tunnel_sock_put(sk); -out: - return err; + return (false == queue_work(l2tp_wq, &tunnel->del_work)); } EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); @@ -1912,6 +1916,13 @@ static int __init l2tp_init(void) if (rc) goto out; + l2tp_wq = alloc_workqueue("l2tp", WQ_NON_REENTRANT | WQ_UNBOUND, 0); + if (!l2tp_wq) { + pr_err("alloc_workqueue failed\n"); + rc = -ENOMEM; + goto out; + } + pr_info("L2TP core driver, %s\n", L2TP_DRV_VERSION); out: @@ -1921,6 +1932,10 @@ out: static void __exit l2tp_exit(void) { unregister_pernet_device(&l2tp_net_ops); + if (l2tp_wq) { + destroy_workqueue(l2tp_wq); + l2tp_wq = NULL; + } } module_init(l2tp_init); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index e62204cad4fe..8eb8f1d47f3a 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -191,6 +191,8 @@ struct l2tp_tunnel { int fd; /* Parent fd, if tunnel socket * was created by userspace */ + struct work_struct del_work; + uint8_t priv[0]; /* private data */ }; -- cgit v1.2.3 From b6fdfdfab0c57d4a62359277c09258bc2f5a1550 Mon Sep 17 00:00:00 2001 From: Tom Parkin Date: Thu, 31 Jan 2013 23:43:01 +0000 Subject: l2tp: set netnsok flag for netlink messages The L2TP netlink code can run in namespaces. Set the netnsok flag in genl_family to true to reflect that fact. Signed-off-by: Tom Parkin Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_netlink.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index bbba3a19e944..c1bab22db85e 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -37,6 +37,7 @@ static struct genl_family l2tp_nl_family = { .version = L2TP_GENL_VERSION, .hdrsize = 0, .maxattr = L2TP_ATTR_MAX, + .netnsok = true, }; /* Accessed under genl lock */ -- cgit v1.2.3 From cbb95e0ca92869cc94a1c5e5ac58395afbbda26e Mon Sep 17 00:00:00 2001 From: Tom Parkin Date: Thu, 31 Jan 2013 23:43:02 +0000 Subject: l2tp: prevent tunnel creation on netns mismatch l2tp_tunnel_create is passed a pointer to the network namespace for the tunnel, along with an optional file descriptor for the tunnel which may be passed in from userspace via. netlink. In the case where the file descriptor is defined, ensure that the namespace associated with that socket matches the namespace explicitly passed to l2tp_tunnel_create. Signed-off-by: Tom Parkin Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 73988c070561..60a498a459dd 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1593,11 +1593,18 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 if (err < 0) goto err; } else { - err = -EBADF; sock = sockfd_lookup(fd, &err); if (!sock) { - pr_err("tunl %hu: sockfd_lookup(fd=%d) returned %d\n", + pr_err("tunl %u: sockfd_lookup(fd=%d) returned %d\n", tunnel_id, fd, err); + err = -EBADF; + goto err; + } + + /* Reject namespace mismatches */ + if (!net_eq(sock_net(sock->sk), net)) { + pr_err("tunl %u: netns mismatch\n", tunnel_id); + err = -EINVAL; goto err; } } -- cgit v1.2.3 From 167eb17e0b178549f5e19036b16b6d6e35856b67 Mon Sep 17 00:00:00 2001 From: Tom Parkin Date: Thu, 31 Jan 2013 23:43:03 +0000 Subject: l2tp: create tunnel sockets in the right namespace When creating unmanaged tunnel sockets we should honour the network namespace passed to l2tp_tunnel_create. Furthermore, unmanaged tunnel sockets should not hold a reference to the network namespace lest they accidentally keep alive a namespace which should otherwise have been released. Unmanaged tunnel sockets now drop their namespace reference via sk_change_net, and are released in a new pernet exit callback, l2tp_exit_net. Signed-off-by: Tom Parkin Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 87 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 60a498a459dd..b6ba65fa3c50 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1409,18 +1409,18 @@ static void l2tp_tunnel_del_work(struct work_struct *work) sock = sk->sk_socket; BUG_ON(!sock); - /* 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 the tunnel socket was created directly by the kernel, use the + * sk_* API to release the socket now. Otherwise go through the + * inet_* layer to shut the socket down, and let userspace close it. + * In either case the tunnel resources are freed in the socket + * destructor when the tunnel socket goes away. */ - inet_shutdown(sock, 2); - - /* If the tunnel's socket was created by the kernel, - * close the socket here since the socket was not - * created by userspace. - */ - if (sock->file == NULL) - inet_release(sock); + if (sock->file == NULL) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sk_release_kernel(sk); + } else { + inet_shutdown(sock, 2); + } l2tp_tunnel_sock_put(sk); } @@ -1428,29 +1428,37 @@ static void l2tp_tunnel_del_work(struct work_struct *work) /* Create a socket for the tunnel, if one isn't set up by * userspace. This is used for static tunnels where there is no * managing L2TP daemon. + * + * Since we don't want these sockets to keep a namespace alive by + * themselves, we drop the socket's namespace refcount after creation. + * These sockets are freed when the namespace exits using the pernet + * exit hook. */ -static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct socket **sockp) +static int l2tp_tunnel_sock_create(struct net *net, + u32 tunnel_id, + u32 peer_tunnel_id, + struct l2tp_tunnel_cfg *cfg, + struct socket **sockp) { int err = -EINVAL; - struct sockaddr_in udp_addr; + struct socket *sock = NULL; + struct sockaddr_in udp_addr = {0}; + struct sockaddr_l2tpip ip_addr = {0}; #if IS_ENABLED(CONFIG_IPV6) - struct sockaddr_in6 udp6_addr; - struct sockaddr_l2tpip6 ip6_addr; + struct sockaddr_in6 udp6_addr = {0}; + struct sockaddr_l2tpip6 ip6_addr = {0}; #endif - struct sockaddr_l2tpip ip_addr; - struct socket *sock = NULL; switch (cfg->encap) { case L2TP_ENCAPTYPE_UDP: #if IS_ENABLED(CONFIG_IPV6) if (cfg->local_ip6 && cfg->peer_ip6) { - err = sock_create(AF_INET6, SOCK_DGRAM, 0, sockp); + err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock); if (err < 0) goto out; - sock = *sockp; + sk_change_net(sock->sk, net); - memset(&udp6_addr, 0, sizeof(udp6_addr)); udp6_addr.sin6_family = AF_INET6; memcpy(&udp6_addr.sin6_addr, cfg->local_ip6, sizeof(udp6_addr.sin6_addr)); @@ -1472,13 +1480,12 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t } else #endif { - err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp); + err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock); if (err < 0) goto out; - sock = *sockp; + sk_change_net(sock->sk, net); - memset(&udp_addr, 0, sizeof(udp_addr)); udp_addr.sin_family = AF_INET; udp_addr.sin_addr = cfg->local_ip; udp_addr.sin_port = htons(cfg->local_udp_port); @@ -1505,14 +1512,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t case L2TP_ENCAPTYPE_IP: #if IS_ENABLED(CONFIG_IPV6) if (cfg->local_ip6 && cfg->peer_ip6) { - err = sock_create(AF_INET6, SOCK_DGRAM, IPPROTO_L2TP, - sockp); + err = sock_create_kern(AF_INET6, SOCK_DGRAM, + IPPROTO_L2TP, &sock); if (err < 0) goto out; - sock = *sockp; + sk_change_net(sock->sk, net); - memset(&ip6_addr, 0, sizeof(ip6_addr)); ip6_addr.l2tp_family = AF_INET6; memcpy(&ip6_addr.l2tp_addr, cfg->local_ip6, sizeof(ip6_addr.l2tp_addr)); @@ -1534,14 +1540,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t } else #endif { - err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, - sockp); + err = sock_create_kern(AF_INET, SOCK_DGRAM, + IPPROTO_L2TP, &sock); if (err < 0) goto out; - sock = *sockp; + sk_change_net(sock->sk, net); - memset(&ip_addr, 0, sizeof(ip_addr)); ip_addr.l2tp_family = AF_INET; ip_addr.l2tp_addr = cfg->local_ip; ip_addr.l2tp_conn_id = tunnel_id; @@ -1565,8 +1570,10 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t } out: + *sockp = sock; if ((err < 0) && sock) { - sock_release(sock); + kernel_sock_shutdown(sock, SHUT_RDWR); + sk_release_kernel(sock->sk); *sockp = NULL; } @@ -1589,7 +1596,8 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 * kernel socket. */ if (fd < 0) { - err = l2tp_tunnel_sock_create(tunnel_id, peer_tunnel_id, cfg, &sock); + err = l2tp_tunnel_sock_create(net, tunnel_id, peer_tunnel_id, + cfg, &sock); if (err < 0) goto err; } else { @@ -1909,8 +1917,21 @@ static __net_init int l2tp_init_net(struct net *net) return 0; } +static __net_exit void l2tp_exit_net(struct net *net) +{ + struct l2tp_net *pn = l2tp_pernet(net); + struct l2tp_tunnel *tunnel = NULL; + + rcu_read_lock_bh(); + list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) { + (void)l2tp_tunnel_delete(tunnel); + } + rcu_read_unlock_bh(); +} + static struct pernet_operations l2tp_net_ops = { .init = l2tp_init_net, + .exit = l2tp_exit_net, .id = &l2tp_net_id, .size = sizeof(struct l2tp_net), }; -- cgit v1.2.3 From 9d6ddb19905223828485d8c40deaec76e4dea3a5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 5 Feb 2013 14:36:02 -0500 Subject: l2tp: Make ipv4 protocol handler namespace aware. The infrastructure is already pretty much entirely there to allow this conversion. The tunnel and session lookups have per-namespace tables, and the ipv4 bind lookup includes the namespace in the lookup key. Set netns_ok in l2tp_ip_protocol. Signed-off-by: David S. Miller --- net/l2tp/l2tp_ip.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 61d8b75d2686..f7ac8f42fee2 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -115,6 +115,7 @@ static inline struct sock *l2tp_ip_bind_lookup(struct net *net, __be32 laddr, in */ static int l2tp_ip_recv(struct sk_buff *skb) { + struct net *net = dev_net(skb->dev); struct sock *sk; u32 session_id; u32 tunnel_id; @@ -142,7 +143,7 @@ static int l2tp_ip_recv(struct sk_buff *skb) } /* Ok, this is a data packet. Lookup the session. */ - session = l2tp_session_find(&init_net, NULL, session_id); + session = l2tp_session_find(net, NULL, session_id); if (session == NULL) goto discard; @@ -173,14 +174,14 @@ pass_up: goto discard; tunnel_id = ntohl(*(__be32 *) &skb->data[4]); - tunnel = l2tp_tunnel_find(&init_net, tunnel_id); + tunnel = l2tp_tunnel_find(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); + sk = __l2tp_ip_bind_lookup(net, iph->daddr, 0, tunnel_id); read_unlock_bh(&l2tp_ip_lock); } @@ -239,6 +240,7 @@ 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; + struct net *net = sock_net(sk); int ret; int chk_addr_ret; @@ -251,7 +253,8 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 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)) + if (__l2tp_ip_bind_lookup(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); @@ -260,7 +263,7 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 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); + chk_addr_ret = inet_addr_type(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) @@ -369,7 +372,7 @@ static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb) return 0; drop: - IP_INC_STATS(&init_net, IPSTATS_MIB_INDISCARDS); + IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS); kfree_skb(skb); return -1; } @@ -605,6 +608,7 @@ static struct inet_protosw l2tp_ip_protosw = { static struct net_protocol l2tp_ip_protocol __read_mostly = { .handler = l2tp_ip_recv, + .netns_ok = 1, }; static int __init l2tp_ip_init(void) -- cgit v1.2.3 From 547472b8e1da72ae226430c0c4273e36fc8ca768 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 5 Feb 2013 14:42:23 -0500 Subject: ipv4: Disallow non-namespace aware protocols to register. All in-tree ipv4 protocol implementations are now namespace aware. Therefore all the run-time checks are superfluous. Reject registry of any non-namespace aware ipv4 protocol. Eventually we'll remove prot->netns_ok and this registry time check as well. Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 19 ------------------- net/ipv4/ip_input.c | 7 ------- net/ipv4/protocol.c | 6 ++++++ 3 files changed, 6 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 49ddca31c4da..1aec92bf8018 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -263,21 +263,6 @@ void build_ehash_secret(void) } EXPORT_SYMBOL(build_ehash_secret); -static inline int inet_netns_ok(struct net *net, __u8 protocol) -{ - const struct net_protocol *ipprot; - - if (net_eq(net, &init_net)) - return 1; - - ipprot = rcu_dereference(inet_protos[protocol]); - if (ipprot == NULL) { - /* raw IP is OK */ - return 1; - } - return ipprot->netns_ok; -} - /* * Create an inet socket. */ @@ -350,10 +335,6 @@ lookup_protocol: !ns_capable(net->user_ns, CAP_NET_RAW)) goto out_rcu_unlock; - err = -EAFNOSUPPORT; - if (!inet_netns_ok(net, protocol)) - goto out_rcu_unlock; - sock->ops = answer->ops; answer_prot = answer->prot; answer_no_check = answer->no_check; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index f1395a6fb35f..87abd3e2bd32 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -208,13 +208,6 @@ static int ip_local_deliver_finish(struct sk_buff *skb) if (ipprot != NULL) { int ret; - if (!net_eq(net, &init_net) && !ipprot->netns_ok) { - net_info_ratelimited("%s: proto %d isn't netns-ready\n", - __func__, protocol); - kfree_skb(skb); - goto out; - } - if (!ipprot->no_policy) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 0f9d09f54bd9..ce848461acbb 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -37,6 +37,12 @@ const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly; int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol) { + if (!prot->netns_ok) { + pr_err("Protocol %u is not namespace aware, cannot register.\n", + protocol); + return -EINVAL; + } + return !cmpxchg((const struct net_protocol **)&inet_protos[protocol], NULL, prot) ? 0 : -1; } -- cgit v1.2.3 From ca2eb5679f8ddffff60156af42595df44a315ef0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 5 Feb 2013 07:25:17 +0000 Subject: tcp: remove Appropriate Byte Count support TCP Appropriate Byte Count was added by me, but later disabled. There is no point in maintaining it since it is a potential source of bugs and Linux already implements other better window protection heuristics. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 11 ----------- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - kernel/sysctl_binary.c | 1 - net/ipv4/sysctl_net_ipv4.c | 7 ------- net/ipv4/tcp.c | 1 - net/ipv4/tcp_cong.c | 30 +----------------------------- net/ipv4/tcp_input.c | 15 --------------- net/ipv4/tcp_minisocks.c | 1 - 9 files changed, 1 insertion(+), 67 deletions(-) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 19ac1802bfd4..dc2dc87d2557 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -130,17 +130,6 @@ somaxconn - INTEGER Defaults to 128. See also tcp_max_syn_backlog for additional tuning for TCP sockets. -tcp_abc - INTEGER - Controls Appropriate Byte Count (ABC) defined in RFC3465. - ABC is a way of increasing congestion window (cwnd) more slowly - in response to partial acknowledgments. - Possible values are: - 0 increase cwnd once per acknowledgment (no ABC) - 1 increase cwnd once per acknowledgment of full sized segment - 2 allow increase cwnd by two if acknowledgment is - of two segments to compensate for delayed acknowledgments. - Default: 0 (off) - tcp_abort_on_overflow - BOOLEAN If listening service is too slow to accept new connections, reset them. Default state is FALSE. It means that if overflow diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4e1d2283e3cc..6d0d46138ae8 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -246,7 +246,6 @@ struct tcp_sock { u32 sacked_out; /* SACK'd packets */ u32 fackets_out; /* FACK'd packets */ u32 tso_deferred; - u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ /* from STCP, retrans queue hinting */ struct sk_buff* lost_skb_hint; diff --git a/include/net/tcp.h b/include/net/tcp.h index 614af8b7758e..23f2e98d4b65 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -279,7 +279,6 @@ extern int sysctl_tcp_dma_copybreak; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; -extern int sysctl_tcp_abc; extern int sysctl_tcp_mtu_probing; extern int sysctl_tcp_base_mss; extern int sysctl_tcp_workaround_signed_windows; diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 5a6384450501..b669ca1fa103 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -387,7 +387,6 @@ static const struct bin_table bin_net_ipv4_table[] = { { CTL_INT, NET_TCP_MODERATE_RCVBUF, "tcp_moderate_rcvbuf" }, { CTL_INT, NET_TCP_TSO_WIN_DIVISOR, "tcp_tso_win_divisor" }, { CTL_STR, NET_TCP_CONG_CONTROL, "tcp_congestion_control" }, - { CTL_INT, NET_TCP_ABC, "tcp_abc" }, { CTL_INT, NET_TCP_MTU_PROBING, "tcp_mtu_probing" }, { CTL_INT, NET_TCP_BASE_MSS, "tcp_base_mss" }, { CTL_INT, NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS, "tcp_workaround_signed_windows" }, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 2622707602d1..960fd29d9b8e 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -632,13 +632,6 @@ static struct ctl_table ipv4_table[] = { .maxlen = TCP_CA_NAME_MAX, .proc_handler = proc_tcp_congestion_control, }, - { - .procname = "tcp_abc", - .data = &sysctl_tcp_abc, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_mtu_probing", .data = &sysctl_tcp_mtu_probing, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3ec1f69c5ceb..2c7e5963c2ea 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2289,7 +2289,6 @@ int tcp_disconnect(struct sock *sk, int flags) tp->packets_out = 0; tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tp->snd_cwnd_cnt = 0; - tp->bytes_acked = 0; tp->window_clamp = 0; tcp_set_ca_state(sk, TCP_CA_Open); tcp_clear_retrans(tp); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index cdf2e707bb10..019c2389a341 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -317,28 +317,11 @@ void tcp_slow_start(struct tcp_sock *tp) snd_cwnd = 1U; } - /* RFC3465: ABC Slow start - * Increase only after a full MSS of bytes is acked - * - * TCP sender SHOULD increase cwnd by the number of - * previously unacknowledged bytes ACKed by each incoming - * acknowledgment, provided the increase is not more than L - */ - if (sysctl_tcp_abc && tp->bytes_acked < tp->mss_cache) - return; - if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh) cnt = sysctl_tcp_max_ssthresh >> 1; /* limited slow start */ else cnt = snd_cwnd; /* exponential increase */ - /* RFC3465: ABC - * We MAY increase by 2 if discovered delayed ack - */ - if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) - cnt <<= 1; - tp->bytes_acked = 0; - tp->snd_cwnd_cnt += cnt; while (tp->snd_cwnd_cnt >= snd_cwnd) { tp->snd_cwnd_cnt -= snd_cwnd; @@ -378,20 +361,9 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* In "safe" area, increase. */ if (tp->snd_cwnd <= tp->snd_ssthresh) tcp_slow_start(tp); - /* In dangerous area, increase slowly. */ - else if (sysctl_tcp_abc) { - /* RFC3465: Appropriate Byte Count - * increase once for each full cwnd acked - */ - if (tp->bytes_acked >= tp->snd_cwnd*tp->mss_cache) { - tp->bytes_acked -= tp->snd_cwnd*tp->mss_cache; - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - } - } else { + else tcp_cong_avoid_ai(tp, tp->snd_cwnd); - } } EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e376aa9591bc..f56bd1082f54 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -98,7 +98,6 @@ int sysctl_tcp_frto_response __read_mostly; int sysctl_tcp_thin_dupack __read_mostly; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; -int sysctl_tcp_abc __read_mostly; int sysctl_tcp_early_retrans __read_mostly = 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ @@ -2007,7 +2006,6 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag) tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; tp->frto_counter = 0; - tp->bytes_acked = 0; tp->reordering = min_t(unsigned int, tp->reordering, sysctl_tcp_reordering); @@ -2056,7 +2054,6 @@ void tcp_enter_loss(struct sock *sk, int how) tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; - tp->bytes_acked = 0; tcp_clear_retrans_partial(tp); if (tcp_is_reno(tp)) @@ -2684,7 +2681,6 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) struct tcp_sock *tp = tcp_sk(sk); tp->high_seq = tp->snd_nxt; - tp->bytes_acked = 0; tp->snd_cwnd_cnt = 0; tp->prior_cwnd = tp->snd_cwnd; tp->prr_delivered = 0; @@ -2735,7 +2731,6 @@ void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) struct tcp_sock *tp = tcp_sk(sk); tp->prior_ssthresh = 0; - tp->bytes_acked = 0; if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { tp->undo_marker = 0; tcp_init_cwnd_reduction(sk, set_ssthresh); @@ -3417,7 +3412,6 @@ static void tcp_conservative_spur_to_response(struct tcp_sock *tp) { tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); tp->snd_cwnd_cnt = 0; - tp->bytes_acked = 0; TCP_ECN_queue_cwr(tp); tcp_moderate_cwnd(tp); } @@ -3609,15 +3603,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (after(ack, prior_snd_una)) flag |= FLAG_SND_UNA_ADVANCED; - if (sysctl_tcp_abc) { - if (icsk->icsk_ca_state < TCP_CA_CWR) - tp->bytes_acked += ack - prior_snd_una; - else if (icsk->icsk_ca_state == TCP_CA_Loss) - /* we assume just one segment left network */ - tp->bytes_acked += min(ack - prior_snd_una, - tp->mss_cache); - } - prior_fackets = tp->fackets_out; prior_in_flight = tcp_packets_in_flight(tp); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f35f2dfb6401..f0409287b5f4 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -446,7 +446,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, */ newtp->snd_cwnd = TCP_INIT_CWND; newtp->snd_cwnd_cnt = 0; - newtp->bytes_acked = 0; newtp->frto_counter = 0; newtp->frto_highmark = 0; -- cgit v1.2.3 From a0073fe18e718a1c815fe8b0120f1ac3c60284ba Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 5 Feb 2013 12:52:55 +0100 Subject: xfrm: Add a state resolution packet queue As the default, we blackhole packets until the key manager resolves the states. This patch implements a packet queue where IPsec packets are queued until the states are resolved. We generate a dummy xfrm bundle, the output routine of the returned route enqueues the packet to a per policy queue and arms a timer that checks for state resolution when dst_output() is called. Once the states are resolved, the packets are sent out of the queue. If the states are not resolved after some time, the queue is flushed. This patch keeps the defaut behaviour to blackhole packets as long as we have no states. To enable the packet queue the sysctl xfrm_larval_drop must be switched off. Signed-off-by: Steffen Klassert --- include/net/dst.h | 1 + include/net/xfrm.h | 7 ++ net/xfrm/xfrm_policy.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 233 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/dst.h b/include/net/dst.h index 9a7881066fb3..3da47e0a4a1f 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -61,6 +61,7 @@ struct dst_entry { #define DST_NOPEER 0x0040 #define DST_FAKE_RTABLE 0x0080 #define DST_XFRM_TUNNEL 0x0100 +#define DST_XFRM_QUEUE 0x0200 unsigned short pending_confirm; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 814a1baa175c..30f3e5b362ee 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -501,6 +501,12 @@ struct xfrm_policy_walk { u32 seq; }; +struct xfrm_policy_queue { + struct sk_buff_head hold_queue; + struct timer_list hold_timer; + unsigned long timeout; +}; + struct xfrm_policy { #ifdef CONFIG_NET_NS struct net *xp_net; @@ -522,6 +528,7 @@ struct xfrm_policy { struct xfrm_lifetime_cfg lft; struct xfrm_lifetime_cur curlft; struct xfrm_policy_walk_entry walk; + struct xfrm_policy_queue polq; u8 type; u8 action; u8 flags; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 41eabc46f110..456b11b0f049 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -35,6 +35,10 @@ #include "xfrm_hash.h" +#define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10)) +#define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ)) +#define XFRM_MAX_QUEUE_LEN 100 + DEFINE_MUTEX(xfrm_cfg_mutex); EXPORT_SYMBOL(xfrm_cfg_mutex); @@ -51,7 +55,7 @@ static struct kmem_cache *xfrm_dst_cache __read_mostly; static void xfrm_init_pmtu(struct dst_entry *dst); static int stale_bundle(struct dst_entry *dst); static int xfrm_bundle_ok(struct xfrm_dst *xdst); - +static void xfrm_policy_queue_process(unsigned long arg); static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int dir); @@ -287,8 +291,11 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp) INIT_HLIST_NODE(&policy->byidx); rwlock_init(&policy->lock); atomic_set(&policy->refcnt, 1); + skb_queue_head_init(&policy->polq.hold_queue); setup_timer(&policy->timer, xfrm_policy_timer, (unsigned long)policy); + setup_timer(&policy->polq.hold_timer, xfrm_policy_queue_process, + (unsigned long)policy); policy->flo.ops = &xfrm_policy_fc_ops; } return policy; @@ -309,6 +316,16 @@ void xfrm_policy_destroy(struct xfrm_policy *policy) } EXPORT_SYMBOL(xfrm_policy_destroy); +static void xfrm_queue_purge(struct sk_buff_head *list) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(list)) != NULL) { + dev_put(skb->dev); + kfree_skb(skb); + } +} + /* Rule must be locked. Release descentant resources, announce * entry dead. The rule must be unlinked from lists to the moment. */ @@ -319,6 +336,9 @@ static void xfrm_policy_kill(struct xfrm_policy *policy) atomic_inc(&policy->genid); + del_timer(&policy->polq.hold_timer); + xfrm_queue_purge(&policy->polq.hold_queue); + if (del_timer(&policy->timer)) xfrm_pol_put(policy); @@ -562,6 +582,31 @@ static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s return 0; } +static void xfrm_policy_requeue(struct xfrm_policy *old, + struct xfrm_policy *new) +{ + struct xfrm_policy_queue *pq = &old->polq; + struct sk_buff_head list; + + __skb_queue_head_init(&list); + + spin_lock_bh(&pq->hold_queue.lock); + skb_queue_splice_init(&pq->hold_queue, &list); + del_timer(&pq->hold_timer); + spin_unlock_bh(&pq->hold_queue.lock); + + if (skb_queue_empty(&list)) + return; + + pq = &new->polq; + + spin_lock_bh(&pq->hold_queue.lock); + skb_queue_splice(&list, &pq->hold_queue); + pq->timeout = XFRM_QUEUE_TMO_MIN; + mod_timer(&pq->hold_timer, jiffies); + spin_unlock_bh(&pq->hold_queue.lock); +} + int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) { struct net *net = xp_net(policy); @@ -603,8 +648,10 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) net->xfrm.policy_count[dir]++; atomic_inc(&flow_cache_genid); rt_genid_bump(net); - if (delpol) + if (delpol) { + xfrm_policy_requeue(delpol, policy); __xfrm_policy_unlink(delpol, dir); + } policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir); hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index)); policy->curlft.add_time = get_seconds(); @@ -1115,11 +1162,15 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir); __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); } - if (old_pol) + if (old_pol) { + if (pol) + xfrm_policy_requeue(old_pol, pol); + /* Unlinking succeeds always. This is the only function * allowed to delete or replace socket policy. */ __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); + } write_unlock_bh(&xfrm_policy_lock); if (old_pol) { @@ -1310,6 +1361,8 @@ static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *f * It means we need to try again resolving. */ if (xdst->num_xfrms > 0) return NULL; + } else if (dst->flags & DST_XFRM_QUEUE) { + return NULL; } else { /* Real bundle */ if (stale_bundle(dst)) @@ -1673,6 +1726,171 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, return xdst; } +static void xfrm_policy_queue_process(unsigned long arg) +{ + int err = 0; + struct sk_buff *skb; + struct sock *sk; + struct dst_entry *dst; + struct net_device *dev; + struct xfrm_policy *pol = (struct xfrm_policy *)arg; + struct xfrm_policy_queue *pq = &pol->polq; + struct flowi fl; + struct sk_buff_head list; + + spin_lock(&pq->hold_queue.lock); + skb = skb_peek(&pq->hold_queue); + dst = skb_dst(skb); + sk = skb->sk; + xfrm_decode_session(skb, &fl, dst->ops->family); + spin_unlock(&pq->hold_queue.lock); + + dst_hold(dst->path); + dst = xfrm_lookup(xp_net(pol), dst->path, &fl, + sk, 0); + if (IS_ERR(dst)) + goto purge_queue; + + if (dst->flags & DST_XFRM_QUEUE) { + dst_release(dst); + + if (pq->timeout >= XFRM_QUEUE_TMO_MAX) + goto purge_queue; + + pq->timeout = pq->timeout << 1; + mod_timer(&pq->hold_timer, jiffies + pq->timeout); + return; + } + + dst_release(dst); + + __skb_queue_head_init(&list); + + spin_lock(&pq->hold_queue.lock); + pq->timeout = 0; + skb_queue_splice_init(&pq->hold_queue, &list); + spin_unlock(&pq->hold_queue.lock); + + while (!skb_queue_empty(&list)) { + skb = __skb_dequeue(&list); + + xfrm_decode_session(skb, &fl, skb_dst(skb)->ops->family); + dst_hold(skb_dst(skb)->path); + dst = xfrm_lookup(xp_net(pol), skb_dst(skb)->path, + &fl, skb->sk, 0); + if (IS_ERR(dst)) { + dev_put(skb->dev); + kfree_skb(skb); + continue; + } + + nf_reset(skb); + skb_dst_drop(skb); + skb_dst_set(skb, dst); + + dev = skb->dev; + err = dst_output(skb); + dev_put(dev); + } + + return; + +purge_queue: + pq->timeout = 0; + xfrm_queue_purge(&pq->hold_queue); +} + +static int xdst_queue_output(struct sk_buff *skb) +{ + unsigned long sched_next; + struct dst_entry *dst = skb_dst(skb); + struct xfrm_dst *xdst = (struct xfrm_dst *) dst; + struct xfrm_policy_queue *pq = &xdst->pols[0]->polq; + + if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) { + kfree_skb(skb); + return -EAGAIN; + } + + skb_dst_force(skb); + dev_hold(skb->dev); + + spin_lock_bh(&pq->hold_queue.lock); + + if (!pq->timeout) + pq->timeout = XFRM_QUEUE_TMO_MIN; + + sched_next = jiffies + pq->timeout; + + if (del_timer(&pq->hold_timer)) { + if (time_before(pq->hold_timer.expires, sched_next)) + sched_next = pq->hold_timer.expires; + } + + __skb_queue_tail(&pq->hold_queue, skb); + mod_timer(&pq->hold_timer, sched_next); + + spin_unlock_bh(&pq->hold_queue.lock); + + return 0; +} + +static struct xfrm_dst *xfrm_create_dummy_bundle(struct net *net, + struct dst_entry *dst, + const struct flowi *fl, + int num_xfrms, + u16 family) +{ + int err; + struct net_device *dev; + struct dst_entry *dst1; + struct xfrm_dst *xdst; + + xdst = xfrm_alloc_dst(net, family); + if (IS_ERR(xdst)) + return xdst; + + if (net->xfrm.sysctl_larval_drop || num_xfrms <= 0 || + (fl->flowi_flags & FLOWI_FLAG_CAN_SLEEP)) + return xdst; + + dst1 = &xdst->u.dst; + dst_hold(dst); + xdst->route = dst; + + dst_copy_metrics(dst1, dst); + + dst1->obsolete = DST_OBSOLETE_FORCE_CHK; + dst1->flags |= DST_HOST | DST_XFRM_QUEUE; + dst1->lastuse = jiffies; + + dst1->input = dst_discard; + dst1->output = xdst_queue_output; + + dst_hold(dst); + dst1->child = dst; + dst1->path = dst; + + xfrm_init_path((struct xfrm_dst *)dst1, dst, 0); + + err = -ENODEV; + dev = dst->dev; + if (!dev) + goto free_dst; + + err = xfrm_fill_dst(xdst, dev, fl); + if (err) + goto free_dst; + +out: + return xdst; + +free_dst: + dst_release(dst1); + xdst = ERR_PTR(err); + goto out; +} + static struct flow_cache_object * xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct flow_cache_object *oldflo, void *ctx) @@ -1751,7 +1969,7 @@ make_dummy_bundle: /* We found policies, but there's no bundles to instantiate: * either because the policy blocks, has no transformations or * we could not build template (no xfrm_states).*/ - xdst = xfrm_alloc_dst(net, family); + xdst = xfrm_create_dummy_bundle(net, dst_orig, fl, num_xfrms, family); if (IS_ERR(xdst)) { xfrm_pols_put(pols, num_pols); return ERR_CAST(xdst); @@ -2359,6 +2577,9 @@ static int xfrm_bundle_ok(struct xfrm_dst *first) (dst->dev && !netif_running(dst->dev))) return 0; + if (dst->flags & DST_XFRM_QUEUE) + return 1; + last = NULL; do { -- cgit v1.2.3 From 1f53c808502f1472bfc5829e6dd80317c7198a4a Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Wed, 6 Feb 2013 10:46:23 +0100 Subject: xfrm: remove unused xfrm4_policy_fini() Function xfrm4_policy_fini() is unused since xfrm4_fini() was removed in 2.6.11. Signed-off-by: Michal Kubecek Signed-off-by: Steffen Klassert --- net/ipv4/xfrm4_policy.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'net') diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 3be0ac2c1920..0e28383c096f 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -270,15 +270,6 @@ static void __init xfrm4_policy_init(void) xfrm_policy_register_afinfo(&xfrm4_policy_afinfo); } -static void __exit xfrm4_policy_fini(void) -{ -#ifdef CONFIG_SYSCTL - if (sysctl_hdr) - unregister_net_sysctl_table(sysctl_hdr); -#endif - xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo); -} - void __init xfrm4_init(void) { dst_entries_init(&xfrm4_dst_ops); -- cgit v1.2.3 From 8d068875caca3b507ffa8a57d521483fd4eebcc7 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Wed, 6 Feb 2013 10:46:33 +0100 Subject: xfrm: make gc_thresh configurable in all namespaces The xfrm gc threshold can be configured via xfrm{4,6}_gc_thresh sysctl but currently only in init_net, other namespaces always use the default value. This can substantially limit the number of IPsec tunnels that can be effectively used. Signed-off-by: Michal Kubecek Signed-off-by: Steffen Klassert --- include/net/netns/ipv4.h | 1 + include/net/netns/ipv6.h | 1 + net/ipv4/xfrm4_policy.c | 49 ++++++++++++++++++++++++++++++++++++++++++--- net/ipv6/xfrm6_policy.c | 52 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 95 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 9b78862014a4..2ba9de89e8ec 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -22,6 +22,7 @@ struct netns_ipv4 { struct ctl_table_header *frags_hdr; struct ctl_table_header *ipv4_hdr; struct ctl_table_header *route_hdr; + struct ctl_table_header *xfrm4_hdr; #endif struct ipv4_devconf *devconf_all; struct ipv4_devconf *devconf_dflt; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 214cb0a53359..1242f371718b 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -16,6 +16,7 @@ struct netns_sysctl_ipv6 { struct ctl_table_header *route_hdr; struct ctl_table_header *icmp_hdr; struct ctl_table_header *frags_hdr; + struct ctl_table_header *xfrm6_hdr; #endif int bindv6only; int flush_delay; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 0e28383c096f..9a459be24af7 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -262,7 +262,51 @@ static struct ctl_table xfrm4_policy_table[] = { { } }; -static struct ctl_table_header *sysctl_hdr; +static int __net_init xfrm4_net_init(struct net *net) +{ + struct ctl_table *table; + struct ctl_table_header *hdr; + + table = xfrm4_policy_table; + if (!net_eq(net, &init_net)) { + table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL); + if (!table) + goto err_alloc; + + table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh; + } + + hdr = register_net_sysctl(net, "net/ipv4", table); + if (!hdr) + goto err_reg; + + net->ipv4.xfrm4_hdr = hdr; + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +static void __net_exit xfrm4_net_exit(struct net *net) +{ + struct ctl_table *table; + + if (net->ipv4.xfrm4_hdr == NULL) + return; + + table = net->ipv4.xfrm4_hdr->ctl_table_arg; + unregister_net_sysctl_table(net->ipv4.xfrm4_hdr); + if (!net_eq(net, &init_net)) + kfree(table); +} + +static struct pernet_operations __net_initdata xfrm4_net_ops = { + .init = xfrm4_net_init, + .exit = xfrm4_net_exit, +}; #endif static void __init xfrm4_policy_init(void) @@ -277,8 +321,7 @@ void __init xfrm4_init(void) xfrm4_state_init(); xfrm4_policy_init(); #ifdef CONFIG_SYSCTL - sysctl_hdr = register_net_sysctl(&init_net, "net/ipv4", - xfrm4_policy_table); + register_pernet_subsys(&xfrm4_net_ops); #endif } diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 128273744332..4ef7bdb65440 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -320,7 +320,51 @@ static struct ctl_table xfrm6_policy_table[] = { { } }; -static struct ctl_table_header *sysctl_hdr; +static int __net_init xfrm6_net_init(struct net *net) +{ + struct ctl_table *table; + struct ctl_table_header *hdr; + + table = xfrm6_policy_table; + if (!net_eq(net, &init_net)) { + table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL); + if (!table) + goto err_alloc; + + table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh; + } + + hdr = register_net_sysctl(net, "net/ipv6", table); + if (!hdr) + goto err_reg; + + net->ipv6.sysctl.xfrm6_hdr = hdr; + return 0; + +err_reg: + if (!net_eq(net, &init_net)) + kfree(table); +err_alloc: + return -ENOMEM; +} + +static void __net_exit xfrm6_net_exit(struct net *net) +{ + struct ctl_table *table; + + if (net->ipv6.sysctl.xfrm6_hdr == NULL) + return; + + table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg; + unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr); + if (!net_eq(net, &init_net)) + kfree(table); +} + +static struct pernet_operations xfrm6_net_ops = { + .init = xfrm6_net_init, + .exit = xfrm6_net_exit, +}; #endif int __init xfrm6_init(void) @@ -339,8 +383,7 @@ int __init xfrm6_init(void) goto out_policy; #ifdef CONFIG_SYSCTL - sysctl_hdr = register_net_sysctl(&init_net, "net/ipv6", - xfrm6_policy_table); + register_pernet_subsys(&xfrm6_net_ops); #endif out: return ret; @@ -352,8 +395,7 @@ out_policy: void xfrm6_fini(void) { #ifdef CONFIG_SYSCTL - if (sysctl_hdr) - unregister_net_sysctl_table(sysctl_hdr); + unregister_pernet_subsys(&xfrm6_net_ops); #endif xfrm6_policy_fini(); xfrm6_state_fini(); -- cgit v1.2.3 From 62b5942aa5182686e6bab2c6db5dbf2672b8981e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 4 Feb 2013 16:48:16 +0000 Subject: net: core: Remove unnecessary alloc/OOM messages alloc failures already get standardized OOM messages and a dump_stack. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- net/core/dev.c | 14 +++++--------- net/core/netprio_cgroup.c | 4 +--- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index a87bc74e9fd0..e04bfdc9e3e4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5958,10 +5958,9 @@ static int netif_alloc_rx_queues(struct net_device *dev) BUG_ON(count < 1); rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL); - if (!rx) { - pr_err("netdev: Unable to allocate %u rx queues\n", count); + if (!rx) return -ENOMEM; - } + dev->_rx = rx; for (i = 0; i < count; i++) @@ -5992,10 +5991,9 @@ static int netif_alloc_netdev_queues(struct net_device *dev) BUG_ON(count < 1); tx = kcalloc(count, sizeof(struct netdev_queue), GFP_KERNEL); - if (!tx) { - pr_err("netdev: Unable to allocate %u tx queues\n", count); + if (!tx) return -ENOMEM; - } + dev->_tx = tx; netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); @@ -6482,10 +6480,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, alloc_size += NETDEV_ALIGN - 1; p = kzalloc(alloc_size, GFP_KERNEL); - if (!p) { - pr_err("alloc_netdev: Unable to allocate device\n"); + if (!p) return NULL; - } dev = PTR_ALIGN(p, NETDEV_ALIGN); dev->padded = (char *)dev - (char *)p; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 5e67defe2cb0..0777d0aa18c3 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -69,10 +69,8 @@ static int extend_netdev_table(struct net_device *dev, u32 target_idx) /* allocate & copy */ new = kzalloc(new_sz, GFP_KERNEL); - if (!new) { - pr_warn("Unable to alloc new priomap!\n"); + if (!new) return -ENOMEM; - } if (old) memcpy(new->priomap, old->priomap, -- cgit v1.2.3 From f4e53e292ad0626a9d4f80cbdb6adae867d8b2bc Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 5 Feb 2013 01:48:05 +0000 Subject: ipv6: Don't send packet to big messages to self Calling icmpv6_send() on a local message size error leads to an incorrect update of the path mtu in the case when IPsec is used. So use ipv6_local_error() instead to notify the socket about the error. Reported-by: Jiri Bohac Signed-off-by: Steffen Klassert Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 906b7e6dd7fb..c953825f9098 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -242,9 +242,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, dst->dev, dst_output); } - net_dbg_ratelimited("IPv6: sending pkt_too_big to self\n"); skb->dev = dst->dev; - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + ipv6_local_error(sk, EMSGSIZE, fl6, mtu); IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); kfree_skb(skb); return -EMSGSIZE; -- cgit v1.2.3 From f458c647ea5cdd956f8055a4178072be460559f1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 5 Feb 2013 04:25:35 +0000 Subject: wpan: whitespace fix Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- net/mac802154/wpan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 199b92261e94..98c867bca986 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -41,7 +41,7 @@ static inline int mac802154_fetch_skb_u8(struct sk_buff *skb, u8 *val) return -EINVAL; *val = skb->data[0]; - skb_pull(skb, 1); + skb_pull(skb, 1); return 0; } -- cgit v1.2.3 From ca99ca14c95ae49fb4c9cd3abf5f84d11a7e8a61 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 5 Feb 2013 08:05:43 +0000 Subject: netpoll: protect napi_poll and poll_controller during dev_[open|close] Ivan Vercera was recently backporting commit 9c13cb8bb477a83b9a3c9e5a5478a4e21294a760 to a RHEL kernel, and I noticed that, while this patch protects the tg3 driver from having its ndo_poll_controller routine called during device initalization, it does nothing for the driver during shutdown. I.e. it would be entirely possible to have the ndo_poll_controller method (or subsequently the ndo_poll) routine called for a driver in the netpoll path on CPU A while in parallel on CPU B, the ndo_close or ndo_open routine could be called. Given that the two latter routines tend to initizlize and free many data structures that the former two rely on, the result can easily be data corruption or various other crashes. Furthermore, it seems that this is potentially a problem with all net drivers that support netpoll, and so this should ideally be fixed in a common path. As Ben H Pointed out to me, we can't preform dev_open/dev_close in atomic context, so I've come up with this solution. We can use a mutex to sleep in open/close paths and just do a mutex_trylock in the napi poll path and abandon the poll attempt if we're locked, as we'll just retry the poll on the next send anyway. I've tested this here by flooding netconsole with messages on a system whos nic driver I modfied to periodically return NETDEV_TX_BUSY, so that the netpoll tx workqueue would be forced to send frames and poll the device. While this was going on I rapidly ifdown/up'ed the interface and watched for any problems. I've not found any. Signed-off-by: Neil Horman CC: Ivan Vecera CC: "David S. Miller" CC: Ben Hutchings CC: Francois Romieu CC: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netpoll.h | 11 ++++++++++- net/core/dev.c | 27 ++++++++++++++++++++++++++- net/core/netpoll.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index f54c3bb6a22b..ab856d507b7e 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -38,8 +38,9 @@ struct netpoll { struct netpoll_info { atomic_t refcnt; - int rx_flags; + unsigned long rx_flags; spinlock_t rx_lock; + struct mutex dev_lock; struct list_head rx_np; /* netpolls that registered an rx_hook */ struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */ @@ -51,6 +52,14 @@ struct netpoll_info { struct rcu_head rcu; }; +#ifdef CONFIG_NETPOLL +extern int netpoll_rx_disable(struct net_device *dev); +extern void netpoll_rx_enable(struct net_device *dev); +#else +static inline int netpoll_rx_disable(struct net_device *dev) { return 0; } +static inline void netpoll_rx_enable(struct net_device *dev) { return; } +#endif + void netpoll_send_udp(struct netpoll *np, const char *msg, int len); void netpoll_print_options(struct netpoll *np); int netpoll_parse_options(struct netpoll *np, char *opt); diff --git a/net/core/dev.c b/net/core/dev.c index e04bfdc9e3e4..2b275a7b8677 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1266,6 +1266,14 @@ static int __dev_open(struct net_device *dev) if (!netif_device_present(dev)) return -ENODEV; + /* Block netpoll from trying to do any rx path servicing. + * If we don't do this there is a chance ndo_poll_controller + * or ndo_poll may be running while we open the device + */ + ret = netpoll_rx_disable(dev); + if (ret) + return ret; + ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev); ret = notifier_to_errno(ret); if (ret) @@ -1279,6 +1287,8 @@ static int __dev_open(struct net_device *dev) if (!ret && ops->ndo_open) ret = ops->ndo_open(dev); + netpoll_rx_enable(dev); + if (ret) clear_bit(__LINK_STATE_START, &dev->state); else { @@ -1370,9 +1380,16 @@ static int __dev_close(struct net_device *dev) int retval; LIST_HEAD(single); + /* Temporarily disable netpoll until the interface is down */ + retval = netpoll_rx_disable(dev); + if (retval) + return retval; + list_add(&dev->unreg_list, &single); retval = __dev_close_many(&single); list_del(&single); + + netpoll_rx_enable(dev); return retval; } @@ -1408,14 +1425,22 @@ static int dev_close_many(struct list_head *head) */ int dev_close(struct net_device *dev) { + int ret = 0; if (dev->flags & IFF_UP) { LIST_HEAD(single); + /* Block netpoll rx while the interface is going down */ + ret = netpoll_rx_disable(dev); + if (ret) + return ret; + list_add(&dev->unreg_list, &single); dev_close_many(&single); list_del(&single); + + netpoll_rx_enable(dev); } - return 0; + return ret; } EXPORT_SYMBOL(dev_close); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 331ccb90f915..edcd9ad95304 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -47,6 +47,8 @@ static struct sk_buff_head skb_pool; static atomic_t trapped; +static struct srcu_struct netpoll_srcu; + #define USEC_PER_POLL 50 #define NETPOLL_RX_ENABLED 1 #define NETPOLL_RX_DROP 2 @@ -199,6 +201,13 @@ static void netpoll_poll_dev(struct net_device *dev) const struct net_device_ops *ops; struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo); + /* Don't do any rx activity if the dev_lock mutex is held + * the dev_open/close paths use this to block netpoll activity + * while changing device state + */ + if (!mutex_trylock(&dev->npinfo->dev_lock)) + return; + if (!dev || !netif_running(dev)) return; @@ -211,6 +220,8 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); + mutex_unlock(&dev->npinfo->dev_lock); + if (dev->flags & IFF_SLAVE) { if (ni) { struct net_device *bond_dev; @@ -231,6 +242,31 @@ static void netpoll_poll_dev(struct net_device *dev) zap_completion_queue(); } +int netpoll_rx_disable(struct net_device *dev) +{ + struct netpoll_info *ni; + int idx; + might_sleep(); + idx = srcu_read_lock(&netpoll_srcu); + ni = srcu_dereference(dev->npinfo, &netpoll_srcu); + if (ni) + mutex_lock(&ni->dev_lock); + srcu_read_unlock(&netpoll_srcu, idx); + return 0; +} +EXPORT_SYMBOL(netpoll_rx_disable); + +void netpoll_rx_enable(struct net_device *dev) +{ + struct netpoll_info *ni; + rcu_read_lock(); + ni = rcu_dereference(dev->npinfo); + if (ni) + mutex_unlock(&ni->dev_lock); + rcu_read_unlock(); +} +EXPORT_SYMBOL(netpoll_rx_enable); + static void refill_skbs(void) { struct sk_buff *skb; @@ -1004,6 +1040,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) INIT_LIST_HEAD(&npinfo->rx_np); spin_lock_init(&npinfo->rx_lock); + mutex_init(&npinfo->dev_lock); skb_queue_head_init(&npinfo->neigh_tx); skb_queue_head_init(&npinfo->txq); INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); @@ -1169,6 +1206,7 @@ EXPORT_SYMBOL(netpoll_setup); static int __init netpoll_init(void) { skb_queue_head_init(&skb_pool); + init_srcu_struct(&netpoll_srcu); return 0; } core_initcall(netpoll_init); @@ -1208,6 +1246,8 @@ void __netpoll_cleanup(struct netpoll *np) spin_unlock_irqrestore(&npinfo->rx_lock, flags); } + synchronize_srcu(&netpoll_srcu); + if (atomic_dec_and_test(&npinfo->refcnt)) { const struct net_device_ops *ops; -- cgit v1.2.3 From a07fdceccf9d9f1b87f781e9a87662182e590d70 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 6 Feb 2013 15:54:38 -0500 Subject: 6lowpan: Remove __init tag from lowpan_netlink_fini(). It's called from both __init and __exit code, so neither tag is appropriate. Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 09cba81d2c4a..f62c3b963f95 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1267,7 +1267,7 @@ static inline int __init lowpan_netlink_init(void) return rtnl_link_register(&lowpan_link_ops); } -static inline void __init lowpan_netlink_fini(void) +static inline void lowpan_netlink_fini(void) { rtnl_link_unregister(&lowpan_link_ops); } -- cgit v1.2.3 From fc4e98dbbaa30ca2180df0b2b0d0608045ed3f19 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 5 Feb 2013 10:23:43 +0000 Subject: 6lowpan: use stack buffer instead of heap head buffer is only temporary available in lowpan_header_create. So it's not necessary to put it on the heap. Also fixed a comment codestyle issue. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index f62c3b963f95..43b95ca61114 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -377,17 +377,14 @@ static int lowpan_header_create(struct sk_buff *skb, struct ipv6hdr *hdr; const u8 *saddr = _saddr; const u8 *daddr = _daddr; - u8 *head; + u8 head[100]; struct ieee802154_addr sa, da; + /* TODO: + * if this package isn't ipv6 one, where should it be routed? + */ if (type != ETH_P_IPV6) return 0; - /* TODO: - * if this package isn't ipv6 one, where should it be routed? - */ - head = kzalloc(100, GFP_KERNEL); - if (head == NULL) - return -ENOMEM; hdr = ipv6_hdr(skb); hc06_ptr = head + 2; @@ -561,8 +558,6 @@ static int lowpan_header_create(struct sk_buff *skb, skb_pull(skb, sizeof(struct ipv6hdr)); memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head); - kfree(head); - lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); -- cgit v1.2.3 From 25060d8f3f2c21daadb4fc1fb0e37ce2c992e30b Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 5 Feb 2013 10:23:44 +0000 Subject: wpan: use stack buffer instead of heap head buffer is only temporary available in mac802154_header_create. So it's not necessary to put it on the heap. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- net/mac802154/wpan.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'net') diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 98c867bca986..d20c6d3c247d 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -137,16 +137,12 @@ static int mac802154_header_create(struct sk_buff *skb, struct ieee802154_addr dev_addr; struct mac802154_sub_if_data *priv = netdev_priv(dev); int pos = 2; - u8 *head; + u8 head[MAC802154_FRAME_HARD_HEADER_LEN]; u16 fc; if (!daddr) return -EINVAL; - head = kzalloc(MAC802154_FRAME_HARD_HEADER_LEN, GFP_KERNEL); - if (head == NULL) - return -ENOMEM; - head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ fc = mac_cb_type(skb); @@ -210,7 +206,6 @@ static int mac802154_header_create(struct sk_buff *skb, head[1] = fc >> 8; memcpy(skb_push(skb, pos), head, pos); - kfree(head); return pos; } -- cgit v1.2.3 From 12b0004d1d1e2a9aa667412d479041e403bcafae Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 5 Feb 2013 16:36:38 +0000 Subject: net: adjust skb_gso_segment() for calling in rx path skb_gso_segment() is almost always called in tx path, except for openvswitch. It calls this function when it receives the packet and tries to queue it to user-space. In this special case, the ->ip_summed check inside skb_gso_segment() is no longer true, as ->ip_summed value has different meanings on rx path. This patch adjusts skb_gso_segment() so that we can at least avoid such warnings on checksum. Cc: Jesse Gross Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 11 +++++++++-- net/core/dev.c | 21 ++++++++++++++++----- net/openvswitch/datapath.c | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 85b0949d9946..ab2774eb49e8 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2662,8 +2662,15 @@ extern int netdev_master_upper_dev_link(struct net_device *dev, extern void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev); extern int skb_checksum_help(struct sk_buff *skb); -extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, - netdev_features_t features); +extern struct sk_buff *__skb_gso_segment(struct sk_buff *skb, + netdev_features_t features, bool tx_path); + +static inline +struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) +{ + return __skb_gso_segment(skb, features, true); +} + #ifdef CONFIG_BUG extern void netdev_rx_csum_fault(struct net_device *dev); #else diff --git a/net/core/dev.c b/net/core/dev.c index 2b275a7b8677..65da698c500b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2327,18 +2327,29 @@ out: } EXPORT_SYMBOL(skb_checksum_help); +/* openvswitch calls this on rx path, so we need a different check. + */ +static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) +{ + if (tx_path) + return skb->ip_summed != CHECKSUM_PARTIAL; + else + return skb->ip_summed == CHECKSUM_NONE; +} + /** - * skb_gso_segment - Perform segmentation on skb. + * __skb_gso_segment - Perform segmentation on skb. * @skb: buffer to segment * @features: features for the output path (see dev->features) + * @tx_path: whether it is called in TX path * * This function segments the given skb and returns a list of segments. * * It may return NULL if the skb requires no segmentation. This is * only possible when GSO is used for verifying header integrity. */ -struct sk_buff *skb_gso_segment(struct sk_buff *skb, - netdev_features_t features) +struct sk_buff *__skb_gso_segment(struct sk_buff *skb, + netdev_features_t features, bool tx_path) { struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); struct packet_offload *ptype; @@ -2361,7 +2372,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, skb->mac_len = skb->network_header - skb->mac_header; __skb_pull(skb, skb->mac_len); - if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + if (unlikely(skb_needs_check(skb, tx_path))) { skb_warn_bad_offload(skb); if (skb_header_cloned(skb) && @@ -2390,7 +2401,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, return segs; } -EXPORT_SYMBOL(skb_gso_segment); +EXPORT_SYMBOL(__skb_gso_segment); /* Take action when hardware reception checksum errors are detected. */ #ifdef CONFIG_BUG diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index d8c13a965459..9dc537df46c4 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -301,7 +301,7 @@ static int queue_gso_packets(struct net *net, int dp_ifindex, struct sk_buff *segs, *nskb; int err; - segs = skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM); + segs = __skb_gso_segment(skb, NETIF_F_SG | NETIF_F_HW_CSUM, false); if (IS_ERR(segs)) return PTR_ERR(segs); -- cgit v1.2.3 From 6d1ccff627806829c46091bd9d9835302a3fbf5f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 5 Feb 2013 20:22:20 +0000 Subject: net: reset mac header in dev_start_xmit() On 64 bit arches : There is a off-by-one error in qdisc_pkt_len_init() because mac_header is not set in xmit path. skb_mac_header() returns an out of bound value that was harmless because hdr_len is an 'unsigned int' On 32bit arches, the error is abysmal. This patch is also a prereq for "macvlan: add multicast filter" Signed-off-by: Eric Dumazet Cc: Ben Greear Signed-off-by: David S. Miller --- net/core/dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 65da698c500b..2f31bf97ba65 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2835,6 +2835,8 @@ int dev_queue_xmit(struct sk_buff *skb) struct Qdisc *q; int rc = -ENOMEM; + skb_reset_mac_header(skb); + /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ -- cgit v1.2.3 From 241448c2b84ff02a46ce88199e05fdeb55bad449 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 7 Feb 2013 01:41:39 +0000 Subject: net: sctp: sctp_auth_make_key_vector: remove duplicate ntohs calls Instead of calling 3 times ntohs(random->param_hdr.length), 2 times ntohs(hmacs->param_hdr.length), and 3 times ntohs(chunks->param_hdr.length) within the same function, we only call each once and store it in a variable. Signed-off-by: Daniel Borkmann Acked-by: Neil Horman Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/sctp/auth.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 159b9bc5d633..94a12de58691 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -200,10 +200,14 @@ static struct sctp_auth_bytes *sctp_auth_make_key_vector( struct sctp_auth_bytes *new; __u32 len; __u32 offset = 0; + __u16 random_len, hmacs_len, chunks_len = 0; - len = ntohs(random->param_hdr.length) + ntohs(hmacs->param_hdr.length); - if (chunks) - len += ntohs(chunks->param_hdr.length); + random_len = ntohs(random->param_hdr.length); + hmacs_len = ntohs(hmacs->param_hdr.length); + if (chunks) + chunks_len = ntohs(chunks->param_hdr.length); + + len = random_len + hmacs_len + chunks_len; new = kmalloc(sizeof(struct sctp_auth_bytes) + len, gfp); if (!new) @@ -211,16 +215,15 @@ static struct sctp_auth_bytes *sctp_auth_make_key_vector( new->len = len; - memcpy(new->data, random, ntohs(random->param_hdr.length)); - offset += ntohs(random->param_hdr.length); + memcpy(new->data, random, random_len); + offset += random_len; if (chunks) { - memcpy(new->data + offset, chunks, - ntohs(chunks->param_hdr.length)); - offset += ntohs(chunks->param_hdr.length); + memcpy(new->data + offset, chunks, chunks_len); + offset += chunks_len; } - memcpy(new->data + offset, hmacs, ntohs(hmacs->param_hdr.length)); + memcpy(new->data + offset, hmacs, hmacs_len); return new; } -- cgit v1.2.3 From e9a4aa3ba386c8a368baaadde6f9e5c3d5f17cfe Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jan 2013 11:16:46 +0300 Subject: NFC: llcp: integer underflow in nfc_llcp_set_remote_gb() If gb_len is less than 3 it would cause an integer underflow and possibly memory corruption in nfc_llcp_parse_gb_tlv(). I removed the old test for gb_len == 0. I also removed the test for ->remote_gb == NULL. It's not possible for ->remote_gb to be NULL and we have already dereferenced ->remote_gb_len so it's too late to test. The old test return -ENODEV but my test returns -EINVAL. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- net/nfc/llcp/llcp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 85bc75c38dea..746f5a2f9804 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -549,14 +549,13 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len) pr_err("No LLCP device\n"); return -ENODEV; } + if (gb_len < 3) + return -EINVAL; memset(local->remote_gb, 0, NFC_MAX_GT_LEN); memcpy(local->remote_gb, gb, gb_len); local->remote_gb_len = gb_len; - if (local->remote_gb == NULL || local->remote_gb_len == 0) - return -ENODEV; - if (memcmp(local->remote_gb, llcp_magic, 3)) { pr_err("MAC does not support LLCP\n"); return -EINVAL; -- cgit v1.2.3 From e5e67305885eb12849b5475764b0542f03dc2b59 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 8 Feb 2013 10:17:15 +0000 Subject: skbuff: Move definition of NETDEV_FRAG_PAGE_MAX_SIZE In order to address the fact that some devices cannot support the full 32K frag size we need to have the value accessible somewhere so that we can use it to do comparisons against what the device can support. As such I am moving the values out of skbuff.c and into skbuff.h. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 ++++ net/core/skbuff.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0259b719bebf..d7573c37a51d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1832,6 +1832,10 @@ static inline void __skb_queue_purge(struct sk_buff_head *list) kfree_skb(skb); } +#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768) +#define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER) +#define NETDEV_PAGECNT_MAX_BIAS NETDEV_FRAG_PAGE_MAX_SIZE + extern void *netdev_alloc_frag(unsigned int fragsz); extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 55f7ef6ada6d..6114c1143564 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -351,10 +351,6 @@ struct netdev_alloc_cache { }; static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); -#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768) -#define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER) -#define NETDEV_PAGECNT_MAX_BIAS NETDEV_FRAG_PAGE_MAX_SIZE - static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) { struct netdev_alloc_cache *nc; -- cgit v1.2.3 From 6a98dcf032106eceb6827e0a7cc667250683f762 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Thu, 7 Feb 2013 15:52:40 +0000 Subject: ipv6: fix a RCU warning in net/ipv6/ip6_flowlabel.c This patch fixes the following RCU warning: [ 51.680236] =============================== [ 51.681914] [ INFO: suspicious RCU usage. ] [ 51.683610] 3.8.0-rc6-next-20130206-sasha-00028-g83214f7-dirty #276 Tainted: G W [ 51.686703] ------------------------------- [ 51.688281] net/ipv6/ip6_flowlabel.c:671 suspicious rcu_dereference_check() usage! we should use rcu_dereference_bh() when we hold rcu_read_lock_bh(). Reported-by: Sasha Levin Cc: David S. Miller Cc: YOSHIFUJI Hideaki Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/ipv6/ip6_flowlabel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index ea42bf40a997..a7da2f472479 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -65,13 +65,13 @@ static DEFINE_SPINLOCK(ip6_fl_lock); static DEFINE_SPINLOCK(ip6_sk_fl_lock); #define for_each_fl_rcu(hash, fl) \ - for (fl = rcu_dereference(fl_ht[(hash)]); \ + for (fl = rcu_dereference_bh(fl_ht[(hash)]); \ fl != NULL; \ - fl = rcu_dereference(fl->next)) + fl = rcu_dereference_bh(fl->next)) #define for_each_fl_continue_rcu(fl) \ - for (fl = rcu_dereference(fl->next); \ + for (fl = rcu_dereference_bh(fl->next); \ fl != NULL; \ - fl = rcu_dereference(fl->next)) + fl = rcu_dereference_bh(fl->next)) #define for_each_sk_fl_rcu(np, sfl) \ for (sfl = rcu_dereference_bh(np->ipv6_fl_list); \ -- cgit v1.2.3 From 03536e23acd3706c0ec93d01dc8bef44dab1a860 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 7 Feb 2013 23:22:58 +0000 Subject: net: sctp: sctp_auth_make_key_vector: use sctp_auth_create_key In sctp_auth_make_key_vector, we allocate a temporary sctp_auth_bytes structure with kmalloc instead of the sctp_auth_create_key allocator. Change this to sctp_auth_create_key as it is the case everywhere else, so that we also can properly free it via sctp_auth_key_put. This makes it easier for future code changes in the structure and allocator itself, since a single API is consistently used for this purpose. Also, by using sctp_auth_create_key we're doing sanity checks over the arguments. Signed-off-by: Daniel Borkmann Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/sctp/auth.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 94a12de58691..5ec7509bb2c9 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -209,12 +209,10 @@ static struct sctp_auth_bytes *sctp_auth_make_key_vector( len = random_len + hmacs_len + chunks_len; - new = kmalloc(sizeof(struct sctp_auth_bytes) + len, gfp); + new = sctp_auth_create_key(len, gfp); if (!new) return NULL; - new->len = len; - memcpy(new->data, random, random_len); offset += random_len; @@ -353,8 +351,8 @@ static struct sctp_auth_bytes *sctp_auth_asoc_create_secret( secret = sctp_auth_asoc_set_secret(ep_key, first_vector, last_vector, gfp); out: - kfree(local_key_vector); - kfree(peer_key_vector); + sctp_auth_key_put(local_key_vector); + sctp_auth_key_put(peer_key_vector); return secret; } -- cgit v1.2.3 From d021c344051af91f42c5ba9fdedc176740cbd238 Mon Sep 17 00:00:00 2001 From: Andy King Date: Wed, 6 Feb 2013 14:23:56 +0000 Subject: VSOCK: Introduce VM Sockets VM Sockets allows communication between virtual machines and the hypervisor. User level applications both in a virtual machine and on the host can use the VM Sockets API, which facilitates fast and efficient communication between guest virtual machines and their host. A socket address family, designed to be compatible with UDP and TCP at the interface level, is provided. Today, VM Sockets is used by various VMware Tools components inside the guest for zero-config, network-less access to VMware host services. In addition to this, VMware's users are using VM Sockets for various applications, where network access of the virtual machine is restricted or non-existent. Examples of this are VMs communicating with device proxies for proprietary hardware running as host applications and automated testing of applications running within virtual machines. The VMware VM Sockets are similar to other socket types, like Berkeley UNIX socket interface. The VM Sockets module supports both connection-oriented stream sockets like TCP, and connectionless datagram sockets like UDP. The VM Sockets protocol family is defined as "AF_VSOCK" and the socket operations split for SOCK_DGRAM and SOCK_STREAM. For additional information about the use of VM Sockets, please refer to the VM Sockets Programming Guide available at: https://www.vmware.com/support/developer/vmci-sdk/ Signed-off-by: George Zhang Signed-off-by: Dmitry Torokhov Signed-off-by: Andy king Signed-off-by: David S. Miller --- include/linux/socket.h | 4 +- include/uapi/linux/vm_sockets.h | 171 ++ net/Kconfig | 1 + net/Makefile | 1 + net/vmw_vsock/Kconfig | 28 + net/vmw_vsock/Makefile | 7 + net/vmw_vsock/af_vsock.c | 2015 ++++++++++++++++++++++++ net/vmw_vsock/af_vsock.h | 175 +++ net/vmw_vsock/vmci_transport.c | 2157 ++++++++++++++++++++++++++ net/vmw_vsock/vmci_transport.h | 139 ++ net/vmw_vsock/vmci_transport_notify.c | 680 ++++++++ net/vmw_vsock/vmci_transport_notify.h | 83 + net/vmw_vsock/vmci_transport_notify_qstate.c | 438 ++++++ net/vmw_vsock/vsock_addr.c | 86 + net/vmw_vsock/vsock_addr.h | 32 + net/vmw_vsock/vsock_version.h | 22 + 16 files changed, 6038 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/vm_sockets.h create mode 100644 net/vmw_vsock/Kconfig create mode 100644 net/vmw_vsock/Makefile create mode 100644 net/vmw_vsock/af_vsock.c create mode 100644 net/vmw_vsock/af_vsock.h create mode 100644 net/vmw_vsock/vmci_transport.c create mode 100644 net/vmw_vsock/vmci_transport.h create mode 100644 net/vmw_vsock/vmci_transport_notify.c create mode 100644 net/vmw_vsock/vmci_transport_notify.h create mode 100644 net/vmw_vsock/vmci_transport_notify_qstate.c create mode 100644 net/vmw_vsock/vsock_addr.c create mode 100644 net/vmw_vsock/vsock_addr.h create mode 100644 net/vmw_vsock/vsock_version.h (limited to 'net') diff --git a/include/linux/socket.h b/include/linux/socket.h index 9a546ff853dc..2b9f74b0ffea 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -178,7 +178,8 @@ struct ucred { #define AF_CAIF 37 /* CAIF sockets */ #define AF_ALG 38 /* Algorithm sockets */ #define AF_NFC 39 /* NFC sockets */ -#define AF_MAX 40 /* For now.. */ +#define AF_VSOCK 40 /* vSockets */ +#define AF_MAX 41 /* For now.. */ /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -221,6 +222,7 @@ struct ucred { #define PF_CAIF AF_CAIF #define PF_ALG AF_ALG #define PF_NFC AF_NFC +#define PF_VSOCK AF_VSOCK #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ diff --git a/include/uapi/linux/vm_sockets.h b/include/uapi/linux/vm_sockets.h new file mode 100644 index 000000000000..f7f2e99dec84 --- /dev/null +++ b/include/uapi/linux/vm_sockets.h @@ -0,0 +1,171 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 _VM_SOCKETS_H_ +#define _VM_SOCKETS_H_ + +#if !defined(__KERNEL__) +#include +#endif + +/* Option name for STREAM socket buffer size. Use as the option name in + * setsockopt(3) or getsockopt(3) to set or get an unsigned long long that + * specifies the size of the buffer underlying a vSockets STREAM socket. + * Value is clamped to the MIN and MAX. + */ + +#define SO_VM_SOCKETS_BUFFER_SIZE 0 + +/* Option name for STREAM socket minimum buffer size. Use as the option name + * in setsockopt(3) or getsockopt(3) to set or get an unsigned long long that + * specifies the minimum size allowed for the buffer underlying a vSockets + * STREAM socket. + */ + +#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1 + +/* Option name for STREAM socket maximum buffer size. Use as the option name + * in setsockopt(3) or getsockopt(3) to set or get an unsigned long long + * that specifies the maximum size allowed for the buffer underlying a + * vSockets STREAM socket. + */ + +#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2 + +/* Option name for socket peer's host-specific VM ID. Use as the option name + * in getsockopt(3) to get a host-specific identifier for the peer endpoint's + * VM. The identifier is a signed integer. + * Only available for hypervisor endpoints. + */ + +#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3 + +/* Option name for socket's service label. Use as the option name in + * setsockopt(3) or getsockopt(3) to set or get the service label for a socket. + * The service label is a C-style NUL-terminated string. Only available for + * hypervisor endpoints. + */ + +#define SO_VM_SOCKETS_SERVICE_LABEL 4 + +/* Option name for determining if a socket is trusted. Use as the option name + * in getsockopt(3) to determine if a socket is trusted. The value is a + * signed integer. + */ + +#define SO_VM_SOCKETS_TRUSTED 5 + +/* Option name for STREAM socket connection timeout. Use as the option name + * in setsockopt(3) or getsockopt(3) to set or get the connection + * timeout for a STREAM socket. + */ + +#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6 + +/* Option name for using non-blocking send/receive. Use as the option name + * for setsockopt(3) or getsockopt(3) to set or get the non-blocking + * transmit/receive flag for a STREAM socket. This flag determines whether + * send() and recv() can be called in non-blocking contexts for the given + * socket. The value is a signed integer. + * + * This option is only relevant to kernel endpoints, where descheduling the + * thread of execution is not allowed, for example, while holding a spinlock. + * It is not to be confused with conventional non-blocking socket operations. + * + * Only available for hypervisor endpoints. + */ + +#define SO_VM_SOCKETS_NONBLOCK_TXRX 7 + +/* The vSocket equivalent of INADDR_ANY. This works for the svm_cid field of + * sockaddr_vm and indicates the context ID of the current endpoint. + */ + +#define VMADDR_CID_ANY -1U + +/* Bind to any available port. Works for the svm_port field of + * sockaddr_vm. + */ + +#define VMADDR_PORT_ANY -1U + +/* Use this as the destination CID in an address when referring to the + * hypervisor. VMCI relies on it being 0, but this would be useful for other + * transports too. + */ + +#define VMADDR_CID_HYPERVISOR 0 + +/* This CID is specific to VMCI and can be considered reserved (even VMCI + * doesn't use it anymore, it's a legacy value from an older release). + */ + +#define VMADDR_CID_RESERVED 1 + +/* Use this as the destination CID in an address when referring to the host + * (any process other than the hypervisor). VMCI relies on it being 2, but + * this would be useful for other transports too. + */ + +#define VMADDR_CID_HOST 2 + +/* Invalid vSockets version. */ + +#define VM_SOCKETS_INVALID_VERSION -1U + +/* The epoch (first) component of the vSockets version. A single byte + * representing the epoch component of the vSockets version. + */ + +#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24) + +/* The major (second) component of the vSockets version. A single byte + * representing the major component of the vSockets version. Typically + * changes for every major release of a product. + */ + +#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16) + +/* The minor (third) component of the vSockets version. Two bytes representing + * the minor component of the vSockets version. + */ + +#define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF)) + +/* Address structure for vSockets. The address family should be set to + * whatever vmci_sock_get_af_value_fd() returns. The structure members should + * all align on their natural boundaries without resorting to compiler packing + * directives. The total size of this structure should be exactly the same as + * that of struct sockaddr. + */ + +struct sockaddr_vm { + sa_family_t svm_family; + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + unsigned char svm_zero[sizeof(struct sockaddr) - + sizeof(sa_family_t) - + sizeof(unsigned short) - + sizeof(unsigned int) - sizeof(unsigned int)]; +}; + +#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9) + +#if defined(__KERNEL__) +int vm_sockets_get_local_cid(void); +#endif + +#endif diff --git a/net/Kconfig b/net/Kconfig index c31348e70aad..5a1888bb036d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -217,6 +217,7 @@ source "net/dcb/Kconfig" source "net/dns_resolver/Kconfig" source "net/batman-adv/Kconfig" source "net/openvswitch/Kconfig" +source "net/vmw_vsock/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index c5aa8b3b49dc..091e7b04f301 100644 --- a/net/Makefile +++ b/net/Makefile @@ -69,3 +69,4 @@ obj-$(CONFIG_CEPH_LIB) += ceph/ obj-$(CONFIG_BATMAN_ADV) += batman-adv/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ +obj-$(CONFIG_VSOCKETS) += vmw_vsock/ diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig new file mode 100644 index 000000000000..b5fa7e40cdcb --- /dev/null +++ b/net/vmw_vsock/Kconfig @@ -0,0 +1,28 @@ +# +# Vsock protocol +# + +config VSOCKETS + tristate "Virtual Socket protocol" + help + Virtual Socket Protocol is a socket protocol similar to TCP/IP + allowing comunication between Virtual Machines and hypervisor + or host. + + You should also select one or more hypervisor-specific transports + below. + + To compile this driver as a module, choose M here: the module + will be called vsock. If unsure, say N. + +config VMWARE_VMCI_VSOCKETS + tristate "VMware VMCI transport for Virtual Sockets" + depends on VSOCKETS && VMWARE_VMCI + help + This module implements a VMCI transport for Virtual Sockets. + + Enable this transport if your Virtual Machine runs on a VMware + hypervisor. + + To compile this driver as a module, choose M here: the module + will be called vmw_vsock_vmci_transport. If unsure, say N. diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile new file mode 100644 index 000000000000..2ce52d70f224 --- /dev/null +++ b/net/vmw_vsock/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_VSOCKETS) += vsock.o +obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o + +vsock-y += af_vsock.o vsock_addr.o + +vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \ + vmci_transport_notify_qstate.o diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c new file mode 100644 index 000000000000..54bb7bdf92d3 --- /dev/null +++ b/net/vmw_vsock/af_vsock.c @@ -0,0 +1,2015 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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. + */ + +/* Implementation notes: + * + * - There are two kinds of sockets: those created by user action (such as + * calling socket(2)) and those created by incoming connection request packets. + * + * - There are two "global" tables, one for bound sockets (sockets that have + * specified an address that they are responsible for) and one for connected + * sockets (sockets that have established a connection with another socket). + * These tables are "global" in that all sockets on the system are placed + * within them. - Note, though, that the bound table contains an extra entry + * for a list of unbound sockets and SOCK_DGRAM sockets will always remain in + * that list. The bound table is used solely for lookup of sockets when packets + * are received and that's not necessary for SOCK_DGRAM sockets since we create + * a datagram handle for each and need not perform a lookup. Keeping SOCK_DGRAM + * sockets out of the bound hash buckets will reduce the chance of collisions + * when looking for SOCK_STREAM sockets and prevents us from having to check the + * socket type in the hash table lookups. + * + * - Sockets created by user action will either be "client" sockets that + * initiate a connection or "server" sockets that listen for connections; we do + * not support simultaneous connects (two "client" sockets connecting). + * + * - "Server" sockets are referred to as listener sockets throughout this + * implementation because they are in the SS_LISTEN state. When a connection + * request is received (the second kind of socket mentioned above), we create a + * new socket and refer to it as a pending socket. These pending sockets are + * placed on the pending connection list of the listener socket. When future + * packets are received for the address the listener socket is bound to, we + * check if the source of the packet is from one that has an existing pending + * connection. If it does, we process the packet for the pending socket. When + * that socket reaches the connected state, it is removed from the listener + * socket's pending list and enqueued in the listener socket's accept queue. + * Callers of accept(2) will accept connected sockets from the listener socket's + * accept queue. If the socket cannot be accepted for some reason then it is + * marked rejected. Once the connection is accepted, it is owned by the user + * process and the responsibility for cleanup falls with that user process. + * + * - It is possible that these pending sockets will never reach the connected + * state; in fact, we may never receive another packet after the connection + * request. Because of this, we must schedule a cleanup function to run in the + * future, after some amount of time passes where a connection should have been + * established. This function ensures that the socket is off all lists so it + * cannot be retrieved, then drops all references to the socket so it is cleaned + * up (sock_put() -> sk_free() -> our sk_destruct implementation). Note this + * function will also cleanup rejected sockets, those that reach the connected + * state but leave it before they have been accepted. + * + * - Sockets created by user action will be cleaned up when the user process + * calls close(2), causing our release implementation to be called. Our release + * implementation will perform some cleanup then drop the last reference so our + * sk_destruct implementation is invoked. Our sk_destruct implementation will + * perform additional cleanup that's common for both types of sockets. + * + * - A socket's reference count is what ensures that the structure won't be + * freed. Each entry in a list (such as the "global" bound and connected tables + * and the listener socket's pending list and connected queue) ensures a + * reference. When we defer work until process context and pass a socket as our + * argument, we must ensure the reference count is increased to ensure the + * socket isn't freed before the function is run; the deferred function will + * then drop the reference. + */ + +#include + +#define EXPORT_SYMTAB +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_vsock.h" +#include "vsock_version.h" + +static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr); +static void vsock_sk_destruct(struct sock *sk); +static int vsock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); + +/* Protocol family. */ +static struct proto vsock_proto = { + .name = "AF_VSOCK", + .owner = THIS_MODULE, + .obj_size = sizeof(struct vsock_sock), +}; + +/* The default peer timeout indicates how long we will wait for a peer response + * to a control message. + */ +#define VSOCK_DEFAULT_CONNECT_TIMEOUT (2 * HZ) + +#define SS_LISTEN 255 + +static const struct vsock_transport *transport; +static DEFINE_MUTEX(vsock_register_mutex); + +/**** EXPORTS ****/ + +/* Get the ID of the local context. This is transport dependent. */ + +int vm_sockets_get_local_cid(void) +{ + return transport->get_local_cid(); +} +EXPORT_SYMBOL_GPL(vm_sockets_get_local_cid); + +/**** UTILS ****/ + +/* Each bound VSocket is stored in the bind hash table and each connected + * VSocket is stored in the connected hash table. + * + * Unbound sockets are all put on the same list attached to the end of the hash + * table (vsock_unbound_sockets). Bound sockets are added to the hash table in + * the bucket that their local address hashes to (vsock_bound_sockets(addr) + * represents the list that addr hashes to). + * + * Specifically, we initialize the vsock_bind_table array to a size of + * VSOCK_HASH_SIZE + 1 so that vsock_bind_table[0] through + * vsock_bind_table[VSOCK_HASH_SIZE - 1] are for bound sockets and + * vsock_bind_table[VSOCK_HASH_SIZE] is for unbound sockets. The hash function + * mods with VSOCK_HASH_SIZE - 1 to ensure this. + */ +#define VSOCK_HASH_SIZE 251 +#define MAX_PORT_RETRIES 24 + +#define VSOCK_HASH(addr) ((addr)->svm_port % (VSOCK_HASH_SIZE - 1)) +#define vsock_bound_sockets(addr) (&vsock_bind_table[VSOCK_HASH(addr)]) +#define vsock_unbound_sockets (&vsock_bind_table[VSOCK_HASH_SIZE]) + +/* XXX This can probably be implemented in a better way. */ +#define VSOCK_CONN_HASH(src, dst) \ + (((src)->svm_cid ^ (dst)->svm_port) % (VSOCK_HASH_SIZE - 1)) +#define vsock_connected_sockets(src, dst) \ + (&vsock_connected_table[VSOCK_CONN_HASH(src, dst)]) +#define vsock_connected_sockets_vsk(vsk) \ + vsock_connected_sockets(&(vsk)->remote_addr, &(vsk)->local_addr) + +static struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1]; +static struct list_head vsock_connected_table[VSOCK_HASH_SIZE]; +static DEFINE_SPINLOCK(vsock_table_lock); + +static __init void vsock_init_tables(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vsock_bind_table); i++) + INIT_LIST_HEAD(&vsock_bind_table[i]); + + for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++) + INIT_LIST_HEAD(&vsock_connected_table[i]); +} + +static void __vsock_insert_bound(struct list_head *list, + struct vsock_sock *vsk) +{ + sock_hold(&vsk->sk); + list_add(&vsk->bound_table, list); +} + +static void __vsock_insert_connected(struct list_head *list, + struct vsock_sock *vsk) +{ + sock_hold(&vsk->sk); + list_add(&vsk->connected_table, list); +} + +static void __vsock_remove_bound(struct vsock_sock *vsk) +{ + list_del_init(&vsk->bound_table); + sock_put(&vsk->sk); +} + +static void __vsock_remove_connected(struct vsock_sock *vsk) +{ + list_del_init(&vsk->connected_table); + sock_put(&vsk->sk); +} + +static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr) +{ + struct vsock_sock *vsk; + + list_for_each_entry(vsk, vsock_bound_sockets(addr), bound_table) + if (vsock_addr_equals_addr_any(addr, &vsk->local_addr)) + return sk_vsock(vsk); + + return NULL; +} + +static struct sock *__vsock_find_connected_socket(struct sockaddr_vm *src, + struct sockaddr_vm *dst) +{ + struct vsock_sock *vsk; + + list_for_each_entry(vsk, vsock_connected_sockets(src, dst), + connected_table) { + if (vsock_addr_equals_addr(src, &vsk->remote_addr) + && vsock_addr_equals_addr(dst, &vsk->local_addr)) { + return sk_vsock(vsk); + } + } + + return NULL; +} + +static bool __vsock_in_bound_table(struct vsock_sock *vsk) +{ + return !list_empty(&vsk->bound_table); +} + +static bool __vsock_in_connected_table(struct vsock_sock *vsk) +{ + return !list_empty(&vsk->connected_table); +} + +static void vsock_insert_unbound(struct vsock_sock *vsk) +{ + spin_lock_bh(&vsock_table_lock); + __vsock_insert_bound(vsock_unbound_sockets, vsk); + spin_unlock_bh(&vsock_table_lock); +} + +void vsock_insert_connected(struct vsock_sock *vsk) +{ + struct list_head *list = vsock_connected_sockets( + &vsk->remote_addr, &vsk->local_addr); + + spin_lock_bh(&vsock_table_lock); + __vsock_insert_connected(list, vsk); + spin_unlock_bh(&vsock_table_lock); +} +EXPORT_SYMBOL_GPL(vsock_insert_connected); + +void vsock_remove_bound(struct vsock_sock *vsk) +{ + spin_lock_bh(&vsock_table_lock); + __vsock_remove_bound(vsk); + spin_unlock_bh(&vsock_table_lock); +} +EXPORT_SYMBOL_GPL(vsock_remove_bound); + +void vsock_remove_connected(struct vsock_sock *vsk) +{ + spin_lock_bh(&vsock_table_lock); + __vsock_remove_connected(vsk); + spin_unlock_bh(&vsock_table_lock); +} +EXPORT_SYMBOL_GPL(vsock_remove_connected); + +struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr) +{ + struct sock *sk; + + spin_lock_bh(&vsock_table_lock); + sk = __vsock_find_bound_socket(addr); + if (sk) + sock_hold(sk); + + spin_unlock_bh(&vsock_table_lock); + + return sk; +} +EXPORT_SYMBOL_GPL(vsock_find_bound_socket); + +struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, + struct sockaddr_vm *dst) +{ + struct sock *sk; + + spin_lock_bh(&vsock_table_lock); + sk = __vsock_find_connected_socket(src, dst); + if (sk) + sock_hold(sk); + + spin_unlock_bh(&vsock_table_lock); + + return sk; +} +EXPORT_SYMBOL_GPL(vsock_find_connected_socket); + +static bool vsock_in_bound_table(struct vsock_sock *vsk) +{ + bool ret; + + spin_lock_bh(&vsock_table_lock); + ret = __vsock_in_bound_table(vsk); + spin_unlock_bh(&vsock_table_lock); + + return ret; +} + +static bool vsock_in_connected_table(struct vsock_sock *vsk) +{ + bool ret; + + spin_lock_bh(&vsock_table_lock); + ret = __vsock_in_connected_table(vsk); + spin_unlock_bh(&vsock_table_lock); + + return ret; +} + +void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)) +{ + int i; + + spin_lock_bh(&vsock_table_lock); + + for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++) { + struct vsock_sock *vsk; + list_for_each_entry(vsk, &vsock_connected_table[i], + connected_table); + fn(sk_vsock(vsk)); + } + + spin_unlock_bh(&vsock_table_lock); +} +EXPORT_SYMBOL_GPL(vsock_for_each_connected_socket); + +void vsock_add_pending(struct sock *listener, struct sock *pending) +{ + struct vsock_sock *vlistener; + struct vsock_sock *vpending; + + vlistener = vsock_sk(listener); + vpending = vsock_sk(pending); + + sock_hold(pending); + sock_hold(listener); + list_add_tail(&vpending->pending_links, &vlistener->pending_links); +} +EXPORT_SYMBOL_GPL(vsock_add_pending); + +void vsock_remove_pending(struct sock *listener, struct sock *pending) +{ + struct vsock_sock *vpending = vsock_sk(pending); + + list_del_init(&vpending->pending_links); + sock_put(listener); + sock_put(pending); +} +EXPORT_SYMBOL_GPL(vsock_remove_pending); + +void vsock_enqueue_accept(struct sock *listener, struct sock *connected) +{ + struct vsock_sock *vlistener; + struct vsock_sock *vconnected; + + vlistener = vsock_sk(listener); + vconnected = vsock_sk(connected); + + sock_hold(connected); + sock_hold(listener); + list_add_tail(&vconnected->accept_queue, &vlistener->accept_queue); +} +EXPORT_SYMBOL_GPL(vsock_enqueue_accept); + +static struct sock *vsock_dequeue_accept(struct sock *listener) +{ + struct vsock_sock *vlistener; + struct vsock_sock *vconnected; + + vlistener = vsock_sk(listener); + + if (list_empty(&vlistener->accept_queue)) + return NULL; + + vconnected = list_entry(vlistener->accept_queue.next, + struct vsock_sock, accept_queue); + + list_del_init(&vconnected->accept_queue); + sock_put(listener); + /* The caller will need a reference on the connected socket so we let + * it call sock_put(). + */ + + return sk_vsock(vconnected); +} + +static bool vsock_is_accept_queue_empty(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + return list_empty(&vsk->accept_queue); +} + +static bool vsock_is_pending(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + return !list_empty(&vsk->pending_links); +} + +static int vsock_send_shutdown(struct sock *sk, int mode) +{ + return transport->shutdown(vsock_sk(sk), mode); +} + +void vsock_pending_work(struct work_struct *work) +{ + struct sock *sk; + struct sock *listener; + struct vsock_sock *vsk; + bool cleanup; + + vsk = container_of(work, struct vsock_sock, dwork.work); + sk = sk_vsock(vsk); + listener = vsk->listener; + cleanup = true; + + lock_sock(listener); + lock_sock(sk); + + if (vsock_is_pending(sk)) { + vsock_remove_pending(listener, sk); + } else if (!vsk->rejected) { + /* We are not on the pending list and accept() did not reject + * us, so we must have been accepted by our user process. We + * just need to drop our references to the sockets and be on + * our way. + */ + cleanup = false; + goto out; + } + + listener->sk_ack_backlog--; + + /* We need to remove ourself from the global connected sockets list so + * incoming packets can't find this socket, and to reduce the reference + * count. + */ + if (vsock_in_connected_table(vsk)) + vsock_remove_connected(vsk); + + sk->sk_state = SS_FREE; + +out: + release_sock(sk); + release_sock(listener); + if (cleanup) + sock_put(sk); + + sock_put(sk); + sock_put(listener); +} +EXPORT_SYMBOL_GPL(vsock_pending_work); + +/**** SOCKET OPERATIONS ****/ + +static int __vsock_bind_stream(struct vsock_sock *vsk, + struct sockaddr_vm *addr) +{ + static u32 port = LAST_RESERVED_PORT + 1; + struct sockaddr_vm new_addr; + + vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port); + + if (addr->svm_port == VMADDR_PORT_ANY) { + bool found = false; + unsigned int i; + + for (i = 0; i < MAX_PORT_RETRIES; i++) { + if (port <= LAST_RESERVED_PORT) + port = LAST_RESERVED_PORT + 1; + + new_addr.svm_port = port++; + + if (!__vsock_find_bound_socket(&new_addr)) { + found = true; + break; + } + } + + if (!found) + return -EADDRNOTAVAIL; + } else { + /* If port is in reserved range, ensure caller + * has necessary privileges. + */ + if (addr->svm_port <= LAST_RESERVED_PORT && + !capable(CAP_NET_BIND_SERVICE)) { + return -EACCES; + } + + if (__vsock_find_bound_socket(&new_addr)) + return -EADDRINUSE; + } + + vsock_addr_init(&vsk->local_addr, new_addr.svm_cid, new_addr.svm_port); + + /* Remove stream sockets from the unbound list and add them to the hash + * table for easy lookup by its address. The unbound list is simply an + * extra entry at the end of the hash table, a trick used by AF_UNIX. + */ + __vsock_remove_bound(vsk); + __vsock_insert_bound(vsock_bound_sockets(&vsk->local_addr), vsk); + + return 0; +} + +static int __vsock_bind_dgram(struct vsock_sock *vsk, + struct sockaddr_vm *addr) +{ + return transport->dgram_bind(vsk, addr); +} + +static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr) +{ + struct vsock_sock *vsk = vsock_sk(sk); + u32 cid; + int retval; + + /* First ensure this socket isn't already bound. */ + if (vsock_addr_bound(&vsk->local_addr)) + return -EINVAL; + + /* Now bind to the provided address or select appropriate values if + * none are provided (VMADDR_CID_ANY and VMADDR_PORT_ANY). Note that + * like AF_INET prevents binding to a non-local IP address (in most + * cases), we only allow binding to the local CID. + */ + cid = transport->get_local_cid(); + if (addr->svm_cid != cid && addr->svm_cid != VMADDR_CID_ANY) + return -EADDRNOTAVAIL; + + switch (sk->sk_socket->type) { + case SOCK_STREAM: + spin_lock_bh(&vsock_table_lock); + retval = __vsock_bind_stream(vsk, addr); + spin_unlock_bh(&vsock_table_lock); + break; + + case SOCK_DGRAM: + retval = __vsock_bind_dgram(vsk, addr); + break; + + default: + retval = -EINVAL; + break; + } + + return retval; +} + +struct sock *__vsock_create(struct net *net, + struct socket *sock, + struct sock *parent, + gfp_t priority, + unsigned short type) +{ + struct sock *sk; + struct vsock_sock *psk; + struct vsock_sock *vsk; + + sk = sk_alloc(net, AF_VSOCK, priority, &vsock_proto); + if (!sk) + return NULL; + + sock_init_data(sock, sk); + + /* sk->sk_type is normally set in sock_init_data, but only if sock is + * non-NULL. We make sure that our sockets always have a type by + * setting it here if needed. + */ + if (!sock) + sk->sk_type = type; + + vsk = vsock_sk(sk); + vsock_addr_init(&vsk->local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + vsock_addr_init(&vsk->remote_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + + sk->sk_destruct = vsock_sk_destruct; + sk->sk_backlog_rcv = vsock_queue_rcv_skb; + sk->sk_state = 0; + sock_reset_flag(sk, SOCK_DONE); + + INIT_LIST_HEAD(&vsk->bound_table); + INIT_LIST_HEAD(&vsk->connected_table); + vsk->listener = NULL; + INIT_LIST_HEAD(&vsk->pending_links); + INIT_LIST_HEAD(&vsk->accept_queue); + vsk->rejected = false; + vsk->sent_request = false; + vsk->ignore_connecting_rst = false; + vsk->peer_shutdown = 0; + + psk = parent ? vsock_sk(parent) : NULL; + if (parent) { + vsk->trusted = psk->trusted; + vsk->owner = get_cred(psk->owner); + vsk->connect_timeout = psk->connect_timeout; + } else { + vsk->trusted = capable(CAP_NET_ADMIN); + vsk->owner = get_current_cred(); + vsk->connect_timeout = VSOCK_DEFAULT_CONNECT_TIMEOUT; + } + + if (transport->init(vsk, psk) < 0) { + sk_free(sk); + return NULL; + } + + if (sock) + vsock_insert_unbound(vsk); + + return sk; +} +EXPORT_SYMBOL_GPL(__vsock_create); + +static void __vsock_release(struct sock *sk) +{ + if (sk) { + struct sk_buff *skb; + struct sock *pending; + struct vsock_sock *vsk; + + vsk = vsock_sk(sk); + pending = NULL; /* Compiler warning. */ + + if (vsock_in_bound_table(vsk)) + vsock_remove_bound(vsk); + + if (vsock_in_connected_table(vsk)) + vsock_remove_connected(vsk); + + transport->release(vsk); + + lock_sock(sk); + sock_orphan(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) + kfree_skb(skb); + + /* Clean up any sockets that never were accepted. */ + while ((pending = vsock_dequeue_accept(sk)) != NULL) { + __vsock_release(pending); + sock_put(pending); + } + + release_sock(sk); + sock_put(sk); + } +} + +static void vsock_sk_destruct(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + transport->destruct(vsk); + + /* When clearing these addresses, there's no need to set the family and + * possibly register the address family with the kernel. + */ + vsock_addr_init(&vsk->local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + vsock_addr_init(&vsk->remote_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + + put_cred(vsk->owner); +} + +static int vsock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err; + + err = sock_queue_rcv_skb(sk, skb); + if (err) + kfree_skb(skb); + + return err; +} + +s64 vsock_stream_has_data(struct vsock_sock *vsk) +{ + return transport->stream_has_data(vsk); +} +EXPORT_SYMBOL_GPL(vsock_stream_has_data); + +s64 vsock_stream_has_space(struct vsock_sock *vsk) +{ + return transport->stream_has_space(vsk); +} +EXPORT_SYMBOL_GPL(vsock_stream_has_space); + +static int vsock_release(struct socket *sock) +{ + __vsock_release(sock->sk); + sock->sk = NULL; + sock->state = SS_FREE; + + return 0; +} + +static int +vsock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + int err; + struct sock *sk; + struct sockaddr_vm *vm_addr; + + sk = sock->sk; + + if (vsock_addr_cast(addr, addr_len, &vm_addr) != 0) + return -EINVAL; + + lock_sock(sk); + err = __vsock_bind(sk, vm_addr); + release_sock(sk); + + return err; +} + +static int vsock_getname(struct socket *sock, + struct sockaddr *addr, int *addr_len, int peer) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + struct sockaddr_vm *vm_addr; + + sk = sock->sk; + vsk = vsock_sk(sk); + err = 0; + + lock_sock(sk); + + if (peer) { + if (sock->state != SS_CONNECTED) { + err = -ENOTCONN; + goto out; + } + vm_addr = &vsk->remote_addr; + } else { + vm_addr = &vsk->local_addr; + } + + if (!vm_addr) { + err = -EINVAL; + goto out; + } + + /* sys_getsockname() and sys_getpeername() pass us a + * MAX_SOCK_ADDR-sized buffer and don't set addr_len. Unfortunately + * that macro is defined in socket.c instead of .h, so we hardcode its + * value here. + */ + BUILD_BUG_ON(sizeof(*vm_addr) > 128); + memcpy(addr, vm_addr, sizeof(*vm_addr)); + *addr_len = sizeof(*vm_addr); + +out: + release_sock(sk); + return err; +} + +static int vsock_shutdown(struct socket *sock, int mode) +{ + int err; + struct sock *sk; + + /* User level uses SHUT_RD (0) and SHUT_WR (1), but the kernel uses + * RCV_SHUTDOWN (1) and SEND_SHUTDOWN (2), so we must increment mode + * here like the other address families do. Note also that the + * increment makes SHUT_RDWR (2) into RCV_SHUTDOWN | SEND_SHUTDOWN (3), + * which is what we want. + */ + mode++; + + if ((mode & ~SHUTDOWN_MASK) || !mode) + return -EINVAL; + + /* If this is a STREAM socket and it is not connected then bail out + * immediately. If it is a DGRAM socket then we must first kick the + * socket so that it wakes up from any sleeping calls, for example + * recv(), and then afterwards return the error. + */ + + sk = sock->sk; + if (sock->state == SS_UNCONNECTED) { + err = -ENOTCONN; + if (sk->sk_type == SOCK_STREAM) + return err; + } else { + sock->state = SS_DISCONNECTING; + err = 0; + } + + /* Receive and send shutdowns are treated alike. */ + mode = mode & (RCV_SHUTDOWN | SEND_SHUTDOWN); + if (mode) { + lock_sock(sk); + sk->sk_shutdown |= mode; + sk->sk_state_change(sk); + release_sock(sk); + + if (sk->sk_type == SOCK_STREAM) { + sock_reset_flag(sk, SOCK_DONE); + vsock_send_shutdown(sk, mode); + } + } + + return err; +} + +static unsigned int vsock_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk; + unsigned int mask; + struct vsock_sock *vsk; + + sk = sock->sk; + vsk = vsock_sk(sk); + + poll_wait(file, sk_sleep(sk), wait); + mask = 0; + + if (sk->sk_err) + /* Signify that there has been an error on this socket. */ + mask |= POLLERR; + + /* INET sockets treat local write shutdown and peer write shutdown as a + * case of POLLHUP set. + */ + if ((sk->sk_shutdown == SHUTDOWN_MASK) || + ((sk->sk_shutdown & SEND_SHUTDOWN) && + (vsk->peer_shutdown & SEND_SHUTDOWN))) { + mask |= POLLHUP; + } + + if (sk->sk_shutdown & RCV_SHUTDOWN || + vsk->peer_shutdown & SEND_SHUTDOWN) { + mask |= POLLRDHUP; + } + + if (sock->type == SOCK_DGRAM) { + /* For datagram sockets we can read if there is something in + * the queue and write as long as the socket isn't shutdown for + * sending. + */ + if (!skb_queue_empty(&sk->sk_receive_queue) || + (sk->sk_shutdown & RCV_SHUTDOWN)) { + mask |= POLLIN | POLLRDNORM; + } + + if (!(sk->sk_shutdown & SEND_SHUTDOWN)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + + } else if (sock->type == SOCK_STREAM) { + lock_sock(sk); + + /* Listening sockets that have connections in their accept + * queue can be read. + */ + if (sk->sk_state == SS_LISTEN + && !vsock_is_accept_queue_empty(sk)) + mask |= POLLIN | POLLRDNORM; + + /* If there is something in the queue then we can read. */ + if (transport->stream_is_active(vsk) && + !(sk->sk_shutdown & RCV_SHUTDOWN)) { + bool data_ready_now = false; + int ret = transport->notify_poll_in( + vsk, 1, &data_ready_now); + if (ret < 0) { + mask |= POLLERR; + } else { + if (data_ready_now) + mask |= POLLIN | POLLRDNORM; + + } + } + + /* Sockets whose connections have been closed, reset, or + * terminated should also be considered read, and we check the + * shutdown flag for that. + */ + if (sk->sk_shutdown & RCV_SHUTDOWN || + vsk->peer_shutdown & SEND_SHUTDOWN) { + mask |= POLLIN | POLLRDNORM; + } + + /* Connected sockets that can produce data can be written. */ + if (sk->sk_state == SS_CONNECTED) { + if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { + bool space_avail_now = false; + int ret = transport->notify_poll_out( + vsk, 1, &space_avail_now); + if (ret < 0) { + mask |= POLLERR; + } else { + if (space_avail_now) + /* Remove POLLWRBAND since INET + * sockets are not setting it. + */ + mask |= POLLOUT | POLLWRNORM; + + } + } + } + + /* Simulate INET socket poll behaviors, which sets + * POLLOUT|POLLWRNORM when peer is closed and nothing to read, + * but local send is not shutdown. + */ + if (sk->sk_state == SS_UNCONNECTED) { + if (!(sk->sk_shutdown & SEND_SHUTDOWN)) + mask |= POLLOUT | POLLWRNORM; + + } + + release_sock(sk); + } + + return mask; +} + +static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + struct sockaddr_vm *remote_addr; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + /* For now, MSG_DONTWAIT is always assumed... */ + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); + + lock_sock(sk); + + if (!vsock_addr_bound(&vsk->local_addr)) { + struct sockaddr_vm local_addr; + + vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + err = __vsock_bind(sk, &local_addr); + if (err != 0) + goto out; + + } + + /* If the provided message contains an address, use that. Otherwise + * fall back on the socket's remote handle (if it has been connected). + */ + if (msg->msg_name && + vsock_addr_cast(msg->msg_name, msg->msg_namelen, + &remote_addr) == 0) { + /* Ensure this address is of the right type and is a valid + * destination. + */ + + if (remote_addr->svm_cid == VMADDR_CID_ANY) + remote_addr->svm_cid = transport->get_local_cid(); + + if (!vsock_addr_bound(remote_addr)) { + err = -EINVAL; + goto out; + } + } else if (sock->state == SS_CONNECTED) { + remote_addr = &vsk->remote_addr; + + if (remote_addr->svm_cid == VMADDR_CID_ANY) + remote_addr->svm_cid = transport->get_local_cid(); + + /* XXX Should connect() or this function ensure remote_addr is + * bound? + */ + if (!vsock_addr_bound(&vsk->remote_addr)) { + err = -EINVAL; + goto out; + } + } else { + err = -EINVAL; + goto out; + } + + if (!transport->dgram_allow(remote_addr->svm_cid, + remote_addr->svm_port)) { + err = -EINVAL; + goto out; + } + + err = transport->dgram_enqueue(vsk, remote_addr, msg->msg_iov, len); + +out: + release_sock(sk); + return err; +} + +static int vsock_dgram_connect(struct socket *sock, + struct sockaddr *addr, int addr_len, int flags) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + struct sockaddr_vm *remote_addr; + + sk = sock->sk; + vsk = vsock_sk(sk); + + err = vsock_addr_cast(addr, addr_len, &remote_addr); + if (err == -EAFNOSUPPORT && remote_addr->svm_family == AF_UNSPEC) { + lock_sock(sk); + vsock_addr_init(&vsk->remote_addr, VMADDR_CID_ANY, + VMADDR_PORT_ANY); + sock->state = SS_UNCONNECTED; + release_sock(sk); + return 0; + } else if (err != 0) + return -EINVAL; + + lock_sock(sk); + + if (!vsock_addr_bound(&vsk->local_addr)) { + struct sockaddr_vm local_addr; + + vsock_addr_init(&local_addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); + err = __vsock_bind(sk, &local_addr); + if (err != 0) + goto out; + + } + + if (!transport->dgram_allow(remote_addr->svm_cid, + remote_addr->svm_port)) { + err = -EINVAL; + goto out; + } + + memcpy(&vsk->remote_addr, remote_addr, sizeof(vsk->remote_addr)); + sock->state = SS_CONNECTED; + +out: + release_sock(sk); + return err; +} + +static int vsock_dgram_recvmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + return transport->dgram_dequeue(kiocb, vsock_sk(sock->sk), msg, len, + flags); +} + +static const struct proto_ops vsock_dgram_ops = { + .family = PF_VSOCK, + .owner = THIS_MODULE, + .release = vsock_release, + .bind = vsock_bind, + .connect = vsock_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = vsock_getname, + .poll = vsock_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = vsock_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = vsock_dgram_sendmsg, + .recvmsg = vsock_dgram_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static void vsock_connect_timeout(struct work_struct *work) +{ + struct sock *sk; + struct vsock_sock *vsk; + + vsk = container_of(work, struct vsock_sock, dwork.work); + sk = sk_vsock(vsk); + + lock_sock(sk); + if (sk->sk_state == SS_CONNECTING && + (sk->sk_shutdown != SHUTDOWN_MASK)) { + sk->sk_state = SS_UNCONNECTED; + sk->sk_err = ETIMEDOUT; + sk->sk_error_report(sk); + } + release_sock(sk); + + sock_put(sk); +} + +static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, + int addr_len, int flags) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + struct sockaddr_vm *remote_addr; + long timeout; + DEFINE_WAIT(wait); + + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); + + lock_sock(sk); + + /* XXX AF_UNSPEC should make us disconnect like AF_INET. */ + switch (sock->state) { + case SS_CONNECTED: + err = -EISCONN; + goto out; + case SS_DISCONNECTING: + err = -EINVAL; + goto out; + case SS_CONNECTING: + /* This continues on so we can move sock into the SS_CONNECTED + * state once the connection has completed (at which point err + * will be set to zero also). Otherwise, we will either wait + * for the connection or return -EALREADY should this be a + * non-blocking call. + */ + err = -EALREADY; + break; + default: + if ((sk->sk_state == SS_LISTEN) || + vsock_addr_cast(addr, addr_len, &remote_addr) != 0) { + err = -EINVAL; + goto out; + } + + /* The hypervisor and well-known contexts do not have socket + * endpoints. + */ + if (!transport->stream_allow(remote_addr->svm_cid, + remote_addr->svm_port)) { + err = -ENETUNREACH; + goto out; + } + + /* Set the remote address that we are connecting to. */ + memcpy(&vsk->remote_addr, remote_addr, + sizeof(vsk->remote_addr)); + + /* Autobind this socket to the local address if necessary. */ + if (!vsock_addr_bound(&vsk->local_addr)) { + struct sockaddr_vm local_addr; + + vsock_addr_init(&local_addr, VMADDR_CID_ANY, + VMADDR_PORT_ANY); + err = __vsock_bind(sk, &local_addr); + if (err != 0) + goto out; + + } + + sk->sk_state = SS_CONNECTING; + + err = transport->connect(vsk); + if (err < 0) + goto out; + + /* Mark sock as connecting and set the error code to in + * progress in case this is a non-blocking connect. + */ + sock->state = SS_CONNECTING; + err = -EINPROGRESS; + } + + /* The receive path will handle all communication until we are able to + * enter the connected state. Here we wait for the connection to be + * completed or a notification of an error. + */ + timeout = vsk->connect_timeout; + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + while (sk->sk_state != SS_CONNECTED && sk->sk_err == 0) { + if (flags & O_NONBLOCK) { + /* If we're not going to block, we schedule a timeout + * function to generate a timeout on the connection + * attempt, in case the peer doesn't respond in a + * timely manner. We hold on to the socket until the + * timeout fires. + */ + sock_hold(sk); + INIT_DELAYED_WORK(&vsk->dwork, + vsock_connect_timeout); + schedule_delayed_work(&vsk->dwork, timeout); + + /* Skip ahead to preserve error code set above. */ + goto out_wait; + } + + release_sock(sk); + timeout = schedule_timeout(timeout); + lock_sock(sk); + + if (signal_pending(current)) { + err = sock_intr_errno(timeout); + goto out_wait_error; + } else if (timeout == 0) { + err = -ETIMEDOUT; + goto out_wait_error; + } + + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + } + + if (sk->sk_err) { + err = -sk->sk_err; + goto out_wait_error; + } else + err = 0; + +out_wait: + finish_wait(sk_sleep(sk), &wait); +out: + release_sock(sk); + return err; + +out_wait_error: + sk->sk_state = SS_UNCONNECTED; + sock->state = SS_UNCONNECTED; + goto out_wait; +} + +static int vsock_accept(struct socket *sock, struct socket *newsock, int flags) +{ + struct sock *listener; + int err; + struct sock *connected; + struct vsock_sock *vconnected; + long timeout; + DEFINE_WAIT(wait); + + err = 0; + listener = sock->sk; + + lock_sock(listener); + + if (sock->type != SOCK_STREAM) { + err = -EOPNOTSUPP; + goto out; + } + + if (listener->sk_state != SS_LISTEN) { + err = -EINVAL; + goto out; + } + + /* Wait for children sockets to appear; these are the new sockets + * created upon connection establishment. + */ + timeout = sock_sndtimeo(listener, flags & O_NONBLOCK); + prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE); + + while ((connected = vsock_dequeue_accept(listener)) == NULL && + listener->sk_err == 0) { + release_sock(listener); + timeout = schedule_timeout(timeout); + lock_sock(listener); + + if (signal_pending(current)) { + err = sock_intr_errno(timeout); + goto out_wait; + } else if (timeout == 0) { + err = -EAGAIN; + goto out_wait; + } + + prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE); + } + + if (listener->sk_err) + err = -listener->sk_err; + + if (connected) { + listener->sk_ack_backlog--; + + lock_sock(connected); + vconnected = vsock_sk(connected); + + /* If the listener socket has received an error, then we should + * reject this socket and return. Note that we simply mark the + * socket rejected, drop our reference, and let the cleanup + * function handle the cleanup; the fact that we found it in + * the listener's accept queue guarantees that the cleanup + * function hasn't run yet. + */ + if (err) { + vconnected->rejected = true; + release_sock(connected); + sock_put(connected); + goto out_wait; + } + + newsock->state = SS_CONNECTED; + sock_graft(connected, newsock); + release_sock(connected); + sock_put(connected); + } + +out_wait: + finish_wait(sk_sleep(listener), &wait); +out: + release_sock(listener); + return err; +} + +static int vsock_listen(struct socket *sock, int backlog) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + + sk = sock->sk; + + lock_sock(sk); + + if (sock->type != SOCK_STREAM) { + err = -EOPNOTSUPP; + goto out; + } + + if (sock->state != SS_UNCONNECTED) { + err = -EINVAL; + goto out; + } + + vsk = vsock_sk(sk); + + if (!vsock_addr_bound(&vsk->local_addr)) { + err = -EINVAL; + goto out; + } + + sk->sk_max_ack_backlog = backlog; + sk->sk_state = SS_LISTEN; + + err = 0; + +out: + release_sock(sk); + return err; +} + +static int vsock_stream_setsockopt(struct socket *sock, + int level, + int optname, + char __user *optval, + unsigned int optlen) +{ + int err; + struct sock *sk; + struct vsock_sock *vsk; + u64 val; + + if (level != AF_VSOCK) + return -ENOPROTOOPT; + +#define COPY_IN(_v) \ + do { \ + if (optlen < sizeof(_v)) { \ + err = -EINVAL; \ + goto exit; \ + } \ + if (copy_from_user(&_v, optval, sizeof(_v)) != 0) { \ + err = -EFAULT; \ + goto exit; \ + } \ + } while (0) + + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); + + lock_sock(sk); + + switch (optname) { + case SO_VM_SOCKETS_BUFFER_SIZE: + COPY_IN(val); + transport->set_buffer_size(vsk, val); + break; + + case SO_VM_SOCKETS_BUFFER_MAX_SIZE: + COPY_IN(val); + transport->set_max_buffer_size(vsk, val); + break; + + case SO_VM_SOCKETS_BUFFER_MIN_SIZE: + COPY_IN(val); + transport->set_min_buffer_size(vsk, val); + break; + + case SO_VM_SOCKETS_CONNECT_TIMEOUT: { + struct timeval tv; + COPY_IN(tv); + if (tv.tv_sec >= 0 && tv.tv_usec < USEC_PER_SEC && + tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1)) { + vsk->connect_timeout = tv.tv_sec * HZ + + DIV_ROUND_UP(tv.tv_usec, (1000000 / HZ)); + if (vsk->connect_timeout == 0) + vsk->connect_timeout = + VSOCK_DEFAULT_CONNECT_TIMEOUT; + + } else { + err = -ERANGE; + } + break; + } + + default: + err = -ENOPROTOOPT; + break; + } + +#undef COPY_IN + +exit: + release_sock(sk); + return err; +} + +static int vsock_stream_getsockopt(struct socket *sock, + int level, int optname, + char __user *optval, + int __user *optlen) +{ + int err; + int len; + struct sock *sk; + struct vsock_sock *vsk; + u64 val; + + if (level != AF_VSOCK) + return -ENOPROTOOPT; + + err = get_user(len, optlen); + if (err != 0) + return err; + +#define COPY_OUT(_v) \ + do { \ + if (len < sizeof(_v)) \ + return -EINVAL; \ + \ + len = sizeof(_v); \ + if (copy_to_user(optval, &_v, len) != 0) \ + return -EFAULT; \ + \ + } while (0) + + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); + + switch (optname) { + case SO_VM_SOCKETS_BUFFER_SIZE: + val = transport->get_buffer_size(vsk); + COPY_OUT(val); + break; + + case SO_VM_SOCKETS_BUFFER_MAX_SIZE: + val = transport->get_max_buffer_size(vsk); + COPY_OUT(val); + break; + + case SO_VM_SOCKETS_BUFFER_MIN_SIZE: + val = transport->get_min_buffer_size(vsk); + COPY_OUT(val); + break; + + case SO_VM_SOCKETS_CONNECT_TIMEOUT: { + struct timeval tv; + tv.tv_sec = vsk->connect_timeout / HZ; + tv.tv_usec = + (vsk->connect_timeout - + tv.tv_sec * HZ) * (1000000 / HZ); + COPY_OUT(tv); + break; + } + default: + return -ENOPROTOOPT; + } + + err = put_user(len, optlen); + if (err != 0) + return -EFAULT; + +#undef COPY_OUT + + return 0; +} + +static int vsock_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk; + struct vsock_sock *vsk; + ssize_t total_written; + long timeout; + int err; + struct vsock_transport_send_notify_data send_data; + + DEFINE_WAIT(wait); + + sk = sock->sk; + vsk = vsock_sk(sk); + total_written = 0; + err = 0; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + lock_sock(sk); + + /* Callers should not provide a destination with stream sockets. */ + if (msg->msg_namelen) { + err = sk->sk_state == SS_CONNECTED ? -EISCONN : -EOPNOTSUPP; + goto out; + } + + /* Send data only if both sides are not shutdown in the direction. */ + if (sk->sk_shutdown & SEND_SHUTDOWN || + vsk->peer_shutdown & RCV_SHUTDOWN) { + err = -EPIPE; + goto out; + } + + if (sk->sk_state != SS_CONNECTED || + !vsock_addr_bound(&vsk->local_addr)) { + err = -ENOTCONN; + goto out; + } + + if (!vsock_addr_bound(&vsk->remote_addr)) { + err = -EDESTADDRREQ; + goto out; + } + + /* Wait for room in the produce queue to enqueue our user's data. */ + timeout = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + + err = transport->notify_send_init(vsk, &send_data); + if (err < 0) + goto out; + + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + while (total_written < len) { + ssize_t written; + + while (vsock_stream_has_space(vsk) == 0 && + sk->sk_err == 0 && + !(sk->sk_shutdown & SEND_SHUTDOWN) && + !(vsk->peer_shutdown & RCV_SHUTDOWN)) { + + /* Don't wait for non-blocking sockets. */ + if (timeout == 0) { + err = -EAGAIN; + goto out_wait; + } + + err = transport->notify_send_pre_block(vsk, &send_data); + if (err < 0) + goto out_wait; + + release_sock(sk); + timeout = schedule_timeout(timeout); + lock_sock(sk); + if (signal_pending(current)) { + err = sock_intr_errno(timeout); + goto out_wait; + } else if (timeout == 0) { + err = -EAGAIN; + goto out_wait; + } + + prepare_to_wait(sk_sleep(sk), &wait, + TASK_INTERRUPTIBLE); + } + + /* These checks occur both as part of and after the loop + * conditional since we need to check before and after + * sleeping. + */ + if (sk->sk_err) { + err = -sk->sk_err; + goto out_wait; + } else if ((sk->sk_shutdown & SEND_SHUTDOWN) || + (vsk->peer_shutdown & RCV_SHUTDOWN)) { + err = -EPIPE; + goto out_wait; + } + + err = transport->notify_send_pre_enqueue(vsk, &send_data); + if (err < 0) + goto out_wait; + + /* Note that enqueue will only write as many bytes as are free + * in the produce queue, so we don't need to ensure len is + * smaller than the queue size. It is the caller's + * responsibility to check how many bytes we were able to send. + */ + + written = transport->stream_enqueue( + vsk, msg->msg_iov, + len - total_written); + if (written < 0) { + err = -ENOMEM; + goto out_wait; + } + + total_written += written; + + err = transport->notify_send_post_enqueue( + vsk, written, &send_data); + if (err < 0) + goto out_wait; + + } + +out_wait: + if (total_written > 0) + err = total_written; + finish_wait(sk_sleep(sk), &wait); +out: + release_sock(sk); + return err; +} + + +static int +vsock_stream_recvmsg(struct kiocb *kiocb, + struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk; + struct vsock_sock *vsk; + int err; + size_t target; + ssize_t copied; + long timeout; + struct vsock_transport_recv_notify_data recv_data; + + DEFINE_WAIT(wait); + + sk = sock->sk; + vsk = vsock_sk(sk); + err = 0; + + lock_sock(sk); + + if (sk->sk_state != SS_CONNECTED) { + /* Recvmsg is supposed to return 0 if a peer performs an + * orderly shutdown. Differentiate between that case and when a + * peer has not connected or a local shutdown occured with the + * SOCK_DONE flag. + */ + if (sock_flag(sk, SOCK_DONE)) + err = 0; + else + err = -ENOTCONN; + + goto out; + } + + if (flags & MSG_OOB) { + err = -EOPNOTSUPP; + goto out; + } + + /* We don't check peer_shutdown flag here since peer may actually shut + * down, but there can be data in the queue that a local socket can + * receive. + */ + if (sk->sk_shutdown & RCV_SHUTDOWN) { + err = 0; + goto out; + } + + /* It is valid on Linux to pass in a zero-length receive buffer. This + * is not an error. We may as well bail out now. + */ + if (!len) { + err = 0; + goto out; + } + + /* We must not copy less than target bytes into the user's buffer + * before returning successfully, so we wait for the consume queue to + * have that much data to consume before dequeueing. Note that this + * makes it impossible to handle cases where target is greater than the + * queue size. + */ + target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); + if (target >= transport->stream_rcvhiwat(vsk)) { + err = -ENOMEM; + goto out; + } + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + copied = 0; + + err = transport->notify_recv_init(vsk, target, &recv_data); + if (err < 0) + goto out; + + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + while (1) { + s64 ready = vsock_stream_has_data(vsk); + + if (ready < 0) { + /* Invalid queue pair content. XXX This should be + * changed to a connection reset in a later change. + */ + + err = -ENOMEM; + goto out_wait; + } else if (ready > 0) { + ssize_t read; + + err = transport->notify_recv_pre_dequeue( + vsk, target, &recv_data); + if (err < 0) + break; + + read = transport->stream_dequeue( + vsk, msg->msg_iov, + len - copied, flags); + if (read < 0) { + err = -ENOMEM; + break; + } + + copied += read; + + err = transport->notify_recv_post_dequeue( + vsk, target, read, + !(flags & MSG_PEEK), &recv_data); + if (err < 0) + goto out_wait; + + if (read >= target || flags & MSG_PEEK) + break; + + target -= read; + } else { + if (sk->sk_err != 0 || (sk->sk_shutdown & RCV_SHUTDOWN) + || (vsk->peer_shutdown & SEND_SHUTDOWN)) { + break; + } + /* Don't wait for non-blocking sockets. */ + if (timeout == 0) { + err = -EAGAIN; + break; + } + + err = transport->notify_recv_pre_block( + vsk, target, &recv_data); + if (err < 0) + break; + + release_sock(sk); + timeout = schedule_timeout(timeout); + lock_sock(sk); + + if (signal_pending(current)) { + err = sock_intr_errno(timeout); + break; + } else if (timeout == 0) { + err = -EAGAIN; + break; + } + + prepare_to_wait(sk_sleep(sk), &wait, + TASK_INTERRUPTIBLE); + } + } + + if (sk->sk_err) + err = -sk->sk_err; + else if (sk->sk_shutdown & RCV_SHUTDOWN) + err = 0; + + if (copied > 0) { + /* We only do these additional bookkeeping/notification steps + * if we actually copied something out of the queue pair + * instead of just peeking ahead. + */ + + if (!(flags & MSG_PEEK)) { + /* If the other side has shutdown for sending and there + * is nothing more to read, then modify the socket + * state. + */ + if (vsk->peer_shutdown & SEND_SHUTDOWN) { + if (vsock_stream_has_data(vsk) <= 0) { + sk->sk_state = SS_UNCONNECTED; + sock_set_flag(sk, SOCK_DONE); + sk->sk_state_change(sk); + } + } + } + err = copied; + } + +out_wait: + finish_wait(sk_sleep(sk), &wait); +out: + release_sock(sk); + return err; +} + +static const struct proto_ops vsock_stream_ops = { + .family = PF_VSOCK, + .owner = THIS_MODULE, + .release = vsock_release, + .bind = vsock_bind, + .connect = vsock_stream_connect, + .socketpair = sock_no_socketpair, + .accept = vsock_accept, + .getname = vsock_getname, + .poll = vsock_poll, + .ioctl = sock_no_ioctl, + .listen = vsock_listen, + .shutdown = vsock_shutdown, + .setsockopt = vsock_stream_setsockopt, + .getsockopt = vsock_stream_getsockopt, + .sendmsg = vsock_stream_sendmsg, + .recvmsg = vsock_stream_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static int vsock_create(struct net *net, struct socket *sock, + int protocol, int kern) +{ + if (!sock) + return -EINVAL; + + if (protocol) + return -EPROTONOSUPPORT; + + switch (sock->type) { + case SOCK_DGRAM: + sock->ops = &vsock_dgram_ops; + break; + case SOCK_STREAM: + sock->ops = &vsock_stream_ops; + break; + default: + return -ESOCKTNOSUPPORT; + } + + sock->state = SS_UNCONNECTED; + + return __vsock_create(net, sock, NULL, GFP_KERNEL, 0) ? 0 : -ENOMEM; +} + +static const struct net_proto_family vsock_family_ops = { + .family = AF_VSOCK, + .create = vsock_create, + .owner = THIS_MODULE, +}; + +static long vsock_dev_do_ioctl(struct file *filp, + unsigned int cmd, void __user *ptr) +{ + u32 __user *p = ptr; + int retval = 0; + + switch (cmd) { + case IOCTL_VM_SOCKETS_GET_LOCAL_CID: + if (put_user(transport->get_local_cid(), p) != 0) + retval = -EFAULT; + break; + + default: + pr_err("Unknown ioctl %d\n", cmd); + retval = -EINVAL; + } + + return retval; +} + +static long vsock_dev_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return vsock_dev_do_ioctl(filp, cmd, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +static long vsock_dev_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return vsock_dev_do_ioctl(filp, cmd, compat_ptr(arg)); +} +#endif + +static const struct file_operations vsock_device_ops = { + .owner = THIS_MODULE, + .unlocked_ioctl = vsock_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vsock_dev_compat_ioctl, +#endif + .open = nonseekable_open, +}; + +static struct miscdevice vsock_device = { + .name = "vsock", + .minor = MISC_DYNAMIC_MINOR, + .fops = &vsock_device_ops, +}; + +static int __vsock_core_init(void) +{ + int err; + + vsock_init_tables(); + + err = misc_register(&vsock_device); + if (err) { + pr_err("Failed to register misc device\n"); + return -ENOENT; + } + + err = proto_register(&vsock_proto, 1); /* we want our slab */ + if (err) { + pr_err("Cannot register vsock protocol\n"); + goto err_misc_deregister; + } + + err = sock_register(&vsock_family_ops); + if (err) { + pr_err("could not register af_vsock (%d) address family: %d\n", + AF_VSOCK, err); + goto err_unregister_proto; + } + + return 0; + +err_unregister_proto: + proto_unregister(&vsock_proto); +err_misc_deregister: + misc_deregister(&vsock_device); + return err; +} + +int vsock_core_init(const struct vsock_transport *t) +{ + int retval = mutex_lock_interruptible(&vsock_register_mutex); + if (retval) + return retval; + + if (transport) { + retval = -EBUSY; + goto out; + } + + transport = t; + retval = __vsock_core_init(); + if (retval) + transport = NULL; + +out: + mutex_unlock(&vsock_register_mutex); + return retval; +} +EXPORT_SYMBOL_GPL(vsock_core_init); + +void vsock_core_exit(void) +{ + mutex_lock(&vsock_register_mutex); + + misc_deregister(&vsock_device); + sock_unregister(AF_VSOCK); + proto_unregister(&vsock_proto); + + /* We do not want the assignment below re-ordered. */ + mb(); + transport = NULL; + + mutex_unlock(&vsock_register_mutex); +} +EXPORT_SYMBOL_GPL(vsock_core_exit); + +MODULE_AUTHOR("VMware, Inc."); +MODULE_DESCRIPTION("VMware Virtual Socket Family"); +MODULE_VERSION(VSOCK_DRIVER_VERSION_STRING); +MODULE_LICENSE("GPL v2"); diff --git a/net/vmw_vsock/af_vsock.h b/net/vmw_vsock/af_vsock.h new file mode 100644 index 000000000000..7d64d3609ec9 --- /dev/null +++ b/net/vmw_vsock/af_vsock.h @@ -0,0 +1,175 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 __AF_VSOCK_H__ +#define __AF_VSOCK_H__ + +#include +#include +#include + +#include "vsock_addr.h" + +#define LAST_RESERVED_PORT 1023 + +#define vsock_sk(__sk) ((struct vsock_sock *)__sk) +#define sk_vsock(__vsk) (&(__vsk)->sk) + +struct vsock_sock { + /* sk must be the first member. */ + struct sock sk; + struct sockaddr_vm local_addr; + struct sockaddr_vm remote_addr; + /* Links for the global tables of bound and connected sockets. */ + struct list_head bound_table; + struct list_head connected_table; + /* Accessed without the socket lock held. This means it can never be + * modified outsided of socket create or destruct. + */ + bool trusted; + bool cached_peer_allow_dgram; /* Dgram communication allowed to + * cached peer? + */ + u32 cached_peer; /* Context ID of last dgram destination check. */ + const struct cred *owner; + /* Rest are SOCK_STREAM only. */ + long connect_timeout; + /* Listening socket that this came from. */ + struct sock *listener; + /* Used for pending list and accept queue during connection handshake. + * The listening socket is the head for both lists. Sockets created + * for connection requests are placed in the pending list until they + * are connected, at which point they are put in the accept queue list + * so they can be accepted in accept(). If accept() cannot accept the + * connection, it is marked as rejected so the cleanup function knows + * to clean up the socket. + */ + struct list_head pending_links; + struct list_head accept_queue; + bool rejected; + struct delayed_work dwork; + u32 peer_shutdown; + bool sent_request; + bool ignore_connecting_rst; + + /* Private to transport. */ + void *trans; +}; + +s64 vsock_stream_has_data(struct vsock_sock *vsk); +s64 vsock_stream_has_space(struct vsock_sock *vsk); +void vsock_pending_work(struct work_struct *work); +struct sock *__vsock_create(struct net *net, + struct socket *sock, + struct sock *parent, + gfp_t priority, unsigned short type); + +/**** TRANSPORT ****/ + +struct vsock_transport_recv_notify_data { + u64 data1; /* Transport-defined. */ + u64 data2; /* Transport-defined. */ + bool notify_on_block; +}; + +struct vsock_transport_send_notify_data { + u64 data1; /* Transport-defined. */ + u64 data2; /* Transport-defined. */ +}; + +struct vsock_transport { + /* Initialize/tear-down socket. */ + int (*init)(struct vsock_sock *, struct vsock_sock *); + void (*destruct)(struct vsock_sock *); + void (*release)(struct vsock_sock *); + + /* Connections. */ + int (*connect)(struct vsock_sock *); + + /* DGRAM. */ + int (*dgram_bind)(struct vsock_sock *, struct sockaddr_vm *); + int (*dgram_dequeue)(struct kiocb *kiocb, struct vsock_sock *vsk, + struct msghdr *msg, size_t len, int flags); + int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, + struct iovec *, size_t len); + bool (*dgram_allow)(u32 cid, u32 port); + + /* STREAM. */ + /* TODO: stream_bind() */ + ssize_t (*stream_dequeue)(struct vsock_sock *, struct iovec *, + size_t len, int flags); + ssize_t (*stream_enqueue)(struct vsock_sock *, struct iovec *, + size_t len); + s64 (*stream_has_data)(struct vsock_sock *); + s64 (*stream_has_space)(struct vsock_sock *); + u64 (*stream_rcvhiwat)(struct vsock_sock *); + bool (*stream_is_active)(struct vsock_sock *); + bool (*stream_allow)(u32 cid, u32 port); + + /* Notification. */ + int (*notify_poll_in)(struct vsock_sock *, size_t, bool *); + int (*notify_poll_out)(struct vsock_sock *, size_t, bool *); + int (*notify_recv_init)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_pre_block)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_pre_dequeue)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_post_dequeue)(struct vsock_sock *, size_t, + ssize_t, bool, struct vsock_transport_recv_notify_data *); + int (*notify_send_init)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_pre_block)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_pre_enqueue)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_post_enqueue)(struct vsock_sock *, ssize_t, + struct vsock_transport_send_notify_data *); + + /* Shutdown. */ + int (*shutdown)(struct vsock_sock *, int); + + /* Buffer sizes. */ + void (*set_buffer_size)(struct vsock_sock *, u64); + void (*set_min_buffer_size)(struct vsock_sock *, u64); + void (*set_max_buffer_size)(struct vsock_sock *, u64); + u64 (*get_buffer_size)(struct vsock_sock *); + u64 (*get_min_buffer_size)(struct vsock_sock *); + u64 (*get_max_buffer_size)(struct vsock_sock *); + + /* Addressing. */ + u32 (*get_local_cid)(void); +}; + +/**** CORE ****/ + +int vsock_core_init(const struct vsock_transport *t); +void vsock_core_exit(void); + +/**** UTILS ****/ + +void vsock_release_pending(struct sock *pending); +void vsock_add_pending(struct sock *listener, struct sock *pending); +void vsock_remove_pending(struct sock *listener, struct sock *pending); +void vsock_enqueue_accept(struct sock *listener, struct sock *connected); +void vsock_insert_connected(struct vsock_sock *vsk); +void vsock_remove_bound(struct vsock_sock *vsk); +void vsock_remove_connected(struct vsock_sock *vsk); +struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr); +struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, + struct sockaddr_vm *dst); +void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)); + +#endif /* __AF_VSOCK_H__ */ diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c new file mode 100644 index 000000000000..e8a87cf37072 --- /dev/null +++ b/net/vmw_vsock/vmci_transport.c @@ -0,0 +1,2157 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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. + */ + +#include + +#define EXPORT_SYMTAB +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_vsock.h" +#include "vmci_transport_notify.h" + +static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg); +static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg); +static void vmci_transport_peer_attach_cb(u32 sub_id, + const struct vmci_event_data *ed, + void *client_data); +static void vmci_transport_peer_detach_cb(u32 sub_id, + const struct vmci_event_data *ed, + void *client_data); +static void vmci_transport_recv_pkt_work(struct work_struct *work); +static int vmci_transport_recv_listen(struct sock *sk, + struct vmci_transport_packet *pkt); +static int vmci_transport_recv_connecting_server( + struct sock *sk, + struct sock *pending, + struct vmci_transport_packet *pkt); +static int vmci_transport_recv_connecting_client( + struct sock *sk, + struct vmci_transport_packet *pkt); +static int vmci_transport_recv_connecting_client_negotiate( + struct sock *sk, + struct vmci_transport_packet *pkt); +static int vmci_transport_recv_connecting_client_invalid( + struct sock *sk, + struct vmci_transport_packet *pkt); +static int vmci_transport_recv_connected(struct sock *sk, + struct vmci_transport_packet *pkt); +static bool vmci_transport_old_proto_override(bool *old_pkt_proto); +static u16 vmci_transport_new_proto_supported_versions(void); +static bool vmci_transport_proto_to_notify_struct(struct sock *sk, u16 *proto, + bool old_pkt_proto); + +struct vmci_transport_recv_pkt_info { + struct work_struct work; + struct sock *sk; + struct vmci_transport_packet pkt; +}; + +static struct vmci_handle vmci_transport_stream_handle = { VMCI_INVALID_ID, + VMCI_INVALID_ID }; +static u32 vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID; + +static int PROTOCOL_OVERRIDE = -1; + +#define VMCI_TRANSPORT_DEFAULT_QP_SIZE_MIN 128 +#define VMCI_TRANSPORT_DEFAULT_QP_SIZE 262144 +#define VMCI_TRANSPORT_DEFAULT_QP_SIZE_MAX 262144 + +/* The default peer timeout indicates how long we will wait for a peer response + * to a control message. + */ +#define VSOCK_DEFAULT_CONNECT_TIMEOUT (2 * HZ) + +#define SS_LISTEN 255 + +/* Helper function to convert from a VMCI error code to a VSock error code. */ + +static s32 vmci_transport_error_to_vsock_error(s32 vmci_error) +{ + int err; + + switch (vmci_error) { + case VMCI_ERROR_NO_MEM: + err = ENOMEM; + break; + case VMCI_ERROR_DUPLICATE_ENTRY: + case VMCI_ERROR_ALREADY_EXISTS: + err = EADDRINUSE; + break; + case VMCI_ERROR_NO_ACCESS: + err = EPERM; + break; + case VMCI_ERROR_NO_RESOURCES: + err = ENOBUFS; + break; + case VMCI_ERROR_INVALID_RESOURCE: + err = EHOSTUNREACH; + break; + case VMCI_ERROR_INVALID_ARGS: + default: + err = EINVAL; + } + + return err > 0 ? -err : err; +} + +static inline void +vmci_transport_packet_init(struct vmci_transport_packet *pkt, + struct sockaddr_vm *src, + struct sockaddr_vm *dst, + u8 type, + u64 size, + u64 mode, + struct vmci_transport_waiting_info *wait, + u16 proto, + struct vmci_handle handle) +{ + /* We register the stream control handler as an any cid handle so we + * must always send from a source address of VMADDR_CID_ANY + */ + pkt->dg.src = vmci_make_handle(VMADDR_CID_ANY, + VMCI_TRANSPORT_PACKET_RID); + pkt->dg.dst = vmci_make_handle(dst->svm_cid, + VMCI_TRANSPORT_PACKET_RID); + pkt->dg.payload_size = sizeof(*pkt) - sizeof(pkt->dg); + pkt->version = VMCI_TRANSPORT_PACKET_VERSION; + pkt->type = type; + pkt->src_port = src->svm_port; + pkt->dst_port = dst->svm_port; + memset(&pkt->proto, 0, sizeof(pkt->proto)); + memset(&pkt->_reserved2, 0, sizeof(pkt->_reserved2)); + + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_INVALID: + pkt->u.size = 0; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_REQUEST: + case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE: + pkt->u.size = size; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_OFFER: + case VMCI_TRANSPORT_PACKET_TYPE_ATTACH: + pkt->u.handle = handle; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_WROTE: + case VMCI_TRANSPORT_PACKET_TYPE_READ: + case VMCI_TRANSPORT_PACKET_TYPE_RST: + pkt->u.size = 0; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN: + pkt->u.mode = mode; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ: + case VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE: + memcpy(&pkt->u.wait, wait, sizeof(pkt->u.wait)); + break; + + case VMCI_TRANSPORT_PACKET_TYPE_REQUEST2: + case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2: + pkt->u.size = size; + pkt->proto = proto; + break; + } +} + +static inline void +vmci_transport_packet_get_addresses(struct vmci_transport_packet *pkt, + struct sockaddr_vm *local, + struct sockaddr_vm *remote) +{ + vsock_addr_init(local, pkt->dg.dst.context, pkt->dst_port); + vsock_addr_init(remote, pkt->dg.src.context, pkt->src_port); +} + +static int +__vmci_transport_send_control_pkt(struct vmci_transport_packet *pkt, + struct sockaddr_vm *src, + struct sockaddr_vm *dst, + enum vmci_transport_packet_type type, + u64 size, + u64 mode, + struct vmci_transport_waiting_info *wait, + u16 proto, + struct vmci_handle handle, + bool convert_error) +{ + int err; + + vmci_transport_packet_init(pkt, src, dst, type, size, mode, wait, + proto, handle); + err = vmci_datagram_send(&pkt->dg); + if (convert_error && (err < 0)) + return vmci_transport_error_to_vsock_error(err); + + return err; +} + +static int +vmci_transport_reply_control_pkt_fast(struct vmci_transport_packet *pkt, + enum vmci_transport_packet_type type, + u64 size, + u64 mode, + struct vmci_transport_waiting_info *wait, + struct vmci_handle handle) +{ + struct vmci_transport_packet reply; + struct sockaddr_vm src, dst; + + if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST) { + return 0; + } else { + vmci_transport_packet_get_addresses(pkt, &src, &dst); + return __vmci_transport_send_control_pkt(&reply, &src, &dst, + type, + size, mode, wait, + VSOCK_PROTO_INVALID, + handle, true); + } +} + +static int +vmci_transport_send_control_pkt_bh(struct sockaddr_vm *src, + struct sockaddr_vm *dst, + enum vmci_transport_packet_type type, + u64 size, + u64 mode, + struct vmci_transport_waiting_info *wait, + struct vmci_handle handle) +{ + /* Note that it is safe to use a single packet across all CPUs since + * two tasklets of the same type are guaranteed to not ever run + * simultaneously. If that ever changes, or VMCI stops using tasklets, + * we can use per-cpu packets. + */ + static struct vmci_transport_packet pkt; + + return __vmci_transport_send_control_pkt(&pkt, src, dst, type, + size, mode, wait, + VSOCK_PROTO_INVALID, handle, + false); +} + +static int +vmci_transport_send_control_pkt(struct sock *sk, + enum vmci_transport_packet_type type, + u64 size, + u64 mode, + struct vmci_transport_waiting_info *wait, + u16 proto, + struct vmci_handle handle) +{ + struct vmci_transport_packet *pkt; + struct vsock_sock *vsk; + int err; + + vsk = vsock_sk(sk); + + if (!vsock_addr_bound(&vsk->local_addr)) + return -EINVAL; + + if (!vsock_addr_bound(&vsk->remote_addr)) + return -EINVAL; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + err = __vmci_transport_send_control_pkt(pkt, &vsk->local_addr, + &vsk->remote_addr, type, size, + mode, wait, proto, handle, + true); + kfree(pkt); + + return err; +} + +static int vmci_transport_send_reset_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src, + struct vmci_transport_packet *pkt) +{ + if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST) + return 0; + return vmci_transport_send_control_pkt_bh( + dst, src, + VMCI_TRANSPORT_PACKET_TYPE_RST, 0, + 0, NULL, VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_reset(struct sock *sk, + struct vmci_transport_packet *pkt) +{ + if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST) + return 0; + return vmci_transport_send_control_pkt(sk, + VMCI_TRANSPORT_PACKET_TYPE_RST, + 0, 0, NULL, VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_negotiate(struct sock *sk, size_t size) +{ + return vmci_transport_send_control_pkt( + sk, + VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE, + size, 0, NULL, + VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_negotiate2(struct sock *sk, size_t size, + u16 version) +{ + return vmci_transport_send_control_pkt( + sk, + VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2, + size, 0, NULL, version, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_qp_offer(struct sock *sk, + struct vmci_handle handle) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_OFFER, 0, + 0, NULL, + VSOCK_PROTO_INVALID, handle); +} + +static int vmci_transport_send_attach(struct sock *sk, + struct vmci_handle handle) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_ATTACH, + 0, 0, NULL, VSOCK_PROTO_INVALID, + handle); +} + +static int vmci_transport_reply_reset(struct vmci_transport_packet *pkt) +{ + return vmci_transport_reply_control_pkt_fast( + pkt, + VMCI_TRANSPORT_PACKET_TYPE_RST, + 0, 0, NULL, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_invalid_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src) +{ + return vmci_transport_send_control_pkt_bh( + dst, src, + VMCI_TRANSPORT_PACKET_TYPE_INVALID, + 0, 0, NULL, VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_wrote_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src) +{ + return vmci_transport_send_control_pkt_bh( + dst, src, + VMCI_TRANSPORT_PACKET_TYPE_WROTE, 0, + 0, NULL, VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_read_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src) +{ + return vmci_transport_send_control_pkt_bh( + dst, src, + VMCI_TRANSPORT_PACKET_TYPE_READ, 0, + 0, NULL, VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_wrote(struct sock *sk) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_WROTE, 0, + 0, NULL, VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_read(struct sock *sk) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_READ, 0, + 0, NULL, VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_waiting_write(struct sock *sk, + struct vmci_transport_waiting_info *wait) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE, + 0, 0, wait, VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +int vmci_transport_send_waiting_read(struct sock *sk, + struct vmci_transport_waiting_info *wait) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ, + 0, 0, wait, VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_shutdown(struct vsock_sock *vsk, int mode) +{ + return vmci_transport_send_control_pkt( + &vsk->sk, + VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN, + 0, mode, NULL, + VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_conn_request(struct sock *sk, size_t size) +{ + return vmci_transport_send_control_pkt(sk, + VMCI_TRANSPORT_PACKET_TYPE_REQUEST, + size, 0, NULL, + VSOCK_PROTO_INVALID, + VMCI_INVALID_HANDLE); +} + +static int vmci_transport_send_conn_request2(struct sock *sk, size_t size, + u16 version) +{ + return vmci_transport_send_control_pkt( + sk, VMCI_TRANSPORT_PACKET_TYPE_REQUEST2, + size, 0, NULL, version, + VMCI_INVALID_HANDLE); +} + +static struct sock *vmci_transport_get_pending( + struct sock *listener, + struct vmci_transport_packet *pkt) +{ + struct vsock_sock *vlistener; + struct vsock_sock *vpending; + struct sock *pending; + + vlistener = vsock_sk(listener); + + list_for_each_entry(vpending, &vlistener->pending_links, + pending_links) { + struct sockaddr_vm src; + struct sockaddr_vm dst; + + vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port); + vsock_addr_init(&dst, pkt->dg.dst.context, pkt->dst_port); + + if (vsock_addr_equals_addr(&src, &vpending->remote_addr) && + vsock_addr_equals_addr(&dst, &vpending->local_addr)) { + pending = sk_vsock(vpending); + sock_hold(pending); + goto found; + } + } + + pending = NULL; +found: + return pending; + +} + +static void vmci_transport_release_pending(struct sock *pending) +{ + sock_put(pending); +} + +/* We allow two kinds of sockets to communicate with a restricted VM: 1) + * trusted sockets 2) sockets from applications running as the same user as the + * VM (this is only true for the host side and only when using hosted products) + */ + +static bool vmci_transport_is_trusted(struct vsock_sock *vsock, u32 peer_cid) +{ + return vsock->trusted || + vmci_is_context_owner(peer_cid, vsock->owner->uid); +} + +/* We allow sending datagrams to and receiving datagrams from a restricted VM + * only if it is trusted as described in vmci_transport_is_trusted. + */ + +static bool vmci_transport_allow_dgram(struct vsock_sock *vsock, u32 peer_cid) +{ + if (vsock->cached_peer != peer_cid) { + vsock->cached_peer = peer_cid; + if (!vmci_transport_is_trusted(vsock, peer_cid) && + (vmci_context_get_priv_flags(peer_cid) & + VMCI_PRIVILEGE_FLAG_RESTRICTED)) { + vsock->cached_peer_allow_dgram = false; + } else { + vsock->cached_peer_allow_dgram = true; + } + } + + return vsock->cached_peer_allow_dgram; +} + +static int +vmci_transport_queue_pair_alloc(struct vmci_qp **qpair, + struct vmci_handle *handle, + u64 produce_size, + u64 consume_size, + u32 peer, u32 flags, bool trusted) +{ + int err = 0; + + if (trusted) { + /* Try to allocate our queue pair as trusted. This will only + * work if vsock is running in the host. + */ + + err = vmci_qpair_alloc(qpair, handle, produce_size, + consume_size, + peer, flags, + VMCI_PRIVILEGE_FLAG_TRUSTED); + if (err != VMCI_ERROR_NO_ACCESS) + goto out; + + } + + err = vmci_qpair_alloc(qpair, handle, produce_size, consume_size, + peer, flags, VMCI_NO_PRIVILEGE_FLAGS); +out: + if (err < 0) { + pr_err("Could not attach to queue pair with %d\n", + err); + err = vmci_transport_error_to_vsock_error(err); + } + + return err; +} + +static int +vmci_transport_datagram_create_hnd(u32 resource_id, + u32 flags, + vmci_datagram_recv_cb recv_cb, + void *client_data, + struct vmci_handle *out_handle) +{ + int err = 0; + + /* Try to allocate our datagram handler as trusted. This will only work + * if vsock is running in the host. + */ + + err = vmci_datagram_create_handle_priv(resource_id, flags, + VMCI_PRIVILEGE_FLAG_TRUSTED, + recv_cb, + client_data, out_handle); + + if (err == VMCI_ERROR_NO_ACCESS) + err = vmci_datagram_create_handle(resource_id, flags, + recv_cb, client_data, + out_handle); + + return err; +} + +/* This is invoked as part of a tasklet that's scheduled when the VMCI + * interrupt fires. This is run in bottom-half context and if it ever needs to + * sleep it should defer that work to a work queue. + */ + +static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg) +{ + struct sock *sk; + size_t size; + struct sk_buff *skb; + struct vsock_sock *vsk; + + sk = (struct sock *)data; + + /* This handler is privileged when this module is running on the host. + * We will get datagrams from all endpoints (even VMs that are in a + * restricted context). If we get one from a restricted context then + * the destination socket must be trusted. + * + * NOTE: We access the socket struct without holding the lock here. + * This is ok because the field we are interested is never modified + * outside of the create and destruct socket functions. + */ + vsk = vsock_sk(sk); + if (!vmci_transport_allow_dgram(vsk, dg->src.context)) + return VMCI_ERROR_NO_ACCESS; + + size = VMCI_DG_SIZE(dg); + + /* Attach the packet to the socket's receive queue as an sk_buff. */ + skb = alloc_skb(size, GFP_ATOMIC); + if (skb) { + /* sk_receive_skb() will do a sock_put(), so hold here. */ + sock_hold(sk); + skb_put(skb, size); + memcpy(skb->data, dg, size); + sk_receive_skb(sk, skb, 0); + } + + return VMCI_SUCCESS; +} + +static bool vmci_transport_stream_allow(u32 cid, u32 port) +{ + static const u32 non_socket_contexts[] = { + VMADDR_CID_HYPERVISOR, + VMADDR_CID_RESERVED, + }; + int i; + + BUILD_BUG_ON(sizeof(cid) != sizeof(*non_socket_contexts)); + + for (i = 0; i < ARRAY_SIZE(non_socket_contexts); i++) { + if (cid == non_socket_contexts[i]) + return false; + } + + return true; +} + +/* This is invoked as part of a tasklet that's scheduled when the VMCI + * interrupt fires. This is run in bottom-half context but it defers most of + * its work to the packet handling work queue. + */ + +static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg) +{ + struct sock *sk; + struct sockaddr_vm dst; + struct sockaddr_vm src; + struct vmci_transport_packet *pkt; + struct vsock_sock *vsk; + bool bh_process_pkt; + int err; + + sk = NULL; + err = VMCI_SUCCESS; + bh_process_pkt = false; + + /* Ignore incoming packets from contexts without sockets, or resources + * that aren't vsock implementations. + */ + + if (!vmci_transport_stream_allow(dg->src.context, -1) + || VMCI_TRANSPORT_PACKET_RID != dg->src.resource) + return VMCI_ERROR_NO_ACCESS; + + if (VMCI_DG_SIZE(dg) < sizeof(*pkt)) + /* Drop datagrams that do not contain full VSock packets. */ + return VMCI_ERROR_INVALID_ARGS; + + pkt = (struct vmci_transport_packet *)dg; + + /* Find the socket that should handle this packet. First we look for a + * connected socket and if there is none we look for a socket bound to + * the destintation address. + */ + vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port); + vsock_addr_init(&dst, pkt->dg.dst.context, pkt->dst_port); + + sk = vsock_find_connected_socket(&src, &dst); + if (!sk) { + sk = vsock_find_bound_socket(&dst); + if (!sk) { + /* We could not find a socket for this specified + * address. If this packet is a RST, we just drop it. + * If it is another packet, we send a RST. Note that + * we do not send a RST reply to RSTs so that we do not + * continually send RSTs between two endpoints. + * + * Note that since this is a reply, dst is src and src + * is dst. + */ + if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0) + pr_err("unable to send reset\n"); + + err = VMCI_ERROR_NOT_FOUND; + goto out; + } + } + + /* If the received packet type is beyond all types known to this + * implementation, reply with an invalid message. Hopefully this will + * help when implementing backwards compatibility in the future. + */ + if (pkt->type >= VMCI_TRANSPORT_PACKET_TYPE_MAX) { + vmci_transport_send_invalid_bh(&dst, &src); + err = VMCI_ERROR_INVALID_ARGS; + goto out; + } + + /* This handler is privileged when this module is running on the host. + * We will get datagram connect requests from all endpoints (even VMs + * that are in a restricted context). If we get one from a restricted + * context then the destination socket must be trusted. + * + * NOTE: We access the socket struct without holding the lock here. + * This is ok because the field we are interested is never modified + * outside of the create and destruct socket functions. + */ + vsk = vsock_sk(sk); + if (!vmci_transport_allow_dgram(vsk, pkt->dg.src.context)) { + err = VMCI_ERROR_NO_ACCESS; + goto out; + } + + /* We do most everything in a work queue, but let's fast path the + * notification of reads and writes to help data transfer performance. + * We can only do this if there is no process context code executing + * for this socket since that may change the state. + */ + bh_lock_sock(sk); + + if (!sock_owned_by_user(sk) && sk->sk_state == SS_CONNECTED) + vmci_trans(vsk)->notify_ops->handle_notify_pkt( + sk, pkt, true, &dst, &src, + &bh_process_pkt); + + bh_unlock_sock(sk); + + if (!bh_process_pkt) { + struct vmci_transport_recv_pkt_info *recv_pkt_info; + + recv_pkt_info = kmalloc(sizeof(*recv_pkt_info), GFP_ATOMIC); + if (!recv_pkt_info) { + if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0) + pr_err("unable to send reset\n"); + + err = VMCI_ERROR_NO_MEM; + goto out; + } + + recv_pkt_info->sk = sk; + memcpy(&recv_pkt_info->pkt, pkt, sizeof(recv_pkt_info->pkt)); + INIT_WORK(&recv_pkt_info->work, vmci_transport_recv_pkt_work); + + schedule_work(&recv_pkt_info->work); + /* Clear sk so that the reference count incremented by one of + * the Find functions above is not decremented below. We need + * that reference count for the packet handler we've scheduled + * to run. + */ + sk = NULL; + } + +out: + if (sk) + sock_put(sk); + + return err; +} + +static void vmci_transport_peer_attach_cb(u32 sub_id, + const struct vmci_event_data *e_data, + void *client_data) +{ + struct sock *sk = client_data; + const struct vmci_event_payload_qp *e_payload; + struct vsock_sock *vsk; + + e_payload = vmci_event_data_const_payload(e_data); + + vsk = vsock_sk(sk); + + /* We don't ask for delayed CBs when we subscribe to this event (we + * pass 0 as flags to vmci_event_subscribe()). VMCI makes no + * guarantees in that case about what context we might be running in, + * so it could be BH or process, blockable or non-blockable. So we + * need to account for all possible contexts here. + */ + local_bh_disable(); + bh_lock_sock(sk); + + /* XXX This is lame, we should provide a way to lookup sockets by + * qp_handle. + */ + if (vmci_handle_is_equal(vmci_trans(vsk)->qp_handle, + e_payload->handle)) { + /* XXX This doesn't do anything, but in the future we may want + * to set a flag here to verify the attach really did occur and + * we weren't just sent a datagram claiming it was. + */ + goto out; + } + +out: + bh_unlock_sock(sk); + local_bh_enable(); +} + +static void vmci_transport_handle_detach(struct sock *sk) +{ + struct vsock_sock *vsk; + + vsk = vsock_sk(sk); + if (!vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle)) { + sock_set_flag(sk, SOCK_DONE); + + /* On a detach the peer will not be sending or receiving + * anymore. + */ + vsk->peer_shutdown = SHUTDOWN_MASK; + + /* We should not be sending anymore since the peer won't be + * there to receive, but we can still receive if there is data + * left in our consume queue. + */ + if (vsock_stream_has_data(vsk) <= 0) { + if (sk->sk_state == SS_CONNECTING) { + /* The peer may detach from a queue pair while + * we are still in the connecting state, i.e., + * if the peer VM is killed after attaching to + * a queue pair, but before we complete the + * handshake. In that case, we treat the detach + * event like a reset. + */ + + sk->sk_state = SS_UNCONNECTED; + sk->sk_err = ECONNRESET; + sk->sk_error_report(sk); + return; + } + sk->sk_state = SS_UNCONNECTED; + } + sk->sk_state_change(sk); + } +} + +static void vmci_transport_peer_detach_cb(u32 sub_id, + const struct vmci_event_data *e_data, + void *client_data) +{ + struct sock *sk = client_data; + const struct vmci_event_payload_qp *e_payload; + struct vsock_sock *vsk; + + e_payload = vmci_event_data_const_payload(e_data); + vsk = vsock_sk(sk); + if (vmci_handle_is_invalid(e_payload->handle)) + return; + + /* Same rules for locking as for peer_attach_cb(). */ + local_bh_disable(); + bh_lock_sock(sk); + + /* XXX This is lame, we should provide a way to lookup sockets by + * qp_handle. + */ + if (vmci_handle_is_equal(vmci_trans(vsk)->qp_handle, + e_payload->handle)) + vmci_transport_handle_detach(sk); + + bh_unlock_sock(sk); + local_bh_enable(); +} + +static void vmci_transport_qp_resumed_cb(u32 sub_id, + const struct vmci_event_data *e_data, + void *client_data) +{ + vsock_for_each_connected_socket(vmci_transport_handle_detach); +} + +static void vmci_transport_recv_pkt_work(struct work_struct *work) +{ + struct vmci_transport_recv_pkt_info *recv_pkt_info; + struct vmci_transport_packet *pkt; + struct sock *sk; + + recv_pkt_info = + container_of(work, struct vmci_transport_recv_pkt_info, work); + sk = recv_pkt_info->sk; + pkt = &recv_pkt_info->pkt; + + lock_sock(sk); + + switch (sk->sk_state) { + case SS_LISTEN: + vmci_transport_recv_listen(sk, pkt); + break; + case SS_CONNECTING: + /* Processing of pending connections for servers goes through + * the listening socket, so see vmci_transport_recv_listen() + * for that path. + */ + vmci_transport_recv_connecting_client(sk, pkt); + break; + case SS_CONNECTED: + vmci_transport_recv_connected(sk, pkt); + break; + default: + /* Because this function does not run in the same context as + * vmci_transport_recv_stream_cb it is possible that the + * socket has closed. We need to let the other side know or it + * could be sitting in a connect and hang forever. Send a + * reset to prevent that. + */ + vmci_transport_send_reset(sk, pkt); + goto out; + } + +out: + release_sock(sk); + kfree(recv_pkt_info); + /* Release reference obtained in the stream callback when we fetched + * this socket out of the bound or connected list. + */ + sock_put(sk); +} + +static int vmci_transport_recv_listen(struct sock *sk, + struct vmci_transport_packet *pkt) +{ + struct sock *pending; + struct vsock_sock *vpending; + int err; + u64 qp_size; + bool old_request = false; + bool old_pkt_proto = false; + + err = 0; + + /* Because we are in the listen state, we could be receiving a packet + * for ourself or any previous connection requests that we received. + * If it's the latter, we try to find a socket in our list of pending + * connections and, if we do, call the appropriate handler for the + * state that that socket is in. Otherwise we try to service the + * connection request. + */ + pending = vmci_transport_get_pending(sk, pkt); + if (pending) { + lock_sock(pending); + switch (pending->sk_state) { + case SS_CONNECTING: + err = vmci_transport_recv_connecting_server(sk, + pending, + pkt); + break; + default: + vmci_transport_send_reset(pending, pkt); + err = -EINVAL; + } + + if (err < 0) + vsock_remove_pending(sk, pending); + + release_sock(pending); + vmci_transport_release_pending(pending); + + return err; + } + + /* The listen state only accepts connection requests. Reply with a + * reset unless we received a reset. + */ + + if (!(pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST || + pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST2)) { + vmci_transport_reply_reset(pkt); + return -EINVAL; + } + + if (pkt->u.size == 0) { + vmci_transport_reply_reset(pkt); + return -EINVAL; + } + + /* If this socket can't accommodate this connection request, we send a + * reset. Otherwise we create and initialize a child socket and reply + * with a connection negotiation. + */ + if (sk->sk_ack_backlog >= sk->sk_max_ack_backlog) { + vmci_transport_reply_reset(pkt); + return -ECONNREFUSED; + } + + pending = __vsock_create(sock_net(sk), NULL, sk, GFP_KERNEL, + sk->sk_type); + if (!pending) { + vmci_transport_send_reset(sk, pkt); + return -ENOMEM; + } + + vpending = vsock_sk(pending); + + vsock_addr_init(&vpending->local_addr, pkt->dg.dst.context, + pkt->dst_port); + vsock_addr_init(&vpending->remote_addr, pkt->dg.src.context, + pkt->src_port); + + /* If the proposed size fits within our min/max, accept it. Otherwise + * propose our own size. + */ + if (pkt->u.size >= vmci_trans(vpending)->queue_pair_min_size && + pkt->u.size <= vmci_trans(vpending)->queue_pair_max_size) { + qp_size = pkt->u.size; + } else { + qp_size = vmci_trans(vpending)->queue_pair_size; + } + + /* Figure out if we are using old or new requests based on the + * overrides pkt types sent by our peer. + */ + if (vmci_transport_old_proto_override(&old_pkt_proto)) { + old_request = old_pkt_proto; + } else { + if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST) + old_request = true; + else if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_REQUEST2) + old_request = false; + + } + + if (old_request) { + /* Handle a REQUEST (or override) */ + u16 version = VSOCK_PROTO_INVALID; + if (vmci_transport_proto_to_notify_struct( + pending, &version, true)) + err = vmci_transport_send_negotiate(pending, qp_size); + else + err = -EINVAL; + + } else { + /* Handle a REQUEST2 (or override) */ + int proto_int = pkt->proto; + int pos; + u16 active_proto_version = 0; + + /* The list of possible protocols is the intersection of all + * protocols the client supports ... plus all the protocols we + * support. + */ + proto_int &= vmci_transport_new_proto_supported_versions(); + + /* We choose the highest possible protocol version and use that + * one. + */ + pos = fls(proto_int); + if (pos) { + active_proto_version = (1 << (pos - 1)); + if (vmci_transport_proto_to_notify_struct( + pending, &active_proto_version, false)) + err = vmci_transport_send_negotiate2(pending, + qp_size, + active_proto_version); + else + err = -EINVAL; + + } else { + err = -EINVAL; + } + } + + if (err < 0) { + vmci_transport_send_reset(sk, pkt); + sock_put(pending); + err = vmci_transport_error_to_vsock_error(err); + goto out; + } + + vsock_add_pending(sk, pending); + sk->sk_ack_backlog++; + + pending->sk_state = SS_CONNECTING; + vmci_trans(vpending)->produce_size = + vmci_trans(vpending)->consume_size = qp_size; + vmci_trans(vpending)->queue_pair_size = qp_size; + + vmci_trans(vpending)->notify_ops->process_request(pending); + + /* We might never receive another message for this socket and it's not + * connected to any process, so we have to ensure it gets cleaned up + * ourself. Our delayed work function will take care of that. Note + * that we do not ever cancel this function since we have few + * guarantees about its state when calling cancel_delayed_work(). + * Instead we hold a reference on the socket for that function and make + * it capable of handling cases where it needs to do nothing but + * release that reference. + */ + vpending->listener = sk; + sock_hold(sk); + sock_hold(pending); + INIT_DELAYED_WORK(&vpending->dwork, vsock_pending_work); + schedule_delayed_work(&vpending->dwork, HZ); + +out: + return err; +} + +static int +vmci_transport_recv_connecting_server(struct sock *listener, + struct sock *pending, + struct vmci_transport_packet *pkt) +{ + struct vsock_sock *vpending; + struct vmci_handle handle; + struct vmci_qp *qpair; + bool is_local; + u32 flags; + u32 detach_sub_id; + int err; + int skerr; + + vpending = vsock_sk(pending); + detach_sub_id = VMCI_INVALID_ID; + + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_OFFER: + if (vmci_handle_is_invalid(pkt->u.handle)) { + vmci_transport_send_reset(pending, pkt); + skerr = EPROTO; + err = -EINVAL; + goto destroy; + } + break; + default: + /* Close and cleanup the connection. */ + vmci_transport_send_reset(pending, pkt); + skerr = EPROTO; + err = pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST ? 0 : -EINVAL; + goto destroy; + } + + /* In order to complete the connection we need to attach to the offered + * queue pair and send an attach notification. We also subscribe to the + * detach event so we know when our peer goes away, and we do that + * before attaching so we don't miss an event. If all this succeeds, + * we update our state and wakeup anything waiting in accept() for a + * connection. + */ + + /* We don't care about attach since we ensure the other side has + * attached by specifying the ATTACH_ONLY flag below. + */ + err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_DETACH, + vmci_transport_peer_detach_cb, + pending, &detach_sub_id); + if (err < VMCI_SUCCESS) { + vmci_transport_send_reset(pending, pkt); + err = vmci_transport_error_to_vsock_error(err); + skerr = -err; + goto destroy; + } + + vmci_trans(vpending)->detach_sub_id = detach_sub_id; + + /* Now attach to the queue pair the client created. */ + handle = pkt->u.handle; + + /* vpending->local_addr always has a context id so we do not need to + * worry about VMADDR_CID_ANY in this case. + */ + is_local = + vpending->remote_addr.svm_cid == vpending->local_addr.svm_cid; + flags = VMCI_QPFLAG_ATTACH_ONLY; + flags |= is_local ? VMCI_QPFLAG_LOCAL : 0; + + err = vmci_transport_queue_pair_alloc( + &qpair, + &handle, + vmci_trans(vpending)->produce_size, + vmci_trans(vpending)->consume_size, + pkt->dg.src.context, + flags, + vmci_transport_is_trusted( + vpending, + vpending->remote_addr.svm_cid)); + if (err < 0) { + vmci_transport_send_reset(pending, pkt); + skerr = -err; + goto destroy; + } + + vmci_trans(vpending)->qp_handle = handle; + vmci_trans(vpending)->qpair = qpair; + + /* When we send the attach message, we must be ready to handle incoming + * control messages on the newly connected socket. So we move the + * pending socket to the connected state before sending the attach + * message. Otherwise, an incoming packet triggered by the attach being + * received by the peer may be processed concurrently with what happens + * below after sending the attach message, and that incoming packet + * will find the listening socket instead of the (currently) pending + * socket. Note that enqueueing the socket increments the reference + * count, so even if a reset comes before the connection is accepted, + * the socket will be valid until it is removed from the queue. + * + * If we fail sending the attach below, we remove the socket from the + * connected list and move the socket to SS_UNCONNECTED before + * releasing the lock, so a pending slow path processing of an incoming + * packet will not see the socket in the connected state in that case. + */ + pending->sk_state = SS_CONNECTED; + + vsock_insert_connected(vpending); + + /* Notify our peer of our attach. */ + err = vmci_transport_send_attach(pending, handle); + if (err < 0) { + vsock_remove_connected(vpending); + pr_err("Could not send attach\n"); + vmci_transport_send_reset(pending, pkt); + err = vmci_transport_error_to_vsock_error(err); + skerr = -err; + goto destroy; + } + + /* We have a connection. Move the now connected socket from the + * listener's pending list to the accept queue so callers of accept() + * can find it. + */ + vsock_remove_pending(listener, pending); + vsock_enqueue_accept(listener, pending); + + /* Callers of accept() will be be waiting on the listening socket, not + * the pending socket. + */ + listener->sk_state_change(listener); + + return 0; + +destroy: + pending->sk_err = skerr; + pending->sk_state = SS_UNCONNECTED; + /* As long as we drop our reference, all necessary cleanup will handle + * when the cleanup function drops its reference and our destruct + * implementation is called. Note that since the listen handler will + * remove pending from the pending list upon our failure, the cleanup + * function won't drop the additional reference, which is why we do it + * here. + */ + sock_put(pending); + + return err; +} + +static int +vmci_transport_recv_connecting_client(struct sock *sk, + struct vmci_transport_packet *pkt) +{ + struct vsock_sock *vsk; + int err; + int skerr; + + vsk = vsock_sk(sk); + + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_ATTACH: + if (vmci_handle_is_invalid(pkt->u.handle) || + !vmci_handle_is_equal(pkt->u.handle, + vmci_trans(vsk)->qp_handle)) { + skerr = EPROTO; + err = -EINVAL; + goto destroy; + } + + /* Signify the socket is connected and wakeup the waiter in + * connect(). Also place the socket in the connected table for + * accounting (it can already be found since it's in the bound + * table). + */ + sk->sk_state = SS_CONNECTED; + sk->sk_socket->state = SS_CONNECTED; + vsock_insert_connected(vsk); + sk->sk_state_change(sk); + + break; + case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE: + case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2: + if (pkt->u.size == 0 + || pkt->dg.src.context != vsk->remote_addr.svm_cid + || pkt->src_port != vsk->remote_addr.svm_port + || !vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle) + || vmci_trans(vsk)->qpair + || vmci_trans(vsk)->produce_size != 0 + || vmci_trans(vsk)->consume_size != 0 + || vmci_trans(vsk)->attach_sub_id != VMCI_INVALID_ID + || vmci_trans(vsk)->detach_sub_id != VMCI_INVALID_ID) { + skerr = EPROTO; + err = -EINVAL; + + goto destroy; + } + + err = vmci_transport_recv_connecting_client_negotiate(sk, pkt); + if (err) { + skerr = -err; + goto destroy; + } + + break; + case VMCI_TRANSPORT_PACKET_TYPE_INVALID: + err = vmci_transport_recv_connecting_client_invalid(sk, pkt); + if (err) { + skerr = -err; + goto destroy; + } + + break; + case VMCI_TRANSPORT_PACKET_TYPE_RST: + /* Older versions of the linux code (WS 6.5 / ESX 4.0) used to + * continue processing here after they sent an INVALID packet. + * This meant that we got a RST after the INVALID. We ignore a + * RST after an INVALID. The common code doesn't send the RST + * ... so we can hang if an old version of the common code + * fails between getting a REQUEST and sending an OFFER back. + * Not much we can do about it... except hope that it doesn't + * happen. + */ + if (vsk->ignore_connecting_rst) { + vsk->ignore_connecting_rst = false; + } else { + skerr = ECONNRESET; + err = 0; + goto destroy; + } + + break; + default: + /* Close and cleanup the connection. */ + skerr = EPROTO; + err = -EINVAL; + goto destroy; + } + + return 0; + +destroy: + vmci_transport_send_reset(sk, pkt); + + sk->sk_state = SS_UNCONNECTED; + sk->sk_err = skerr; + sk->sk_error_report(sk); + return err; +} + +static int vmci_transport_recv_connecting_client_negotiate( + struct sock *sk, + struct vmci_transport_packet *pkt) +{ + int err; + struct vsock_sock *vsk; + struct vmci_handle handle; + struct vmci_qp *qpair; + u32 attach_sub_id; + u32 detach_sub_id; + bool is_local; + u32 flags; + bool old_proto = true; + bool old_pkt_proto; + u16 version; + + vsk = vsock_sk(sk); + handle = VMCI_INVALID_HANDLE; + attach_sub_id = VMCI_INVALID_ID; + detach_sub_id = VMCI_INVALID_ID; + + /* If we have gotten here then we should be past the point where old + * linux vsock could have sent the bogus rst. + */ + vsk->sent_request = false; + vsk->ignore_connecting_rst = false; + + /* Verify that we're OK with the proposed queue pair size */ + if (pkt->u.size < vmci_trans(vsk)->queue_pair_min_size || + pkt->u.size > vmci_trans(vsk)->queue_pair_max_size) { + err = -EINVAL; + goto destroy; + } + + /* At this point we know the CID the peer is using to talk to us. */ + + if (vsk->local_addr.svm_cid == VMADDR_CID_ANY) + vsk->local_addr.svm_cid = pkt->dg.dst.context; + + /* Setup the notify ops to be the highest supported version that both + * the server and the client support. + */ + + if (vmci_transport_old_proto_override(&old_pkt_proto)) { + old_proto = old_pkt_proto; + } else { + if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE) + old_proto = true; + else if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2) + old_proto = false; + + } + + if (old_proto) + version = VSOCK_PROTO_INVALID; + else + version = pkt->proto; + + if (!vmci_transport_proto_to_notify_struct(sk, &version, old_proto)) { + err = -EINVAL; + goto destroy; + } + + /* Subscribe to attach and detach events first. + * + * XXX We attach once for each queue pair created for now so it is easy + * to find the socket (it's provided), but later we should only + * subscribe once and add a way to lookup sockets by queue pair handle. + */ + err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_ATTACH, + vmci_transport_peer_attach_cb, + sk, &attach_sub_id); + if (err < VMCI_SUCCESS) { + err = vmci_transport_error_to_vsock_error(err); + goto destroy; + } + + err = vmci_event_subscribe(VMCI_EVENT_QP_PEER_DETACH, + vmci_transport_peer_detach_cb, + sk, &detach_sub_id); + if (err < VMCI_SUCCESS) { + err = vmci_transport_error_to_vsock_error(err); + goto destroy; + } + + /* Make VMCI select the handle for us. */ + handle = VMCI_INVALID_HANDLE; + is_local = vsk->remote_addr.svm_cid == vsk->local_addr.svm_cid; + flags = is_local ? VMCI_QPFLAG_LOCAL : 0; + + err = vmci_transport_queue_pair_alloc(&qpair, + &handle, + pkt->u.size, + pkt->u.size, + vsk->remote_addr.svm_cid, + flags, + vmci_transport_is_trusted( + vsk, + vsk-> + remote_addr.svm_cid)); + if (err < 0) + goto destroy; + + err = vmci_transport_send_qp_offer(sk, handle); + if (err < 0) { + err = vmci_transport_error_to_vsock_error(err); + goto destroy; + } + + vmci_trans(vsk)->qp_handle = handle; + vmci_trans(vsk)->qpair = qpair; + + vmci_trans(vsk)->produce_size = vmci_trans(vsk)->consume_size = + pkt->u.size; + + vmci_trans(vsk)->attach_sub_id = attach_sub_id; + vmci_trans(vsk)->detach_sub_id = detach_sub_id; + + vmci_trans(vsk)->notify_ops->process_negotiate(sk); + + return 0; + +destroy: + if (attach_sub_id != VMCI_INVALID_ID) + vmci_event_unsubscribe(attach_sub_id); + + if (detach_sub_id != VMCI_INVALID_ID) + vmci_event_unsubscribe(detach_sub_id); + + if (!vmci_handle_is_invalid(handle)) + vmci_qpair_detach(&qpair); + + return err; +} + +static int +vmci_transport_recv_connecting_client_invalid(struct sock *sk, + struct vmci_transport_packet *pkt) +{ + int err = 0; + struct vsock_sock *vsk = vsock_sk(sk); + + if (vsk->sent_request) { + vsk->sent_request = false; + vsk->ignore_connecting_rst = true; + + err = vmci_transport_send_conn_request( + sk, vmci_trans(vsk)->queue_pair_size); + if (err < 0) + err = vmci_transport_error_to_vsock_error(err); + else + err = 0; + + } + + return err; +} + +static int vmci_transport_recv_connected(struct sock *sk, + struct vmci_transport_packet *pkt) +{ + struct vsock_sock *vsk; + bool pkt_processed = false; + + /* In cases where we are closing the connection, it's sufficient to + * mark the state change (and maybe error) and wake up any waiting + * threads. Since this is a connected socket, it's owned by a user + * process and will be cleaned up when the failure is passed back on + * the current or next system call. Our system call implementations + * must therefore check for error and state changes on entry and when + * being awoken. + */ + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN: + if (pkt->u.mode) { + vsk = vsock_sk(sk); + + vsk->peer_shutdown |= pkt->u.mode; + sk->sk_state_change(sk); + } + break; + + case VMCI_TRANSPORT_PACKET_TYPE_RST: + vsk = vsock_sk(sk); + /* It is possible that we sent our peer a message (e.g a + * WAITING_READ) right before we got notified that the peer had + * detached. If that happens then we can get a RST pkt back + * from our peer even though there is data available for us to + * read. In that case, don't shutdown the socket completely but + * instead allow the local client to finish reading data off + * the queuepair. Always treat a RST pkt in connected mode like + * a clean shutdown. + */ + sock_set_flag(sk, SOCK_DONE); + vsk->peer_shutdown = SHUTDOWN_MASK; + if (vsock_stream_has_data(vsk) <= 0) + sk->sk_state = SS_DISCONNECTING; + + sk->sk_state_change(sk); + break; + + default: + vsk = vsock_sk(sk); + vmci_trans(vsk)->notify_ops->handle_notify_pkt( + sk, pkt, false, NULL, NULL, + &pkt_processed); + if (!pkt_processed) + return -EINVAL; + + break; + } + + return 0; +} + +static int vmci_transport_socket_init(struct vsock_sock *vsk, + struct vsock_sock *psk) +{ + vsk->trans = kmalloc(sizeof(struct vmci_transport), GFP_KERNEL); + if (!vsk->trans) + return -ENOMEM; + + vmci_trans(vsk)->dg_handle = VMCI_INVALID_HANDLE; + vmci_trans(vsk)->qp_handle = VMCI_INVALID_HANDLE; + vmci_trans(vsk)->qpair = NULL; + vmci_trans(vsk)->produce_size = vmci_trans(vsk)->consume_size = 0; + vmci_trans(vsk)->attach_sub_id = vmci_trans(vsk)->detach_sub_id = + VMCI_INVALID_ID; + vmci_trans(vsk)->notify_ops = NULL; + if (psk) { + vmci_trans(vsk)->queue_pair_size = + vmci_trans(psk)->queue_pair_size; + vmci_trans(vsk)->queue_pair_min_size = + vmci_trans(psk)->queue_pair_min_size; + vmci_trans(vsk)->queue_pair_max_size = + vmci_trans(psk)->queue_pair_max_size; + } else { + vmci_trans(vsk)->queue_pair_size = + VMCI_TRANSPORT_DEFAULT_QP_SIZE; + vmci_trans(vsk)->queue_pair_min_size = + VMCI_TRANSPORT_DEFAULT_QP_SIZE_MIN; + vmci_trans(vsk)->queue_pair_max_size = + VMCI_TRANSPORT_DEFAULT_QP_SIZE_MAX; + } + + return 0; +} + +static void vmci_transport_destruct(struct vsock_sock *vsk) +{ + if (vmci_trans(vsk)->attach_sub_id != VMCI_INVALID_ID) { + vmci_event_unsubscribe(vmci_trans(vsk)->attach_sub_id); + vmci_trans(vsk)->attach_sub_id = VMCI_INVALID_ID; + } + + if (vmci_trans(vsk)->detach_sub_id != VMCI_INVALID_ID) { + vmci_event_unsubscribe(vmci_trans(vsk)->detach_sub_id); + vmci_trans(vsk)->detach_sub_id = VMCI_INVALID_ID; + } + + if (!vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle)) { + vmci_qpair_detach(&vmci_trans(vsk)->qpair); + vmci_trans(vsk)->qp_handle = VMCI_INVALID_HANDLE; + vmci_trans(vsk)->produce_size = 0; + vmci_trans(vsk)->consume_size = 0; + } + + if (vmci_trans(vsk)->notify_ops) + vmci_trans(vsk)->notify_ops->socket_destruct(vsk); + + kfree(vsk->trans); + vsk->trans = NULL; +} + +static void vmci_transport_release(struct vsock_sock *vsk) +{ + if (!vmci_handle_is_invalid(vmci_trans(vsk)->dg_handle)) { + vmci_datagram_destroy_handle(vmci_trans(vsk)->dg_handle); + vmci_trans(vsk)->dg_handle = VMCI_INVALID_HANDLE; + } +} + +static int vmci_transport_dgram_bind(struct vsock_sock *vsk, + struct sockaddr_vm *addr) +{ + u32 port; + u32 flags; + int err; + + /* VMCI will select a resource ID for us if we provide + * VMCI_INVALID_ID. + */ + port = addr->svm_port == VMADDR_PORT_ANY ? + VMCI_INVALID_ID : addr->svm_port; + + if (port <= LAST_RESERVED_PORT && !capable(CAP_NET_BIND_SERVICE)) + return -EACCES; + + flags = addr->svm_cid == VMADDR_CID_ANY ? + VMCI_FLAG_ANYCID_DG_HND : 0; + + err = vmci_transport_datagram_create_hnd(port, flags, + vmci_transport_recv_dgram_cb, + &vsk->sk, + &vmci_trans(vsk)->dg_handle); + if (err < VMCI_SUCCESS) + return vmci_transport_error_to_vsock_error(err); + vsock_addr_init(&vsk->local_addr, addr->svm_cid, + vmci_trans(vsk)->dg_handle.resource); + + return 0; +} + +static int vmci_transport_dgram_enqueue( + struct vsock_sock *vsk, + struct sockaddr_vm *remote_addr, + struct iovec *iov, + size_t len) +{ + int err; + struct vmci_datagram *dg; + + if (len > VMCI_MAX_DG_PAYLOAD_SIZE) + return -EMSGSIZE; + + if (!vmci_transport_allow_dgram(vsk, remote_addr->svm_cid)) + return -EPERM; + + /* Allocate a buffer for the user's message and our packet header. */ + dg = kmalloc(len + sizeof(*dg), GFP_KERNEL); + if (!dg) + return -ENOMEM; + + memcpy_fromiovec(VMCI_DG_PAYLOAD(dg), iov, len); + + dg->dst = vmci_make_handle(remote_addr->svm_cid, + remote_addr->svm_port); + dg->src = vmci_make_handle(vsk->local_addr.svm_cid, + vsk->local_addr.svm_port); + dg->payload_size = len; + + err = vmci_datagram_send(dg); + kfree(dg); + if (err < 0) + return vmci_transport_error_to_vsock_error(err); + + return err - sizeof(*dg); +} + +static int vmci_transport_dgram_dequeue(struct kiocb *kiocb, + struct vsock_sock *vsk, + struct msghdr *msg, size_t len, + int flags) +{ + int err; + int noblock; + struct vmci_datagram *dg; + size_t payload_len; + struct sk_buff *skb; + + noblock = flags & MSG_DONTWAIT; + + if (flags & MSG_OOB || flags & MSG_ERRQUEUE) + return -EOPNOTSUPP; + + /* Retrieve the head sk_buff from the socket's receive queue. */ + err = 0; + skb = skb_recv_datagram(&vsk->sk, flags, noblock, &err); + if (err) + return err; + + if (!skb) + return -EAGAIN; + + dg = (struct vmci_datagram *)skb->data; + if (!dg) + /* err is 0, meaning we read zero bytes. */ + goto out; + + payload_len = dg->payload_size; + /* Ensure the sk_buff matches the payload size claimed in the packet. */ + if (payload_len != skb->len - sizeof(*dg)) { + err = -EINVAL; + goto out; + } + + if (payload_len > len) { + payload_len = len; + msg->msg_flags |= MSG_TRUNC; + } + + /* Place the datagram payload in the user's iovec. */ + err = skb_copy_datagram_iovec(skb, sizeof(*dg), msg->msg_iov, + payload_len); + if (err) + goto out; + + msg->msg_namelen = 0; + if (msg->msg_name) { + struct sockaddr_vm *vm_addr; + + /* Provide the address of the sender. */ + vm_addr = (struct sockaddr_vm *)msg->msg_name; + vsock_addr_init(vm_addr, dg->src.context, dg->src.resource); + msg->msg_namelen = sizeof(*vm_addr); + } + err = payload_len; + +out: + skb_free_datagram(&vsk->sk, skb); + return err; +} + +static bool vmci_transport_dgram_allow(u32 cid, u32 port) +{ + if (cid == VMADDR_CID_HYPERVISOR) { + /* Registrations of PBRPC Servers do not modify VMX/Hypervisor + * state and are allowed. + */ + return port == VMCI_UNITY_PBRPC_REGISTER; + } + + return true; +} + +static int vmci_transport_connect(struct vsock_sock *vsk) +{ + int err; + bool old_pkt_proto = false; + struct sock *sk = &vsk->sk; + + if (vmci_transport_old_proto_override(&old_pkt_proto) && + old_pkt_proto) { + err = vmci_transport_send_conn_request( + sk, vmci_trans(vsk)->queue_pair_size); + if (err < 0) { + sk->sk_state = SS_UNCONNECTED; + return err; + } + } else { + int supported_proto_versions = + vmci_transport_new_proto_supported_versions(); + err = vmci_transport_send_conn_request2( + sk, vmci_trans(vsk)->queue_pair_size, + supported_proto_versions); + if (err < 0) { + sk->sk_state = SS_UNCONNECTED; + return err; + } + + vsk->sent_request = true; + } + + return err; +} + +static ssize_t vmci_transport_stream_dequeue( + struct vsock_sock *vsk, + struct iovec *iov, + size_t len, + int flags) +{ + if (flags & MSG_PEEK) + return vmci_qpair_peekv(vmci_trans(vsk)->qpair, iov, len, 0); + else + return vmci_qpair_dequev(vmci_trans(vsk)->qpair, iov, len, 0); +} + +static ssize_t vmci_transport_stream_enqueue( + struct vsock_sock *vsk, + struct iovec *iov, + size_t len) +{ + return vmci_qpair_enquev(vmci_trans(vsk)->qpair, iov, len, 0); +} + +static s64 vmci_transport_stream_has_data(struct vsock_sock *vsk) +{ + return vmci_qpair_consume_buf_ready(vmci_trans(vsk)->qpair); +} + +static s64 vmci_transport_stream_has_space(struct vsock_sock *vsk) +{ + return vmci_qpair_produce_free_space(vmci_trans(vsk)->qpair); +} + +static u64 vmci_transport_stream_rcvhiwat(struct vsock_sock *vsk) +{ + return vmci_trans(vsk)->consume_size; +} + +static bool vmci_transport_stream_is_active(struct vsock_sock *vsk) +{ + return !vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle); +} + +static u64 vmci_transport_get_buffer_size(struct vsock_sock *vsk) +{ + return vmci_trans(vsk)->queue_pair_size; +} + +static u64 vmci_transport_get_min_buffer_size(struct vsock_sock *vsk) +{ + return vmci_trans(vsk)->queue_pair_min_size; +} + +static u64 vmci_transport_get_max_buffer_size(struct vsock_sock *vsk) +{ + return vmci_trans(vsk)->queue_pair_max_size; +} + +static void vmci_transport_set_buffer_size(struct vsock_sock *vsk, u64 val) +{ + if (val < vmci_trans(vsk)->queue_pair_min_size) + vmci_trans(vsk)->queue_pair_min_size = val; + if (val > vmci_trans(vsk)->queue_pair_max_size) + vmci_trans(vsk)->queue_pair_max_size = val; + vmci_trans(vsk)->queue_pair_size = val; +} + +static void vmci_transport_set_min_buffer_size(struct vsock_sock *vsk, + u64 val) +{ + if (val > vmci_trans(vsk)->queue_pair_size) + vmci_trans(vsk)->queue_pair_size = val; + vmci_trans(vsk)->queue_pair_min_size = val; +} + +static void vmci_transport_set_max_buffer_size(struct vsock_sock *vsk, + u64 val) +{ + if (val < vmci_trans(vsk)->queue_pair_size) + vmci_trans(vsk)->queue_pair_size = val; + vmci_trans(vsk)->queue_pair_max_size = val; +} + +static int vmci_transport_notify_poll_in( + struct vsock_sock *vsk, + size_t target, + bool *data_ready_now) +{ + return vmci_trans(vsk)->notify_ops->poll_in( + &vsk->sk, target, data_ready_now); +} + +static int vmci_transport_notify_poll_out( + struct vsock_sock *vsk, + size_t target, + bool *space_available_now) +{ + return vmci_trans(vsk)->notify_ops->poll_out( + &vsk->sk, target, space_available_now); +} + +static int vmci_transport_notify_recv_init( + struct vsock_sock *vsk, + size_t target, + struct vsock_transport_recv_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->recv_init( + &vsk->sk, target, + (struct vmci_transport_recv_notify_data *)data); +} + +static int vmci_transport_notify_recv_pre_block( + struct vsock_sock *vsk, + size_t target, + struct vsock_transport_recv_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->recv_pre_block( + &vsk->sk, target, + (struct vmci_transport_recv_notify_data *)data); +} + +static int vmci_transport_notify_recv_pre_dequeue( + struct vsock_sock *vsk, + size_t target, + struct vsock_transport_recv_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->recv_pre_dequeue( + &vsk->sk, target, + (struct vmci_transport_recv_notify_data *)data); +} + +static int vmci_transport_notify_recv_post_dequeue( + struct vsock_sock *vsk, + size_t target, + ssize_t copied, + bool data_read, + struct vsock_transport_recv_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->recv_post_dequeue( + &vsk->sk, target, copied, data_read, + (struct vmci_transport_recv_notify_data *)data); +} + +static int vmci_transport_notify_send_init( + struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->send_init( + &vsk->sk, + (struct vmci_transport_send_notify_data *)data); +} + +static int vmci_transport_notify_send_pre_block( + struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->send_pre_block( + &vsk->sk, + (struct vmci_transport_send_notify_data *)data); +} + +static int vmci_transport_notify_send_pre_enqueue( + struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->send_pre_enqueue( + &vsk->sk, + (struct vmci_transport_send_notify_data *)data); +} + +static int vmci_transport_notify_send_post_enqueue( + struct vsock_sock *vsk, + ssize_t written, + struct vsock_transport_send_notify_data *data) +{ + return vmci_trans(vsk)->notify_ops->send_post_enqueue( + &vsk->sk, written, + (struct vmci_transport_send_notify_data *)data); +} + +static bool vmci_transport_old_proto_override(bool *old_pkt_proto) +{ + if (PROTOCOL_OVERRIDE != -1) { + if (PROTOCOL_OVERRIDE == 0) + *old_pkt_proto = true; + else + *old_pkt_proto = false; + + pr_info("Proto override in use\n"); + return true; + } + + return false; +} + +static bool vmci_transport_proto_to_notify_struct(struct sock *sk, + u16 *proto, + bool old_pkt_proto) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + if (old_pkt_proto) { + if (*proto != VSOCK_PROTO_INVALID) { + pr_err("Can't set both an old and new protocol\n"); + return false; + } + vmci_trans(vsk)->notify_ops = &vmci_transport_notify_pkt_ops; + goto exit; + } + + switch (*proto) { + case VSOCK_PROTO_PKT_ON_NOTIFY: + vmci_trans(vsk)->notify_ops = + &vmci_transport_notify_pkt_q_state_ops; + break; + default: + pr_err("Unknown notify protocol version\n"); + return false; + } + +exit: + vmci_trans(vsk)->notify_ops->socket_init(sk); + return true; +} + +static u16 vmci_transport_new_proto_supported_versions(void) +{ + if (PROTOCOL_OVERRIDE != -1) + return PROTOCOL_OVERRIDE; + + return VSOCK_PROTO_ALL_SUPPORTED; +} + +static u32 vmci_transport_get_local_cid(void) +{ + return vmci_get_context_id(); +} + +static struct vsock_transport vmci_transport = { + .init = vmci_transport_socket_init, + .destruct = vmci_transport_destruct, + .release = vmci_transport_release, + .connect = vmci_transport_connect, + .dgram_bind = vmci_transport_dgram_bind, + .dgram_dequeue = vmci_transport_dgram_dequeue, + .dgram_enqueue = vmci_transport_dgram_enqueue, + .dgram_allow = vmci_transport_dgram_allow, + .stream_dequeue = vmci_transport_stream_dequeue, + .stream_enqueue = vmci_transport_stream_enqueue, + .stream_has_data = vmci_transport_stream_has_data, + .stream_has_space = vmci_transport_stream_has_space, + .stream_rcvhiwat = vmci_transport_stream_rcvhiwat, + .stream_is_active = vmci_transport_stream_is_active, + .stream_allow = vmci_transport_stream_allow, + .notify_poll_in = vmci_transport_notify_poll_in, + .notify_poll_out = vmci_transport_notify_poll_out, + .notify_recv_init = vmci_transport_notify_recv_init, + .notify_recv_pre_block = vmci_transport_notify_recv_pre_block, + .notify_recv_pre_dequeue = vmci_transport_notify_recv_pre_dequeue, + .notify_recv_post_dequeue = vmci_transport_notify_recv_post_dequeue, + .notify_send_init = vmci_transport_notify_send_init, + .notify_send_pre_block = vmci_transport_notify_send_pre_block, + .notify_send_pre_enqueue = vmci_transport_notify_send_pre_enqueue, + .notify_send_post_enqueue = vmci_transport_notify_send_post_enqueue, + .shutdown = vmci_transport_shutdown, + .set_buffer_size = vmci_transport_set_buffer_size, + .set_min_buffer_size = vmci_transport_set_min_buffer_size, + .set_max_buffer_size = vmci_transport_set_max_buffer_size, + .get_buffer_size = vmci_transport_get_buffer_size, + .get_min_buffer_size = vmci_transport_get_min_buffer_size, + .get_max_buffer_size = vmci_transport_get_max_buffer_size, + .get_local_cid = vmci_transport_get_local_cid, +}; + +static int __init vmci_transport_init(void) +{ + int err; + + /* Create the datagram handle that we will use to send and receive all + * VSocket control messages for this context. + */ + err = vmci_transport_datagram_create_hnd(VMCI_TRANSPORT_PACKET_RID, + VMCI_FLAG_ANYCID_DG_HND, + vmci_transport_recv_stream_cb, + NULL, + &vmci_transport_stream_handle); + if (err < VMCI_SUCCESS) { + pr_err("Unable to create datagram handle. (%d)\n", err); + return vmci_transport_error_to_vsock_error(err); + } + + err = vmci_event_subscribe(VMCI_EVENT_QP_RESUMED, + vmci_transport_qp_resumed_cb, + NULL, &vmci_transport_qp_resumed_sub_id); + if (err < VMCI_SUCCESS) { + pr_err("Unable to subscribe to resumed event. (%d)\n", err); + err = vmci_transport_error_to_vsock_error(err); + vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID; + goto err_destroy_stream_handle; + } + + err = vsock_core_init(&vmci_transport); + if (err < 0) + goto err_unsubscribe; + + return 0; + +err_unsubscribe: + vmci_event_unsubscribe(vmci_transport_qp_resumed_sub_id); +err_destroy_stream_handle: + vmci_datagram_destroy_handle(vmci_transport_stream_handle); + return err; +} +module_init(vmci_transport_init); + +static void __exit vmci_transport_exit(void) +{ + if (!vmci_handle_is_invalid(vmci_transport_stream_handle)) { + if (vmci_datagram_destroy_handle( + vmci_transport_stream_handle) != VMCI_SUCCESS) + pr_err("Couldn't destroy datagram handle\n"); + vmci_transport_stream_handle = VMCI_INVALID_HANDLE; + } + + if (vmci_transport_qp_resumed_sub_id != VMCI_INVALID_ID) { + vmci_event_unsubscribe(vmci_transport_qp_resumed_sub_id); + vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID; + } + + vsock_core_exit(); +} +module_exit(vmci_transport_exit); + +MODULE_AUTHOR("VMware, Inc."); +MODULE_DESCRIPTION("VMCI transport for Virtual Sockets"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("vmware_vsock"); +MODULE_ALIAS_NETPROTO(PF_VSOCK); diff --git a/net/vmw_vsock/vmci_transport.h b/net/vmw_vsock/vmci_transport.h new file mode 100644 index 000000000000..1bf991803ec0 --- /dev/null +++ b/net/vmw_vsock/vmci_transport.h @@ -0,0 +1,139 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 _VMCI_TRANSPORT_H_ +#define _VMCI_TRANSPORT_H_ + +#include +#include + +#include "vsock_addr.h" +#include "af_vsock.h" + +/* If the packet format changes in a release then this should change too. */ +#define VMCI_TRANSPORT_PACKET_VERSION 1 + +/* The resource ID on which control packets are sent. */ +#define VMCI_TRANSPORT_PACKET_RID 1 + +#define VSOCK_PROTO_INVALID 0 +#define VSOCK_PROTO_PKT_ON_NOTIFY (1 << 0) +#define VSOCK_PROTO_ALL_SUPPORTED (VSOCK_PROTO_PKT_ON_NOTIFY) + +#define vmci_trans(_vsk) ((struct vmci_transport *)((_vsk)->trans)) + +enum vmci_transport_packet_type { + VMCI_TRANSPORT_PACKET_TYPE_INVALID = 0, + VMCI_TRANSPORT_PACKET_TYPE_REQUEST, + VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE, + VMCI_TRANSPORT_PACKET_TYPE_OFFER, + VMCI_TRANSPORT_PACKET_TYPE_ATTACH, + VMCI_TRANSPORT_PACKET_TYPE_WROTE, + VMCI_TRANSPORT_PACKET_TYPE_READ, + VMCI_TRANSPORT_PACKET_TYPE_RST, + VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN, + VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE, + VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ, + VMCI_TRANSPORT_PACKET_TYPE_REQUEST2, + VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2, + VMCI_TRANSPORT_PACKET_TYPE_MAX +}; + +struct vmci_transport_waiting_info { + u64 generation; + u64 offset; +}; + +/* Control packet type for STREAM sockets. DGRAMs have no control packets nor + * special packet header for data packets, they are just raw VMCI DGRAM + * messages. For STREAMs, control packets are sent over the control channel + * while data is written and read directly from queue pairs with no packet + * format. + */ +struct vmci_transport_packet { + struct vmci_datagram dg; + u8 version; + u8 type; + u16 proto; + u32 src_port; + u32 dst_port; + u32 _reserved2; + union { + u64 size; + u64 mode; + struct vmci_handle handle; + struct vmci_transport_waiting_info wait; + } u; +}; + +struct vmci_transport_notify_pkt { + u64 write_notify_window; + u64 write_notify_min_window; + bool peer_waiting_read; + bool peer_waiting_write; + bool peer_waiting_write_detected; + bool sent_waiting_read; + bool sent_waiting_write; + struct vmci_transport_waiting_info peer_waiting_read_info; + struct vmci_transport_waiting_info peer_waiting_write_info; + u64 produce_q_generation; + u64 consume_q_generation; +}; + +struct vmci_transport_notify_pkt_q_state { + u64 write_notify_window; + u64 write_notify_min_window; + bool peer_waiting_write; + bool peer_waiting_write_detected; +}; + +union vmci_transport_notify { + struct vmci_transport_notify_pkt pkt; + struct vmci_transport_notify_pkt_q_state pkt_q_state; +}; + +/* Our transport-specific data. */ +struct vmci_transport { + /* For DGRAMs. */ + struct vmci_handle dg_handle; + /* For STREAMs. */ + struct vmci_handle qp_handle; + struct vmci_qp *qpair; + u64 produce_size; + u64 consume_size; + u64 queue_pair_size; + u64 queue_pair_min_size; + u64 queue_pair_max_size; + u32 attach_sub_id; + u32 detach_sub_id; + union vmci_transport_notify notify; + struct vmci_transport_notify_ops *notify_ops; +}; + +int vmci_transport_register(void); +void vmci_transport_unregister(void); + +int vmci_transport_send_wrote_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src); +int vmci_transport_send_read_bh(struct sockaddr_vm *dst, + struct sockaddr_vm *src); +int vmci_transport_send_wrote(struct sock *sk); +int vmci_transport_send_read(struct sock *sk); +int vmci_transport_send_waiting_write(struct sock *sk, + struct vmci_transport_waiting_info *wait); +int vmci_transport_send_waiting_read(struct sock *sk, + struct vmci_transport_waiting_info *wait); + +#endif diff --git a/net/vmw_vsock/vmci_transport_notify.c b/net/vmw_vsock/vmci_transport_notify.c new file mode 100644 index 000000000000..9a730744e7bc --- /dev/null +++ b/net/vmw_vsock/vmci_transport_notify.c @@ -0,0 +1,680 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2009-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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. + */ + +#include +#include +#include +#include + +#include "vmci_transport_notify.h" + +#define PKT_FIELD(vsk, field_name) (vmci_trans(vsk)->notify.pkt.field_name) + +static bool vmci_transport_notify_waiting_write(struct vsock_sock *vsk) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + bool retval; + u64 notify_limit; + + if (!PKT_FIELD(vsk, peer_waiting_write)) + return false; + +#ifdef VSOCK_OPTIMIZATION_FLOW_CONTROL + /* When the sender blocks, we take that as a sign that the sender is + * faster than the receiver. To reduce the transmit rate of the sender, + * we delay the sending of the read notification by decreasing the + * write_notify_window. The notification is delayed until the number of + * bytes used in the queue drops below the write_notify_window. + */ + + if (!PKT_FIELD(vsk, peer_waiting_write_detected)) { + PKT_FIELD(vsk, peer_waiting_write_detected) = true; + if (PKT_FIELD(vsk, write_notify_window) < PAGE_SIZE) { + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + } else { + PKT_FIELD(vsk, write_notify_window) -= PAGE_SIZE; + if (PKT_FIELD(vsk, write_notify_window) < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + + } + } + notify_limit = vmci_trans(vsk)->consume_size - + PKT_FIELD(vsk, write_notify_window); +#else + notify_limit = 0; +#endif + + /* For now we ignore the wait information and just see if the free + * space exceeds the notify limit. Note that improving this function + * to be more intelligent will not require a protocol change and will + * retain compatibility between endpoints with mixed versions of this + * function. + * + * The notify_limit is used to delay notifications in the case where + * flow control is enabled. Below the test is expressed in terms of + * free space in the queue: if free_space > ConsumeSize - + * write_notify_window then notify An alternate way of expressing this + * is to rewrite the expression to use the data ready in the receive + * queue: if write_notify_window > bufferReady then notify as + * free_space == ConsumeSize - bufferReady. + */ + retval = vmci_qpair_consume_free_space(vmci_trans(vsk)->qpair) > + notify_limit; +#ifdef VSOCK_OPTIMIZATION_FLOW_CONTROL + if (retval) { + /* + * Once we notify the peer, we reset the detected flag so the + * next wait will again cause a decrease in the window size. + */ + + PKT_FIELD(vsk, peer_waiting_write_detected) = false; + } +#endif + return retval; +#else + return true; +#endif +} + +static bool vmci_transport_notify_waiting_read(struct vsock_sock *vsk) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + if (!PKT_FIELD(vsk, peer_waiting_read)) + return false; + + /* For now we ignore the wait information and just see if there is any + * data for our peer to read. Note that improving this function to be + * more intelligent will not require a protocol change and will retain + * compatibility between endpoints with mixed versions of this + * function. + */ + return vmci_qpair_produce_buf_ready(vmci_trans(vsk)->qpair) > 0; +#else + return true; +#endif +} + +static void +vmci_transport_handle_waiting_read(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, + struct sockaddr_vm *src) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk; + + vsk = vsock_sk(sk); + + PKT_FIELD(vsk, peer_waiting_read) = true; + memcpy(&PKT_FIELD(vsk, peer_waiting_read_info), &pkt->u.wait, + sizeof(PKT_FIELD(vsk, peer_waiting_read_info))); + + if (vmci_transport_notify_waiting_read(vsk)) { + bool sent; + + if (bottom_half) + sent = vmci_transport_send_wrote_bh(dst, src) > 0; + else + sent = vmci_transport_send_wrote(sk) > 0; + + if (sent) + PKT_FIELD(vsk, peer_waiting_read) = false; + } +#endif +} + +static void +vmci_transport_handle_waiting_write(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, + struct sockaddr_vm *src) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk; + + vsk = vsock_sk(sk); + + PKT_FIELD(vsk, peer_waiting_write) = true; + memcpy(&PKT_FIELD(vsk, peer_waiting_write_info), &pkt->u.wait, + sizeof(PKT_FIELD(vsk, peer_waiting_write_info))); + + if (vmci_transport_notify_waiting_write(vsk)) { + bool sent; + + if (bottom_half) + sent = vmci_transport_send_read_bh(dst, src) > 0; + else + sent = vmci_transport_send_read(sk) > 0; + + if (sent) + PKT_FIELD(vsk, peer_waiting_write) = false; + } +#endif +} + +static void +vmci_transport_handle_read(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, struct sockaddr_vm *src) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk; + + vsk = vsock_sk(sk); + PKT_FIELD(vsk, sent_waiting_write) = false; +#endif + + sk->sk_write_space(sk); +} + +static bool send_waiting_read(struct sock *sk, u64 room_needed) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk; + struct vmci_transport_waiting_info waiting_info; + u64 tail; + u64 head; + u64 room_left; + bool ret; + + vsk = vsock_sk(sk); + + if (PKT_FIELD(vsk, sent_waiting_read)) + return true; + + if (PKT_FIELD(vsk, write_notify_window) < + vmci_trans(vsk)->consume_size) + PKT_FIELD(vsk, write_notify_window) = + min(PKT_FIELD(vsk, write_notify_window) + PAGE_SIZE, + vmci_trans(vsk)->consume_size); + + vmci_qpair_get_consume_indexes(vmci_trans(vsk)->qpair, &tail, &head); + room_left = vmci_trans(vsk)->consume_size - head; + if (room_needed >= room_left) { + waiting_info.offset = room_needed - room_left; + waiting_info.generation = + PKT_FIELD(vsk, consume_q_generation) + 1; + } else { + waiting_info.offset = head + room_needed; + waiting_info.generation = PKT_FIELD(vsk, consume_q_generation); + } + + ret = vmci_transport_send_waiting_read(sk, &waiting_info) > 0; + if (ret) + PKT_FIELD(vsk, sent_waiting_read) = true; + + return ret; +#else + return true; +#endif +} + +static bool send_waiting_write(struct sock *sk, u64 room_needed) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk; + struct vmci_transport_waiting_info waiting_info; + u64 tail; + u64 head; + u64 room_left; + bool ret; + + vsk = vsock_sk(sk); + + if (PKT_FIELD(vsk, sent_waiting_write)) + return true; + + vmci_qpair_get_produce_indexes(vmci_trans(vsk)->qpair, &tail, &head); + room_left = vmci_trans(vsk)->produce_size - tail; + if (room_needed + 1 >= room_left) { + /* Wraps around to current generation. */ + waiting_info.offset = room_needed + 1 - room_left; + waiting_info.generation = PKT_FIELD(vsk, produce_q_generation); + } else { + waiting_info.offset = tail + room_needed + 1; + waiting_info.generation = + PKT_FIELD(vsk, produce_q_generation) - 1; + } + + ret = vmci_transport_send_waiting_write(sk, &waiting_info) > 0; + if (ret) + PKT_FIELD(vsk, sent_waiting_write) = true; + + return ret; +#else + return true; +#endif +} + +static int vmci_transport_send_read_notification(struct sock *sk) +{ + struct vsock_sock *vsk; + bool sent_read; + unsigned int retries; + int err; + + vsk = vsock_sk(sk); + sent_read = false; + retries = 0; + err = 0; + + if (vmci_transport_notify_waiting_write(vsk)) { + /* Notify the peer that we have read, retrying the send on + * failure up to our maximum value. XXX For now we just log + * the failure, but later we should schedule a work item to + * handle the resend until it succeeds. That would require + * keeping track of work items in the vsk and cleaning them up + * upon socket close. + */ + while (!(vsk->peer_shutdown & RCV_SHUTDOWN) && + !sent_read && + retries < VMCI_TRANSPORT_MAX_DGRAM_RESENDS) { + err = vmci_transport_send_read(sk); + if (err >= 0) + sent_read = true; + + retries++; + } + + if (retries >= VMCI_TRANSPORT_MAX_DGRAM_RESENDS) + pr_err("%p unable to send read notify to peer\n", sk); + else +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + PKT_FIELD(vsk, peer_waiting_write) = false; +#endif + + } + return err; +} + +static void +vmci_transport_handle_wrote(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, struct sockaddr_vm *src) +{ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + struct vsock_sock *vsk = vsock_sk(sk); + PKT_FIELD(vsk, sent_waiting_read) = false; +#endif + sk->sk_data_ready(sk, 0); +} + +static void vmci_transport_notify_pkt_socket_init(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = PAGE_SIZE; + PKT_FIELD(vsk, write_notify_min_window) = PAGE_SIZE; + PKT_FIELD(vsk, peer_waiting_read) = false; + PKT_FIELD(vsk, peer_waiting_write) = false; + PKT_FIELD(vsk, peer_waiting_write_detected) = false; + PKT_FIELD(vsk, sent_waiting_read) = false; + PKT_FIELD(vsk, sent_waiting_write) = false; + PKT_FIELD(vsk, produce_q_generation) = 0; + PKT_FIELD(vsk, consume_q_generation) = 0; + + memset(&PKT_FIELD(vsk, peer_waiting_read_info), 0, + sizeof(PKT_FIELD(vsk, peer_waiting_read_info))); + memset(&PKT_FIELD(vsk, peer_waiting_write_info), 0, + sizeof(PKT_FIELD(vsk, peer_waiting_write_info))); +} + +static void vmci_transport_notify_pkt_socket_destruct(struct vsock_sock *vsk) +{ +} + +static int +vmci_transport_notify_pkt_poll_in(struct sock *sk, + size_t target, bool *data_ready_now) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + if (vsock_stream_has_data(vsk)) { + *data_ready_now = true; + } else { + /* We can't read right now because there is nothing in the + * queue. Ask for notifications when there is something to + * read. + */ + if (sk->sk_state == SS_CONNECTED) { + if (!send_waiting_read(sk, 1)) + return -1; + + } + *data_ready_now = false; + } + + return 0; +} + +static int +vmci_transport_notify_pkt_poll_out(struct sock *sk, + size_t target, bool *space_avail_now) +{ + s64 produce_q_free_space; + struct vsock_sock *vsk = vsock_sk(sk); + + produce_q_free_space = vsock_stream_has_space(vsk); + if (produce_q_free_space > 0) { + *space_avail_now = true; + return 0; + } else if (produce_q_free_space == 0) { + /* This is a connected socket but we can't currently send data. + * Notify the peer that we are waiting if the queue is full. We + * only send a waiting write if the queue is full because + * otherwise we end up in an infinite WAITING_WRITE, READ, + * WAITING_WRITE, READ, etc. loop. Treat failing to send the + * notification as a socket error, passing that back through + * the mask. + */ + if (!send_waiting_write(sk, 1)) + return -1; + + *space_avail_now = false; + } + + return 0; +} + +static int +vmci_transport_notify_pkt_recv_init( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + struct vsock_sock *vsk = vsock_sk(sk); + +#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY + data->consume_head = 0; + data->produce_tail = 0; +#ifdef VSOCK_OPTIMIZATION_FLOW_CONTROL + data->notify_on_block = false; + + if (PKT_FIELD(vsk, write_notify_min_window) < target + 1) { + PKT_FIELD(vsk, write_notify_min_window) = target + 1; + if (PKT_FIELD(vsk, write_notify_window) < + PKT_FIELD(vsk, write_notify_min_window)) { + /* If the current window is smaller than the new + * minimal window size, we need to reevaluate whether + * we need to notify the sender. If the number of ready + * bytes are smaller than the new window, we need to + * send a notification to the sender before we block. + */ + + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + data->notify_on_block = true; + } + } +#endif +#endif + + return 0; +} + +static int +vmci_transport_notify_pkt_recv_pre_block( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + int err = 0; + + /* Notify our peer that we are waiting for data to read. */ + if (!send_waiting_read(sk, target)) { + err = -EHOSTUNREACH; + return err; + } +#ifdef VSOCK_OPTIMIZATION_FLOW_CONTROL + if (data->notify_on_block) { + err = vmci_transport_send_read_notification(sk); + if (err < 0) + return err; + + data->notify_on_block = false; + } +#endif + + return err; +} + +static int +vmci_transport_notify_pkt_recv_pre_dequeue( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + /* Now consume up to len bytes from the queue. Note that since we have + * the socket locked we should copy at least ready bytes. + */ +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + vmci_qpair_get_consume_indexes(vmci_trans(vsk)->qpair, + &data->produce_tail, + &data->consume_head); +#endif + + return 0; +} + +static int +vmci_transport_notify_pkt_recv_post_dequeue( + struct sock *sk, + size_t target, + ssize_t copied, + bool data_read, + struct vmci_transport_recv_notify_data *data) +{ + struct vsock_sock *vsk; + int err; + + vsk = vsock_sk(sk); + err = 0; + + if (data_read) { +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + /* Detect a wrap-around to maintain queue generation. Note + * that this is safe since we hold the socket lock across the + * two queue pair operations. + */ + if (copied >= + vmci_trans(vsk)->consume_size - data->consume_head) + PKT_FIELD(vsk, consume_q_generation)++; +#endif + + err = vmci_transport_send_read_notification(sk); + if (err < 0) + return err; + + } + return err; +} + +static int +vmci_transport_notify_pkt_send_init( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ +#ifdef VSOCK_OPTIMIZATION_WAITING_NOTIFY + data->consume_head = 0; + data->produce_tail = 0; +#endif + + return 0; +} + +static int +vmci_transport_notify_pkt_send_pre_block( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ + /* Notify our peer that we are waiting for room to write. */ + if (!send_waiting_write(sk, 1)) + return -EHOSTUNREACH; + + return 0; +} + +static int +vmci_transport_notify_pkt_send_pre_enqueue( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ + struct vsock_sock *vsk = vsock_sk(sk); + +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + vmci_qpair_get_produce_indexes(vmci_trans(vsk)->qpair, + &data->produce_tail, + &data->consume_head); +#endif + + return 0; +} + +static int +vmci_transport_notify_pkt_send_post_enqueue( + struct sock *sk, + ssize_t written, + struct vmci_transport_send_notify_data *data) +{ + int err = 0; + struct vsock_sock *vsk; + bool sent_wrote = false; + int retries = 0; + + vsk = vsock_sk(sk); + +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + /* Detect a wrap-around to maintain queue generation. Note that this + * is safe since we hold the socket lock across the two queue pair + * operations. + */ + if (written >= vmci_trans(vsk)->produce_size - data->produce_tail) + PKT_FIELD(vsk, produce_q_generation)++; + +#endif + + if (vmci_transport_notify_waiting_read(vsk)) { + /* Notify the peer that we have written, retrying the send on + * failure up to our maximum value. See the XXX comment for the + * corresponding piece of code in StreamRecvmsg() for potential + * improvements. + */ + while (!(vsk->peer_shutdown & RCV_SHUTDOWN) && + !sent_wrote && + retries < VMCI_TRANSPORT_MAX_DGRAM_RESENDS) { + err = vmci_transport_send_wrote(sk); + if (err >= 0) + sent_wrote = true; + + retries++; + } + + if (retries >= VMCI_TRANSPORT_MAX_DGRAM_RESENDS) { + pr_err("%p unable to send wrote notify to peer\n", sk); + return err; + } else { +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) + PKT_FIELD(vsk, peer_waiting_read) = false; +#endif + } + } + return err; +} + +static void +vmci_transport_notify_pkt_handle_pkt( + struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, + struct sockaddr_vm *src, bool *pkt_processed) +{ + bool processed = false; + + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_WROTE: + vmci_transport_handle_wrote(sk, pkt, bottom_half, dst, src); + processed = true; + break; + case VMCI_TRANSPORT_PACKET_TYPE_READ: + vmci_transport_handle_read(sk, pkt, bottom_half, dst, src); + processed = true; + break; + case VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE: + vmci_transport_handle_waiting_write(sk, pkt, bottom_half, + dst, src); + processed = true; + break; + + case VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ: + vmci_transport_handle_waiting_read(sk, pkt, bottom_half, + dst, src); + processed = true; + break; + } + + if (pkt_processed) + *pkt_processed = processed; +} + +static void vmci_transport_notify_pkt_process_request(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = vmci_trans(vsk)->consume_size; + if (vmci_trans(vsk)->consume_size < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_min_window) = + vmci_trans(vsk)->consume_size; +} + +static void vmci_transport_notify_pkt_process_negotiate(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = vmci_trans(vsk)->consume_size; + if (vmci_trans(vsk)->consume_size < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_min_window) = + vmci_trans(vsk)->consume_size; +} + +/* Socket control packet based operations. */ +struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops = { + vmci_transport_notify_pkt_socket_init, + vmci_transport_notify_pkt_socket_destruct, + vmci_transport_notify_pkt_poll_in, + vmci_transport_notify_pkt_poll_out, + vmci_transport_notify_pkt_handle_pkt, + vmci_transport_notify_pkt_recv_init, + vmci_transport_notify_pkt_recv_pre_block, + vmci_transport_notify_pkt_recv_pre_dequeue, + vmci_transport_notify_pkt_recv_post_dequeue, + vmci_transport_notify_pkt_send_init, + vmci_transport_notify_pkt_send_pre_block, + vmci_transport_notify_pkt_send_pre_enqueue, + vmci_transport_notify_pkt_send_post_enqueue, + vmci_transport_notify_pkt_process_request, + vmci_transport_notify_pkt_process_negotiate, +}; diff --git a/net/vmw_vsock/vmci_transport_notify.h b/net/vmw_vsock/vmci_transport_notify.h new file mode 100644 index 000000000000..7df793249b6c --- /dev/null +++ b/net/vmw_vsock/vmci_transport_notify.h @@ -0,0 +1,83 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2009-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 __VMCI_TRANSPORT_NOTIFY_H__ +#define __VMCI_TRANSPORT_NOTIFY_H__ + +#include +#include +#include +#include + +#include "vmci_transport.h" + +/* Comment this out to compare with old protocol. */ +#define VSOCK_OPTIMIZATION_WAITING_NOTIFY 1 +#if defined(VSOCK_OPTIMIZATION_WAITING_NOTIFY) +/* Comment this out to remove flow control for "new" protocol */ +#define VSOCK_OPTIMIZATION_FLOW_CONTROL 1 +#endif + +#define VMCI_TRANSPORT_MAX_DGRAM_RESENDS 10 + +struct vmci_transport_recv_notify_data { + u64 consume_head; + u64 produce_tail; + bool notify_on_block; +}; + +struct vmci_transport_send_notify_data { + u64 consume_head; + u64 produce_tail; +}; + +/* Socket notification callbacks. */ +struct vmci_transport_notify_ops { + void (*socket_init) (struct sock *sk); + void (*socket_destruct) (struct vsock_sock *vsk); + int (*poll_in) (struct sock *sk, size_t target, + bool *data_ready_now); + int (*poll_out) (struct sock *sk, size_t target, + bool *space_avail_now); + void (*handle_notify_pkt) (struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, struct sockaddr_vm *dst, + struct sockaddr_vm *src, + bool *pkt_processed); + int (*recv_init) (struct sock *sk, size_t target, + struct vmci_transport_recv_notify_data *data); + int (*recv_pre_block) (struct sock *sk, size_t target, + struct vmci_transport_recv_notify_data *data); + int (*recv_pre_dequeue) (struct sock *sk, size_t target, + struct vmci_transport_recv_notify_data *data); + int (*recv_post_dequeue) (struct sock *sk, size_t target, + ssize_t copied, bool data_read, + struct vmci_transport_recv_notify_data *data); + int (*send_init) (struct sock *sk, + struct vmci_transport_send_notify_data *data); + int (*send_pre_block) (struct sock *sk, + struct vmci_transport_send_notify_data *data); + int (*send_pre_enqueue) (struct sock *sk, + struct vmci_transport_send_notify_data *data); + int (*send_post_enqueue) (struct sock *sk, ssize_t written, + struct vmci_transport_send_notify_data *data); + void (*process_request) (struct sock *sk); + void (*process_negotiate) (struct sock *sk); +}; + +extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops; +extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops; + +#endif /* __VMCI_TRANSPORT_NOTIFY_H__ */ diff --git a/net/vmw_vsock/vmci_transport_notify_qstate.c b/net/vmw_vsock/vmci_transport_notify_qstate.c new file mode 100644 index 000000000000..622bd7aa1016 --- /dev/null +++ b/net/vmw_vsock/vmci_transport_notify_qstate.c @@ -0,0 +1,438 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2009-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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. + */ + +#include +#include +#include +#include + +#include "vmci_transport_notify.h" + +#define PKT_FIELD(vsk, field_name) \ + (vmci_trans(vsk)->notify.pkt_q_state.field_name) + +static bool vmci_transport_notify_waiting_write(struct vsock_sock *vsk) +{ + bool retval; + u64 notify_limit; + + if (!PKT_FIELD(vsk, peer_waiting_write)) + return false; + + /* When the sender blocks, we take that as a sign that the sender is + * faster than the receiver. To reduce the transmit rate of the sender, + * we delay the sending of the read notification by decreasing the + * write_notify_window. The notification is delayed until the number of + * bytes used in the queue drops below the write_notify_window. + */ + + if (!PKT_FIELD(vsk, peer_waiting_write_detected)) { + PKT_FIELD(vsk, peer_waiting_write_detected) = true; + if (PKT_FIELD(vsk, write_notify_window) < PAGE_SIZE) { + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + } else { + PKT_FIELD(vsk, write_notify_window) -= PAGE_SIZE; + if (PKT_FIELD(vsk, write_notify_window) < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + + } + } + notify_limit = vmci_trans(vsk)->consume_size - + PKT_FIELD(vsk, write_notify_window); + + /* The notify_limit is used to delay notifications in the case where + * flow control is enabled. Below the test is expressed in terms of + * free space in the queue: if free_space > ConsumeSize - + * write_notify_window then notify An alternate way of expressing this + * is to rewrite the expression to use the data ready in the receive + * queue: if write_notify_window > bufferReady then notify as + * free_space == ConsumeSize - bufferReady. + */ + + retval = vmci_qpair_consume_free_space(vmci_trans(vsk)->qpair) > + notify_limit; + + if (retval) { + /* Once we notify the peer, we reset the detected flag so the + * next wait will again cause a decrease in the window size. + */ + + PKT_FIELD(vsk, peer_waiting_write_detected) = false; + } + return retval; +} + +static void +vmci_transport_handle_read(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, struct sockaddr_vm *src) +{ + sk->sk_write_space(sk); +} + +static void +vmci_transport_handle_wrote(struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, struct sockaddr_vm *src) +{ + sk->sk_data_ready(sk, 0); +} + +static void vsock_block_update_write_window(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + if (PKT_FIELD(vsk, write_notify_window) < vmci_trans(vsk)->consume_size) + PKT_FIELD(vsk, write_notify_window) = + min(PKT_FIELD(vsk, write_notify_window) + PAGE_SIZE, + vmci_trans(vsk)->consume_size); +} + +static int vmci_transport_send_read_notification(struct sock *sk) +{ + struct vsock_sock *vsk; + bool sent_read; + unsigned int retries; + int err; + + vsk = vsock_sk(sk); + sent_read = false; + retries = 0; + err = 0; + + if (vmci_transport_notify_waiting_write(vsk)) { + /* Notify the peer that we have read, retrying the send on + * failure up to our maximum value. XXX For now we just log + * the failure, but later we should schedule a work item to + * handle the resend until it succeeds. That would require + * keeping track of work items in the vsk and cleaning them up + * upon socket close. + */ + while (!(vsk->peer_shutdown & RCV_SHUTDOWN) && + !sent_read && + retries < VMCI_TRANSPORT_MAX_DGRAM_RESENDS) { + err = vmci_transport_send_read(sk); + if (err >= 0) + sent_read = true; + + retries++; + } + + if (retries >= VMCI_TRANSPORT_MAX_DGRAM_RESENDS && !sent_read) + pr_err("%p unable to send read notification to peer\n", + sk); + else + PKT_FIELD(vsk, peer_waiting_write) = false; + + } + return err; +} + +static void vmci_transport_notify_pkt_socket_init(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = PAGE_SIZE; + PKT_FIELD(vsk, write_notify_min_window) = PAGE_SIZE; + PKT_FIELD(vsk, peer_waiting_write) = false; + PKT_FIELD(vsk, peer_waiting_write_detected) = false; +} + +static void vmci_transport_notify_pkt_socket_destruct(struct vsock_sock *vsk) +{ + PKT_FIELD(vsk, write_notify_window) = PAGE_SIZE; + PKT_FIELD(vsk, write_notify_min_window) = PAGE_SIZE; + PKT_FIELD(vsk, peer_waiting_write) = false; + PKT_FIELD(vsk, peer_waiting_write_detected) = false; +} + +static int +vmci_transport_notify_pkt_poll_in(struct sock *sk, + size_t target, bool *data_ready_now) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + if (vsock_stream_has_data(vsk)) { + *data_ready_now = true; + } else { + /* We can't read right now because there is nothing in the + * queue. Ask for notifications when there is something to + * read. + */ + if (sk->sk_state == SS_CONNECTED) + vsock_block_update_write_window(sk); + *data_ready_now = false; + } + + return 0; +} + +static int +vmci_transport_notify_pkt_poll_out(struct sock *sk, + size_t target, bool *space_avail_now) +{ + s64 produce_q_free_space; + struct vsock_sock *vsk = vsock_sk(sk); + + produce_q_free_space = vsock_stream_has_space(vsk); + if (produce_q_free_space > 0) { + *space_avail_now = true; + return 0; + } else if (produce_q_free_space == 0) { + /* This is a connected socket but we can't currently send data. + * Nothing else to do. + */ + *space_avail_now = false; + } + + return 0; +} + +static int +vmci_transport_notify_pkt_recv_init( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + data->consume_head = 0; + data->produce_tail = 0; + data->notify_on_block = false; + + if (PKT_FIELD(vsk, write_notify_min_window) < target + 1) { + PKT_FIELD(vsk, write_notify_min_window) = target + 1; + if (PKT_FIELD(vsk, write_notify_window) < + PKT_FIELD(vsk, write_notify_min_window)) { + /* If the current window is smaller than the new + * minimal window size, we need to reevaluate whether + * we need to notify the sender. If the number of ready + * bytes are smaller than the new window, we need to + * send a notification to the sender before we block. + */ + + PKT_FIELD(vsk, write_notify_window) = + PKT_FIELD(vsk, write_notify_min_window); + data->notify_on_block = true; + } + } + + return 0; +} + +static int +vmci_transport_notify_pkt_recv_pre_block( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + int err = 0; + + vsock_block_update_write_window(sk); + + if (data->notify_on_block) { + err = vmci_transport_send_read_notification(sk); + if (err < 0) + return err; + data->notify_on_block = false; + } + + return err; +} + +static int +vmci_transport_notify_pkt_recv_post_dequeue( + struct sock *sk, + size_t target, + ssize_t copied, + bool data_read, + struct vmci_transport_recv_notify_data *data) +{ + struct vsock_sock *vsk; + int err; + bool was_full = false; + u64 free_space; + + vsk = vsock_sk(sk); + err = 0; + + if (data_read) { + smp_mb(); + + free_space = + vmci_qpair_consume_free_space(vmci_trans(vsk)->qpair); + was_full = free_space == copied; + + if (was_full) + PKT_FIELD(vsk, peer_waiting_write) = true; + + err = vmci_transport_send_read_notification(sk); + if (err < 0) + return err; + + /* See the comment in + * vmci_transport_notify_pkt_send_post_enqueue(). + */ + sk->sk_data_ready(sk, 0); + } + + return err; +} + +static int +vmci_transport_notify_pkt_send_init( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ + data->consume_head = 0; + data->produce_tail = 0; + + return 0; +} + +static int +vmci_transport_notify_pkt_send_post_enqueue( + struct sock *sk, + ssize_t written, + struct vmci_transport_send_notify_data *data) +{ + int err = 0; + struct vsock_sock *vsk; + bool sent_wrote = false; + bool was_empty; + int retries = 0; + + vsk = vsock_sk(sk); + + smp_mb(); + + was_empty = + vmci_qpair_produce_buf_ready(vmci_trans(vsk)->qpair) == written; + if (was_empty) { + while (!(vsk->peer_shutdown & RCV_SHUTDOWN) && + !sent_wrote && + retries < VMCI_TRANSPORT_MAX_DGRAM_RESENDS) { + err = vmci_transport_send_wrote(sk); + if (err >= 0) + sent_wrote = true; + + retries++; + } + } + + if (retries >= VMCI_TRANSPORT_MAX_DGRAM_RESENDS && !sent_wrote) { + pr_err("%p unable to send wrote notification to peer\n", + sk); + return err; + } + + return err; +} + +static void +vmci_transport_notify_pkt_handle_pkt( + struct sock *sk, + struct vmci_transport_packet *pkt, + bool bottom_half, + struct sockaddr_vm *dst, + struct sockaddr_vm *src, bool *pkt_processed) +{ + bool processed = false; + + switch (pkt->type) { + case VMCI_TRANSPORT_PACKET_TYPE_WROTE: + vmci_transport_handle_wrote(sk, pkt, bottom_half, dst, src); + processed = true; + break; + case VMCI_TRANSPORT_PACKET_TYPE_READ: + vmci_transport_handle_read(sk, pkt, bottom_half, dst, src); + processed = true; + break; + } + + if (pkt_processed) + *pkt_processed = processed; +} + +static void vmci_transport_notify_pkt_process_request(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = vmci_trans(vsk)->consume_size; + if (vmci_trans(vsk)->consume_size < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_min_window) = + vmci_trans(vsk)->consume_size; +} + +static void vmci_transport_notify_pkt_process_negotiate(struct sock *sk) +{ + struct vsock_sock *vsk = vsock_sk(sk); + + PKT_FIELD(vsk, write_notify_window) = vmci_trans(vsk)->consume_size; + if (vmci_trans(vsk)->consume_size < + PKT_FIELD(vsk, write_notify_min_window)) + PKT_FIELD(vsk, write_notify_min_window) = + vmci_trans(vsk)->consume_size; +} + +static int +vmci_transport_notify_pkt_recv_pre_dequeue( + struct sock *sk, + size_t target, + struct vmci_transport_recv_notify_data *data) +{ + return 0; /* NOP for QState. */ +} + +static int +vmci_transport_notify_pkt_send_pre_block( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ + return 0; /* NOP for QState. */ +} + +static int +vmci_transport_notify_pkt_send_pre_enqueue( + struct sock *sk, + struct vmci_transport_send_notify_data *data) +{ + return 0; /* NOP for QState. */ +} + +/* Socket always on control packet based operations. */ +struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops = { + vmci_transport_notify_pkt_socket_init, + vmci_transport_notify_pkt_socket_destruct, + vmci_transport_notify_pkt_poll_in, + vmci_transport_notify_pkt_poll_out, + vmci_transport_notify_pkt_handle_pkt, + vmci_transport_notify_pkt_recv_init, + vmci_transport_notify_pkt_recv_pre_block, + vmci_transport_notify_pkt_recv_pre_dequeue, + vmci_transport_notify_pkt_recv_post_dequeue, + vmci_transport_notify_pkt_send_init, + vmci_transport_notify_pkt_send_pre_block, + vmci_transport_notify_pkt_send_pre_enqueue, + vmci_transport_notify_pkt_send_post_enqueue, + vmci_transport_notify_pkt_process_request, + vmci_transport_notify_pkt_process_negotiate, +}; diff --git a/net/vmw_vsock/vsock_addr.c b/net/vmw_vsock/vsock_addr.c new file mode 100644 index 000000000000..b7df1aea7c59 --- /dev/null +++ b/net/vmw_vsock/vsock_addr.c @@ -0,0 +1,86 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2012 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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. + */ + +#include +#include +#include +#include + +#include "vsock_addr.h" + +void vsock_addr_init(struct sockaddr_vm *addr, u32 cid, u32 port) +{ + memset(addr, 0, sizeof(*addr)); + addr->svm_family = AF_VSOCK; + addr->svm_cid = cid; + addr->svm_port = port; +} +EXPORT_SYMBOL_GPL(vsock_addr_init); + +int vsock_addr_validate(const struct sockaddr_vm *addr) +{ + if (!addr) + return -EFAULT; + + if (addr->svm_family != AF_VSOCK) + return -EAFNOSUPPORT; + + if (addr->svm_zero[0] != 0) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(vsock_addr_validate); + +bool vsock_addr_bound(const struct sockaddr_vm *addr) +{ + return addr->svm_port != VMADDR_PORT_ANY; +} +EXPORT_SYMBOL_GPL(vsock_addr_bound); + +void vsock_addr_unbind(struct sockaddr_vm *addr) +{ + vsock_addr_init(addr, VMADDR_CID_ANY, VMADDR_PORT_ANY); +} +EXPORT_SYMBOL_GPL(vsock_addr_unbind); + +bool vsock_addr_equals_addr(const struct sockaddr_vm *addr, + const struct sockaddr_vm *other) +{ + return addr->svm_cid == other->svm_cid && + addr->svm_port == other->svm_port; +} +EXPORT_SYMBOL_GPL(vsock_addr_equals_addr); + +bool vsock_addr_equals_addr_any(const struct sockaddr_vm *addr, + const struct sockaddr_vm *other) +{ + return (addr->svm_cid == VMADDR_CID_ANY || + other->svm_cid == VMADDR_CID_ANY || + addr->svm_cid == other->svm_cid) && + addr->svm_port == other->svm_port; +} +EXPORT_SYMBOL_GPL(vsock_addr_equals_addr_any); + +int vsock_addr_cast(const struct sockaddr *addr, + size_t len, struct sockaddr_vm **out_addr) +{ + if (len < sizeof(**out_addr)) + return -EFAULT; + + *out_addr = (struct sockaddr_vm *)addr; + return vsock_addr_validate(*out_addr); +} +EXPORT_SYMBOL_GPL(vsock_addr_cast); diff --git a/net/vmw_vsock/vsock_addr.h b/net/vmw_vsock/vsock_addr.h new file mode 100644 index 000000000000..cdfbcefdf843 --- /dev/null +++ b/net/vmw_vsock/vsock_addr.h @@ -0,0 +1,32 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 _VSOCK_ADDR_H_ +#define _VSOCK_ADDR_H_ + +#include + +void vsock_addr_init(struct sockaddr_vm *addr, u32 cid, u32 port); +int vsock_addr_validate(const struct sockaddr_vm *addr); +bool vsock_addr_bound(const struct sockaddr_vm *addr); +void vsock_addr_unbind(struct sockaddr_vm *addr); +bool vsock_addr_equals_addr(const struct sockaddr_vm *addr, + const struct sockaddr_vm *other); +bool vsock_addr_equals_addr_any(const struct sockaddr_vm *addr, + const struct sockaddr_vm *other); +int vsock_addr_cast(const struct sockaddr *addr, size_t len, + struct sockaddr_vm **out_addr); + +#endif diff --git a/net/vmw_vsock/vsock_version.h b/net/vmw_vsock/vsock_version.h new file mode 100644 index 000000000000..4df7f5e2151c --- /dev/null +++ b/net/vmw_vsock/vsock_version.h @@ -0,0 +1,22 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2011-2012 VMware, Inc. All rights reserved. + * + * 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 version 2 and no 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 _VSOCK_VERSION_H_ +#define _VSOCK_VERSION_H_ + +#define VSOCK_DRIVER_VERSION_PARTS { 1, 0, 0, 0 } +#define VSOCK_DRIVER_VERSION_STRING "1.0.0.0-k" + +#endif /* _VSOCK_VERSION_H_ */ -- cgit v1.2.3 From febf018d22347b5df94066bca05d0c11a84e839d Mon Sep 17 00:00:00 2001 From: David Ward Date: Fri, 8 Feb 2013 17:17:06 +0000 Subject: net/802: Implement Multiple Registration Protocol (MRP) Initial implementation of the Multiple Registration Protocol (MRP) from IEEE 802.1Q-2011, based on the existing implementation of the Generic Attribute Registration Protocol (GARP). Signed-off-by: David Ward Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 + include/net/mrp.h | 143 ++++++++ net/802/Kconfig | 3 + net/802/Makefile | 1 + net/802/mrp.c | 895 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1044 insertions(+) create mode 100644 include/net/mrp.h create mode 100644 net/802/mrp.c (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ab2774eb49e8..25bd46f52877 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1290,6 +1290,8 @@ struct net_device { }; /* GARP */ struct garp_port __rcu *garp_port; + /* MRP */ + struct mrp_port __rcu *mrp_port; /* class/net/name entry */ struct device dev; diff --git a/include/net/mrp.h b/include/net/mrp.h new file mode 100644 index 000000000000..4fbf02aa2ec1 --- /dev/null +++ b/include/net/mrp.h @@ -0,0 +1,143 @@ +#ifndef _NET_MRP_H +#define _NET_MRP_H + +#define MRP_END_MARK 0x0 + +struct mrp_pdu_hdr { + u8 version; +}; + +struct mrp_msg_hdr { + u8 attrtype; + u8 attrlen; +}; + +struct mrp_vecattr_hdr { + __be16 lenflags; + unsigned char firstattrvalue[]; +#define MRP_VECATTR_HDR_LEN_MASK cpu_to_be16(0x1FFF) +#define MRP_VECATTR_HDR_FLAG_LA cpu_to_be16(0x2000) +}; + +enum mrp_vecattr_event { + MRP_VECATTR_EVENT_NEW, + MRP_VECATTR_EVENT_JOIN_IN, + MRP_VECATTR_EVENT_IN, + MRP_VECATTR_EVENT_JOIN_MT, + MRP_VECATTR_EVENT_MT, + MRP_VECATTR_EVENT_LV, + __MRP_VECATTR_EVENT_MAX +}; + +struct mrp_skb_cb { + struct mrp_msg_hdr *mh; + struct mrp_vecattr_hdr *vah; + unsigned char attrvalue[]; +}; + +static inline struct mrp_skb_cb *mrp_cb(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct mrp_skb_cb) > + FIELD_SIZEOF(struct sk_buff, cb)); + return (struct mrp_skb_cb *)skb->cb; +} + +enum mrp_applicant_state { + MRP_APPLICANT_INVALID, + MRP_APPLICANT_VO, + MRP_APPLICANT_VP, + MRP_APPLICANT_VN, + MRP_APPLICANT_AN, + MRP_APPLICANT_AA, + MRP_APPLICANT_QA, + MRP_APPLICANT_LA, + MRP_APPLICANT_AO, + MRP_APPLICANT_QO, + MRP_APPLICANT_AP, + MRP_APPLICANT_QP, + __MRP_APPLICANT_MAX +}; +#define MRP_APPLICANT_MAX (__MRP_APPLICANT_MAX - 1) + +enum mrp_event { + MRP_EVENT_NEW, + MRP_EVENT_JOIN, + MRP_EVENT_LV, + MRP_EVENT_TX, + MRP_EVENT_R_NEW, + MRP_EVENT_R_JOIN_IN, + MRP_EVENT_R_IN, + MRP_EVENT_R_JOIN_MT, + MRP_EVENT_R_MT, + MRP_EVENT_R_LV, + MRP_EVENT_R_LA, + MRP_EVENT_REDECLARE, + MRP_EVENT_PERIODIC, + __MRP_EVENT_MAX +}; +#define MRP_EVENT_MAX (__MRP_EVENT_MAX - 1) + +enum mrp_tx_action { + MRP_TX_ACTION_NONE, + MRP_TX_ACTION_S_NEW, + MRP_TX_ACTION_S_JOIN_IN, + MRP_TX_ACTION_S_JOIN_IN_OPTIONAL, + MRP_TX_ACTION_S_IN_OPTIONAL, + MRP_TX_ACTION_S_LV, +}; + +struct mrp_attr { + struct rb_node node; + enum mrp_applicant_state state; + u8 type; + u8 len; + unsigned char value[]; +}; + +enum mrp_applications { + MRP_APPLICATION_MVRP, + __MRP_APPLICATION_MAX +}; +#define MRP_APPLICATION_MAX (__MRP_APPLICATION_MAX - 1) + +struct mrp_application { + enum mrp_applications type; + unsigned int maxattr; + struct packet_type pkttype; + unsigned char group_address[ETH_ALEN]; + u8 version; +}; + +struct mrp_applicant { + struct mrp_application *app; + struct net_device *dev; + struct timer_list join_timer; + + spinlock_t lock; + struct sk_buff_head queue; + struct sk_buff *pdu; + struct rb_root mad; + struct rcu_head rcu; +}; + +struct mrp_port { + struct mrp_applicant __rcu *applicants[MRP_APPLICATION_MAX + 1]; + struct rcu_head rcu; +}; + +extern int mrp_register_application(struct mrp_application *app); +extern void mrp_unregister_application(struct mrp_application *app); + +extern int mrp_init_applicant(struct net_device *dev, + struct mrp_application *app); +extern void mrp_uninit_applicant(struct net_device *dev, + struct mrp_application *app); + +extern int mrp_request_join(const struct net_device *dev, + const struct mrp_application *app, + const void *value, u8 len, u8 type); +extern void mrp_request_leave(const struct net_device *dev, + const struct mrp_application *app, + const void *value, u8 len, u8 type); + +#endif /* _NET_MRP_H */ diff --git a/net/802/Kconfig b/net/802/Kconfig index be33d27c8e69..80d4bf78905d 100644 --- a/net/802/Kconfig +++ b/net/802/Kconfig @@ -5,3 +5,6 @@ config STP config GARP tristate select STP + +config MRP + tristate diff --git a/net/802/Makefile b/net/802/Makefile index a30d6e385aed..37e654d6615e 100644 --- a/net/802/Makefile +++ b/net/802/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o obj-$(CONFIG_ATALK) += p8022.o psnap.o obj-$(CONFIG_STP) += stp.o obj-$(CONFIG_GARP) += garp.o +obj-$(CONFIG_MRP) += mrp.o diff --git a/net/802/mrp.c b/net/802/mrp.c new file mode 100644 index 000000000000..47a9e14c8ba7 --- /dev/null +++ b/net/802/mrp.c @@ -0,0 +1,895 @@ +/* + * IEEE 802.1Q Multiple Registration Protocol (MRP) + * + * Copyright (c) 2012 Massachusetts Institute of Technology + * + * Adapted from code in net/802/garp.c + * Copyright (c) 2008 Patrick McHardy + * + * 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 + +static unsigned int mrp_join_time __read_mostly = 200; +module_param(mrp_join_time, uint, 0644); +MODULE_PARM_DESC(mrp_join_time, "Join time in ms (default 200ms)"); +MODULE_LICENSE("GPL"); + +static const u8 +mrp_applicant_state_table[MRP_APPLICANT_MAX + 1][MRP_EVENT_MAX + 1] = { + [MRP_APPLICANT_VO] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_VP, + [MRP_EVENT_LV] = MRP_APPLICANT_VO, + [MRP_EVENT_TX] = MRP_APPLICANT_VO, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_VO, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AO, + [MRP_EVENT_R_IN] = MRP_APPLICANT_VO, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VO, + [MRP_EVENT_R_MT] = MRP_APPLICANT_VO, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VO, + }, + [MRP_APPLICANT_VP] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_VP, + [MRP_EVENT_LV] = MRP_APPLICANT_VO, + [MRP_EVENT_TX] = MRP_APPLICANT_AA, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_VP, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AP, + [MRP_EVENT_R_IN] = MRP_APPLICANT_VP, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VP, + [MRP_EVENT_R_MT] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VP, + }, + [MRP_APPLICANT_VN] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_VN, + [MRP_EVENT_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_TX] = MRP_APPLICANT_AN, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_VN, + [MRP_EVENT_R_IN] = MRP_APPLICANT_VN, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_VN, + [MRP_EVENT_R_MT] = MRP_APPLICANT_VN, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VN, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VN, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_VN, + }, + [MRP_APPLICANT_AN] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_AN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_AN, + [MRP_EVENT_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_TX] = MRP_APPLICANT_QA, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_AN, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_AN, + [MRP_EVENT_R_IN] = MRP_APPLICANT_AN, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AN, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AN, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VN, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VN, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VN, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AN, + }, + [MRP_APPLICANT_AA] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_AA, + [MRP_EVENT_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_TX] = MRP_APPLICANT_QA, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_AA, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA, + [MRP_EVENT_R_IN] = MRP_APPLICANT_AA, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AA, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA, + }, + [MRP_APPLICANT_QA] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_QA, + [MRP_EVENT_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_TX] = MRP_APPLICANT_QA, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_QA, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QA, + [MRP_EVENT_R_IN] = MRP_APPLICANT_QA, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AA, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AA, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AA, + }, + [MRP_APPLICANT_LA] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_AA, + [MRP_EVENT_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_TX] = MRP_APPLICANT_VO, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_LA, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_LA, + [MRP_EVENT_R_IN] = MRP_APPLICANT_LA, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_LA, + [MRP_EVENT_R_MT] = MRP_APPLICANT_LA, + [MRP_EVENT_R_LV] = MRP_APPLICANT_LA, + [MRP_EVENT_R_LA] = MRP_APPLICANT_LA, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_LA, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_LA, + }, + [MRP_APPLICANT_AO] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_AP, + [MRP_EVENT_LV] = MRP_APPLICANT_AO, + [MRP_EVENT_TX] = MRP_APPLICANT_AO, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_AO, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO, + [MRP_EVENT_R_IN] = MRP_APPLICANT_AO, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AO, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AO, + }, + [MRP_APPLICANT_QO] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_QP, + [MRP_EVENT_LV] = MRP_APPLICANT_QO, + [MRP_EVENT_TX] = MRP_APPLICANT_QO, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_QO, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QO, + [MRP_EVENT_R_IN] = MRP_APPLICANT_QO, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AO, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AO, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VO, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VO, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VO, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_QO, + }, + [MRP_APPLICANT_AP] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_AP, + [MRP_EVENT_LV] = MRP_APPLICANT_AO, + [MRP_EVENT_TX] = MRP_APPLICANT_QA, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_AP, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP, + [MRP_EVENT_R_IN] = MRP_APPLICANT_AP, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AP, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP, + }, + [MRP_APPLICANT_QP] = { + [MRP_EVENT_NEW] = MRP_APPLICANT_VN, + [MRP_EVENT_JOIN] = MRP_APPLICANT_QP, + [MRP_EVENT_LV] = MRP_APPLICANT_QO, + [MRP_EVENT_TX] = MRP_APPLICANT_QP, + [MRP_EVENT_R_NEW] = MRP_APPLICANT_QP, + [MRP_EVENT_R_JOIN_IN] = MRP_APPLICANT_QP, + [MRP_EVENT_R_IN] = MRP_APPLICANT_QP, + [MRP_EVENT_R_JOIN_MT] = MRP_APPLICANT_AP, + [MRP_EVENT_R_MT] = MRP_APPLICANT_AP, + [MRP_EVENT_R_LV] = MRP_APPLICANT_VP, + [MRP_EVENT_R_LA] = MRP_APPLICANT_VP, + [MRP_EVENT_REDECLARE] = MRP_APPLICANT_VP, + [MRP_EVENT_PERIODIC] = MRP_APPLICANT_AP, + }, +}; + +static const u8 +mrp_tx_action_table[MRP_APPLICANT_MAX + 1] = { + [MRP_APPLICANT_VO] = MRP_TX_ACTION_S_IN_OPTIONAL, + [MRP_APPLICANT_VP] = MRP_TX_ACTION_S_JOIN_IN, + [MRP_APPLICANT_VN] = MRP_TX_ACTION_S_NEW, + [MRP_APPLICANT_AN] = MRP_TX_ACTION_S_NEW, + [MRP_APPLICANT_AA] = MRP_TX_ACTION_S_JOIN_IN, + [MRP_APPLICANT_QA] = MRP_TX_ACTION_S_JOIN_IN_OPTIONAL, + [MRP_APPLICANT_LA] = MRP_TX_ACTION_S_LV, + [MRP_APPLICANT_AO] = MRP_TX_ACTION_S_IN_OPTIONAL, + [MRP_APPLICANT_QO] = MRP_TX_ACTION_S_IN_OPTIONAL, + [MRP_APPLICANT_AP] = MRP_TX_ACTION_S_JOIN_IN, + [MRP_APPLICANT_QP] = MRP_TX_ACTION_S_IN_OPTIONAL, +}; + +static void mrp_attrvalue_inc(void *value, u8 len) +{ + u8 *v = (u8 *)value; + + /* Add 1 to the last byte. If it becomes zero, + * go to the previous byte and repeat. + */ + while (len > 0 && !++v[--len]) + ; +} + +static int mrp_attr_cmp(const struct mrp_attr *attr, + const void *value, u8 len, u8 type) +{ + if (attr->type != type) + return attr->type - type; + if (attr->len != len) + return attr->len - len; + return memcmp(attr->value, value, len); +} + +static struct mrp_attr *mrp_attr_lookup(const struct mrp_applicant *app, + const void *value, u8 len, u8 type) +{ + struct rb_node *parent = app->mad.rb_node; + struct mrp_attr *attr; + int d; + + while (parent) { + attr = rb_entry(parent, struct mrp_attr, node); + d = mrp_attr_cmp(attr, value, len, type); + if (d > 0) + parent = parent->rb_left; + else if (d < 0) + parent = parent->rb_right; + else + return attr; + } + return NULL; +} + +static struct mrp_attr *mrp_attr_create(struct mrp_applicant *app, + const void *value, u8 len, u8 type) +{ + struct rb_node *parent = NULL, **p = &app->mad.rb_node; + struct mrp_attr *attr; + int d; + + while (*p) { + parent = *p; + attr = rb_entry(parent, struct mrp_attr, node); + d = mrp_attr_cmp(attr, value, len, type); + if (d > 0) + p = &parent->rb_left; + else if (d < 0) + p = &parent->rb_right; + else { + /* The attribute already exists; re-use it. */ + return attr; + } + } + attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); + if (!attr) + return attr; + attr->state = MRP_APPLICANT_VO; + attr->type = type; + attr->len = len; + memcpy(attr->value, value, len); + + rb_link_node(&attr->node, parent, p); + rb_insert_color(&attr->node, &app->mad); + return attr; +} + +static void mrp_attr_destroy(struct mrp_applicant *app, struct mrp_attr *attr) +{ + rb_erase(&attr->node, &app->mad); + kfree(attr); +} + +static int mrp_pdu_init(struct mrp_applicant *app) +{ + struct sk_buff *skb; + struct mrp_pdu_hdr *ph; + + skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), + GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + skb->dev = app->dev; + skb->protocol = app->app->pkttype.type; + skb_reserve(skb, LL_RESERVED_SPACE(app->dev)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + ph = (struct mrp_pdu_hdr *)__skb_put(skb, sizeof(*ph)); + ph->version = app->app->version; + + app->pdu = skb; + return 0; +} + +static int mrp_pdu_append_end_mark(struct mrp_applicant *app) +{ + __be16 *endmark; + + if (skb_tailroom(app->pdu) < sizeof(*endmark)) + return -1; + endmark = (__be16 *)__skb_put(app->pdu, sizeof(*endmark)); + put_unaligned(MRP_END_MARK, endmark); + return 0; +} + +static void mrp_pdu_queue(struct mrp_applicant *app) +{ + if (!app->pdu) + return; + + if (mrp_cb(app->pdu)->mh) + mrp_pdu_append_end_mark(app); + mrp_pdu_append_end_mark(app); + + dev_hard_header(app->pdu, app->dev, ntohs(app->app->pkttype.type), + app->app->group_address, app->dev->dev_addr, + app->pdu->len); + + skb_queue_tail(&app->queue, app->pdu); + app->pdu = NULL; +} + +static void mrp_queue_xmit(struct mrp_applicant *app) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&app->queue))) + dev_queue_xmit(skb); +} + +static int mrp_pdu_append_msg_hdr(struct mrp_applicant *app, + u8 attrtype, u8 attrlen) +{ + struct mrp_msg_hdr *mh; + + if (mrp_cb(app->pdu)->mh) { + if (mrp_pdu_append_end_mark(app) < 0) + return -1; + mrp_cb(app->pdu)->mh = NULL; + mrp_cb(app->pdu)->vah = NULL; + } + + if (skb_tailroom(app->pdu) < sizeof(*mh)) + return -1; + mh = (struct mrp_msg_hdr *)__skb_put(app->pdu, sizeof(*mh)); + mh->attrtype = attrtype; + mh->attrlen = attrlen; + mrp_cb(app->pdu)->mh = mh; + return 0; +} + +static int mrp_pdu_append_vecattr_hdr(struct mrp_applicant *app, + const void *firstattrvalue, u8 attrlen) +{ + struct mrp_vecattr_hdr *vah; + + if (skb_tailroom(app->pdu) < sizeof(*vah) + attrlen) + return -1; + vah = (struct mrp_vecattr_hdr *)__skb_put(app->pdu, + sizeof(*vah) + attrlen); + put_unaligned(0, &vah->lenflags); + memcpy(vah->firstattrvalue, firstattrvalue, attrlen); + mrp_cb(app->pdu)->vah = vah; + memcpy(mrp_cb(app->pdu)->attrvalue, firstattrvalue, attrlen); + return 0; +} + +static int mrp_pdu_append_vecattr_event(struct mrp_applicant *app, + const struct mrp_attr *attr, + enum mrp_vecattr_event vaevent) +{ + u16 len, pos; + u8 *vaevents; + int err; +again: + if (!app->pdu) { + err = mrp_pdu_init(app); + if (err < 0) + return err; + } + + /* If there is no Message header in the PDU, or the Message header is + * for a different attribute type, add an EndMark (if necessary) and a + * new Message header to the PDU. + */ + if (!mrp_cb(app->pdu)->mh || + mrp_cb(app->pdu)->mh->attrtype != attr->type || + mrp_cb(app->pdu)->mh->attrlen != attr->len) { + if (mrp_pdu_append_msg_hdr(app, attr->type, attr->len) < 0) + goto queue; + } + + /* If there is no VectorAttribute header for this Message in the PDU, + * or this attribute's value does not sequentially follow the previous + * attribute's value, add a new VectorAttribute header to the PDU. + */ + if (!mrp_cb(app->pdu)->vah || + memcmp(mrp_cb(app->pdu)->attrvalue, attr->value, attr->len)) { + if (mrp_pdu_append_vecattr_hdr(app, attr->value, attr->len) < 0) + goto queue; + } + + len = be16_to_cpu(get_unaligned(&mrp_cb(app->pdu)->vah->lenflags)); + pos = len % 3; + + /* Events are packed into Vectors in the PDU, three to a byte. Add a + * byte to the end of the Vector if necessary. + */ + if (!pos) { + if (skb_tailroom(app->pdu) < sizeof(u8)) + goto queue; + vaevents = (u8 *)__skb_put(app->pdu, sizeof(u8)); + } else { + vaevents = (u8 *)(skb_tail_pointer(app->pdu) - sizeof(u8)); + } + + switch (pos) { + case 0: + *vaevents = vaevent * (__MRP_VECATTR_EVENT_MAX * + __MRP_VECATTR_EVENT_MAX); + break; + case 1: + *vaevents += vaevent * __MRP_VECATTR_EVENT_MAX; + break; + case 2: + *vaevents += vaevent; + break; + default: + WARN_ON(1); + } + + /* Increment the length of the VectorAttribute in the PDU, as well as + * the value of the next attribute that would continue its Vector. + */ + put_unaligned(cpu_to_be16(++len), &mrp_cb(app->pdu)->vah->lenflags); + mrp_attrvalue_inc(mrp_cb(app->pdu)->attrvalue, attr->len); + + return 0; + +queue: + mrp_pdu_queue(app); + goto again; +} + +static void mrp_attr_event(struct mrp_applicant *app, + struct mrp_attr *attr, enum mrp_event event) +{ + enum mrp_applicant_state state; + + state = mrp_applicant_state_table[attr->state][event]; + if (state == MRP_APPLICANT_INVALID) { + WARN_ON(1); + return; + } + + if (event == MRP_EVENT_TX) { + /* When appending the attribute fails, don't update its state + * in order to retry at the next TX event. + */ + + switch (mrp_tx_action_table[attr->state]) { + case MRP_TX_ACTION_NONE: + case MRP_TX_ACTION_S_JOIN_IN_OPTIONAL: + case MRP_TX_ACTION_S_IN_OPTIONAL: + break; + case MRP_TX_ACTION_S_NEW: + if (mrp_pdu_append_vecattr_event( + app, attr, MRP_VECATTR_EVENT_NEW) < 0) + return; + break; + case MRP_TX_ACTION_S_JOIN_IN: + if (mrp_pdu_append_vecattr_event( + app, attr, MRP_VECATTR_EVENT_JOIN_IN) < 0) + return; + break; + case MRP_TX_ACTION_S_LV: + if (mrp_pdu_append_vecattr_event( + app, attr, MRP_VECATTR_EVENT_LV) < 0) + return; + /* As a pure applicant, sending a leave message + * implies that the attribute was unregistered and + * can be destroyed. + */ + mrp_attr_destroy(app, attr); + return; + default: + WARN_ON(1); + } + } + + attr->state = state; +} + +int mrp_request_join(const struct net_device *dev, + const struct mrp_application *appl, + const void *value, u8 len, u8 type) +{ + struct mrp_port *port = rtnl_dereference(dev->mrp_port); + struct mrp_applicant *app = rtnl_dereference( + port->applicants[appl->type]); + struct mrp_attr *attr; + + if (sizeof(struct mrp_skb_cb) + len > + FIELD_SIZEOF(struct sk_buff, cb)) + return -ENOMEM; + + spin_lock_bh(&app->lock); + attr = mrp_attr_create(app, value, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return -ENOMEM; + } + mrp_attr_event(app, attr, MRP_EVENT_JOIN); + spin_unlock_bh(&app->lock); + return 0; +} +EXPORT_SYMBOL_GPL(mrp_request_join); + +void mrp_request_leave(const struct net_device *dev, + const struct mrp_application *appl, + const void *value, u8 len, u8 type) +{ + struct mrp_port *port = rtnl_dereference(dev->mrp_port); + struct mrp_applicant *app = rtnl_dereference( + port->applicants[appl->type]); + struct mrp_attr *attr; + + if (sizeof(struct mrp_skb_cb) + len > + FIELD_SIZEOF(struct sk_buff, cb)) + return; + + spin_lock_bh(&app->lock); + attr = mrp_attr_lookup(app, value, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return; + } + mrp_attr_event(app, attr, MRP_EVENT_LV); + spin_unlock_bh(&app->lock); +} +EXPORT_SYMBOL_GPL(mrp_request_leave); + +static void mrp_mad_event(struct mrp_applicant *app, enum mrp_event event) +{ + struct rb_node *node, *next; + struct mrp_attr *attr; + + for (node = rb_first(&app->mad); + next = node ? rb_next(node) : NULL, node != NULL; + node = next) { + attr = rb_entry(node, struct mrp_attr, node); + mrp_attr_event(app, attr, event); + } +} + +static void mrp_join_timer_arm(struct mrp_applicant *app) +{ + unsigned long delay; + + delay = (u64)msecs_to_jiffies(mrp_join_time) * net_random() >> 32; + mod_timer(&app->join_timer, jiffies + delay); +} + +static void mrp_join_timer(unsigned long data) +{ + struct mrp_applicant *app = (struct mrp_applicant *)data; + + spin_lock(&app->lock); + mrp_mad_event(app, MRP_EVENT_TX); + mrp_pdu_queue(app); + spin_unlock(&app->lock); + + mrp_queue_xmit(app); + mrp_join_timer_arm(app); +} + +static int mrp_pdu_parse_end_mark(struct sk_buff *skb, int *offset) +{ + __be16 endmark; + + if (skb_copy_bits(skb, *offset, &endmark, sizeof(endmark)) < 0) + return -1; + if (endmark == MRP_END_MARK) { + *offset += sizeof(endmark); + return -1; + } + return 0; +} + +static void mrp_pdu_parse_vecattr_event(struct mrp_applicant *app, + struct sk_buff *skb, + enum mrp_vecattr_event vaevent) +{ + struct mrp_attr *attr; + enum mrp_event event; + + attr = mrp_attr_lookup(app, mrp_cb(skb)->attrvalue, + mrp_cb(skb)->mh->attrlen, + mrp_cb(skb)->mh->attrtype); + if (attr == NULL) + return; + + switch (vaevent) { + case MRP_VECATTR_EVENT_NEW: + event = MRP_EVENT_R_NEW; + break; + case MRP_VECATTR_EVENT_JOIN_IN: + event = MRP_EVENT_R_JOIN_IN; + break; + case MRP_VECATTR_EVENT_IN: + event = MRP_EVENT_R_IN; + break; + case MRP_VECATTR_EVENT_JOIN_MT: + event = MRP_EVENT_R_JOIN_MT; + break; + case MRP_VECATTR_EVENT_MT: + event = MRP_EVENT_R_MT; + break; + case MRP_VECATTR_EVENT_LV: + event = MRP_EVENT_R_LV; + break; + default: + return; + } + + mrp_attr_event(app, attr, event); +} + +static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, + struct sk_buff *skb, int *offset) +{ + struct mrp_vecattr_hdr _vah; + u16 valen; + u8 vaevents, vaevent; + + mrp_cb(skb)->vah = skb_header_pointer(skb, *offset, sizeof(_vah), + &_vah); + if (!mrp_cb(skb)->vah) + return -1; + *offset += sizeof(_vah); + + if (get_unaligned(&mrp_cb(skb)->vah->lenflags) & + MRP_VECATTR_HDR_FLAG_LA) + mrp_mad_event(app, MRP_EVENT_R_LA); + valen = be16_to_cpu(get_unaligned(&mrp_cb(skb)->vah->lenflags) & + MRP_VECATTR_HDR_LEN_MASK); + + /* The VectorAttribute structure in a PDU carries event information + * about one or more attributes having consecutive values. Only the + * value for the first attribute is contained in the structure. So + * we make a copy of that value, and then increment it each time we + * advance to the next event in its Vector. + */ + if (sizeof(struct mrp_skb_cb) + mrp_cb(skb)->mh->attrlen > + FIELD_SIZEOF(struct sk_buff, cb)) + return -1; + if (skb_copy_bits(skb, *offset, mrp_cb(skb)->attrvalue, + mrp_cb(skb)->mh->attrlen) < 0) + return -1; + *offset += mrp_cb(skb)->mh->attrlen; + + /* In a VectorAttribute, the Vector contains events which are packed + * three to a byte. We process one byte of the Vector at a time. + */ + while (valen > 0) { + if (skb_copy_bits(skb, *offset, &vaevents, + sizeof(vaevents)) < 0) + return -1; + *offset += sizeof(vaevents); + + /* Extract and process the first event. */ + vaevent = vaevents / (__MRP_VECATTR_EVENT_MAX * + __MRP_VECATTR_EVENT_MAX); + if (vaevent >= __MRP_VECATTR_EVENT_MAX) { + /* The byte is malformed; stop processing. */ + return -1; + } + mrp_pdu_parse_vecattr_event(app, skb, vaevent); + + /* If present, extract and process the second event. */ + if (!--valen) + break; + mrp_attrvalue_inc(mrp_cb(skb)->attrvalue, + mrp_cb(skb)->mh->attrlen); + vaevents %= (__MRP_VECATTR_EVENT_MAX * + __MRP_VECATTR_EVENT_MAX); + vaevent = vaevents / __MRP_VECATTR_EVENT_MAX; + mrp_pdu_parse_vecattr_event(app, skb, vaevent); + + /* If present, extract and process the third event. */ + if (!--valen) + break; + mrp_attrvalue_inc(mrp_cb(skb)->attrvalue, + mrp_cb(skb)->mh->attrlen); + vaevents %= __MRP_VECATTR_EVENT_MAX; + vaevent = vaevents; + mrp_pdu_parse_vecattr_event(app, skb, vaevent); + } + return 0; +} + +static int mrp_pdu_parse_msg(struct mrp_applicant *app, struct sk_buff *skb, + int *offset) +{ + struct mrp_msg_hdr _mh; + + mrp_cb(skb)->mh = skb_header_pointer(skb, *offset, sizeof(_mh), &_mh); + if (!mrp_cb(skb)->mh) + return -1; + *offset += sizeof(_mh); + + if (mrp_cb(skb)->mh->attrtype == 0 || + mrp_cb(skb)->mh->attrtype > app->app->maxattr || + mrp_cb(skb)->mh->attrlen == 0) + return -1; + + while (skb->len > *offset) { + if (mrp_pdu_parse_end_mark(skb, offset) < 0) + break; + if (mrp_pdu_parse_vecattr(app, skb, offset) < 0) + return -1; + } + return 0; +} + +int mrp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct mrp_application *appl = container_of(pt, struct mrp_application, + pkttype); + struct mrp_port *port; + struct mrp_applicant *app; + struct mrp_pdu_hdr _ph; + const struct mrp_pdu_hdr *ph; + int offset = skb_network_offset(skb); + + /* If the interface is in promiscuous mode, drop the packet if + * it was unicast to another host. + */ + if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) + goto out; + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) + goto out; + port = rcu_dereference(dev->mrp_port); + if (unlikely(!port)) + goto out; + app = rcu_dereference(port->applicants[appl->type]); + if (unlikely(!app)) + goto out; + + ph = skb_header_pointer(skb, offset, sizeof(_ph), &_ph); + if (!ph) + goto out; + offset += sizeof(_ph); + + if (ph->version != app->app->version) + goto out; + + spin_lock(&app->lock); + while (skb->len > offset) { + if (mrp_pdu_parse_end_mark(skb, &offset) < 0) + break; + if (mrp_pdu_parse_msg(app, skb, &offset) < 0) + break; + } + spin_unlock(&app->lock); +out: + kfree_skb(skb); + return 0; +} + +static int mrp_init_port(struct net_device *dev) +{ + struct mrp_port *port; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + rcu_assign_pointer(dev->mrp_port, port); + return 0; +} + +static void mrp_release_port(struct net_device *dev) +{ + struct mrp_port *port = rtnl_dereference(dev->mrp_port); + unsigned int i; + + for (i = 0; i <= MRP_APPLICATION_MAX; i++) { + if (rtnl_dereference(port->applicants[i])) + return; + } + RCU_INIT_POINTER(dev->mrp_port, NULL); + kfree_rcu(port, rcu); +} + +int mrp_init_applicant(struct net_device *dev, struct mrp_application *appl) +{ + struct mrp_applicant *app; + int err; + + ASSERT_RTNL(); + + if (!rtnl_dereference(dev->mrp_port)) { + err = mrp_init_port(dev); + if (err < 0) + goto err1; + } + + err = -ENOMEM; + app = kzalloc(sizeof(*app), GFP_KERNEL); + if (!app) + goto err2; + + err = dev_mc_add(dev, appl->group_address); + if (err < 0) + goto err3; + + app->dev = dev; + app->app = appl; + app->mad = RB_ROOT; + spin_lock_init(&app->lock); + skb_queue_head_init(&app->queue); + rcu_assign_pointer(dev->mrp_port->applicants[appl->type], app); + setup_timer(&app->join_timer, mrp_join_timer, (unsigned long)app); + mrp_join_timer_arm(app); + return 0; + +err3: + kfree(app); +err2: + mrp_release_port(dev); +err1: + return err; +} +EXPORT_SYMBOL_GPL(mrp_init_applicant); + +void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *appl) +{ + struct mrp_port *port = rtnl_dereference(dev->mrp_port); + struct mrp_applicant *app = rtnl_dereference( + port->applicants[appl->type]); + + ASSERT_RTNL(); + + RCU_INIT_POINTER(port->applicants[appl->type], NULL); + + /* Delete timer and generate a final TX event to flush out + * all pending messages before the applicant is gone. + */ + del_timer_sync(&app->join_timer); + mrp_mad_event(app, MRP_EVENT_TX); + mrp_pdu_queue(app); + mrp_queue_xmit(app); + + dev_mc_del(dev, appl->group_address); + kfree_rcu(app, rcu); + mrp_release_port(dev); +} +EXPORT_SYMBOL_GPL(mrp_uninit_applicant); + +int mrp_register_application(struct mrp_application *appl) +{ + appl->pkttype.func = mrp_rcv; + dev_add_pack(&appl->pkttype); + return 0; +} +EXPORT_SYMBOL_GPL(mrp_register_application); + +void mrp_unregister_application(struct mrp_application *appl) +{ + dev_remove_pack(&appl->pkttype); +} +EXPORT_SYMBOL_GPL(mrp_unregister_application); -- cgit v1.2.3 From 86fbe9bb599fcaf7e92e38dbfdad0414a2d68f7d Mon Sep 17 00:00:00 2001 From: David Ward Date: Fri, 8 Feb 2013 17:17:07 +0000 Subject: net/8021q: Implement Multiple VLAN Registration Protocol (MVRP) Initial implementation of the Multiple VLAN Registration Protocol (MVRP) from IEEE 802.1Q-2011, based on the existing implementation of the GARP VLAN Registration Protocol (GVRP). Signed-off-by: David Ward Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/uapi/linux/if_ether.h | 1 + include/uapi/linux/if_vlan.h | 1 + net/8021q/Kconfig | 11 +++++++ net/8021q/Makefile | 1 + net/8021q/vlan.c | 27 +++++++++++++--- net/8021q/vlan.h | 16 ++++++++++ net/8021q/vlan_dev.c | 12 +++++++- net/8021q/vlan_mvrp.c | 72 +++++++++++++++++++++++++++++++++++++++++++ net/8021q/vlan_netlink.c | 2 +- 9 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 net/8021q/vlan_mvrp.c (limited to 'net') diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 67fb87ca1094..798032d01112 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -83,6 +83,7 @@ #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ #define ETH_P_TIPC 0x88CA /* TIPC */ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ +#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ #define ETH_P_TDLS 0x890D /* TDLS */ diff --git a/include/uapi/linux/if_vlan.h b/include/uapi/linux/if_vlan.h index 0744f8e65d15..7e5e6b397332 100644 --- a/include/uapi/linux/if_vlan.h +++ b/include/uapi/linux/if_vlan.h @@ -34,6 +34,7 @@ enum vlan_flags { VLAN_FLAG_REORDER_HDR = 0x1, VLAN_FLAG_GVRP = 0x2, VLAN_FLAG_LOOSE_BINDING = 0x4, + VLAN_FLAG_MVRP = 0x8, }; enum vlan_name_types { diff --git a/net/8021q/Kconfig b/net/8021q/Kconfig index fa073a54963e..8f7517df41a5 100644 --- a/net/8021q/Kconfig +++ b/net/8021q/Kconfig @@ -27,3 +27,14 @@ config VLAN_8021Q_GVRP automatic propagation of registered VLANs to switches. If unsure, say N. + +config VLAN_8021Q_MVRP + bool "MVRP (Multiple VLAN Registration Protocol) support" + depends on VLAN_8021Q + select MRP + help + Select this to enable MVRP end-system support. MVRP is used for + automatic propagation of registered VLANs to switches; it + supersedes GVRP and is not backwards-compatible. + + If unsure, say N. diff --git a/net/8021q/Makefile b/net/8021q/Makefile index 9f4f174ead1c..7bc8db08d7ef 100644 --- a/net/8021q/Makefile +++ b/net/8021q/Makefile @@ -6,5 +6,6 @@ obj-$(CONFIG_VLAN_8021Q) += 8021q.o 8021q-y := vlan.o vlan_dev.o vlan_netlink.o 8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o +8021q-$(CONFIG_VLAN_8021Q_MVRP) += vlan_mvrp.o 8021q-$(CONFIG_PROC_FS) += vlanproc.o diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index addc578d5443..a18714469bf7 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -95,6 +95,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) grp->nr_vlan_devs--; + if (vlan->flags & VLAN_FLAG_MVRP) + vlan_mvrp_request_leave(dev); if (vlan->flags & VLAN_FLAG_GVRP) vlan_gvrp_request_leave(dev); @@ -107,8 +109,10 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) netdev_upper_dev_unlink(real_dev, dev); - if (grp->nr_vlan_devs == 0) + if (grp->nr_vlan_devs == 0) { + vlan_mvrp_uninit_applicant(real_dev); vlan_gvrp_uninit_applicant(real_dev); + } /* Get rid of the vlan's reference to real_dev */ dev_put(real_dev); @@ -151,15 +155,18 @@ int register_vlan_dev(struct net_device *dev) err = vlan_gvrp_init_applicant(real_dev); if (err < 0) goto out_vid_del; + err = vlan_mvrp_init_applicant(real_dev); + if (err < 0) + goto out_uninit_gvrp; } err = vlan_group_prealloc_vid(grp, vlan_id); if (err < 0) - goto out_uninit_applicant; + goto out_uninit_mvrp; err = netdev_upper_dev_link(real_dev, dev); if (err) - goto out_uninit_applicant; + goto out_uninit_mvrp; err = register_netdevice(dev); if (err < 0) @@ -181,7 +188,10 @@ int register_vlan_dev(struct net_device *dev) out_upper_dev_unlink: netdev_upper_dev_unlink(real_dev, dev); -out_uninit_applicant: +out_uninit_mvrp: + if (grp->nr_vlan_devs == 0) + vlan_mvrp_uninit_applicant(real_dev); +out_uninit_gvrp: if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); out_vid_del: @@ -655,13 +665,19 @@ static int __init vlan_proto_init(void) if (err < 0) goto err3; - err = vlan_netlink_init(); + err = vlan_mvrp_init(); if (err < 0) goto err4; + err = vlan_netlink_init(); + if (err < 0) + goto err5; + vlan_ioctl_set(vlan_ioctl_handler); return 0; +err5: + vlan_mvrp_uninit(); err4: vlan_gvrp_uninit(); err3: @@ -682,6 +698,7 @@ static void __exit vlan_cleanup_module(void) unregister_pernet_subsys(&vlan_net_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ + vlan_mvrp_uninit(); vlan_gvrp_uninit(); } diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index a4886d94c40c..670f1e8cfc0f 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -171,6 +171,22 @@ static inline int vlan_gvrp_init(void) { return 0; } static inline void vlan_gvrp_uninit(void) {} #endif +#ifdef CONFIG_VLAN_8021Q_MVRP +extern int vlan_mvrp_request_join(const struct net_device *dev); +extern void vlan_mvrp_request_leave(const struct net_device *dev); +extern int vlan_mvrp_init_applicant(struct net_device *dev); +extern void vlan_mvrp_uninit_applicant(struct net_device *dev); +extern int vlan_mvrp_init(void); +extern void vlan_mvrp_uninit(void); +#else +static inline int vlan_mvrp_request_join(const struct net_device *dev) { return 0; } +static inline void vlan_mvrp_request_leave(const struct net_device *dev) {} +static inline int vlan_mvrp_init_applicant(struct net_device *dev) { return 0; } +static inline void vlan_mvrp_uninit_applicant(struct net_device *dev) {} +static inline int vlan_mvrp_init(void) { return 0; } +static inline void vlan_mvrp_uninit(void) {} +#endif + extern const char vlan_fullname[]; extern const char vlan_version[]; extern int vlan_netlink_init(void); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 09f9108d4688..34df5b3c9b75 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -261,7 +261,7 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) u32 old_flags = vlan->flags; if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | - VLAN_FLAG_LOOSE_BINDING)) + VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) return -EINVAL; vlan->flags = (old_flags & ~mask) | (flags & mask); @@ -272,6 +272,13 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) else vlan_gvrp_request_leave(dev); } + + if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) { + if (vlan->flags & VLAN_FLAG_MVRP) + vlan_mvrp_request_join(dev); + else + vlan_mvrp_request_leave(dev); + } return 0; } @@ -312,6 +319,9 @@ static int vlan_dev_open(struct net_device *dev) if (vlan->flags & VLAN_FLAG_GVRP) vlan_gvrp_request_join(dev); + if (vlan->flags & VLAN_FLAG_MVRP) + vlan_mvrp_request_join(dev); + if (netif_carrier_ok(real_dev)) netif_carrier_on(dev); return 0; diff --git a/net/8021q/vlan_mvrp.c b/net/8021q/vlan_mvrp.c new file mode 100644 index 000000000000..d9ec1d5964aa --- /dev/null +++ b/net/8021q/vlan_mvrp.c @@ -0,0 +1,72 @@ +/* + * IEEE 802.1Q Multiple VLAN Registration Protocol (MVRP) + * + * Copyright (c) 2012 Massachusetts Institute of Technology + * + * Adapted from code in net/8021q/vlan_gvrp.c + * Copyright (c) 2008 Patrick McHardy + * + * 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 "vlan.h" + +#define MRP_MVRP_ADDRESS { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 } + +enum mvrp_attributes { + MVRP_ATTR_INVALID, + MVRP_ATTR_VID, + __MVRP_ATTR_MAX +}; +#define MVRP_ATTR_MAX (__MVRP_ATTR_MAX - 1) + +static struct mrp_application vlan_mrp_app __read_mostly = { + .type = MRP_APPLICATION_MVRP, + .maxattr = MVRP_ATTR_MAX, + .pkttype.type = htons(ETH_P_MVRP), + .group_address = MRP_MVRP_ADDRESS, + .version = 0, +}; + +int vlan_mvrp_request_join(const struct net_device *dev) +{ + const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + __be16 vlan_id = htons(vlan->vlan_id); + + return mrp_request_join(vlan->real_dev, &vlan_mrp_app, + &vlan_id, sizeof(vlan_id), MVRP_ATTR_VID); +} + +void vlan_mvrp_request_leave(const struct net_device *dev) +{ + const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + __be16 vlan_id = htons(vlan->vlan_id); + + mrp_request_leave(vlan->real_dev, &vlan_mrp_app, + &vlan_id, sizeof(vlan_id), MVRP_ATTR_VID); +} + +int vlan_mvrp_init_applicant(struct net_device *dev) +{ + return mrp_init_applicant(dev, &vlan_mrp_app); +} + +void vlan_mvrp_uninit_applicant(struct net_device *dev) +{ + mrp_uninit_applicant(dev, &vlan_mrp_app); +} + +int __init vlan_mvrp_init(void) +{ + return mrp_register_application(&vlan_mrp_app); +} + +void vlan_mvrp_uninit(void) +{ + mrp_unregister_application(&vlan_mrp_app); +} diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index 708c80ea1874..1789658b7cd7 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -62,7 +62,7 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) flags = nla_data(data[IFLA_VLAN_FLAGS]); if ((flags->flags & flags->mask) & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | - VLAN_FLAG_LOOSE_BINDING)) + VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) return -EINVAL; } -- cgit v1.2.3 From ec16ef2228829698fdbd2f6b160ff52551a6bf8a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sat, 9 Feb 2013 04:29:58 +0000 Subject: ipv6 mcast: Do not join device multicast for interface-local multicasts. RFC4291 (IPv6 addressing architecture) says that interface-Local scope spans only a single interface on a node. We should not join L2 device multicast list for addresses in interface-local (or smaller) scope. Signed-off-by: YOSHIFUJI Hideaki Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/mcast.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index e5de48551864..3a2849f660ca 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -661,6 +661,10 @@ static void igmp6_group_added(struct ifmcaddr6 *mc) struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < + IPV6_ADDR_SCOPE_LINKLOCAL) + return; + spin_lock_bh(&mc->mca_lock); if (!(mc->mca_flags&MAF_LOADED)) { mc->mca_flags |= MAF_LOADED; @@ -687,6 +691,10 @@ static void igmp6_group_dropped(struct ifmcaddr6 *mc) struct net_device *dev = mc->idev->dev; char buf[MAX_ADDR_LEN]; + if (IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < + IPV6_ADDR_SCOPE_LINKLOCAL) + return; + spin_lock_bh(&mc->mca_lock); if (mc->mca_flags&MAF_LOADED) { mc->mca_flags &= ~MAF_LOADED; -- cgit v1.2.3 From daaba4fa17d7826807b0b131f796543b4099ef4a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Sat, 9 Feb 2013 07:00:59 +0000 Subject: net neighbour, decnet: Ensure to align device private data on preferred alignment. To allow both of protocol-specific data and device-specific data attached with neighbour entry, and to eliminate size calculation cost when allocating entry, sizeof protocol-speicic data must be multiple of NEIGH_PRIV_ALIGN. On 64bit archs, sizeof(struct dn_neigh) is multiple of NEIGH_PRIV_ALIGN, but on 32bit archs, it was not. Introduce NEIGH_ENTRY_SPACE() macro to ensure that protocol-specific entry-size meets our requirement. Reported-by: Fengguang Wu Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 + net/decnet/dn_neigh.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 629ee573c6d0..7e748ad8b50c 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -181,6 +181,7 @@ struct neigh_table { }; #define NEIGH_PRIV_ALIGN sizeof(long long) +#define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) static inline void *neighbour_priv(const struct neighbour *n) { diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 3aede1b459fd..856636addd76 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -95,7 +95,7 @@ static u32 dn_neigh_hash(const void *pkey, struct neigh_table dn_neigh_table = { .family = PF_DECnet, - .entry_size = sizeof(struct dn_neigh), + .entry_size = NEIGH_ENTRY_SIZE(sizeof(struct dn_neigh)), .key_len = sizeof(__le16), .hash = dn_neigh_hash, .constructor = dn_neigh_construct, -- cgit v1.2.3 From 7cb8a93968e395e40a72a50da0b6114e752304b4 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Mon, 11 Feb 2013 07:02:36 +0100 Subject: xfrm: Allow inserting policies with matching mark and different priorities We currently can not insert policies with mark and mask such that some flows would be matched from both policies. We make this possible when the priority of these policies are different. If both policies match a flow, the one with the higher priority is used. Reported-by: Emmanuel Thierry Reported-by: Romain Kuntz Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_policy.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 456b11b0f049..257dfb18aef4 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -607,6 +607,21 @@ static void xfrm_policy_requeue(struct xfrm_policy *old, spin_unlock_bh(&pq->hold_queue.lock); } +static bool xfrm_policy_mark_match(struct xfrm_policy *policy, + struct xfrm_policy *pol) +{ + u32 mark = policy->mark.v & policy->mark.m; + + if (policy->mark.v == pol->mark.v && policy->mark.m == pol->mark.m) + return true; + + if ((mark & pol->mark.m) == pol->mark.v && + policy->priority == pol->priority) + return true; + + return false; +} + int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) { struct net *net = xp_net(policy); @@ -614,7 +629,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) struct xfrm_policy *delpol; struct hlist_head *chain; struct hlist_node *entry, *newpos; - u32 mark = policy->mark.v & policy->mark.m; write_lock_bh(&xfrm_policy_lock); chain = policy_hash_bysel(net, &policy->selector, policy->family, dir); @@ -623,7 +637,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) hlist_for_each_entry(pol, entry, chain, bydst) { if (pol->type == policy->type && !selector_cmp(&pol->selector, &policy->selector) && - (mark & pol->mark.m) == pol->mark.v && + xfrm_policy_mark_match(policy, pol) && xfrm_sec_ctx_match(pol->security, policy->security) && !WARN_ON(delpol)) { if (excl) { -- cgit v1.2.3 From b7566fc363e23f0efd3fa1e1460f9421cdc0d77e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 4 Feb 2013 15:33:44 -0600 Subject: cfg80211: Fix memory leak When a driver requests a specific regulatory domain after cfg80211 already has one, a struct ieee80211_regdomain is leaked. Reported-by: Larry Finger Tested-by: Larry Finger Signed-off-by: Larry Finger Signed-off-by: Johannes Berg --- net/wireless/reg.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6ea626b30a2a..08d3da2c70ab 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2189,10 +2189,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ - if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) + if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + const struct ieee80211_regdomain *tmp; + + tmp = get_wiphy_regdom(request_wiphy); rcu_assign_pointer(request_wiphy->regd, rd); - else + rcu_free_regdom(tmp); + } else { kfree(rd); + } rd = NULL; -- cgit v1.2.3 From bba87ffe606b7fc5fba73d1c0c5ac1eacd2cebe8 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 3 Feb 2013 08:28:27 +0200 Subject: cfg80211: fix radar check for P2P_DEVICE Radar is not required for P2P_DEVICE interfaces. Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- net/wireless/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/util.c b/net/wireless/util.c index d7873c7ae0ec..37a56ee1e1ed 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1217,10 +1217,10 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_MONITOR: radar_required = false; break; - case NL80211_IFTYPE_P2P_DEVICE: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_UNSPECIFIED: default: -- cgit v1.2.3 From 5b112d3d098c97b867cc580f590395cd1e72f18c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 01:49:58 +0100 Subject: cfg80211: pass wiphy to cfg80211_ref_bss/put_bss This prepares for using the spinlock instead of krefs which is needed in the next patch to track the refs of combined BSSes correctly. Acked-by: Bing Zhao [mwifiex] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 4 ++-- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- drivers/net/wireless/ath/wil6210/cfg80211.c | 2 +- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 ++-- drivers/net/wireless/libertas/cfg.c | 8 ++++---- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/mwifiex/scan.c | 2 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 4 ++-- drivers/net/wireless/orinoco/scan.c | 4 ++-- drivers/net/wireless/rndis_wlan.c | 4 ++-- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 6 ++++-- net/mac80211/ibss.c | 4 ++-- net/mac80211/mlme.c | 6 +++--- net/mac80211/scan.c | 3 ++- net/wireless/core.c | 2 +- net/wireless/ibss.c | 4 ++-- net/wireless/mlme.c | 14 +++++++------- net/wireless/scan.c | 4 ++-- net/wireless/sme.c | 16 ++++++++-------- 21 files changed, 51 insertions(+), 48 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 4225cca0f198..a7fb442a99aa 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -767,7 +767,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", nw_type & ADHOC_CREATOR ? "creator" : "joiner"); cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(ar->wiphy, bss); return; } @@ -778,7 +778,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, assoc_req_ie, assoc_req_len, assoc_resp_ie, assoc_resp_len, WLAN_STATUS_SUCCESS, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(ar->wiphy, bss); } else if (vif->sme_state == SME_CONNECTED) { /* inform roam event to cfg80211 */ cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 998f8b0f62fd..d366cf105c7c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1108,7 +1108,7 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, kfree(mgmt); if (bss == NULL) return -ENOMEM; - cfg80211_put_bss(bss); + cfg80211_put_bss(ar->wiphy, bss); /* * Firmware doesn't return any event when scheduled scan has diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 002851fceb2f..9ecc1968262c 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -341,7 +341,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, } out: - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); return rc; } diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 0b70e17cd1fb..79d608caa903 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -338,7 +338,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) if (bss) { wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid); - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); } else { wil_err(wil, "cfg80211_inform_bss() failed\n"); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 481f41ad7989..cecc3eff72e9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2323,7 +2323,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, if (!bss) return -ENOMEM; - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); return err; } @@ -2429,7 +2429,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, goto CleanUp; } - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); CleanUp: diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 230f8ebbe289..61735db3b051 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -657,7 +657,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, capa, intvl, ie, ielen, LBS_SCAN_RSSI_TO_MBM(rssi), GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); } } else lbs_deb_scan("scan response: missing BSS channel IE\n"); @@ -1444,7 +1444,7 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, done: if (bss) - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); return ret; } @@ -1766,7 +1766,7 @@ static void lbs_join_post(struct lbs_private *priv, params->beacon_interval, fake_ie, fake - fake_ie, 0, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->wdev->wiphy, bss); memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; @@ -2011,7 +2011,7 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, if (bss) { ret = lbs_ibss_join_existing(priv, params, bss); - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); } else ret = lbs_ibss_start_new(priv, params); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 3a004b85b99f..81c84a29308f 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1430,7 +1430,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, 0, ie_buf, ie_len, 0, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->wdev->wiphy, bss); memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); return 0; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 9189a32b7844..232492487527 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1746,7 +1746,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, .mac_address, ETH_ALEN)) mwifiex_update_curr_bss_params(priv, bss); - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->wdev->wiphy, bss); } } else { dev_dbg(adapter->dev, "missing BSS channel IE\n"); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index f542bb8ccbc8..ee85b41a4dfd 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -324,7 +324,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, } if (bss) - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->adapter->wiphy, bss); } else { /* Adhoc mode */ /* If the requested SSID matches current SSID, return */ @@ -354,7 +354,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, " list. Joining...\n"); ret = mwifiex_adhoc_join(priv, bss_desc); if (bss) - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->adapter->wiphy, bss); } else { dev_dbg(adapter->dev, "info: Network not found in " "the list, creating adhoc with ssid = %s\n", diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index 96e39edfec77..e8c5714bfd11 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -125,7 +125,7 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, capability, beacon_interval, ie_buf, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(cbss); + cfg80211_put_bss(wiphy, cbss); } void orinoco_add_extscan_result(struct orinoco_private *priv, @@ -158,7 +158,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(cbss); + cfg80211_put_bss(wiphy, cbss); } void orinoco_add_hostscan_results(struct orinoco_private *priv, diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index abe1d039be81..fe2f272689aa 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2029,7 +2029,7 @@ static bool rndis_bss_info_update(struct usbnet *usbdev, bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); return (bss != NULL); } @@ -2718,7 +2718,7 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid, timestamp, capability, beacon_period, ie_buf, ie_len, signal, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); } /* diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 18c06a59c091..a233f64ca22f 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -424,7 +424,7 @@ int prism2_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) goto exit; } - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); } if (result) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3ec70e1681d3..691b347545cb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3166,19 +3166,21 @@ cfg80211_get_ibss(struct wiphy *wiphy, /** * cfg80211_ref_bss - reference BSS struct + * @wiphy: the wiphy this BSS struct belongs to * @bss: the BSS struct to reference * * Increments the refcount of the given BSS struct. */ -void cfg80211_ref_bss(struct cfg80211_bss *bss); +void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); /** * cfg80211_put_bss - unref BSS struct + * @wiphy: the wiphy this BSS struct belongs to * @bss: the BSS struct * * Decrements the refcount of the given BSS struct. */ -void cfg80211_put_bss(struct cfg80211_bss *bss); +void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); /** * cfg80211_unlink_bss - unlink BSS from internal data structures diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a54c8248e0e0..71c55cc0f7b6 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -228,7 +228,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan, mgmt, skb->len, 0, GFP_KERNEL); - cfg80211_put_bss(bss); + cfg80211_put_bss(local->hw.wiphy, bss); netif_carrier_on(sdata->dev); cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); } @@ -1159,7 +1159,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) if (cbss) { cfg80211_unlink_bss(local->hw.wiphy, cbss); - cfg80211_put_bss(cbss); + cfg80211_put_bss(local->hw.wiphy, cbss); } } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 353b690900e9..15f2edd08a95 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1934,7 +1934,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, ieee80211_vif_release_channel(sdata); } - cfg80211_put_bss(auth_data->bss); + cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss); kfree(auth_data); sdata->u.mgd.auth_data = NULL; } @@ -2387,7 +2387,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ ieee80211_destroy_assoc_data(sdata, false); - cfg80211_put_bss(*bss); + cfg80211_put_bss(sdata->local->hw.wiphy, *bss); return RX_MGMT_CFG80211_ASSOC_TIMEOUT; } sdata_info(sdata, "associated\n"); @@ -3831,7 +3831,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, } /* hold our own reference */ - cfg80211_ref_bss(auth_data->bss); + cfg80211_ref_bss(local->hw.wiphy, auth_data->bss); err = 0; goto out_unlock; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7f80f0a5026e..400153f7f21f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -34,7 +34,8 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local, { if (!bss) return; - cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); + cfg80211_put_bss(local->hw.wiphy, + container_of((void *)bss, struct cfg80211_bss, priv)); } static bool is_uapsd_supported(struct ieee802_11_elems *elems) diff --git a/net/wireless/core.c b/net/wireless/core.c index ce827242f390..f0a1bbe95cff 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -715,7 +715,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) kfree(reg); } list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) - cfg80211_put_bss(&scan->pub); + cfg80211_put_bss(&rdev->wiphy, &scan->pub); kfree(rdev); } diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 9b9551e4a6f9..d80e47194d49 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -37,7 +37,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); } cfg80211_hold_bss(bss_from_pub(bss)); @@ -182,7 +182,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); } wdev->current_bss = NULL; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index fee9bf70efcf..8e6920728c43 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -58,7 +58,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, */ if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && cfg80211_sme_failed_reassoc(wdev)) { - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); goto out; } @@ -70,7 +70,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, * do not call connect_result() now because the * sme will schedule work that does it later. */ - cfg80211_put_bss(bss); + cfg80211_put_bss(wiphy, bss); goto out; } @@ -108,7 +108,7 @@ void __cfg80211_send_deauth(struct net_device *dev, if (wdev->current_bss && ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; was_current = true; } @@ -164,7 +164,7 @@ void __cfg80211_send_disassoc(struct net_device *dev, ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { cfg80211_sme_disassoc(dev, wdev->current_bss); cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; } else WARN_ON(1); @@ -324,7 +324,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, err = rdev_auth(rdev, dev, &req); out: - cfg80211_put_bss(req.bss); + cfg80211_put_bss(&rdev->wiphy, req.bss); return err; } @@ -432,7 +432,7 @@ out: if (err) { if (was_connected) wdev->sme_state = CFG80211_SME_CONNECTED; - cfg80211_put_bss(req.bss); + cfg80211_put_bss(&rdev->wiphy, req.bss); } return err; @@ -572,7 +572,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; } } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 36daacb31788..dacb44ac2f74 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -818,7 +818,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_inform_bss_frame); -void cfg80211_ref_bss(struct cfg80211_bss *pub) +void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_internal_bss *bss; @@ -830,7 +830,7 @@ void cfg80211_ref_bss(struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_ref_bss); -void cfg80211_put_bss(struct cfg80211_bss *pub) +void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_internal_bss *bss; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a825dfe12cf7..f432bd3755b1 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -301,7 +301,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) bss = cfg80211_get_conn_bss(wdev); if (bss) { - cfg80211_put_bss(bss); + cfg80211_put_bss(&rdev->wiphy, bss); } else { /* not found */ if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) @@ -464,7 +464,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; } @@ -480,7 +480,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, kfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; - cfg80211_put_bss(bss); + cfg80211_put_bss(wdev->wiphy, bss); return; } @@ -586,7 +586,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, } cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; cfg80211_hold_bss(bss_from_pub(bss)); @@ -621,7 +621,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, return; out: - cfg80211_put_bss(bss); + cfg80211_put_bss(wdev->wiphy, bss); } void cfg80211_roamed(struct net_device *dev, @@ -663,7 +663,7 @@ void cfg80211_roamed_bss(struct net_device *dev, ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); if (!ev) { - cfg80211_put_bss(bss); + cfg80211_put_bss(wdev->wiphy, bss); return; } @@ -704,7 +704,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); + cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); } wdev->current_bss = NULL; @@ -875,7 +875,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, if (bss) { wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; err = cfg80211_conn_do_work(wdev); - cfg80211_put_bss(bss); + cfg80211_put_bss(wdev->wiphy, bss); } else { /* otherwise we'll need to scan for the AP first */ err = cfg80211_conn_scan(wdev); -- cgit v1.2.3 From 077f897a8be9c617e69035af4d17a472d4af272b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 09:06:36 +0100 Subject: wireless: fix kernel-doc Fix most kernel-doc warnings, for some reason it seems to have issues with __aligned, don't remove the documentation entries it considers to be in excess due to that. Reported-by: Fengguang Wu Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 10 ++++++---- net/mac80211/sta_info.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 691b347545cb..5d8554bd95da 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -535,7 +535,7 @@ struct mac_address { * struct cfg80211_acl_data - Access control list data * * @acl_policy: ACL policy to be applied on the station's - entry specified by mac_addr + * entry specified by mac_addr * @n_acl_entries: Number of MAC address entries passed * @mac_addrs: List of MAC addresses of stations to be used for ACL */ @@ -666,6 +666,8 @@ struct station_parameters { * @STATION_INFO_INACTIVE_TIME: @inactive_time filled * @STATION_INFO_RX_BYTES: @rx_bytes filled * @STATION_INFO_TX_BYTES: @tx_bytes filled + * @STATION_INFO_RX_BYTES64: @rx_bytes filled with 64-bit value + * @STATION_INFO_TX_BYTES64: @tx_bytes filled with 64-bit value * @STATION_INFO_LLID: @llid filled * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled @@ -674,8 +676,6 @@ struct station_parameters { * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value - * @STATION_INFO_RX_PACKETS64: @rx_packets filled with 64-bit value - * @STATION_INFO_TX_PACKETS64: @tx_packets filled with 64-bit value * @STATION_INFO_TX_RETRIES: @tx_retries filled * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled @@ -1226,6 +1226,7 @@ struct cfg80211_match_set { * @n_match_sets: number of match sets * @wiphy: the wiphy this was for * @dev: the interface + * @scan_start: start time of the scheduled scan * @channels: channels to scan * @rssi_thold: don't report scan results below this threshold (in s32 dBm) */ @@ -1404,6 +1405,8 @@ 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: if set, change local state only and + * do not set a deauth frame */ struct cfg80211_deauth_request { const u8 *bssid; @@ -2629,7 +2632,6 @@ struct cfg80211_cached_keys; * the user-set AP, monitor and WDS channel * @preset_chan: (private) Used by the internal configuration code to * track the channel to be used for AP later - * @preset_chantype: (private) the corresponding channel type * @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 diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5a1deba2c645..350578c396c0 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -298,6 +298,7 @@ struct sta_ampdu_mlme { * @beacon_loss_count: number of times beacon loss has triggered * @supports_40mhz: tracks whether the station advertised 40 MHz support * as we overwrite its HT parameters with the currently used value + * @rcu_head: RCU head used for freeing this station struct */ struct sta_info { /* General information, mostly static */ -- cgit v1.2.3 From 45b5028e86292284f4d5794047d5dfd742c22421 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 6 Feb 2013 10:17:21 -0800 Subject: mac80211: fix mesh sta teardown The patch "mac80211: clean up mesh sta allocation warning" moved some mesh initialization into a path which is only called when the kernel handles peering. This causes a hang when mac80211 tries to clean up a userspace-allocated station entry and delete a timer which has never been initialized. To avoid this, only do any mesh sta peering teardown if the kernel is actually handling it. The same is true when quiescing before suspend. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 25 +++++++++++++++++++++++++ net/mac80211/mesh.h | 3 ++- net/mac80211/mesh_plink.c | 8 ++++++-- net/mac80211/sta_info.c | 9 ++------- 4 files changed, 35 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 35ac38871420..0c51b78b8fdc 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -149,6 +149,31 @@ u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata) return changed; } +/* + * mesh_sta_cleanup - clean up any mesh sta state + * + * @sta: mesh sta to clean up. + */ +void mesh_sta_cleanup(struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u32 changed; + + /* + * maybe userspace handles peer allocation and peering, but in either + * case the beacon is still generated by the kernel and we might need + * an update. + */ + changed = mesh_accept_plinks_update(sdata); + if (sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { + changed |= mesh_plink_deactivate(sta); + del_timer_sync(&sta->plink_timer); + } + + if (changed) + ieee80211_bss_info_change_notify(sdata, changed); +} + int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) { int i; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index eb336253b6b3..3b9d862744ba 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -288,12 +288,13 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); -void mesh_plink_deactivate(struct sta_info *sta); +u32 mesh_plink_deactivate(struct sta_info *sta); int mesh_plink_open(struct sta_info *sta); void mesh_plink_block(struct sta_info *sta); void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status); +void mesh_sta_cleanup(struct sta_info *sta); /* Private interfaces */ /* Mesh tables */ diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 67524e7d2548..56c9b318a97e 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -214,7 +214,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) * * All mesh paths with this peer as next hop will be flushed */ -void mesh_plink_deactivate(struct sta_info *sta) +u32 mesh_plink_deactivate(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; u32 changed; @@ -227,7 +227,7 @@ void mesh_plink_deactivate(struct sta_info *sta) sta->reason); spin_unlock_bh(&sta->lock); - ieee80211_bss_info_change_notify(sdata, changed); + return changed; } static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, @@ -595,6 +595,10 @@ void mesh_plink_quiesce(struct sta_info *sta) if (!ieee80211_vif_is_mesh(&sta->sdata->vif)) return; + /* no kernel mesh sta timers have been initialized */ + if (sta->sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) + return; + if (del_timer_sync(&sta->plink_timer)) sta->plink_timer_was_running = true; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 47a0f0601768..19db20a58e23 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -137,13 +137,8 @@ static void cleanup_single_sta(struct sta_info *sta) ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]); } -#ifdef CONFIG_MAC80211_MESH - if (ieee80211_vif_is_mesh(&sdata->vif)) { - mesh_accept_plinks_update(sdata); - mesh_plink_deactivate(sta); - del_timer_sync(&sta->plink_timer); - } -#endif + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_sta_cleanup(sta); cancel_work_sync(&sta->drv_unblock_wk); -- cgit v1.2.3 From 601513aa208f27ea87400a410d42c978421530ec Mon Sep 17 00:00:00 2001 From: Emanuel Taube Date: Wed, 6 Feb 2013 14:17:17 +0100 Subject: mac80211: Add the DS Params for mesh to every band There seems to be no reason, why it has to be limited to 2.4 GHz. Signed-off-by: Emanuel Taube [remove 'local' variable] Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0c51b78b8fdc..2bf0158c3f82 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -393,8 +393,6 @@ mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; u8 *pos; @@ -411,13 +409,10 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, chan = chanctx_conf->def.chan; rcu_read_unlock(); - sband = local->hw.wiphy->bands[chan->band]; - if (sband->band == IEEE80211_BAND_2GHZ) { - pos = skb_put(skb, 2 + 1); - *pos++ = WLAN_EID_DS_PARAMS; - *pos++ = 1; - *pos++ = ieee80211_frequency_to_channel(chan->center_freq); - } + pos = skb_put(skb, 2 + 1); + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = ieee80211_frequency_to_channel(chan->center_freq); return 0; } -- cgit v1.2.3 From f9e124fbd8cbea974b5dc7e9dafddd17d21df7e2 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 4 Feb 2013 17:44:44 +0000 Subject: mac80211: protect rx-path with spinlock This patch fixes the problem which was discussed in "mac80211: Fix PN corruption in case of multiple virtual interface" [1]. Amit Shakya reported a serious issue with my patch: mac80211: serialize rx path workers" [2]: In case, ieee80211_rx_handlers processing is going on for skbs received on one vif and at the same time, rx aggregation reorder timer expires on another vif then sta_rx_agg_reorder_timer_expired is invoked and it will push skbs into the single queue (local->rx_skb_queue). ieee80211_rx_handlers in the while loop assumes that the skbs are for the same sdata and sta. This assumption doesn't hold good in this scenario and the PN gets corrupted by PN received in other vif's skb, causing traffic to stop due to PN mismatch." [1] Message-Id: http://mid.gmane.org/201302041844.44436.chunkeey@googlemail.com [2] Commit-Id: 24a8fdad35835e8d71f7 Reported-by: Amit Shakya Signed-off-by: Christian Lamparter Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 9 +----- net/mac80211/main.c | 14 +------- net/mac80211/rx.c | 81 ++++++++++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5fe9db707880..080cf0942ce7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -958,14 +958,7 @@ struct ieee80211_local { struct sk_buff_head skb_queue; struct sk_buff_head skb_queue_unreliable; - /* - * Internal FIFO queue which is shared between multiple rx path - * stages. Its main task is to provide a serialization mechanism, - * so all rx handlers can enjoy having exclusive access to their - * private data structures. - */ - struct sk_buff_head rx_skb_queue; - bool running_rx_handler; /* protected by rx_skb_queue.lock */ + spinlock_t rx_path_lock; /* Station data */ /* diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2bdd454e8bcf..2220331f4b7c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -34,8 +34,6 @@ #include "cfg.h" #include "debugfs.h" -static struct lock_class_key ieee80211_rx_skb_queue_class; - void ieee80211_configure_filter(struct ieee80211_local *local) { u64 mc; @@ -613,21 +611,12 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, mutex_init(&local->key_mtx); spin_lock_init(&local->filter_lock); + spin_lock_init(&local->rx_path_lock); spin_lock_init(&local->queue_stop_reason_lock); INIT_LIST_HEAD(&local->chanctx_list); mutex_init(&local->chanctx_mtx); - /* - * The rx_skb_queue is only accessed from tasklets, - * but other SKB queues are used from within IRQ - * context. Therefore, this one needs a different - * locking class so our direct, non-irq-safe use of - * the queue's lock doesn't throw lockdep warnings. - */ - skb_queue_head_init_class(&local->rx_skb_queue, - &ieee80211_rx_skb_queue_class); - INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); INIT_WORK(&local->restart_work, ieee80211_restart_work); @@ -1089,7 +1078,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) wiphy_warn(local->hw.wiphy, "skb_queue not empty\n"); skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue_unreliable); - skb_queue_purge(&local->rx_skb_queue); destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c98be0593756..b5f1bba7ffe1 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -668,9 +668,9 @@ static inline u16 seq_sub(u16 sq1, u16 sq2) static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, - int index) + int index, + struct sk_buff_head *frames) { - struct ieee80211_local *local = sdata->local; struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; struct ieee80211_rx_status *status; @@ -684,7 +684,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, tid_agg_rx->reorder_buf[index] = NULL; status = IEEE80211_SKB_RXCB(skb); status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; - skb_queue_tail(&local->rx_skb_queue, skb); + __skb_queue_tail(frames, skb); no_frame: tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); @@ -692,7 +692,8 @@ no_frame: static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, - u16 head_seq_num) + u16 head_seq_num, + struct sk_buff_head *frames) { int index; @@ -701,7 +702,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; - ieee80211_release_reorder_frame(sdata, tid_agg_rx, index); + ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, + frames); } } @@ -717,7 +719,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata #define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10) static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, - struct tid_ampdu_rx *tid_agg_rx) + struct tid_ampdu_rx *tid_agg_rx, + struct sk_buff_head *frames) { int index, j; @@ -746,7 +749,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n"); - ieee80211_release_reorder_frame(sdata, tid_agg_rx, j); + ieee80211_release_reorder_frame(sdata, tid_agg_rx, j, + frames); /* * Increment the head seq# also for the skipped slots. @@ -756,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, skipped = 0; } } else while (tid_agg_rx->reorder_buf[index]) { - ieee80211_release_reorder_frame(sdata, tid_agg_rx, index); + ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, + frames); index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; } @@ -788,7 +793,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, */ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, - struct sk_buff *skb) + struct sk_buff *skb, + struct sk_buff_head *frames) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 sc = le16_to_cpu(hdr->seq_ctrl); @@ -816,7 +822,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); /* release stored frames up to new head to stack */ ieee80211_release_reorder_frames(sdata, tid_agg_rx, - head_seq_num); + head_seq_num, frames); } /* Now the new frame is always in the range of the reordering buffer */ @@ -846,7 +852,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata tid_agg_rx->reorder_buf[index] = skb; tid_agg_rx->reorder_time[index] = jiffies; tid_agg_rx->stored_mpdu_num++; - ieee80211_sta_reorder_release(sdata, tid_agg_rx); + ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); out: spin_unlock(&tid_agg_rx->reorder_lock); @@ -857,7 +863,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns * true if the MPDU was buffered, false if it should be processed. */ -static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) +static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, + struct sk_buff_head *frames) { struct sk_buff *skb = rx->skb; struct ieee80211_local *local = rx->local; @@ -922,11 +929,12 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) * sure that we cannot get to it any more before doing * anything with it. */ - if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb)) + if (ieee80211_sta_manage_reorder_buf(rx->sdata, tid_agg_rx, skb, + frames)) return; dont_reorder: - skb_queue_tail(&local->rx_skb_queue, skb); + __skb_queue_tail(frames, skb); } static ieee80211_rx_result debug_noinline @@ -2184,7 +2192,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) } static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) +ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames) { struct sk_buff *skb = rx->skb; struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data; @@ -2223,7 +2231,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) spin_lock(&tid_agg_rx->reorder_lock); /* release stored frames up to start of BAR */ ieee80211_release_reorder_frames(rx->sdata, tid_agg_rx, - start_seq_num); + start_seq_num, frames); spin_unlock(&tid_agg_rx->reorder_lock); kfree_skb(skb); @@ -2808,7 +2816,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, } } -static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) +static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, + struct sk_buff_head *frames) { ieee80211_rx_result res = RX_DROP_MONITOR; struct sk_buff *skb; @@ -2820,15 +2829,9 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) goto rxh_next; \ } while (0); - spin_lock(&rx->local->rx_skb_queue.lock); - if (rx->local->running_rx_handler) - goto unlock; - - rx->local->running_rx_handler = true; - - while ((skb = __skb_dequeue(&rx->local->rx_skb_queue))) { - spin_unlock(&rx->local->rx_skb_queue.lock); + spin_lock_bh(&rx->local->rx_path_lock); + while ((skb = __skb_dequeue(frames))) { /* * all the other fields are valid across frames * that belong to an aMPDU since they are on the @@ -2849,7 +2852,12 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) #endif CALL_RXH(ieee80211_rx_h_amsdu) CALL_RXH(ieee80211_rx_h_data) - CALL_RXH(ieee80211_rx_h_ctrl); + + /* special treatment -- needs the queue */ + res = ieee80211_rx_h_ctrl(rx, frames); + if (res != RX_CONTINUE) + goto rxh_next; + CALL_RXH(ieee80211_rx_h_mgmt_check) CALL_RXH(ieee80211_rx_h_action) CALL_RXH(ieee80211_rx_h_userspace_mgmt) @@ -2858,20 +2866,20 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) rxh_next: ieee80211_rx_handlers_result(rx, res); - spin_lock(&rx->local->rx_skb_queue.lock); + #undef CALL_RXH } - rx->local->running_rx_handler = false; - - unlock: - spin_unlock(&rx->local->rx_skb_queue.lock); + spin_unlock_bh(&rx->local->rx_path_lock); } static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) { + struct sk_buff_head reorder_release; ieee80211_rx_result res = RX_DROP_MONITOR; + __skb_queue_head_init(&reorder_release); + #define CALL_RXH(rxh) \ do { \ res = rxh(rx); \ @@ -2881,9 +2889,9 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) CALL_RXH(ieee80211_rx_h_check) - ieee80211_rx_reorder_ampdu(rx); + ieee80211_rx_reorder_ampdu(rx, &reorder_release); - ieee80211_rx_handlers(rx); + ieee80211_rx_handlers(rx, &reorder_release); return; rxh_next: @@ -2898,6 +2906,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) */ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) { + struct sk_buff_head frames; struct ieee80211_rx_data rx = { .sta = sta, .sdata = sta->sdata, @@ -2913,11 +2922,13 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) if (!tid_agg_rx) return; + __skb_queue_head_init(&frames); + spin_lock(&tid_agg_rx->reorder_lock); - ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx); + ieee80211_sta_reorder_release(sta->sdata, tid_agg_rx, &frames); spin_unlock(&tid_agg_rx->reorder_lock); - ieee80211_rx_handlers(&rx); + ieee80211_rx_handlers(&rx, &frames); } /* main receive path */ -- cgit v1.2.3 From 112c31f095a7106fbb3ced0dae50aa9b36ac2662 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 22:59:00 +0100 Subject: mac80211: fix AP beacon loss messages The messages currently refer to probe request probes, but on some devices null data packets will be used instead. Make the messages more generic. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 15f2edd08a95..40ce00c6d4ca 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1746,7 +1746,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, if (beacon) mlme_dbg_ratelimited(sdata, - "detected beacon loss from AP - sending probe request\n"); + "detected beacon loss from AP - probing\n"); ieee80211_cqm_rssi_notify(&sdata->vif, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); @@ -2641,7 +2641,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { mlme_dbg_ratelimited(sdata, - "cancelling probereq poll due to a received beacon\n"); + "cancelling AP probe due to a received beacon\n"); mutex_lock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; ieee80211_run_deferred_scan(local); -- cgit v1.2.3 From 757af6fefd53628bcfe75f7fbd302c5d4c02eba5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 21:29:59 +0100 Subject: mac80211: fix chandef tracing bug The chandef tracing writes center_freq1 twice, so that it is always 0 (no driver supports 80+80 yet) and leaves center_freq2 unset. Fix this mistake. Signed-off-by: Johannes Berg --- net/mac80211/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 6ca53d64cb28..a2ca72ce3380 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -36,7 +36,7 @@ __entry->control_freq = (c)->chan->center_freq; \ __entry->chan_width = (c)->width; \ __entry->center_freq1 = (c)->center_freq1; \ - __entry->center_freq1 = (c)->center_freq2; + __entry->center_freq2 = (c)->center_freq2; #define CHANDEF_PR_FMT " control:%d MHz width:%d center: %d/%d MHz" #define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \ __entry->center_freq1, __entry->center_freq2 -- cgit v1.2.3 From 1f4ac5a63f897a480fffd0d5c843b03f02c384a5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 12:07:44 +0100 Subject: mac80211: explicitly copy channels to VLANs where needed Currently the code assigns channel contexts to VLANs (for use by the TX/RX code) when the AP master gets its channel context assigned. This works fine, but in the upcoming radar detection work the VLANs don't require a channel context (during radar detection) and assigning one to them anyway causes issues with locking and also inconsistencies -- a VLAN interface that is added before radar detection would get the channel context, while one added during it wouldn't. Fix these issues moving the channel context copying to a new explicit operation that will not be used in the radar detection code. Acked-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 ++ net/mac80211/chan.c | 52 ++++++++++++++++++++++++++++++---------------- net/mac80211/ieee80211_i.h | 2 ++ 3 files changed, 38 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f4f7e7691077..8f6b593a921f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -933,6 +933,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, IEEE80211_CHANCTX_SHARED); if (err) return err; + ieee80211_vif_copy_chanctx_to_vlans(sdata, false); /* * Apply control port protocol, this allows us to @@ -1047,6 +1048,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); skb_queue_purge(&sdata->u.ap.ps.bc_buf); + ieee80211_vif_copy_chanctx_to_vlans(sdata, true); ieee80211_vif_release_channel(sdata); return 0; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 1bfe0a8b19d2..b5b50762f03e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -198,15 +198,6 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ctx = container_of(conf, struct ieee80211_chanctx, conf); - if (sdata->vif.type == NL80211_IFTYPE_AP) { - struct ieee80211_sub_if_data *vlan; - - /* for the VLAN list */ - ASSERT_RTNL(); - list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - rcu_assign_pointer(vlan->vif.chanctx_conf, NULL); - } - ieee80211_unassign_vif_chanctx(sdata, ctx); if (ctx->refcount == 0) ieee80211_free_chanctx(local, ctx); @@ -326,15 +317,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, goto out; } - if (sdata->vif.type == NL80211_IFTYPE_AP) { - struct ieee80211_sub_if_data *vlan; - - /* for the VLAN list */ - ASSERT_RTNL(); - list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf); - } - ieee80211_recalc_smps_chanctx(local, ctx); out: mutex_unlock(&local->chanctx_mtx); @@ -369,6 +351,40 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->chanctx_mtx); } +void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, + bool clear) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *vlan; + struct ieee80211_chanctx_conf *conf; + + ASSERT_RTNL(); + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) + return; + + mutex_lock(&local->chanctx_mtx); + + /* + * Check that conf exists, even when clearing this function + * must be called with the AP's channel context still there + * as it would otherwise cause VLANs to have an invalid + * channel context pointer for a while, possibly pointing + * to a channel context that has already been freed. + */ + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + WARN_ON(!conf); + + if (clear) + conf = NULL; + + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + rcu_assign_pointer(vlan->vif.chanctx_conf, conf); + + mutex_unlock(&local->chanctx_mtx); +} + void ieee80211_iter_chan_contexts_atomic( struct ieee80211_hw *hw, void (*iter)(struct ieee80211_hw *hw, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 080cf0942ce7..8e884fcbe79b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1605,6 +1605,8 @@ ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, enum ieee80211_chanctx_mode mode); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); +void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, + bool clear); void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); -- cgit v1.2.3 From 776b3580178f2065838fa0db0eb7a41b57495c0a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Feb 2013 02:06:18 +0100 Subject: cfg80211: track hidden SSID networks properly Currently, cfg80211 will copy beacon IEs from a previously received hidden SSID beacon to a probe response entry, if that entry is created after the beacon entry. However, if it is the other way around, or if the beacon is updated, such changes aren't propagated. Fix this by tracking the relation between the probe response and beacon BSS structs in this case. In case drivers have private data stored in a BSS struct and need access to such data from a beacon entry, cfg80211 now provides the hidden_beacon_bss pointer from the probe response entry to the beacon entry. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++ net/wireless/core.h | 4 +- net/wireless/scan.c | 273 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 232 insertions(+), 54 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5d8554bd95da..04a702d1f2a5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1292,7 +1292,14 @@ struct cfg80211_bss_ies { * either the beacon_ies or proberesp_ies depending on whether Probe * Response frame has been received * @beacon_ies: the information elements from the last Beacon frame + * (implementation note: if @hidden_beacon_bss is set this struct doesn't + * own the beacon_ies, but they're just pointers to the ones from the + * @hidden_beacon_bss struct) * @proberesp_ies: the information elements from the last Probe Response frame + * @hidden_beacon_bss: in case this BSS struct represents a probe response from + * a BSS that hides the SSID in its beacon, this points to the BSS struct + * that holds the beacon data. @beacon_ies is still valid, of course, and + * points to the same data as hidden_beacon_bss->beacon_ies in that case. * @signal: signal strength value (type depends on the wiphy's signal_type) * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ @@ -1305,6 +1312,8 @@ struct cfg80211_bss { const struct cfg80211_bss_ies __rcu *beacon_ies; const struct cfg80211_bss_ies __rcu *proberesp_ies; + struct cfg80211_bss *hidden_beacon_bss; + s32 signal; u16 beacon_interval; diff --git a/net/wireless/core.h b/net/wireless/core.h index 8396f7671c8d..37d70dc2fe82 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -124,9 +123,10 @@ static inline void assert_cfg80211_lock(void) struct cfg80211_internal_bss { struct list_head list; + struct list_head hidden_list; struct rb_node rbn; unsigned long ts; - struct kref ref; + unsigned long refcount; atomic_t hold; /* must be last because of priv member */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index dacb44ac2f74..5e0983d60428 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -19,46 +19,124 @@ #include "wext-compat.h" #include "rdev-ops.h" +/** + * DOC: BSS tree/list structure + * + * At the top level, the BSS list is kept in both a list in each + * registered device (@bss_list) as well as an RB-tree for faster + * lookup. In the RB-tree, entries can be looked up using their + * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID + * for other BSSes. + * + * Due to the possibility of hidden SSIDs, there's a second level + * structure, the "hidden_list" and "hidden_beacon_bss" pointer. + * The hidden_list connects all BSSes belonging to a single AP + * that has a hidden SSID, and connects beacon and probe response + * entries. For a probe response entry for a hidden SSID, the + * hidden_beacon_bss pointer points to the BSS struct holding the + * beacon's information. + * + * Reference counting is done for all these references except for + * the hidden_list, so that a beacon BSS struct that is otherwise + * not referenced has one reference for being on the bss_list and + * one for each probe response entry that points to it using the + * hidden_beacon_bss pointer. When a BSS struct that has such a + * pointer is get/put, the refcount update is also propagated to + * the referenced struct, this ensure that it cannot get removed + * while somebody is using the probe response version. + * + * Note that the hidden_beacon_bss pointer never changes, due to + * the reference counting. Therefore, no locking is needed for + * it. + * + * Also note that the hidden_beacon_bss pointer is only relevant + * if the driver uses something other than the IEs, e.g. private + * data stored stored in the BSS struct, since the beacon IEs are + * also linked into the probe response struct. + */ + #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) -static void bss_release(struct kref *ref) +static void bss_free(struct cfg80211_internal_bss *bss) { struct cfg80211_bss_ies *ies; - struct cfg80211_internal_bss *bss; - - bss = container_of(ref, struct cfg80211_internal_bss, ref); if (WARN_ON(atomic_read(&bss->hold))) return; ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); - if (ies) + if (ies && !bss->pub.hidden_beacon_bss) kfree_rcu(ies, rcu_head); ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); if (ies) kfree_rcu(ies, rcu_head); + /* + * This happens when the module is removed, it doesn't + * really matter any more save for completeness + */ + if (!list_empty(&bss->hidden_list)) + list_del(&bss->hidden_list); + kfree(bss); } -static inline void bss_ref_get(struct cfg80211_internal_bss *bss) +static inline void bss_ref_get(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) { - kref_get(&bss->ref); + lockdep_assert_held(&dev->bss_lock); + + bss->refcount++; + if (bss->pub.hidden_beacon_bss) { + bss = container_of(bss->pub.hidden_beacon_bss, + struct cfg80211_internal_bss, + pub); + bss->refcount++; + } } -static inline void bss_ref_put(struct cfg80211_internal_bss *bss) +static inline void bss_ref_put(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) { - kref_put(&bss->ref, bss_release); + lockdep_assert_held(&dev->bss_lock); + + if (bss->pub.hidden_beacon_bss) { + struct cfg80211_internal_bss *hbss; + hbss = container_of(bss->pub.hidden_beacon_bss, + struct cfg80211_internal_bss, + pub); + hbss->refcount--; + if (hbss->refcount == 0) + bss_free(hbss); + } + bss->refcount--; + if (bss->refcount == 0) + bss_free(bss); } -static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, +static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { lockdep_assert_held(&dev->bss_lock); + if (!list_empty(&bss->hidden_list)) { + /* + * don't remove the beacon entry if it has + * probe responses associated with it + */ + if (!bss->pub.hidden_beacon_bss) + return false; + /* + * if it's a probe response entry break its + * link to the other entries in the group + */ + list_del_init(&bss->hidden_list); + } + list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); - bss_ref_put(bss); + bss_ref_put(dev, bss); + return true; } static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, @@ -75,8 +153,8 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, if (!time_after(expire_time, bss->ts)) continue; - __cfg80211_unlink_bss(dev, bss); - expired = true; + if (__cfg80211_unlink_bss(dev, bss)) + expired = true; } if (expired) @@ -466,7 +544,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { res = bss; - bss_ref_get(res); + bss_ref_get(dev, res); break; } } @@ -532,23 +610,67 @@ rb_find_bss(struct cfg80211_registered_device *dev, return NULL; } -static void -copy_hidden_ies(struct cfg80211_internal_bss *res, - struct cfg80211_internal_bss *hidden) +static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *new) { const struct cfg80211_bss_ies *ies; + struct cfg80211_internal_bss *bss; + const u8 *ie; + int i, ssidlen; + u8 fold = 0; - if (rcu_access_pointer(res->pub.beacon_ies)) - return; - - ies = rcu_access_pointer(hidden->pub.beacon_ies); + ies = rcu_access_pointer(new->pub.beacon_ies); if (WARN_ON(!ies)) - return; + return false; - ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); - if (unlikely(!ies)) - return; - rcu_assign_pointer(res->pub.beacon_ies, ies); + ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); + if (!ie) { + /* nothing to do */ + return true; + } + + ssidlen = ie[1]; + for (i = 0; i < ssidlen; i++) + fold |= ie[2 + i]; + + if (fold) { + /* not a hidden SSID */ + return true; + } + + /* This is the bad part ... */ + + list_for_each_entry(bss, &dev->bss_list, list) { + if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) + continue; + if (bss->pub.channel != new->pub.channel) + continue; + if (rcu_access_pointer(bss->pub.beacon_ies)) + continue; + ies = rcu_access_pointer(bss->pub.ies); + if (!ies) + continue; + ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); + if (!ie) + continue; + if (ssidlen && ie[1] != ssidlen) + continue; + /* that would be odd ... */ + if (bss->pub.beacon_ies) + continue; + if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) + continue; + if (WARN_ON_ONCE(!list_empty(&bss->hidden_list))) + list_del(&bss->hidden_list); + /* combine them */ + list_add(&bss->hidden_list, &new->hidden_list); + bss->pub.hidden_beacon_bss = &new->pub; + new->refcount += bss->refcount; + rcu_assign_pointer(bss->pub.beacon_ies, + new->pub.beacon_ies); + } + + return true; } static struct cfg80211_internal_bss * @@ -594,6 +716,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, rcu_head); } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { const struct cfg80211_bss_ies *old; + struct cfg80211_internal_bss *bss; + + if (found->pub.hidden_beacon_bss && + !list_empty(&found->hidden_list)) { + /* + * The found BSS struct is one of the probe + * response members of a group, but we're + * receiving a beacon (beacon_ies in the tmp + * bss is used). This can only mean that the + * AP changed its beacon from not having an + * SSID to showing it, which is confusing so + * drop this information. + */ + goto drop; + } old = rcu_access_pointer(found->pub.beacon_ies); @@ -605,6 +742,18 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, rcu_assign_pointer(found->pub.ies, tmp->pub.beacon_ies); + /* Assign beacon IEs to all sub entries */ + list_for_each_entry(bss, &found->hidden_list, + hidden_list) { + const struct cfg80211_bss_ies *ies; + + ies = rcu_access_pointer(bss->pub.beacon_ies); + WARN_ON(ies != old); + + rcu_assign_pointer(bss->pub.beacon_ies, + tmp->pub.beacon_ies); + } + if (old) kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); @@ -614,24 +763,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *hidden; struct cfg80211_bss_ies *ies; - /* First check if the beacon is a probe response from - * a hidden bss. If so, copy beacon ies (with nullified - * ssid) into the probe response bss entry (with real ssid). - * It is required basically for PSM implementation - * (probe responses do not contain tim ie) */ - - /* TODO: The code is not trying to update existing probe - * response bss entries when beacon ies are - * getting changed. */ - hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); - if (hidden) { - copy_hidden_ies(tmp, hidden); - } else { - hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL); - if (hidden) - copy_hidden_ies(tmp, hidden); - } - /* * create a copy -- the "res" variable that is passed in * is allocated on the stack since it's not needed in the @@ -646,21 +777,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); if (ies) kfree_rcu(ies, rcu_head); - spin_unlock_bh(&dev->bss_lock); - return NULL; + goto drop; } memcpy(new, tmp, sizeof(*new)); - kref_init(&new->ref); + new->refcount = 1; + INIT_LIST_HEAD(&new->hidden_list); + + if (rcu_access_pointer(tmp->pub.proberesp_ies)) { + hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); + if (!hidden) + hidden = rb_find_bss(dev, tmp, + BSS_CMP_HIDE_NUL); + if (hidden) { + new->pub.hidden_beacon_bss = &hidden->pub; + list_add(&new->hidden_list, + &hidden->hidden_list); + hidden->refcount++; + rcu_assign_pointer(new->pub.beacon_ies, + hidden->pub.beacon_ies); + } + } else { + /* + * Ok so we found a beacon, and don't have an entry. If + * it's a beacon with hidden SSID, we might be in for an + * expensive search for any probe responses that should + * be grouped with this beacon for updates ... + */ + if (!cfg80211_combine_bsses(dev, new)) { + kfree(new); + goto drop; + } + } + list_add_tail(&new->list, &dev->bss_list); rb_insert_bss(dev, new); found = new; } dev->bss_generation++; + bss_ref_get(dev, found); spin_unlock_bh(&dev->bss_lock); - bss_ref_get(found); return found; + drop: + spin_unlock_bh(&dev->bss_lock); + return NULL; } static struct ieee80211_channel * @@ -820,25 +981,33 @@ EXPORT_SYMBOL(cfg80211_inform_bss_frame); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss; if (!pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss_ref_get(bss); + + spin_lock_bh(&dev->bss_lock); + bss_ref_get(dev, bss); + spin_unlock_bh(&dev->bss_lock); } EXPORT_SYMBOL(cfg80211_ref_bss); void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss; if (!pub) return; bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss_ref_put(bss); + + spin_lock_bh(&dev->bss_lock); + bss_ref_put(dev, bss); + spin_unlock_bh(&dev->bss_lock); } EXPORT_SYMBOL(cfg80211_put_bss); @@ -854,8 +1023,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) spin_lock_bh(&dev->bss_lock); if (!list_empty(&bss->list)) { - __cfg80211_unlink_bss(dev, bss); - dev->bss_generation++; + if (__cfg80211_unlink_bss(dev, bss)) + dev->bss_generation++; } spin_unlock_bh(&dev->bss_lock); } -- cgit v1.2.3 From b207cdb07f3f01ec1adaac62e9d0cc918c60a81a Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Sat, 22 Dec 2012 10:43:33 +0200 Subject: mac80211: add vif debugfs driver callbacks Add debugfs driver callbacks so drivers can add debugfs entries for interfaces. Note that they _must_ remove the entries again as add/remove in the driver doesn't correspond to add/remove in debugfs; the former is up/down while the latter is netdev create/destroy. Signed-off-by: Alexander Bondar Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 18 ++++++++++++++++++ net/mac80211/driver-ops.h | 37 +++++++++++++++++++++++++++++++++++++ net/mac80211/iface.c | 4 ++++ 3 files changed, 59 insertions(+) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7da11211825d..46e08ba92b97 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2167,6 +2167,18 @@ enum ieee80211_rate_control_changed { * MAC address of the device going away. * Hence, this callback must be implemented. It can sleep. * + * @add_interface_debugfs: Drivers can use this callback to add debugfs files + * when a vif is added to mac80211. This callback and + * @remove_interface_debugfs should be within a CONFIG_MAC80211_DEBUGFS + * conditional. @remove_interface_debugfs must be provided for cleanup. + * This callback can sleep. + * + * @remove_interface_debugfs: Remove the debugfs files which were added using + * @add_interface_debugfs. This callback must remove all debugfs entries + * that were added because mac80211 only removes interface debugfs when the + * interface is destroyed, not when it is removed from the driver. + * This callback can sleep. + * * @config: Handler for configuration requests. IEEE 802.11 code calls this * function to change hardware configuration, e.g., channel. * This function should never fail but returns a negative error code @@ -2580,6 +2592,12 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); + void (*add_interface_debugfs)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct dentry *dir); + void (*remove_interface_debugfs)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct dentry *dir); #endif void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 434b3c4f31b5..2b08b9982d06 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -528,6 +528,43 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, local->ops->sta_remove_debugfs(&local->hw, &sdata->vif, sta, dir); } + +static inline +void drv_add_interface_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + check_sdata_in_driver(sdata); + + if (!local->ops->add_interface_debugfs) + return; + + local->ops->add_interface_debugfs(&local->hw, &sdata->vif, + sdata->debugfs.dir); +} + +static inline +void drv_remove_interface_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + check_sdata_in_driver(sdata); + + if (!local->ops->remove_interface_debugfs) + return; + + local->ops->remove_interface_debugfs(&local->hw, &sdata->vif, + sdata->debugfs.dir); +} +#else +static inline +void drv_add_interface_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) {} +static inline +void drv_remove_interface_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) {} #endif static inline __must_check diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0a36dc6346bb..deb78e864af6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -621,6 +621,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) goto err_del_interface; } + drv_add_interface_debugfs(local, sdata); + if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; local->fif_probe_req++; @@ -882,6 +884,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, */ ieee80211_free_keys(sdata); + drv_remove_interface_debugfs(local, sdata); + if (going_down) drv_remove_interface(local, sdata); } -- cgit v1.2.3 From 83c7aa1a1475ae1c42640ab6e4559016142efc67 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Feb 2013 16:51:29 +0100 Subject: cfg80211: remove scan ies NULL check There's no way scan BSS IEs can be NULL as even if the allocation fails the frame is discarded. Remove some code checking for this and document that it is always non-NULL. Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/sta_ioctl.c | 5 ----- include/net/cfg80211.h | 8 ++++---- net/wireless/scan.c | 11 +++-------- 3 files changed, 7 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index ee85b41a4dfd..8866a2b69c94 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -162,11 +162,6 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, rcu_read_lock(); ies = rcu_dereference(bss->ies); - if (WARN_ON(!ies)) { - /* should never happen */ - rcu_read_unlock(); - return -EINVAL; - } beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); beacon_ie_len = ies->len; rcu_read_unlock(); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 04a702d1f2a5..fb766314b3a1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1287,10 +1287,10 @@ struct cfg80211_bss_ies { * @tsf: timestamp of last received update * @beacon_interval: the beacon interval as from the frame * @capability: the capability field in host byte order - * @ies: the information elements (Note that there - * is no guarantee that these are well-formed!); this is a pointer to - * either the beacon_ies or proberesp_ies depending on whether Probe - * Response frame has been received + * @ies: the information elements (Note that there is no guarantee that these + * are well-formed!); this is a pointer to either the beacon_ies or + * proberesp_ies depending on whether Probe Response frame has been + * received. It is always non-%NULL. * @beacon_ies: the information elements from the last Beacon frame * (implementation note: if @hidden_beacon_bss is set this struct doesn't * own the beacon_ies, but they're just pointers to the ones from the diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 5e0983d60428..02a238329c83 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1293,15 +1293,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, rcu_read_lock(); ies = rcu_dereference(bss->pub.ies); - if (ies) { - rem = ies->len; - ie = ies->data; - } else { - rem = 0; - ie = NULL; - } + rem = ies->len; + ie = ies->data; - while (ies && rem >= 2) { + while (rem >= 2) { /* invalid data */ if (ie[1] > rem - 2) break; -- cgit v1.2.3 From 8cef2c9df88fdd13f518e6607de9d664b31f26cc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Feb 2013 16:54:31 +0100 Subject: cfg80211: move TSF into IEs While technically the TSF isn't an IE, it can be necessary to distinguish between the TSF from a beacon and a probe response, in particular in order to know the next DTIM TBTT, as not all APs are spec compliant wrt. TSF==0 being a DTIM TBTT and thus the DTIM count needs to be taken into account as well. To allow this, move the TSF into the IE struct so it can be known whence it came. Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- include/net/cfg80211.h | 5 ++--- net/mac80211/ibss.c | 14 ++++++++++---- net/mac80211/mlme.c | 6 +++++- net/wireless/nl80211.c | 27 ++++++++++++++++----------- net/wireless/scan.c | 7 +++---- 6 files changed, 37 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 8866a2b69c94..0d018460daf9 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -164,6 +164,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, ies = rcu_dereference(bss->ies); beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); beacon_ie_len = ies->len; + bss_desc->timestamp = ies->tsf; rcu_read_unlock(); if (!beacon_ie) { @@ -179,7 +180,6 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, bss_desc->cap_info_bitmap = bss->capability; bss_desc->bss_band = bss_priv->band; bss_desc->fw_tsf = bss_priv->fw_tsf; - bss_desc->timestamp = bss->tsf; if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { dev_dbg(priv->adapter->dev, "info: InterpretIE: AP WEP enabled\n"); bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fb766314b3a1..77686ca28948 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1266,11 +1266,13 @@ enum cfg80211_signal_type { /** * struct cfg80211_bss_ie_data - BSS entry IE data + * @tsf: TSF contained in the frame that carried these IEs * @rcu_head: internal use, for freeing * @len: length of the IEs * @data: IE data */ struct cfg80211_bss_ies { + u64 tsf; struct rcu_head rcu_head; int len; u8 data[]; @@ -1284,7 +1286,6 @@ struct cfg80211_bss_ies { * * @channel: channel this BSS is on * @bssid: BSSID of the BSS - * @tsf: timestamp of last received update * @beacon_interval: the beacon interval as from the frame * @capability: the capability field in host byte order * @ies: the information elements (Note that there is no guarantee that these @@ -1304,8 +1305,6 @@ struct cfg80211_bss_ies { * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes */ struct cfg80211_bss { - u64 tsf; - struct ieee80211_channel *channel; const struct cfg80211_bss_ies __rcu *ies; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 71c55cc0f7b6..055fa9436e95 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -242,6 +242,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, u32 basic_rates; int i, j; u16 beacon_int = cbss->beacon_interval; + const struct cfg80211_bss_ies *ies; + u64 tsf; lockdep_assert_held(&sdata->u.ibss.mtx); @@ -265,13 +267,17 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } } + rcu_read_lock(); + ies = rcu_dereference(cbss->ies); + tsf = ies->tsf; + rcu_read_unlock(); + __ieee80211_sta_join_ibss(sdata, cbss->bssid, beacon_int, cbss->channel, basic_rates, cbss->capability, - cbss->tsf, - false); + tsf, false); } static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, @@ -535,8 +541,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, cbss = container_of((void *)bss, struct cfg80211_bss, priv); - /* was just updated in ieee80211_bss_info_update */ - beacon_timestamp = cbss->tsf; + /* same for beacon and probe response */ + beacon_timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); /* check if we need to merge IBSS */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 40ce00c6d4ca..51eca5a0cdaa 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3667,6 +3667,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; struct ieee80211_supported_band *sband; + const struct cfg80211_bss_ies *ies; sband = local->hw.wiphy->bands[cbss->channel->band]; @@ -3710,7 +3711,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, /* set timing information */ sdata->vif.bss_conf.beacon_int = cbss->beacon_interval; - sdata->vif.bss_conf.sync_tsf = cbss->tsf; + rcu_read_lock(); + ies = rcu_dereference(cbss->ies); + sdata->vif.bss_conf.sync_tsf = ies->tsf; + rcu_read_unlock(); sdata->vif.bss_conf.sync_device_ts = bss->device_ts; /* tell driver about BSSID, basic rates and timing */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 807d448e702e..93bc63eae076 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4997,6 +4997,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, const struct cfg80211_bss_ies *ies; void *hdr; struct nlattr *bss; + bool tsf = false; ASSERT_WDEV_LOCK(wdev); @@ -5020,22 +5021,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, rcu_read_lock(); ies = rcu_dereference(res->ies); - if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, - ies->len, ies->data)) { - rcu_read_unlock(); - goto nla_put_failure; + if (ies) { + if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) + goto fail_unlock_rcu; + tsf = true; + if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, + ies->len, ies->data)) + goto fail_unlock_rcu; } ies = rcu_dereference(res->beacon_ies); - if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, - ies->len, ies->data)) { - rcu_read_unlock(); - goto nla_put_failure; + if (ies) { + if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) + goto fail_unlock_rcu; + if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, + ies->len, ies->data)) + goto fail_unlock_rcu; } rcu_read_unlock(); - if (res->tsf && - nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) - goto nla_put_failure; if (res->beacon_interval && nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval)) goto nla_put_failure; @@ -5080,6 +5083,8 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, return genlmsg_end(msg, hdr); + fail_unlock_rcu: + rcu_read_unlock(); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 02a238329c83..b7a167984986 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -695,7 +695,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, if (found) { found->pub.beacon_interval = tmp->pub.beacon_interval; - found->pub.tsf = tmp->pub.tsf; found->pub.signal = tmp->pub.signal; found->pub.capability = tmp->pub.capability; found->ts = tmp->ts; @@ -880,7 +879,6 @@ cfg80211_inform_bss(struct wiphy *wiphy, memcpy(tmp.pub.bssid, bssid, ETH_ALEN); tmp.pub.channel = channel; tmp.pub.signal = signal; - tmp.pub.tsf = tsf; tmp.pub.beacon_interval = beacon_interval; tmp.pub.capability = capability; /* @@ -895,6 +893,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, if (!ies) return NULL; ies->len = ielen; + ies->tsf = tsf; memcpy(ies->data, ie, ielen); rcu_assign_pointer(tmp.pub.beacon_ies, ies); @@ -951,6 +950,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, if (!ies) return NULL; ies->len = ielen; + ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); if (ieee80211_is_probe_resp(mgmt->frame_control)) @@ -962,7 +962,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); tmp.pub.channel = channel; tmp.pub.signal = signal; - tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); @@ -1409,7 +1408,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, if (buf) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); + sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); iwe.u.data.length = strlen(buf); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); -- cgit v1.2.3 From ef429dadf33feeb150098dbe84ccaa877e3261f6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Feb 2013 17:48:40 +0100 Subject: mac80211: introduce beacon-only timing data In order to be able to predict the next DTIM TBTT in the driver, add the ability to use timing data from beacons only with the new hardware flag IEEE80211_HW_TIMING_BEACON_ONLY and the BSS info value sync_dtim_count which is only valid if the timing data came from a beacon. The data can only come from a beacon, and if no beacon was received before association it is updated later together with the DTIM count notification. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 14 +++++++++- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/mlme.c | 65 +++++++++++++++++++++++++++++++++++++++++++--- net/mac80211/scan.c | 5 +++- net/mac80211/trace.h | 2 ++ 5 files changed, 81 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 46e08ba92b97..b9e02460d3d7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -277,9 +277,16 @@ enum ieee80211_rssi_event { * valid in station mode only if after the driver was notified * with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then. * @sync_tsf: last beacon's/probe response's TSF timestamp (could be old - * as it may have been received during scanning long ago) + * as it may have been received during scanning long ago). If the + * HW flag %IEEE80211_HW_TIMING_BEACON_ONLY is set, then this can + * only come from a beacon, but might not become valid until after + * association when a beacon is received (which is notified with the + * %BSS_CHANGED_DTIM flag.) * @sync_device_ts: the device timestamp corresponding to the sync_tsf, * the driver/device can use this to calculate synchronisation + * (see @sync_tsf) + * @sync_dtim_count: Only valid when %IEEE80211_HW_TIMING_BEACON_ONLY + * is requested, see @sync_tsf/@sync_device_ts. * @beacon_int: beacon interval * @assoc_capability: capabilities taken from assoc resp * @basic_rates: bitmap of basic rates, each bit stands for an @@ -331,6 +338,7 @@ struct ieee80211_bss_conf { u16 assoc_capability; u64 sync_tsf; u32 sync_device_ts; + u8 sync_dtim_count; u32 basic_rates; int mcast_rate[IEEE80211_NUM_BANDS]; u16 ht_operation_mode; @@ -1371,6 +1379,9 @@ struct ieee80211_tx_control { * @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any * P2P Interface. This will be honoured even if more than one interface * is supported. + * + * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames + * only, to allow getting TBTT of a DTIM beacon. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1399,6 +1410,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, + IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, }; /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8e884fcbe79b..d794856933f2 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -86,7 +86,7 @@ struct ieee80211_fragment_entry { struct ieee80211_bss { - u32 device_ts; + u32 device_ts_beacon, device_ts_presp; bool wmm_used; bool uapsd_supported; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 51eca5a0cdaa..a29d09cb834c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2567,6 +2567,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; ifmgd->assoc_data->need_beacon = false; + if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { + sdata->vif.bss_conf.sync_tsf = + le64_to_cpu(mgmt->u.beacon.timestamp); + sdata->vif.bss_conf.sync_device_ts = + rx_status->device_timestamp; + if (elems.tim) + sdata->vif.bss_conf.sync_dtim_count = + elems.tim->dtim_count; + else + sdata->vif.bss_conf.sync_dtim_count = 0; + } /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; run_again(ifmgd, ifmgd->assoc_data->timeout); @@ -2725,7 +2736,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, /* * If we haven't had a beacon before, tell the driver about the - * DTIM period now. + * DTIM period (and beacon timing if desired) now. */ if (!bss_conf->dtim_period) { /* a few bogus AP send dtim_period = 0 or no TIM IE */ @@ -2733,6 +2744,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, bss_conf->dtim_period = elems.tim->dtim_period ?: 1; else bss_conf->dtim_period = 1; + + if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { + sdata->vif.bss_conf.sync_tsf = + le64_to_cpu(mgmt->u.beacon.timestamp); + sdata->vif.bss_conf.sync_device_ts = + rx_status->device_timestamp; + if (elems.tim) + sdata->vif.bss_conf.sync_dtim_count = + elems.tim->dtim_count; + else + sdata->vif.bss_conf.sync_dtim_count = 0; + } + changed |= BSS_CHANGED_DTIM_PERIOD; } @@ -3712,10 +3736,33 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, /* set timing information */ sdata->vif.bss_conf.beacon_int = cbss->beacon_interval; rcu_read_lock(); - ies = rcu_dereference(cbss->ies); - sdata->vif.bss_conf.sync_tsf = ies->tsf; + ies = rcu_dereference(cbss->beacon_ies); + if (ies) { + const u8 *tim_ie; + + sdata->vif.bss_conf.sync_tsf = ies->tsf; + sdata->vif.bss_conf.sync_device_ts = + bss->device_ts_beacon; + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, + ies->data, ies->len); + if (tim_ie && tim_ie[1] >= 2) + sdata->vif.bss_conf.sync_dtim_count = tim_ie[2]; + else + sdata->vif.bss_conf.sync_dtim_count = 0; + } else if (!(local->hw.flags & + IEEE80211_HW_TIMING_BEACON_ONLY)) { + ies = rcu_dereference(cbss->proberesp_ies); + /* must be non-NULL since beacon IEs were NULL */ + sdata->vif.bss_conf.sync_tsf = ies->tsf; + sdata->vif.bss_conf.sync_device_ts = + bss->device_ts_presp; + sdata->vif.bss_conf.sync_dtim_count = 0; + } else { + sdata->vif.bss_conf.sync_tsf = 0; + sdata->vif.bss_conf.sync_device_ts = 0; + sdata->vif.bss_conf.sync_dtim_count = 0; + } rcu_read_unlock(); - sdata->vif.bss_conf.sync_device_ts = bss->device_ts; /* tell driver about BSSID, basic rates and timing */ ieee80211_bss_info_change_notify(sdata, @@ -4041,13 +4088,23 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, beacon_ies->data, beacon_ies->len); + u8 dtim_count = 0; + if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) { const struct ieee80211_tim_ie *tim; tim = (void *)(tim_ie + 2); ifmgd->dtim_period = tim->dtim_period; + dtim_count = tim->dtim_count; } assoc_data->have_beacon = true; assoc_data->timeout = jiffies; + + if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { + sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf; + sdata->vif.bss_conf.sync_device_ts = + bss->device_ts_beacon; + sdata->vif.bss_conf.sync_dtim_count = dtim_count; + } } else { assoc_data->timeout = jiffies; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 400153f7f21f..edd47d9acb99 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -80,7 +80,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss = (void *)cbss->priv; - bss->device_ts = rx_status->device_timestamp; + if (beacon) + bss->device_ts_beacon = rx_status->device_timestamp; + else + bss->device_ts_presp = rx_status->device_timestamp; if (elems->parse_error) { if (beacon) diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index a2ca72ce3380..0bdd7aeb8958 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -340,6 +340,7 @@ TRACE_EVENT(drv_bss_info_changed, __field(u16, assoc_cap) __field(u64, sync_tsf) __field(u32, sync_device_ts) + __field(u8, sync_dtim_count) __field(u32, basic_rates) __array(int, mcast_rate, IEEE80211_NUM_BANDS) __field(u16, ht_operation_mode) @@ -379,6 +380,7 @@ TRACE_EVENT(drv_bss_info_changed, __entry->assoc_cap = info->assoc_capability; __entry->sync_tsf = info->sync_tsf; __entry->sync_device_ts = info->sync_device_ts; + __entry->sync_dtim_count = info->sync_dtim_count; __entry->basic_rates = info->basic_rates; memcpy(__entry->mcast_rate, info->mcast_rate, sizeof(__entry->mcast_rate)); -- cgit v1.2.3 From 09b85568c142fc1c776dea86a24fcb05f0eeb48b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Feb 2013 23:07:41 +0100 Subject: mac80211: remove dynamic PS driver interface The functions were added for some sort of Bluetooth coexistence, but aren't used, so remove them again. Reviewed-by: Luciano Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 39 --------------------------------------- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/mlme.c | 42 ++---------------------------------------- 3 files changed, 2 insertions(+), 81 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b9e02460d3d7..8d25769b3259 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1695,15 +1695,6 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS * enabled whenever user has enabled powersave. * - * Some hardware need to toggle a single shared antenna between WLAN and - * Bluetooth to facilitate co-existence. These types of hardware set - * limitations on the use of host controlled dynamic powersave whenever there - * is simultaneous WLAN and Bluetooth traffic. For these types of hardware, the - * driver may request temporarily going into full power save, in order to - * enable toggling the antenna between BT and WLAN. If the driver requests - * disabling dynamic powersave, the @dynamic_ps_timeout value will be - * temporarily set to zero until the driver re-enables dynamic powersave. - * * Driver informs U-APSD client support by enabling * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS @@ -3938,36 +3929,6 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif); */ void ieee80211_resume_disconnect(struct ieee80211_vif *vif); -/** - * ieee80211_disable_dyn_ps - force mac80211 to temporarily disable dynamic psm - * - * @vif: &struct ieee80211_vif pointer from the add_interface callback. - * - * Some hardware require full power save to manage simultaneous BT traffic - * on the WLAN frequency. Full PSM is required periodically, whenever there are - * burst of BT traffic. The hardware gets information of BT traffic via - * hardware co-existence lines, and consequentially requests mac80211 to - * (temporarily) enter full psm. - * This function will only temporarily disable dynamic PS, not enable PSM if - * it was not already enabled. - * The driver must make sure to re-enable dynamic PS using - * ieee80211_enable_dyn_ps() if the driver has disabled it. - * - */ -void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif); - -/** - * ieee80211_enable_dyn_ps - restore dynamic psm after being disabled - * - * @vif: &struct ieee80211_vif pointer from the add_interface callback. - * - * This function restores dynamic PS after being temporarily disabled via - * ieee80211_disable_dyn_ps(). Each ieee80211_disable_dyn_ps() call must - * be coupled with an eventual call to this function. - * - */ -void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif); - /** * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring * rssi threshold triggered diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d794856933f2..fafeb309a5fe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1099,8 +1099,6 @@ struct ieee80211_local { * this will override whatever chosen by mac80211 internally. */ int dynamic_ps_forced_timeout; - int dynamic_ps_user_timeout; - bool disable_dynamic_ps; int user_power_level; /* in dBm, for all interfaces */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a29d09cb834c..1e7662c5788e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -951,39 +951,6 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) -{ - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_local *local = sdata->local; - struct ieee80211_conf *conf = &local->hw.conf; - - WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || - !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || - (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); - - local->disable_dynamic_ps = false; - conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout; -} -EXPORT_SYMBOL(ieee80211_enable_dyn_ps); - -void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif) -{ - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_local *local = sdata->local; - struct ieee80211_conf *conf = &local->hw.conf; - - WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || - !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) || - (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)); - - local->disable_dynamic_ps = true; - conf->dynamic_ps_timeout = 0; - del_timer_sync(&local->dynamic_ps_timer); - ieee80211_queue_work(&local->hw, - &local->dynamic_ps_enable_work); -} -EXPORT_SYMBOL(ieee80211_disable_dyn_ps); - /* powersave */ static void ieee80211_enable_ps(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) @@ -1086,7 +1053,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) } if (count == 1 && ieee80211_powersave_allowed(found)) { - struct ieee80211_conf *conf = &local->hw.conf; s32 beaconint_us; if (latency < 0) @@ -1110,10 +1076,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) else timeout = 100; } - local->dynamic_ps_user_timeout = timeout; - if (!local->disable_dynamic_ps) - conf->dynamic_ps_timeout = - local->dynamic_ps_user_timeout; + local->hw.conf.dynamic_ps_timeout = timeout; if (beaconint_us > latency) { local->ps_sdata = NULL; @@ -1183,8 +1146,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) if (local->hw.conf.flags & IEEE80211_CONF_PS) return; - if (!local->disable_dynamic_ps && - local->hw.conf.dynamic_ps_timeout > 0) { + if (local->hw.conf.dynamic_ps_timeout > 0) { /* don't enter PS if TX frames are pending */ if (drv_tx_frames_pending(local)) { mod_timer(&local->dynamic_ps_timer, jiffies + -- cgit v1.2.3 From f1e3e0515646dd0f4c783c1c39839d2706501344 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Feb 2013 23:57:57 +0100 Subject: mac80211: remove IEEE80211_HW_SCAN_WHILE_IDLE There are only a few drivers that use HW scan, and all of those don't need a non-idle transition before starting the scan -- some don't even care about idle at all. Remove the flag and code associated with it. The only driver that really actually needed this is wl1251 and it can just do it itself in the hw_scan callback -- implement that. Acked-by: Luciano Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 +-- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 1 - drivers/net/wireless/ti/wl1251/event.c | 6 +++++- drivers/net/wireless/ti/wl1251/main.c | 24 +++++++++++++++++++----- drivers/net/wireless/ti/wlcore/main.c | 1 - include/net/mac80211.h | 5 ----- net/mac80211/debugfs.c | 2 -- net/mac80211/iface.c | 8 ++------ net/mac80211/main.c | 3 --- 9 files changed, 27 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 75f6f6cfdd47..0fccf725a2e6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -151,8 +151,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_SCAN_WHILE_IDLE; + IEEE80211_HW_WANT_MONITOR_VIF; hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index ce6127caabdf..bbb8a5b35662 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -113,7 +113,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_QUEUE_CONTROL | IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_SCAN_WHILE_IDLE | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | diff --git a/drivers/net/wireless/ti/wl1251/event.c b/drivers/net/wireless/ti/wl1251/event.c index 5ec50a476a69..74ae8e1c2e33 100644 --- a/drivers/net/wireless/ti/wl1251/event.c +++ b/drivers/net/wireless/ti/wl1251/event.c @@ -29,6 +29,8 @@ static int wl1251_event_scan_complete(struct wl1251 *wl, struct event_mailbox *mbox) { + int ret = 0; + wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", mbox->scheduled_scan_status, mbox->scheduled_scan_channels); @@ -37,9 +39,11 @@ static int wl1251_event_scan_complete(struct wl1251 *wl, ieee80211_scan_completed(wl->hw, false); wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed"); wl->scanning = false; + if (wl->hw->conf.flags & IEEE80211_CONF_IDLE) + ret = wl1251_ps_set_mode(wl, STATION_IDLE); } - return 0; + return ret; } static void wl1251_event_mbox_dump(struct event_mailbox *mbox) diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index f47e8b0482ad..bbbf68cf50a7 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -623,7 +623,7 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) } } - if (changed & IEEE80211_CONF_CHANGE_IDLE) { + if (changed & IEEE80211_CONF_CHANGE_IDLE && !wl->scanning) { if (conf->flags & IEEE80211_CONF_IDLE) { ret = wl1251_ps_set_mode(wl, STATION_IDLE); if (ret < 0) @@ -895,11 +895,21 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (hw->conf.flags & IEEE80211_CONF_IDLE) { + ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (ret < 0) + goto out_sleep; + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); + if (ret < 0) + goto out_sleep; + } + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, req->ie_len); if (!skb) { ret = -ENOMEM; - goto out; + goto out_idle; } if (req->ie_len) memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len); @@ -908,11 +918,11 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, skb->len); dev_kfree_skb(skb); if (ret < 0) - goto out_sleep; + goto out_idle; ret = wl1251_cmd_trigger_scan_to(wl, 0); if (ret < 0) - goto out_sleep; + goto out_idle; wl->scanning = true; @@ -920,9 +930,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, req->n_channels, WL1251_SCAN_NUM_PROBES); if (ret < 0) { wl->scanning = false; - goto out_sleep; + goto out_idle; } + goto out_sleep; +out_idle: + if (hw->conf.flags & IEEE80211_CONF_IDLE) + ret = wl1251_ps_set_mode(wl, STATION_IDLE); out_sleep: wl1251_ps_elp_sleep(wl); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 28a37576d568..2c2ff3e1f849 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5636,7 +5636,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | - IEEE80211_HW_SCAN_WHILE_IDLE | IEEE80211_HW_QUEUE_CONTROL; wl->hw->wiphy->cipher_suites = cipher_suites; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8d25769b3259..86ad2c341525 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1363,10 +1363,6 @@ struct ieee80211_tx_control { * setup strictly in HW. mac80211 should not attempt to do this in * software. * - * @IEEE80211_HW_SCAN_WHILE_IDLE: The device can do hw scan while - * being idle (i.e. mac80211 doesn't have to go idle-off during the - * the scan). - * * @IEEE80211_HW_WANT_MONITOR_VIF: The driver would like to be informed of * a virtual monitor interface when monitor interfaces are the only * active interfaces. @@ -1408,7 +1404,6 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21, IEEE80211_HW_AP_LINK_PS = 1<<22, IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23, - IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24, IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, }; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 8e6040998ba6..b0e32d628114 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -151,8 +151,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); - if (local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE) - sf += snprintf(buf + sf, mxln - sf, "SCAN_WHILE_IDLE\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index deb78e864af6..3ece8eb6d2df 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -151,12 +151,8 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) } } - sdata = rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)); - if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) { - scanning = true; - sdata->vif.bss_conf.idle = false; - } + scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || + test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR || diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2220331f4b7c..38b3468bc515 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -696,9 +696,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) return -EINVAL; #endif - if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) - return -EINVAL; - if (!local->use_chanctx) { for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *comb; -- cgit v1.2.3 From fd0f979a1b67f0889aea24a7c7d2a54d6706a1cf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 00:14:51 +0100 Subject: mac80211: simplify idle handling Now that we have channel contexts, idle is (pretty much) equivalent to not having a channel context. Change the code to use this relation so that there no longer is a need for a lot of idle recalculate calls everywhere. Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 13 +++++++ net/mac80211/ibss.c | 8 ---- net/mac80211/ieee80211_i.h | 3 -- net/mac80211/iface.c | 94 ++++++---------------------------------------- net/mac80211/mlme.c | 34 ----------------- 5 files changed, 25 insertions(+), 127 deletions(-) (limited to 'net') diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index b5b50762f03e..038f249966d6 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -91,6 +91,10 @@ ieee80211_new_chanctx(struct ieee80211_local *local, list_add_rcu(&ctx->list, &local->chanctx_list); + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); + return ctx; } @@ -110,6 +114,10 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, list_del_rcu(&ctx->list); kfree_rcu(ctx, rcu_head); + + mutex_lock(&local->mtx); + ieee80211_recalc_idle(local); + mutex_unlock(&local->mtx); } static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, @@ -128,6 +136,8 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ctx->refcount++; ieee80211_recalc_txpower(sdata); + sdata->vif.bss_conf.idle = false; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); return 0; } @@ -175,6 +185,9 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ctx->refcount--; rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); + sdata->vif.bss_conf.idle = true; + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); + drv_unassign_vif_chanctx(local, sdata, ctx); if (ctx->refcount > 0) { diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 055fa9436e95..2db1f2b90bfe 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1108,10 +1108,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, mutex_unlock(&sdata->u.ibss.mtx); - mutex_lock(&sdata->local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&sdata->local->mtx); - /* * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is * reserved, but an HT STA shall protect HT transmissions as though @@ -1209,9 +1205,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) mutex_unlock(&sdata->u.ibss.mtx); - mutex_lock(&local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&local->mtx); - return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fafeb309a5fe..5635dfc7da34 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -689,9 +689,6 @@ struct ieee80211_sub_if_data { char name[IFNAMSIZ]; - /* to detect idle changes */ - bool old_idle; - /* Fragment table for host-based reassembly */ struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; unsigned int fragment_next; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 3ece8eb6d2df..40ff0307d089 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -78,8 +78,7 @@ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER); } -static u32 ieee80211_idle_off(struct ieee80211_local *local, - const char *reason) +static u32 ieee80211_idle_off(struct ieee80211_local *local) { if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) return 0; @@ -99,106 +98,45 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local) return IEEE80211_CONF_CHANGE_IDLE; } -static u32 __ieee80211_recalc_idle(struct ieee80211_local *local) +void ieee80211_recalc_idle(struct ieee80211_local *local) { - struct ieee80211_sub_if_data *sdata; - int count = 0; - bool working = false, scanning = false; + bool working = false, scanning, active; unsigned int led_trig_start = 0, led_trig_stop = 0; struct ieee80211_roc_work *roc; + u32 change; -#ifdef CONFIG_PROVE_LOCKING - WARN_ON(debug_locks && !lockdep_rtnl_is_held() && - !lockdep_is_held(&local->iflist_mtx)); -#endif lockdep_assert_held(&local->mtx); - list_for_each_entry(sdata, &local->interfaces, list) { - if (!ieee80211_sdata_running(sdata)) { - sdata->vif.bss_conf.idle = true; - continue; - } - - sdata->old_idle = sdata->vif.bss_conf.idle; - - /* do not count disabled managed interfaces */ - if (sdata->vif.type == NL80211_IFTYPE_STATION && - !sdata->u.mgd.associated && - !sdata->u.mgd.auth_data && - !sdata->u.mgd.assoc_data) { - sdata->vif.bss_conf.idle = true; - continue; - } - /* do not count unused IBSS interfaces */ - if (sdata->vif.type == NL80211_IFTYPE_ADHOC && - !sdata->u.ibss.ssid_len) { - sdata->vif.bss_conf.idle = true; - continue; - } - - if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) - continue; - - /* count everything else */ - sdata->vif.bss_conf.idle = false; - count++; - } + active = !list_empty(&local->chanctx_list); if (!local->ops->remain_on_channel) { list_for_each_entry(roc, &local->roc_list, list) { working = true; - roc->sdata->vif.bss_conf.idle = false; + break; } } scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); - list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type == NL80211_IFTYPE_MONITOR || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN || - sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) - continue; - if (sdata->old_idle == sdata->vif.bss_conf.idle) - continue; - if (!ieee80211_sdata_running(sdata)) - continue; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); - } - if (working || scanning) led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK; else led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK; - if (count) + if (active) led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; else led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED; ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop); - if (working) - return ieee80211_idle_off(local, "working"); - if (scanning) - return ieee80211_idle_off(local, "scanning"); - if (!count) - return ieee80211_idle_on(local); + if (working || scanning || active) + change = ieee80211_idle_off(local); else - return ieee80211_idle_off(local, "in use"); - - return 0; -} - -void ieee80211_recalc_idle(struct ieee80211_local *local) -{ - u32 chg; - - mutex_lock(&local->iflist_mtx); - chg = __ieee80211_recalc_idle(local); - mutex_unlock(&local->iflist_mtx); - if (chg) - ieee80211_hw_config(local, chg); + change = ieee80211_idle_on(local); + if (change) + ieee80211_hw_config(local, change); } static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) @@ -692,10 +630,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_inc(&local->iff_promiscs); - mutex_lock(&local->mtx); - hw_reconf_flags |= __ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); - if (coming_up) local->open_count++; @@ -888,10 +822,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, sdata->bss = NULL; - mutex_lock(&local->mtx); - hw_reconf_flags |= __ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); - ieee80211_recalc_ps(local, -1); if (local->open_count == 0) { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1e7662c5788e..3bcf39365be8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1792,7 +1792,6 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get); static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; mutex_lock(&ifmgd->mtx); @@ -1812,10 +1811,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) * but that's not a problem. */ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); - - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); } static void ieee80211_beacon_connection_loss_work(struct work_struct *work) @@ -2048,10 +2043,6 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - mutex_lock(&sdata->local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&sdata->local->mtx); - return RX_MGMT_CFG80211_DEAUTH; } @@ -2079,10 +2070,6 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - mutex_lock(&sdata->local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&sdata->local->mtx); - return RX_MGMT_CFG80211_DISASSOC; } @@ -2839,7 +2826,6 @@ static void ieee80211_sta_timer(unsigned long data) static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 reason, bool tx) { - struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -2853,10 +2839,6 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, */ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); - mutex_lock(&ifmgd->mtx); } @@ -3127,10 +3109,6 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) } mutex_unlock(&ifmgd->mtx); - - mutex_lock(&local->mtx); - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); } static void ieee80211_sta_bcn_mon_timer(unsigned long data) @@ -3644,10 +3622,6 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, return -ENOMEM; } - mutex_lock(&local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&local->mtx); - if (new_sta) { u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit; @@ -4138,10 +4112,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); out: - mutex_lock(&sdata->local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&sdata->local->mtx); - if (sent_frame) __cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); @@ -4182,10 +4152,6 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, __cfg80211_send_disassoc(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); - mutex_lock(&sdata->local->mtx); - ieee80211_recalc_idle(sdata->local); - mutex_unlock(&sdata->local->mtx); - return 0; } -- cgit v1.2.3 From 3e4d40fa118e58ce51e85015587a944ec5c69400 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 17:19:08 +0100 Subject: mac80211: remove unused code to mark AP station authenticated When we get to association, the AP station already exists and is marked authenticated, so moving it into IEEE80211_STA_AUTH again is a NOP, remove it. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3bcf39365be8..76cacdb062c8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2212,9 +2212,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (elems.wmm_param) set_sta_flag(sta, WLAN_STA_WME); - err = sta_info_move_state(sta, IEEE80211_STA_AUTH); - if (!err) - err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); + err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); if (err) { -- cgit v1.2.3 From dd40851521361e07c1f61e79cca941a63ad311bc Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sun, 10 Feb 2013 02:33:35 +0000 Subject: ipv6: don't let node/interface scoped multicast traffic escape on the wire Reported-by: Erik Hugne Cc: Erik Hugne Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Acked-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c953825f9098..155eccfa7760 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -120,6 +120,13 @@ static int ip6_finish_output2(struct sk_buff *skb) IP6_UPD_PO_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTMCAST, skb->len); + + if (IPV6_ADDR_MC_SCOPE(&ipv6_hdr(skb)->daddr) <= + IPV6_ADDR_SCOPE_NODELOCAL && + !(dev->flags & IFF_LOOPBACK)) { + kfree_skb(skb); + return 0; + } } rcu_read_lock_bh(); -- cgit v1.2.3 From 20314092c1b41894d8c181bf9aa6f022be2416aa Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sun, 10 Feb 2013 05:35:22 +0000 Subject: ipv6: don't accept multicast traffic with scope 0 v2: a) moved before multicast source address check b) changed comment to netdev style Cc: Erik Hugne Cc: YOSHIFUJI Hideaki Acked-by: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Acked-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- net/ipv6/ip6_input.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net') diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 4ac5bf30e16a..5b10414e619e 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -118,6 +118,15 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt ipv6_addr_loopback(&hdr->daddr)) goto err; + /* RFC4291 2.7 + * Nodes must not originate a packet to a multicast address whose scope + * field contains the reserved value 0; if such a packet is received, it + * must be silently dropped. + */ + if (ipv6_addr_is_multicast(&hdr->daddr) && + IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) + goto err; + /* * RFC4291 2.7 * Multicast addresses must not be used as source addresses in IPv6 -- cgit v1.2.3 From 2c5e89338493882719f8d138f8f2717ee9a04153 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Sun, 10 Feb 2013 03:50:18 +0000 Subject: ipv6: by default join ff01::1 and in case of forwarding ff01::2 and ff05:2 Cc: Erik Hugne Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/linux/in6.h | 9 +++++++++ net/ipv6/addrconf.c | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/linux/in6.h b/include/linux/in6.h index a16e19349ec0..34edf1f6c9a3 100644 --- a/include/linux/in6.h +++ b/include/linux/in6.h @@ -36,4 +36,13 @@ extern const struct in6_addr in6addr_linklocal_allnodes; extern const struct in6_addr in6addr_linklocal_allrouters; #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } } +extern const struct in6_addr in6addr_interfacelocal_allnodes; +#define IN6ADDR_INTERFACELOCAL_ALLNODES_INIT \ + { { { 0xff,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +extern const struct in6_addr in6addr_interfacelocal_allrouters; +#define IN6ADDR_INTERFACELOCAL_ALLROUTERS_INIT \ + { { { 0xff,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } } +extern const struct in6_addr in6addr_sitelocal_allrouters; +#define IN6ADDR_SITELOCAL_ALLROUTERS_INIT \ + { { { 0xff,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } } #endif diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index bd9f9360f769..86c235d05aba 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -244,6 +244,9 @@ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; +const struct in6_addr in6addr_interfacelocal_allnodes = IN6ADDR_INTERFACELOCAL_ALLNODES_INIT; +const struct in6_addr in6addr_interfacelocal_allrouters = IN6ADDR_INTERFACELOCAL_ALLROUTERS_INIT; +const struct in6_addr in6addr_sitelocal_allrouters = IN6ADDR_SITELOCAL_ALLROUTERS_INIT; /* Check if a valid qdisc is available */ static inline bool addrconf_qdisc_ok(const struct net_device *dev) @@ -428,6 +431,9 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) /* protected by rtnl_lock */ rcu_assign_pointer(dev->ip6_ptr, ndev); + /* Join interface-local all-node multicast group */ + ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes); + /* Join all-node multicast group */ ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); @@ -611,10 +617,15 @@ static void dev_forward_change(struct inet6_dev *idev) if (idev->cnf.forwarding) dev_disable_lro(dev); if (dev->flags & IFF_MULTICAST) { - if (idev->cnf.forwarding) + if (idev->cnf.forwarding) { ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); - else + ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters); + ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters); + } else { ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters); + ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters); + ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters); + } } list_for_each_entry(ifa, &idev->addr_list, if_list) { -- cgit v1.2.3 From b2748267d6487857f1400dc42748c5409e0ee130 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sun, 10 Feb 2013 11:41:01 +0000 Subject: bridge: use dev->addr_assign_type to see if user change mac And remove no longer used br->flags. Signed-off-by: Jiri Pirko Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bridge/br_device.c | 1 - net/bridge/br_private.h | 3 --- net/bridge/br_stp_if.c | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'net') diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index e1bc090bc00a..ba6fb2d60940 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -176,7 +176,6 @@ static int br_set_mac_address(struct net_device *dev, void *p) br_fdb_change_mac_address(br, addr->sa_data); br_stp_change_bridge_id(br, addr->sa_data); } - br->flags |= BR_SET_MAC_ADDR; spin_unlock_bh(&br->lock); return 0; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 711094aed41a..06e85d9c05aa 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -197,9 +197,6 @@ struct net_bridge bool nf_call_ip6tables; bool nf_call_arptables; #endif - unsigned long flags; -#define BR_SET_MAC_ADDR 0x00000001 - u16 group_fwd_mask; /* STP */ diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 7b5197c7de13..0bdb4ebd362b 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -216,7 +216,7 @@ bool br_stp_recalculate_bridge_id(struct net_bridge *br) struct net_bridge_port *p; /* user has chosen a value so keep it */ - if (br->flags & BR_SET_MAC_ADDR) + if (br->dev->addr_assign_type == NET_ADDR_SET) return false; list_for_each_entry(p, &br->port_list, list) { -- cgit v1.2.3 From 954b1244530064d48dde133f906026e56ba0ae56 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 11 Feb 2013 06:22:28 +0000 Subject: ethtool: fix sparse warning Fixes sparse complaints about dropping __user in casts. warning: cast removes address space of expression Signed-off-by: Stephen Hemminger Acked-by: Ben Hutchings Signed-off-by: David S. Miller --- net/socket.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/socket.c b/net/socket.c index 5c4d82c05293..ee0d029e5130 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2837,7 +2837,7 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) } ifr = compat_alloc_user_space(buf_size); - rxnfc = (void *)ifr + ALIGN(sizeof(struct ifreq), 8); + rxnfc = (void __user *)ifr + ALIGN(sizeof(struct ifreq), 8); if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) return -EFAULT; @@ -2861,12 +2861,12 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) offsetof(struct ethtool_rxnfc, fs.ring_cookie)); if (copy_in_user(rxnfc, compat_rxnfc, - (void *)(&rxnfc->fs.m_ext + 1) - - (void *)rxnfc) || + (void __user *)(&rxnfc->fs.m_ext + 1) - + (void __user *)rxnfc) || copy_in_user(&rxnfc->fs.ring_cookie, &compat_rxnfc->fs.ring_cookie, - (void *)(&rxnfc->fs.location + 1) - - (void *)&rxnfc->fs.ring_cookie) || + (void __user *)(&rxnfc->fs.location + 1) - + (void __user *)&rxnfc->fs.ring_cookie) || copy_in_user(&rxnfc->rule_cnt, &compat_rxnfc->rule_cnt, sizeof(rxnfc->rule_cnt))) return -EFAULT; @@ -2878,12 +2878,12 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) if (convert_out) { if (copy_in_user(compat_rxnfc, rxnfc, - (const void *)(&rxnfc->fs.m_ext + 1) - - (const void *)rxnfc) || + (const void __user *)(&rxnfc->fs.m_ext + 1) - + (const void __user *)rxnfc) || copy_in_user(&compat_rxnfc->fs.ring_cookie, &rxnfc->fs.ring_cookie, - (const void *)(&rxnfc->fs.location + 1) - - (const void *)&rxnfc->fs.ring_cookie) || + (const void __user *)(&rxnfc->fs.location + 1) - + (const void __user *)&rxnfc->fs.ring_cookie) || copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt, sizeof(rxnfc->rule_cnt))) return -EFAULT; -- cgit v1.2.3 From 7e307c67c94619fa53b22735673fbf99b427e760 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 11 Feb 2013 06:25:24 +0000 Subject: mrp: make mrp_rcv static Sparse spotted local function that could be static. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/802/mrp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/802/mrp.c b/net/802/mrp.c index 47a9e14c8ba7..a4cc3229952a 100644 --- a/net/802/mrp.c +++ b/net/802/mrp.c @@ -744,8 +744,8 @@ static int mrp_pdu_parse_msg(struct mrp_applicant *app, struct sk_buff *skb, return 0; } -int mrp_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static int mrp_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { struct mrp_application *appl = container_of(pt, struct mrp_application, pkttype); -- cgit v1.2.3 From 7f4fe17bc2d4fd46598b03bc51281cfa7ef6abf5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 11 Feb 2013 14:36:03 +0100 Subject: mac80211/minstrel_ht: show the number of retries for each rate in debugfs Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht_debugfs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index e788f76a1dfe..f2b7d26370f0 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -38,8 +38,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; - p += sprintf(p, "type rate throughput ewma prob this prob " - "this succ/attempt success attempts\n"); + p += sprintf(p, "type rate throughput ewma prob this prob " + "retry this succ/attempt success attempts\n"); for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) { char htmode = '2'; char gimode = 'L'; @@ -64,18 +64,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; - p += sprintf(p, "MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * + p += sprintf(p, " MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * MCS_GROUP_RATES + j); tp = mr->cur_tp / 10; prob = MINSTREL_TRUNC(mr->cur_prob * 1000); eprob = MINSTREL_TRUNC(mr->probability * 1000); - p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " - "%3u(%3u) %8llu %8llu\n", + p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " + "%3u %3u(%3u) %8llu %8llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, + mr->retry_count, mr->last_success, mr->last_attempts, (unsigned long long)mr->succ_hist, -- cgit v1.2.3 From df15a6c4fa7f77511663d7b5f9134f37ad2e8c92 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 11 Feb 2013 14:36:04 +0100 Subject: mac80211/minstrel_ht: remove the sampling bypass check for the lowest rate It's more of an unnecessary micro-optimization and it prevents switching from long-GI to short-GI in HT20/single-stream for the lowest rate Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 9f9c453bc45d..5bb316aff21a 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -231,10 +231,6 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) if (!mr->cur_tp) continue; - /* ignore the lowest rate of each single-stream group */ - if (!i && minstrel_mcs_groups[group].streams == 1) - continue; - if ((mr->cur_tp > cur_prob_tp && mr->probability > MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) { mg->max_prob_rate = index; -- cgit v1.2.3 From 6c17b77b67587b9f9e3070fb89fe98cef3187131 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 11 Feb 2013 11:21:07 -0600 Subject: mac80211: Fix tx queue handling during scans Scans currently work by stopping the netdev tx queues but leaving the mac80211 queues active. This stops the flow of incoming packets while still allowing mac80211 to transmit nullfunc and probe request frames to facilitate scanning. However, the driver may try to wake the mac80211 queues while in this state, which will also wake the netdev queues. To prevent this, add a new queue stop reason, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, to be used when stopping the tx queues for off-channel operation. This prevents the netdev queues from waking when a driver wakes the mac80211 queues. This also stops all frames from being transmitted, even those meant to be sent off-channel. Add a new tx control flag, IEEE80211_TX_CTL_OFFCHAN_TX_OK, which allows frames to be transmitted when the queues are stopped only for the off-channel stop reason. Update all locations transmitting off-channel frames to use this flag. Signed-off-by: Seth Forshee Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/cfg.c | 3 ++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 3 ++- net/mac80211/offchannel.c | 30 ++++++++++-------------------- net/mac80211/scan.c | 9 ++++++--- net/mac80211/tx.c | 15 +++++++++++++++ 7 files changed, 40 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 86ad2c341525..0eaa9092364b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -399,6 +399,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be * set by rate control algorithms to indicate probe rate, will * be cleared for fragmented frames (except on the last fragment) + * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate + * that a frame can be transmitted while the queues are stopped for + * off-channel operation. * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, * used to indicate that a pending frame requires TX processing before * it can be sent out. @@ -464,6 +467,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU = BIT(10), IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), + IEEE80211_TX_INTFL_OFFCHAN_TX_OK = BIT(13), IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8f6b593a921f..e3dec80cf617 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2749,7 +2749,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, goto out_unlock; } - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN | + IEEE80211_TX_INTFL_OFFCHAN_TX_OK; if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) IEEE80211_SKB_CB(skb)->hw_queue = local->hw.offchannel_tx_hw_queue; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5635dfc7da34..76cdcfcd614c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -809,6 +809,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_AGGREGATION, IEEE80211_QUEUE_STOP_REASON_SUSPEND, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, + IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, }; #ifdef CONFIG_MAC80211_LEDS diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 76cacdb062c8..efb22763d56d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -685,7 +685,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, if (powersave) nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | + IEEE80211_TX_INTFL_OFFCHAN_TX_OK; if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 82baf5b6ecf4..4c3ee3e8285c 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -113,6 +113,10 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) * notify the AP about us leaving the channel and stop all * STA interfaces. */ + + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); + mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) @@ -133,12 +137,9 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) sdata, BSS_CHANGED_BEACON_ENABLED); } - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { - netif_tx_stop_all_queues(sdata->dev); - if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.associated) - ieee80211_offchannel_ps_enable(sdata); - } + if (sdata->vif.type == NL80211_IFTYPE_STATION && + sdata->u.mgd.associated) + ieee80211_offchannel_ps_enable(sdata); } mutex_unlock(&local->iflist_mtx); } @@ -166,20 +167,6 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) sdata->u.mgd.associated) ieee80211_offchannel_ps_disable(sdata); - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) { - /* - * This may wake up queues even though the driver - * currently has them stopped. This is not very - * likely, since the driver won't have gotten any - * (or hardly any) new packets while we weren't - * on the right channel, and even if it happens - * it will at most lead to queueing up one more - * packet per queue in mac80211 rather than on - * the interface qdisc. - */ - netif_tx_wake_all_queues(sdata->dev); - } - if (test_and_clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state)) { sdata->vif.bss_conf.enable_beacon = true; @@ -188,6 +175,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) } } mutex_unlock(&local->iflist_mtx); + + ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); } void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index edd47d9acb99..d9e2df96f676 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -382,6 +382,11 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, int i; struct ieee80211_sub_if_data *sdata; enum ieee80211_band band = local->hw.conf.channel->band; + u32 tx_flags; + + tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; + if (local->scan_req->no_cck) + tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE; sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); @@ -393,9 +398,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, local->scan_req->rates[band], false, - local->scan_req->no_cck ? - IEEE80211_TX_CTL_NO_CCK_RATE : 0, - local->hw.conf.channel, true); + tx_flags, local->hw.conf.channel, true); /* * After sending probe requests, wait for probe responses diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2ef0e19b06bb..f476aa6a771d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1230,6 +1230,21 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || (!txpending && !skb_queue_empty(&local->pending[q]))) { + if (unlikely(info->flags & + IEEE80211_TX_INTFL_OFFCHAN_TX_OK && + local->queue_stop_reasons[q] & + ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) { + /* + * Drop off-channel frames if queues are stopped + * for any reason other than off-channel + * operation. Never queue them. + */ + spin_unlock_irqrestore( + &local->queue_stop_reason_lock, flags); + ieee80211_purge_tx_queue(&local->hw, skbs); + return true; + } + /* * Since queue is stopped, queue up frames for later * transmission from the tx-pending tasklet when the -- cgit v1.2.3 From 9c35d7d2368f54313b988a01c408e5cf863ffd9e Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Mon, 11 Feb 2013 11:21:08 -0600 Subject: mac80211: Add flushes before going off-channel We've got a couple of races when enabling powersave with an AP for off-channel operation. The first is fairly simple. If we go off-channel before the nullfunc frame to enable PS is transmitted then it may not be received by the AP. Add a flush after enabling off-channel PS to prevent this from happening. The second race is a bit more subtle. If the driver supports QoS and has frames queued when the nullfunc frame is queued, those frames may get transmitted after the nullfunc frame. If PM is not set then the AP is being told that we've exited PS before we go off-channel and may try to deliver frames. To prevent this, add a flush after stopping the queues but before passing the nullfunc frame to the driver. Signed-off-by: Seth Forshee Signed-off-by: Johannes Berg --- net/mac80211/offchannel.c | 5 +++++ net/mac80211/scan.c | 3 +++ 2 files changed, 8 insertions(+) (limited to 'net') diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 4c3ee3e8285c..cc79b4a2e821 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -114,8 +114,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) * STA interfaces. */ + /* + * Stop queues and transmit all frames queued by the driver + * before sending nullfunc to enable powersave at the AP. + */ ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); + drv_flush(local, false); mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d9e2df96f676..6d0b89e4aa31 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -334,6 +334,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) ieee80211_offchannel_stop_vifs(local); + /* ensure nullfunc is transmitted before leaving operating channel */ + drv_flush(local, false); + ieee80211_configure_filter(local); /* We need to set power level at maximum rate for scanning. */ -- cgit v1.2.3 From f05de73bf82fbbc00265c06d12efb7273f7dc54a Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Mon, 11 Feb 2013 13:30:38 +0000 Subject: skbuff: create skb_panic() function and its wrappers Create skb_panic() function in lieu of both skb_over_panic() and skb_under_panic() so that code duplication would be avoided. Update type and variable name where necessary. Jiri Pirko suggested using wrappers so that we would be able to keep the fruits of the original code. Signed-off-by: Jean Sacren Cc: Jiri Pirko Signed-off-by: David S. Miller --- net/core/skbuff.c | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6114c1143564..8731c39b7a54 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -104,47 +104,37 @@ static const struct pipe_buf_operations sock_pipe_buf_ops = { .get = sock_pipe_buf_get, }; -/* - * Keep out-of-line to prevent kernel bloat. - * __builtin_return_address is not used because it is not always - * reliable. - */ - /** - * skb_over_panic - private function - * @skb: buffer - * @sz: size - * @here: address - * - * Out of line support code for skb_put(). Not user callable. + * skb_panic - private function for out-of-line support + * @skb: buffer + * @sz: size + * @addr: address + * @panic: skb_over_panic or skb_under_panic + * + * Out-of-line support for skb_put() and skb_push(). + * Called via the wrapper skb_over_panic() or skb_under_panic(). + * Keep out of line to prevent kernel bloat. + * __builtin_return_address is not used because it is not always reliable. */ -static void skb_over_panic(struct sk_buff *skb, int sz, void *here) +static void skb_panic(struct sk_buff *skb, unsigned int sz, void *addr, + const char panic[]) { pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n", - __func__, here, skb->len, sz, skb->head, skb->data, + panic, addr, skb->len, sz, skb->head, skb->data, (unsigned long)skb->tail, (unsigned long)skb->end, skb->dev ? skb->dev->name : ""); BUG(); } -/** - * skb_under_panic - private function - * @skb: buffer - * @sz: size - * @here: address - * - * Out of line support code for skb_push(). Not user callable. - */ - -static void skb_under_panic(struct sk_buff *skb, int sz, void *here) +static void skb_over_panic(struct sk_buff *skb, unsigned int sz, void *addr) { - pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n", - __func__, here, skb->len, sz, skb->head, skb->data, - (unsigned long)skb->tail, (unsigned long)skb->end, - skb->dev ? skb->dev->name : ""); - BUG(); + skb_panic(skb, sz, addr, __func__); } +static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr) +{ + skb_panic(skb, sz, addr, __func__); +} /* * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells -- cgit v1.2.3 From 2cde6acd49daca58b96f1fbc697492825511ad31 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 11 Feb 2013 10:25:30 +0000 Subject: netpoll: Fix __netpoll_rcu_free so that it can hold the rtnl lock __netpoll_rcu_free is used to free netpoll structures when the rtnl_lock is already held. The mechanism is used to asynchronously call __netpoll_cleanup outside of the holding of the rtnl_lock, so as to avoid deadlock. Unfortunately, __netpoll_cleanup modifies pointers (dev->np), which means the rtnl_lock must be held while calling it. Further, it cannot be held, because rcu callbacks may be issued in softirq contexts, which cannot sleep. Fix this by converting the rcu callback to a work queue that is guaranteed to get scheduled in process context, so that we can hold the rtnl properly while calling __netpoll_cleanup Tested successfully by myself. Signed-off-by: Neil Horman CC: "David S. Miller" CC: Cong Wang CC: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- include/linux/netpoll.h | 4 ++-- net/8021q/vlan_dev.c | 2 +- net/bridge/br_device.c | 2 +- net/core/netpoll.c | 16 ++++++++++------ 5 files changed, 15 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 22399374b1e1..94c1534dd578 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1249,7 +1249,7 @@ static inline void slave_disable_netpoll(struct slave *slave) return; slave->np = NULL; - __netpoll_free_rcu(np); + __netpoll_free_async(np); } static inline bool slave_dev_support_netpoll(struct net_device *slave_dev) { diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index ab856d507b7e..9d7d8c64f7c8 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -32,7 +32,7 @@ struct netpoll { u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ - struct rcu_head rcu; + struct work_struct cleanup_work; }; struct netpoll_info { @@ -68,7 +68,7 @@ int netpoll_setup(struct netpoll *np); int netpoll_trap(void); void netpoll_set_trap(int trap); void __netpoll_cleanup(struct netpoll *np); -void __netpoll_free_rcu(struct netpoll *np); +void __netpoll_free_async(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo); void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 34df5b3c9b75..19cf81bf9f69 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -733,7 +733,7 @@ static void vlan_dev_netpoll_cleanup(struct net_device *dev) vlan->netpoll = NULL; - __netpoll_free_rcu(netpoll); + __netpoll_free_async(netpoll); } #endif /* CONFIG_NET_POLL_CONTROLLER */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ba6fb2d60940..ca98fa5b2c78 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -265,7 +265,7 @@ void br_netpoll_disable(struct net_bridge_port *p) p->np = NULL; - __netpoll_free_rcu(np); + __netpoll_free_async(np); } #endif diff --git a/net/core/netpoll.c b/net/core/netpoll.c index edcd9ad95304..c536474e2260 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -61,6 +61,7 @@ static struct srcu_struct netpoll_srcu; static void zap_completion_queue(void); static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo); +static void netpoll_async_cleanup(struct work_struct *work); static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); @@ -1020,6 +1021,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) np->dev = ndev; strlcpy(np->dev_name, ndev->name, IFNAMSIZ); + INIT_WORK(&np->cleanup_work, netpoll_async_cleanup); if ((ndev->priv_flags & IFF_DISABLE_NETPOLL) || !ndev->netdev_ops->ndo_poll_controller) { @@ -1255,25 +1257,27 @@ void __netpoll_cleanup(struct netpoll *np) if (ops->ndo_netpoll_cleanup) ops->ndo_netpoll_cleanup(np->dev); - RCU_INIT_POINTER(np->dev->npinfo, NULL); + rcu_assign_pointer(np->dev->npinfo, NULL); call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info); } } EXPORT_SYMBOL_GPL(__netpoll_cleanup); -static void rcu_cleanup_netpoll(struct rcu_head *rcu_head) +static void netpoll_async_cleanup(struct work_struct *work) { - struct netpoll *np = container_of(rcu_head, struct netpoll, rcu); + struct netpoll *np = container_of(work, struct netpoll, cleanup_work); + rtnl_lock(); __netpoll_cleanup(np); + rtnl_unlock(); kfree(np); } -void __netpoll_free_rcu(struct netpoll *np) +void __netpoll_free_async(struct netpoll *np) { - call_rcu_bh(&np->rcu, rcu_cleanup_netpoll); + schedule_work(&np->cleanup_work); } -EXPORT_SYMBOL_GPL(__netpoll_free_rcu); +EXPORT_SYMBOL_GPL(__netpoll_free_async); void netpoll_cleanup(struct netpoll *np) { -- cgit v1.2.3 From 0790bbb68f9d483348c1d65381f3dd92602bfd05 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 11 Feb 2013 10:25:31 +0000 Subject: netpoll: cleanup sparse warnings With my recent commit I introduced two sparse warnings. Looking closer there were a few more in the same file, so I fixed them all up. Basic rcu pointer dereferencing suff. I've validated these changes using CONFIG_PROVE_RCU while starting and stopping netconsole repeatedly in bonded and non-bonded configurations Signed-off-by: Neil Horman CC: fengguang.wu@intel.com CC: David Miller CC: eric.dumazet@gmail.com Signed-off-by: David S. Miller --- net/core/netpoll.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index c536474e2260..bcfd4f4599a5 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -206,7 +206,7 @@ static void netpoll_poll_dev(struct net_device *dev) * the dev_open/close paths use this to block netpoll activity * while changing device state */ - if (!mutex_trylock(&dev->npinfo->dev_lock)) + if (!mutex_trylock(&ni->dev_lock)) return; if (!dev || !netif_running(dev)) @@ -221,7 +221,7 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); - mutex_unlock(&dev->npinfo->dev_lock); + mutex_unlock(&ni->dev_lock); if (dev->flags & IFF_SLAVE) { if (ni) { @@ -1056,7 +1056,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) goto free_npinfo; } } else { - npinfo = ndev->npinfo; + npinfo = rtnl_dereference(ndev->npinfo); atomic_inc(&npinfo->refcnt); } @@ -1236,7 +1236,11 @@ void __netpoll_cleanup(struct netpoll *np) struct netpoll_info *npinfo; unsigned long flags; - npinfo = np->dev->npinfo; + /* rtnl_dereference would be preferable here but + * rcu_cleanup_netpoll path can put us in here safely without + * holding the rtnl, so plain rcu_dereference it is + */ + npinfo = rtnl_dereference(np->dev->npinfo); if (!npinfo) return; -- cgit v1.2.3 From 66d575705154525c33ab99b5f9ac2d246c348db6 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 10 Feb 2013 15:55:20 +0200 Subject: mac80211: allow driver to be stateless wrt. SMSP requests Bail out if no update is made to the SMPS state. This allows the driver to avoid duplicating the state. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 61ac7c48ac0c..9e7560becbbb 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -406,6 +406,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) smps_mode = IEEE80211_SMPS_AUTOMATIC; + if (sdata->u.mgd.driver_smps_mode == smps_mode) + return; + sdata->u.mgd.driver_smps_mode = smps_mode; ieee80211_queue_work(&sdata->local->hw, -- cgit v1.2.3 From 570617e79c3ab31ce426efe9024af84efca862eb Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 12 Feb 2013 05:15:33 +0000 Subject: net: sctp: remove unused multiple cookie keys Vlad says: The whole multiple cookie keys code is completely unused and has been all this time. Noone uses anything other then the secret_key[0] since there is no changeover support anywhere. Thus, for now clean up its left-over fragments. Cc: Neil Horman Cc: Vlad Yasevich Signed-off-by: Daniel Borkmann Acked-by: Neil Horman Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 2 +- include/net/sctp/structs.h | 5 +---- net/sctp/endpointola.c | 9 ++------- net/sctp/sm_make_chunk.c | 31 +++++++------------------------ 4 files changed, 11 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index c29707d654c0..a7dd5c50df79 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -303,7 +303,7 @@ enum { SCTP_MAX_GABS = 16 }; * to which we will raise the P-MTU. */ #define SCTP_DEFAULT_MINSEGMENT 512 /* MTU size ... if no mtu disc */ -#define SCTP_HOW_MANY_SECRETS 2 /* How many secrets I keep */ + #define SCTP_SECRET_SIZE 32 /* Number of octets in a 256 bits. */ #define SCTP_SIGNATURE_SIZE 20 /* size of a SLA-1 signature */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index fdeb85a970fc..0e0f9d2322e3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1236,10 +1236,7 @@ struct sctp_endpoint { * Discussion in [RFC1750] can be helpful in * selection of the key. */ - __u8 secret_key[SCTP_HOW_MANY_SECRETS][SCTP_SECRET_SIZE]; - int current_key; - int last_key; - int key_changed_at; + __u8 secret_key[SCTP_SECRET_SIZE]; /* digest: This is a digest of the sctp cookie. This field is * only used on the receive path when we try to validate diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 1a9c5fb77310..73aad3d16a45 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -151,9 +151,7 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, ep->rcvbuf_policy = net->sctp.rcvbuf_policy; /* Initialize the secret key used with cookie. */ - get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE); - ep->last_key = ep->current_key = 0; - ep->key_changed_at = jiffies; + get_random_bytes(ep->secret_key, sizeof(ep->secret_key)); /* SCTP-AUTH extensions*/ INIT_LIST_HEAD(&ep->endpoint_shared_keys); @@ -249,8 +247,6 @@ void sctp_endpoint_free(struct sctp_endpoint *ep) /* Final destructor for endpoint. */ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) { - int i; - SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); /* Free up the HMAC transform. */ @@ -273,8 +269,7 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) sctp_inq_free(&ep->base.inqueue); sctp_bind_addr_free(&ep->base.bind_addr); - for (i = 0; i < SCTP_HOW_MANY_SECRETS; ++i) - memset(&ep->secret_key[i], 0, SCTP_SECRET_SIZE); + memset(ep->secret_key, 0, sizeof(ep->secret_key)); /* Remove and free the port */ if (sctp_sk(ep->base.sk)->bind_hash) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e1c5fc2be6b8..a193f3bc8144 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1589,8 +1589,6 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, struct sctp_signed_cookie *cookie; struct scatterlist sg; int headersize, bodysize; - unsigned int keylen; - char *key; /* Header size is static data prior to the actual cookie, including * any padding. @@ -1650,12 +1648,11 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, /* Sign the message. */ sg_init_one(&sg, &cookie->c, bodysize); - keylen = SCTP_SECRET_SIZE; - key = (char *)ep->secret_key[ep->current_key]; desc.tfm = sctp_sk(ep->base.sk)->hmac; desc.flags = 0; - if (crypto_hash_setkey(desc.tfm, key, keylen) || + if (crypto_hash_setkey(desc.tfm, ep->secret_key, + sizeof(ep->secret_key)) || crypto_hash_digest(&desc, &sg, bodysize, cookie->signature)) goto free_cookie; } @@ -1682,8 +1679,7 @@ struct sctp_association *sctp_unpack_cookie( int headersize, bodysize, fixed_size; __u8 *digest = ep->digest; struct scatterlist sg; - unsigned int keylen, len; - char *key; + unsigned int len; sctp_scope_t scope; struct sk_buff *skb = chunk->skb; struct timeval tv; @@ -1718,34 +1714,21 @@ struct sctp_association *sctp_unpack_cookie( goto no_hmac; /* Check the signature. */ - keylen = SCTP_SECRET_SIZE; sg_init_one(&sg, bear_cookie, bodysize); - key = (char *)ep->secret_key[ep->current_key]; desc.tfm = sctp_sk(ep->base.sk)->hmac; desc.flags = 0; memset(digest, 0x00, SCTP_SIGNATURE_SIZE); - if (crypto_hash_setkey(desc.tfm, key, keylen) || + if (crypto_hash_setkey(desc.tfm, ep->secret_key, + sizeof(ep->secret_key)) || crypto_hash_digest(&desc, &sg, bodysize, digest)) { *error = -SCTP_IERROR_NOMEM; goto fail; } if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { - /* Try the previous key. */ - key = (char *)ep->secret_key[ep->last_key]; - memset(digest, 0x00, SCTP_SIGNATURE_SIZE); - if (crypto_hash_setkey(desc.tfm, key, keylen) || - crypto_hash_digest(&desc, &sg, bodysize, digest)) { - *error = -SCTP_IERROR_NOMEM; - goto fail; - } - - if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { - /* Yikes! Still bad signature! */ - *error = -SCTP_IERROR_BAD_SIG; - goto fail; - } + *error = -SCTP_IERROR_BAD_SIG; + goto fail; } no_hmac: -- cgit v1.2.3 From 442fad9423b78319e0019a7f5047eddf3317afbc Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 11 Feb 2013 23:22:38 +0000 Subject: netfilter: ctnetlink: don't permit ct creation with random tuple Userspace can cause kernel panic by not specifying orig/reply tuple: kernel will create a tuple with random stack values. Problem is that tuple.dst.dir will be random, too, which causes nf_ct_tuplehash_to_ctrack() to return garbage. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 627b0e50b238..a081915e0531 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1705,6 +1705,9 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, if (nlh->nlmsg_flags & NLM_F_CREATE) { enum ip_conntrack_events events; + if (!cda[CTA_TUPLE_ORIG] || !cda[CTA_TUPLE_REPLY]) + return -EINVAL; + ct = ctnetlink_create_conntrack(net, zone, cda, &otuple, &rtuple, u3); if (IS_ERR(ct)) -- cgit v1.2.3 From 6e2f0aa8cf8892868bf2c19349cb5d7c407f690d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 12 Feb 2013 05:59:53 +0000 Subject: netfilter: nf_ct_helper: don't discard helper if it is actually the same commit (32f5376 netfilter: nf_ct_helper: disable automatic helper re-assignment of different type) broke transparent proxy scenarios. For example, initial helper lookup might yield "ftp" (dport 21), while re-lookup after REDIRECT yields "ftp-2121". This causes the autoassign code to toss the ftp helper, even though these are just different instances of the same helper. Change the test to check for the helper function address instead of the helper address, as suggested by Pablo. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 884f2b39319a..91527d5ba018 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -236,7 +236,9 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, /* We only allow helper re-assignment of the same sort since * we cannot reallocate the helper extension area. */ - if (help->helper != helper) { + struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); + + if (tmp && tmp->help != helper->help) { RCU_INIT_POINTER(help->helper, NULL); goto out; } -- cgit v1.2.3 From 324f5aa52814b3cc0b24eec071be2946dbca0e3a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:11:59 +0000 Subject: htb: use PSCHED_TICKS2NS() Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_htb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 79e8ed4ac7ce..14a83dc0bfff 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1512,8 +1512,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, htb_precompute_ratedata(&cl->rate); htb_precompute_ratedata(&cl->ceil); - cl->buffer = hopt->buffer << PSCHED_SHIFT; - cl->cbuffer = hopt->buffer << PSCHED_SHIFT; + cl->buffer = PSCHED_TICKS2NS(hopt->buffer); + cl->cbuffer = PSCHED_TICKS2NS(hopt->buffer); sch_tree_unlock(sch); -- cgit v1.2.3 From bdd6998b1e050f5589562e40aa852b626ee6fdea Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:01 +0000 Subject: htb: remove pointless first initialization of buffer and cbuffer These are initialized correctly a couple of lines later in the function. Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_htb.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 14a83dc0bfff..547912e94038 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1503,9 +1503,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, cl->prio = TC_HTB_NUMPRIO - 1; } - cl->buffer = hopt->buffer; - cl->cbuffer = hopt->cbuffer; - cl->rate.rate_bps = (u64)hopt->rate.rate << 3; cl->ceil.rate_bps = (u64)hopt->ceil.rate << 3; -- cgit v1.2.3 From b9a7afdefdf90dc9e64902b2565170b8b017aa75 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:02 +0000 Subject: htb: initialize cl->tokens and cl->ctokens correctly These are in ns so convert from ticks to ns. Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_htb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 547912e94038..2b225446b3de 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1459,8 +1459,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, cl->parent = parent; /* set class to be in HTB_CAN_SEND state */ - cl->tokens = hopt->buffer; - cl->ctokens = hopt->cbuffer; + cl->tokens = PSCHED_TICKS2NS(hopt->buffer); + cl->ctokens = PSCHED_TICKS2NS(hopt->cbuffer); cl->mbuffer = 60 * PSCHED_TICKS_PER_SEC; /* 1min */ cl->t_c = psched_get_time(); cl->cmode = HTB_CAN_SEND; -- cgit v1.2.3 From 292f1c7ff6cc10516076ceeea45ed11833bb71c7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:03 +0000 Subject: sch: make htb_rate_cfg and functions around that generic As it is going to be used in tbf as well, push these to generic code. Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sch_generic.h | 19 ++++++++++++++ net/sched/sch_generic.c | 37 +++++++++++++++++++++++++++ net/sched/sch_htb.c | 65 +++++++---------------------------------------- 3 files changed, 65 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 2d06c2a53de1..2761c905504e 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -679,4 +679,23 @@ static inline struct sk_buff *skb_act_clone(struct sk_buff *skb, gfp_t gfp_mask, } #endif +struct psched_ratecfg { + u64 rate_bps; + u32 mult; + u32 shift; +}; + +static inline u64 psched_l2t_ns(const struct psched_ratecfg *r, + unsigned int len) +{ + return ((u64)len * r->mult) >> r->shift; +} + +extern void psched_ratecfg_precompute(struct psched_ratecfg *r, u32 rate); + +static inline u32 psched_ratecfg_getrate(const struct psched_ratecfg *r) +{ + return r->rate_bps >> 3; +} + #endif diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 5d81a4478514..ffad48109a22 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -896,3 +897,39 @@ void dev_shutdown(struct net_device *dev) WARN_ON(timer_pending(&dev->watchdog_timer)); } + +void psched_ratecfg_precompute(struct psched_ratecfg *r, u32 rate) +{ + u64 factor; + u64 mult; + int shift; + + r->rate_bps = rate << 3; + r->shift = 0; + r->mult = 1; + /* + * Calibrate mult, shift so that token counting is accurate + * for smallest packet size (64 bytes). Token (time in ns) is + * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps. It will + * work as long as the smallest packet transfer time can be + * accurately represented in nanosec. + */ + if (r->rate_bps > 0) { + /* + * Higher shift gives better accuracy. Find the largest + * shift such that mult fits in 32 bits. + */ + for (shift = 0; shift < 16; shift++) { + r->shift = shift; + factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); + mult = div64_u64(factor, r->rate_bps); + if (mult > UINT_MAX) + break; + } + + r->shift = shift - 1; + factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); + r->mult = div64_u64(factor, r->rate_bps); + } +} +EXPORT_SYMBOL(psched_ratecfg_precompute); diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 2b225446b3de..03c2692ca01e 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -38,6 +38,7 @@ #include #include #include +#include #include /* HTB algorithm. @@ -71,12 +72,6 @@ enum htb_cmode { HTB_CAN_SEND /* class can send */ }; -struct htb_rate_cfg { - u64 rate_bps; - u32 mult; - u32 shift; -}; - /* interior & leaf nodes; props specific to leaves are marked L: */ struct htb_class { struct Qdisc_class_common common; @@ -124,8 +119,8 @@ struct htb_class { int filter_cnt; /* token bucket parameters */ - struct htb_rate_cfg rate; - struct htb_rate_cfg ceil; + struct psched_ratecfg rate; + struct psched_ratecfg ceil; s64 buffer, cbuffer; /* token bucket depth/rate */ psched_tdiff_t mbuffer; /* max wait time */ s64 tokens, ctokens; /* current number of tokens */ @@ -168,45 +163,6 @@ struct htb_sched { struct work_struct work; }; -static u64 l2t_ns(struct htb_rate_cfg *r, unsigned int len) -{ - return ((u64)len * r->mult) >> r->shift; -} - -static void htb_precompute_ratedata(struct htb_rate_cfg *r) -{ - u64 factor; - u64 mult; - int shift; - - r->shift = 0; - r->mult = 1; - /* - * Calibrate mult, shift so that token counting is accurate - * for smallest packet size (64 bytes). Token (time in ns) is - * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps. It will - * work as long as the smallest packet transfer time can be - * accurately represented in nanosec. - */ - if (r->rate_bps > 0) { - /* - * Higher shift gives better accuracy. Find the largest - * shift such that mult fits in 32 bits. - */ - for (shift = 0; shift < 16; shift++) { - r->shift = shift; - factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); - mult = div64_u64(factor, r->rate_bps); - if (mult > UINT_MAX) - break; - } - - r->shift = shift - 1; - factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); - r->mult = div64_u64(factor, r->rate_bps); - } -} - /* find class in global hash table using given handle */ static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch) { @@ -632,7 +588,7 @@ static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, s64 diff) if (toks > cl->buffer) toks = cl->buffer; - toks -= (s64) l2t_ns(&cl->rate, bytes); + toks -= (s64) psched_l2t_ns(&cl->rate, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; @@ -645,7 +601,7 @@ static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, s64 diff) if (toks > cl->cbuffer) toks = cl->cbuffer; - toks -= (s64) l2t_ns(&cl->ceil, bytes); + toks -= (s64) psched_l2t_ns(&cl->ceil, bytes); if (toks <= -cl->mbuffer) toks = 1 - cl->mbuffer; @@ -1134,9 +1090,9 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, memset(&opt, 0, sizeof(opt)); - opt.rate.rate = cl->rate.rate_bps >> 3; + opt.rate.rate = psched_ratecfg_getrate(&cl->rate); opt.buffer = PSCHED_NS2TICKS(cl->buffer); - opt.ceil.rate = cl->ceil.rate_bps >> 3; + opt.ceil.rate = psched_ratecfg_getrate(&cl->ceil); opt.cbuffer = PSCHED_NS2TICKS(cl->cbuffer); opt.quantum = cl->quantum; opt.prio = cl->prio; @@ -1503,11 +1459,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, cl->prio = TC_HTB_NUMPRIO - 1; } - cl->rate.rate_bps = (u64)hopt->rate.rate << 3; - cl->ceil.rate_bps = (u64)hopt->ceil.rate << 3; - - htb_precompute_ratedata(&cl->rate); - htb_precompute_ratedata(&cl->ceil); + psched_ratecfg_precompute(&cl->rate, hopt->rate.rate); + psched_ratecfg_precompute(&cl->ceil, hopt->ceil.rate); cl->buffer = PSCHED_TICKS2NS(hopt->buffer); cl->cbuffer = PSCHED_TICKS2NS(hopt->buffer); -- cgit v1.2.3 From 34c5d292ce05d2bf52e692c44292b0ababba2853 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:04 +0000 Subject: sch_api: introduce qdisc_watchdog_schedule_ns() tbf will need to schedule watchdog in ns. No need to convert it twice. Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 10 ++++++++-- net/sched/sch_api.c | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 66f5ac370f92..388bf8b6d060 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -65,8 +65,14 @@ struct qdisc_watchdog { }; extern void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc); -extern void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, - psched_time_t expires); +extern void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires); + +static inline void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, + psched_time_t expires) +{ + qdisc_watchdog_schedule_ns(wd, PSCHED_TICKS2NS(expires)); +} + extern void qdisc_watchdog_cancel(struct qdisc_watchdog *wd); extern struct Qdisc_ops pfifo_qdisc_ops; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index d84f7e734cd7..fe1ba54b93f7 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -493,7 +493,7 @@ void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_watchdog_init); -void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires) +void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires) { if (test_bit(__QDISC_STATE_DEACTIVATED, &qdisc_root_sleeping(wd->qdisc)->state)) @@ -502,10 +502,10 @@ void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires) qdisc_throttled(wd->qdisc); hrtimer_start(&wd->timer, - ns_to_ktime(PSCHED_TICKS2NS(expires)), + ns_to_ktime(expires), HRTIMER_MODE_ABS); } -EXPORT_SYMBOL(qdisc_watchdog_schedule); +EXPORT_SYMBOL(qdisc_watchdog_schedule_ns); void qdisc_watchdog_cancel(struct qdisc_watchdog *wd) { -- cgit v1.2.3 From b757c9336d63f94c6b57532bb4e8651d8b28786f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:05 +0000 Subject: tbf: improved accuracy at high rates Current TBF uses rate table computed by the "tc" userspace program, which has the following issue: The rate table has 256 entries to map packet lengths to token (time units). With TSO sized packets, the 256 entry granularity leads to loss/gain of rate, making the token bucket inaccurate. Thus, instead of relying on rate table, this patch explicitly computes the time and accounts for packet transmission times with nanosecond granularity. This is a followup to 56b765b79e9a78dc7d3f8850ba5e5567205a3ecd ("htb: improved accuracy at high rates"). Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_tbf.c | 76 ++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 39 deletions(-) (limited to 'net') diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 4b056c15e90c..c8388f3c3426 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -100,23 +101,21 @@ struct tbf_sched_data { /* Parameters */ u32 limit; /* Maximal length of backlog: bytes */ - u32 buffer; /* Token bucket depth/rate: MUST BE >= MTU/B */ - u32 mtu; + s64 buffer; /* Token bucket depth/rate: MUST BE >= MTU/B */ + s64 mtu; u32 max_size; - struct qdisc_rate_table *R_tab; - struct qdisc_rate_table *P_tab; + struct psched_ratecfg rate; + struct psched_ratecfg peak; + bool peak_present; /* Variables */ - long tokens; /* Current number of B tokens */ - long ptokens; /* Current number of P tokens */ - psched_time_t t_c; /* Time check-point */ + s64 tokens; /* Current number of B tokens */ + s64 ptokens; /* Current number of P tokens */ + s64 t_c; /* Time check-point */ struct Qdisc *qdisc; /* Inner qdisc, default - bfifo queue */ struct qdisc_watchdog watchdog; /* Watchdog timer */ }; -#define L2T(q, L) qdisc_l2t((q)->R_tab, L) -#define L2T_P(q, L) qdisc_l2t((q)->P_tab, L) - static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct tbf_sched_data *q = qdisc_priv(sch); @@ -156,24 +155,24 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch) skb = q->qdisc->ops->peek(q->qdisc); if (skb) { - psched_time_t now; - long toks; - long ptoks = 0; + s64 now; + s64 toks; + s64 ptoks = 0; unsigned int len = qdisc_pkt_len(skb); - now = psched_get_time(); - toks = psched_tdiff_bounded(now, q->t_c, q->buffer); + now = ktime_to_ns(ktime_get()); + toks = min_t(s64, now - q->t_c, q->buffer); - if (q->P_tab) { + if (q->peak_present) { ptoks = toks + q->ptokens; - if (ptoks > (long)q->mtu) + if (ptoks > q->mtu) ptoks = q->mtu; - ptoks -= L2T_P(q, len); + ptoks -= (s64) psched_l2t_ns(&q->peak, len); } toks += q->tokens; - if (toks > (long)q->buffer) + if (toks > q->buffer) toks = q->buffer; - toks -= L2T(q, len); + toks -= (s64) psched_l2t_ns(&q->rate, len); if ((toks|ptoks) >= 0) { skb = qdisc_dequeue_peeked(q->qdisc); @@ -189,8 +188,8 @@ static struct sk_buff *tbf_dequeue(struct Qdisc *sch) return skb; } - qdisc_watchdog_schedule(&q->watchdog, - now + max_t(long, -toks, -ptoks)); + qdisc_watchdog_schedule_ns(&q->watchdog, + now + max_t(long, -toks, -ptoks)); /* Maybe we have a shorter packet in the queue, which can be sent now. It sounds cool, @@ -214,7 +213,7 @@ static void tbf_reset(struct Qdisc *sch) qdisc_reset(q->qdisc); sch->q.qlen = 0; - q->t_c = psched_get_time(); + q->t_c = ktime_to_ns(ktime_get()); q->tokens = q->buffer; q->ptokens = q->mtu; qdisc_watchdog_cancel(&q->watchdog); @@ -293,14 +292,19 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) q->qdisc = child; } q->limit = qopt->limit; - q->mtu = qopt->mtu; + q->mtu = PSCHED_TICKS2NS(qopt->mtu); q->max_size = max_size; - q->buffer = qopt->buffer; + q->buffer = PSCHED_TICKS2NS(qopt->buffer); q->tokens = q->buffer; q->ptokens = q->mtu; - swap(q->R_tab, rtab); - swap(q->P_tab, ptab); + psched_ratecfg_precompute(&q->rate, rtab->rate.rate); + if (ptab) { + psched_ratecfg_precompute(&q->peak, ptab->rate.rate); + q->peak_present = true; + } else { + q->peak_present = false; + } sch_tree_unlock(sch); err = 0; @@ -319,7 +323,7 @@ static int tbf_init(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - q->t_c = psched_get_time(); + q->t_c = ktime_to_ns(ktime_get()); qdisc_watchdog_init(&q->watchdog, sch); q->qdisc = &noop_qdisc; @@ -331,12 +335,6 @@ static void tbf_destroy(struct Qdisc *sch) struct tbf_sched_data *q = qdisc_priv(sch); qdisc_watchdog_cancel(&q->watchdog); - - if (q->P_tab) - qdisc_put_rtab(q->P_tab); - if (q->R_tab) - qdisc_put_rtab(q->R_tab); - qdisc_destroy(q->qdisc); } @@ -352,13 +350,13 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb) goto nla_put_failure; opt.limit = q->limit; - opt.rate = q->R_tab->rate; - if (q->P_tab) - opt.peakrate = q->P_tab->rate; + opt.rate.rate = psched_ratecfg_getrate(&q->rate); + if (q->peak_present) + opt.peakrate.rate = psched_ratecfg_getrate(&q->peak); else memset(&opt.peakrate, 0, sizeof(opt.peakrate)); - opt.mtu = q->mtu; - opt.buffer = q->buffer; + opt.mtu = PSCHED_NS2TICKS(q->mtu); + opt.buffer = PSCHED_NS2TICKS(q->buffer); if (nla_put(skb, TCA_TBF_PARMS, sizeof(opt), &opt)) goto nla_put_failure; -- cgit v1.2.3 From 0e243218bac42ebd0a9c19ceb0003410d302d008 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:06 +0000 Subject: act_police: move struct tcf_police to act_police.c It's not used anywhere else, so move it. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/act_api.h | 15 --------------- net/sched/act_police.c | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 112c25c393a2..06ef7e926a66 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -35,21 +35,6 @@ struct tcf_common { #define tcf_lock common.tcfc_lock #define tcf_rcu common.tcfc_rcu -struct tcf_police { - struct tcf_common common; - int tcfp_result; - u32 tcfp_ewma_rate; - u32 tcfp_burst; - u32 tcfp_mtu; - u32 tcfp_toks; - u32 tcfp_ptoks; - psched_time_t tcfp_t_c; - struct qdisc_rate_table *tcfp_R_tab; - struct qdisc_rate_table *tcfp_P_tab; -}; -#define to_police(pc) \ - container_of(pc, struct tcf_police, common) - struct tcf_hashinfo { struct tcf_common **htab; unsigned int hmask; diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 8dbd695c160b..378a6494ba5a 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -22,6 +22,21 @@ #include #include +struct tcf_police { + struct tcf_common common; + int tcfp_result; + u32 tcfp_ewma_rate; + u32 tcfp_burst; + u32 tcfp_mtu; + u32 tcfp_toks; + u32 tcfp_ptoks; + psched_time_t tcfp_t_c; + struct qdisc_rate_table *tcfp_R_tab; + struct qdisc_rate_table *tcfp_P_tab; +}; +#define to_police(pc) \ + container_of(pc, struct tcf_police, common) + #define L2T(p, L) qdisc_l2t((p)->tcfp_R_tab, L) #define L2T_P(p, L) qdisc_l2t((p)->tcfp_P_tab, L) -- cgit v1.2.3 From c6d14ff11bec4c02601be198c84a5c76d1af90fc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 12 Feb 2013 00:12:07 +0000 Subject: act_police: improved accuracy at high rates Current act_police uses rate table computed by the "tc" userspace program, which has the following issue: The rate table has 256 entries to map packet lengths to token (time units). With TSO sized packets, the 256 entry granularity leads to loss/gain of rate, making the token bucket inaccurate. Thus, instead of relying on rate table, this patch explicitly computes the time and accounts for packet transmission times with nanosecond granularity. This is a followup to 56b765b79e9a78dc7d3f8850ba5e5567205a3ecd ("htb: improved accuracy at high rates"). Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/act_police.c | 99 +++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 46 deletions(-) (limited to 'net') diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 378a6494ba5a..823463adbd21 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -26,20 +26,20 @@ struct tcf_police { struct tcf_common common; int tcfp_result; u32 tcfp_ewma_rate; - u32 tcfp_burst; + s64 tcfp_burst; u32 tcfp_mtu; - u32 tcfp_toks; - u32 tcfp_ptoks; - psched_time_t tcfp_t_c; - struct qdisc_rate_table *tcfp_R_tab; - struct qdisc_rate_table *tcfp_P_tab; + s64 tcfp_toks; + s64 tcfp_ptoks; + s64 tcfp_mtu_ptoks; + s64 tcfp_t_c; + struct psched_ratecfg rate; + bool rate_present; + struct psched_ratecfg peak; + bool peak_present; }; #define to_police(pc) \ container_of(pc, struct tcf_police, common) -#define L2T(p, L) qdisc_l2t((p)->tcfp_R_tab, L) -#define L2T_P(p, L) qdisc_l2t((p)->tcfp_P_tab, L) - #define POL_TAB_MASK 15 static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1]; static u32 police_idx_gen; @@ -123,10 +123,6 @@ static void tcf_police_destroy(struct tcf_police *p) write_unlock_bh(&police_lock); gen_kill_estimator(&p->tcf_bstats, &p->tcf_rate_est); - if (p->tcfp_R_tab) - qdisc_put_rtab(p->tcfp_R_tab); - if (p->tcfp_P_tab) - qdisc_put_rtab(p->tcfp_P_tab); /* * gen_estimator est_timer() might access p->tcf_lock * or bstats, wait a RCU grace period before freeing p @@ -227,26 +223,36 @@ override: } /* No failure allowed after this point */ - if (R_tab != NULL) { - qdisc_put_rtab(police->tcfp_R_tab); - police->tcfp_R_tab = R_tab; + police->tcfp_mtu = parm->mtu; + if (police->tcfp_mtu == 0) { + police->tcfp_mtu = ~0; + if (R_tab) + police->tcfp_mtu = 255 << R_tab->rate.cell_log; + } + if (R_tab) { + police->rate_present = true; + psched_ratecfg_precompute(&police->rate, R_tab->rate.rate); + qdisc_put_rtab(R_tab); + } else { + police->rate_present = false; } - if (P_tab != NULL) { - qdisc_put_rtab(police->tcfp_P_tab); - police->tcfp_P_tab = P_tab; + if (P_tab) { + police->peak_present = true; + psched_ratecfg_precompute(&police->peak, P_tab->rate.rate); + qdisc_put_rtab(P_tab); + } else { + police->peak_present = false; } if (tb[TCA_POLICE_RESULT]) police->tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); - police->tcfp_toks = police->tcfp_burst = parm->burst; - police->tcfp_mtu = parm->mtu; - if (police->tcfp_mtu == 0) { - police->tcfp_mtu = ~0; - if (police->tcfp_R_tab) - police->tcfp_mtu = 255<tcfp_R_tab->rate.cell_log; + police->tcfp_burst = PSCHED_TICKS2NS(parm->burst); + police->tcfp_toks = police->tcfp_burst; + if (police->peak_present) { + police->tcfp_mtu_ptoks = (s64) psched_l2t_ns(&police->peak, + police->tcfp_mtu); + police->tcfp_ptoks = police->tcfp_mtu_ptoks; } - if (police->tcfp_P_tab) - police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); police->tcf_action = parm->action; if (tb[TCA_POLICE_AVRATE]) @@ -256,7 +262,7 @@ override: if (ret != ACT_P_CREATED) return ret; - police->tcfp_t_c = psched_get_time(); + police->tcfp_t_c = ktime_to_ns(ktime_get()); police->tcf_index = parm->index ? parm->index : tcf_hash_new_index(&police_idx_gen, &police_hash_info); h = tcf_hash(police->tcf_index, POL_TAB_MASK); @@ -302,9 +308,9 @@ static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct tcf_police *police = a->priv; - psched_time_t now; - long toks; - long ptoks = 0; + s64 now; + s64 toks; + s64 ptoks = 0; spin_lock(&police->tcf_lock); @@ -320,24 +326,25 @@ static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a, } if (qdisc_pkt_len(skb) <= police->tcfp_mtu) { - if (police->tcfp_R_tab == NULL) { + if (!police->rate_present) { spin_unlock(&police->tcf_lock); return police->tcfp_result; } - now = psched_get_time(); - toks = psched_tdiff_bounded(now, police->tcfp_t_c, - police->tcfp_burst); - if (police->tcfp_P_tab) { + now = ktime_to_ns(ktime_get()); + toks = min_t(s64, now - police->tcfp_t_c, + police->tcfp_burst); + if (police->peak_present) { ptoks = toks + police->tcfp_ptoks; - if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) - ptoks = (long)L2T_P(police, police->tcfp_mtu); - ptoks -= L2T_P(police, qdisc_pkt_len(skb)); + if (ptoks > police->tcfp_mtu_ptoks) + ptoks = police->tcfp_mtu_ptoks; + ptoks -= (s64) psched_l2t_ns(&police->peak, + qdisc_pkt_len(skb)); } toks += police->tcfp_toks; - if (toks > (long)police->tcfp_burst) + if (toks > police->tcfp_burst) toks = police->tcfp_burst; - toks -= L2T(police, qdisc_pkt_len(skb)); + toks -= (s64) psched_l2t_ns(&police->rate, qdisc_pkt_len(skb)); if ((toks|ptoks) >= 0) { police->tcfp_t_c = now; police->tcfp_toks = toks; @@ -363,15 +370,15 @@ tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) .index = police->tcf_index, .action = police->tcf_action, .mtu = police->tcfp_mtu, - .burst = police->tcfp_burst, + .burst = PSCHED_NS2TICKS(police->tcfp_burst), .refcnt = police->tcf_refcnt - ref, .bindcnt = police->tcf_bindcnt - bind, }; - if (police->tcfp_R_tab) - opt.rate = police->tcfp_R_tab->rate; - if (police->tcfp_P_tab) - opt.peakrate = police->tcfp_P_tab->rate; + if (police->rate_present) + opt.rate.rate = psched_ratecfg_getrate(&police->rate); + if (police->peak_present) + opt.peakrate.rate = psched_ratecfg_getrate(&police->peak); if (nla_put(skb, TCA_POLICE_TBF, sizeof(opt), &opt)) goto nla_put_failure; if (police->tcfp_result && -- cgit v1.2.3 From bb92d19983a4b54be3e3b83441a8076d92cd04bc Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 12 Feb 2013 12:16:26 -0800 Subject: nl80211: add packet offset information for wowlan pattern If user knows the location of a wowlan pattern to be matched in Rx packet, he can provide an offset with the pattern. This will help drivers to ignore initial bytes and match the pattern efficiently. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao [refactor pattern sending] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 +++ include/uapi/linux/nl80211.h | 12 +++++-- net/wireless/nl80211.c | 74 ++++++++++++++++++++++++++++---------------- 3 files changed, 60 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 77686ca28948..d3a73818e44c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1576,6 +1576,7 @@ struct cfg80211_pmksa { * one bit per byte, in same format as nl80211 * @pattern: bytes to match where bitmask is 1 * @pattern_len: length of pattern (in bytes) + * @pkt_offset: packet offset (in bytes) * * Internal note: @mask and @pattern are allocated in one chunk of * memory, free @mask only! @@ -1583,6 +1584,7 @@ struct cfg80211_pmksa { struct cfg80211_wowlan_trig_pkt_pattern { u8 *mask, *pattern; int pattern_len; + int pkt_offset; }; /** @@ -2290,12 +2292,14 @@ enum wiphy_wowlan_support_flags { * (see nl80211.h for the pattern definition) * @pattern_max_len: maximum length of each pattern * @pattern_min_len: minimum length of each pattern + * @max_pkt_offset: maximum Rx packet offset */ struct wiphy_wowlan_support { u32 flags; int n_patterns; int pattern_max_len; int pattern_min_len; + int max_pkt_offset; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b23321154e8b..eb7b32247ec5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2910,6 +2910,8 @@ enum nl80211_tx_power_setting { * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. + * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after + * these fixed number of bytes of received packet * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number */ @@ -2917,6 +2919,7 @@ enum nl80211_wowlan_packet_pattern_attr { __NL80211_WOWLAN_PKTPAT_INVALID, NL80211_WOWLAN_PKTPAT_MASK, NL80211_WOWLAN_PKTPAT_PATTERN, + NL80211_WOWLAN_PKTPAT_OFFSET, NUM_NL80211_WOWLAN_PKTPAT, MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, @@ -2927,6 +2930,7 @@ enum nl80211_wowlan_packet_pattern_attr { * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern + * @max_pkt_offset: maximum Rx packet offset * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the @@ -2936,6 +2940,7 @@ struct nl80211_wowlan_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; + __u32 max_pkt_offset; } __attribute__((packed)); /** @@ -2951,9 +2956,10 @@ struct nl80211_wowlan_pattern_support { * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns * which are passed in an array of nested attributes, each nested attribute * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. - * Each pattern defines a wakeup packet. The matching is done on the MSDU, - * i.e. as though the packet was an 802.3 packet, so the pattern matching - * is done after the packet is converted to the MSDU. + * Each pattern defines a wakeup packet. Packet offset is associated with + * each pattern which is used while matching the pattern. The matching is + * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the + * pattern matching is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute * carrying a &struct nl80211_wowlan_pattern_support. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 93bc63eae076..cc0fad30b8c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1238,6 +1238,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag dev->wiphy.wowlan.pattern_min_len, .max_pattern_len = dev->wiphy.wowlan.pattern_max_len, + .max_pkt_offset = + dev->wiphy.wowlan.max_pkt_offset, }; if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, sizeof(pat), &pat)) @@ -6895,6 +6897,39 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) } #ifdef CONFIG_PM +static int nl80211_send_wowlan_patterns(struct sk_buff *msg, + struct cfg80211_registered_device *rdev) +{ + struct nlattr *nl_pats, *nl_pat; + int i, pat_len; + + if (!rdev->wowlan->n_patterns) + return 0; + + nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN); + if (!nl_pats) + return -ENOBUFS; + + for (i = 0; i < rdev->wowlan->n_patterns; i++) { + nl_pat = nla_nest_start(msg, i + 1); + if (!nl_pat) + return -ENOBUFS; + pat_len = rdev->wowlan->patterns[i].pattern_len; + if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, + DIV_ROUND_UP(pat_len, 8), + rdev->wowlan->patterns[i].mask) || + nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, + pat_len, rdev->wowlan->patterns[i].pattern) || + nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, + rdev->wowlan->patterns[i].pkt_offset)) + return -ENOBUFS; + nla_nest_end(msg, nl_pat); + } + nla_nest_end(msg, nl_pats); + + return 0; +} + static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6935,32 +6970,8 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) (rdev->wowlan->rfkill_release && nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) goto nla_put_failure; - if (rdev->wowlan->n_patterns) { - struct nlattr *nl_pats, *nl_pat; - int i, pat_len; - - nl_pats = nla_nest_start(msg, - NL80211_WOWLAN_TRIG_PKT_PATTERN); - if (!nl_pats) - goto nla_put_failure; - - for (i = 0; i < rdev->wowlan->n_patterns; i++) { - nl_pat = nla_nest_start(msg, i + 1); - if (!nl_pat) - goto nla_put_failure; - pat_len = rdev->wowlan->patterns[i].pattern_len; - if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, - DIV_ROUND_UP(pat_len, 8), - rdev->wowlan->patterns[i].mask) || - nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, - pat_len, - rdev->wowlan->patterns[i].pattern)) - goto nla_put_failure; - nla_nest_end(msg, nl_pat); - } - nla_nest_end(msg, nl_pats); - } - + if (nl80211_send_wowlan_patterns(msg, rdev)) + goto nla_put_failure; nla_nest_end(msg, nl_wowlan); } @@ -7046,7 +7057,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { struct nlattr *pat; int n_patterns = 0; - int rem, pat_len, mask_len; + int rem, pat_len, mask_len, pkt_offset; struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], @@ -7081,6 +7092,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) pat_len < wowlan->pattern_min_len) goto error; + if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) + pkt_offset = 0; + else + pkt_offset = nla_get_u32( + pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); + if (pkt_offset > wowlan->max_pkt_offset) + goto error; + new_triggers.patterns[i].pkt_offset = pkt_offset; + new_triggers.patterns[i].mask = kmalloc(mask_len + pat_len, GFP_KERNEL); if (!new_triggers.patterns[i].mask) { -- cgit v1.2.3 From 6719429dd61cde1fe30d9644d0aa2369eefc9005 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 12 Feb 2013 20:11:38 +0200 Subject: cfg80211: check vendor IE length to avoid overrun cfg80211_find_vendor_ie() was checking only that the vendor IE would fit in the remaining IEs buffer. If a corrupt includes a vendor IE that is too small, we could potentially overrun the IEs buffer. Fix this by checking that the vendor IE fits in the reported IE length field and skip it otherwise. Reported-by: Jouni Malinen Signed-off-by: Luciano Coelho [change BUILD_BUG_ON to != 1 (from >= 2)] Signed-off-by: Johannes Berg --- net/wireless/scan.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index b7a167984986..d0fc6da2d097 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -365,14 +365,18 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, if (!pos) return NULL; - if (end - pos < sizeof(*ie)) - return NULL; - ie = (struct ieee80211_vendor_ie *)pos; + + /* make sure we can access ie->len */ + BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1); + + if (ie->len < sizeof(*ie)) + goto cont; + ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; if (ie_oui == oui && ie->oui_type == oui_type) return pos; - +cont: pos += 2 + ie->len; } return NULL; -- cgit v1.2.3 From a0497f9f57478c5a37c5628eb32833dd9729a821 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 13 Feb 2013 10:51:08 +0100 Subject: mac80211/minstrel_ht: add support for using CCK rates When MCS rates start to get bad in 2.4 GHz because of long range or strong interference, CCK rates can be a lot more robust. This patch adds a pseudo MCS group containing CCK rates (long preamble in the lower 4 slots, short preamble in the upper slots). Signed-off-by: Felix Fietkau [make minstrel_ht_get_stats static] Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 29 ++++++ net/mac80211/rc80211_minstrel.h | 2 + net/mac80211/rc80211_minstrel_ht.c | 152 +++++++++++++++++++++++++---- net/mac80211/rc80211_minstrel_ht.h | 5 +- net/mac80211/rc80211_minstrel_ht_debugfs.c | 109 +++++++++++++-------- 5 files changed, 237 insertions(+), 60 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 8c5acdc06226..eea45a2c7c35 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -494,6 +494,33 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) kfree(mi); } +static void +minstrel_init_cck_rates(struct minstrel_priv *mp) +{ + static const int bitrates[4] = { 10, 20, 55, 110 }; + struct ieee80211_supported_band *sband; + int i, j; + + sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + if (!sband) + return; + + for (i = 0, j = 0; i < sband->n_bitrates; i++) { + struct ieee80211_rate *rate = &sband->bitrates[i]; + + if (rate->flags & IEEE80211_RATE_ERP_G) + continue; + + for (j = 0; j < ARRAY_SIZE(bitrates); j++) { + if (rate->bitrate != bitrates[j]) + continue; + + mp->cck_rates[j] = i; + break; + } + } +} + static void * minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { @@ -539,6 +566,8 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); #endif + minstrel_init_cck_rates(mp); + return mp; } diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 5d278eccaef0..5ecf757817f2 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -79,6 +79,8 @@ struct minstrel_priv { unsigned int lookaround_rate; unsigned int lookaround_rate_mrr; + u8 cck_rates[4]; + #ifdef CONFIG_MAC80211_DEBUGFS /* * enable fixed rate processing per RC diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 5bb316aff21a..6e35dcd2aa2f 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Felix Fietkau + * Copyright (C) 2010-2013 Felix Fietkau * * 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 @@ -63,6 +63,30 @@ } \ } +#define CCK_DURATION(_bitrate, _short, _len) \ + (10 /* SIFS */ + \ + (_short ? 72 + 24 : 144 + 48 ) + \ + (8 * (_len + 4) * 10) / (_bitrate)) + +#define CCK_ACK_DURATION(_bitrate, _short) \ + (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \ + CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE)) + +#define CCK_DURATION_LIST(_short) \ + CCK_ACK_DURATION(10, _short), \ + CCK_ACK_DURATION(20, _short), \ + CCK_ACK_DURATION(55, _short), \ + CCK_ACK_DURATION(110, _short) + +#define CCK_GROUP \ + [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \ + .streams = 0, \ + .duration = { \ + CCK_DURATION_LIST(false), \ + CCK_DURATION_LIST(true) \ + } \ + } + /* * To enable sufficiently targeted rate sampling, MCS rates are divided into * groups, based on the number of streams and flags (HT40, SGI) that they @@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_groups[] = { #if MINSTREL_MAX_STREAMS >= 3 MCS_GROUP(3, 1, 1), #endif + + /* must be last */ + CCK_GROUP }; +#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1) + static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; /* @@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); } +static struct minstrel_rate_stats * +minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + struct ieee80211_tx_rate *rate) +{ + int group, idx; + + if (rate->flags & IEEE80211_TX_RC_MCS) { + group = minstrel_ht_get_group_idx(rate); + idx = rate->idx % MCS_GROUP_RATES; + } else { + group = MINSTREL_CCK_GROUP; + + for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++) + if (rate->idx == mp->cck_rates[idx]) + break; + + /* short preamble */ + if (!(mi->groups[group].supported & BIT(idx))) + idx += 4; + } + return &mi->groups[group].rates[idx]; +} + static inline struct minstrel_rate_stats * minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) { @@ -159,7 +211,7 @@ static void minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) { struct minstrel_rate_stats *mr; - unsigned int usecs; + unsigned int usecs = 0; mr = &mi->groups[group].rates[rate]; @@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) return; } - usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); + if (group != MINSTREL_CCK_GROUP) + usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); + usecs += minstrel_mcs_groups[group].duration[rate]; mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability); } @@ -293,7 +347,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) } static bool -minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) +minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate) { if (rate->idx < 0) return false; @@ -301,7 +355,13 @@ minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) if (!rate->count) return false; - return !!(rate->flags & IEEE80211_TX_RC_MCS); + if (rate->flags & IEEE80211_TX_RC_MCS) + return true; + + return rate->idx == mp->cck_rates[0] || + rate->idx == mp->cck_rates[1] || + rate->idx == mp->cck_rates[2] || + rate->idx == mp->cck_rates[3]; } static void @@ -386,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, struct minstrel_rate_stats *rate, *rate2; struct minstrel_priv *mp = priv; bool last; - int group; int i; if (!msp->is_ht) @@ -415,13 +474,12 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) mi->sample_packets += info->status.ampdu_len; - last = !minstrel_ht_txstat_valid(&ar[0]); + last = !minstrel_ht_txstat_valid(mp, &ar[0]); for (i = 0; !last; i++) { last = (i == IEEE80211_TX_MAX_RATES - 1) || - !minstrel_ht_txstat_valid(&ar[i + 1]); + !minstrel_ht_txstat_valid(mp, &ar[i + 1]); - group = minstrel_ht_get_group_idx(&ar[i]); - rate = &mi->groups[group].rates[ar[i].idx % 8]; + rate = minstrel_ht_get_stats(mp, mi, &ar[i]); if (last) rate->success += info->status.ampdu_ack_len; @@ -447,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { minstrel_ht_update_stats(mp, mi); - if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) + if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && + mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) minstrel_aggr_check(sta, skb); } } @@ -463,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, unsigned int ctime = 0; unsigned int t_slot = 9; /* FIXME */ unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); + unsigned int overhead = 0, overhead_rtscts = 0; mr = minstrel_get_ratestats(mi, index); if (mr->probability < MINSTREL_FRAC(1, 10)) { @@ -484,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ctime += (t_slot * cw) >> 1; cw = min((cw << 1) | 1, mp->cw_max); + if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) { + overhead = mi->overhead; + overhead_rtscts = mi->overhead_rtscts; + } + /* Total TX time for data and Contention after first 2 tries */ - tx_time = ctime + 2 * (mi->overhead + tx_time_data); - tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data); + tx_time = ctime + 2 * (overhead + tx_time_data); + tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data); /* See how many more tries we can fit inside segment size */ do { @@ -495,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, cw = min((cw << 1) | 1, mp->cw_max); /* Total TX time after this try */ - tx_time += ctime + mi->overhead + tx_time_data; - tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data; + tx_time += ctime + overhead + tx_time_data; + tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; if (tx_time_rtscts < mp->segment_size) mr->retry_count_rtscts++; @@ -526,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, else rate->count = mr->retry_count; - rate->flags = IEEE80211_TX_RC_MCS | group->flags; + rate->flags = 0; if (rtscts) rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; + + if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { + rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; + return; + } + + rate->flags |= IEEE80211_TX_RC_MCS | group->flags; rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; } @@ -591,6 +663,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) return sample_idx; } +static void +minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp, + struct minstrel_ht_sta *mi, bool val) +{ + u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported; + + if (!supported || !mi->cck_supported_short) + return; + + if (supported & (mi->cck_supported_short << (val * 4))) + return; + + supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4); + mi->groups[MINSTREL_CCK_GROUP].supported = supported; +} + static void minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) @@ -610,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); info->flags |= mi->tx_flags; + minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); /* Don't use EAPOL frames for sampling on non-mrr hw */ if (mp->hw->max_rates == 1 && @@ -682,6 +771,30 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, } } +static void +minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, + struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + int i; + + if (sband->band != IEEE80211_BAND_2GHZ) + return; + + mi->cck_supported = 0; + mi->cck_supported_short = 0; + for (i = 0; i < 4; i++) { + if (!rate_supported(sta, sband->band, mp->cck_rates[i])) + continue; + + mi->cck_supported |= BIT(i); + if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE) + mi->cck_supported_short |= BIT(i); + } + + mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported; +} + static void minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) @@ -702,7 +815,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, goto use_legacy; BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != - MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); + MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1); msp->is_ht = true; memset(mi, 0, sizeof(*mi)); @@ -738,6 +851,11 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, u16 req = 0; mi->groups[i].supported = 0; + if (i == MINSTREL_CCK_GROUP) { + minstrel_ht_update_cck(mp, mi, sband, sta); + continue; + } + if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) req |= IEEE80211_HT_CAP_SGI_40; diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 462d2b227ed5..302dbd52180d 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -107,8 +107,11 @@ struct minstrel_ht_sta { /* current MCS group to be sampled */ u8 sample_group; + u8 cck_supported; + u8 cck_supported_short; + /* MCS rate group info and statistics */ - struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; + struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1]; }; struct minstrel_ht_sta_priv { diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index f2b7d26370f0..df44a5ad8270 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -15,13 +15,76 @@ #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" +static char * +minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) +{ + unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; + const struct mcs_group *mg; + unsigned int j, tp, prob, eprob; + char htmode = '2'; + char gimode = 'L'; + + if (!mi->groups[i].supported) + return p; + + mg = &minstrel_mcs_groups[i]; + if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + htmode = '4'; + if (mg->flags & IEEE80211_TX_RC_SHORT_GI) + gimode = 'S'; + + for (j = 0; j < MCS_GROUP_RATES; j++) { + struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; + static const int bitrates[4] = { 10, 20, 55, 110 }; + int idx = i * MCS_GROUP_RATES + j; + + if (!(mi->groups[i].supported & BIT(j))) + continue; + + if (i == max_mcs) + p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S'); + else + p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); + + *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; + *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; + *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; + + if (i == max_mcs) { + int r = bitrates[j % 4]; + p += sprintf(p, " %2u.%1uM", r / 10, r % 10); + } else { + p += sprintf(p, " MCS%-2u", (mg->streams - 1) * + MCS_GROUP_RATES + j); + } + + tp = mr->cur_tp / 10; + prob = MINSTREL_TRUNC(mr->cur_prob * 1000); + eprob = MINSTREL_TRUNC(mr->probability * 1000); + + p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " + "%3u %3u(%3u) %8llu %8llu\n", + tp / 10, tp % 10, + eprob / 10, eprob % 10, + prob / 10, prob % 10, + mr->retry_count, + mr->last_success, + mr->last_attempts, + (unsigned long long)mr->succ_hist, + (unsigned long long)mr->att_hist); + } + + return p; +} + static int minstrel_ht_stats_open(struct inode *inode, struct file *file) { struct minstrel_ht_sta_priv *msp = inode->i_private; struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_debugfs_info *ms; - unsigned int i, j, tp, prob, eprob; + unsigned int i; + unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; char *p; int ret; @@ -40,49 +103,11 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) p = ms->buf; p += sprintf(p, "type rate throughput ewma prob this prob " "retry this succ/attempt success attempts\n"); - for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) { - char htmode = '2'; - char gimode = 'L'; - - if (!mi->groups[i].supported) - continue; - - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - htmode = '4'; - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) - gimode = 'S'; - for (j = 0; j < MCS_GROUP_RATES; j++) { - struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; - int idx = i * MCS_GROUP_RATES + j; + p = minstrel_ht_stats_dump(mi, max_mcs, p); + for (i = 0; i < max_mcs; i++) + p = minstrel_ht_stats_dump(mi, i, p); - if (!(mi->groups[i].supported & BIT(j))) - continue; - - p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); - - *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; - *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; - *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; - p += sprintf(p, " MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * - MCS_GROUP_RATES + j); - - tp = mr->cur_tp / 10; - prob = MINSTREL_TRUNC(mr->cur_prob * 1000); - eprob = MINSTREL_TRUNC(mr->probability * 1000); - - p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " - "%3u %3u(%3u) %8llu %8llu\n", - tp / 10, tp % 10, - eprob / 10, eprob % 10, - prob / 10, prob % 10, - mr->retry_count, - mr->last_success, - mr->last_attempts, - (unsigned long long)mr->succ_hist, - (unsigned long long)mr->att_hist); - } - } p += sprintf(p, "\nTotal packet count:: ideal %d " "lookaround %d\n", max(0, (int) mi->total_packets - (int) mi->sample_packets), -- cgit v1.2.3 From 2a0e047ed62f20664005881b8e7f9328f910316a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 23 Jan 2013 22:57:40 +0100 Subject: cfg80211: configuration for WoWLAN over TCP Intel Wireless devices are able to make a TCP connection after suspending, sending some data and waking up when the connection receives wakeup data (or breaks). Add the WoWLAN configuration and feature advertising API for it. Acked-by: David S. Miller Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 55 +++++++- include/uapi/linux/nl80211.h | 125 ++++++++++++++++++ net/wireless/core.h | 3 + net/wireless/nl80211.c | 295 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 474 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d3a73818e44c..7e6569e1f16f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -19,6 +19,7 @@ #include #include #include +#include #include /** @@ -1587,6 +1588,41 @@ struct cfg80211_wowlan_trig_pkt_pattern { int pkt_offset; }; +/** + * struct cfg80211_wowlan_tcp - TCP connection parameters + * + * @sock: (internal) socket for source port allocation + * @src: source IP address + * @dst: destination IP address + * @dst_mac: destination MAC address + * @src_port: source port + * @dst_port: destination port + * @payload_len: data payload length + * @payload: data payload buffer + * @payload_seq: payload sequence stamping configuration + * @data_interval: interval at which to send data packets + * @wake_len: wakeup payload match length + * @wake_data: wakeup payload match data + * @wake_mask: wakeup payload match mask + * @tokens_size: length of the tokens buffer + * @payload_tok: payload token usage configuration + */ +struct cfg80211_wowlan_tcp { + struct socket *sock; + __be32 src, dst; + u16 src_port, dst_port; + u8 dst_mac[ETH_ALEN]; + int payload_len; + const u8 *payload; + struct nl80211_wowlan_tcp_data_seq payload_seq; + u32 data_interval; + u32 wake_len; + const u8 *wake_data, *wake_mask; + u32 tokens_size; + /* must be last, variable member */ + struct nl80211_wowlan_tcp_data_token payload_tok; +}; + /** * struct cfg80211_wowlan - Wake on Wireless-LAN support info * @@ -1601,12 +1637,15 @@ struct cfg80211_wowlan_trig_pkt_pattern { * @eap_identity_req: wake up on EAP identity request packet * @four_way_handshake: wake up on 4-way handshake * @rfkill_release: wake up when rfkill is released + * @tcp: TCP connection establishment/wakeup parameters, see nl80211.h. + * NULL if not configured. */ struct cfg80211_wowlan { bool any, disconnect, magic_pkt, gtk_rekey_failure, eap_identity_req, four_way_handshake, rfkill_release; struct cfg80211_wowlan_trig_pkt_pattern *patterns; + struct cfg80211_wowlan_tcp *tcp; int n_patterns; }; @@ -1626,11 +1665,15 @@ struct cfg80211_wowlan { * frame triggers an 802.3 frame should be reported, for * disconnect due to deauth 802.11 frame. This indicates which * it is. + * @tcp_match: TCP wakeup packet received + * @tcp_connlost: TCP connection lost or failed to establish + * @tcp_nomoretokens: TCP data ran out of tokens */ struct cfg80211_wowlan_wakeup { bool disconnect, magic_pkt, gtk_rekey_failure, eap_identity_req, four_way_handshake, - rfkill_release, packet_80211; + rfkill_release, packet_80211, + tcp_match, tcp_connlost, tcp_nomoretokens; s32 pattern_idx; u32 packet_present_len, packet_len; const void *packet; @@ -2285,6 +2328,14 @@ enum wiphy_wowlan_support_flags { WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7), }; +struct wiphy_wowlan_tcp_support { + const struct nl80211_wowlan_tcp_data_token_feature *tok; + u32 data_payload_max; + u32 data_interval_max; + u32 wake_payload_max; + bool seq; +}; + /** * struct wiphy_wowlan_support - WoWLAN support data * @flags: see &enum wiphy_wowlan_support_flags @@ -2293,6 +2344,7 @@ enum wiphy_wowlan_support_flags { * @pattern_max_len: maximum length of each pattern * @pattern_min_len: minimum length of each pattern * @max_pkt_offset: maximum Rx packet offset + * @tcp: TCP wakeup support information */ struct wiphy_wowlan_support { u32 flags; @@ -2300,6 +2352,7 @@ struct wiphy_wowlan_support { int pattern_max_len; int pattern_min_len; int max_pkt_offset; + const struct wiphy_wowlan_tcp_support *tcp; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index eb7b32247ec5..5309b34930ea 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2991,6 +2991,17 @@ struct nl80211_wowlan_pattern_support { * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section + * "TCP connection wakeup" for more details. This is a nested attribute + * containing the exact information for establishing and keeping alive + * the TCP connection. + * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * wakeup packet was received on the TCP connection + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the + * TCP connection was lost or failed to be established + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, + * the TCP connection ran out of tokens to use for data to send to the + * service * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * @@ -3012,12 +3023,126 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, + NL80211_WOWLAN_TRIG_TCP_CONNECTION, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, /* keep last */ NUM_NL80211_WOWLAN_TRIG, MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 }; +/** + * DOC: TCP connection wakeup + * + * Some devices can establish a TCP connection in order to be woken up by a + * packet coming in from outside their network segment, or behind NAT. If + * configured, the device will establish a TCP connection to the given + * service, and periodically send data to that service. The first data + * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. + * The data packets can optionally include a (little endian) sequence + * number (in the TCP payload!) that is generated by the device, and, also + * optionally, a token from a list of tokens. This serves as a keep-alive + * with the service, and for NATed connections, etc. + * + * During this keep-alive period, the server doesn't send any data to the + * client. When receiving data, it is compared against the wakeup pattern + * (and mask) and if it matches, the host is woken up. Similarly, if the + * connection breaks or cannot be established to start with, the host is + * also woken up. + * + * Developer's note: ARP offload is required for this, otherwise TCP + * response packets might not go through correctly. + */ + +/** + * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence + * @start: starting value + * @offset: offset of sequence number in packet + * @len: length of the sequence value to write, 1 through 4 + * + * Note: don't confuse with the TCP sequence number(s), this is for the + * keepalive packet payload. The actual value is written into the packet + * in little endian. + */ +struct nl80211_wowlan_tcp_data_seq { + __u32 start, offset, len; +}; + +/** + * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config + * @offset: offset of token in packet + * @len: length of each token + * @token_stream: stream of data to be used for the tokens, the length must + * be a multiple of @len for this to make sense + */ +struct nl80211_wowlan_tcp_data_token { + __u32 offset, len; + __u8 token_stream[]; +}; + +/** + * struct nl80211_wowlan_tcp_data_token_feature - data token features + * @min_len: minimum token length + * @max_len: maximum token length + * @bufsize: total available token buffer size (max size of @token_stream) + */ +struct nl80211_wowlan_tcp_data_token_feature { + __u32 min_len, max_len, bufsize; +}; + +/** + * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters + * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) + * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address + * (in network byte order) + * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because + * route lookup when configured might be invalid by the time we suspend, + * and doing a route lookup when suspending is no longer possible as it + * might require ARP querying. + * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a + * socket and port will be allocated + * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. + * For feature advertising, a u32 attribute holding the maximum length + * of the data payload. + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration + * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature + * advertising it is just a flag + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, + * see &struct nl80211_wowlan_tcp_data_token and for advertising see + * &struct nl80211_wowlan_tcp_data_token_feature. + * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum + * interval in feature advertising (u32) + * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a + * u32 attribute holding the maximum length + * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for + * feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK + * but on the TCP payload only. + * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes + * @MAX_NL80211_WOWLAN_TCP: highest attribute number + */ +enum nl80211_wowlan_tcp_attrs { + __NL80211_WOWLAN_TCP_INVALID, + NL80211_WOWLAN_TCP_SRC_IPV4, + NL80211_WOWLAN_TCP_DST_IPV4, + NL80211_WOWLAN_TCP_DST_MAC, + NL80211_WOWLAN_TCP_SRC_PORT, + NL80211_WOWLAN_TCP_DST_PORT, + NL80211_WOWLAN_TCP_DATA_PAYLOAD, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + NL80211_WOWLAN_TCP_DATA_INTERVAL, + NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + NL80211_WOWLAN_TCP_WAKE_MASK, + + /* keep last */ + NUM_NL80211_WOWLAN_TCP, + MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +}; + /** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) diff --git a/net/wireless/core.h b/net/wireless/core.h index 37d70dc2fe82..949c9573d8d7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -108,6 +108,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) for (i = 0; i < rdev->wowlan->n_patterns; i++) kfree(rdev->wowlan->patterns[i].mask); kfree(rdev->wowlan->patterns); + if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock) + sock_release(rdev->wowlan->tcp->sock); + kfree(rdev->wowlan->tcp); kfree(rdev->wowlan); } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cc0fad30b8c9..d29a461b4981 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "core.h" #include "nl80211.h" #include "reg.h" @@ -399,6 +400,26 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, + [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy +nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { + [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 }, + [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 }, + [NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN }, + [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 }, + [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 }, + [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 }, + [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = { + .len = sizeof(struct nl80211_wowlan_tcp_data_seq) + }, + [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = { + .len = sizeof(struct nl80211_wowlan_tcp_data_token) + }, + [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 }, + [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 }, + [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, }; /* policy for GTK rekey offload attributes */ @@ -872,6 +893,48 @@ nla_put_failure: return -ENOBUFS; } +#ifdef CONFIG_PM +static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev, + struct sk_buff *msg) +{ + const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp; + struct nlattr *nl_tcp; + + if (!tcp) + return 0; + + nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); + if (!nl_tcp) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, + tcp->data_payload_max)) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, + tcp->data_payload_max)) + return -ENOBUFS; + + if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ)) + return -ENOBUFS; + + if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + sizeof(*tcp->tok), tcp->tok)) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, + tcp->data_interval_max)) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + tcp->wake_payload_max)) + return -ENOBUFS; + + nla_nest_end(msg, nl_tcp); + return 0; +} +#endif + static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *dev) { @@ -1246,6 +1309,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; } + if (nl80211_send_wowlan_tcp_caps(dev, msg)) + goto nla_put_failure; + nla_nest_end(msg, nl_wowlan); } #endif @@ -6930,16 +6996,67 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg, return 0; } +static int nl80211_send_wowlan_tcp(struct sk_buff *msg, + struct cfg80211_wowlan_tcp *tcp) +{ + struct nlattr *nl_tcp; + + if (!tcp) + return 0; + + nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); + if (!nl_tcp) + return -ENOBUFS; + + if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) || + nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) || + nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) || + nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) || + nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) || + nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, + tcp->payload_len, tcp->payload) || + nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, + tcp->data_interval) || + nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + tcp->wake_len, tcp->wake_data) || + nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK, + DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask)) + return -ENOBUFS; + + if (tcp->payload_seq.len && + nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + sizeof(tcp->payload_seq), &tcp->payload_seq)) + return -ENOBUFS; + + if (tcp->payload_tok.len && + nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + sizeof(tcp->payload_tok) + tcp->tokens_size, + &tcp->payload_tok)) + return -ENOBUFS; + + return 0; +} + static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct sk_buff *msg; void *hdr; + u32 size = NLMSG_DEFAULT_SIZE; - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && + !rdev->wiphy.wowlan.tcp) return -EOPNOTSUPP; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (rdev->wowlan && rdev->wowlan->tcp) { + /* adjust size to have room for all the data */ + size += rdev->wowlan->tcp->tokens_size + + rdev->wowlan->tcp->payload_len + + rdev->wowlan->tcp->wake_len + + rdev->wowlan->tcp->wake_len / 8; + } + + msg = nlmsg_new(size, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -6970,8 +7087,13 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) (rdev->wowlan->rfkill_release && nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) goto nla_put_failure; + if (nl80211_send_wowlan_patterns(msg, rdev)) goto nla_put_failure; + + if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp)) + goto nla_put_failure; + nla_nest_end(msg, nl_wowlan); } @@ -6983,6 +7105,150 @@ nla_put_failure: return -ENOBUFS; } +static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, + struct nlattr *attr, + struct cfg80211_wowlan *trig) +{ + struct nlattr *tb[NUM_NL80211_WOWLAN_TCP]; + struct cfg80211_wowlan_tcp *cfg; + struct nl80211_wowlan_tcp_data_token *tok = NULL; + struct nl80211_wowlan_tcp_data_seq *seq = NULL; + u32 size; + u32 data_size, wake_size, tokens_size = 0, wake_mask_size; + int err, port; + + if (!rdev->wiphy.wowlan.tcp) + return -EINVAL; + + err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP, + nla_data(attr), nla_len(attr), + nl80211_wowlan_tcp_policy); + if (err) + return err; + + if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] || + !tb[NL80211_WOWLAN_TCP_DST_IPV4] || + !tb[NL80211_WOWLAN_TCP_DST_MAC] || + !tb[NL80211_WOWLAN_TCP_DST_PORT] || + !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] || + !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] || + !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] || + !tb[NL80211_WOWLAN_TCP_WAKE_MASK]) + return -EINVAL; + + data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]); + if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max) + return -EINVAL; + + if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) > + rdev->wiphy.wowlan.tcp->data_interval_max) + return -EINVAL; + + wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]); + if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max) + return -EINVAL; + + wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]); + if (wake_mask_size != DIV_ROUND_UP(wake_size, 8)) + return -EINVAL; + + if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) { + u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); + + tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); + tokens_size = tokln - sizeof(*tok); + + if (!tok->len || tokens_size % tok->len) + return -EINVAL; + if (!rdev->wiphy.wowlan.tcp->tok) + return -EINVAL; + if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len) + return -EINVAL; + if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len) + return -EINVAL; + if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize) + return -EINVAL; + if (tok->offset + tok->len > data_size) + return -EINVAL; + } + + if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) { + seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]); + if (!rdev->wiphy.wowlan.tcp->seq) + return -EINVAL; + if (seq->len == 0 || seq->len > 4) + return -EINVAL; + if (seq->len + seq->offset > data_size) + return -EINVAL; + } + + size = sizeof(*cfg); + size += data_size; + size += wake_size + wake_mask_size; + size += tokens_size; + + cfg = kzalloc(size, GFP_KERNEL); + if (!cfg) + return -ENOMEM; + cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]); + cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]); + memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]), + ETH_ALEN); + if (tb[NL80211_WOWLAN_TCP_SRC_PORT]) + port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]); + else + port = 0; +#ifdef CONFIG_INET + /* allocate a socket and port for it and use it */ + err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM, + IPPROTO_TCP, &cfg->sock, 1); + if (err) { + kfree(cfg); + return err; + } + if (inet_csk_get_port(cfg->sock->sk, port)) { + sock_release(cfg->sock); + kfree(cfg); + return -EADDRINUSE; + } + cfg->src_port = inet_sk(cfg->sock->sk)->inet_num; +#else + if (!port) { + kfree(cfg); + return -EINVAL; + } + cfg->src_port = port; +#endif + + cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]); + cfg->payload_len = data_size; + cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size; + memcpy((void *)cfg->payload, + nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]), + data_size); + if (seq) + cfg->payload_seq = *seq; + cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]); + cfg->wake_len = wake_size; + cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size; + memcpy((void *)cfg->wake_data, + nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]), + wake_size); + cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size + + data_size + wake_size; + memcpy((void *)cfg->wake_mask, + nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]), + wake_mask_size); + if (tok) { + cfg->tokens_size = tokens_size; + memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size); + } + + trig->tcp = cfg; + + return 0; +} + static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6993,7 +7259,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) int err, i; bool prev_enabled = rdev->wowlan; - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) + if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && + !rdev->wiphy.wowlan.tcp) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { @@ -7120,6 +7387,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } } + if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { + err = nl80211_parse_wowlan_tcp( + rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], + &new_triggers); + if (err) + goto error; + } + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); if (!ntrig) { err = -ENOMEM; @@ -7137,6 +7412,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) for (i = 0; i < new_triggers.n_patterns; i++) kfree(new_triggers.patterns[i].mask); kfree(new_triggers.patterns); + if (new_triggers.tcp && new_triggers.tcp->sock) + sock_release(new_triggers.tcp->sock); + kfree(new_triggers.tcp); return err; } #endif @@ -9418,6 +9696,17 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, wakeup->pattern_idx)) goto free_msg; + if (wakeup->tcp_match) + nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH); + + if (wakeup->tcp_connlost) + nla_put_flag(msg, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST); + + if (wakeup->tcp_nomoretokens) + nla_put_flag(msg, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS); + if (wakeup->packet) { u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; -- cgit v1.2.3 From 99d5851eefe589346c976a4b539ee498267bc5fd Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 13 Feb 2013 11:20:27 +0000 Subject: net: skbuff: fix compile error in skb_panic() I get the following build error on next-20130213 due to the following commit: commit f05de73bf82fbbc00265c06d12efb7273f7dc54a ("skbuff: create skb_panic() function and its wrappers"). It adds an argument called panic to a function that uses the BUG() macro which tries to call panic, but the argument masks the panic() function declaration, resulting in the following error (gcc 4.2.4): net/core/skbuff.c In function 'skb_panic': net/core/skbuff.c +126 : error: called object 'panic' is not a function This is fixed by renaming the argument to msg. Signed-off-by: James Hogan Cc: Jean Sacren Cc: Jiri Pirko Cc: David S. Miller Signed-off-by: David S. Miller --- net/core/skbuff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 8731c39b7a54..21a22cce6e53 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -109,7 +109,7 @@ static const struct pipe_buf_operations sock_pipe_buf_ops = { * @skb: buffer * @sz: size * @addr: address - * @panic: skb_over_panic or skb_under_panic + * @msg: skb_over_panic or skb_under_panic * * Out-of-line support for skb_put() and skb_push(). * Called via the wrapper skb_over_panic() or skb_under_panic(). @@ -117,10 +117,10 @@ static const struct pipe_buf_operations sock_pipe_buf_ops = { * __builtin_return_address is not used because it is not always reliable. */ static void skb_panic(struct sk_buff *skb, unsigned int sz, void *addr, - const char panic[]) + const char msg[]) { pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n", - panic, addr, skb->len, sz, skb->head, skb->data, + msg, addr, skb->len, sz, skb->head, skb->data, (unsigned long)skb->tail, (unsigned long)skb->end, skb->dev ? skb->dev->name : ""); BUG(); -- cgit v1.2.3 From 959d5fdee7aa32fa04bc2c37e3d3871ad266fe97 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 13 Feb 2013 11:32:42 -0500 Subject: netpoll: fix smatch warnings in netpoll core code Dan Carpenter contacted me with some notes regarding some smatch warnings in the netpoll code, some of which I introduced with my recent netpoll locking fixes, some which were there prior. Specifically they were: net-next/net/core/netpoll.c:243 netpoll_poll_dev() warn: inconsistent returns mutex:&ni->dev_lock: locked (213,217) unlocked (210,243) net-next/net/core/netpoll.c:706 netpoll_neigh_reply() warn: potential pointer math issue ('skb_transport_header(send_skb)' is a 128 bit pointer) This patch corrects the locking imbalance (the first error), and adds some parenthesis to correct the second error. Tested by myself. Applies to net-next Signed-off-by: Neil Horman CC: Dan Carpenter CC: "David S. Miller" Signed-off-by: David S. Miller --- net/core/netpoll.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/netpoll.c b/net/core/netpoll.c index bcfd4f4599a5..fa32899006a2 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -209,12 +209,16 @@ static void netpoll_poll_dev(struct net_device *dev) if (!mutex_trylock(&ni->dev_lock)) return; - if (!dev || !netif_running(dev)) + if (!netif_running(dev)) { + mutex_unlock(&ni->dev_lock); return; + } ops = dev->netdev_ops; - if (!ops->ndo_poll_controller) + if (!ops->ndo_poll_controller) { + mutex_unlock(&ni->dev_lock); return; + } /* Process pending work on NIC */ ops->ndo_poll_controller(dev); @@ -703,7 +707,7 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo icmp6h->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; icmp6h->icmp6_router = 0; icmp6h->icmp6_solicited = 1; - target = (struct in6_addr *)skb_transport_header(send_skb) + sizeof(struct icmp6hdr); + target = (struct in6_addr *)(skb_transport_header(send_skb) + sizeof(struct icmp6hdr)); *target = msg->target; icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, size, IPPROTO_ICMPV6, -- cgit v1.2.3 From ceaa1fef65a7c2e017b260b879b310dd24888083 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 11 Feb 2013 05:50:17 +0000 Subject: tcp: adding a per-socket timestamp offset This functionality is used for restoring tcp sockets. A tcp timestamp depends on how long a system has been running, so it's differ for each host. The solution is to set a per-socket offset. A per-socket offset for a TIME_WAIT socket is inherited from a proper tcp socket. tcp_request_sock doesn't have a timestamp offset, because the repair mode for them are not implemented. Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Cc: Eric Dumazet Cc: Pavel Emelyanov Signed-off-by: Andrey Vagin Signed-off-by: David S. Miller --- include/linux/tcp.h | 3 +++ net/ipv4/tcp.c | 2 ++ net/ipv4/tcp_minisocks.c | 2 ++ 3 files changed, 7 insertions(+) (limited to 'net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 6d0d46138ae8..f28408c07dc2 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -162,6 +162,8 @@ struct tcp_sock { u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ + u32 tsoffset; /* timestamp offset */ + struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ unsigned long tsq_flags; @@ -353,6 +355,7 @@ struct tcp_timewait_sock { u32 tw_rcv_nxt; u32 tw_snd_nxt; u32 tw_rcv_wnd; + u32 tw_ts_offset; u32 tw_ts_recent; long tw_ts_recent_stamp; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2c7e5963c2ea..8a90bda96038 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -400,6 +400,8 @@ void tcp_init_sock(struct sock *sk) tcp_enable_early_retrans(tp); icsk->icsk_ca_ops = &tcp_init_congestion_ops; + tp->tsoffset = 0; + sk->sk_state = TCP_CLOSE; sk->sk_write_space = sk_stream_write_space; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f0409287b5f4..4dfc99f54f67 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -288,6 +288,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) tcptw->tw_rcv_wnd = tcp_receive_window(tp); tcptw->tw_ts_recent = tp->rx_opt.ts_recent; tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp; + tcptw->tw_ts_offset = tp->tsoffset; #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == PF_INET6) { @@ -499,6 +500,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rx_opt.ts_recent_stamp = 0; newtp->tcp_header_len = sizeof(struct tcphdr); } + newtp->tsoffset = 0; #ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info = NULL; /*XXX*/ if (newtp->af_specific->md5_lookup(sk, newsk)) -- cgit v1.2.3 From 93be6ce0e91b6a94783e012b1857a347a5e6e9f2 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 11 Feb 2013 05:50:18 +0000 Subject: tcp: set and get per-socket timestamp A timestamp can be set, only if a socket is in the repair mode. This patch adds a new socket option TCP_TIMESTAMP, which allows to get and set current tcp times stamp. Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Cc: Eric Dumazet Cc: Pavel Emelyanov Signed-off-by: Andrey Vagin Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index e962faa5ab0d..6b1ead0b0c9d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -111,6 +111,7 @@ enum { #define TCP_QUEUE_SEQ 21 #define TCP_REPAIR_OPTIONS 22 #define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ +#define TCP_TIMESTAMP 24 struct tcp_repair_opt { __u32 opt_code; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8a90bda96038..801b07b796f0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2714,6 +2714,12 @@ static int do_tcp_setsockopt(struct sock *sk, int level, else err = -EINVAL; break; + case TCP_TIMESTAMP: + if (!tp->repair) + err = -EPERM; + else + tp->tsoffset = val - tcp_time_stamp; + break; default: err = -ENOPROTOOPT; break; @@ -2962,6 +2968,9 @@ static int do_tcp_getsockopt(struct sock *sk, int level, case TCP_USER_TIMEOUT: val = jiffies_to_msecs(icsk->icsk_user_timeout); break; + case TCP_TIMESTAMP: + val = tcp_time_stamp + tp->tsoffset; + break; default: return -ENOPROTOOPT; } -- cgit v1.2.3 From ee684b6f2830047d19877e5547989740f18b1a5d Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 11 Feb 2013 05:50:19 +0000 Subject: tcp: send packets with a socket timestamp A socket timestamp is a sum of the global tcp_time_stamp and a per-socket offset. A socket offset is added in places where externally visible tcp timestamp option is parsed/initialized. Connections in the SYN_RECV state are not supported, global tcp_time_stamp is used for them, because repair mode doesn't support this state. In a future it can be implemented by the similar way as for TIME_WAIT sockets. Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Cc: Eric Dumazet Cc: Pavel Emelyanov Signed-off-by: Andrey Vagin Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 8 +++++++- net/ipv4/tcp_ipv4.c | 12 +++++++----- net/ipv4/tcp_minisocks.c | 1 + net/ipv4/tcp_output.c | 4 ++-- net/ipv6/tcp_ipv6.c | 22 +++++++++++++--------- 5 files changed, 30 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ea678b62e94f..d9bfaea34322 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3860,7 +3860,7 @@ static bool tcp_parse_aligned_timestamp(struct tcp_sock *tp, const struct tcphdr ++ptr; tp->rx_opt.rcv_tsval = ntohl(*ptr); ++ptr; - tp->rx_opt.rcv_tsecr = ntohl(*ptr); + tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset; return true; } return false; @@ -3884,7 +3884,11 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb, if (tcp_parse_aligned_timestamp(tp, th)) return true; } + tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL); + if (tp->rx_opt.saw_tstamp) + tp->rx_opt.rcv_tsecr -= tp->tsoffset; + return true; } @@ -5665,6 +5669,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, int saved_clamp = tp->rx_opt.mss_clamp; tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, &foc); + if (tp->rx_opt.saw_tstamp) + tp->rx_opt.rcv_tsecr -= tp->tsoffset; if (th->ack) { /* rfc793: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0eaf685bddc9..77f5050efc8d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -726,7 +726,7 @@ release_sk1: */ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, - u32 win, u32 ts, int oif, + u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, int reply_flags, u8 tos) { @@ -747,12 +747,12 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); - if (ts) { + if (tsecr) { rep.opt[0] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - rep.opt[1] = htonl(tcp_time_stamp); - rep.opt[2] = htonl(ts); + rep.opt[1] = htonl(tsval); + rep.opt[2] = htonl(tsecr); arg.iov[0].iov_len += TCPOLEN_TSTAMP_ALIGNED; } @@ -767,7 +767,7 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, #ifdef CONFIG_TCP_MD5SIG if (key) { - int offset = (ts) ? 3 : 0; + int offset = (tsecr) ? 3 : 0; rep.opt[offset++] = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -802,6 +802,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, + tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), @@ -821,6 +822,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, tcp_rsk(req)->rcv_nxt, req->rcv_wnd, + tcp_time_stamp, req->ts_recent, 0, tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 4dfc99f54f67..b83a49cc3816 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -102,6 +102,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); if (tmp_opt.saw_tstamp) { + tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; tmp_opt.ts_recent = tcptw->tw_ts_recent; tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; paws_reject = tcp_paws_reject(&tmp_opt, th->rst); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 367e2ec01da1..564bf89d9fd3 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -622,7 +622,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, if (likely(sysctl_tcp_timestamps && *md5 == NULL)) { opts->options |= OPTION_TS; - opts->tsval = TCP_SKB_CB(skb)->when; + opts->tsval = TCP_SKB_CB(skb)->when + tp->tsoffset; opts->tsecr = tp->rx_opt.ts_recent; remaining -= TCPOLEN_TSTAMP_ALIGNED; } @@ -806,7 +806,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb if (likely(tp->rx_opt.tstamp_ok)) { opts->options |= OPTION_TS; - opts->tsval = tcb ? tcb->when : 0; + opts->tsval = tcb ? tcb->when + tp->tsoffset : 0; opts->tsecr = tp->rx_opt.ts_recent; size += TCPOLEN_TSTAMP_ALIGNED; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bbb28ae7e5f3..9b6460055df5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -713,7 +713,8 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { #endif static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, - u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass) + u32 tsval, u32 tsecr, + struct tcp_md5sig_key *key, int rst, u8 tclass) { const struct tcphdr *th = tcp_hdr(skb); struct tcphdr *t1; @@ -725,7 +726,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, struct dst_entry *dst; __be32 *topt; - if (ts) + if (tsecr) tot_len += TCPOLEN_TSTAMP_ALIGNED; #ifdef CONFIG_TCP_MD5SIG if (key) @@ -755,11 +756,11 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, topt = (__be32 *)(t1 + 1); - if (ts) { + if (tsecr) { *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP); - *topt++ = htonl(tcp_time_stamp); - *topt++ = htonl(ts); + *topt++ = htonl(tsval); + *topt++ = htonl(tsecr); } #ifdef CONFIG_TCP_MD5SIG @@ -860,7 +861,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2); - tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1, 0); + tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0); #ifdef CONFIG_TCP_MD5SIG release_sk1: @@ -871,10 +872,11 @@ release_sk1: #endif } -static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts, +static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, + u32 win, u32 tsval, u32 tsecr, struct tcp_md5sig_key *key, u8 tclass) { - tcp_v6_send_response(skb, seq, ack, win, ts, key, 0, tclass); + tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass); } static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) @@ -884,6 +886,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, + tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw), tw->tw_tclass); @@ -893,7 +896,8 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { - tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent, + tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, + req->rcv_wnd, tcp_time_stamp, req->ts_recent, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0); } -- cgit v1.2.3 From c9af6db4c11ccc6c3e7f19bbc15d54023956f97c Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Mon, 11 Feb 2013 09:27:41 +0000 Subject: net: Fix possible wrong checksum generation. Patch cef401de7be8c4e (net: fix possible wrong checksum generation) fixed wrong checksum calculation but it broke TSO by defining new GSO type but not a netdev feature for that type. net_gso_ok() would not allow hardware checksum/segmentation offload of such packets without the feature. Following patch fixes TSO and wrong checksum. This patch uses same logic that Eric Dumazet used. Patch introduces new flag SKBTX_SHARED_FRAG if at least one frag can be modified by the user. but SKBTX_SHARED_FRAG flag is kept in skb shared info tx_flags rather than gso_type. tx_flags is better compared to gso_type since we can have skb with shared frag without gso packet. It does not link SHARED_FRAG to GSO, So there is no need to define netdev feature for this. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 4 ++-- drivers/net/tun.c | 13 +++++-------- drivers/net/virtio_net.c | 13 +++++-------- include/linux/skbuff.h | 17 +++++++++-------- net/core/skbuff.c | 5 ++--- net/ipv4/af_inet.c | 1 - net/ipv4/ip_output.c | 1 + net/ipv4/tcp.c | 4 +--- net/ipv4/tcp_input.c | 4 ++-- net/ipv4/tcp_output.c | 4 ++-- net/ipv6/ip6_offload.c | 1 - 11 files changed, 29 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index b181dfb3d6d6..97243011d319 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -543,7 +543,6 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, skb->data_len += len; skb->len += len; skb->truesize += truesize; - skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; atomic_add(truesize, &skb->sk->sk_wmem_alloc); while (len) { int off = base & ~PAGE_MASK; @@ -599,7 +598,7 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { skb_shinfo(skb)->gso_size = vnet_hdr->gso_size; - skb_shinfo(skb)->gso_type |= gso_type; + skb_shinfo(skb)->gso_type = gso_type; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; @@ -743,6 +742,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, if (zerocopy) { skb_shinfo(skb)->destructor_arg = m->msg_control; skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; } if (vlan) macvlan_start_xmit(skb, vlan->dev); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b1038c0e2240..b6f45c5d84d5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1019,7 +1019,6 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, skb->data_len += len; skb->len += len; skb->truesize += truesize; - skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; atomic_add(truesize, &skb->sk->sk_wmem_alloc); while (len) { int off = base & ~PAGE_MASK; @@ -1165,18 +1164,16 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { - unsigned short gso_type = 0; - pr_debug("GSO!\n"); switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: - gso_type = SKB_GSO_TCPV4; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; break; case VIRTIO_NET_HDR_GSO_TCPV6: - gso_type = SKB_GSO_TCPV6; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; break; case VIRTIO_NET_HDR_GSO_UDP: - gso_type = SKB_GSO_UDP; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; break; default: tun->dev->stats.rx_frame_errors++; @@ -1185,10 +1182,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) - gso_type |= SKB_GSO_TCP_ECN; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; skb_shinfo(skb)->gso_size = gso.gso_size; - skb_shinfo(skb)->gso_type |= gso_type; if (skb_shinfo(skb)->gso_size == 0) { tun->dev->stats.rx_frame_errors++; kfree_skb(skb); @@ -1204,6 +1200,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (zerocopy) { skb_shinfo(skb)->destructor_arg = msg_control; skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; } skb_reset_network_header(skb); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 381a2d8d8a81..192c91c8e799 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -227,7 +227,7 @@ static void set_skb_frag(struct sk_buff *skb, struct page *page, skb->len += size; skb->truesize += PAGE_SIZE; skb_shinfo(skb)->nr_frags++; - skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; *len -= size; } @@ -387,18 +387,16 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) ntohs(skb->protocol), skb->len, skb->pkt_type); if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { - unsigned short gso_type = 0; - pr_debug("GSO!\n"); switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_TCPV4: - gso_type = SKB_GSO_TCPV4; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; break; case VIRTIO_NET_HDR_GSO_UDP: - gso_type = SKB_GSO_UDP; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; break; case VIRTIO_NET_HDR_GSO_TCPV6: - gso_type = SKB_GSO_TCPV6; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; break; default: net_warn_ratelimited("%s: bad gso type %u.\n", @@ -407,7 +405,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) } if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN) - gso_type |= SKB_GSO_TCP_ECN; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; skb_shinfo(skb)->gso_size = hdr->hdr.gso_size; if (skb_shinfo(skb)->gso_size == 0) { @@ -415,7 +413,6 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) goto frame_err; } - skb_shinfo(skb)->gso_type |= gso_type; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; skb_shinfo(skb)->gso_segs = 0; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d7573c37a51d..9da99520ccd5 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -230,6 +230,13 @@ enum { /* generate wifi status information (where possible) */ SKBTX_WIFI_STATUS = 1 << 4, + + /* This indicates at least one fragment might be overwritten + * (as in vmsplice(), sendfile() ...) + * If we need to compute a TX checksum, we'll need to copy + * all frags to avoid possible bad checksum + */ + SKBTX_SHARED_FRAG = 1 << 5, }; /* @@ -307,13 +314,6 @@ enum { SKB_GSO_TCPV6 = 1 << 4, SKB_GSO_FCOE = 1 << 5, - - /* This indicates at least one fragment might be overwritten - * (as in vmsplice(), sendfile() ...) - * If we need to compute a TX checksum, we'll need to copy - * all frags to avoid possible bad checksum - */ - SKB_GSO_SHARED_FRAG = 1 << 6, }; #if BITS_PER_LONG > 32 @@ -2220,7 +2220,8 @@ static inline int skb_linearize(struct sk_buff *skb) */ static inline bool skb_has_shared_frag(const struct sk_buff *skb) { - return skb_shinfo(skb)->gso_type & SKB_GSO_SHARED_FRAG; + return skb_is_nonlinear(skb) && + skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; } /** diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 21a22cce6e53..6c1ad09f8796 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2326,8 +2326,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) { int pos = skb_headlen(skb); - skb_shinfo(skb1)->gso_type = skb_shinfo(skb)->gso_type; - + skb_shinfo(skb)->tx_flags = skb_shinfo(skb1)->tx_flags & SKBTX_SHARED_FRAG; if (len < pos) /* Split line is inside header. */ skb_split_inside_header(skb, skb1, len, pos); else /* Second chunk has no header, nothing to copy. */ @@ -2833,7 +2832,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) skb_copy_from_linear_data_offset(skb, offset, skb_put(nskb, hsize), hsize); - skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type; + skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; while (pos < offset + len && i < nfrags) { *frag = skb_shinfo(skb)->frags[i]; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 1aec92bf8018..e6e5d8506336 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1287,7 +1287,6 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | - SKB_GSO_SHARED_FRAG | 0))) goto out; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 3e98ed2bff55..5e12dca7b3dd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -598,6 +598,7 @@ slow_path: /* for offloaded checksums cleanup checksum before fragmentation */ if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb)) goto fail; + iph = ip_hdr(skb); left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 801b07b796f0..1f0bedb8622f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -897,8 +897,7 @@ new_segment: get_page(page); skb_fill_page_desc(skb, i, page, offset, copy); } - - skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG; + skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; skb->len += copy; skb->data_len += copy; @@ -3044,7 +3043,6 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | - SKB_GSO_SHARED_FRAG | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) goto out; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d9bfaea34322..a759e19496d2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1239,13 +1239,13 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, */ if (!skb_shinfo(prev)->gso_size) { skb_shinfo(prev)->gso_size = mss; - skb_shinfo(prev)->gso_type |= sk->sk_gso_type; + skb_shinfo(prev)->gso_type = sk->sk_gso_type; } /* CHECKME: To clear or not to clear? Mimics normal skb currently */ if (skb_shinfo(skb)->gso_segs <= 1) { skb_shinfo(skb)->gso_size = 0; - skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG; + skb_shinfo(skb)->gso_type = 0; } /* Difference in this won't matter, both ACKed by the same cumul. ACK */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 564bf89d9fd3..6182d90e97b0 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1133,7 +1133,6 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, unsigned int mss_now) { - skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG; if (skb->len <= mss_now || !sk_can_gso(sk) || skb->ip_summed == CHECKSUM_NONE) { /* Avoid the costly divide in the normal @@ -1141,10 +1140,11 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, */ skb_shinfo(skb)->gso_segs = 1; skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; } else { skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); skb_shinfo(skb)->gso_size = mss_now; - skb_shinfo(skb)->gso_type |= sk->sk_gso_type; + skb_shinfo(skb)->gso_type = sk->sk_gso_type; } } diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index d141fc32a2ea..f26f0da7f095 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -100,7 +100,6 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | - SKB_GSO_SHARED_FRAG | 0))) goto out; -- cgit v1.2.3 From 1e558174637689a53a24b4c94f6f1aed38361b3e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 13 Feb 2013 01:03:49 +0000 Subject: net: sctp: minor: make jsctp_sf_eat_sack static The function jsctp_sf_eat_sack can be made static, no need to extend its visibility. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/sctp/probe.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 5f7518de2fd1..4eaaaf68e5b7 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -122,12 +122,12 @@ static const struct file_operations sctpprobe_fops = { .llseek = noop_llseek, }; -sctp_disposition_t jsctp_sf_eat_sack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - void *arg, - sctp_cmd_seq_t *commands) +static sctp_disposition_t jsctp_sf_eat_sack(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) { struct sctp_transport *sp; static __u32 lcwnd = 0; -- cgit v1.2.3 From 222229974824a4f30b417531cdc9b5b869d6a6b7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 13 Feb 2013 01:03:50 +0000 Subject: net: sctp: add build check for sctp_sf_eat_sack_6_2/jsctp_sf_eat_sack In order to avoid any future surprises of kernel panics due to jprobes function mismatches (as e.g. fixed in 4cb9d6eaf85ecd: sctp: jsctp_sf_eat_sack: fix jprobes function signature mismatch), we should check both function types during build and scream loudly if they do not match. __same_type resolves to __builtin_types_compatible_p, which is 1 in case both types are the same and 0 otherwise, qualifiers are ignored. Tested by myself. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/sctp/probe.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 4eaaaf68e5b7..261b7b9858a4 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -183,6 +183,13 @@ static __init int sctpprobe_init(void) { int ret = -ENOMEM; + /* Warning: if the function signature of sctp_sf_eat_sack_6_2, + * has been changed, you also have to change the signature of + * jsctp_sf_eat_sack, otherwise you end up right here! + */ + BUILD_BUG_ON(__same_type(sctp_sf_eat_sack_6_2, + jsctp_sf_eat_sack) == 0); + init_waitqueue_head(&sctpw.wait); spin_lock_init(&sctpw.lock); if (kfifo_alloc(&sctpw.fifo, bufsize, GFP_KERNEL)) -- cgit v1.2.3 From 894e2ac82bd0029adce7ad6c8d25501fdd82c994 Mon Sep 17 00:00:00 2001 From: Michal Kubeček Date: Tue, 12 Feb 2013 23:46:09 +0000 Subject: netfilter: nf_ct_reasm: fix per-netns sysctl initialization Adjusting of data pointers in net/netfilter/nf_conntrack_frag6_* sysctl table for other namespaces points to wrong netns_frags structure and has reversed order of entries. Problem introduced by commit c038a767cd69 in 3.7-rc1 Signed-off-by: Michal Kubecek Signed-off-by: Pablo Neira Ayuso --- net/ipv6/netfilter/nf_conntrack_reasm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3dacecc99065..0156d07d732f 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -97,9 +97,9 @@ static int nf_ct_frag6_sysctl_register(struct net *net) if (table == NULL) goto err_alloc; - table[0].data = &net->ipv6.frags.high_thresh; - table[1].data = &net->ipv6.frags.low_thresh; - table[2].data = &net->ipv6.frags.timeout; + table[0].data = &net->nf_frag.frags.timeout; + table[1].data = &net->nf_frag.frags.low_thresh; + table[2].data = &net->nf_frag.frags.high_thresh; } hdr = register_net_sysctl(net, "net/netfilter", table); -- cgit v1.2.3 From 243a2e63f5f47763b802e9dee8dbf1611a1c1322 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:09 +0000 Subject: bridge: Add vlan filtering infrastructure Adds an optional infrustructure component to bridge that would allow native vlan filtering in the bridge. Each bridge port (as well as the bridge device) now get a VLAN bitmap. Each bit in the bitmap is associated with a vlan id. This way if the bit corresponding to the vid is set in the bitmap that the packet with vid is allowed to enter and exit the port. Write access the bitmap is protected by RTNL and read access protected by RCU. Vlan functionality is disabled by default. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/Kconfig | 14 ++++ net/bridge/Makefile | 2 + net/bridge/br_if.c | 1 + net/bridge/br_private.h | 59 ++++++++++++++ net/bridge/br_sysfs_br.c | 21 +++++ net/bridge/br_vlan.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 296 insertions(+) create mode 100644 net/bridge/br_vlan.c (limited to 'net') diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index 6dee7bf648a9..aa0d3b2f1bb7 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -46,3 +46,17 @@ config BRIDGE_IGMP_SNOOPING Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config BRIDGE_VLAN_FILTERING + bool "VLAN filtering" + depends on BRIDGE + depends on VLAN_8021Q + default n + ---help--- + If you say Y here, then the Ethernet bridge will be able selectively + receive and forward traffic based on VLAN information in the packet + any VLAN information configured on the bridge port or bridge device. + + Say N to exclude this support and reduce the binary size. + + If unsure, say Y. diff --git a/net/bridge/Makefile b/net/bridge/Makefile index e859098f5ee9..e85498b2f166 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -14,4 +14,6 @@ bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o +bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o + obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 2148d474a04f..af9d65ab4001 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -139,6 +139,7 @@ static void del_nbp(struct net_bridge_port *p) br_ifinfo_notify(RTM_DELLINK, p); + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); list_del_rcu(&p->list); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 06e85d9c05aa..1f3b309beea8 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -18,6 +18,7 @@ #include #include #include +#include #define BR_HASH_BITS 8 #define BR_HASH_SIZE (1 << BR_HASH_BITS) @@ -26,6 +27,7 @@ #define BR_PORT_BITS 10 #define BR_MAX_PORTS (1<priv_flags & IFF_BRIDGE_PORT) @@ -257,6 +272,10 @@ struct net_bridge struct timer_list topology_change_timer; struct timer_list gc_timer; struct kobject *ifobj; +#ifdef CONFIG_BRIDGE_VLAN_FILTERING + u8 vlan_enabled; + struct net_port_vlans __rcu *vlan_info; +#endif }; struct br_input_skb_cb { @@ -531,6 +550,46 @@ static inline void br_mdb_uninit(void) } #endif +/* br_vlan.c */ +#ifdef CONFIG_BRIDGE_VLAN_FILTERING +extern int br_vlan_add(struct net_bridge *br, u16 vid); +extern int br_vlan_delete(struct net_bridge *br, u16 vid); +extern void br_vlan_flush(struct net_bridge *br); +extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); +extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); +extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); +extern void nbp_vlan_flush(struct net_bridge_port *port); +#else +static inline int br_vlan_add(struct net_bridge *br, u16 vid) +{ + return -EOPNOTSUPP; +} + +static inline int br_vlan_delete(struct net_bridge *br, u16 vid) +{ + return -EOPNOTSUPP; +} + +static inline void br_vlan_flush(struct net_bridge *br) +{ +} + +static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +{ + return -EOPNOTSUPP; +} + +static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) +{ + return -EOPNOTSUPP; +} + +static inline void nbp_vlan_flush(struct net_bridge_port *port) +{ +} + +#endif + /* br_netfilter.c */ #ifdef CONFIG_BRIDGE_NETFILTER extern int br_netfilter_init(void); diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 5913a3a0047b..8baa9c08e1a4 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -691,6 +691,24 @@ static ssize_t store_nf_call_arptables( static DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, show_nf_call_arptables, store_nf_call_arptables); #endif +#ifdef CONFIG_BRIDGE_VLAN_FILTERING +static ssize_t show_vlan_filtering(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%d\n", br->vlan_enabled); +} + +static ssize_t store_vlan_filtering(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, br_vlan_filter_toggle); +} +static DEVICE_ATTR(vlan_filtering, S_IRUGO | S_IWUSR, + show_vlan_filtering, store_vlan_filtering); +#endif static struct attribute *bridge_attrs[] = { &dev_attr_forward_delay.attr, @@ -731,6 +749,9 @@ static struct attribute *bridge_attrs[] = { &dev_attr_nf_call_iptables.attr, &dev_attr_nf_call_ip6tables.attr, &dev_attr_nf_call_arptables.attr, +#endif +#ifdef CONFIG_BRIDGE_VLAN_FILTERING + &dev_attr_vlan_filtering.attr, #endif NULL }; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c new file mode 100644 index 000000000000..209464ef5242 --- /dev/null +++ b/net/bridge/br_vlan.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include + +#include "br_private.h" + +static int __vlan_add(struct net_port_vlans *v, u16 vid) +{ + int err; + + if (test_bit(vid, v->vlan_bitmap)) + return -EEXIST; + + if (v->port_idx && vid) { + struct net_device *dev = v->parent.port->dev; + + /* Add VLAN to the device filter if it is supported. + * Stricly speaking, this is not necessary now, since devices + * are made promiscuous by the bridge, but if that ever changes + * this code will allow tagged traffic to enter the bridge. + */ + if (dev->features & NETIF_F_HW_VLAN_FILTER) { + err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); + if (err) + return err; + } + } + + set_bit(vid, v->vlan_bitmap); + return 0; +} + +static int __vlan_del(struct net_port_vlans *v, u16 vid) +{ + if (!test_bit(vid, v->vlan_bitmap)) + return -EINVAL; + + if (v->port_idx && vid) { + struct net_device *dev = v->parent.port->dev; + + if (dev->features & NETIF_F_HW_VLAN_FILTER) + dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid); + } + + clear_bit(vid, v->vlan_bitmap); + if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + if (v->port_idx) + rcu_assign_pointer(v->parent.port->vlan_info, NULL); + else + rcu_assign_pointer(v->parent.br->vlan_info, NULL); + kfree_rcu(v, rcu); + } + return 0; +} + +static void __vlan_flush(struct net_port_vlans *v) +{ + bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); + if (v->port_idx) + rcu_assign_pointer(v->parent.port->vlan_info, NULL); + else + rcu_assign_pointer(v->parent.br->vlan_info, NULL); + kfree_rcu(v, rcu); +} + +/* Must be protected by RTNL */ +int br_vlan_add(struct net_bridge *br, u16 vid) +{ + struct net_port_vlans *pv = NULL; + int err; + + ASSERT_RTNL(); + + pv = rtnl_dereference(br->vlan_info); + if (pv) + return __vlan_add(pv, vid); + + /* Create port vlan infomration + */ + pv = kzalloc(sizeof(*pv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + + pv->parent.br = br; + err = __vlan_add(pv, vid); + if (err) + goto out; + + rcu_assign_pointer(br->vlan_info, pv); + return 0; +out: + kfree(pv); + return err; +} + +/* Must be protected by RTNL */ +int br_vlan_delete(struct net_bridge *br, u16 vid) +{ + struct net_port_vlans *pv; + + ASSERT_RTNL(); + + pv = rtnl_dereference(br->vlan_info); + if (!pv) + return -EINVAL; + + __vlan_del(pv, vid); + return 0; +} + +void br_vlan_flush(struct net_bridge *br) +{ + struct net_port_vlans *pv; + + ASSERT_RTNL(); + + pv = rtnl_dereference(br->vlan_info); + if (!pv) + return; + + __vlan_flush(pv); +} + +int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) +{ + if (!rtnl_trylock()) + return restart_syscall(); + + if (br->vlan_enabled == val) + goto unlock; + + br->vlan_enabled = val; + +unlock: + rtnl_unlock(); + return 0; +} + +/* Must be protected by RTNL */ +int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +{ + struct net_port_vlans *pv = NULL; + int err; + + ASSERT_RTNL(); + + pv = rtnl_dereference(port->vlan_info); + if (pv) + return __vlan_add(pv, vid); + + /* Create port vlan infomration + */ + pv = kzalloc(sizeof(*pv), GFP_KERNEL); + if (!pv) { + err = -ENOMEM; + goto clean_up; + } + + pv->port_idx = port->port_no; + pv->parent.port = port; + err = __vlan_add(pv, vid); + if (err) + goto clean_up; + + rcu_assign_pointer(port->vlan_info, pv); + return 0; + +clean_up: + kfree(pv); + return err; +} + +/* Must be protected by RTNL */ +int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) +{ + struct net_port_vlans *pv; + + ASSERT_RTNL(); + + pv = rtnl_dereference(port->vlan_info); + if (!pv) + return -EINVAL; + + return __vlan_del(pv, vid); +} + +void nbp_vlan_flush(struct net_bridge_port *port) +{ + struct net_port_vlans *pv; + + ASSERT_RTNL(); + + pv = rtnl_dereference(port->vlan_info); + if (!pv) + return; + + __vlan_flush(pv); +} -- cgit v1.2.3 From a37b85c9fbd1dc69fbec3985763f373203eaf9e3 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:10 +0000 Subject: bridge: Validate that vlan is permitted on ingress When a frame arrives on a port or transmitted by the bridge, if we have VLANs configured, validate that a given VLAN is allowed to enter the bridge. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_device.c | 3 +++ net/bridge/br_input.c | 4 ++++ net/bridge/br_private.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_vlan.c | 25 +++++++++++++++++++++++ 4 files changed, 85 insertions(+) (limited to 'net') diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ca98fa5b2c78..35a2c2c84f33 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -45,6 +45,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) brstats->tx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); + if (!br_allowed_ingress(br, br_get_vlan_info(br), skb)) + goto out; + BR_INPUT_SKB_CB(skb)->brdev = dev; skb_reset_mac_header(skb); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 4b34207419b1..4ef3f6b17bd0 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "br_private.h" /* Hook for brouter */ @@ -54,6 +55,9 @@ int br_handle_frame_finish(struct sk_buff *skb) if (!p || p->state == BR_STATE_DISABLED) goto drop; + if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb)) + goto drop; + /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; br_fdb_update(br, p, eth_hdr(skb)->h_source); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1f3b309beea8..ed7c764ee9da 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -552,6 +552,8 @@ static inline void br_mdb_uninit(void) /* br_vlan.c */ #ifdef CONFIG_BRIDGE_VLAN_FILTERING +extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, + struct sk_buff *skb); extern int br_vlan_add(struct net_bridge *br, u16 vid); extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern void br_vlan_flush(struct net_bridge *br); @@ -559,7 +561,43 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); extern void nbp_vlan_flush(struct net_bridge_port *port); + +static inline struct net_port_vlans *br_get_vlan_info( + const struct net_bridge *br) +{ + return rcu_dereference(br->vlan_info); +} + +static inline struct net_port_vlans *nbp_get_vlan_info( + const struct net_bridge_port *p) +{ + return rcu_dereference(p->vlan_info); +} + +/* Since bridge now depends on 8021Q module, but the time bridge sees the + * skb, the vlan tag will always be present if the frame was tagged. + */ +static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) +{ + int err = 0; + + if (vlan_tx_tag_present(skb)) + *vid = vlan_tx_tag_get(skb) & VLAN_VID_MASK; + else { + *vid = 0; + err = -EINVAL; + } + + return err; +} #else +static inline bool br_allowed_ingress(struct net_bridge *br, + struct net_port_vlans *v, + struct sk_buff *skb) +{ + return true; +} + static inline int br_vlan_add(struct net_bridge *br, u16 vid) { return -EOPNOTSUPP; @@ -588,6 +626,21 @@ static inline void nbp_vlan_flush(struct net_bridge_port *port) { } +static inline struct net_port_vlans *br_get_vlan_info( + const struct net_bridge *br) +{ + return NULL; +} +static inline struct net_port_vlans *nbp_get_vlan_info( + const struct net_bridge_port *p) +{ + return NULL; +} + +static inline u16 br_vlan_get_tag(const struct sk_buff *skb) +{ + return 0; +} #endif /* br_netfilter.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 209464ef5242..8b4bcd8ff46e 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -64,6 +64,31 @@ static void __vlan_flush(struct net_port_vlans *v) kfree_rcu(v, rcu); } +/* Called under RCU */ +bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, + struct sk_buff *skb) +{ + u16 vid; + + /* If VLAN filtering is disabled on the bridge, all packets are + * permitted. + */ + if (!br->vlan_enabled) + return true; + + /* If there are no vlan in the permitted list, all packets are + * rejected. + */ + if (!v) + return false; + + br_vlan_get_tag(skb, &vid); + if (test_bit(vid, v->vlan_bitmap)) + return true; + + return false; +} + /* Must be protected by RTNL */ int br_vlan_add(struct net_bridge *br, u16 vid) { -- cgit v1.2.3 From 85f46c6baef1486ce20e13dd7cdea5dd15be2a90 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:11 +0000 Subject: bridge: Verify that a vlan is allowed to egress on given port When bridge forwards a frame, make sure that a frame is allowed to egress on that port. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_forward.c | 1 + net/bridge/br_input.c | 10 ++++++++++ net/bridge/br_private.h | 10 ++++++++++ net/bridge/br_vlan.c | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+) (limited to 'net') diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 02015a505d2a..35b0671f135d 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -31,6 +31,7 @@ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) { return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && + br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) && p->state == BR_STATE_FORWARDING); } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 4ef3f6b17bd0..787d7dad6b7e 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -35,6 +35,16 @@ static int br_pass_frame_up(struct sk_buff *skb) brstats->rx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); + /* Bridge is just like any other port. Make sure the + * packet is allowed except in promisc modue when someone + * may be running packet capture. + */ + if (!(brdev->flags & IFF_PROMISC) && + !br_allowed_egress(br, br_get_vlan_info(br), skb)) { + kfree_skb(skb); + return NET_RX_DROP; + } + indev = skb->dev; skb->dev = brdev; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ed7c764ee9da..f0f24610d111 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -554,6 +554,9 @@ static inline void br_mdb_uninit(void) #ifdef CONFIG_BRIDGE_VLAN_FILTERING extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, struct sk_buff *skb); +extern bool br_allowed_egress(struct net_bridge *br, + const struct net_port_vlans *v, + const struct sk_buff *skb); extern int br_vlan_add(struct net_bridge *br, u16 vid); extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern void br_vlan_flush(struct net_bridge *br); @@ -598,6 +601,13 @@ static inline bool br_allowed_ingress(struct net_bridge *br, return true; } +static inline bool br_allowed_egress(struct net_bridge *br, + const struct net_port_vlans *v, + const struct sk_buff *skb) +{ + return true; +} + static inline int br_vlan_add(struct net_bridge *br, u16 vid) { return -EOPNOTSUPP; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 8b4bcd8ff46e..d8690bfe63d4 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -89,6 +89,26 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, return false; } +/* Called under RCU. */ +bool br_allowed_egress(struct net_bridge *br, + const struct net_port_vlans *v, + const struct sk_buff *skb) +{ + u16 vid; + + if (!br->vlan_enabled) + return true; + + if (!v) + return false; + + br_vlan_get_tag(skb, &vid); + if (test_bit(vid, v->vlan_bitmap)) + return true; + + return false; +} + /* Must be protected by RTNL */ int br_vlan_add(struct net_bridge *br, u16 vid) { -- cgit v1.2.3 From 407af3299ef1ac7e87ce3fb530e32a009d1a9efd Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:12 +0000 Subject: bridge: Add netlink interface to configure vlans on bridge ports Add a netlink interface to add and remove vlan configuration on bridge port. The interface uses the RTM_SETLINK message and encodes the vlan configuration inside the IFLA_AF_SPEC. It is possble to include multiple vlans to either add or remove in a single message. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 + include/uapi/linux/if_bridge.h | 9 +++ net/bridge/br_device.c | 1 + net/bridge/br_if.c | 1 + net/bridge/br_netlink.c | 139 +++++++++++++++++++++++++++++++++++------ net/bridge/br_private.h | 1 + net/core/rtnetlink.c | 72 +++++++++++++++++++++ 7 files changed, 207 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 25bd46f52877..1b90f9401000 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1020,6 +1020,8 @@ struct net_device_ops { int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev); + int (*ndo_bridge_dellink)(struct net_device *dev, + struct nlmsghdr *nlh); int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier); }; diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 5db297514aec..3ca9817ca7e8 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -108,15 +108,24 @@ struct __fdb_entry { * [IFLA_AF_SPEC] = { * [IFLA_BRIDGE_FLAGS] * [IFLA_BRIDGE_MODE] + * [IFLA_BRIDGE_VLAN_INFO] * } */ enum { IFLA_BRIDGE_FLAGS, IFLA_BRIDGE_MODE, + IFLA_BRIDGE_VLAN_INFO, __IFLA_BRIDGE_MAX, }; #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) +#define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ + +struct bridge_vlan_info { + u16 flags; + u16 vid; +}; + /* Bridge multicast database attributes * [MDBA_MDB] = { * [MDBA_MDB_ENTRY] = { diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 35a2c2c84f33..091bedf266a0 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -316,6 +316,7 @@ static const struct net_device_ops br_netdev_ops = { .ndo_fdb_dump = br_fdb_dump, .ndo_bridge_getlink = br_getlink, .ndo_bridge_setlink = br_setlink, + .ndo_bridge_dellink = br_dellink, }; static void br_dev_free(struct net_device *dev) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index af9d65ab4001..335c60cebfd1 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "br_private.h" diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 39ca9796f3f7..534a9f4587a9 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "br_private.h" #include "br_private_stp.h" @@ -119,10 +120,14 @@ nla_put_failure: */ void br_ifinfo_notify(int event, struct net_bridge_port *port) { - struct net *net = dev_net(port->dev); + struct net *net; struct sk_buff *skb; int err = -ENOBUFS; + if (!port) + return; + + net = dev_net(port->dev); br_debug(port->br, "port %u(%s) event %d\n", (unsigned int)port->port_no, port->dev->name, event); @@ -144,6 +149,7 @@ errout: rtnl_set_sk_err(net, RTNLGRP_LINK, err); } + /* * Dump information about all ports, in response to GETLINK */ @@ -162,6 +168,64 @@ out: return err; } +const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { + [IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, + [IFLA_BRIDGE_MODE] = { .type = NLA_U16 }, + [IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY, + .len = sizeof(struct bridge_vlan_info), }, +}; + +static int br_afspec(struct net_bridge *br, + struct net_bridge_port *p, + struct nlattr *af_spec, + int cmd) +{ + struct nlattr *tb[IFLA_BRIDGE_MAX+1]; + int err = 0; + + err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy); + if (err) + return err; + + if (tb[IFLA_BRIDGE_VLAN_INFO]) { + struct bridge_vlan_info *vinfo; + + vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); + + if (vinfo->vid >= VLAN_N_VID) + return -EINVAL; + + switch (cmd) { + case RTM_SETLINK: + if (p) { + err = nbp_vlan_add(p, vinfo->vid); + if (err) + break; + + if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) + err = br_vlan_add(p->br, vinfo->vid); + } else + err = br_vlan_add(br, vinfo->vid); + + if (err) + break; + + break; + + case RTM_DELLINK: + if (p) { + nbp_vlan_delete(p, vinfo->vid); + if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) + br_vlan_delete(p->br, vinfo->vid); + } else + br_vlan_delete(br, vinfo->vid); + break; + } + } + + return err; +} + static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, [IFLA_BRPORT_COST] = { .type = NLA_U32 }, @@ -241,6 +305,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) { struct ifinfomsg *ifm; struct nlattr *protinfo; + struct nlattr *afspec; struct net_bridge_port *p; struct nlattr *tb[IFLA_BRPORT_MAX + 1]; int err; @@ -248,38 +313,76 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) ifm = nlmsg_data(nlh); protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); - if (!protinfo) + afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); + if (!protinfo && !afspec) return 0; p = br_port_get_rtnl(dev); - if (!p) + /* We want to accept dev as bridge itself if the AF_SPEC + * is set to see if someone is setting vlan info on the brigde + */ + if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec)) return -EINVAL; - if (protinfo->nla_type & NLA_F_NESTED) { - err = nla_parse_nested(tb, IFLA_BRPORT_MAX, - protinfo, ifla_brport_policy); + if (p && protinfo) { + if (protinfo->nla_type & NLA_F_NESTED) { + err = nla_parse_nested(tb, IFLA_BRPORT_MAX, + protinfo, ifla_brport_policy); + if (err) + return err; + + spin_lock_bh(&p->br->lock); + err = br_setport(p, tb); + spin_unlock_bh(&p->br->lock); + } else { + /* Binary compatability with old RSTP */ + if (nla_len(protinfo) < sizeof(u8)) + return -EINVAL; + + spin_lock_bh(&p->br->lock); + err = br_set_port_state(p, nla_get_u8(protinfo)); + spin_unlock_bh(&p->br->lock); + } if (err) - return err; - - spin_lock_bh(&p->br->lock); - err = br_setport(p, tb); - spin_unlock_bh(&p->br->lock); - } else { - /* Binary compatability with old RSTP */ - if (nla_len(protinfo) < sizeof(u8)) - return -EINVAL; + goto out; + } - spin_lock_bh(&p->br->lock); - err = br_set_port_state(p, nla_get_u8(protinfo)); - spin_unlock_bh(&p->br->lock); + if (afspec) { + err = br_afspec((struct net_bridge *)netdev_priv(dev), p, + afspec, RTM_SETLINK); } if (err == 0) br_ifinfo_notify(RTM_NEWLINK, p); +out: return err; } +/* Delete port information */ +int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) +{ + struct ifinfomsg *ifm; + struct nlattr *afspec; + struct net_bridge_port *p; + int err; + + ifm = nlmsg_data(nlh); + + afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); + if (!afspec) + return 0; + + p = br_port_get_rtnl(dev); + /* We want to accept dev as bridge itself as well */ + if (!p && !(dev->priv_flags & IFF_EBRIDGE)) + return -EINVAL; + + err = br_afspec((struct net_bridge *)netdev_priv(dev), p, + afspec, RTM_DELLINK); + + return err; +} static int br_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f0f24610d111..a42f9d49a64e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -713,6 +713,7 @@ extern int br_netlink_init(void); extern void br_netlink_fini(void); extern void br_ifinfo_notify(int event, struct net_bridge_port *port); extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); +extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c1e4db60eeca..2c9ccbfbd93c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2464,6 +2464,77 @@ out: return err; } +static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, + void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ifinfomsg *ifm; + struct net_device *dev; + struct nlattr *br_spec, *attr = NULL; + int rem, err = -EOPNOTSUPP; + u16 oflags, flags = 0; + bool have_flags = false; + + if (nlmsg_len(nlh) < sizeof(*ifm)) + return -EINVAL; + + ifm = nlmsg_data(nlh); + if (ifm->ifi_family != AF_BRIDGE) + return -EPFNOSUPPORT; + + dev = __dev_get_by_index(net, ifm->ifi_index); + if (!dev) { + pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n"); + return -ENODEV; + } + + br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (br_spec) { + nla_for_each_nested(attr, br_spec, rem) { + if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + have_flags = true; + flags = nla_get_u16(attr); + break; + } + } + } + + oflags = flags; + + if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + + if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) { + err = -EOPNOTSUPP; + goto out; + } + + err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh); + if (err) + goto out; + + flags &= ~BRIDGE_FLAGS_MASTER; + } + + if ((flags & BRIDGE_FLAGS_SELF)) { + if (!dev->netdev_ops->ndo_bridge_dellink) + err = -EOPNOTSUPP; + else + err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh); + + if (!err) + flags &= ~BRIDGE_FLAGS_SELF; + } + + if (have_flags) + memcpy(nla_data(attr), &flags, sizeof(flags)); + /* Generate event to notify upper layer of bridge change */ + if (!err) + err = rtnl_bridge_notify(dev, oflags); +out: + return err; +} + /* Protected by RTNL sempahore. */ static struct rtattr **rta_buf; static int rtattr_max; @@ -2647,6 +2718,7 @@ void __init rtnetlink_init(void) rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); + rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL); rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); } -- cgit v1.2.3 From 6cbdceeb1cb12c7d620161925a8c3e81daadb2e4 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:13 +0000 Subject: bridge: Dump vlan information from a bridge port Using the RTM_GETLINK dump the vlan filter list of a given bridge port. The information depends on setting the filter flag similar to how nic VF info is dumped. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 +- include/linux/netdevice.h | 3 +- include/uapi/linux/rtnetlink.h | 1 + net/bridge/br_netlink.c | 94 +++++++++++++++++++++++---- net/bridge/br_private.h | 3 +- net/bridge/br_vlan.c | 2 + net/core/rtnetlink.c | 16 +++-- 7 files changed, 104 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 6999269b3a4a..4e2aa47193cb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7079,7 +7079,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, } static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev) + struct net_device *dev, + u32 filter_mask) { struct ixgbe_adapter *adapter = netdev_priv(dev); u16 mode; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1b90f9401000..1964ca66df56 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1019,7 +1019,8 @@ struct net_device_ops { struct nlmsghdr *nlh); int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev); + struct net_device *dev, + u32 filter_mask); int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh); int (*ndo_change_carrier)(struct net_device *dev, diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 7a5eb196ade9..7a2144e1afae 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -630,6 +630,7 @@ struct tcamsg { /* New extended info filters for IFLA_EXT_MASK */ #define RTEXT_FILTER_VF (1 << 0) +#define RTEXT_FILTER_BRVLAN (1 << 1) /* End of information exported to user level */ diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 534a9f4587a9..fe1980d5a7e4 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -65,15 +65,21 @@ static int br_port_fill_attrs(struct sk_buff *skb, * Create one netlink message for one interface * Contains port and master info as well as carrier and bridge state. */ -static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port, - u32 pid, u32 seq, int event, unsigned int flags) +static int br_fill_ifinfo(struct sk_buff *skb, + const struct net_bridge_port *port, + u32 pid, u32 seq, int event, unsigned int flags, + u32 filter_mask, const struct net_device *dev) { - const struct net_bridge *br = port->br; - const struct net_device *dev = port->dev; + const struct net_bridge *br; struct ifinfomsg *hdr; struct nlmsghdr *nlh; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; + if (port) + br = port->br; + else + br = netdev_priv(dev); + br_debug(br, "br_fill_info event %d port %s master %s\n", event, dev->name, br->dev->name); @@ -99,7 +105,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por nla_put_u32(skb, IFLA_LINK, dev->iflink))) goto nla_put_failure; - if (event == RTM_NEWLINK) { + if (event == RTM_NEWLINK && port) { struct nlattr *nest = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); @@ -108,6 +114,40 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por nla_nest_end(skb, nest); } + /* Check if the VID information is requested */ + if (filter_mask & RTEXT_FILTER_BRVLAN) { + struct nlattr *af; + const struct net_port_vlans *pv; + struct bridge_vlan_info vinfo; + u16 vid; + + if (port) + pv = nbp_get_vlan_info(port); + else + pv = br_get_vlan_info(br); + + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) + goto done; + + af = nla_nest_start(skb, IFLA_AF_SPEC); + if (!af) + goto nla_put_failure; + + for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); + vid < BR_VLAN_BITMAP_LEN; + vid = find_next_bit(pv->vlan_bitmap, + BR_VLAN_BITMAP_LEN, vid+1)) { + vinfo.vid = vid; + vinfo.flags = 0; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, + sizeof(vinfo), &vinfo)) + goto nla_put_failure; + } + + nla_nest_end(skb, af); + } + +done: return nlmsg_end(skb, nlh); nla_put_failure: @@ -135,7 +175,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) if (skb == NULL) goto errout; - err = br_fill_ifinfo(skb, port, 0, 0, event, 0); + err = br_fill_ifinfo(skb, port, 0, 0, event, 0, 0, port->dev); if (err < 0) { /* -EMSGSIZE implies BUG in br_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -154,16 +194,17 @@ errout: * Dump information about all ports, in response to GETLINK */ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev) + struct net_device *dev, u32 filter_mask) { int err = 0; struct net_bridge_port *port = br_port_get_rcu(dev); - /* not a bridge port */ - if (!port) + /* not a bridge port and */ + if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN)) goto out; - err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI); + err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI, + filter_mask, dev); out: return err; } @@ -395,6 +436,29 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[]) return 0; } +static size_t br_get_link_af_size(const struct net_device *dev) +{ + struct net_port_vlans *pv; + + if (br_port_exists(dev)) + pv = nbp_get_vlan_info(br_port_get_rcu(dev)); + else if (dev->priv_flags & IFF_EBRIDGE) + pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev)); + else + return 0; + + if (!pv) + return 0; + + /* Each VLAN is returned in bridge_vlan_info along with flags */ + return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info)); +} + +struct rtnl_af_ops br_af_ops = { + .family = AF_BRIDGE, + .get_link_af_size = br_get_link_af_size, +}; + struct rtnl_link_ops br_link_ops __read_mostly = { .kind = "bridge", .priv_size = sizeof(struct net_bridge), @@ -408,11 +472,18 @@ int __init br_netlink_init(void) int err; br_mdb_init(); - err = rtnl_link_register(&br_link_ops); + err = rtnl_af_register(&br_af_ops); if (err) goto out; + err = rtnl_link_register(&br_link_ops); + if (err) + goto out_af; + return 0; + +out_af: + rtnl_af_unregister(&br_af_ops); out: br_mdb_uninit(); return err; @@ -421,5 +492,6 @@ out: void __exit br_netlink_fini(void) { br_mdb_uninit(); + rtnl_af_unregister(&br_af_ops); rtnl_link_unregister(&br_link_ops); } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a42f9d49a64e..ce2235255c2f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -73,6 +73,7 @@ struct net_port_vlans { } parent; struct rcu_head rcu; unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN]; + u16 num_vlans; }; struct net_bridge_fdb_entry @@ -715,7 +716,7 @@ extern void br_ifinfo_notify(int event, struct net_bridge_port *port); extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev); + struct net_device *dev, u32 filter_mask); #ifdef CONFIG_SYSFS /* br_sysfs_if.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index d8690bfe63d4..f2bf5a197ea3 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -28,6 +28,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid) } set_bit(vid, v->vlan_bitmap); + v->num_vlans++; return 0; } @@ -44,6 +45,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) } clear_bit(vid, v->vlan_bitmap); + v->num_vlans--; if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2c9ccbfbd93c..f3a112ec86d5 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2315,6 +2315,13 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) int idx = 0; u32 portid = NETLINK_CB(cb->skb).portid; u32 seq = cb->nlh->nlmsg_seq; + struct nlattr *extfilt; + u32 filter_mask = 0; + + extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct rtgenmsg), + IFLA_EXT_MASK); + if (extfilt) + filter_mask = nla_get_u32(extfilt); rcu_read_lock(); for_each_netdev_rcu(net, dev) { @@ -2324,14 +2331,15 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { if (idx >= cb->args[0] && br_dev->netdev_ops->ndo_bridge_getlink( - skb, portid, seq, dev) < 0) + skb, portid, seq, dev, filter_mask) < 0) break; idx++; } if (ops->ndo_bridge_getlink) { if (idx >= cb->args[0] && - ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0) + ops->ndo_bridge_getlink(skb, portid, seq, dev, + filter_mask) < 0) break; idx++; } @@ -2372,14 +2380,14 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags) if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { - err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0); if (err < 0) goto errout; } if ((flags & BRIDGE_FLAGS_SELF) && dev->netdev_ops->ndo_bridge_getlink) { - err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0); if (err < 0) goto errout; } -- cgit v1.2.3 From 7885198861fc9a3dfdc6bb90dc0ba12689d6cd57 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:14 +0000 Subject: bridge: Implement vlan ingress/egress policy with PVID. At ingress, any untagged traffic is assigned to the PVID. Any tagged traffic is filtered according to membership bitmap. At egress, if the vlan matches the PVID, the frame is sent untagged. Otherwise the frame is sent tagged. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/8021q/vlan_core.c | 1 + net/bridge/br_device.c | 3 +- net/bridge/br_forward.c | 8 +++++ net/bridge/br_input.c | 7 ++++- net/bridge/br_private.h | 34 ++++++++++++++++++-- net/bridge/br_vlan.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 126 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 71b64fde8dc9..f3b6f515eba6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -144,6 +144,7 @@ err_free: kfree_skb(skb); return NULL; } +EXPORT_SYMBOL(vlan_untag); /* diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 091bedf266a0..9509139da49c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -30,6 +30,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); + u16 vid = 0; rcu_read_lock(); #ifdef CONFIG_BRIDGE_NETFILTER @@ -45,7 +46,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) brstats->tx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); - if (!br_allowed_ingress(br, br_get_vlan_info(br), skb)) + if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) goto out; BR_INPUT_SKB_CB(skb)->brdev = dev; diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 35b0671f135d..092b20e4ee4c 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -64,6 +64,10 @@ int br_forward_finish(struct sk_buff *skb) static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { + skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb); + if (!skb) + return; + skb->dev = to->dev; if (unlikely(netpoll_tx_running(to->br->dev))) { @@ -89,6 +93,10 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) return; } + skb = br_handle_vlan(to->br, nbp_get_vlan_info(to), skb); + if (!skb) + return; + indev = skb->dev; skb->dev = to->dev; skb_forward_csum(skb); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 787d7dad6b7e..a63f227ad963 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -45,6 +45,10 @@ static int br_pass_frame_up(struct sk_buff *skb) return NET_RX_DROP; } + skb = br_handle_vlan(br, br_get_vlan_info(br), skb); + if (!skb) + return NET_RX_DROP; + indev = skb->dev; skb->dev = brdev; @@ -61,11 +65,12 @@ int br_handle_frame_finish(struct sk_buff *skb) struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; + u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) goto drop; - if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb)) + if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) goto drop; /* insert into forwarding database after filtering to avoid spoofing */ diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ce2235255c2f..ea8e7efd9137 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -67,6 +67,7 @@ struct br_ip struct net_port_vlans { u16 port_idx; + u16 pvid; union { struct net_bridge_port *port; struct net_bridge *br; @@ -554,10 +555,13 @@ static inline void br_mdb_uninit(void) /* br_vlan.c */ #ifdef CONFIG_BRIDGE_VLAN_FILTERING extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, - struct sk_buff *skb); + struct sk_buff *skb, u16 *vid); extern bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v, const struct sk_buff *skb); +extern struct sk_buff *br_handle_vlan(struct net_bridge *br, + const struct net_port_vlans *v, + struct sk_buff *skb); extern int br_vlan_add(struct net_bridge *br, u16 vid); extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern void br_vlan_flush(struct net_bridge *br); @@ -594,10 +598,23 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) return err; } + +static inline u16 br_get_pvid(const struct net_port_vlans *v) +{ + /* Return just the VID if it is set, or VLAN_N_VID (invalid vid) if + * vid wasn't set + */ + smp_rmb(); + return (v->pvid & VLAN_TAG_PRESENT) ? + (v->pvid & ~VLAN_TAG_PRESENT) : + VLAN_N_VID; +} + #else static inline bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, - struct sk_buff *skb) + struct sk_buff *skb, + u16 *vid) { return true; } @@ -609,6 +626,13 @@ static inline bool br_allowed_egress(struct net_bridge *br, return true; } +static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, + const struct net_port_vlans *v, + struct sk_buff *skb) +{ + return skb; +} + static inline int br_vlan_add(struct net_bridge *br, u16 vid) { return -EOPNOTSUPP; @@ -648,10 +672,14 @@ static inline struct net_port_vlans *nbp_get_vlan_info( return NULL; } -static inline u16 br_vlan_get_tag(const struct sk_buff *skb) +static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) { return 0; } +static inline u16 br_get_pvid(const struct net_port_vlans *v) +{ + return VLAN_N_VID; /* Returns invalid vid */ +} #endif /* br_netfilter.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index f2bf5a197ea3..20057de56db0 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -66,12 +66,68 @@ static void __vlan_flush(struct net_port_vlans *v) kfree_rcu(v, rcu); } -/* Called under RCU */ -bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, - struct sk_buff *skb) +/* Strip the tag from the packet. Will return skb with tci set 0. */ +static struct sk_buff *br_vlan_untag(struct sk_buff *skb) +{ + if (skb->protocol != htons(ETH_P_8021Q)) { + skb->vlan_tci = 0; + return skb; + } + + skb->vlan_tci = 0; + skb = vlan_untag(skb); + if (skb) + skb->vlan_tci = 0; + + return skb; +} + +struct sk_buff *br_handle_vlan(struct net_bridge *br, + const struct net_port_vlans *pv, + struct sk_buff *skb) { u16 vid; + if (!br->vlan_enabled) + goto out; + + /* At this point, we know that the frame was filtered and contains + * a valid vlan id. If the vlan id matches the pvid of current port + * send untagged; otherwise, send taged. + */ + br_vlan_get_tag(skb, &vid); + if (vid == br_get_pvid(pv)) + skb = br_vlan_untag(skb); + else { + /* Egress policy says "send tagged". If output device + * is the bridge, we need to add the VLAN header + * ourselves since we'll be going through the RX path. + * Sending to ports puts the frame on the TX path and + * we let dev_hard_start_xmit() add the header. + */ + if (skb->protocol != htons(ETH_P_8021Q) && + pv->port_idx == 0) { + /* vlan_put_tag expects skb->data to point to + * mac header. + */ + skb_push(skb, ETH_HLEN); + skb = __vlan_put_tag(skb, skb->vlan_tci); + if (!skb) + goto out; + /* put skb->data back to where it was */ + skb_pull(skb, ETH_HLEN); + skb->vlan_tci = 0; + } + } + +out: + return skb; +} + +/* Called under RCU */ +bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, + struct sk_buff *skb, u16 *vid) +{ /* If VLAN filtering is disabled on the bridge, all packets are * permitted. */ @@ -84,8 +140,25 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, if (!v) return false; - br_vlan_get_tag(skb, &vid); - if (test_bit(vid, v->vlan_bitmap)) + if (br_vlan_get_tag(skb, vid)) { + u16 pvid = br_get_pvid(v); + + /* Frame did not have a tag. See if pvid is set + * on this port. That tells us which vlan untagged + * traffic belongs to. + */ + if (pvid == VLAN_N_VID) + return false; + + /* PVID is set on this port. Any untagged ingress + * frame is considered to belong to this vlan. + */ + __vlan_hwaccel_put_tag(skb, pvid); + return true; + } + + /* Frame had a valid vlan tag. See if vlan is allowed */ + if (test_bit(*vid, v->vlan_bitmap)) return true; return false; -- cgit v1.2.3 From 552406c488ec2cf1aaf8b5bd24d1750c9fd6d8cc Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:15 +0000 Subject: bridge: Add the ability to configure pvid A user may designate a certain vlan as PVID. This means that any ingress frame that does not contain a vlan tag is assigned to this vlan and any forwarding decisions are made with this vlan in mind. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/uapi/linux/if_bridge.h | 1 + net/bridge/br_netlink.c | 11 +++++++--- net/bridge/br_private.h | 8 +++---- net/bridge/br_vlan.c | 47 +++++++++++++++++++++++++++++++++--------- 4 files changed, 50 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 3ca9817ca7e8..c6c30e28f396 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -120,6 +120,7 @@ enum { #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ +#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ struct bridge_vlan_info { u16 flags; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index fe1980d5a7e4..e044cc0b5650 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -120,6 +120,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_port_vlans *pv; struct bridge_vlan_info vinfo; u16 vid; + u16 pvid; if (port) pv = nbp_get_vlan_info(port); @@ -133,12 +134,15 @@ static int br_fill_ifinfo(struct sk_buff *skb, if (!af) goto nla_put_failure; + pvid = br_get_pvid(pv); for (vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); vid < BR_VLAN_BITMAP_LEN; vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) { vinfo.vid = vid; vinfo.flags = 0; + if (vid == pvid) + vinfo.flags |= BRIDGE_VLAN_INFO_PVID; if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo)) goto nla_put_failure; @@ -239,14 +243,15 @@ static int br_afspec(struct net_bridge *br, switch (cmd) { case RTM_SETLINK: if (p) { - err = nbp_vlan_add(p, vinfo->vid); + err = nbp_vlan_add(p, vinfo->vid, vinfo->flags); if (err) break; if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) - err = br_vlan_add(p->br, vinfo->vid); + err = br_vlan_add(p->br, vinfo->vid, + vinfo->flags); } else - err = br_vlan_add(br, vinfo->vid); + err = br_vlan_add(br, vinfo->vid, vinfo->flags); if (err) break; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ea8e7efd9137..1ae6395a0369 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -562,11 +562,11 @@ extern bool br_allowed_egress(struct net_bridge *br, extern struct sk_buff *br_handle_vlan(struct net_bridge *br, const struct net_port_vlans *v, struct sk_buff *skb); -extern int br_vlan_add(struct net_bridge *br, u16 vid); +extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); extern int br_vlan_delete(struct net_bridge *br, u16 vid); extern void br_vlan_flush(struct net_bridge *br); extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); -extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid); +extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); extern void nbp_vlan_flush(struct net_bridge_port *port); @@ -633,7 +633,7 @@ static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, return skb; } -static inline int br_vlan_add(struct net_bridge *br, u16 vid) +static inline int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) { return -EOPNOTSUPP; } @@ -647,7 +647,7 @@ static inline void br_vlan_flush(struct net_bridge *br) { } -static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) { return -EOPNOTSUPP; } diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 20057de56db0..c79940cff3a1 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -5,12 +5,33 @@ #include "br_private.h" -static int __vlan_add(struct net_port_vlans *v, u16 vid) +static void __vlan_add_pvid(struct net_port_vlans *v, u16 vid) +{ + if (v->pvid == vid) + return; + + smp_wmb(); + v->pvid = vid; +} + +static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid) +{ + if (v->pvid != vid) + return; + + smp_wmb(); + v->pvid = 0; +} + +static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) { int err; - if (test_bit(vid, v->vlan_bitmap)) - return -EEXIST; + if (test_bit(vid, v->vlan_bitmap)) { + if (flags & BRIDGE_VLAN_INFO_PVID) + __vlan_add_pvid(v, vid); + return 0; + } if (v->port_idx && vid) { struct net_device *dev = v->parent.port->dev; @@ -29,6 +50,9 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid) set_bit(vid, v->vlan_bitmap); v->num_vlans++; + if (flags & BRIDGE_VLAN_INFO_PVID) + __vlan_add_pvid(v, vid); + return 0; } @@ -37,6 +61,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) if (!test_bit(vid, v->vlan_bitmap)) return -EINVAL; + __vlan_delete_pvid(v, vid); + if (v->port_idx && vid) { struct net_device *dev = v->parent.port->dev; @@ -58,6 +84,8 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) static void __vlan_flush(struct net_port_vlans *v) { + smp_wmb(); + v->pvid = 0; bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); @@ -185,7 +213,7 @@ bool br_allowed_egress(struct net_bridge *br, } /* Must be protected by RTNL */ -int br_vlan_add(struct net_bridge *br, u16 vid) +int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags) { struct net_port_vlans *pv = NULL; int err; @@ -194,7 +222,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid) pv = rtnl_dereference(br->vlan_info); if (pv) - return __vlan_add(pv, vid); + return __vlan_add(pv, vid, flags); /* Create port vlan infomration */ @@ -203,7 +231,7 @@ int br_vlan_add(struct net_bridge *br, u16 vid) return -ENOMEM; pv->parent.br = br; - err = __vlan_add(pv, vid); + err = __vlan_add(pv, vid, flags); if (err) goto out; @@ -234,7 +262,6 @@ void br_vlan_flush(struct net_bridge *br) struct net_port_vlans *pv; ASSERT_RTNL(); - pv = rtnl_dereference(br->vlan_info); if (!pv) return; @@ -258,7 +285,7 @@ unlock: } /* Must be protected by RTNL */ -int nbp_vlan_add(struct net_bridge_port *port, u16 vid) +int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) { struct net_port_vlans *pv = NULL; int err; @@ -267,7 +294,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid) pv = rtnl_dereference(port->vlan_info); if (pv) - return __vlan_add(pv, vid); + return __vlan_add(pv, vid, flags); /* Create port vlan infomration */ @@ -279,7 +306,7 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid) pv->port_idx = port->port_no; pv->parent.port = port; - err = __vlan_add(pv, vid); + err = __vlan_add(pv, vid, flags); if (err) goto clean_up; -- cgit v1.2.3 From 2ba071ecb6d41ce172f9ccb3996f28cb337b3576 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:16 +0000 Subject: bridge: Add vlan to unicast fdb entries This patch adds vlan to unicast fdb entries that are created for learned addresses (not the manually configured ones). It adds vlan id into the hash mix and uses vlan as an addditional parameter for an entry match. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_device.c | 2 +- net/bridge/br_fdb.c | 69 ++++++++++++++++++++++++++++--------------------- net/bridge/br_input.c | 9 ++++--- net/bridge/br_private.h | 7 +++-- 4 files changed, 52 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 9509139da49c..d5f1d3fd4b28 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -71,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) br_multicast_deliver(mdst, skb); else br_flood_deliver(br, skb); - } else if ((dst = __br_fdb_get(br, dest)) != NULL) + } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL) br_deliver(dst->dst, skb); else br_flood_deliver(br, skb); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index d9576e6de2b8..276a52254606 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "br_private.h" static struct kmem_cache *br_fdb_cache __read_mostly; @@ -67,11 +68,11 @@ static inline int has_expired(const struct net_bridge *br, time_before_eq(fdb->updated + hold_time(br), jiffies); } -static inline int br_mac_hash(const unsigned char *mac) +static inline int br_mac_hash(const unsigned char *mac, __u16 vid) { - /* use 1 byte of OUI cnd 3 bytes of NIC */ + /* use 1 byte of OUI and 3 bytes of NIC */ u32 key = get_unaligned((u32 *)(mac + 2)); - return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1); + return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1); } static void fdb_rcu_free(struct rcu_head *head) @@ -132,7 +133,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) struct net_bridge_fdb_entry *f; /* If old entry was unassociated with any port, then delete it. */ - f = __br_fdb_get(br, br->dev->dev_addr); + f = __br_fdb_get(br, br->dev->dev_addr, 0); if (f && f->is_local && !f->dst) fdb_delete(br, f); @@ -231,13 +232,16 @@ void br_fdb_delete_by_port(struct net_bridge *br, /* No locking or refcounting, assumes caller has rcu_read_lock */ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, - const unsigned char *addr) + const unsigned char *addr, + __u16 vid) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; - hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { - if (ether_addr_equal(fdb->addr.addr, addr)) { + hlist_for_each_entry_rcu(fdb, h, + &br->hash[br_mac_hash(addr, vid)], hlist) { + if (ether_addr_equal(fdb->addr.addr, addr) && + fdb->vlan_id == vid) { if (unlikely(has_expired(br, fdb))) break; return fdb; @@ -261,7 +265,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) if (!port) ret = 0; else { - fdb = __br_fdb_get(port->br, addr); + fdb = __br_fdb_get(port->br, addr, 0); ret = fdb && fdb->dst && fdb->dst->dev != dev && fdb->dst->state == BR_STATE_FORWARDING; } @@ -325,26 +329,30 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, } static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, - const unsigned char *addr) + const unsigned char *addr, + __u16 vid) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; hlist_for_each_entry(fdb, h, head, hlist) { - if (ether_addr_equal(fdb->addr.addr, addr)) + if (ether_addr_equal(fdb->addr.addr, addr) && + fdb->vlan_id == vid) return fdb; } return NULL; } static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, - const unsigned char *addr) + const unsigned char *addr, + __u16 vid) { struct hlist_node *h; struct net_bridge_fdb_entry *fdb; hlist_for_each_entry_rcu(fdb, h, head, hlist) { - if (ether_addr_equal(fdb->addr.addr, addr)) + if (ether_addr_equal(fdb->addr.addr, addr) && + fdb->vlan_id == vid) return fdb; } return NULL; @@ -352,7 +360,8 @@ static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, + __u16 vid) { struct net_bridge_fdb_entry *fdb; @@ -360,6 +369,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); fdb->dst = source; + fdb->vlan_id = vid; fdb->is_local = 0; fdb->is_static = 0; fdb->updated = fdb->used = jiffies; @@ -371,13 +381,13 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr) { - struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; struct net_bridge_fdb_entry *fdb; if (!is_valid_ether_addr(addr)) return -EINVAL; - fdb = fdb_find(head, addr); + fdb = fdb_find(head, addr, 0); if (fdb) { /* it is okay to have multiple ports with same * address, just use the first one. @@ -390,7 +400,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, fdb_delete(br, fdb); } - fdb = fdb_create(head, source, addr); + fdb = fdb_create(head, source, addr, 0); if (!fdb) return -ENOMEM; @@ -412,9 +422,9 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, } void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { - struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; /* some users want to always flood. */ @@ -426,7 +436,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, source->state == BR_STATE_FORWARDING)) return; - fdb = fdb_find_rcu(head, addr); + fdb = fdb_find_rcu(head, addr, vid); if (likely(fdb)) { /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { @@ -441,8 +451,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, } } else { spin_lock(&br->hash_lock); - if (likely(!fdb_find(head, addr))) { - fdb = fdb_create(head, source, addr); + if (likely(!fdb_find(head, addr, vid))) { + fdb = fdb_create(head, source, addr, vid); if (fdb) fdb_notify(br, fdb, RTM_NEWNEIGH); } @@ -571,18 +581,18 @@ out: /* Update (create or replace) forwarding database entry */ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, - __u16 state, __u16 flags) + __u16 state, __u16 flags, __u16 vid) { struct net_bridge *br = source->br; - struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; - fdb = fdb_find(head, addr); + fdb = fdb_find(head, addr, vid); if (fdb == NULL) { if (!(flags & NLM_F_CREATE)) return -ENOENT; - fdb = fdb_create(head, source, addr); + fdb = fdb_create(head, source, addr, vid); if (!fdb) return -ENOMEM; fdb_notify(br, fdb, RTM_NEWNEIGH); @@ -629,11 +639,12 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], if (ndm->ndm_flags & NTF_USE) { rcu_read_lock(); - br_fdb_update(p->br, p, addr); + br_fdb_update(p->br, p, addr, 0); rcu_read_unlock(); } else { spin_lock_bh(&p->br->hash_lock); - err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags); + err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags, + 0); spin_unlock_bh(&p->br->hash_lock); } @@ -643,10 +654,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) { struct net_bridge *br = p->br; - struct hlist_head *head = &br->hash[br_mac_hash(addr)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; struct net_bridge_fdb_entry *fdb; - fdb = fdb_find(head, addr); + fdb = fdb_find(head, addr, 0); if (!fdb) return -ENOENT; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index a63f227ad963..480330151898 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -75,7 +75,7 @@ int br_handle_frame_finish(struct sk_buff *skb) /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; - br_fdb_update(br, p, eth_hdr(skb)->h_source); + br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb)) @@ -110,7 +110,8 @@ int br_handle_frame_finish(struct sk_buff *skb) skb2 = skb; br->dev->stats.multicast++; - } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { + } else if ((dst = __br_fdb_get(br, dest, vid)) && + dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; @@ -138,8 +139,10 @@ drop: static int br_handle_local_finish(struct sk_buff *skb) { struct net_bridge_port *p = br_port_get_rcu(skb->dev); + u16 vid = 0; - br_fdb_update(p->br, p, eth_hdr(skb)->h_source); + br_vlan_get_tag(skb, &vid); + br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid); return 0; /* process further */ } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1ae6395a0369..f4ae87b5aa6e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -88,6 +88,7 @@ struct net_bridge_fdb_entry mac_addr addr; unsigned char is_local; unsigned char is_static; + __u16 vlan_id; }; struct net_bridge_port_group { @@ -373,7 +374,8 @@ extern void br_fdb_cleanup(unsigned long arg); extern void br_fdb_delete_by_port(struct net_bridge *br, const struct net_bridge_port *p, int do_all); extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, - const unsigned char *addr); + const unsigned char *addr, + __u16 vid); extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); @@ -382,7 +384,8 @@ extern int br_fdb_insert(struct net_bridge *br, const unsigned char *addr); extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, + u16 vid); extern int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, -- cgit v1.2.3 From b0e9a30dd669a844bb4f74515f8bcd307018ffd0 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:17 +0000 Subject: bridge: Add vlan id to multicast groups Add vlan_id to multicasts groups so that we know which vlan each group belongs to and can correctly forward to appropriate vlan. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 69 +++++++++++++++++++++++++++++++++-------------- net/bridge/br_private.h | 1 + 2 files changed, 50 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 6d6f26531de2..7d886b0a8b7b 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -39,6 +39,8 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) { if (a->proto != b->proto) return 0; + if (a->vid != b->vid) + return 0; switch (a->proto) { case htons(ETH_P_IP): return a->u.ip4 == b->u.ip4; @@ -50,16 +52,19 @@ static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b) return 0; } -static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip) +static inline int __br_ip4_hash(struct net_bridge_mdb_htable *mdb, __be32 ip, + __u16 vid) { - return jhash_1word(mdb->secret, (__force u32)ip) & (mdb->max - 1); + return jhash_2words((__force u32)ip, vid, mdb->secret) & (mdb->max - 1); } #if IS_ENABLED(CONFIG_IPV6) static inline int __br_ip6_hash(struct net_bridge_mdb_htable *mdb, - const struct in6_addr *ip) + const struct in6_addr *ip, + __u16 vid) { - return jhash2((__force u32 *)ip->s6_addr32, 4, mdb->secret) & (mdb->max - 1); + return jhash_2words(ipv6_addr_hash(ip), vid, + mdb->secret) & (mdb->max - 1); } #endif @@ -68,10 +73,10 @@ static inline int br_ip_hash(struct net_bridge_mdb_htable *mdb, { switch (ip->proto) { case htons(ETH_P_IP): - return __br_ip4_hash(mdb, ip->u.ip4); + return __br_ip4_hash(mdb, ip->u.ip4, ip->vid); #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): - return __br_ip6_hash(mdb, &ip->u.ip6); + return __br_ip6_hash(mdb, &ip->u.ip6, ip->vid); #endif } return 0; @@ -101,24 +106,27 @@ struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, } static struct net_bridge_mdb_entry *br_mdb_ip4_get( - struct net_bridge_mdb_htable *mdb, __be32 dst) + struct net_bridge_mdb_htable *mdb, __be32 dst, __u16 vid) { struct br_ip br_dst; br_dst.u.ip4 = dst; br_dst.proto = htons(ETH_P_IP); + br_dst.vid = vid; return br_mdb_ip_get(mdb, &br_dst); } #if IS_ENABLED(CONFIG_IPV6) static struct net_bridge_mdb_entry *br_mdb_ip6_get( - struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst) + struct net_bridge_mdb_htable *mdb, const struct in6_addr *dst, + __u16 vid) { struct br_ip br_dst; br_dst.u.ip6 = *dst; br_dst.proto = htons(ETH_P_IPV6); + br_dst.vid = vid; return br_mdb_ip_get(mdb, &br_dst); } @@ -694,7 +702,8 @@ err: static int br_ip4_multicast_add_group(struct net_bridge *br, struct net_bridge_port *port, - __be32 group) + __be32 group, + __u16 vid) { struct br_ip br_group; @@ -703,6 +712,7 @@ static int br_ip4_multicast_add_group(struct net_bridge *br, br_group.u.ip4 = group; br_group.proto = htons(ETH_P_IP); + br_group.vid = vid; return br_multicast_add_group(br, port, &br_group); } @@ -710,7 +720,8 @@ static int br_ip4_multicast_add_group(struct net_bridge *br, #if IS_ENABLED(CONFIG_IPV6) static int br_ip6_multicast_add_group(struct net_bridge *br, struct net_bridge_port *port, - const struct in6_addr *group) + const struct in6_addr *group, + __u16 vid) { struct br_ip br_group; @@ -719,6 +730,7 @@ static int br_ip6_multicast_add_group(struct net_bridge *br, br_group.u.ip6 = *group; br_group.proto = htons(ETH_P_IPV6); + br_group.vid = vid; return br_multicast_add_group(br, port, &br_group); } @@ -895,10 +907,12 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, int type; int err = 0; __be32 group; + u16 vid = 0; if (!pskb_may_pull(skb, sizeof(*ih))) return -EINVAL; + br_vlan_get_tag(skb, &vid); ih = igmpv3_report_hdr(skb); num = ntohs(ih->ngrec); len = sizeof(*ih); @@ -930,7 +944,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, continue; } - err = br_ip4_multicast_add_group(br, port, group); + err = br_ip4_multicast_add_group(br, port, group, vid); if (err) break; } @@ -949,10 +963,12 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, int len; int num; int err = 0; + u16 vid = 0; if (!pskb_may_pull(skb, sizeof(*icmp6h))) return -EINVAL; + br_vlan_get_tag(skb, &vid); icmp6h = icmp6_hdr(skb); num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); len = sizeof(*icmp6h); @@ -990,7 +1006,8 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, continue; } - err = br_ip6_multicast_add_group(br, port, &grec->grec_mca); + err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, + vid); if (!err) break; } @@ -1074,6 +1091,7 @@ static int br_ip4_multicast_query(struct net_bridge *br, unsigned long now = jiffies; __be32 group; int err = 0; + u16 vid = 0; spin_lock(&br->multicast_lock); if (!netif_running(br->dev) || @@ -1108,7 +1126,8 @@ static int br_ip4_multicast_query(struct net_bridge *br, if (!group) goto out; - mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group); + br_vlan_get_tag(skb, &vid); + mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid); if (!mp) goto out; @@ -1149,6 +1168,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, unsigned long now = jiffies; const struct in6_addr *group = NULL; int err = 0; + u16 vid = 0; spin_lock(&br->multicast_lock); if (!netif_running(br->dev) || @@ -1180,7 +1200,8 @@ static int br_ip6_multicast_query(struct net_bridge *br, if (!group) goto out; - mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group); + br_vlan_get_tag(skb, &vid); + mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid); if (!mp) goto out; @@ -1286,7 +1307,8 @@ out: static void br_ip4_multicast_leave_group(struct net_bridge *br, struct net_bridge_port *port, - __be32 group) + __be32 group, + __u16 vid) { struct br_ip br_group; @@ -1295,6 +1317,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br, br_group.u.ip4 = group; br_group.proto = htons(ETH_P_IP); + br_group.vid = vid; br_multicast_leave_group(br, port, &br_group); } @@ -1302,7 +1325,8 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br, #if IS_ENABLED(CONFIG_IPV6) static void br_ip6_multicast_leave_group(struct net_bridge *br, struct net_bridge_port *port, - const struct in6_addr *group) + const struct in6_addr *group, + __u16 vid) { struct br_ip br_group; @@ -1311,6 +1335,7 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br, br_group.u.ip6 = *group; br_group.proto = htons(ETH_P_IPV6); + br_group.vid = vid; br_multicast_leave_group(br, port, &br_group); } @@ -1326,6 +1351,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, unsigned int len; unsigned int offset; int err; + u16 vid = 0; /* We treat OOM as packet loss for now. */ if (!pskb_may_pull(skb, sizeof(*iph))) @@ -1386,6 +1412,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, err = 0; + br_vlan_get_tag(skb2, &vid); BR_INPUT_SKB_CB(skb)->igmp = 1; ih = igmp_hdr(skb2); @@ -1393,7 +1420,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: BR_INPUT_SKB_CB(skb)->mrouters_only = 1; - err = br_ip4_multicast_add_group(br, port, ih->group); + err = br_ip4_multicast_add_group(br, port, ih->group, vid); break; case IGMPV3_HOST_MEMBERSHIP_REPORT: err = br_ip4_multicast_igmp3_report(br, port, skb2); @@ -1402,7 +1429,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, err = br_ip4_multicast_query(br, port, skb2); break; case IGMP_HOST_LEAVE_MESSAGE: - br_ip4_multicast_leave_group(br, port, ih->group); + br_ip4_multicast_leave_group(br, port, ih->group, vid); break; } @@ -1427,6 +1454,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, unsigned int len; int offset; int err; + u16 vid = 0; if (!pskb_may_pull(skb, sizeof(*ip6h))) return -EINVAL; @@ -1510,6 +1538,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, err = 0; + br_vlan_get_tag(skb, &vid); BR_INPUT_SKB_CB(skb)->igmp = 1; switch (icmp6_type) { @@ -1522,7 +1551,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, } mld = (struct mld_msg *)skb_transport_header(skb2); BR_INPUT_SKB_CB(skb)->mrouters_only = 1; - err = br_ip6_multicast_add_group(br, port, &mld->mld_mca); + err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid); break; } case ICMPV6_MLD2_REPORT: @@ -1539,7 +1568,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, goto out; } mld = (struct mld_msg *)skb_transport_header(skb2); - br_ip6_multicast_leave_group(br, port, &mld->mld_mca); + br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid); } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f4ae87b5aa6e..22915c8e9961 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -63,6 +63,7 @@ struct br_ip #endif } u; __be16 proto; + __u16 vid; }; struct net_port_vlans { -- cgit v1.2.3 From 1690be63a27b20ae65c792729a44f5970561ffa4 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:18 +0000 Subject: bridge: Add vlan support to static neighbors When a user adds bridge neighbors, allow him to specify VLAN id. If the VLAN id is not specified, the neighbor will be added for VLANs currently in the ports filter list. If no VLANs are configured on the port, we use vlan 0 and only add 1 entry. Signed-off-by: Vlad Yasevich Acked-by: Jitendra Kalsaria Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 4 +- drivers/net/macvlan.c | 2 +- drivers/net/vxlan.c | 3 +- include/linux/netdevice.h | 4 +- include/uapi/linux/neighbour.h | 1 + net/bridge/br_fdb.c | 148 ++++++++++++++++++++--- net/bridge/br_private.h | 6 +- net/core/rtnetlink.c | 26 ++-- 10 files changed, 162 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 4e2aa47193cb..1c0efcb7920f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7002,7 +7002,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return err; } -static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, +static int ixgbe_ndo_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr) { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 937bcc3d3212..5088dc5c3d1a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -1959,6 +1959,7 @@ static int mlx4_en_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } static int mlx4_en_fdb_del(struct ndmsg *ndm, + struct nlattr *tb[], struct net_device *dev, const unsigned char *addr) { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index b745194391a1..b95316831587 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -247,8 +247,8 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p) return 0; } -static int qlcnic_fdb_del(struct ndmsg *ndm, struct net_device *netdev, - const unsigned char *addr) +static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *netdev, const unsigned char *addr) { struct qlcnic_adapter *adapter = netdev_priv(netdev); int err = -EOPNOTSUPP; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index e4b8078e88a9..defcd8a85744 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -599,7 +599,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return err; } -static int macvlan_fdb_del(struct ndmsg *ndm, +static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr) { diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 72485b9b9005..9d70421cf3a0 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -393,7 +393,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } /* Delete entry (via netlink) */ -static int vxlan_fdb_delete(struct ndmsg *ndm, struct net_device *dev, +static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr) { struct vxlan_dev *vxlan = netdev_priv(dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1964ca66df56..9deb672d999f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -884,7 +884,8 @@ struct netdev_fcoe_hbainfo { * struct net_device *dev, * const unsigned char *addr, u16 flags) * Adds an FDB entry to dev for addr. - * int (*ndo_fdb_del)(struct ndmsg *ndm, struct net_device *dev, + * int (*ndo_fdb_del)(struct ndmsg *ndm, struct nlattr *tb[], + * struct net_device *dev, * const unsigned char *addr) * Deletes the FDB entry from dev coresponding to addr. * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb, @@ -1008,6 +1009,7 @@ struct net_device_ops { const unsigned char *addr, u16 flags); int (*ndo_fdb_del)(struct ndmsg *ndm, + struct nlattr *tb[], struct net_device *dev, const unsigned char *addr); int (*ndo_fdb_dump)(struct sk_buff *skb, diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index 275e5d65dcb2..adb068c53c4e 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -20,6 +20,7 @@ enum { NDA_LLADDR, NDA_CACHEINFO, NDA_PROBES, + NDA_VLAN, __NDA_MAX }; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 276a52254606..4b75ad43aa85 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -505,6 +505,10 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ci.ndm_refcnt = 0; if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) goto nla_put_failure; + + if (nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id)) + goto nla_put_failure; + return nlmsg_end(skb, nlh); nla_put_failure: @@ -516,6 +520,7 @@ static inline size_t fdb_nlmsg_size(void) { return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + + nla_total_size(sizeof(u16)) /* NDA_VLAN */ + nla_total_size(sizeof(struct nda_cacheinfo)); } @@ -617,6 +622,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, return 0; } +static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p, + const unsigned char *addr, u16 nlh_flags, u16 vid) +{ + int err = 0; + + if (ndm->ndm_flags & NTF_USE) { + rcu_read_lock(); + br_fdb_update(p->br, p, addr, vid); + rcu_read_unlock(); + } else { + spin_lock_bh(&p->br->hash_lock); + err = fdb_add_entry(p, addr, ndm->ndm_state, + nlh_flags, vid); + spin_unlock_bh(&p->br->hash_lock); + } + + return err; +} + /* Add new permanent fdb entry with RTM_NEWNEIGH */ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, @@ -624,12 +648,29 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], { struct net_bridge_port *p; int err = 0; + struct net_port_vlans *pv; + unsigned short vid = VLAN_N_VID; if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); return -EINVAL; } + if (tb[NDA_VLAN]) { + if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { + pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); + return -EINVAL; + } + + vid = nla_get_u16(tb[NDA_VLAN]); + + if (vid >= VLAN_N_VID) { + pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", + vid); + return -EINVAL; + } + } + p = br_port_get_rtnl(dev); if (p == NULL) { pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", @@ -637,41 +678,90 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return -EINVAL; } - if (ndm->ndm_flags & NTF_USE) { - rcu_read_lock(); - br_fdb_update(p->br, p, addr, 0); - rcu_read_unlock(); + pv = nbp_get_vlan_info(p); + if (vid != VLAN_N_VID) { + if (!pv || !test_bit(vid, pv->vlan_bitmap)) { + pr_info("bridge: RTM_NEWNEIGH with unconfigured " + "vlan %d on port %s\n", vid, dev->name); + return -EINVAL; + } + + /* VID was specified, so use it. */ + err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); } else { - spin_lock_bh(&p->br->hash_lock); - err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags, - 0); - spin_unlock_bh(&p->br->hash_lock); + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); + goto out; + } + + /* We have vlans configured on this port and user didn't + * specify a VLAN. To be nice, add/update entry for every + * vlan on this port. + */ + vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); + while (vid < BR_VLAN_BITMAP_LEN) { + err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); + if (err) + goto out; + vid = find_next_bit(pv->vlan_bitmap, + BR_VLAN_BITMAP_LEN, vid+1); + } } +out: return err; } -static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) +static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, + u16 vlan) { - struct net_bridge *br = p->br; - struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; struct net_bridge_fdb_entry *fdb; - fdb = fdb_find(head, addr, 0); + fdb = fdb_find(head, addr, vlan); if (!fdb) return -ENOENT; - fdb_delete(p->br, fdb); + fdb_delete(br, fdb); return 0; } +static int __br_fdb_delete(struct net_bridge_port *p, + const unsigned char *addr, u16 vid) +{ + int err; + + spin_lock_bh(&p->br->hash_lock); + err = fdb_delete_by_addr(p->br, addr, vid); + spin_unlock_bh(&p->br->hash_lock); + + return err; +} + /* Remove neighbor entry with RTM_DELNEIGH */ -int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, +int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr) { struct net_bridge_port *p; int err; + struct net_port_vlans *pv; + unsigned short vid = VLAN_N_VID; + if (tb[NDA_VLAN]) { + if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { + pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); + return -EINVAL; + } + + vid = nla_get_u16(tb[NDA_VLAN]); + + if (vid >= VLAN_N_VID) { + pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", + vid); + return -EINVAL; + } + } p = br_port_get_rtnl(dev); if (p == NULL) { pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", @@ -679,9 +769,33 @@ int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, return -EINVAL; } - spin_lock_bh(&p->br->hash_lock); - err = fdb_delete_by_addr(p, addr); - spin_unlock_bh(&p->br->hash_lock); + pv = nbp_get_vlan_info(p); + if (vid != VLAN_N_VID) { + if (!pv || !test_bit(vid, pv->vlan_bitmap)) { + pr_info("bridge: RTM_DELNEIGH with unconfigured " + "vlan %d on port %s\n", vid, dev->name); + return -EINVAL; + } + err = __br_fdb_delete(p, addr, vid); + } else { + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { + err = __br_fdb_delete(p, addr, 0); + goto out; + } + + /* We have vlans configured on this port and user didn't + * specify a VLAN. To be nice, add/update entry for every + * vlan on this port. + */ + err = -ENOENT; + vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN); + while (vid < BR_VLAN_BITMAP_LEN) { + err &= __br_fdb_delete(p, addr, vid); + vid = find_next_bit(pv->vlan_bitmap, + BR_VLAN_BITMAP_LEN, vid+1); + } + } +out: return err; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 22915c8e9961..799dbb37e5a2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -388,7 +388,7 @@ extern void br_fdb_update(struct net_bridge *br, const unsigned char *addr, u16 vid); -extern int br_fdb_delete(struct ndmsg *ndm, +extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr); extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], @@ -577,13 +577,13 @@ extern void nbp_vlan_flush(struct net_bridge_port *port); static inline struct net_port_vlans *br_get_vlan_info( const struct net_bridge *br) { - return rcu_dereference(br->vlan_info); + return rcu_dereference_rtnl(br->vlan_info); } static inline struct net_port_vlans *nbp_get_vlan_info( const struct net_bridge_port *p) { - return rcu_dereference(p->vlan_info); + return rcu_dereference_rtnl(p->vlan_info); } /* Since bridge now depends on 8021Q module, but the time bridge sees the diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index f3a112ec86d5..d8aa20f6a46e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2119,13 +2119,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); struct ndmsg *ndm; - struct nlattr *llattr; + struct nlattr *tb[NDA_MAX+1]; struct net_device *dev; int err = -EINVAL; __u8 *addr; - if (nlmsg_len(nlh) < sizeof(*ndm)) - return -EINVAL; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + if (err < 0) + return err; ndm = nlmsg_data(nlh); if (ndm->ndm_ifindex == 0) { @@ -2139,13 +2143,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) return -ENODEV; } - llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); - if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { - pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n"); + if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(tb[NDA_LLADDR]); + if (!is_valid_ether_addr(addr)) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ether address\n"); return -EINVAL; } - addr = nla_data(llattr); err = -EOPNOTSUPP; /* Support fdb on master device the net/bridge default case */ @@ -2155,7 +2163,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) const struct net_device_ops *ops = br_dev->netdev_ops; if (ops->ndo_fdb_del) - err = ops->ndo_fdb_del(ndm, dev, addr); + err = ops->ndo_fdb_del(ndm, tb, dev, addr); if (err) goto out; @@ -2165,7 +2173,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* Embedded bridge, macvlan, and any other device support */ if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) { - err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr); + err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr); if (!err) { rtnl_fdb_notify(dev, addr, RTM_DELNEIGH); -- cgit v1.2.3 From bc9a25d21ef8bad30e259af5114ccfb845c066db Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:19 +0000 Subject: bridge: Add vlan support for local fdb entries When VLAN is added to the port, a local fdb entry for that port (the entry with the mac address of the port) is added for that VLAN. This way we can correctly determine if the traffic is for the bridge itself. If the address of the port changes, we try to change all the local fdb entries we have for that port. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 61 +++++++++++++++++++++++++++++---------- net/bridge/br_if.c | 2 +- net/bridge/br_private.h | 10 ++++++- net/bridge/br_vlan.c | 77 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 125 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 4b75ad43aa85..8117900af4de 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -28,7 +28,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly; static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, u16 vid); static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *, int); @@ -92,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) { struct net_bridge *br = p->br; + bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false; int i; spin_lock_bh(&br->hash_lock); @@ -106,10 +107,12 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) if (f->dst == p && f->is_local) { /* maybe another port has same hw addr? */ struct net_bridge_port *op; + u16 vid = f->vlan_id; list_for_each_entry(op, &br->port_list, list) { if (op != p && ether_addr_equal(op->dev->dev_addr, - f->addr.addr)) { + f->addr.addr) && + nbp_vlan_find(op, vid)) { f->dst = op; goto insert; } @@ -117,27 +120,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) /* delete old one */ fdb_delete(br, f); - goto insert; +insert: + /* insert new address, may fail if invalid + * address or dup. + */ + fdb_insert(br, p, newaddr, vid); + + /* if this port has no vlan information + * configured, we can safely be done at + * this point. + */ + if (no_vlan) + goto done; } } } - insert: - /* insert new address, may fail if invalid address or dup. */ - fdb_insert(br, p, newaddr); +done: spin_unlock_bh(&br->hash_lock); } void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) { struct net_bridge_fdb_entry *f; + struct net_port_vlans *pv; + u16 vid = 0; /* If old entry was unassociated with any port, then delete it. */ f = __br_fdb_get(br, br->dev->dev_addr, 0); if (f && f->is_local && !f->dst) fdb_delete(br, f); - fdb_insert(br, NULL, newaddr); + fdb_insert(br, NULL, newaddr, 0); + + /* Now remove and add entries for every VLAN configured on the + * bridge. This function runs under RTNL so the bitmap will not + * change from under us. + */ + pv = br_get_vlan_info(br); + if (!pv) + return; + + for (vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid); + vid < BR_VLAN_BITMAP_LEN; + vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) { + f = __br_fdb_get(br, br->dev->dev_addr, vid); + if (f && f->is_local && !f->dst) + fdb_delete(br, f); + fdb_insert(br, NULL, newaddr, vid); + } } void br_fdb_cleanup(unsigned long _data) @@ -379,15 +410,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, } static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { - struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; + struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; if (!is_valid_ether_addr(addr)) return -EINVAL; - fdb = fdb_find(head, addr, 0); + fdb = fdb_find(head, addr, vid); if (fdb) { /* it is okay to have multiple ports with same * address, just use the first one. @@ -400,7 +431,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, fdb_delete(br, fdb); } - fdb = fdb_create(head, source, addr, 0); + fdb = fdb_create(head, source, addr, vid); if (!fdb) return -ENOMEM; @@ -411,12 +442,12 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, /* Add entry for local address of interface */ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { int ret; spin_lock_bh(&br->hash_lock); - ret = fdb_insert(br, source, addr); + ret = fdb_insert(br, source, addr, vid); spin_unlock_bh(&br->hash_lock); return ret; } @@ -712,8 +743,8 @@ out: return err; } -static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, - u16 vlan) +int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, + u16 vlan) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; struct net_bridge_fdb_entry *fdb; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 335c60cebfd1..ef1b91431c6b 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -397,7 +397,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev_set_mtu(br->dev, br_min_mtu(br)); - if (br_fdb_insert(br, p, dev->dev_addr)) + if (br_fdb_insert(br, p, dev->dev_addr, 0)) netdev_err(dev, "failed insert local address bridge forwarding table\n"); kobject_uevent(&p->kobj, KOBJ_ADD); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 799dbb37e5a2..32ecfa4ef47f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -382,11 +382,13 @@ extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); extern int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, - const unsigned char *addr); + const unsigned char *addr, + u16 vid); extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr, u16 vid); +extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, @@ -573,6 +575,7 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); extern void nbp_vlan_flush(struct net_bridge_port *port); +extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); static inline struct net_port_vlans *br_get_vlan_info( const struct net_bridge *br) @@ -676,6 +679,11 @@ static inline struct net_port_vlans *nbp_get_vlan_info( return NULL; } +static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid) +{ + return false; +} + static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) { return 0; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index c79940cff3a1..9ea358fbbf78 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -25,6 +25,9 @@ static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid) static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) { + struct net_bridge_port *p = NULL; + struct net_bridge *br; + struct net_device *dev; int err; if (test_bit(vid, v->vlan_bitmap)) { @@ -33,19 +36,35 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) return 0; } - if (v->port_idx && vid) { - struct net_device *dev = v->parent.port->dev; + if (vid) { + if (v->port_idx) { + p = v->parent.port; + br = p->br; + dev = p->dev; + } else { + br = v->parent.br; + dev = br->dev; + } - /* Add VLAN to the device filter if it is supported. - * Stricly speaking, this is not necessary now, since devices - * are made promiscuous by the bridge, but if that ever changes - * this code will allow tagged traffic to enter the bridge. - */ - if (dev->features & NETIF_F_HW_VLAN_FILTER) { + if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) { + /* Add VLAN to the device filter if it is supported. + * Stricly speaking, this is not necessary now, since + * devices are made promiscuous by the bridge, but if + * that ever changes this code will allow tagged + * traffic to enter the bridge. + */ err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); if (err) return err; } + + err = br_fdb_insert(br, p, dev->dev_addr, vid); + if (err) { + br_err(br, "failed insert local address into bridge " + "forwarding table\n"); + goto out_filt; + } + } set_bit(vid, v->vlan_bitmap); @@ -54,6 +73,11 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) __vlan_add_pvid(v, vid); return 0; + +out_filt: + if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) + dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid); + return err; } static int __vlan_del(struct net_port_vlans *v, u16 vid) @@ -253,6 +277,15 @@ int br_vlan_delete(struct net_bridge *br, u16 vid) if (!pv) return -EINVAL; + if (vid) { + /* If the VID !=0 remove fdb for this vid. VID 0 is special + * in that it's the default and is always there in the fdb. + */ + spin_lock_bh(&br->hash_lock); + fdb_delete_by_addr(br, br->dev->dev_addr, vid); + spin_unlock_bh(&br->hash_lock); + } + __vlan_del(pv, vid); return 0; } @@ -329,6 +362,15 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) if (!pv) return -EINVAL; + if (vid) { + /* If the VID !=0 remove fdb for this vid. VID 0 is special + * in that it's the default and is always there in the fdb. + */ + spin_lock_bh(&port->br->hash_lock); + fdb_delete_by_addr(port->br, port->dev->dev_addr, vid); + spin_unlock_bh(&port->br->hash_lock); + } + return __vlan_del(pv, vid); } @@ -344,3 +386,22 @@ void nbp_vlan_flush(struct net_bridge_port *port) __vlan_flush(pv); } + +bool nbp_vlan_find(struct net_bridge_port *port, u16 vid) +{ + struct net_port_vlans *pv; + bool found = false; + + rcu_read_lock(); + pv = rcu_dereference(port->vlan_info); + + if (!pv) + goto out; + + if (test_bit(vid, pv->vlan_bitmap)) + found = true; + +out: + rcu_read_unlock(); + return found; +} -- cgit v1.2.3 From 35e03f3a0275a1ba57e432d7c948cf6f70fbb37a Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 13 Feb 2013 12:00:20 +0000 Subject: bridge: Separate egress policy bitmap Add an ability to configure a separate "untagged" egress policy to the VLAN information of the bridge. This superseeds PVID policy and makes PVID ingress-only. The policy is configured with a new flag and is represented as a port bitmap per vlan. Egress frames with a VLAN id in "untagged" policy bitmap would egress the port without VLAN header. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/uapi/linux/if_bridge.h | 1 + net/bridge/br_netlink.c | 4 ++++ net/bridge/br_private.h | 1 + net/bridge/br_vlan.c | 20 ++++++++++++++------ 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index c6c30e28f396..f1bf8d34ac9f 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -121,6 +121,7 @@ enum { #define BRIDGE_VLAN_INFO_MASTER (1<<0) /* Operate on Bridge device as well */ #define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ +#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ struct bridge_vlan_info { u16 flags; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index e044cc0b5650..d1dda476d743 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -143,6 +143,10 @@ static int br_fill_ifinfo(struct sk_buff *skb, vinfo.flags = 0; if (vid == pvid) vinfo.flags |= BRIDGE_VLAN_INFO_PVID; + + if (test_bit(vid, pv->untagged_bitmap)) + vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo)) goto nla_put_failure; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 32ecfa4ef47f..6d314c4e6bcb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -75,6 +75,7 @@ struct net_port_vlans { } parent; struct rcu_head rcu; unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN]; + unsigned long untagged_bitmap[BR_VLAN_BITMAP_LEN]; u16 num_vlans; }; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 9ea358fbbf78..93dde75923f0 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -23,6 +23,15 @@ static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid) v->pvid = 0; } +static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags) +{ + if (flags & BRIDGE_VLAN_INFO_PVID) + __vlan_add_pvid(v, vid); + + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) + set_bit(vid, v->untagged_bitmap); +} + static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) { struct net_bridge_port *p = NULL; @@ -31,8 +40,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) int err; if (test_bit(vid, v->vlan_bitmap)) { - if (flags & BRIDGE_VLAN_INFO_PVID) - __vlan_add_pvid(v, vid); + __vlan_add_flags(v, vid, flags); return 0; } @@ -69,8 +77,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) set_bit(vid, v->vlan_bitmap); v->num_vlans++; - if (flags & BRIDGE_VLAN_INFO_PVID) - __vlan_add_pvid(v, vid); + __vlan_add_flags(v, vid, flags); return 0; @@ -86,6 +93,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) return -EINVAL; __vlan_delete_pvid(v, vid); + clear_bit(vid, v->untagged_bitmap); if (v->port_idx && vid) { struct net_device *dev = v->parent.port->dev; @@ -144,11 +152,11 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, goto out; /* At this point, we know that the frame was filtered and contains - * a valid vlan id. If the vlan id matches the pvid of current port + * a valid vlan id. If the vlan id is set in the untagged bitmap, * send untagged; otherwise, send taged. */ br_vlan_get_tag(skb, &vid); - if (vid == br_get_pvid(pv)) + if (test_bit(vid, pv->untagged_bitmap)) skb = br_vlan_untag(skb); else { /* Egress policy says "send tagged". If output device -- cgit v1.2.3 From 15004cab947314ac0f2fd47169de95ce48bafb15 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 13 Feb 2013 19:57:12 +0000 Subject: bridge: make ifla_br_policy and br_af_ops static They are only used within this file. Cc: Vlad Yasevich Cc: Stephen Hemminger Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/bridge/br_netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index d1dda476d743..27aa3ee517ce 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -217,7 +217,7 @@ out: return err; } -const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { +static const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { [IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, [IFLA_BRIDGE_MODE] = { .type = NLA_U16 }, [IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY, @@ -463,7 +463,7 @@ static size_t br_get_link_af_size(const struct net_device *dev) return pv->num_vlans * nla_total_size(sizeof(struct bridge_vlan_info)); } -struct rtnl_af_ops br_af_ops = { +static struct rtnl_af_ops br_af_ops = { .family = AF_BRIDGE, .get_link_af_size = br_get_link_af_size, }; -- cgit v1.2.3 From 9754e293491e3a4e6c1ac020d25140b1ed3d9cd2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 14 Feb 2013 15:57:38 -0500 Subject: net: Don't write to current task flags on every packet received. Even for non-pfmalloc SKBs, __netif_receive_skb() will do a tsk_restore_flags() on current unconditionally. Make __netif_receive_skb() a shim around the existing code, renamed to __netif_receive_skb_core(). Let __netif_receive_skb() wrap the __netif_receive_skb_core() call with the task flag modifications, if necessary. Signed-off-by: David S. Miller --- net/core/dev.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 2f31bf97ba65..f44473696b8b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3457,7 +3457,7 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb) } } -static int __netif_receive_skb(struct sk_buff *skb) +static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) { struct packet_type *ptype, *pt_prev; rx_handler_func_t *rx_handler; @@ -3466,24 +3466,11 @@ static int __netif_receive_skb(struct sk_buff *skb) bool deliver_exact = false; int ret = NET_RX_DROP; __be16 type; - unsigned long pflags = current->flags; net_timestamp_check(!netdev_tstamp_prequeue, skb); trace_netif_receive_skb(skb); - /* - * PFMEMALLOC skbs are special, they should - * - be delivered to SOCK_MEMALLOC sockets only - * - stay away from userspace - * - have bounded memory usage - * - * Use PF_MEMALLOC as this saves us from propagating the allocation - * context down to all allocation sites. - */ - if (sk_memalloc_socks() && skb_pfmemalloc(skb)) - current->flags |= PF_MEMALLOC; - /* if we've gotten here through NAPI, check netpoll */ if (netpoll_receive_skb(skb)) goto out; @@ -3517,7 +3504,7 @@ another_round: } #endif - if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + if (pfmemalloc) goto skip_taps; list_for_each_entry_rcu(ptype, &ptype_all, list) { @@ -3536,8 +3523,7 @@ skip_taps: ncls: #endif - if (sk_memalloc_socks() && skb_pfmemalloc(skb) - && !skb_pfmemalloc_protocol(skb)) + if (pfmemalloc && !skb_pfmemalloc_protocol(skb)) goto drop; if (vlan_tx_tag_present(skb)) { @@ -3607,7 +3593,31 @@ drop: unlock: rcu_read_unlock(); out: - tsk_restore_flags(current, pflags, PF_MEMALLOC); + return ret; +} + +static int __netif_receive_skb(struct sk_buff *skb) +{ + int ret; + + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) { + unsigned long pflags = current->flags; + + /* + * PFMEMALLOC skbs are special, they should + * - be delivered to SOCK_MEMALLOC sockets only + * - stay away from userspace + * - have bounded memory usage + * + * Use PF_MEMALLOC as this saves us from propagating the allocation + * context down to all allocation sites. + */ + current->flags |= PF_MEMALLOC; + ret = __netif_receive_skb_core(skb, true); + tsk_restore_flags(current, pflags, PF_MEMALLOC); + } else + ret = __netif_receive_skb_core(skb, false); + return ret; } -- cgit v1.2.3 From 04f39047af2a6df64b763ea5a271db24879d0391 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 8 Feb 2013 18:16:19 +0100 Subject: nl80211/cfg80211: add radar detection command/event Add new NL80211_CMD_RADAR_DETECT, which starts the Channel Availability Check (CAC). This command will also notify the usermode about events (CAC finished, CAC aborted, radar detected, NOP finished). Once radar detection has started it should continuously monitor for radars as long as the channel is active. This patch enables DFS for AP mode in nl80211/cfg80211. Based on original patch by Victor Goldenshtein Signed-off-by: Simon Wunderlich [remove WIPHY_FLAG_HAS_RADAR_DETECT again -- my mistake] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 46 +++++++++++++++ include/uapi/linux/nl80211.h | 61 +++++++++++++++++++ net/wireless/chan.c | 129 ++++++++++++++++++++++++++++++++++++++++- net/wireless/core.c | 3 + net/wireless/core.h | 28 +++++++++ net/wireless/mlme.c | 120 ++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 135 +++++++++++++++++++++++++++++++++++++++++-- net/wireless/nl80211.h | 7 +++ net/wireless/reg.c | 3 + net/wireless/scan.c | 10 ---- net/wireless/trace.h | 45 +++++++++++++++ 11 files changed, 570 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7e6569e1f16f..ee11a3db730b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -114,6 +114,9 @@ enum ieee80211_channel_flags { #define IEEE80211_CHAN_NO_HT40 \ (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) +#define IEEE80211_DFS_MIN_CAC_TIME_MS 60000 +#define IEEE80211_DFS_MIN_NOP_TIME_MS (30 * 60 * 1000) + /** * struct ieee80211_channel - channel definition * @@ -134,6 +137,9 @@ enum ieee80211_channel_flags { * to enable this, this is useful only on 5 GHz band. * @orig_mag: internal use * @orig_mpwr: internal use + * @dfs_state: current state of this channel. Only relevant if radar is required + * on this channel. + * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered. */ struct ieee80211_channel { enum ieee80211_band band; @@ -146,6 +152,8 @@ struct ieee80211_channel { bool beacon_found; u32 orig_flags; int orig_mag, orig_mpwr; + enum nl80211_dfs_state dfs_state; + unsigned long dfs_state_entered; }; /** @@ -569,6 +577,7 @@ struct cfg80211_acl_data { * @p2p_opp_ps: P2P opportunistic PS * @acl: ACL configuration used by the drivers which has support for * MAC address based access control + * @radar_required: set if radar detection is required */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -586,6 +595,7 @@ struct cfg80211_ap_settings { u8 p2p_ctwindow; bool p2p_opp_ps; const struct cfg80211_acl_data *acl; + bool radar_required; }; /** @@ -1909,6 +1919,8 @@ struct cfg80211_gtk_rekey_data { * this new list replaces the existing one. Driver has to clear its ACL * when number of MAC addresses entries is passed as 0. Drivers which * advertise the support for MAC based ACL have to implement this callback. + * + * @start_radar_detection: Start radar detection in the driver. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2132,6 +2144,10 @@ struct cfg80211_ops { int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_acl_data *params); + + int (*start_radar_detection)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef); }; /* @@ -2715,6 +2731,8 @@ struct cfg80211_cached_keys; * beacons, 0 when not valid * @address: The address for this device, valid only if @netdev is %NULL * @p2p_started: true if this is a P2P Device that has been started + * @cac_started: true if DFS channel availability check has been started + * @cac_start_time: timestamp (jiffies) when the dfs state was entered. */ struct wireless_dev { struct wiphy *wiphy; @@ -2766,6 +2784,9 @@ struct wireless_dev { u32 ap_unexpected_nlportid; + bool cac_started; + unsigned long cac_start_time; + #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { @@ -3754,6 +3775,31 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); +/** + * cfg80211_radar_event - radar detection event + * @wiphy: the wiphy + * @chandef: chandef for the current channel + * @gfp: context flags + * + * This function is called when a radar is detected on the current chanenl. + */ +void cfg80211_radar_event(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, gfp_t gfp); + +/** + * cfg80211_cac_event - Channel availability check (CAC) event + * @netdev: network device + * @event: type of event + * @gfp: context flags + * + * This function is called when a Channel availability check (CAC) is finished + * or aborted. This must be called to notify the completion of a CAC process, + * also by full-MAC drivers. + */ +void cfg80211_cac_event(struct net_device *netdev, + enum nl80211_radar_event event, gfp_t gfp); + + /** * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer * @dev: network device diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5309b34930ea..90b7af86f392 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -603,6 +603,14 @@ * command is used in AP/P2P GO mode. Driver has to make sure to clear its * ACL list during %NL80211_CMD_STOP_AP. * + * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once + * a radar is detected or the channel availability scan (CAC) has finished + * or was aborted, or a radar was detected, usermode will be notified with + * this event. This command is also used to notify userspace about radars + * while operating on this channel. + * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the + * event. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -755,6 +763,8 @@ enum nl80211_commands { NL80211_CMD_SET_MAC_ACL, + NL80211_CMD_RADAR_DETECT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1342,6 +1352,9 @@ enum nl80211_commands { * number of MAC addresses that a device can support for MAC * ACL. * + * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, + * contains a value of enum nl80211_radar_event (u32). + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1620,6 +1633,8 @@ enum nl80211_attrs { NL80211_ATTR_MAC_ACL_MAX, + NL80211_ATTR_RADAR_EVENT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2022,6 +2037,10 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS + * (enum nl80211_dfs_state) + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * this channel is in this DFS state. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2034,6 +2053,8 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_IBSS, NL80211_FREQUENCY_ATTR_RADAR, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + NL80211_FREQUENCY_ATTR_DFS_STATE, + NL80211_FREQUENCY_ATTR_DFS_TIME, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -3489,4 +3510,44 @@ enum nl80211_acl_policy { NL80211_ACL_POLICY_DENY_UNLESS_LISTED, }; +/** + * enum nl80211_radar_event - type of radar event for DFS operation + * + * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace + * about detected radars or success of the channel available check (CAC) + * + * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is + * now unusable. + * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, + * the channel is now available. + * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no + * change to the channel status. + * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is + * over, channel becomes usable. + */ +enum nl80211_radar_event { + NL80211_RADAR_DETECTED, + NL80211_RADAR_CAC_FINISHED, + NL80211_RADAR_CAC_ABORTED, + NL80211_RADAR_NOP_FINISHED, +}; + +/** + * enum nl80211_dfs_state - DFS states for channels + * + * Channel states used by the DFS code. + * + * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability + * check (CAC) must be performed before using it for AP or IBSS. + * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * is therefore marked as not available. + * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + */ + +enum nl80211_dfs_state { + NL80211_DFS_USABLE, + NL80211_DFS_UNAVAILABLE, + NL80211_DFS_AVAILABLE, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 396373f3ec26..810c23cfb894 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -147,6 +147,32 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c, } } +static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) +{ + int width; + + switch (c->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + width = 20; + break; + case NL80211_CHAN_WIDTH_40: + width = 40; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_80: + width = 80; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + break; + default: + WARN_ON_ONCE(1); + return -1; + } + return width; +} + const struct cfg80211_chan_def * cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2) @@ -192,6 +218,93 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, } EXPORT_SYMBOL(cfg80211_chandef_compatible); +static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq, + u32 bandwidth, + enum nl80211_dfs_state dfs_state) +{ + struct ieee80211_channel *c; + u32 freq; + + for (freq = center_freq - bandwidth/2 + 10; + freq <= center_freq + bandwidth/2 - 10; + freq += 20) { + c = ieee80211_get_channel(wiphy, freq); + if (!c || !(c->flags & IEEE80211_CHAN_RADAR)) + continue; + + c->dfs_state = dfs_state; + c->dfs_state_entered = jiffies; + } +} + +void cfg80211_set_dfs_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state) +{ + int width; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return; + + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return; + + cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1, + width, dfs_state); + + if (!chandef->center_freq2) + return; + cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2, + width, dfs_state); +} + +static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, + u32 center_freq, + u32 bandwidth) +{ + struct ieee80211_channel *c; + u32 freq; + + for (freq = center_freq - bandwidth/2 + 10; + freq <= center_freq + bandwidth/2 - 10; + freq += 20) { + c = ieee80211_get_channel(wiphy, freq); + if (!c) + return -EINVAL; + + if (c->flags & IEEE80211_CHAN_RADAR) + return 1; + } + return 0; +} + + +int cfg80211_chandef_dfs_required(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef) +{ + int width; + int r; + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return -EINVAL; + + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return -EINVAL; + + r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, + width); + if (r) + return r; + + if (!chandef->center_freq2) + return 0; + + return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, + width); +} + static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, u32 prohibited_flags) @@ -203,7 +316,16 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, freq <= center_freq + bandwidth/2 - 10; freq += 20) { c = ieee80211_get_channel(wiphy, freq); - if (!c || c->flags & prohibited_flags) + if (!c) + return false; + + /* check for radar flags */ + if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) && + (c->dfs_state != NL80211_DFS_AVAILABLE)) + return false; + + /* check for the other flags */ + if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) return false; } @@ -344,7 +466,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (wdev->beacon_interval) { + if (wdev->cac_started) { + *chan = wdev->channel; + *chanmode = CHAN_MODE_SHARED; + } else if (wdev->beacon_interval) { *chan = wdev->channel; *chanmode = CHAN_MODE_SHARED; } diff --git a/net/wireless/core.c b/net/wireless/core.c index f0a1bbe95cff..922002105062 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -324,6 +324,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); + INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, + cfg80211_dfs_channels_update_work); #ifdef CONFIG_CFG80211_WEXT rdev->wiphy.wext = &cfg80211_wext_handler; #endif @@ -695,6 +697,7 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); flush_work(&rdev->event_work); + cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); if (rdev->wowlan && rdev->ops->set_wakeup) rdev_set_wakeup(rdev, false); diff --git a/net/wireless/core.h b/net/wireless/core.h index 949c9573d8d7..3aec0e429d8a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -86,6 +86,8 @@ struct cfg80211_registered_device { struct cfg80211_wowlan *wowlan; + struct delayed_work dfs_update_channels_wk; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); @@ -431,6 +433,22 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chanmode, u8 radar_detect); +/** + * cfg80211_chandef_dfs_required - checks if radar detection is required + * @wiphy: the wiphy to validate against + * @chandef: the channel definition to check + * Return: 1 if radar detection is required, 0 if it is not, < 0 on error + */ +int cfg80211_chandef_dfs_required(struct wiphy *wiphy, + const struct cfg80211_chan_def *c); + +void cfg80211_set_dfs_state(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state); + +void cfg80211_dfs_channels_update_work(struct work_struct *work); + + static inline int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, @@ -457,6 +475,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, chan, chanmode, 0); } +static inline unsigned int elapsed_jiffies_msecs(unsigned long start) +{ + unsigned long end = jiffies; + + if (end >= start) + return jiffies_to_msecs(end - start); + + return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); +} + void cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8e6920728c43..caddca35d686 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -987,3 +987,123 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); + +void cfg80211_dfs_channels_update_work(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct cfg80211_registered_device *rdev; + struct cfg80211_chan_def chandef; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *c; + struct wiphy *wiphy; + bool check_again = false; + unsigned long timeout, next_time = 0; + int bandid, i; + + delayed_work = container_of(work, struct delayed_work, work); + rdev = container_of(delayed_work, struct cfg80211_registered_device, + dfs_update_channels_wk); + wiphy = &rdev->wiphy; + + mutex_lock(&cfg80211_mutex); + for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) { + sband = wiphy->bands[bandid]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + c = &sband->channels[i]; + + if (c->dfs_state != NL80211_DFS_UNAVAILABLE) + continue; + + timeout = c->dfs_state_entered + + IEEE80211_DFS_MIN_NOP_TIME_MS; + + if (time_after_eq(jiffies, timeout)) { + c->dfs_state = NL80211_DFS_USABLE; + cfg80211_chandef_create(&chandef, c, + NL80211_CHAN_NO_HT); + + nl80211_radar_notify(rdev, &chandef, + NL80211_RADAR_NOP_FINISHED, + NULL, GFP_ATOMIC); + continue; + } + + if (!check_again) + next_time = timeout - jiffies; + else + next_time = min(next_time, timeout - jiffies); + check_again = true; + } + } + mutex_unlock(&cfg80211_mutex); + + /* reschedule if there are other channels waiting to be cleared again */ + if (check_again) + queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, + next_time); +} + + +void cfg80211_radar_event(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + unsigned long timeout; + + trace_cfg80211_radar_event(wiphy, chandef); + + /* only set the chandef supplied channel to unavailable, in + * case the radar is detected on only one of multiple channels + * spanned by the chandef. + */ + cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); + + timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); + queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, + timeout); + + nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); +} +EXPORT_SYMBOL(cfg80211_radar_event); + +void cfg80211_cac_event(struct net_device *netdev, + enum nl80211_radar_event event, gfp_t gfp) +{ + struct wireless_dev *wdev = netdev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct cfg80211_chan_def chandef; + unsigned long timeout; + + trace_cfg80211_cac_event(netdev, event); + + if (WARN_ON(!wdev->cac_started)) + return; + + if (WARN_ON(!wdev->channel)) + return; + + cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT); + + switch (event) { + case NL80211_RADAR_CAC_FINISHED: + timeout = wdev->cac_start_time + + msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); + WARN_ON(!time_after_eq(jiffies, timeout)); + cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE); + break; + case NL80211_RADAR_CAC_ABORTED: + break; + default: + WARN_ON(1); + return; + } + wdev->cac_started = false; + + nl80211_radar_notify(rdev, &chandef, event, netdev, gfp); +} +EXPORT_SYMBOL(cfg80211_cac_event); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d29a461b4981..c1e18ccf4049 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -552,9 +552,16 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) goto nla_put_failure; - if ((chan->flags & IEEE80211_CHAN_RADAR) && - nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) - goto nla_put_failure; + if (chan->flags & IEEE80211_CHAN_RADAR) { + u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered); + if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) + goto nla_put_failure; + if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE, + chan->dfs_state)) + goto nla_put_failure; + if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time)) + goto nla_put_failure; + } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, DBM_TO_MBM(chan->max_power))) @@ -2775,6 +2782,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_ap_settings params; int err; + u8 radar_detect_width = 0; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) @@ -2893,9 +2901,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) return -EINVAL; + err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); + if (err < 0) + return err; + if (err) { + radar_detect_width = BIT(params.chandef.width); + params.radar_required = true; + } + mutex_lock(&rdev->devlist_mtx); - err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan, - CHAN_MODE_SHARED); + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + params.chandef.chan, + CHAN_MODE_SHARED, + radar_detect_width); mutex_unlock(&rdev->devlist_mtx); if (err) @@ -5055,6 +5073,54 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb, return err; } +static int nl80211_start_radar_detection(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_chan_def chandef; + int err; + + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; + + if (wdev->cac_started) + return -EBUSY; + + err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef); + if (err < 0) + return err; + + if (err == 0) + return -EINVAL; + + if (chandef.chan->dfs_state != NL80211_DFS_USABLE) + return -EINVAL; + + if (!rdev->ops->start_radar_detection) + return -EOPNOTSUPP; + + mutex_lock(&rdev->devlist_mtx); + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + chandef.chan, CHAN_MODE_SHARED, + BIT(chandef.width)); + if (err) + goto err_locked; + + err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); + if (!err) { + wdev->channel = chandef.chan; + wdev->cac_started = true; + wdev->cac_start_time = jiffies; + } +err_locked: + mutex_unlock(&rdev->devlist_mtx); + + return err; +} + static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, u32 seq, int flags, struct cfg80211_registered_device *rdev, @@ -8305,6 +8371,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_RADAR_DETECT, + .doit = nl80211_start_radar_detection, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -9501,6 +9575,57 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_radar_notify(struct cfg80211_registered_device *rdev, + struct cfg80211_chan_def *chandef, + enum nl80211_radar_event event, + struct net_device *netdev, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + + /* NOP and radar events don't need a netdev parameter */ + if (netdev) { + struct wireless_dev *wdev = netdev->ieee80211_ptr; + + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) + goto nla_put_failure; + } + + if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event)) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) + goto nla_put_failure; + + 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); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2acba8477e9d..b061da4919e1 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -108,6 +108,13 @@ 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); + +void +nl80211_radar_notify(struct cfg80211_registered_device *rdev, + struct cfg80211_chan_def *chandef, + enum nl80211_radar_event event, + struct net_device *netdev, gfp_t gfp); + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 08d3da2c70ab..e97d5b071ab6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -884,6 +884,9 @@ static void handle_channel(struct wiphy *wiphy, return; } + chan->dfs_state = NL80211_DFS_USABLE; + chan->dfs_state_entered = jiffies; + chan->beacon_found = false; chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); chan->max_antenna_gain = diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d0fc6da2d097..f0d9b5154bab 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1210,16 +1210,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, } } -static inline unsigned int elapsed_jiffies_msecs(unsigned long start) -{ - unsigned long end = jiffies; - - if (end >= start) - return jiffies_to_msecs(end - start); - - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); -} - static char * ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, struct cfg80211_internal_bss *bss, char *current_ev, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index c9cafb0ea95f..b7a531380e19 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2051,6 +2051,21 @@ TRACE_EVENT(cfg80211_reg_can_beacon, WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ); +TRACE_EVENT(cfg80211_chandef_dfs_required, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + TRACE_EVENT(cfg80211_ch_switch_notify, TP_PROTO(struct net_device *netdev, struct cfg80211_chan_def *chandef), @@ -2067,6 +2082,36 @@ TRACE_EVENT(cfg80211_ch_switch_notify, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) ); +TRACE_EVENT(cfg80211_radar_event, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), + TP_STRUCT__entry( + WIPHY_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(cfg80211_cac_event, + TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt), + TP_ARGS(netdev, evt), + TP_STRUCT__entry( + NETDEV_ENTRY + __field(enum nl80211_radar_event, evt) + ), + TP_fast_assign( + NETDEV_ASSIGN; + __entry->evt = evt; + ), + TP_printk(NETDEV_PR_FMT ", event: %d", + NETDEV_PR_ARG, __entry->evt) +); + DECLARE_EVENT_CLASS(cfg80211_rx_evt, TP_PROTO(struct net_device *netdev, const u8 *addr), TP_ARGS(netdev, addr), -- cgit v1.2.3 From 164eb02d070af987890e1db1c12b8ae0394b19f7 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 8 Feb 2013 18:16:20 +0100 Subject: mac80211: add radar detection command/event Add command to trigger radar detection in the driver/FW. Once radar detection is started it should continuously monitor for radars as long as the channel active. If radar is detected usermode notified with 'radar detected' event. Scanning and remain on channel functionality must be disabled while doing radar detection/scanning, and vice versa. Based on original patch by Victor Goldenshtein Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- include/net/mac80211.h | 14 ++++++++++++++ net/mac80211/cfg.c | 36 +++++++++++++++++++++++++++++++++++- net/mac80211/chan.c | 33 +++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 14 ++++++++++++++ net/mac80211/iface.c | 12 ++++++++++++ net/mac80211/main.c | 13 +++++++++++++ net/mac80211/mlme.c | 13 +++++++++++++ net/mac80211/pm.c | 2 ++ net/mac80211/scan.c | 3 +++ net/mac80211/trace.h | 19 +++++++++++++++++++ net/mac80211/util.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 204 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0eaa9092364b..7241962f9f13 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -147,10 +147,12 @@ struct ieee80211_low_level_stats { * enum ieee80211_chanctx_change - change flag for channel context * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed + * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1), + IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2), }; /** @@ -165,6 +167,7 @@ enum ieee80211_chanctx_change { * @rx_chains_dynamic: The number of RX chains that must be enabled * after RTS/CTS handshake to receive SMPS MIMO transmissions; * this will always be >= @rx_chains_static. + * @radar_enabled: whether radar detection is enabled on this channel. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. */ @@ -173,6 +176,8 @@ struct ieee80211_chanctx_conf { u8 rx_chains_static, rx_chains_dynamic; + bool radar_enabled; + u8 drv_priv[0] __aligned(sizeof(void *)); }; @@ -967,6 +972,7 @@ enum ieee80211_smps_mode { * * @channel: the channel to tune to * @channel_type: the channel (HT) type + * @radar_enabled: whether radar detection is enabled * * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, @@ -993,6 +999,7 @@ struct ieee80211_conf { struct ieee80211_channel *channel; enum nl80211_channel_type channel_type; + bool radar_enabled; enum ieee80211_smps_mode smps_mode; }; @@ -3944,6 +3951,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); +/** + * ieee80211_radar_detected - inform that a radar was detected + * + * @hw: pointer as obtained from ieee80211_alloc_hw() + */ +void ieee80211_radar_detected(struct ieee80211_hw *hw); + /** * ieee80211_chswitch_done - Complete channel switch process * @vif: &struct ieee80211_vif pointer from the add_interface callback. diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e3dec80cf617..0969978c2d92 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -928,6 +928,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, /* TODO: make hostapd tell us what it wants */ sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = sdata->local->rx_chains; + sdata->radar_required = params->radar_required; err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, IEEE80211_CHANCTX_SHARED); @@ -2395,7 +2396,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, INIT_LIST_HEAD(&roc->dependents); /* if there's one pending or we're scanning, queue this one */ - if (!list_empty(&local->roc_list) || local->scanning) + if (!list_empty(&local->roc_list) || + local->scanning || local->radar_detect_enabled) goto out_check_combine; /* if not HW assist, just queue & schedule work */ @@ -2645,6 +2647,37 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, return ieee80211_cancel_roc(local, cookie, false); } +static int ieee80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + unsigned long timeout; + int err; + + if (!list_empty(&local->roc_list) || local->scanning) + return -EBUSY; + + /* whatever, but channel contexts should not complain about that one */ + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = local->rx_chains; + sdata->radar_required = true; + + mutex_lock(&local->iflist_mtx); + err = ieee80211_vif_use_channel(sdata, chandef, + IEEE80211_CHANCTX_SHARED); + mutex_unlock(&local->iflist_mtx); + if (err) + return err; + + timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); + ieee80211_queue_delayed_work(&sdata->local->hw, + &sdata->dfs_cac_timer_work, timeout); + + return 0; +} + static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, unsigned int wait, const u8 *buf, size_t len, @@ -3350,4 +3383,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_et_stats = ieee80211_get_et_stats, .get_et_strings = ieee80211_get_et_strings, .get_channel = ieee80211_cfg_get_channel, + .start_radar_detection = ieee80211_start_radar_detection, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 038f249966d6..2e6faeda22ad 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -193,6 +193,7 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, if (ctx->refcount > 0) { ieee80211_recalc_chanctx_chantype(sdata->local, ctx); ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); } } @@ -216,6 +217,37 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ieee80211_free_chanctx(local, ctx); } +void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *chanctx) +{ + struct ieee80211_sub_if_data *sdata; + bool radar_enabled = false; + + lockdep_assert_held(&local->chanctx_mtx); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->radar_required) { + radar_enabled = true; + break; + } + } + rcu_read_unlock(); + + if (radar_enabled == chanctx->conf.radar_enabled) + return; + + chanctx->conf.radar_enabled = radar_enabled; + local->radar_detect_enabled = chanctx->conf.radar_enabled; + + if (!local->use_chanctx) { + local->hw.conf.radar_enabled = chanctx->conf.radar_enabled; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + } + + drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); +} + void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx) { @@ -331,6 +363,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, } ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); out: mutex_unlock(&local->chanctx_mtx); return ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 76cdcfcd614c..0e0a9776be39 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -722,6 +722,9 @@ struct ieee80211_sub_if_data { int user_power_level; /* in dBm */ int ap_power_level; /* in dBm */ + bool radar_required; + struct delayed_work dfs_cac_timer_work; + /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for @@ -942,6 +945,10 @@ struct ieee80211_local { /* wowlan is enabled -- don't reconfig on resume */ bool wowlan; + /* DFS/radar detection is enabled */ + bool radar_detect_enabled; + struct work_struct radar_detected_work; + /* number of RX chains the hardware has */ u8 rx_chains; @@ -1606,6 +1613,13 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx); +void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *chanctx); + +void ieee80211_dfs_cac_timer(unsigned long data); +void ieee80211_dfs_cac_timer_work(struct work_struct *work); +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); +void ieee80211_dfs_radar_detected_work(struct work_struct *work); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 40ff0307d089..e9223ce2bf99 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -749,6 +749,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->recalc_smps); + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); + + if (sdata->wdev.cac_started) { + mutex_lock(&local->iflist_mtx); + ieee80211_vif_release_channel(sdata); + mutex_unlock(&local->iflist_mtx); + cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); + } + /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; @@ -1513,6 +1523,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, spin_lock_init(&sdata->cleanup_stations_lock); INIT_LIST_HEAD(&sdata->cleanup_stations); INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk); + INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work, + ieee80211_dfs_cac_timer_work); for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 38b3468bc515..9cdbc774cfd7 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -621,6 +621,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->restart_work, ieee80211_restart_work); + INIT_WORK(&local->radar_detected_work, + ieee80211_dfs_radar_detected_work); + INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); local->smps_mode = IEEE80211_SMPS_OFF; @@ -713,6 +716,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) return -EINVAL; + + /* DFS currently not supported with channel context drivers */ + for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { + const struct ieee80211_iface_combination *comb; + + comb = &local->hw.wiphy->iface_combinations[i]; + + if (comb->radar_detect_widths) + return -EINVAL; + } } /* Only HW csum features are currently compatible with mac80211 */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index efb22763d56d..7d4cde7af98e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1212,6 +1212,19 @@ void ieee80211_dynamic_ps_timer(unsigned long data) ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } +void ieee80211_dfs_cac_timer_work(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct ieee80211_sub_if_data *sdata = + container_of(delayed_work, struct ieee80211_sub_if_data, + dfs_cac_timer_work); + + ieee80211_vif_release_channel(sdata); + + cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); +} + /* MLME */ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 53801d20176d..d0275f34bf70 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -38,6 +38,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_scan_cancel(local); + ieee80211_dfs_cac_cancel(local); + if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 6d0b89e4aa31..43a45cf00e06 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -351,6 +351,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) static bool ieee80211_can_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { + if (local->radar_detect_enabled) + return false; + if (!list_empty(&local->roc_list)) return false; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 0bdd7aeb8958..1183c4a4fee5 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1862,6 +1862,25 @@ TRACE_EVENT(drv_set_default_unicast_key, LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx) ); +TRACE_EVENT(api_radar_detected, + TP_PROTO(struct ieee80211_local *local), + + TP_ARGS(local), + + TP_STRUCT__entry( + LOCAL_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT " radar detected", + LOCAL_PR_ARG + ) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6cb71a350edd..218cb52f2b59 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2133,3 +2133,49 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, return ts; } + +void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) { + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); + + if (sdata->wdev.cac_started) { + ieee80211_vif_release_channel(sdata); + cfg80211_cac_event(sdata->dev, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); + } + } + mutex_unlock(&local->iflist_mtx); +} + +void ieee80211_dfs_radar_detected_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, radar_detected_work); + struct cfg80211_chan_def chandef; + + ieee80211_dfs_cac_cancel(local); + + if (local->use_chanctx) + /* currently not handled */ + WARN_ON(1); + else { + cfg80211_chandef_create(&chandef, local->hw.conf.channel, + local->hw.conf.channel_type); + cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); + } +} + +void ieee80211_radar_detected(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + trace_api_radar_detected(local); + + ieee80211_queue_work(hw, &local->radar_detected_work); +} +EXPORT_SYMBOL(ieee80211_radar_detected); -- cgit v1.2.3 From 397a7a24ef8c2967a3e8090013f9c54714110c48 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Wed, 13 Feb 2013 16:49:15 +0100 Subject: mac80211: free ps->bc_buf skbs on vlan device stop When the vlan device is removed, ps->bc_buf processing can no longer send its frames. Signed-off-by: Michael Braun Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e9223ce2bf99..86c83084542a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -680,6 +680,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, *tmp; u32 hw_reconf_flags = 0; int i, flushed; + struct ps_data *ps; clear_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -768,6 +769,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); + } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + /* remove all packets in parent bc_buf pointing to this dev */ + ps = &sdata->bss->ps; + + spin_lock_irqsave(&ps->bc_buf.lock, flags); + skb_queue_walk_safe(&ps->bc_buf, skb, tmp) { + if (skb->dev == sdata->dev) { + __skb_unlink(skb, &ps->bc_buf); + local->total_ps_buffered--; + ieee80211_free_txskb(&local->hw, skb); + } + } + spin_unlock_irqrestore(&ps->bc_buf.lock, flags); } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { ieee80211_mgd_stop(sdata); } -- cgit v1.2.3 From de74a1d9032f4d37ea453ad2a647e1aff4cd2591 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Wed, 13 Feb 2013 16:49:16 +0100 Subject: mac80211: fix WPA with VLAN on AP side with ps-sta When sending a broadcast while at least on of the connected stations is sleeping, it gets queued and send after a DTIM beacon is sent. If the packet was to be sent on a vlan interface, the vif used for dequeing from the per-bss queue does not hold the per-vlan sdata. The correct sdata is required to use the correct per-vlan broadcast/multicast key. This patch fixes this by restoring the per-vlan sdata using the skb->dev entry. Signed-off-by: Michael Braun Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f476aa6a771d..71385bfc15d6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2785,6 +2785,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, cpu_to_le16(IEEE80211_FCTL_MOREDATA); } + sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); if (!ieee80211_tx_prepare(sdata, &tx, skb)) break; dev_kfree_skb_any(skb); -- cgit v1.2.3 From 39886b618aba3c39e650c191d601e26ec581ce0f Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 13 Feb 2013 12:14:19 -0800 Subject: mac80211: consolidate MBSS change notification A few mesh utility functions will call ieee80211_bss_info_change_notify(), and then the caller might notify the driver of the same change again. Avoid this redundancy by propagating the BSS changes and generally calling bss_info_change_notify() once per change. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 20 +++++++++++--------- net/mac80211/mesh.c | 2 +- net/mac80211/mesh.h | 10 +++++----- net/mac80211/mesh_plink.c | 33 ++++++++++++++++++--------------- net/mac80211/mesh_ps.c | 27 ++++++++++++++++++++------- 5 files changed, 55 insertions(+), 37 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0969978c2d92..f4db30624f67 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1262,9 +1262,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH + u32 changed = 0; if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) { - u32 changed = 0; - switch (params->plink_state) { case NL80211_PLINK_ESTAB: if (sta->plink_state != NL80211_PLINK_ESTAB) @@ -1273,8 +1272,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->plink_state = params->plink_state; ieee80211_mps_sta_status_update(sta); - ieee80211_mps_set_sta_local_pm(sta, - sdata->u.mesh.mshcfg.power_mode); + changed |= ieee80211_mps_set_sta_local_pm(sta, + sdata->u.mesh.mshcfg.power_mode); break; case NL80211_PLINK_LISTEN: case NL80211_PLINK_BLOCKED: @@ -1288,26 +1287,29 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->plink_state = params->plink_state; ieee80211_mps_sta_status_update(sta); - ieee80211_mps_local_status_update(sdata); + changed |= + ieee80211_mps_local_status_update(sdata); break; default: /* nothing */ break; } - ieee80211_bss_info_change_notify(sdata, changed); } else { switch (params->plink_action) { case PLINK_ACTION_OPEN: - mesh_plink_open(sta); + changed |= mesh_plink_open(sta); break; case PLINK_ACTION_BLOCK: - mesh_plink_block(sta); + changed |= mesh_plink_block(sta); break; } } if (params->local_pm) - ieee80211_mps_set_sta_local_pm(sta, params->local_pm); + changed |= + ieee80211_mps_set_sta_local_pm(sta, + params->local_pm); + ieee80211_bss_info_change_notify(sdata, changed); #endif } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 2bf0158c3f82..0adec3d539d3 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -675,7 +675,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(local, band); - ieee80211_mps_local_status_update(sdata); + changed |= ieee80211_mps_local_status_update(sdata); ieee80211_bss_info_change_notify(sdata, changed); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 3b9d862744ba..7ad035f0cacc 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -245,9 +245,9 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); /* mesh power save */ -void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); -void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, - enum nl80211_mesh_power_mode pm); +u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); +u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, + enum nl80211_mesh_power_mode pm); void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_hdr *hdr); @@ -289,8 +289,8 @@ bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); u32 mesh_plink_deactivate(struct sta_info *sta); -int mesh_plink_open(struct sta_info *sta); -void mesh_plink_block(struct sta_info *sta); +u32 mesh_plink_open(struct sta_info *sta); +u32 mesh_plink_block(struct sta_info *sta); void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 56c9b318a97e..a4c7a7e98d14 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -202,7 +202,7 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) mesh_path_flush_by_nexthop(sta); ieee80211_mps_sta_status_update(sta); - ieee80211_mps_local_status_update(sdata); + changed |= ieee80211_mps_local_status_update(sdata); return changed; } @@ -494,6 +494,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems) { struct sta_info *sta; + u32 changed = 0; sta = mesh_sta_info_get(sdata, hw_addr, elems); if (!sta) @@ -504,11 +505,12 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks && rssi_threshold_check(sta, sdata)) - mesh_plink_open(sta); + changed = mesh_plink_open(sta); ieee80211_mps_frame_release(sta, elems); out: rcu_read_unlock(); + ieee80211_bss_info_change_notify(sdata, changed); } static void mesh_plink_timer(unsigned long data) @@ -621,13 +623,14 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) add_timer(&sta->plink_timer); } -int mesh_plink_open(struct sta_info *sta) +u32 mesh_plink_open(struct sta_info *sta) { __le16 llid; struct ieee80211_sub_if_data *sdata = sta->sdata; + u32 changed; if (!test_sta_flag(sta, WLAN_STA_AUTH)) - return -EPERM; + return 0; spin_lock_bh(&sta->lock); get_random_bytes(&llid, 2); @@ -635,7 +638,7 @@ int mesh_plink_open(struct sta_info *sta) if (sta->plink_state != NL80211_PLINK_LISTEN && sta->plink_state != NL80211_PLINK_BLOCKED) { spin_unlock_bh(&sta->lock); - return -EBUSY; + return 0; } sta->plink_state = NL80211_PLINK_OPN_SNT; mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); @@ -645,15 +648,15 @@ int mesh_plink_open(struct sta_info *sta) sta->sta.addr); /* set the non-peer mode to active during peering */ - ieee80211_mps_local_status_update(sdata); + changed = ieee80211_mps_local_status_update(sdata); - return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, - sta->sta.addr, llid, 0, 0); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, + sta->sta.addr, llid, 0, 0); + return changed; } -void mesh_plink_block(struct sta_info *sta) +u32 mesh_plink_block(struct sta_info *sta) { - struct ieee80211_sub_if_data *sdata = sta->sdata; u32 changed; spin_lock_bh(&sta->lock); @@ -661,7 +664,7 @@ void mesh_plink_block(struct sta_info *sta) sta->plink_state = NL80211_PLINK_BLOCKED; spin_unlock_bh(&sta->lock); - ieee80211_bss_info_change_notify(sdata, changed); + return changed; } @@ -882,7 +885,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mshcfg->dot11MeshRetryTimeout); /* set the non-peer mode to active during peering */ - ieee80211_mps_local_status_update(sdata); + changed |= ieee80211_mps_local_status_update(sdata); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, @@ -978,7 +981,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); ieee80211_mps_sta_status_update(sta); - ieee80211_mps_set_sta_local_pm(sta, + changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); break; default: @@ -1020,8 +1023,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); ieee80211_mps_sta_status_update(sta); - ieee80211_mps_set_sta_local_pm(sta, - mshcfg->power_mode); + changed |= ieee80211_mps_set_sta_local_pm(sta, + mshcfg->power_mode); break; default: spin_unlock_bh(&sta->lock); diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index b677962525ed..3b7bfc01ee36 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c @@ -74,14 +74,17 @@ static void mps_qos_null_tx(struct sta_info *sta) * @sdata: local mesh subif * * sets the non-peer power mode and triggers the driver PS (re-)configuration + * Return BSS_CHANGED_BEACON if a beacon update is necessary. */ -void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) +u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct sta_info *sta; bool peering = false; int light_sleep_cnt = 0; int deep_sleep_cnt = 0; + u32 changed = 0; + enum nl80211_mesh_power_mode nonpeer_pm; rcu_read_lock(); list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { @@ -115,17 +118,26 @@ void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) */ if (peering) { mps_dbg(sdata, "setting non-peer PM to active for peering\n"); - ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; + nonpeer_pm = NL80211_MESH_POWER_ACTIVE; } else if (light_sleep_cnt || deep_sleep_cnt) { mps_dbg(sdata, "setting non-peer PM to deep sleep\n"); - ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP; + nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP; } else { mps_dbg(sdata, "setting non-peer PM to user value\n"); - ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode; + nonpeer_pm = ifmsh->mshcfg.power_mode; } + /* need update if sleep counts move between 0 and non-zero */ + if (ifmsh->nonpeer_pm != nonpeer_pm || + !ifmsh->ps_peers_light_sleep != !light_sleep_cnt || + !ifmsh->ps_peers_deep_sleep != !deep_sleep_cnt) + changed = BSS_CHANGED_BEACON; + + ifmsh->nonpeer_pm = nonpeer_pm; ifmsh->ps_peers_light_sleep = light_sleep_cnt; ifmsh->ps_peers_deep_sleep = deep_sleep_cnt; + + return changed; } /** @@ -133,9 +145,10 @@ void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata) * * @sta: mesh STA * @pm: the power mode to set + * Return BSS_CHANGED_BEACON if a beacon update is in order. */ -void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, - enum nl80211_mesh_power_mode pm) +u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, + enum nl80211_mesh_power_mode pm) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -151,7 +164,7 @@ void ieee80211_mps_set_sta_local_pm(struct sta_info *sta, if (sta->plink_state == NL80211_PLINK_ESTAB) mps_qos_null_tx(sta); - ieee80211_mps_local_status_update(sdata); + return ieee80211_mps_local_status_update(sdata); } /** -- cgit v1.2.3 From cab1c7fd8024ce896119535b5b067224b0b699aa Mon Sep 17 00:00:00 2001 From: Wojciech Dubowik Date: Thu, 14 Feb 2013 14:08:37 +0100 Subject: mac80211: fix ieee80211_sta_tx_notify for nullfunc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function ieee80211_sta_reset_conn_monitor has been resetting probe_send_count too early and nullfunc check was never called after succesfull ack. Reported-by: Magnus Cederlöf Tested-by: Magnus Cederlöf Signed-off-by: Wojciech Dubowik Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7d4cde7af98e..c156573ea3d0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1637,17 +1637,18 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, if (!ieee80211_is_data(hdr->frame_control)) return; - if (ack) - ieee80211_sta_reset_conn_monitor(sdata); - if (ieee80211_is_nullfunc(hdr->frame_control) && sdata->u.mgd.probe_send_count > 0) { if (ack) - sdata->u.mgd.probe_send_count = 0; + ieee80211_sta_reset_conn_monitor(sdata); else sdata->u.mgd.nullfunc_failed = true; ieee80211_queue_work(&sdata->local->hw, &sdata->work); + return; } + + if (ack) + ieee80211_sta_reset_conn_monitor(sdata); } static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) -- cgit v1.2.3 From 89afe614c0c737fd40eda1f8c8ef686246cf3cb6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 15:39:57 +0100 Subject: mac80211: fix auth/assoc timeout handling In my commit 1672c0e31917f49d31d30d79067103432bc20cc7 ("mac80211: start auth/assoc timeout on frame status") I broke auth/assoc timeout handling: in case we wait for the TX status, it now leaves the timeout field set to 0, which is a valid time and can compare as being before now ("jiffies"). Thus, if the work struct runs for some other reason, the auth/assoc is treated as having timed out. Fix this by introducing a separate "timeout_started" variable that tracks whether the timeout has started and is checked before timing out. Additionally, for proper TX status handling the change requires that the skb->dev pointer is set up for all the frames, so set it up for all frames in mac80211. Reported-by: Wojciech Dubowik Tested-by: Wojciech Dubowik Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 23 +++++++++++++++++++---- net/mac80211/sta_info.c | 2 ++ net/mac80211/tx.c | 2 ++ 4 files changed, 25 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0e0a9776be39..52bf954ef61c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -343,6 +343,7 @@ struct ieee80211_mgd_auth_data { u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; bool done; + bool timeout_started; u16 sae_trans, sae_status; size_t data_len; @@ -364,6 +365,7 @@ struct ieee80211_mgd_assoc_data { bool wmm, uapsd; bool have_beacon, need_beacon; bool synced; + bool timeout_started; u8 ap_ht_param; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c156573ea3d0..a7fb276d87cb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2000,6 +2000,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; + ifmgd->auth_data->timeout_started = true; run_again(ifmgd, ifmgd->auth_data->timeout); if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && @@ -2334,6 +2335,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", mgmt->sa, tu, ms); assoc_data->timeout = jiffies + msecs_to_jiffies(ms); + assoc_data->timeout_started = true; if (ms > IEEE80211_ASSOC_TIMEOUT) run_again(ifmgd, assoc_data->timeout); return RX_MGMT_NONE; @@ -2457,6 +2459,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "direct probe responded\n"); ifmgd->auth_data->tries = 0; ifmgd->auth_data->timeout = jiffies; + ifmgd->auth_data->timeout_started = true; run_again(ifmgd, ifmgd->auth_data->timeout); } } @@ -2542,6 +2545,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; + ifmgd->assoc_data->timeout_started = true; run_again(ifmgd, ifmgd->assoc_data->timeout); return; } @@ -2934,7 +2938,10 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + ifmgd->auth_data->timeout_started = true; run_again(ifmgd, auth_data->timeout); + } else { + auth_data->timeout_started = false; } return 0; @@ -2968,7 +2975,10 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; + assoc_data->timeout_started = true; run_again(&sdata->u.mgd, assoc_data->timeout); + } else { + assoc_data->timeout_started = false; } return 0; @@ -3007,6 +3017,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) } else { ifmgd->auth_data->timeout = jiffies - 1; } + ifmgd->auth_data->timeout_started = true; } else if (ifmgd->assoc_data && (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))) { @@ -3017,10 +3028,11 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) } else { ifmgd->assoc_data->timeout = jiffies - 1; } + ifmgd->assoc_data->timeout_started = true; } } - if (ifmgd->auth_data && + if (ifmgd->auth_data && ifmgd->auth_data->timeout_started && time_after(jiffies, ifmgd->auth_data->timeout)) { if (ifmgd->auth_data->done) { /* @@ -3039,10 +3051,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) cfg80211_send_auth_timeout(sdata->dev, bssid); mutex_lock(&ifmgd->mtx); } - } else if (ifmgd->auth_data) + } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) run_again(ifmgd, ifmgd->auth_data->timeout); - if (ifmgd->assoc_data && + if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started && time_after(jiffies, ifmgd->assoc_data->timeout)) { if ((ifmgd->assoc_data->need_beacon && !ifmgd->assoc_data->have_beacon) || @@ -3057,7 +3069,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) cfg80211_send_assoc_timeout(sdata->dev, bssid); mutex_lock(&ifmgd->mtx); } - } else if (ifmgd->assoc_data) + } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) run_again(ifmgd, ifmgd->assoc_data->timeout); if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | @@ -4032,6 +4044,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "waiting for beacon from %pM\n", ifmgd->bssid); assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); + assoc_data->timeout_started = true; assoc_data->need_beacon = true; } else if (beacon_ies) { const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, @@ -4047,6 +4060,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } assoc_data->have_beacon = true; assoc_data->timeout = jiffies; + assoc_data->timeout_started = true; if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf; @@ -4056,6 +4070,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } } else { assoc_data->timeout = jiffies; + assoc_data->timeout_started = true; } rcu_read_unlock(); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 19db20a58e23..fb3b5865fc39 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1120,6 +1120,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false); + skb->dev = sdata->dev; + rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (WARN_ON(!chanctx_conf)) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 71385bfc15d6..f4c89f506e9d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2818,6 +2818,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, skb_set_queue_mapping(skb, ac); skb->priority = tid; + skb->dev = sdata->dev; + /* * The other path calling ieee80211_xmit is from the tasklet, * and while we can handle concurrent transmissions locking -- cgit v1.2.3 From 5bbe754d9ecc4a09994ecd0cf3b167224e114da5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 13:50:51 +0100 Subject: mac80211: don't call bss_info_changed on p2p-device/monitor Since the idle decision rework, mac80211 started calling bss_info_changed() for the driver's monitor interface, which causes a crash for iwlwifi, but drivers generally don't expect this to happen. Therefore, avoid it. While at it, also prevent calling it in such cases and only print a warning. For the P2P Device interface the idle will no longer be called (no channel context), so also prevent that and warn on it. Reported-by: Chaitanya Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 10 ++++++++-- net/mac80211/driver-ops.h | 17 ++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 2e6faeda22ad..d91ccfcaa6d1 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -137,7 +137,10 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_txpower(sdata); sdata->vif.bss_conf.idle = false; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); + + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && + sdata->vif.type != NL80211_IFTYPE_MONITOR) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); return 0; } @@ -186,7 +189,10 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); sdata->vif.bss_conf.idle = true; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); + + if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && + sdata->vif.type != NL80211_IFTYPE_MONITOR) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); drv_unassign_vif_chanctx(local, sdata, ctx); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2b08b9982d06..ee56d0779d8b 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -207,13 +207,16 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { might_sleep(); - WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_ENABLED) && - sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT); - WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE && - changed & ~BSS_CHANGED_IDLE); + if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED) && + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) + return; + + if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || + sdata->vif.type == NL80211_IFTYPE_MONITOR)) + return; check_sdata_in_driver(sdata); -- cgit v1.2.3 From 5b36ebd8249f403c7edf7cf68d68e9a0d0f55243 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 14:21:45 +0100 Subject: mac80211: always unblock CSA queue stop when disconnecting In some cases when disconnecting after (or during?) CSA the queues might not recover, and then the only way to recover is reloading the module. Fix this by always unblocking the queue CSA reason when disconnecting. Cc: stable@vger.kernel.org Reported-by: Jan-Michael Brummer Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a7fb276d87cb..99be3b61a225 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1819,6 +1819,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&ifmgd->mtx); /* @@ -1859,8 +1861,6 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.csa_connection_drop_work); - ieee80211_wake_queues_by_reason(&sdata->local->hw, - IEEE80211_QUEUE_STOP_REASON_CSA); __ieee80211_disconnect(sdata); } -- cgit v1.2.3 From 441a33baf1805861354fb9e3149c000311b6996f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 12 Feb 2013 16:27:04 +0100 Subject: mac80211: don't pick up WPA vendor IE There's no use for it, WPA is entirely handled in wpa_supplicant in userspace, so don't pick the IE. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 -- net/mac80211/util.c | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 52bf954ef61c..8f9564460cb0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1175,7 +1175,6 @@ struct ieee802_11_elems { struct ieee80211_tim_ie *tim; u8 *ibss_params; u8 *challenge; - u8 *wpa; u8 *rsn; u8 *erp_info; u8 *ext_supp_rates; @@ -1208,7 +1207,6 @@ struct ieee802_11_elems { u8 tim_len; u8 ibss_params_len; u8 challenge_len; - u8 wpa_len; u8 rsn_len; u8 erp_info_len; u8 ext_supp_rates_len; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 218cb52f2b59..b231bc2ed740 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -739,11 +739,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, if (calc_crc) crc = crc32_be(crc, pos - 2, elen + 2); - if (pos[3] == 1) { - /* OUI Type 1 - WPA IE */ - elems->wpa = pos; - elems->wpa_len = elen; - } else if (elen >= 5 && pos[3] == 2) { + if (elen >= 5 && pos[3] == 2) { /* OUI Type 2 - WMM IE */ if (pos[4] == 0) { elems->wmm_info = pos; -- cgit v1.2.3 From 65f704a52ec5db356c58f8ba53a31d3aef02737f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 17:39:53 +0100 Subject: mac80211: use spin_lock_bh() for tim_lock There's no need to use _irqsave() as the lock is never used in interrupt context. This also fixes a problem in the iwlwifi MVM driver that calls spin_unlock_bh() within its set_tim() callback. Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 5 ++--- net/mac80211/tx.c | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index fb3b5865fc39..0794b9018ed4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -571,7 +571,6 @@ void sta_info_recalc_tim(struct sta_info *sta) { struct ieee80211_local *local = sta->local; struct ps_data *ps; - unsigned long flags; bool indicate_tim = false; u8 ignore_for_tim = sta->sta.uapsd_queues; int ac; @@ -628,7 +627,7 @@ void sta_info_recalc_tim(struct sta_info *sta) } done: - spin_lock_irqsave(&local->tim_lock, flags); + spin_lock_bh(&local->tim_lock); if (indicate_tim) __bss_tim_set(ps->tim, id); @@ -641,7 +640,7 @@ void sta_info_recalc_tim(struct sta_info *sta) local->tim_in_locked_section = false; } - spin_unlock_irqrestore(&local->tim_lock, flags); + spin_unlock_bh(&local->tim_lock); } static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f4c89f506e9d..7d8c629f1e6a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2364,11 +2364,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, if (local->tim_in_locked_section) { __ieee80211_beacon_add_tim(sdata, ps, skb); } else { - unsigned long flags; - - spin_lock_irqsave(&local->tim_lock, flags); + spin_lock(&local->tim_lock); __ieee80211_beacon_add_tim(sdata, ps, skb); - spin_unlock_irqrestore(&local->tim_lock, flags); + spin_unlock(&local->tim_lock); } return 0; -- cgit v1.2.3 From 03395003bf8ac813b1a0ac9299b3496484caf228 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 18:03:00 +0100 Subject: mac80211: use spin_lock_bh() for TKIP lock Since no driver calls the TKIP functions from interrupt context, there's no need to use spin_lock_irqsave(). Just use spin_lock_bh() (and spin_lock() in the TX path where we're in a BH or they're already disabled.) Signed-off-by: Johannes Berg --- net/mac80211/tkip.c | 10 ++++------ net/mac80211/wpa.c | 5 ++--- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 57e14d59e12f..3ed801d90f1e 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -177,12 +177,11 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf, struct ieee80211_key *key = (struct ieee80211_key *) container_of(keyconf, struct ieee80211_key, conf); struct tkip_ctx *ctx = &key->u.tkip.tx; - unsigned long flags; - spin_lock_irqsave(&key->u.tkip.txlock, flags); + spin_lock_bh(&key->u.tkip.txlock); ieee80211_compute_tkip_p1k(key, iv32); memcpy(p1k, ctx->p1k, sizeof(ctx->p1k)); - spin_unlock_irqrestore(&key->u.tkip.txlock, flags); + spin_unlock_bh(&key->u.tkip.txlock); } EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv); @@ -208,12 +207,11 @@ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); u32 iv32 = get_unaligned_le32(&data[4]); u16 iv16 = data[2] | (data[0] << 8); - unsigned long flags; - spin_lock_irqsave(&key->u.tkip.txlock, flags); + spin_lock_bh(&key->u.tkip.txlock); ieee80211_compute_tkip_p1k(key, iv32); tkip_mixing_phase2(tk, ctx, iv16, p2k); - spin_unlock_irqrestore(&key->u.tkip.txlock, flags); + spin_unlock_bh(&key->u.tkip.txlock); } EXPORT_SYMBOL(ieee80211_get_tkip_p2k); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c175ee866ff4..c7c6d644486f 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -181,7 +181,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - unsigned long flags; unsigned int hdrlen; int len, tail; u8 *pos; @@ -216,12 +215,12 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; /* Increase IV for the frame */ - spin_lock_irqsave(&key->u.tkip.txlock, flags); + spin_lock(&key->u.tkip.txlock); key->u.tkip.tx.iv16++; if (key->u.tkip.tx.iv16 == 0) key->u.tkip.tx.iv32++; pos = ieee80211_tkip_add_iv(pos, key); - spin_unlock_irqrestore(&key->u.tkip.txlock, flags); + spin_unlock(&key->u.tkip.txlock); /* hwaccel - with software IV */ if (info->control.hw_key) -- cgit v1.2.3 From 4a34215ef7487b1cbd783e7cc485eb03de893bd0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 11:58:58 +0100 Subject: mac80211: pass station to ieee80211_vht_cap_ie_to_sta_vht_cap Like with HT, make things a bit simpler in future patches by passing the station to ieee80211_vht_cap_ie_to_sta_vht_cap() instead of the vht_cap pointer. Also disable VHT here if HT isn't supported. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 +-- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/mlme.c | 3 +-- net/mac80211/vht.c | 8 +++++--- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9b179309e073..9c9496d01120 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1257,8 +1257,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - params->vht_capa, - &sta->sta.vht_cap); + params->vht_capa, sta); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8f9564460cb0..9206d43ca572 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1430,7 +1430,8 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_vht_cap *vht_cap_ie, - struct ieee80211_sta_vht_cap *vht_cap); + struct sta_info *sta); + /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ac1374a8c30a..58b6e67ffbed 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2217,8 +2217,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - elems.vht_cap_elem, - &sta->sta.vht_cap); + elems.vht_cap_elem, sta); rate_control_rate_init(sta); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index f311388aeedf..1606aa165d5f 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -15,13 +15,15 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_vht_cap *vht_cap_ie, - struct ieee80211_sta_vht_cap *vht_cap) + struct sta_info *sta) { - if (WARN_ON_ONCE(!vht_cap)) - return; + struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; memset(vht_cap, 0, sizeof(*vht_cap)); + if (!sta->sta.ht_cap.ht_supported) + return; + if (!vht_cap_ie || !sband->vht_cap.vht_supported) return; -- cgit v1.2.3 From e1a0c6b3a4b27ed5f21291d0bbee2167ec201ef5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 11:47:44 +0100 Subject: mac80211: stop toggling IEEE80211_HT_CAP_SUP_WIDTH_20_40 For VHT, many more bandwidth changes are possible. As a first step, stop toggling the IEEE80211_HT_CAP_SUP_WIDTH_20_40 flag in the HT capabilities and instead introduce a bandwidth field indicating the currently usable bandwidth to transmit to the station. Of course, make all drivers use it. To achieve this, make ieee80211_ht_cap_ie_to_sta_ht_cap() get the station as an argument, rather than the new capabilities, so it can set up the new bandwidth field. If the station is a VHT station and VHT bandwidth is in use, also set the bandwidth accordingly. Doing this allows us to get rid of the supports_40mhz flag as the HT capabilities now reflect the true capability instead of the current setting. While at it, also fix ieee80211_ht_cap_ie_to_sta_ht_cap() to not ignore HT cap overrides when MCS TX isn't supported (not that it really happens...) Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/rc.c | 2 +- drivers/net/wireless/iwlwifi/dvm/agn.h | 2 +- drivers/net/wireless/iwlwifi/dvm/rs.c | 6 +- drivers/net/wireless/iwlwifi/dvm/sta.c | 19 ++----- drivers/net/wireless/iwlwifi/mvm/rs.c | 24 ++------ drivers/net/wireless/rtlwifi/base.c | 7 +-- drivers/net/wireless/rtlwifi/rc.c | 5 +- drivers/net/wireless/rtlwifi/rtl8192ce/hw.c | 6 +- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 3 +- drivers/net/wireless/rtlwifi/rtl8192de/hw.c | 3 +- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 3 +- drivers/net/wireless/rtlwifi/rtl8192se/hw.c | 3 +- drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 3 +- drivers/net/wireless/rtlwifi/rtl8723ae/hw.c | 3 +- drivers/net/wireless/rtlwifi/rtl8723ae/trx.c | 3 +- drivers/net/wireless/ti/wl18xx/main.c | 2 +- include/net/mac80211.h | 24 +++++++- net/mac80211/cfg.c | 3 +- net/mac80211/ht.c | 82 +++++++++++++++++----------- net/mac80211/ibss.c | 23 +++----- net/mac80211/ieee80211_i.h | 5 +- net/mac80211/mesh_plink.c | 6 +- net/mac80211/mlme.c | 22 ++++---- net/mac80211/rc80211_minstrel_ht.c | 19 +++---- net/mac80211/rx.c | 21 +++---- net/mac80211/sta_info.h | 4 -- net/mac80211/vht.c | 39 +++++++++++++ 27 files changed, 184 insertions(+), 158 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 714558d1ba78..54150b6a39ae 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1204,7 +1204,7 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta) caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; else if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; - if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { caps |= WLAN_RC_40_FLAG; if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) caps |= WLAN_RC_SGI_FLAG; diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index f41ae79e6bc0..41ec27cb6efe 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -338,7 +338,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta_ht_cap *ht_cap); + struct ieee80211_sta *sta); static inline int iwl_sta_id(struct ieee80211_sta *sta) { diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index a131227c49e9..b25de02964f9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1305,7 +1305,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; @@ -1361,7 +1361,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; rate_mask = lq_sta->active_mimo3_rate; - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; @@ -1410,7 +1410,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index ab768045696b..6deab38c7aee 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -173,7 +173,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta_ht_cap *ht_cap) + struct ieee80211_sta *sta) { if (!ctx->ht.enabled || !ctx->ht.is_40mhz) return false; @@ -183,20 +183,11 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, return false; #endif - /* - * Remainder of this function checks ht_cap, but if it's - * NULL then we can do HT40 (special case for RXON) - */ - if (!ht_cap) + /* special case for RXON */ + if (!sta) return true; - if (!ht_cap->ht_supported) - return false; - - if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - return false; - - return true; + return sta->bandwidth >= IEEE80211_STA_RX_BW_40; } static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, @@ -246,7 +237,7 @@ static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, *flags |= cpu_to_le32( (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) *flags |= STA_FLG_HT40_EN_MSK; } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 60a4291ca221..8ba36e5e4b67 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1209,23 +1209,9 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, return new_rate; } -static bool iwl_is_ht40_tx_allowed(struct iwl_mvm *mvm, - struct ieee80211_sta_ht_cap *ht_cap) +static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta) { - /* - * Remainder of this function checks ht_cap, but if it's - * NULL then we can do HT40 (special case for RXON) - */ - if (!ht_cap) - return true; - - if (!ht_cap->ht_supported) - return false; - - if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - return false; - - return true; + return sta->bandwidth >= IEEE80211_STA_RX_BW_40; } /* @@ -1258,7 +1244,7 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; - if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; @@ -1311,7 +1297,7 @@ static int rs_switch_to_mimo3(struct iwl_mvm *mvm, tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; rate_mask = lq_sta->active_mimo3_rate; - if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; @@ -1356,7 +1342,7 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; - if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap)) + if (iwl_is_ht40_tx_allowed(sta)) tbl->is_ht40 = 1; else tbl->is_ht40 = 0; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 4494d130b37c..84ea04dab303 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -523,8 +523,8 @@ static void _rtl_query_shortgi(struct ieee80211_hw *hw, if (mac->opmode == NL80211_IFTYPE_STATION) bw_40 = mac->bw_40; else if (mac->opmode == NL80211_IFTYPE_AP || - mac->opmode == NL80211_IFTYPE_ADHOC) - bw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + mac->opmode == NL80211_IFTYPE_ADHOC) + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; if (bw_40 && sgi_40) tcb_desc->use_shortgi = true; @@ -634,8 +634,7 @@ static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw, return; if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { - if (!(sta->ht_cap.ht_supported) || - !(sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + if (sta->bandwidth == IEEE80211_STA_RX_BW_20) return; } else if (mac->opmode == NL80211_IFTYPE_STATION) { if (!mac->bw_40 || !(sta->ht_cap.ht_supported)) diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index 204f46c4510d..c12a866e1261 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -116,9 +116,8 @@ static void _rtl_rc_rate_set_series(struct rtl_priv *rtlpriv, if (txrc->short_preamble) rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; if (mac->opmode == NL80211_IFTYPE_AP || - mac->opmode == NL80211_IFTYPE_ADHOC) { - if (sta && (sta->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + mac->opmode == NL80211_IFTYPE_ADHOC) { + if (sta && (sta->bandwidth >= IEEE80211_STA_RX_BW_40)) rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; } else { if (mac->bw_40) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index d1f34f6ffbdf..1b65db7fd651 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -1846,9 +1846,9 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; - u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) - ? 1 : 0; - u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; + u8 curshortgi_40mhz = curtxbw_40mhz && + (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index da0e9022a99a..b9b1a6e0b16e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -626,8 +626,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) - bw_40 = sta->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index f4051f4f0390..aa5b42521bb4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -1970,8 +1970,7 @@ static void rtl92de_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; - u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) - ? 1 : 0; + u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index cdb570ffb4b5..941080e03c06 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -574,8 +574,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) - bw_40 = sta->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index 28526a7361f5..084e7773bce2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -2085,8 +2085,7 @@ static void rtl92se_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index = 0; - u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) - ? 1 : 0; + u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index f8431a3c2c9d..7b0a2e75b8b8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -621,8 +621,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) - bw_40 = sta->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index 149804816ac4..9a0c71c2e15e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -1866,8 +1866,7 @@ static void rtl8723ae_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; - u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) - ? 1 : 0; + u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index b1fd2b328abf..ac081297db50 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -395,8 +395,7 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, } else if (mac->opmode == NL80211_IFTYPE_AP || mac->opmode == NL80211_IFTYPE_ADHOC) { if (sta) - bw_40 = sta->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40; + bw_40 = sta->bandwidth >= IEEE80211_STA_RX_BW_40; } seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a10b7a7a215a..da3ef1b10a9c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1374,7 +1374,7 @@ static void wl18xx_sta_rc_update(struct wl1271 *wl, struct ieee80211_sta *sta, u32 changed) { - bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + bool wide = sta->bandwidth >= IEEE80211_STA_RX_BW_40; wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7241962f9f13..1e3b4f730397 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1196,6 +1196,24 @@ enum ieee80211_sta_state { IEEE80211_STA_AUTHORIZED, }; +/** + * enum ieee80211_sta_rx_bandwidth - station RX bandwidth + * @IEEE80211_STA_RX_BW_20: station can only receive 20 MHz + * @IEEE80211_STA_RX_BW_40: station can receive up to 40 MHz + * @IEEE80211_STA_RX_BW_80: station can receive up to 80 MHz + * @IEEE80211_STA_RX_BW_160: station can receive up to 160 MHz + * (including 80+80 MHz) + * + * Implementation note: 20 must be zero to be initialized + * correctly, the values must be sorted. + */ +enum ieee80211_sta_rx_bandwidth { + IEEE80211_STA_RX_BW_20 = 0, + IEEE80211_STA_RX_BW_40, + IEEE80211_STA_RX_BW_80, + IEEE80211_STA_RX_BW_160, +}; + /** * struct ieee80211_sta - station table entry * @@ -1218,6 +1236,7 @@ enum ieee80211_sta_state { * @uapsd_queues: bitmap of queues configured for uapsd. Only valid * if wme is supported. * @max_sp: max Service Period. Only valid if wme is supported. + * @bandwidth: current bandwidth the station can receive with */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1228,6 +1247,7 @@ struct ieee80211_sta { bool wme; u8 uapsd_queues; u8 max_sp; + enum ieee80211_sta_rx_bandwidth bandwidth; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); @@ -2086,7 +2106,9 @@ enum ieee80211_frame_release_type { * enum ieee80211_rate_control_changed - flags to indicate what changed * * @IEEE80211_RC_BW_CHANGED: The bandwidth that can be used to transmit - * to this station changed. + * to this station changed. The actual bandwidth is in the station + * information -- for HT20/40 the IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * flag changes, for HT and VHT the bandwidth field changes. * @IEEE80211_RC_SMPS_CHANGED: The SMPS state of the station changed. * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer * changed (in IBSS mode) due to discovering more information about diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9c9496d01120..c3869ba42343 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1252,8 +1252,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (params->ht_capa) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, - params->ht_capa, - &sta->sta.ht_cap); + params->ht_capa, sta); if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 9e7560becbbb..a64b4f0d373f 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); int i; + if (!ht_cap->ht_supported) + return; + if (sdata->vif.type != NL80211_IFTYPE_STATION) { /* AP interfaces call this code when adding new stations, * so just silently ignore non station interfaces. @@ -89,22 +92,23 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, } -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, +bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, - struct ieee80211_sta_ht_cap *ht_cap) + struct sta_info *sta) { + struct ieee80211_sta_ht_cap ht_cap; u8 ampdu_info, tx_mcs_set_cap; int i, max_tx_streams; + bool changed; + enum ieee80211_sta_rx_bandwidth bw; - BUG_ON(!ht_cap); - - memset(ht_cap, 0, sizeof(*ht_cap)); + memset(&ht_cap, 0, sizeof(ht_cap)); if (!ht_cap_ie || !sband->ht_cap.ht_supported) - return; + goto apply; - ht_cap->ht_supported = true; + ht_cap.ht_supported = true; /* * The bits listed in this expression should be @@ -112,7 +116,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, * advertises more then we can't use those thus * we mask them out. */ - ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & + ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & (sband->ht_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | @@ -121,44 +125,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40)); - /* Unset 40 MHz if we're not using a 40 MHz channel */ - switch (sdata->vif.bss_conf.chandef.width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40; - ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - break; - case NL80211_CHAN_WIDTH_40: - case NL80211_CHAN_WIDTH_80: - case NL80211_CHAN_WIDTH_80P80: - case NL80211_CHAN_WIDTH_160: - break; - } - /* * The STBC bits are asymmetric -- if we don't have * TX then mask out the peer's RX and vice versa. */ if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) - ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; + ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) - ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; + ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; ampdu_info = ht_cap_ie->ampdu_params_info; - ht_cap->ampdu_factor = + ht_cap.ampdu_factor = ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; - ht_cap->ampdu_density = + ht_cap.ampdu_density = (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; /* own MCS TX capabilities */ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; /* Copy peer MCS TX capabilities, the driver might need them. */ - ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; + ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; /* can we TX with MCS rates? */ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) - return; + goto apply; /* Counting from 0, therefore +1 */ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) @@ -176,25 +166,53 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, * - remainder are multiple spatial streams using unequal modulation */ for (i = 0; i < max_tx_streams; i++) - ht_cap->mcs.rx_mask[i] = + ht_cap.mcs.rx_mask[i] = sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; i < IEEE80211_HT_MCS_MASK_LEN; i++) - ht_cap->mcs.rx_mask[i] = + ht_cap.mcs.rx_mask[i] = sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; /* handle MCS rate 32 too */ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) - ht_cap->mcs.rx_mask[32/8] |= 1; + ht_cap.mcs.rx_mask[32/8] |= 1; + apply: /* * If user has specified capability over-rides, take care * of that here. */ - ieee80211_apply_htcap_overrides(sdata, ht_cap); + ieee80211_apply_htcap_overrides(sdata, &ht_cap); + + changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + + memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); + + switch (sdata->vif.bss_conf.chandef.width) { + default: + WARN_ON_ONCE(1); + /* fall through */ + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + bw = IEEE80211_STA_RX_BW_20; + break; + case NL80211_CHAN_WIDTH_40: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + break; + } + + if (bw != sta->sta.bandwidth) + changed = true; + sta->sta.bandwidth = bw; + + return changed; } void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 2db1f2b90bfe..40b71dfcc79d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -496,33 +496,26 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (sta && elems->ht_operation && elems->ht_cap_elem && sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { /* we both use HT */ - struct ieee80211_sta_ht_cap sta_ht_cap_new; + struct ieee80211_ht_cap htcap_ie; struct cfg80211_chan_def chandef; ieee80211_ht_oper_to_chandef(channel, elems->ht_operation, &chandef); - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, - elems->ht_cap_elem, - &sta_ht_cap_new); + memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); /* * fall back to HT20 if we don't use or use * the other extension channel */ - if (chandef.width != NL80211_CHAN_WIDTH_40 || - cfg80211_get_chandef_type(&chandef) != + if (cfg80211_get_chandef_type(&chandef) != sdata->u.ibss.channel_type) - sta_ht_cap_new.cap &= - ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - - if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new, - sizeof(sta_ht_cap_new))) { - memcpy(&sta->sta.ht_cap, &sta_ht_cap_new, - sizeof(sta_ht_cap_new)); - rates_updated = true; - } + htcap_ie.cap_info &= + cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + + rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap( + sdata, sband, &htcap_ie, sta); } if (sta && rates_updated) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9206d43ca572..6b41d7787a5a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1384,10 +1384,10 @@ void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, /* HT */ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap); -void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, +bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_ht_cap *ht_cap_ie, - struct ieee80211_sta_ht_cap *ht_cap); + struct sta_info *sta); void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, u16 initiator, u16 reason_code); @@ -1431,6 +1431,7 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_vht_cap *vht_cap_ie, struct sta_info *sta); +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index a4c7a7e98d14..7765139b24aa 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -373,8 +373,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, if (elems->ht_cap_elem && sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, - elems->ht_cap_elem, - &sta->sta.ht_cap); + elems->ht_cap_elem, sta); else memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); @@ -383,8 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, if (!(elems->ht_operation->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) - sta->sta.ht_cap.cap &= - ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, elems->ht_operation, &chandef); if (sta->ch_width != chandef.width) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 58b6e67ffbed..be1147286801 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -219,19 +219,20 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); - WARN_ON_ONCE(!sta); + if (WARN_ON_ONCE(!sta)) { + mutex_unlock(&local->sta_mtx); + return changed; + } - if (sta && !sta->supports_40mhz) + if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) disable_40 = true; - if (sta && (!reconfig || - (disable_40 != !(sta->sta.ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) { - + if (!reconfig || + disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { if (disable_40) - sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; else - sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); rate_control_rate_update(local, sband, sta, IEEE80211_RC_BW_CHANGED); @@ -2210,10 +2211,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, - elems.ht_cap_elem, &sta->sta.ht_cap); - - sta->supports_40mhz = - sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; + elems.ht_cap_elem, sta); if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 6e35dcd2aa2f..6176c71d47ff 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -848,8 +848,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, IEEE80211_HT_CAP_SM_PS_SHIFT; for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { - u16 req = 0; - mi->groups[i].supported = 0; if (i == MINSTREL_CCK_GROUP) { minstrel_ht_update_cck(mp, mi, sband, sta); @@ -857,16 +855,17 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, } if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - req |= IEEE80211_HT_CAP_SGI_40; - else - req |= IEEE80211_HT_CAP_SGI_20; + if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (!(sta_cap & IEEE80211_HT_CAP_SGI_40)) + continue; + } else { + if (!(sta_cap & IEEE80211_HT_CAP_SGI_20)) + continue; + } } - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - - if ((sta_cap & req) != req) + if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH && + sta->bandwidth < IEEE80211_STA_RX_BW_40) continue; /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b5f1bba7ffe1..8a861a50b12f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2410,26 +2410,21 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: { struct ieee80211_supported_band *sband; u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth; - bool old_40mhz, new_40mhz; + enum ieee80211_sta_rx_bandwidth new_bw; /* If it doesn't support 40 MHz it can't change ... */ - if (!rx->sta->supports_40mhz) + if (!(rx->sta->sta.ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40)) goto handled; - old_40mhz = rx->sta->sta.ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40; - new_40mhz = chanwidth == IEEE80211_HT_CHANWIDTH_ANY; + if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ) + new_bw = IEEE80211_STA_RX_BW_20; + else + new_bw = ieee80211_sta_cur_vht_bw(rx->sta); - if (old_40mhz == new_40mhz) + if (rx->sta->sta.bandwidth == new_bw) goto handled; - if (new_40mhz) - rx->sta->sta.ht_cap.cap |= - IEEE80211_HT_CAP_SUP_WIDTH_20_40; - else - rx->sta->sta.ht_cap.cap &= - ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband = rx->local->hw.wiphy->bands[status->band]; rate_control_rate_update(local, sband, rx->sta, diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 350578c396c0..03c42f8d39f0 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -296,8 +296,6 @@ struct sta_ampdu_mlme { * @sta: station information we share with the driver * @sta_state: duplicates information about station state (for debug) * @beacon_loss_count: number of times beacon loss has triggered - * @supports_40mhz: tracks whether the station advertised 40 MHz support - * as we overwrite its HT parameters with the currently used value * @rcu_head: RCU head used for freeing this station struct */ struct sta_info { @@ -403,8 +401,6 @@ struct sta_info { unsigned int lost_packets; unsigned int beacon_loss_count; - bool supports_40mhz; - /* keep last! */ struct ieee80211_sta sta; }; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 1606aa165d5f..0fc9a2fb8d53 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -27,6 +27,10 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!vht_cap_ie || !sband->vht_cap.vht_supported) return; + /* A VHT STA must support 40 MHz */ + if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + return; + vht_cap->vht_supported = true; vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); @@ -34,4 +38,39 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, /* Copy peer MCS info, the driver might need them. */ memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, sizeof(struct ieee80211_vht_mcs_info)); + + sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); +} + +enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u32 cap = sta->sta.vht_cap.cap; + + if (!sta->sta.vht_cap.vht_supported) + return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + + /* TODO: handle VHT opmode notification data */ + + switch (sdata->vif.bss_conf.chandef.width) { + default: + WARN_ON_ONCE(1); + /* fall through */ + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + case NL80211_CHAN_WIDTH_160: + if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) + return IEEE80211_STA_RX_BW_160; + /* fall through */ + case NL80211_CHAN_WIDTH_80P80: + if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) + return IEEE80211_STA_RX_BW_160; + /* fall through */ + case NL80211_CHAN_WIDTH_80: + return IEEE80211_STA_RX_BW_80; + } } -- cgit v1.2.3 From 8921d04e8df7475d733d853564bdb001e83bf33f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Dec 2012 18:26:42 +0100 Subject: mac80211: track number of spatial streams With VHT, a station can change the number of spatial streams it can receive on the fly, not unlike spatial multiplexing in HT. Prepare for that by tracking the maximum number of spatial streams it can receive when the connection is established. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/rate.h | 2 ++ net/mac80211/vht.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1e3b4f730397..a608ab9879b4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1237,6 +1237,10 @@ enum ieee80211_sta_rx_bandwidth { * if wme is supported. * @max_sp: max Service Period. Only valid if wme is supported. * @bandwidth: current bandwidth the station can receive with + * @rx_nss: in HT/VHT, the maximum number of spatial streams the + * station can receive at the moment, changed by operating mode + * notifications and capabilities. The value is only valid after + * the station moves to associated state. */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1247,6 +1251,7 @@ struct ieee80211_sta { bool wme; u8 uapsd_queues; u8 max_sp; + u8 rx_nss; enum ieee80211_sta_rx_bandwidth bandwidth; /* must be last */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6b41d7787a5a..3b13af4e6c49 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1432,6 +1432,7 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_vht_cap *vht_cap_ie, struct sta_info *sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); +void ieee80211_sta_set_rx_nss(struct sta_info *sta); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 301386dabf88..d35a5dd3fb13 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -68,6 +68,8 @@ static inline void rate_control_rate_init(struct sta_info *sta) sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; rcu_read_unlock(); + ieee80211_sta_set_rx_nss(sta); + ref->ops->rate_init(ref->priv, sband, ista, priv_sta); set_sta_flag(sta, WLAN_STA_RATE_CONTROL); } diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 0fc9a2fb8d53..67436e3efbbd 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -74,3 +74,44 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) return IEEE80211_STA_RX_BW_80; } } + +void ieee80211_sta_set_rx_nss(struct sta_info *sta) +{ + u8 ht_rx_nss = 0, vht_rx_nss = 0; + + /* if we received a notification already don't overwrite it */ + if (sta->sta.rx_nss) + return; + + if (sta->sta.ht_cap.ht_supported) { + if (sta->sta.ht_cap.mcs.rx_mask[0]) + ht_rx_nss++; + if (sta->sta.ht_cap.mcs.rx_mask[1]) + ht_rx_nss++; + if (sta->sta.ht_cap.mcs.rx_mask[2]) + ht_rx_nss++; + if (sta->sta.ht_cap.mcs.rx_mask[3]) + ht_rx_nss++; + /* FIXME: consider rx_highest? */ + } + + if (sta->sta.vht_cap.vht_supported) { + int i; + u16 rx_mcs_map; + + rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map); + + for (i = 7; i >= 0; i--) { + u8 mcs = (rx_mcs_map >> (2 * i)) & 3; + + if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + vht_rx_nss = i + 1; + break; + } + } + /* FIXME: consider rx_highest? */ + } + + ht_rx_nss = max(ht_rx_nss, vht_rx_nss); + sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); +} -- cgit v1.2.3 From 0af83d3df5863224336a18c24a14fda542b712f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 27 Dec 2012 18:55:36 +0100 Subject: mac80211: handle VHT operating mode notification Handle the operating mode notification action frame. When the supported streams or the bandwidth change let the driver and rate control algorithm know. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + include/net/mac80211.h | 3 ++ net/mac80211/ht.c | 4 ++ net/mac80211/ieee80211_i.h | 3 ++ net/mac80211/rx.c | 30 +++++++++++++++ net/mac80211/sta_info.h | 4 ++ net/mac80211/vht.c | 93 +++++++++++++++++++++++++++++++++++++++++----- 7 files changed, 128 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 67c1a6c45837..12b5996533ec 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1301,6 +1301,7 @@ struct ieee80211_vht_operation { #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 #define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000C #define IEEE80211_VHT_CAP_RXLDPC 0x00000010 #define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020 #define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a608ab9879b4..b7fb311e83f4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2118,11 +2118,14 @@ enum ieee80211_frame_release_type { * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer * changed (in IBSS mode) due to discovering more information about * the peer. + * @IEEE80211_RC_NSS_CHANGED: N_SS (number of spatial streams) was changed + * by the peer */ enum ieee80211_rate_control_changed { IEEE80211_RC_BW_CHANGED = BIT(0), IEEE80211_RC_SMPS_CHANGED = BIT(1), IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2), + IEEE80211_RC_NSS_CHANGED = BIT(3), }; /** diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index a64b4f0d373f..797969bc26e1 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -212,6 +212,10 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, changed = true; sta->sta.bandwidth = bw; + sta->cur_max_bandwidth = + ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + return changed; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3b13af4e6c49..4947c91c6c86 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1433,6 +1433,9 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct sta_info *sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); void ieee80211_sta_set_rx_nss(struct sta_info *sta); +void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, u8 opmode, + enum ieee80211_band band); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 8a861a50b12f..1617e0bd4ca6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2435,6 +2435,36 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto invalid; } + break; + case WLAN_CATEGORY_VHT: + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN && + sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_ADHOC) + break; + + /* verify action code is present */ + if (len < IEEE80211_MIN_ACTION_SIZE + 1) + goto invalid; + + switch (mgmt->u.action.u.vht_opmode_notif.action_code) { + case WLAN_VHT_ACTION_OPMODE_NOTIF: { + u8 opmode; + + /* verify opmode is present */ + if (len < IEEE80211_MIN_ACTION_SIZE + 2) + goto invalid; + + opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode; + + ieee80211_vht_handle_opmode(rx->sdata, rx->sta, + opmode, status->band); + goto handled; + } + default: + break; + } break; case WLAN_CATEGORY_BACK: if (sdata->vif.type != NL80211_IFTYPE_STATION && diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 03c42f8d39f0..63dfdb5e91da 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -297,6 +297,8 @@ struct sta_ampdu_mlme { * @sta_state: duplicates information about station state (for debug) * @beacon_loss_count: number of times beacon loss has triggered * @rcu_head: RCU head used for freeing this station struct + * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, + * taken from HT/VHT capabilities or VHT operating mode notification */ struct sta_info { /* General information, mostly static */ @@ -398,6 +400,8 @@ struct sta_info { } debugfs; #endif + enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; + unsigned int lost_packets; unsigned int beacon_loss_count; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 67436e3efbbd..0951f74e7ff5 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -10,6 +10,7 @@ #include #include #include "ieee80211_i.h" +#include "rate.h" void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, @@ -39,6 +40,15 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, sizeof(struct ieee80211_vht_mcs_info)); + switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; + break; + default: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; + } + sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); } @@ -46,12 +56,13 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; u32 cap = sta->sta.vht_cap.cap; + enum ieee80211_sta_rx_bandwidth bw; - if (!sta->sta.vht_cap.vht_supported) - return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + if (!sta->sta.vht_cap.vht_supported) { + bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; - - /* TODO: handle VHT opmode notification data */ + goto check_max; + } switch (sdata->vif.bss_conf.chandef.width) { default: @@ -60,19 +71,31 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_40: - return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? + bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + break; case NL80211_CHAN_WIDTH_160: - if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) - return IEEE80211_STA_RX_BW_160; + if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) { + bw = IEEE80211_STA_RX_BW_160; + break; + } /* fall through */ case NL80211_CHAN_WIDTH_80P80: - if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) - return IEEE80211_STA_RX_BW_160; + if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { + bw = IEEE80211_STA_RX_BW_160; + break; + } /* fall through */ case NL80211_CHAN_WIDTH_80: - return IEEE80211_STA_RX_BW_80; + bw = IEEE80211_STA_RX_BW_80; } + + check_max: + if (bw > sta->cur_max_bandwidth) + bw = sta->cur_max_bandwidth; + return bw; } void ieee80211_sta_set_rx_nss(struct sta_info *sta) @@ -115,3 +138,53 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta) ht_rx_nss = max(ht_rx_nss, vht_rx_nss); sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); } + +void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, u8 opmode, + enum ieee80211_band band) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + enum ieee80211_sta_rx_bandwidth new_bw; + u32 changed = 0; + u8 nss; + + sband = local->hw.wiphy->bands[band]; + + /* ignore - no support for BF yet */ + if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) + return; + + nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; + nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; + nss += 1; + + if (sta->sta.rx_nss != nss) { + sta->sta.rx_nss = nss; + changed |= IEEE80211_RC_NSS_CHANGED; + } + + switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { + case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; + break; + case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; + break; + case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; + break; + case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; + break; + } + + new_bw = ieee80211_sta_cur_vht_bw(sta); + if (new_bw != sta->sta.bandwidth) { + sta->sta.bandwidth = new_bw; + changed |= IEEE80211_RC_NSS_CHANGED; + } + + if (changed) + rate_control_rate_update(local, sband, sta, changed); +} -- cgit v1.2.3 From 1128958dc22cbfa2562082da2621a06ba71bdce2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 17:36:12 +0100 Subject: mac80211: init HT TX data before rate control In case of connection, the station data is initialised from the beacon/probe response first and then updated from the association response. If the latter is different we update the rate control algorithm and driver. Instead of doing it this way, set the station data properly with data from the association response before initializing rate control. Also simplify the code by passing the station pointer. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index be1147286801..b97b615d5711 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -175,6 +175,7 @@ static int ecw2cw(int ecw) } static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, struct ieee80211_ht_operation *ht_oper, const u8 *bssid, bool reconfig) { @@ -182,11 +183,13 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; - struct sta_info *sta; u32 changed = 0; u16 ht_opmode; bool disable_40 = false; + if (WARN_ON_ONCE(!sta)) + return 0; + rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (WARN_ON(!chanctx_conf)) { @@ -216,28 +219,19 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) disable_40 = true; - mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, bssid); - - if (WARN_ON_ONCE(!sta)) { - mutex_unlock(&local->sta_mtx); - return changed; - } - if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) disable_40 = true; - if (!reconfig || - disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { + if (disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { if (disable_40) sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; else sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); - rate_control_rate_update(local, sband, sta, - IEEE80211_RC_BW_CHANGED); + if (reconfig) + rate_control_rate_update(local, sband, sta, + IEEE80211_RC_BW_CHANGED); } - mutex_unlock(&local->sta_mtx); ht_opmode = le16_to_cpu(ht_oper->operation_mode); @@ -2217,6 +2211,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, elems.vht_cap_elem, sta); + if (elems.ht_operation && elems.wmm_param && + !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) + changed |= ieee80211_config_ht_tx(sdata, sta, + elems.ht_operation, + cbss->bssid, false); + rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) @@ -2254,11 +2254,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_set_wmm_default(sdata, false); changed |= BSS_CHANGED_QOS; - if (elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) - changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, - cbss->bssid, false); - /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; @@ -2734,10 +2729,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, erp_valid, erp_value); + mutex_lock(&local->sta_mtx); if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) - changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, + changed |= ieee80211_config_ht_tx(sdata, + sta_info_get(sdata, bssid), + elems.ht_operation, bssid, true); + mutex_unlock(&local->sta_mtx); if (elems.country_elem && elems.pwr_constr_elem && mgmt->u.probe_resp.capab_info & -- cgit v1.2.3 From cb145022c82bde532b386c7a6f1dcef1b2bca2e5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 20:41:50 +0100 Subject: mac80211: fix HT/VHT disable flags The code to disable HT and VHT if VHT was advertised without VHT is wrong -- it accidentally uses the wrong flags. Fix that. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b97b615d5711..e6da6bf8cc27 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3588,8 +3588,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, vht_oper = NULL; sdata_info(sdata, "AP advertised VHT without HT, disabling both\n"); - sdata->flags |= IEEE80211_STA_DISABLE_HT; - sdata->flags |= IEEE80211_STA_DISABLE_VHT; + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; } } -- cgit v1.2.3 From 18942d3be0e0e67aa40550ce3266e48b51845d52 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 21:30:37 +0100 Subject: mac80211: fix ieee80211_change_chandef name This should be called ieee80211_change_chanctx() since it changes the channel context, not a chandef. Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index d91ccfcaa6d1..78dbb371f16e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -9,7 +9,7 @@ #include "ieee80211_i.h" #include "driver-ops.h" -static void ieee80211_change_chandef(struct ieee80211_local *local, +static void ieee80211_change_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct cfg80211_chan_def *chandef) { @@ -49,7 +49,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local, if (!compat) continue; - ieee80211_change_chandef(local, ctx, compat); + ieee80211_change_chanctx(local, ctx, compat); return ctx; } @@ -175,7 +175,7 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, if (WARN_ON_ONCE(!compat)) return; - ieee80211_change_chandef(local, ctx, compat); + ieee80211_change_chanctx(local, ctx, compat); } static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, -- cgit v1.2.3 From bee7f58699a406a4210ba9e0367bae7ac666abd0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 22:24:55 +0100 Subject: mac80211: handle operating mode notif in beacon/assoc response In beacons and association response frames an AP may include an operating mode notification element to advertise changes in the number of spatial streams it can receive. Handle this using the existing function that handles the action frame, but only handle NSS changes, not bandwidth changes which aren't allowed here. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/mlme.c | 26 +++++++++++++++++++++++--- net/mac80211/rx.c | 3 ++- net/mac80211/util.c | 6 ++++++ net/mac80211/vht.c | 6 +++++- 5 files changed, 38 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4947c91c6c86..892bac64a189 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1197,6 +1197,7 @@ struct ieee802_11_elems { u8 *pwr_constr_elem; u8 *quiet_elem; /* first quite element */ u8 *timeout_int; + u8 *opmode_notif; /* length of them, respectively */ u8 ssid_len; @@ -1435,7 +1436,7 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); void ieee80211_sta_set_rx_nss(struct sta_info *sta); void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, u8 opmode, - enum ieee80211_band band); + enum ieee80211_band band, bool nss_only); /* Spectrum management */ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e6da6bf8cc27..f2c6f7794f35 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2217,6 +2217,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, elems.ht_operation, cbss->bssid, false); + /* + * If an operating mode notification IE is present, override the + * NSS calculation (that would be done in rate_control_rate_init()) + * and use the # of streams from that element. + */ + if (elems.opmode_notif && + !(*elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { + u8 nss; + + nss = *elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; + nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; + nss += 1; + sta->sta.rx_nss = nss; + } + rate_control_rate_init(sta); if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) @@ -2489,6 +2504,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; + struct sta_info *sta; u32 changed = 0; bool erp_valid; u8 erp_value = 0; @@ -2728,14 +2744,18 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, le16_to_cpu(mgmt->u.beacon.capab_info), erp_valid, erp_value); - mutex_lock(&local->sta_mtx); + sta = sta_info_get(sdata, bssid); + if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) - changed |= ieee80211_config_ht_tx(sdata, - sta_info_get(sdata, bssid), + changed |= ieee80211_config_ht_tx(sdata, sta, elems.ht_operation, bssid, true); + + if (sta && elems.opmode_notif) + ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif, + rx_status->band, true); mutex_unlock(&local->sta_mtx); if (elems.country_elem && elems.pwr_constr_elem && diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1617e0bd4ca6..30f1ba6de8f3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2459,7 +2459,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode; ieee80211_vht_handle_opmode(rx->sdata, rx->sta, - opmode, status->band); + opmode, status->band, + false); goto handled; } default: diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b231bc2ed740..e24ff38606a9 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -787,6 +787,12 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, else elem_parse_failed = true; break; + case WLAN_EID_OPMODE_NOTIF: + if (elen > 0) + elems->opmode_notif = pos; + else + elem_parse_failed = true; + break; case WLAN_EID_MESH_ID: elems->mesh_id = pos; elems->mesh_id_len = elen; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 0951f74e7ff5..a9549fcc5a04 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -141,7 +141,7 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta) void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, u8 opmode, - enum ieee80211_band band) + enum ieee80211_band band, bool nss_only) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -164,6 +164,9 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, changed |= IEEE80211_RC_NSS_CHANGED; } + if (nss_only) + goto change; + switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; @@ -185,6 +188,7 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, changed |= IEEE80211_RC_NSS_CHANGED; } + change: if (changed) rate_control_rate_update(local, sband, sta, changed); } -- cgit v1.2.3 From 08e6effa1187cc5f401a1cc286ce84f0a581b35a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 23:33:32 +0100 Subject: mac80211: disable HT/VHT if AP has no HT/VHT capability Having HT/VHT operation IEs but not capability IEs leads to a strange situation where we configure the channel to an HT or VHT bandwidth and then can't actually use it. Prevent this by checking that the HT and VHT capability IEs are present as well as the operation IEs; if not, disable HT and/or VHT. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f2c6f7794f35..03f278880dba 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3589,16 +3589,22 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && sband->ht_cap.ht_supported) { - const u8 *ht_oper_ie; + const u8 *ht_oper_ie, *ht_cap; ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) ht_oper = (void *)(ht_oper_ie + 2); + + ht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); + if (!ht_cap || ht_cap[1] < sizeof(struct ieee80211_ht_cap)) { + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; + ht_oper = NULL; + } } if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && sband->vht_cap.vht_supported) { - const u8 *vht_oper_ie; + const u8 *vht_oper_ie, *vht_cap; vht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_OPERATION); @@ -3611,6 +3617,12 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; } + + vht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); + if (!vht_cap || vht_cap[1] < sizeof(struct ieee80211_vht_cap)) { + ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; + vht_oper = NULL; + } } ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, -- cgit v1.2.3 From f2d9330ee820b01e2b1caf46370bc7963d259908 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 13:15:55 +0100 Subject: mac80211: clean up channel use in ieee80211_config_ht_tx The channel use is confusing, some uses the channel context and some the bss_conf.chandef. The latter is fine, so get rid of the channel context part. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 03f278880dba..8fdf1e1aee5e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -181,7 +181,6 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; u32 changed = 0; u16 ht_opmode; @@ -190,23 +189,16 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, if (WARN_ON_ONCE(!sta)) return 0; - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (WARN_ON(!chanctx_conf)) { - rcu_read_unlock(); - return 0; - } - chan = chanctx_conf->def.chan; - rcu_read_unlock(); + chan = sdata->vif.bss_conf.chandef.chan; sband = local->hw.wiphy->bands[chan->band]; switch (sdata->vif.bss_conf.chandef.width) { case NL80211_CHAN_WIDTH_40: - if (sdata->vif.bss_conf.chandef.chan->center_freq > + if (chan->center_freq > sdata->vif.bss_conf.chandef.center_freq1 && chan->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; - if (sdata->vif.bss_conf.chandef.chan->center_freq < + if (chan->center_freq < sdata->vif.bss_conf.chandef.center_freq1 && chan->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; -- cgit v1.2.3 From 2c9b735982ee8a2d34e7eeb3e26b683f81872fdb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2013 21:37:29 +0100 Subject: mac80211: add ieee80211_vif_change_bandwidth For HT and VHT the current bandwidth can change, add the function ieee80211_vif_change_bandwidth() to take care of this. It returns a failure if the new bandwidth isn't compatible with the existing channel context, the caller has to handle that. When it happens, also inform the driver that the bandwidth changed for this virtual interface (no drivers would actually care today though.) Changing to/from HT/VHT isn't allowed though. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ++++ net/mac80211/chan.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 4 ++++ 3 files changed, 57 insertions(+) (limited to 'net') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b7fb311e83f4..54e2add8429f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -215,6 +215,9 @@ struct ieee80211_chanctx_conf { * changed (currently only in P2P client mode, GO mode will be later) * @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when * it becomes valid, managed mode only) + * @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed, + * note that this is only called when it changes after the channel + * context had been assigned. */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -238,6 +241,7 @@ enum ieee80211_bss_change { BSS_CHANGED_TXPOWER = 1<<18, BSS_CHANGED_P2P_PS = 1<<19, BSS_CHANGED_DTIM_PERIOD = 1<<20, + BSS_CHANGED_BANDWIDTH = 1<<21, /* when adding here, make sure to change ieee80211_reconfig */ }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 78dbb371f16e..78c0d90dd641 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -375,6 +375,55 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, return ret; } +int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 *changed) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + int ret; + + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + mutex_lock(&local->chanctx_mtx); + if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) { + ret = 0; + goto out; + } + + if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { + ret = -EINVAL; + goto out; + } + + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) { + ret = -EINVAL; + goto out; + } + + ctx = container_of(conf, struct ieee80211_chanctx, conf); + if (!cfg80211_chandef_compatible(&conf->def, chandef)) { + ret = -EINVAL; + goto out; + } + + sdata->vif.bss_conf.chandef = *chandef; + + ieee80211_recalc_chanctx_chantype(local, ctx); + + *changed |= BSS_CHANGED_BANDWIDTH; + ret = 0; + out: + mutex_unlock(&local->chanctx_mtx); + return ret; +} + void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) { WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 892bac64a189..d1074442f1b5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1613,6 +1613,10 @@ int __must_check ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode); +int __must_check +ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 *changed); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, -- cgit v1.2.3 From 6565ec9b58483a9706fbe888364aeceb359aeced Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 14:52:32 +0100 Subject: mac80211: move ieee80211_determine_chantype function The next patch will need it further up in the file, so move it unchanged now. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 390 ++++++++++++++++++++++++++-------------------------- 1 file changed, 195 insertions(+), 195 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8fdf1e1aee5e..50423c66c239 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -174,6 +174,201 @@ static int ecw2cw(int ecw) return (1 << ecw) - 1; } +static u32 chandef_downgrade(struct cfg80211_chan_def *c) +{ + u32 ret; + int tmp; + + switch (c->width) { + case NL80211_CHAN_WIDTH_20: + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_40: + c->width = NL80211_CHAN_WIDTH_20; + c->center_freq1 = c->chan->center_freq; + ret = IEEE80211_STA_DISABLE_40MHZ | + IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80: + tmp = (30 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; + c->width = NL80211_CHAN_WIDTH_40; + ret = IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80P80: + c->center_freq2 = 0; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + case NL80211_CHAN_WIDTH_160: + /* n_P20 */ + tmp = (70 + c->chan->center_freq - c->center_freq1)/20; + /* n_P80 */ + tmp /= 4; + c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + default: + case NL80211_CHAN_WIDTH_20_NOHT: + WARN_ON_ONCE(1); + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + } + + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + + return ret; +} + +static u32 +ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + struct ieee80211_channel *channel, + const struct ieee80211_ht_operation *ht_oper, + const struct ieee80211_vht_operation *vht_oper, + struct cfg80211_chan_def *chandef) +{ + struct cfg80211_chan_def vht_chandef; + u32 ht_cfreq, ret; + + chandef->chan = channel; + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = channel->center_freq; + chandef->center_freq2 = 0; + + if (!ht_oper || !sband->ht_cap.ht_supported) { + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + goto out; + } + + chandef->width = NL80211_CHAN_WIDTH_20; + + ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, + channel->band); + /* check that channel matches the right operating channel */ + if (channel->center_freq != ht_cfreq) { + /* + * It's possible that some APs are confused here; + * Netgear WNDR3700 sometimes reports 4 higher than + * the actual channel in association responses, but + * since we look at probe response/beacon data here + * it should be OK. + */ + sdata_info(sdata, + "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", + channel->center_freq, ht_cfreq, + ht_oper->primary_chan, channel->band); + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + goto out; + } + + /* check 40 MHz support, if we have it */ + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 += 10; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 -= 10; + break; + } + } else { + /* 40 MHz (and 80 MHz) must be supported for VHT */ + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (!vht_oper || !sband->vht_cap.vht_supported) { + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + vht_chandef.chan = channel; + vht_chandef.center_freq1 = + ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, + channel->band); + vht_chandef.center_freq2 = 0; + + if (vht_oper->center_freq_seg2_idx) + vht_chandef.center_freq2 = + ieee80211_channel_to_frequency( + vht_oper->center_freq_seg2_idx, + channel->band); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_USE_HT: + vht_chandef.width = chandef->width; + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_160; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_chandef.width = NL80211_CHAN_WIDTH_80P80; + break; + default: + sdata_info(sdata, + "AP VHT operation IE has invalid channel width (%d), disable VHT\n", + vht_oper->chan_width); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (!cfg80211_chandef_valid(&vht_chandef)) { + sdata_info(sdata, + "AP VHT information is invalid, disable VHT\n"); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + if (cfg80211_chandef_identical(chandef, &vht_chandef)) { + ret = 0; + goto out; + } + + if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { + sdata_info(sdata, + "AP VHT information doesn't match HT, disable VHT\n"); + ret = IEEE80211_STA_DISABLE_VHT; + goto out; + } + + *chandef = vht_chandef; + + ret = 0; + +out: + while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) { + if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { + ret = IEEE80211_STA_DISABLE_HT | + IEEE80211_STA_DISABLE_VHT; + goto out; + } + + ret |= chandef_downgrade(chandef); + } + + if (chandef->width != vht_chandef.width) + sdata_info(sdata, + "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); + + WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); + return ret; +} + static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_ht_operation *ht_oper, @@ -3321,201 +3516,6 @@ int ieee80211_max_network_latency(struct notifier_block *nb, return 0; } -static u32 chandef_downgrade(struct cfg80211_chan_def *c) -{ - u32 ret; - int tmp; - - switch (c->width) { - case NL80211_CHAN_WIDTH_20: - c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_40: - c->width = NL80211_CHAN_WIDTH_20; - c->center_freq1 = c->chan->center_freq; - ret = IEEE80211_STA_DISABLE_40MHZ | - IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_80: - tmp = (30 + c->chan->center_freq - c->center_freq1)/20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; - c->width = NL80211_CHAN_WIDTH_40; - ret = IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_80P80: - c->center_freq2 = 0; - c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_STA_DISABLE_80P80MHZ | - IEEE80211_STA_DISABLE_160MHZ; - break; - case NL80211_CHAN_WIDTH_160: - /* n_P20 */ - tmp = (70 + c->chan->center_freq - c->center_freq1)/20; - /* n_P80 */ - tmp /= 4; - c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; - c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_STA_DISABLE_80P80MHZ | - IEEE80211_STA_DISABLE_160MHZ; - break; - default: - case NL80211_CHAN_WIDTH_20_NOHT: - WARN_ON_ONCE(1); - c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - break; - } - - WARN_ON_ONCE(!cfg80211_chandef_valid(c)); - - return ret; -} - -static u32 -ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - struct ieee80211_channel *channel, - const struct ieee80211_ht_operation *ht_oper, - const struct ieee80211_vht_operation *vht_oper, - struct cfg80211_chan_def *chandef) -{ - struct cfg80211_chan_def vht_chandef; - u32 ht_cfreq, ret; - - chandef->chan = channel; - chandef->width = NL80211_CHAN_WIDTH_20_NOHT; - chandef->center_freq1 = channel->center_freq; - chandef->center_freq2 = 0; - - if (!ht_oper || !sband->ht_cap.ht_supported) { - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - goto out; - } - - chandef->width = NL80211_CHAN_WIDTH_20; - - ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, - channel->band); - /* check that channel matches the right operating channel */ - if (channel->center_freq != ht_cfreq) { - /* - * It's possible that some APs are confused here; - * Netgear WNDR3700 sometimes reports 4 higher than - * the actual channel in association responses, but - * since we look at probe response/beacon data here - * it should be OK. - */ - sdata_info(sdata, - "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", - channel->center_freq, ht_cfreq, - ht_oper->primary_chan, channel->band); - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - goto out; - } - - /* check 40 MHz support, if we have it */ - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - chandef->width = NL80211_CHAN_WIDTH_40; - chandef->center_freq1 += 10; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - chandef->width = NL80211_CHAN_WIDTH_40; - chandef->center_freq1 -= 10; - break; - } - } else { - /* 40 MHz (and 80 MHz) must be supported for VHT */ - ret = IEEE80211_STA_DISABLE_VHT; - goto out; - } - - if (!vht_oper || !sband->vht_cap.vht_supported) { - ret = IEEE80211_STA_DISABLE_VHT; - goto out; - } - - vht_chandef.chan = channel; - vht_chandef.center_freq1 = - ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, - channel->band); - vht_chandef.center_freq2 = 0; - - if (vht_oper->center_freq_seg2_idx) - vht_chandef.center_freq2 = - ieee80211_channel_to_frequency( - vht_oper->center_freq_seg2_idx, - channel->band); - - switch (vht_oper->chan_width) { - case IEEE80211_VHT_CHANWIDTH_USE_HT: - vht_chandef.width = chandef->width; - break; - case IEEE80211_VHT_CHANWIDTH_80MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_80; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_160; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - vht_chandef.width = NL80211_CHAN_WIDTH_80P80; - break; - default: - sdata_info(sdata, - "AP VHT operation IE has invalid channel width (%d), disable VHT\n", - vht_oper->chan_width); - ret = IEEE80211_STA_DISABLE_VHT; - goto out; - } - - if (!cfg80211_chandef_valid(&vht_chandef)) { - sdata_info(sdata, - "AP VHT information is invalid, disable VHT\n"); - ret = IEEE80211_STA_DISABLE_VHT; - goto out; - } - - if (cfg80211_chandef_identical(chandef, &vht_chandef)) { - ret = 0; - goto out; - } - - if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { - sdata_info(sdata, - "AP VHT information doesn't match HT, disable VHT\n"); - ret = IEEE80211_STA_DISABLE_VHT; - goto out; - } - - *chandef = vht_chandef; - - ret = 0; - -out: - while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, - IEEE80211_CHAN_DISABLED)) { - if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { - ret = IEEE80211_STA_DISABLE_HT | - IEEE80211_STA_DISABLE_VHT; - goto out; - } - - ret |= chandef_downgrade(chandef); - } - - if (chandef->width != vht_chandef.width) - sdata_info(sdata, - "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); - - WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); - return ret; -} - static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *cbss) { -- cgit v1.2.3 From 30eb1dc2c43039e0fe278e6f3a288de9f216d70d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 Feb 2013 15:12:14 +0100 Subject: mac80211: properly track HT/VHT operation changes A while ago, I made the mac80211 station code never change the channel type after association. This solved a number of issues but is ultimately wrong, we should react if the AP changes the HT operation IE and switches bandwidth. One of the issues is that we associate as HT40 capable, but if the AP ever switches to 40 MHz we won't be able to receive such frames because we never set our channel to 40 MHz. This addresses this and VHT operation changes. If there's a change that is incompatible with our setup, e.g. if the AP decides to change the channel entirely (and for some reason we still hear the beacon) we'll just disconnect. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 236 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 168 insertions(+), 68 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 50423c66c239..05b229e3b226 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -114,6 +114,9 @@ enum rx_mgmt_action { /* caller must call cfg80211_send_assoc_timeout() */ RX_MGMT_CFG80211_ASSOC_TIMEOUT, + + /* used when a processed beacon causes a deauth */ + RX_MGMT_CFG80211_TX_DEAUTH, }; /* utils */ @@ -234,7 +237,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_vht_operation *vht_oper, - struct cfg80211_chan_def *chandef) + struct cfg80211_chan_def *chandef, bool verbose) { struct cfg80211_chan_def vht_chandef; u32 ht_cfreq, ret; @@ -262,10 +265,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, * since we look at probe response/beacon data here * it should be OK. */ - sdata_info(sdata, - "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", - channel->center_freq, ht_cfreq, - ht_oper->primary_chan, channel->band); + if (verbose) + sdata_info(sdata, + "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", + channel->center_freq, ht_cfreq, + ht_oper->primary_chan, channel->band); ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; goto out; } @@ -319,16 +323,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, vht_chandef.width = NL80211_CHAN_WIDTH_80P80; break; default: - sdata_info(sdata, - "AP VHT operation IE has invalid channel width (%d), disable VHT\n", - vht_oper->chan_width); + if (verbose) + sdata_info(sdata, + "AP VHT operation IE has invalid channel width (%d), disable VHT\n", + vht_oper->chan_width); ret = IEEE80211_STA_DISABLE_VHT; goto out; } if (!cfg80211_chandef_valid(&vht_chandef)) { - sdata_info(sdata, - "AP VHT information is invalid, disable VHT\n"); + if (verbose) + sdata_info(sdata, + "AP VHT information is invalid, disable VHT\n"); ret = IEEE80211_STA_DISABLE_VHT; goto out; } @@ -339,8 +345,9 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { - sdata_info(sdata, - "AP VHT information doesn't match HT, disable VHT\n"); + if (verbose) + sdata_info(sdata, + "AP VHT information doesn't match HT, disable VHT\n"); ret = IEEE80211_STA_DISABLE_VHT; goto out; } @@ -361,7 +368,7 @@ out: ret |= chandef_downgrade(chandef); } - if (chandef->width != vht_chandef.width) + if (chandef->width != vht_chandef.width && verbose) sdata_info(sdata, "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); @@ -369,66 +376,128 @@ out: return ret; } -static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, - struct sta_info *sta, - struct ieee80211_ht_operation *ht_oper, - const u8 *bssid, bool reconfig) +static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + const struct ieee80211_ht_operation *ht_oper, + const struct ieee80211_vht_operation *vht_oper, + const u8 *bssid, u32 *changed) { struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - u32 changed = 0; + struct cfg80211_chan_def chandef; u16 ht_opmode; - bool disable_40 = false; + u32 flags; + enum ieee80211_sta_rx_bandwidth new_sta_bw; + int ret; - if (WARN_ON_ONCE(!sta)) + /* if HT was/is disabled, don't track any bandwidth changes */ + if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper) return 0; + /* don't check VHT if we associated as non-VHT station */ + if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) + vht_oper = NULL; + + if (WARN_ON_ONCE(!sta)) + return -EINVAL; + chan = sdata->vif.bss_conf.chandef.chan; sband = local->hw.wiphy->bands[chan->band]; - switch (sdata->vif.bss_conf.chandef.width) { + /* calculate new channel (type) based on HT/VHT operation IEs */ + flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper, + vht_oper, &chandef, false); + + /* + * Downgrade the new channel if we associated with restricted + * capabilities. For example, if we associated as a 20 MHz STA + * to a 40 MHz AP (due to regulatory, capabilities or config + * reasons) then switching to a 40 MHz channel now won't do us + * any good -- we couldn't use it with the AP. + */ + if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && + chandef.width == NL80211_CHAN_WIDTH_80P80) + flags |= chandef_downgrade(&chandef); + if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && + chandef.width == NL80211_CHAN_WIDTH_160) + flags |= chandef_downgrade(&chandef); + if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && + chandef.width > NL80211_CHAN_WIDTH_20) + flags |= chandef_downgrade(&chandef); + + if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef)) + return 0; + + sdata_info(sdata, + "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n", + ifmgd->bssid, chandef.chan->center_freq, chandef.width, + chandef.center_freq1, chandef.center_freq2); + + if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | + IEEE80211_STA_DISABLE_VHT | + IEEE80211_STA_DISABLE_40MHZ | + IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ)) || + !cfg80211_chandef_valid(&chandef)) { + sdata_info(sdata, + "AP %pM changed bandwidth in a way we can't support - disconnect\n", + ifmgd->bssid); + return -EINVAL; + } + + switch (chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + new_sta_bw = IEEE80211_STA_RX_BW_20; + break; case NL80211_CHAN_WIDTH_40: - if (chan->center_freq > - sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - disable_40 = true; - if (chan->center_freq < - sdata->vif.bss_conf.chandef.center_freq1 && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - disable_40 = true; + new_sta_bw = IEEE80211_STA_RX_BW_40; break; - default: + case NL80211_CHAN_WIDTH_80: + new_sta_bw = IEEE80211_STA_RX_BW_80; break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + new_sta_bw = IEEE80211_STA_RX_BW_160; + break; + default: + return -EINVAL; } - /* This can change during the lifetime of the BSS */ - if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) - disable_40 = true; + if (new_sta_bw > sta->cur_max_bandwidth) + new_sta_bw = sta->cur_max_bandwidth; - if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - disable_40 = true; + if (new_sta_bw < sta->sta.bandwidth) { + sta->sta.bandwidth = new_sta_bw; + rate_control_rate_update(local, sband, sta, + IEEE80211_RC_BW_CHANGED); + } - if (disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { - if (disable_40) - sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; - else - sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); + ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed); + if (ret) { + sdata_info(sdata, + "AP %pM changed bandwidth to incompatible one - disconnect\n", + ifmgd->bssid); + return ret; + } - if (reconfig) - rate_control_rate_update(local, sband, sta, - IEEE80211_RC_BW_CHANGED); + if (new_sta_bw > sta->sta.bandwidth) { + sta->sta.bandwidth = new_sta_bw; + rate_control_rate_update(local, sband, sta, + IEEE80211_RC_BW_CHANGED); } ht_opmode = le16_to_cpu(ht_oper->operation_mode); /* if bss configuration changed store the new one */ - if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) { - changed |= BSS_CHANGED_HT; + if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { + *changed |= BSS_CHANGED_HT; sdata->vif.bss_conf.ht_operation_mode = ht_opmode; } - return changed; + return 0; } /* frame sending functions */ @@ -2377,6 +2446,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ifmgd->aid = aid; + /* + * We previously checked these in the beacon/probe response, so + * they should be present here. This is just a safety net. + */ + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && + (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) { + sdata_info(sdata, + "HT AP is missing WMM params or HT capability/operation in AssocResp\n"); + return false; + } + + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && + (!elems.vht_cap_elem || !elems.vht_operation)) { + sdata_info(sdata, + "VHT AP is missing VHT capability/operation in AssocResp\n"); + return false; + } + mutex_lock(&sdata->local->sta_mtx); /* * station info was already allocated and inserted before @@ -2390,6 +2477,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; + /* Set up internal HT/VHT capabilities */ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems.ht_cap_elem, sta); @@ -2398,11 +2486,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, elems.vht_cap_elem, sta); - if (elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) - changed |= ieee80211_config_ht_tx(sdata, sta, - elems.ht_operation, - cbss->bssid, false); + /* + * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data + * in their association response, so ignore that data for our own + * configuration. If it changed since the last beacon, we'll get the + * next beacon and update then. + */ /* * If an operating mode notification IE is present, override the @@ -2679,10 +2768,10 @@ static const u64 care_about_ies = (1ULL << WLAN_EID_HT_CAPABILITY) | (1ULL << WLAN_EID_HT_OPERATION); -static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - struct ieee80211_rx_status *rx_status) +static enum rx_mgmt_action +ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + u8 *deauth_buf, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; @@ -2703,18 +2792,18 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) - return; + return RX_MGMT_NONE; rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) { rcu_read_unlock(); - return; + return RX_MGMT_NONE; } if (rx_status->freq != chanctx_conf->def.chan->center_freq) { rcu_read_unlock(); - return; + return RX_MGMT_NONE; } chan = chanctx_conf->def.chan; rcu_read_unlock(); @@ -2742,12 +2831,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->assoc_data->timeout = jiffies; ifmgd->assoc_data->timeout_started = true; run_again(ifmgd, ifmgd->assoc_data->timeout); - return; + return RX_MGMT_NONE; } if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) - return; + return RX_MGMT_NONE; bssid = ifmgd->associated->bssid; /* Track average RSSI from the Beacon frames of the current AP */ @@ -2885,7 +2974,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) - return; + return RX_MGMT_NONE; ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; @@ -2934,11 +3023,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, bssid); - if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) - changed |= ieee80211_config_ht_tx(sdata, sta, - elems.ht_operation, - bssid, true); + if (ieee80211_config_bw(sdata, sta, elems.ht_operation, + elems.vht_operation, bssid, &changed)) { + mutex_unlock(&local->sta_mtx); + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DEAUTH_LEAVING, + true, deauth_buf); + return RX_MGMT_CFG80211_TX_DEAUTH; + } if (sta && elems.opmode_notif) ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif, @@ -2954,6 +3046,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, elems.pwr_constr_elem); ieee80211_bss_info_change_notify(sdata, changed); + + return RX_MGMT_NONE; } void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -2964,6 +3058,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; struct cfg80211_bss *bss = NULL; enum rx_mgmt_action rma = RX_MGMT_NONE; + u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; u16 fc; rx_status = (struct ieee80211_rx_status *) skb->cb; @@ -2974,7 +3069,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_BEACON: - ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); + rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, + deauth_buf, rx_status); break; case IEEE80211_STYPE_PROBE_RESP: ieee80211_rx_mgmt_probe_resp(sdata, skb); @@ -3023,6 +3119,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, case RX_MGMT_CFG80211_ASSOC_TIMEOUT: cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); break; + case RX_MGMT_CFG80211_TX_DEAUTH: + cfg80211_send_deauth(sdata->dev, deauth_buf, + sizeof(deauth_buf)); + break; default: WARN(1, "unexpected: %d", rma); } @@ -3620,7 +3720,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, cbss->channel, ht_oper, vht_oper, - &chandef); + &chandef, true); sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), local->rx_chains); -- cgit v1.2.3 From c7a6ee27abd46247c1c7edfc49fb935138da7875 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Dec 2012 17:50:39 +0100 Subject: cfg80211: allow drivers to selectively disable 80/160 MHz Some drivers might support 80 or 160 MHz only on some channels for whatever reason, so allow them to disable these channel widths. Also maintain the new flags when regulatory bandwidth limitations would disable these wide channels. Reviewed-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 ++++++++++++ net/wireless/chan.c | 13 ++++++++++++- net/wireless/reg.c | 8 ++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ee11a3db730b..8a9200f2f4a4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -100,6 +100,16 @@ enum ieee80211_band { * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel * is not permitted. * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel. + * @IEEE80211_CHAN_NO_80MHZ: If the driver supports 80 MHz on the band, + * this flag indicates that an 80 MHz channel cannot use this + * channel as the control or any of the secondary channels. + * This may be due to the driver or due to regulatory bandwidth + * restrictions. + * @IEEE80211_CHAN_NO_160MHZ: If the driver supports 160 MHz on the band, + * this flag indicates that an 160 MHz channel cannot use this + * channel as the control or any of the secondary channels. + * This may be due to the driver or due to regulatory bandwidth + * restrictions. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, @@ -109,6 +119,8 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_NO_HT40PLUS = 1<<4, IEEE80211_CHAN_NO_HT40MINUS = 1<<5, IEEE80211_CHAN_NO_OFDM = 1<<6, + IEEE80211_CHAN_NO_80MHZ = 1<<7, + IEEE80211_CHAN_NO_160MHZ = 1<<8, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 810c23cfb894..fd556ac05fdb 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -375,6 +375,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, case NL80211_CHAN_WIDTH_80: if (!vht_cap->vht_supported) return false; + prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; width = 80; break; case NL80211_CHAN_WIDTH_160: @@ -382,6 +383,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, return false; if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) return false; + prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; width = 160; break; default: @@ -389,7 +391,16 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, return false; } - /* TODO: missing regulatory check on 80/160 bandwidth */ + /* + * TODO: What if there are only certain 80/160/80+80 MHz channels + * allowed by the driver, or only certain combinations? + * For 40 MHz the driver can set the NO_HT40 flags, but for + * 80/160 MHz and in particular 80+80 MHz this isn't really + * feasible and we only have NO_80MHZ/NO_160MHZ so far but + * no way to cover 80+80 MHz or more complex restrictions. + * Note that such restrictions also need to be advertised to + * userspace, for example for P2P channel selection. + */ if (width > 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e97d5b071ab6..93ab840957a0 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -866,6 +866,10 @@ static void handle_channel(struct wiphy *wiphy, if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) + bw_flags |= IEEE80211_CHAN_NO_80MHZ; + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) + bw_flags |= IEEE80211_CHAN_NO_160MHZ; if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && @@ -1264,6 +1268,10 @@ static void handle_channel_custom(struct wiphy *wiphy, if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) + bw_flags |= IEEE80211_CHAN_NO_80MHZ; + if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) + bw_flags |= IEEE80211_CHAN_NO_160MHZ; chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); -- cgit v1.2.3 From 50640f169372b9977487a328dedf13a8debedff7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Dec 2012 17:59:39 +0100 Subject: nl80211: advertise HT/VHT channel limitations When drivers or regulatory have limitations on 40, 80 or 160 MHz channels, advertise these to userspace via nl80211. Also add a new feature flag to let userspace know this is supported. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 17 +++++++++++++++++ net/wireless/core.c | 3 ++- net/wireless/nl80211.c | 12 ++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 90b7af86f392..3880f6ad7ed1 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2041,6 +2041,16 @@ enum nl80211_band_attr { * (enum nl80211_dfs_state) * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long * this channel is in this DFS state. + * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible, + * this includes 80+80 channels + * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel + * using this channel as the primary or any of the secondary channels + * isn't possible * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2055,6 +2065,10 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_MAX_TX_POWER, NL80211_FREQUENCY_ATTR_DFS_STATE, NL80211_FREQUENCY_ATTR_DFS_TIME, + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + NL80211_FREQUENCY_ATTR_NO_80MHZ, + NL80211_FREQUENCY_ATTR_NO_160MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -3421,6 +3435,8 @@ enum nl80211_ap_sme_features { * Note that even for drivers that support this, the default is to add * stations in authenticated/associated state, so to add unauthenticated * stations the authenticated/associated bits have to be set in the mask. + * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits + * (HT40, VHT 80/160 MHz) if this flag is set */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3437,6 +3453,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, }; /** diff --git a/net/wireless/core.c b/net/wireless/core.c index 922002105062..33b75b9b8efa 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -367,7 +367,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.rts_threshold = (u32) -1; rdev->wiphy.coverage_class = 0; - rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH; + rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH | + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; return &rdev->wiphy; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c1e18ccf4049..7e40b9e82b45 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -562,6 +562,18 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time)) goto nla_put_failure; } + if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ)) + goto nla_put_failure; if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, DBM_TO_MBM(chan->max_power))) -- cgit v1.2.3 From 4a3cb702b05868f67c4ee3da3380461c5b90b4ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 12 Feb 2013 16:43:19 +0100 Subject: mac80211: constify IE parsing Make all the parsed IE pointers const, and propagate the change to all the users etc. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 +- net/mac80211/ht.c | 2 +- net/mac80211/ieee80211_i.h | 87 +++++++++++++++++++++++---------------------- net/mac80211/mesh.h | 11 +++--- net/mac80211/mesh_hwmp.c | 42 ++++++++++++---------- net/mac80211/mesh_pathtbl.c | 11 +++--- net/mac80211/mlme.c | 15 ++++---- net/mac80211/util.c | 4 +-- net/mac80211/vht.c | 9 ++--- 9 files changed, 96 insertions(+), 87 deletions(-) (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 12b5996533ec..e085fcf52b26 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2152,7 +2152,7 @@ static inline unsigned long ieee80211_tu_to_usec(unsigned long tu) * @tim_len: length of the TIM IE * @aid: the AID to look for */ -static inline bool ieee80211_check_tim(struct ieee80211_tim_ie *tim, +static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, u8 tim_len, u16 aid) { u8 mask; diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 797969bc26e1..b84147ac5b4c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -94,7 +94,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, - struct ieee80211_ht_cap *ht_cap_ie, + const struct ieee80211_ht_cap *ht_cap_ie, struct sta_info *sta) { struct ieee80211_sta_ht_cap ht_cap; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d1074442f1b5..d702f7dc321b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1163,41 +1163,41 @@ struct ieee80211_ra_tid { /* Parsed Information Elements */ struct ieee802_11_elems { - u8 *ie_start; + const u8 *ie_start; size_t total_len; /* pointers to IEs */ - u8 *ssid; - u8 *supp_rates; - u8 *fh_params; - u8 *ds_params; - u8 *cf_params; - struct ieee80211_tim_ie *tim; - u8 *ibss_params; - u8 *challenge; - u8 *rsn; - u8 *erp_info; - u8 *ext_supp_rates; - u8 *wmm_info; - u8 *wmm_param; - struct ieee80211_ht_cap *ht_cap_elem; - struct ieee80211_ht_operation *ht_operation; - struct ieee80211_vht_cap *vht_cap_elem; - struct ieee80211_vht_operation *vht_operation; - struct ieee80211_meshconf_ie *mesh_config; - u8 *mesh_id; - u8 *peering; - __le16 *awake_window; - u8 *preq; - u8 *prep; - u8 *perr; - struct ieee80211_rann_ie *rann; - struct ieee80211_channel_sw_ie *ch_switch_ie; - u8 *country_elem; - u8 *pwr_constr_elem; - u8 *quiet_elem; /* first quite element */ - u8 *timeout_int; - u8 *opmode_notif; + const u8 *ssid; + const u8 *supp_rates; + const u8 *fh_params; + const u8 *ds_params; + const u8 *cf_params; + const struct ieee80211_tim_ie *tim; + const u8 *ibss_params; + const u8 *challenge; + const u8 *rsn; + const u8 *erp_info; + const u8 *ext_supp_rates; + const u8 *wmm_info; + const u8 *wmm_param; + const struct ieee80211_ht_cap *ht_cap_elem; + const struct ieee80211_ht_operation *ht_operation; + const struct ieee80211_vht_cap *vht_cap_elem; + const struct ieee80211_vht_operation *vht_operation; + const struct ieee80211_meshconf_ie *mesh_config; + const u8 *mesh_id; + const u8 *peering; + const __le16 *awake_window; + const u8 *preq; + const u8 *prep; + const u8 *perr; + const struct ieee80211_rann_ie *rann; + const struct ieee80211_channel_sw_ie *ch_switch_ie; + const u8 *country_elem; + const u8 *pwr_constr_elem; + const u8 *quiet_elem; /* first quite element */ + const u8 *timeout_int; + const u8 *opmode_notif; /* length of them, respectively */ u8 ssid_len; @@ -1276,10 +1276,10 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy); int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); -void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel_sw_ie *sw_elem, - struct ieee80211_bss *bss, - u64 timestamp); +void +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_channel_sw_ie *sw_elem, + struct ieee80211_bss *bss, u64 timestamp); void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata); @@ -1387,7 +1387,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap *ht_cap); bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, - struct ieee80211_ht_cap *ht_cap_ie, + const struct ieee80211_ht_cap *ht_cap_ie, struct sta_info *sta); void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, const u8 *da, u16 tid, @@ -1428,10 +1428,11 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs); /* VHT */ -void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - struct ieee80211_vht_cap *vht_cap_ie, - struct sta_info *sta); +void +ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct ieee80211_vht_cap *vht_cap_ie, + struct sta_info *sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); void ieee80211_sta_set_rx_nss(struct sta_info *sta); void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, @@ -1555,7 +1556,7 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, - u8 *extra, size_t extra_len, const u8 *bssid, + const u8 *extra, size_t extra_len, const u8 *bssid, const u8 *da, const u8 *key, u8 key_len, u8 key_idx, u32 tx_flags); void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, @@ -1606,7 +1607,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, /* channel management */ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, - struct ieee80211_ht_operation *ht_oper, + const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef); int __must_check diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 7ad035f0cacc..a1bad310f2e9 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -265,8 +265,8 @@ int mesh_nexthop_lookup(struct sk_buff *skb, int mesh_nexthop_resolve(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); -struct mesh_path *mesh_path_lookup(u8 *dst, - struct ieee80211_sub_if_data *sdata); +struct mesh_path *mesh_path_lookup(const u8 *dst, + struct ieee80211_sub_if_data *sdata); struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata); int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); @@ -276,7 +276,7 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); -int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata); +int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata); int mesh_path_add_gate(struct mesh_path *mpath); int mesh_path_send_to_gates(struct mesh_path *mpath); @@ -301,8 +301,9 @@ void mesh_sta_cleanup(struct sta_info *sta); void mesh_mpath_table_grow(void); void mesh_mpp_table_grow(void); /* Mesh paths */ -int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode, - const u8 *ra, struct ieee80211_sub_if_data *sdata); +int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn, + __le16 target_rcode, const u8 *ra, + struct ieee80211_sub_if_data *sdata); void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); void mesh_path_flush_pending(struct mesh_path *mpath); void mesh_path_tx_pending(struct mesh_path *mpath); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index f0dd8742ed42..585c1e26cca8 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -30,14 +30,14 @@ static void mesh_queue_preq(struct mesh_path *, u8); -static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae) +static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae) { if (ae) offset += 6; return get_unaligned_le32(preq_elem + offset); } -static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) +static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae) { if (ae) offset += 6; @@ -102,10 +102,13 @@ enum mpath_frame_type { static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, - u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target, - __le32 target_sn, const u8 *da, u8 hop_count, u8 ttl, - __le32 lifetime, __le32 metric, __le32 preq_id, - struct ieee80211_sub_if_data *sdata) + const u8 *orig_addr, __le32 orig_sn, + u8 target_flags, const u8 *target, + __le32 target_sn, const u8 *da, + u8 hop_count, u8 ttl, + __le32 lifetime, __le32 metric, + __le32 preq_id, + struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -235,7 +238,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, * also acquires in the TX path. To avoid a deadlock we don't transmit the * frame directly but add it to the pending queue instead. */ -int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, +int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn, __le16 target_rcode, const u8 *ra, struct ieee80211_sub_if_data *sdata) { @@ -369,14 +372,14 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, * path routing information is updated. */ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - u8 *hwmp_ie, enum mpath_frame_type action) + struct ieee80211_mgmt *mgmt, + const u8 *hwmp_ie, enum mpath_frame_type action) { struct ieee80211_local *local = sdata->local; struct mesh_path *mpath; struct sta_info *sta; bool fresh_info; - u8 *orig_addr, *ta; + const u8 *orig_addr, *ta; u32 orig_sn, orig_metric; unsigned long orig_lifetime, exp_time; u32 last_hop_metric, new_metric; @@ -511,11 +514,11 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - u8 *preq_elem, u32 metric) + const u8 *preq_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath = NULL; - u8 *target_addr, *orig_addr; + const u8 *target_addr, *orig_addr; const u8 *da; u8 target_flags, ttl, flags; u32 orig_sn, target_sn, lifetime, orig_metric; @@ -648,11 +651,11 @@ next_hop_deref_protected(struct mesh_path *mpath) static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - u8 *prep_elem, u32 metric) + const u8 *prep_elem, u32 metric) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; - u8 *target_addr, *orig_addr; + const u8 *target_addr, *orig_addr; u8 ttl, hopcount, flags; u8 next_hop[ETH_ALEN]; u32 target_sn, orig_sn, lifetime; @@ -711,12 +714,13 @@ fail: } static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, u8 *perr_elem) + struct ieee80211_mgmt *mgmt, + const u8 *perr_elem) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; u8 ttl; - u8 *ta, *target_addr; + const u8 *ta, *target_addr; u32 target_sn; u16 target_rcode; @@ -758,15 +762,15 @@ endperr: } static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - struct ieee80211_rann_ie *rann) + struct ieee80211_mgmt *mgmt, + const struct ieee80211_rann_ie *rann) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct mesh_path *mpath; u8 ttl, flags, hopcount; - u8 *orig_addr; + const u8 *orig_addr; u32 orig_sn, metric, metric_txsta, interval; bool root_is_gate; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index d5786c3eaee2..2ce4c4023a97 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -181,7 +181,7 @@ errcopy: return -ENOMEM; } -static u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, +static u32 mesh_table_hash(const u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl) { /* Use last four bytes of hw addr and interface index as hash index */ @@ -326,8 +326,8 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, } -static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst, - struct ieee80211_sub_if_data *sdata) +static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst, + struct ieee80211_sub_if_data *sdata) { struct mesh_path *mpath; struct hlist_node *n; @@ -359,7 +359,8 @@ static struct mesh_path *mpath_lookup(struct mesh_table *tbl, u8 *dst, * * Locking: must be called within a read rcu section. */ -struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +struct mesh_path *mesh_path_lookup(const u8 *dst, + struct ieee80211_sub_if_data *sdata) { return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); } @@ -494,7 +495,7 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata) * * State: the initial state of the new path is set to 0 */ -int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) +int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 05b229e3b226..7a8cd789e487 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1041,10 +1041,10 @@ static void ieee80211_chswitch_timer(unsigned long data) ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); } -void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel_sw_ie *sw_elem, - struct ieee80211_bss *bss, - u64 timestamp) +void +ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_channel_sw_ie *sw_elem, + struct ieee80211_bss *bss, u64 timestamp) { struct cfg80211_bss *cbss = container_of((void *)bss, struct cfg80211_bss, priv); @@ -1479,13 +1479,14 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work) /* MLME */ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - u8 *wmm_param, size_t wmm_param_len) + const u8 *wmm_param, size_t wmm_param_len) { struct ieee80211_tx_queue_params params; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t left; int count; - u8 *pos, uapsd_queues = 0; + const u8 *pos; + u8 uapsd_queues = 0; if (!local->ops->conf_tx) return false; @@ -2670,7 +2671,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period; if (elems->tim && !elems->parse_error) { - struct ieee80211_tim_ie *tim_ie = elems->tim; + const struct ieee80211_tim_ie *tim_ie = elems->tim; sdata->u.mgd.dtim_period = tim_ie->dtim_period; } } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e24ff38606a9..0f38f43ac62e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1035,7 +1035,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local, void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, - u8 *extra, size_t extra_len, const u8 *da, + const u8 *extra, size_t extra_len, const u8 *da, const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, u32 tx_flags) { @@ -1947,7 +1947,7 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, } void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, - struct ieee80211_ht_operation *ht_oper, + const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef) { enum nl80211_channel_type channel_type; diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index a9549fcc5a04..a2c2258bc84e 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -13,10 +13,11 @@ #include "rate.h" -void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, - struct ieee80211_supported_band *sband, - struct ieee80211_vht_cap *vht_cap_ie, - struct sta_info *sta) +void +ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, + struct ieee80211_supported_band *sband, + const struct ieee80211_vht_cap *vht_cap_ie, + struct sta_info *sta) { struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; -- cgit v1.2.3 From 2b5e19677592c167d012c2d129407f39d2bdeb8d Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 14 Feb 2013 11:20:13 -0800 Subject: mac80211: cache mesh beacon Previously, the entire mesh beacon would be generated each time the beacon timer fired. Instead generate a beacon head and tail (so the TIM can easily be inserted when mesh power save is on) when starting a mesh or the MBSS parameters change. Also add a mutex for protecting beacon updates and preventing leaks. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +-- net/mac80211/ieee80211_i.h | 3 + net/mac80211/mesh.c | 154 ++++++++++++++++++++++++++++++++++++++++++++- net/mac80211/mesh.h | 5 +- net/mac80211/mesh_plink.c | 4 +- net/mac80211/tx.c | 61 +++--------------- 6 files changed, 171 insertions(+), 65 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c3869ba42343..0800fb331ce5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1800,11 +1800,10 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, conf->power_mode = nconf->power_mode; ieee80211_mps_local_status_update(sdata); } - if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) { + if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) conf->dot11MeshAwakeWindowDuration = nconf->dot11MeshAwakeWindowDuration; - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); - } + ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON); return 0; } @@ -1830,9 +1829,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, if (err) return err; - ieee80211_start_mesh(sdata); - - return 0; + return ieee80211_start_mesh(sdata); } static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d702f7dc321b..388580a1bada 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -580,6 +580,9 @@ struct ieee80211_if_mesh { u32 mesh_seqnum; bool accepting_plinks; int num_gates; + struct beacon_data __rcu *beacon; + /* just protects beacon updates for now */ + struct mutex mtx; const u8 *ie; u8 ie_len; enum { diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0adec3d539d3..6ff8ee9d9ff5 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -171,7 +171,7 @@ void mesh_sta_cleanup(struct sta_info *sta) } if (changed) - ieee80211_bss_info_change_notify(sdata, changed); + ieee80211_mbss_info_change_notify(sdata, changed); } int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) @@ -593,7 +593,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); - ieee80211_bss_info_change_notify(sdata, changed); + ieee80211_mbss_info_change_notify(sdata, changed); mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); @@ -644,7 +644,140 @@ void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata) } #endif -void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) +static int +ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) +{ + struct beacon_data *bcn; + int head_len, tail_len; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_band band; + u8 *pos; + struct ieee80211_sub_if_data *sdata; + int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + + sizeof(mgmt->u.beacon); + + sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + band = chanctx_conf->def.chan->band; + rcu_read_unlock(); + + head_len = hdr_len + + 2 + /* NULL SSID */ + 2 + 8 + /* supported rates */ + 2 + 3; /* DS params */ + tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_operation) + + 2 + ifmsh->mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + 2 + sizeof(__le16) + /* awake window */ + ifmsh->ie_len; + + bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL); + /* need an skb for IE builders to operate on */ + skb = dev_alloc_skb(max(head_len, tail_len)); + + if (!bcn || !skb) + goto out_free; + + /* + * pointers go into the block we allocated, + * memory is | beacon_data | head | tail | + */ + bcn->head = ((u8 *) bcn) + sizeof(*bcn); + + /* fill in the head */ + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt); + mgmt->u.beacon.beacon_int = + cpu_to_le16(sdata->vif.bss_conf.beacon_int); + mgmt->u.beacon.capab_info |= cpu_to_le16( + sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); + + pos = skb_put(skb, 2); + *pos++ = WLAN_EID_SSID; + *pos++ = 0x0; + + if (ieee80211_add_srates_ie(sdata, skb, true, band) || + mesh_add_ds_params_ie(skb, sdata)) + goto out_free; + + bcn->head_len = skb->len; + memcpy(bcn->head, skb->data, bcn->head_len); + + /* now the tail */ + skb_trim(skb, 0); + bcn->tail = bcn->head + bcn->head_len; + + if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) || + mesh_add_rsn_ie(skb, sdata) || + mesh_add_ht_cap_ie(skb, sdata) || + mesh_add_ht_oper_ie(skb, sdata) || + mesh_add_meshid_ie(skb, sdata) || + mesh_add_meshconf_ie(skb, sdata) || + mesh_add_awake_window_ie(skb, sdata) || + mesh_add_vendor_ies(skb, sdata)) + goto out_free; + + bcn->tail_len = skb->len; + memcpy(bcn->tail, skb->data, bcn->tail_len); + + dev_kfree_skb(skb); + rcu_assign_pointer(ifmsh->beacon, bcn); + return 0; +out_free: + kfree(bcn); + dev_kfree_skb(skb); + return -ENOMEM; +} + +static int +ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh) +{ + struct ieee80211_sub_if_data *sdata; + struct beacon_data *old_bcn; + int ret; + sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); + + mutex_lock(&ifmsh->mtx); + + old_bcn = rcu_dereference_protected(ifmsh->beacon, + lockdep_is_held(&ifmsh->mtx)); + ret = ieee80211_mesh_build_beacon(ifmsh); + if (ret) + /* just reuse old beacon */ + goto out; + + if (old_bcn) + kfree_rcu(old_bcn, rcu_head); +out: + mutex_unlock(&ifmsh->mtx); + return ret; +} + +void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, + u32 changed) +{ + if (sdata->vif.bss_conf.enable_beacon && + (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_HT | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_BEACON_INT))) + if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh)) + return; + ieee80211_bss_info_change_notify(sdata, changed); +} + +int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; @@ -677,15 +810,22 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) changed |= ieee80211_mps_local_status_update(sdata); + if (ieee80211_mesh_build_beacon(ifmsh)) { + ieee80211_stop_mesh(sdata); + return -ENOMEM; + } + ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(sdata->dev); + return 0; } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct beacon_data *bcn; netif_carrier_off(sdata->dev); @@ -694,6 +834,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.enable_beacon = false; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + mutex_lock(&ifmsh->mtx); + bcn = rcu_dereference_protected(ifmsh->beacon, + lockdep_is_held(&ifmsh->mtx)); + rcu_assign_pointer(ifmsh->beacon, NULL); + kfree_rcu(bcn, rcu_head); + mutex_unlock(&ifmsh->mtx); /* flush STAs and mpaths on this iface */ sta_info_flush(sdata); @@ -883,6 +1029,8 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) skb_queue_head_init(&ifmsh->ps.bc_buf); spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); + RCU_INIT_POINTER(ifmsh->beacon, NULL); + mutex_init(&ifmsh->mtx); sdata->vif.bss_conf.bssid = zero_addr; } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index a1bad310f2e9..1a1da877b1d2 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -239,10 +239,13 @@ void ieee80211s_update_metric(struct ieee80211_local *local, struct sta_info *sta, struct sk_buff *skb); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); -void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); +int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); +/* wrapper for ieee80211_bss_info_change_notify() */ +void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, + u32 changed); /* mesh power save */ u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7765139b24aa..f7526e509aa8 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -508,7 +508,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, ieee80211_mps_frame_release(sta, elems); out: rcu_read_unlock(); - ieee80211_bss_info_change_notify(sdata, changed); + ieee80211_mbss_info_change_notify(sdata, changed); } static void mesh_plink_timer(unsigned long data) @@ -1090,5 +1090,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m rcu_read_unlock(); if (changed) - ieee80211_bss_info_change_notify(sdata, changed); + ieee80211_mbss_info_change_notify(sdata, changed); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7d8c629f1e6a..fe644f91ae05 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2444,71 +2444,26 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); } else if (ieee80211_vif_is_mesh(&sdata->vif)) { - struct ieee80211_mgmt *mgmt; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - u8 *pos; - int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + - sizeof(mgmt->u.beacon); + struct beacon_data *bcn = rcu_dereference(ifmsh->beacon); -#ifdef CONFIG_MAC80211_MESH - if (!sdata->u.mesh.mesh_id_len) + if (!bcn) goto out; -#endif if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt( sdata); skb = dev_alloc_skb(local->tx_headroom + - hdr_len + - 2 + /* NULL SSID */ - 2 + 8 + /* supported rates */ - 2 + 3 + /* DS params */ + bcn->head_len + 256 + /* TIM IE */ - 2 + (IEEE80211_MAX_SUPP_RATES - 8) + - 2 + sizeof(struct ieee80211_ht_cap) + - 2 + sizeof(struct ieee80211_ht_operation) + - 2 + sdata->u.mesh.mesh_id_len + - 2 + sizeof(struct ieee80211_meshconf_ie) + - sdata->u.mesh.ie_len + - 2 + sizeof(__le16)); /* awake window */ + bcn->tail_len); if (!skb) goto out; - - skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); - memset(mgmt, 0, hdr_len); - mgmt->frame_control = - cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); - eth_broadcast_addr(mgmt->da); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); - memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); - ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt); - mgmt->u.beacon.beacon_int = - cpu_to_le16(sdata->vif.bss_conf.beacon_int); - mgmt->u.beacon.capab_info |= cpu_to_le16( - sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0); - - pos = skb_put(skb, 2); - *pos++ = WLAN_EID_SSID; - *pos++ = 0x0; - - band = chanctx_conf->def.chan->band; - - if (ieee80211_add_srates_ie(sdata, skb, true, band) || - mesh_add_ds_params_ie(skb, sdata) || - ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) || - ieee80211_add_ext_srates_ie(sdata, skb, true, band) || - mesh_add_rsn_ie(skb, sdata) || - mesh_add_ht_cap_ie(skb, sdata) || - mesh_add_ht_oper_ie(skb, sdata) || - mesh_add_meshid_ie(skb, sdata) || - mesh_add_meshconf_ie(skb, sdata) || - mesh_add_awake_window_ie(skb, sdata) || - mesh_add_vendor_ies(skb, sdata)) { - pr_err("o11s: couldn't add ies!\n"); - goto out; - } + skb_reserve(skb, local->tx_headroom); + memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len); + ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb); + memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len); } else { WARN_ON(1); goto out; -- cgit v1.2.3 From 9fb04b501a57ad29fa989ab3cd2123482e7eac5f Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 14 Feb 2013 11:20:14 -0800 Subject: mac80211: generate mesh probe responses Mesh interfaces will now respond to any broadcast (or matching directed mesh) probe requests with a probe response. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/rx.c | 5 +++-- 2 files changed, 63 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6ff8ee9d9ff5..a77d40ed4e61 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -868,6 +868,63 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) sdata->u.mesh.timers_running = 0; } +static void +ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct sk_buff *presp; + struct beacon_data *bcn; + struct ieee80211_mgmt *hdr; + struct ieee802_11_elems elems; + size_t baselen; + u8 *pos, *end; + + end = ((u8 *) mgmt) + len; + pos = mgmt->u.probe_req.variable; + baselen = (u8 *) pos - (u8 *) mgmt; + if (baselen > len) + return; + + ieee802_11_parse_elems(pos, len - baselen, &elems); + + /* 802.11-2012 10.1.4.3.2 */ + if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && + !is_broadcast_ether_addr(mgmt->da)) || + elems.ssid_len != 0) + return; + + if (elems.mesh_id_len != 0 && + (elems.mesh_id_len != ifmsh->mesh_id_len || + memcmp(elems.mesh_id, ifmsh->mesh_id, ifmsh->mesh_id_len))) + return; + + rcu_read_lock(); + bcn = rcu_dereference(ifmsh->beacon); + + if (!bcn) + goto out; + + presp = dev_alloc_skb(local->tx_headroom + + bcn->head_len + bcn->tail_len); + if (!presp) + goto out; + + skb_reserve(presp, local->tx_headroom); + memcpy(skb_put(presp, bcn->head_len), bcn->head, bcn->head_len); + memcpy(skb_put(presp, bcn->tail_len), bcn->tail, bcn->tail_len); + hdr = (struct ieee80211_mgmt *) presp->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + memcpy(hdr->da, mgmt->sa, ETH_ALEN); + mpl_dbg(sdata, "sending probe resp. to %pM\n", hdr->da); + IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + ieee80211_tx_skb(sdata, presp); +out: + rcu_read_unlock(); +} + static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, @@ -957,6 +1014,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_mesh_rx_bcn_presp(sdata, stype, mgmt, skb->len, rx_status); break; + case IEEE80211_STYPE_PROBE_REQ: + ieee80211_mesh_rx_probe_req(sdata, mgmt, skb->len); + break; case IEEE80211_STYPE_ACTION: ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); break; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 30f1ba6de8f3..296a4aeadedc 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2718,8 +2718,9 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; break; case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): - /* process only for ibss */ - if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + /* process only for ibss and mesh */ + if (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) return RX_DROP_MONITOR; break; default: -- cgit v1.2.3 From af0ed69badc67a0b6e976543f52029fce9ac8f69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 12 Feb 2013 14:21:00 +0100 Subject: mac80211: stop modifying HT SMPS capability Instead of modifying the HT SMPS capability field for stations, track the SMPS mode explicitly in a new field in the station struct and use it in the drivers that care about it. This simplifies the code using it. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/4965-rs.c | 3 +-- drivers/net/wireless/iwlegacy/common.c | 16 +++++++--------- drivers/net/wireless/iwlwifi/dvm/rs.c | 6 ++---- drivers/net/wireless/iwlwifi/dvm/sta.c | 17 +++++++---------- drivers/net/wireless/iwlwifi/mvm/rs.c | 6 ++---- drivers/net/wireless/rt2x00/rt2x00queue.c | 5 +---- include/net/mac80211.h | 2 ++ net/mac80211/ht.c | 19 +++++++++++++++++++ net/mac80211/rc80211_minstrel_ht.c | 6 +----- net/mac80211/rx.c | 16 ++++++---------- net/mac80211/sta_info.c | 2 ++ 11 files changed, 50 insertions(+), 48 deletions(-) (limited to 'net') diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index f3b8e91aa3dc..e8324b5e5bfe 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -1183,8 +1183,7 @@ il4965_rs_switch_to_mimo2(struct il_priv *il, struct il_lq_sta *lq_sta, if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) == - WLAN_HT_CAP_SM_PS_STATIC) + if (sta->smps_mode == IEEE80211_SMPS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index 1f598604a79c..4c9aafb1b0cd 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -1830,32 +1830,30 @@ il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) { struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; __le32 sta_flags; - u8 mimo_ps_mode; if (!sta || !sta_ht_inf->ht_supported) goto done; - mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; D_ASSOC("spatial multiplexing power save mode: %s\n", - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? "static" : - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? "dynamic" : + (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" : + (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : "disabled"); sta_flags = il->stations[idx].sta.station_flags; sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); - switch (mimo_ps_mode) { - case WLAN_HT_CAP_SM_PS_STATIC: + switch (sta->smps_mode) { + case IEEE80211_SMPS_STATIC: sta_flags |= STA_FLG_MIMO_DIS_MSK; break; - case WLAN_HT_CAP_SM_PS_DYNAMIC: + case IEEE80211_SMPS_DYNAMIC: sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; break; - case WLAN_HT_CAP_SM_PS_DISABLED: + case IEEE80211_SMPS_OFF: break; default: - IL_WARN("Invalid MIMO PS mode %d\n", mimo_ps_mode); + IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode); break; } diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index b25de02964f9..abe304267261 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1289,8 +1289,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) + if (sta->smps_mode == IEEE80211_SMPS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ @@ -1345,8 +1344,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) + if (sta->smps_mode == IEEE80211_SMPS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index 6deab38c7aee..d5faf74c7991 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -196,7 +196,6 @@ static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, __le32 *flags, __le32 *mask) { struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; - u8 mimo_ps_mode; *mask = STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK | @@ -208,26 +207,24 @@ static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, if (!sta || !sta_ht_inf->ht_supported) return; - mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; - IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", sta->addr, - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? + (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" : - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? + (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : "disabled"); - switch (mimo_ps_mode) { - case WLAN_HT_CAP_SM_PS_STATIC: + switch (sta->smps_mode) { + case IEEE80211_SMPS_STATIC: *flags |= STA_FLG_MIMO_DIS_MSK; break; - case WLAN_HT_CAP_SM_PS_DYNAMIC: + case IEEE80211_SMPS_DYNAMIC: *flags |= STA_FLG_RTS_MIMO_PROT_MSK; break; - case WLAN_HT_CAP_SM_PS_DISABLED: + case IEEE80211_SMPS_OFF: break; default: - IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); + IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode); break; } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 8ba36e5e4b67..56b636d9ab30 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1229,8 +1229,7 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, if (!sta->ht_cap.ht_supported) return -1; - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) + if (sta->smps_mode == IEEE80211_SMPS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ @@ -1282,8 +1281,7 @@ static int rs_switch_to_mimo3(struct iwl_mvm *mvm, if (!sta->ht_cap.ht_supported) return -1; - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) + if (sta->smps_mode == IEEE80211_SMPS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index f35d85a71bbc..9503e6954b89 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -343,10 +343,7 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, * when using more then one tx stream (>MCS7). */ if (sta && txdesc->u.ht.mcs > 7 && - ((sta->ht_cap.cap & - IEEE80211_HT_CAP_SM_PS) >> - IEEE80211_HT_CAP_SM_PS_SHIFT) == - WLAN_HT_CAP_SM_PS_DYNAMIC) + sta->smps_mode == IEEE80211_SMPS_DYNAMIC) __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags); } else { txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 54e2add8429f..f7eba1300d82 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1245,6 +1245,7 @@ enum ieee80211_sta_rx_bandwidth { * station can receive at the moment, changed by operating mode * notifications and capabilities. The value is only valid after * the station moves to associated state. + * @smps_mode: current SMPS mode (off, static or dynamic) */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; @@ -1257,6 +1258,7 @@ struct ieee80211_sta { u8 max_sp; u8 rx_nss; enum ieee80211_sta_rx_bandwidth bandwidth; + enum ieee80211_smps_mode smps_mode; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index b84147ac5b4c..0db25d4bb223 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -102,6 +102,7 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, int i, max_tx_streams; bool changed; enum ieee80211_sta_rx_bandwidth bw; + enum ieee80211_smps_mode smps_mode; memset(&ht_cap, 0, sizeof(ht_cap)); @@ -216,6 +217,24 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; + switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) + >> IEEE80211_HT_CAP_SM_PS_SHIFT) { + case WLAN_HT_CAP_SM_PS_INVALID: + case WLAN_HT_CAP_SM_PS_STATIC: + smps_mode = IEEE80211_SMPS_STATIC; + break; + case WLAN_HT_CAP_SM_PS_DYNAMIC: + smps_mode = IEEE80211_SMPS_DYNAMIC; + break; + case WLAN_HT_CAP_SM_PS_DISABLED: + smps_mode = IEEE80211_SMPS_OFF; + break; + } + + if (smps_mode != sta->sta.smps_mode) + changed = true; + sta->sta.smps_mode = smps_mode; + return changed; } diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 6176c71d47ff..3af141c69712 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -808,7 +808,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, int ack_dur; int stbc; int i; - unsigned int smps; /* fall back to the old minstrel for legacy stations */ if (!sta->ht_cap.ht_supported) @@ -844,9 +843,6 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) mi->tx_flags |= IEEE80211_TX_CTL_LDPC; - smps = (sta_cap & IEEE80211_HT_CAP_SM_PS) >> - IEEE80211_HT_CAP_SM_PS_SHIFT; - for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { mi->groups[i].supported = 0; if (i == MINSTREL_CCK_GROUP) { @@ -869,7 +865,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, continue; /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ - if (smps == WLAN_HT_CAP_SM_PS_STATIC && + if (sta->smps_mode == IEEE80211_SMPS_STATIC && minstrel_mcs_groups[i].streams > 1) continue; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 296a4aeadedc..3acb70b73e22 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2375,31 +2375,27 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) switch (mgmt->u.action.u.ht_smps.action) { case WLAN_HT_ACTION_SMPS: { struct ieee80211_supported_band *sband; - u8 smps; + enum ieee80211_smps_mode smps_mode; /* convert to HT capability */ switch (mgmt->u.action.u.ht_smps.smps_control) { case WLAN_HT_SMPS_CONTROL_DISABLED: - smps = WLAN_HT_CAP_SM_PS_DISABLED; + smps_mode = IEEE80211_SMPS_OFF; break; case WLAN_HT_SMPS_CONTROL_STATIC: - smps = WLAN_HT_CAP_SM_PS_STATIC; + smps_mode = IEEE80211_SMPS_STATIC; break; case WLAN_HT_SMPS_CONTROL_DYNAMIC: - smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps_mode = IEEE80211_SMPS_DYNAMIC; break; default: goto invalid; } - smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; /* if no change do nothing */ - if ((rx->sta->sta.ht_cap.cap & - IEEE80211_HT_CAP_SM_PS) == smps) + if (rx->sta->sta.smps_mode == smps_mode) goto handled; - - rx->sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SM_PS; - rx->sta->sta.ht_cap.cap |= smps; + rx->sta->sta.smps_mode = smps_mode; sband = rx->local->hw.wiphy->bands[status->band]; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 0794b9018ed4..a79ce820cb50 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -375,6 +375,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < IEEE80211_NUM_TIDS; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); + sta->sta.smps_mode = IEEE80211_SMPS_OFF; + sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); return sta; -- cgit v1.2.3 From a50df0c4c0d97170a6c43573612acacc43e62fe7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 11 Feb 2013 14:20:05 +0100 Subject: cfg80211: advertise extended capabilities to userspace In many cases, userspace may need to know which of the 802.11 extended capabilities ("Extended Capabilities element") are implemented in the driver or device, to include them e.g. in beacons, assoc request/response or other frames. Add a new nl80211 attribute to hold the extended capabilities bitmap for this. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 11 +++++++++++ include/uapi/linux/nl80211.h | 9 +++++++++ net/wireless/nl80211.c | 9 +++++++++ 3 files changed, 29 insertions(+) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8a9200f2f4a4..a229046d86d4 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2480,6 +2480,14 @@ struct wiphy_wowlan_support { * * @max_acl_mac_addrs: Maximum number of MAC addresses that the device * supports for ACL. + * + * @extended_capabilities: extended capabilities supported by the driver, + * additional capabilities might be supported by userspace; these are + * the 802.11 extended capabilities ("Extended Capabilities element") + * and are in the same format as in the information element. See + * 802.11-2012 8.4.2.29 for the defined fields. + * @extended_capabilities_mask: mask of the valid values + * @extended_capabilities_len: length of the extended capabilities */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2546,6 +2554,9 @@ struct wiphy { */ u32 probe_resp_offload; + const u8 *extended_capabilities, *extended_capabilities_mask; + u8 extended_capabilities_len; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3880f6ad7ed1..1fd6e5611896 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1355,6 +1355,12 @@ enum nl80211_commands { * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, * contains a value of enum nl80211_radar_event (u32). * + * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver + * has and handles. The format is the same as the IE contents. See + * 802.11-2012 8.4.2.29 for more information. + * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver + * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1635,6 +1641,9 @@ enum nl80211_attrs { NL80211_ATTR_RADAR_EVENT, + NL80211_ATTR_EXT_CAPA, + NL80211_ATTR_EXT_CAPA_MASK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7e40b9e82b45..1237431c3efa 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1363,6 +1363,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag dev->wiphy.max_acl_mac_addrs)) goto nla_put_failure; + if (dev->wiphy.extended_capabilities && + (nla_put(msg, NL80211_ATTR_EXT_CAPA, + dev->wiphy.extended_capabilities_len, + dev->wiphy.extended_capabilities) || + nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK, + dev->wiphy.extended_capabilities_len, + dev->wiphy.extended_capabilities_mask))) + goto nla_put_failure; + return genlmsg_end(msg, hdr); nla_put_failure: -- cgit v1.2.3 From c6f9d6c3bdeb337809d667ef2a41597229a1ce57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 11 Feb 2013 14:27:08 +0100 Subject: mac80211: advertise operating mode notification capability Use the new extended capabilities advertising to advertise the fact that operating mode notification is supported. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 ++ net/mac80211/main.c | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'net') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e085fcf52b26..7e24fe0cfbcd 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1776,6 +1776,8 @@ enum ieee80211_tdls_actioncode { #define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5) #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) +#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6) + /* TDLS specific payload type in the LLC/SNAP header */ #define WLAN_TDLS_SNAP_RFTYPE 0x2 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 9cdbc774cfd7..035344bc6b9c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -501,6 +501,11 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = { }, }; +static const u8 extended_capabilities[] = { + 0, 0, 0, 0, 0, 0, 0, + WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, const struct ieee80211_ops *ops) { @@ -557,6 +562,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, WIPHY_FLAG_REPORTS_OBSS | WIPHY_FLAG_OFFCHAN_TX; + wiphy->extended_capabilities = extended_capabilities; + wiphy->extended_capabilities_mask = extended_capabilities; + wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities); + if (ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; -- cgit v1.2.3 From 9d62a98617298c1da288f50e84c5dd67732e79b7 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 14 Feb 2013 21:10:13 +0200 Subject: cfg80211: Pass station (extended) capability info to kernel The information of the peer's capabilities and extended capabilities are required for the driver to perform TDLS Peer UAPSD operations and off channel operations. This information of the peer is passed from user space using NL80211_CMD_SET_STATION command. This commit enhances the function nl80211_set_station to pass the capability information of the peer to the driver. Similarly, there may be need for capability information for other modes, so allow this to be provided with both add_station and change_station. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++++++ include/uapi/linux/nl80211.h | 10 ++++++++++ net/wireless/nl80211.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) (limited to 'net') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a229046d86d4..fa2612952c19 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -626,12 +626,14 @@ enum plink_actions { /** * enum station_parameters_apply_mask - station parameter values to apply * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) + * @STATION_PARAM_APPLY_CAPABILITY: apply new capability * * Not all station parameters have in-band "no change" signalling, * for those that don't these flags will are used. */ enum station_parameters_apply_mask { STATION_PARAM_APPLY_UAPSD = BIT(0), + STATION_PARAM_APPLY_CAPABILITY = BIT(1), }; /** @@ -662,6 +664,9 @@ enum station_parameters_apply_mask { * see &enum station_parameters_apply_mask * @local_pm: local link-specific mesh power save mode (no change when set * to unknown) + * @capability: station capability + * @ext_capab: extended capabilities of the station + * @ext_capab_len: number of extended capabilities */ struct station_parameters { u8 *supported_rates; @@ -678,6 +683,9 @@ struct station_parameters { u8 uapsd_queues; u8 max_sp; enum nl80211_mesh_power_mode local_pm; + u16 capability; + u8 *ext_capab; + u8 ext_capab_len; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1fd6e5611896..f7c35ca01efc 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1361,6 +1361,13 @@ enum nl80211_commands { * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. * + * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to + * the driver, e.g., to enable TDLS power save (PU-APSD). + * + * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are + * advertised to the driver, e.g., to enable TDLS off channel operations + * and PU-APSD. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1644,6 +1651,9 @@ enum nl80211_attrs { NL80211_ATTR_EXT_CAPA, NL80211_ATTR_EXT_CAPA_MASK, + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1237431c3efa..be9f2b5a403f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -368,6 +368,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, + [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, }; /* policy for the key attributes */ @@ -3435,6 +3437,19 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); } + if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { + params.capability = + nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); + params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; + } + + if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { + params.ext_capab = + nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); + params.ext_capab_len = + nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); + } + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || info->attrs[NL80211_ATTR_HT_CAPABILITY]) return -EINVAL; @@ -3505,6 +3520,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) /* reject other things that can't change */ if (params.supported_rates) return -EINVAL; + if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) + return -EINVAL; + if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) + return -EINVAL; /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); @@ -3537,6 +3556,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (params.supported_rates) return -EINVAL; + if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) + return -EINVAL; + if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) + return -EINVAL; /* * No special handling for TDLS here -- the userspace * mesh code doesn't have this bug. @@ -3601,6 +3624,19 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (!params.aid || params.aid > IEEE80211_MAX_AID) return -EINVAL; + if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { + params.capability = + nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); + params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; + } + + if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { + params.ext_capab = + nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); + params.ext_capab_len = + nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); + } + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) params.ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); -- cgit v1.2.3 From df881293c6ba9a12868491a717b25cb14ec1fa4a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 14 Feb 2013 21:10:54 +0200 Subject: cfg80211: Pass TDLS peer's QoS/HT/VHT information during set_station The information of the peer's capabilities is required for the driver to perform TDLS Peer UAPSD operations. This information of the peer is passed by the supplicant using NL80211_CMD_SET_STATION command. This commit enhances the function nl80211_set_station to pass this information of the peer to the driver in case this command is used with the TDLS peer STA. In addition, make the HT/VHT capability configuration handled more consistently for other STA cases (reject both instead of just HT). Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 6 ++-- net/wireless/nl80211.c | 90 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 85 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0800fb331ce5..179dcbd8be1c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1412,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, return -ENOENT; } - /* in station mode, supported rates are only valid with TDLS */ + /* in station mode, some updates are only valid with TDLS */ if (sdata->vif.type == NL80211_IFTYPE_STATION && - params->supported_rates && + (params->supported_rates || params->ht_capa || params->vht_capa || + params->sta_modify_mask || + (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) && !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { mutex_unlock(&local->sta_mtx); return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index be9f2b5a403f..580ffeaef3d5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3409,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info, return ERR_PTR(ret); } +static struct nla_policy +nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { + [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, + [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, +}; + +static int nl80211_set_station_tdls(struct genl_info *info, + struct station_parameters *params) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct nlattr *tb[NL80211_STA_WME_MAX + 1]; + struct nlattr *nla; + int err; + + /* Can only set if TDLS ... */ + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -EOPNOTSUPP; + + /* ... with external setup is supported */ + if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -EOPNOTSUPP; + + /* Dummy STA entry gets updated once the peer capabilities are known */ + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) + params->ht_capa = + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) + params->vht_capa = + nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + + /* parse WME attributes if present */ + if (!info->attrs[NL80211_ATTR_STA_WME]) + return 0; + + nla = info->attrs[NL80211_ATTR_STA_WME]; + err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, + nl80211_sta_wme_policy); + if (err) + return err; + + if (tb[NL80211_STA_WME_UAPSD_QUEUES]) + params->uapsd_queues = nla_get_u8( + tb[NL80211_STA_WME_UAPSD_QUEUES]); + if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + return -EINVAL; + + if (tb[NL80211_STA_WME_MAX_SP]) + params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); + + if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) + return -EINVAL; + + params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; + + return 0; +} + static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -3450,8 +3507,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); } - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || - info->attrs[NL80211_ATTR_HT_CAPABILITY]) + if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) return -EINVAL; if (!rdev->ops->change_station) @@ -3524,6 +3580,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) return -EINVAL; + if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || + info->attrs[NL80211_ATTR_VHT_CAPABILITY]) + return -EINVAL; /* must be last in here for error handling */ params.vlan = get_vlan(info, rdev); @@ -3539,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) * to change the flag. */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); - /* fall through */ + /* Include parameters for TDLS peer (driver will check) */ + err = nl80211_set_station_tdls(info, ¶ms); + if (err) + return err; + /* disallow things sta doesn't support */ + if (params.plink_action) + return -EINVAL; + if (params.local_pm) + return -EINVAL; + /* reject any changes other than AUTHORIZED or WME (for TDLS) */ + if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_WME))) + return -EINVAL; + break; case NL80211_IFTYPE_ADHOC: /* disallow things sta doesn't support */ if (params.plink_action) return -EINVAL; if (params.local_pm) return -EINVAL; + if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || + info->attrs[NL80211_ATTR_VHT_CAPABILITY]) + return -EINVAL; /* reject any changes other than AUTHORIZED */ if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) return -EINVAL; @@ -3560,6 +3635,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) return -EINVAL; + if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || + info->attrs[NL80211_ATTR_VHT_CAPABILITY]) + return -EINVAL; /* * No special handling for TDLS here -- the userspace * mesh code doesn't have this bug. @@ -3584,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return err; } -static struct nla_policy -nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { - [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, - [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, -}; - static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; -- cgit v1.2.3 From 932dd97c5fef091dd6f605fb1d40143d67d91e09 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Feb 2013 11:56:13 +0100 Subject: nl80211: renumber NL80211_FEATURE_FULL_AP_CLIENT_STATE Adding the flag to mac80211 already without testing was clearly a mistake, one that we now pay for by having to reserve bit 13 forever. The problem is cfg80211 doesn't allow capability/rate changes for station entries that were added unassociated, so the station entries cannot be set up properly when marked associated. Change the NL80211_FEATURE_FULL_AP_CLIENT_STATE value to make it clear to userspace implementations that all current kernels don't actually support it, even though the previous bit is set, and of course also remove the flag from mac80211 until we test and fix the issues. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 3 ++- net/mac80211/main.c | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f7c35ca01efc..c46bb016f4e4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3471,8 +3471,9 @@ enum nl80211_feature_flags { NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, - NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, + /* bit 13 is reserved */ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, }; /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 035344bc6b9c..f9747689d604 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -572,8 +572,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | - NL80211_FEATURE_VIF_TXPOWER | - NL80211_FEATURE_FULL_AP_CLIENT_STATE; + NL80211_FEATURE_VIF_TXPOWER; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | -- cgit v1.2.3 From 586e01ededf9b713a1512dd658806791a7ca1a50 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Feb 2013 12:13:53 +0100 Subject: mac80211: prevent spurious HT/VHT downgrade message Even when connecting to an AP that doesn't support VHT, and even when the local device doesn't support it either, the downgrade message gets printed. Suppress the message if HT and/or VHT is disabled. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7a8cd789e487..9f6464f3e05f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -357,6 +357,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, ret = 0; out: + /* don't print the message below for VHT mismatch if VHT is disabled */ + if (ret & IEEE80211_STA_DISABLE_VHT) + vht_chandef = *chandef; + while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, IEEE80211_CHAN_DISABLED)) { if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { -- cgit v1.2.3 From bf7cd94dcc71682cd6af4a9028f95307b7db41c5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 14:40:31 +0100 Subject: mac80211: clean up mesh code There's various code with strange indentation, questionable loop and locking constructs, etc. The bigger change is moving the "sdata" argument to the first argument of all functions, like all other mac80211 functions that have one. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 14 +++--- net/mac80211/main.c | 3 +- net/mac80211/mesh.c | 115 +++++++++++++++++++++++--------------------- net/mac80211/mesh.h | 105 +++++++++++++++++++--------------------- net/mac80211/mesh_hwmp.c | 68 +++++++++++++------------- net/mac80211/mesh_pathtbl.c | 89 +++++++++++++++++----------------- net/mac80211/mesh_plink.c | 36 ++++++++------ net/mac80211/mesh_sync.c | 47 ++++++------------ net/mac80211/rx.c | 12 ++--- net/mac80211/tx.c | 26 +++++----- 10 files changed, 252 insertions(+), 263 deletions(-) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 179dcbd8be1c..09d96a8f6c2c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1500,13 +1500,13 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, return -ENOENT; } - err = mesh_path_add(dst, sdata); + err = mesh_path_add(sdata, dst); if (err) { rcu_read_unlock(); return err; } - mpath = mesh_path_lookup(dst, sdata); + mpath = mesh_path_lookup(sdata, dst); if (!mpath) { rcu_read_unlock(); return -ENXIO; @@ -1518,12 +1518,12 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, - u8 *dst) + u8 *dst) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (dst) - return mesh_path_del(dst, sdata); + return mesh_path_del(sdata, dst); mesh_path_flush_by_iface(sdata); return 0; @@ -1547,7 +1547,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, return -ENOENT; } - mpath = mesh_path_lookup(dst, sdata); + mpath = mesh_path_lookup(sdata, dst); if (!mpath) { rcu_read_unlock(); return -ENOENT; @@ -1611,7 +1611,7 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); - mpath = mesh_path_lookup(dst, sdata); + mpath = mesh_path_lookup(sdata, dst); if (!mpath) { rcu_read_unlock(); return -ENOENT; @@ -1632,7 +1632,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); rcu_read_lock(); - mpath = mesh_path_lookup_by_idx(idx, sdata); + mpath = mesh_path_lookup_by_idx(sdata, idx); if (!mpath) { rcu_read_unlock(); return -ENOENT; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f9747689d604..d0dd11153a6c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1173,8 +1173,7 @@ static void __exit ieee80211_exit(void) rc80211_minstrel_ht_exit(); rc80211_minstrel_exit(); - if (mesh_allocated) - ieee80211s_stop(); + ieee80211s_stop(); ieee80211_iface_exit(); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a77d40ed4e61..b0223326d9cd 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -17,7 +17,7 @@ #define TMR_RUNNING_MP 1 #define TMR_RUNNING_MPR 2 -int mesh_allocated; +static int mesh_allocated; static struct kmem_cache *rm_cache; bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt) @@ -36,6 +36,8 @@ void ieee80211s_init(void) void ieee80211s_stop(void) { + if (!mesh_allocated) + return; mesh_pathtbl_unregister(); kmem_cache_destroy(rm_cache); } @@ -90,24 +92,22 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) - goto mismatch; + return false; ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata), &basic_rates); if (sdata->vif.bss_conf.basic_rates != basic_rates) - goto mismatch; + return false; ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, ie->ht_operation, &sta_chan_def); if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, &sta_chan_def)) - goto mismatch; + return false; return true; -mismatch: - return false; } /** @@ -118,7 +118,7 @@ mismatch: bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & - IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; + IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; } /** @@ -196,11 +196,12 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) if (!sdata->u.mesh.rmc) return; - for (i = 0; i < RMC_BUCKETS; i++) + for (i = 0; i < RMC_BUCKETS; i++) { list_for_each_entry_safe(p, n, &rmc->bucket[i], list) { list_del(&p->list); kmem_cache_free(rm_cache, p); } + } kfree(rmc); sdata->u.mesh.rmc = NULL; @@ -209,6 +210,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) /** * mesh_rmc_check - Check frame in recent multicast cache and add if absent. * + * @sdata: interface * @sa: source address * @mesh_hdr: mesh_header * @@ -218,8 +220,8 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata) * received this frame lately. If the frame is not in the cache, it is added to * it. */ -int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, - struct ieee80211_sub_if_data *sdata) +int mesh_rmc_check(struct ieee80211_sub_if_data *sdata, + const u8 *sa, struct ieee80211s_hdr *mesh_hdr) { struct mesh_rmc *rmc = sdata->u.mesh.rmc; u32 seqnum = 0; @@ -233,12 +235,11 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) { ++entries; if (time_after(jiffies, p->exp_time) || - (entries == RMC_QUEUE_MAX_LEN)) { + entries == RMC_QUEUE_MAX_LEN) { list_del(&p->list); kmem_cache_free(rm_cache, p); --entries; - } else if ((seqnum == p->seqnum) && - (ether_addr_equal(sa, p->sa))) + } else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa)) return -1; } @@ -253,8 +254,8 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr, return 0; } -int -mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos, neighbors; @@ -285,19 +286,18 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) /* Mesh capability */ *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; *pos |= ifmsh->accepting_plinks ? - IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; + IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; /* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */ *pos |= ifmsh->ps_peers_deep_sleep ? - IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; + IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00; *pos++ |= ifmsh->adjusting_tbtt ? - IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; return 0; } -int -mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos; @@ -314,8 +314,8 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } -int mesh_add_awake_window_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +static int mesh_add_awake_window_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos; @@ -337,8 +337,8 @@ int mesh_add_awake_window_ie(struct sk_buff *skb, return 0; } -int -mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 offset, len; @@ -361,8 +361,7 @@ mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } -int -mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) +int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 len = 0; @@ -390,8 +389,8 @@ mesh_add_rsn_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) return 0; } -int mesh_add_ds_params_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +static int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; @@ -417,8 +416,8 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, return 0; } -int mesh_add_ht_cap_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); @@ -439,8 +438,8 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb, return 0; } -int mesh_add_ht_oper_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; @@ -475,6 +474,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, return 0; } + static void ieee80211_mesh_path_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = @@ -520,7 +520,7 @@ void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh) /** * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame - * @hdr: 802.11 frame header + * @hdr: 802.11 frame header * @fc: frame control field * @meshda: destination address in the mesh * @meshsa: source address address in the mesh. Same as TA, as frame is @@ -551,8 +551,8 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, /** * ieee80211_new_mesh_header - create a new mesh header - * @meshhdr: uninitialized mesh header * @sdata: mesh interface to be used + * @meshhdr: uninitialized mesh header * @addr4or5: 1st address in the ae header, which may correspond to address 4 * (if addr6 is NULL) or address 5 (if addr6 is present). It may * be NULL. @@ -561,32 +561,38 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, * * Return the header length. */ -int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata, char *addr4or5, - char *addr6) +int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, + struct ieee80211s_hdr *meshhdr, + const char *addr4or5, const char *addr6) { - int aelen = 0; - BUG_ON(!addr4or5 && addr6); + if (WARN_ON(!addr4or5 && addr6)) + return 0; + memset(meshhdr, 0, sizeof(*meshhdr)); + meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL; + + /* FIXME: racy -- TX on multiple queues can be concurrent */ put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum); sdata->u.mesh.mesh_seqnum++; + if (addr4or5 && !addr6) { meshhdr->flags |= MESH_FLAGS_AE_A4; - aelen += ETH_ALEN; memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); + return 2 * ETH_ALEN; } else if (addr4or5 && addr6) { meshhdr->flags |= MESH_FLAGS_AE_A5_A6; - aelen += 2 * ETH_ALEN; memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN); memcpy(meshhdr->eaddr2, addr6, ETH_ALEN); + return 3 * ETH_ALEN; } - return 6 + aelen; + + return ETH_ALEN; } -static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, - struct ieee80211_if_mesh *ifmsh) +static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 changed; ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); @@ -596,7 +602,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, ieee80211_mbss_info_change_notify(sdata, changed); mod_timer(&ifmsh->housekeeping_timer, - round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); + round_jiffies(jiffies + + IEEE80211_MESH_HOUSEKEEPING_INTERVAL)); } static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) @@ -708,7 +715,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) *pos++ = 0x0; if (ieee80211_add_srates_ie(sdata, skb, true, band) || - mesh_add_ds_params_ie(skb, sdata)) + mesh_add_ds_params_ie(sdata, skb)) goto out_free; bcn->head_len = skb->len; @@ -719,13 +726,13 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) bcn->tail = bcn->head + bcn->head_len; if (ieee80211_add_ext_srates_ie(sdata, skb, true, band) || - mesh_add_rsn_ie(skb, sdata) || - mesh_add_ht_cap_ie(skb, sdata) || - mesh_add_ht_oper_ie(skb, sdata) || - mesh_add_meshid_ie(skb, sdata) || - mesh_add_meshconf_ie(skb, sdata) || - mesh_add_awake_window_ie(skb, sdata) || - mesh_add_vendor_ies(skb, sdata)) + mesh_add_rsn_ie(sdata, skb) || + mesh_add_ht_cap_ie(sdata, skb) || + mesh_add_ht_oper_ie(sdata, skb) || + mesh_add_meshid_ie(sdata, skb) || + mesh_add_meshconf_ie(sdata, skb) || + mesh_add_awake_window_ie(sdata, skb) || + mesh_add_vendor_ies(sdata, skb)) goto out_free; bcn->tail_len = skb->len; @@ -1039,7 +1046,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) mesh_mpp_table_grow(); if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags)) - ieee80211_mesh_housekeeping(sdata, ifmsh); + ieee80211_mesh_housekeeping(sdata); if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags)) ieee80211_mesh_rootpath(sdata); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 1a1da877b1d2..336c88a16687 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -26,12 +26,12 @@ * @MESH_PATH_ACTIVE: the mesh path can be used for forwarding * @MESH_PATH_RESOLVING: the discovery process is running for this mesh path * @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence - * number + * number * @MESH_PATH_FIXED: the mesh path has been manually set and should not be - * modified + * modified * @MESH_PATH_RESOLVED: the mesh path can has been resolved * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination - * already queued up, waiting for the discovery process to start. + * already queued up, waiting for the discovery process to start. * * MESH_PATH_RESOLVED is used by the mesh path timer to * decide when to stop or cancel the mesh path discovery. @@ -73,16 +73,16 @@ enum mesh_deferred_task_flags { * @dst: mesh path destination mac address * @sdata: mesh subif * @next_hop: mesh neighbor to which frames for this destination will be - * forwarded + * forwarded * @timer: mesh path discovery timer * @frame_queue: pending queue for frames sent to this destination while the - * path is unresolved + * path is unresolved * @sn: target sequence number * @metric: current metric to this destination * @hop_count: hops to destination * @exp_time: in jiffies, when the path will expire or when it expired * @discovery_timeout: timeout (lapse in jiffies) used for the last discovery - * retry + * retry * @discovery_retries: number of discovery retries * @flags: mesh path flags, as specified on &enum mesh_path_flags * @state_lock: mesh path state lock used to protect changes to the @@ -206,38 +206,33 @@ struct mesh_rmc { /* Various */ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, const u8 *da, const u8 *sa); -int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr, - struct ieee80211_sub_if_data *sdata, char *addr4or5, - char *addr6); -int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr, - struct ieee80211_sub_if_data *sdata); +int ieee80211_new_mesh_header(struct ieee80211_sub_if_data *sdata, + struct ieee80211s_hdr *meshhdr, + const char *addr4or5, const char *addr6); +int mesh_rmc_check(struct ieee80211_sub_if_data *sdata, + const u8 *addr, struct ieee80211s_hdr *mesh_hdr); bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *ie); void mesh_ids_set_default(struct ieee80211_if_mesh *mesh); -void mesh_mgmt_ies_add(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_meshconf_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_meshid_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_rsn_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_awake_window_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_vendor_ies(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_ds_params_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_ht_cap_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_add_ht_oper_ie(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); +void mesh_mgmt_ies_add(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); void ieee80211s_update_metric(struct ieee80211_local *local, - struct sta_info *sta, struct sk_buff *skb); -void ieee80211s_stop(void); + struct sta_info *sta, struct sk_buff *skb); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); @@ -263,31 +258,32 @@ void ieee80211_mps_frame_release(struct sta_info *sta, struct ieee802_11_elems *elems); /* Mesh paths */ -int mesh_nexthop_lookup(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); -int mesh_nexthop_resolve(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); +int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata); -struct mesh_path *mesh_path_lookup(const u8 *dst, - struct ieee80211_sub_if_data *sdata); -struct mesh_path *mpp_path_lookup(u8 *dst, - struct ieee80211_sub_if_data *sdata); -int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata); -struct mesh_path *mesh_path_lookup_by_idx(int idx, - struct ieee80211_sub_if_data *sdata); +struct mesh_path *mesh_path_lookup(struct ieee80211_sub_if_data *sdata, + const u8 *dst); +struct mesh_path *mpp_path_lookup(struct ieee80211_sub_if_data *sdata, + const u8 *dst); +int mpp_path_add(struct ieee80211_sub_if_data *sdata, + const u8 *dst, const u8 *mpp); +struct mesh_path * +mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len); -int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata); + struct ieee80211_mgmt *mgmt, size_t len); +int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst); int mesh_path_add_gate(struct mesh_path *mpath); int mesh_path_send_to_gates(struct mesh_path *mpath); int mesh_gate_num(struct ieee80211_sub_if_data *sdata); + /* Mesh plinks */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr, - struct ieee802_11_elems *ie); + u8 *hw_addr, struct ieee802_11_elems *ie); bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie); u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata); void mesh_plink_broken(struct sta_info *sta); @@ -304,19 +300,19 @@ void mesh_sta_cleanup(struct sta_info *sta); void mesh_mpath_table_grow(void); void mesh_mpp_table_grow(void); /* Mesh paths */ -int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn, - __le16 target_rcode, const u8 *ra, - struct ieee80211_sub_if_data *sdata); +int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, + u8 ttl, const u8 *target, __le32 target_sn, + __le16 target_rcode, const u8 *ra); void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); void mesh_path_flush_pending(struct mesh_path *mpath); void mesh_path_tx_pending(struct mesh_path *mpath); int mesh_pathtbl_init(void); void mesh_pathtbl_unregister(void); -int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata); +int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr); void mesh_path_timer(unsigned long data); void mesh_path_flush_by_nexthop(struct sta_info *sta); -void mesh_path_discard_frame(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata); +void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata); void mesh_path_restart(struct ieee80211_sub_if_data *sdata); void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); @@ -325,8 +321,6 @@ bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); extern int mesh_paths_generation; #ifdef CONFIG_MAC80211_MESH -extern int mesh_allocated; - static inline u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { @@ -371,8 +365,8 @@ void mesh_plink_quiesce(struct sta_info *sta); void mesh_plink_restart(struct sta_info *sta); void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata); void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata); +void ieee80211s_stop(void); #else -#define mesh_allocated 0 static inline void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {} static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata) @@ -385,6 +379,7 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) { return false; } static inline void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) {} +static inline void ieee80211s_stop(void) {} #endif #endif /* IEEE80211S_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 585c1e26cca8..bdb8d3b14587 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -238,9 +238,9 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, * also acquires in the TX path. To avoid a deadlock we don't transmit the * frame directly but add it to the pending queue instead. */ -int mesh_path_error_tx(u8 ttl, const u8 *target, __le32 target_sn, - __le16 target_rcode, const u8 *ra, - struct ieee80211_sub_if_data *sdata) +int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata, + u8 ttl, const u8 *target, __le32 target_sn, + __le16 target_rcode, const u8 *ra) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; @@ -430,7 +430,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, process = false; fresh_info = false; } else { - mpath = mesh_path_lookup(orig_addr, sdata); + mpath = mesh_path_lookup(sdata, orig_addr); if (mpath) { spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_FIXED) @@ -445,8 +445,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, } } } else { - mesh_path_add(orig_addr, sdata); - mpath = mesh_path_lookup(orig_addr, sdata); + mesh_path_add(sdata, orig_addr); + mpath = mesh_path_lookup(sdata, orig_addr); if (!mpath) { rcu_read_unlock(); return 0; @@ -478,7 +478,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, else { fresh_info = true; - mpath = mesh_path_lookup(ta, sdata); + mpath = mesh_path_lookup(sdata, ta); if (mpath) { spin_lock_bh(&mpath->state_lock); if ((mpath->flags & MESH_PATH_FIXED) || @@ -486,8 +486,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, (last_hop_metric > mpath->metric))) fresh_info = false; } else { - mesh_path_add(ta, sdata); - mpath = mesh_path_lookup(ta, sdata); + mesh_path_add(sdata, ta); + mpath = mesh_path_lookup(sdata, ta); if (!mpath) { rcu_read_unlock(); return 0; @@ -553,7 +553,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, } else if (is_broadcast_ether_addr(target_addr) && (target_flags & IEEE80211_PREQ_TO_FLAG)) { rcu_read_lock(); - mpath = mesh_path_lookup(orig_addr, sdata); + mpath = mesh_path_lookup(sdata, orig_addr); if (mpath) { if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) { reply = true; @@ -568,7 +568,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); } else { rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(sdata, target_addr); if (mpath) { if ((!(mpath->flags & MESH_PATH_SN_VALID)) || SN_LT(mpath->sn, target_sn)) { @@ -678,7 +678,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, } rcu_read_lock(); - mpath = mesh_path_lookup(orig_addr, sdata); + mpath = mesh_path_lookup(sdata, orig_addr); if (mpath) spin_lock_bh(&mpath->state_lock); else @@ -736,7 +736,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, target_rcode = PERR_IE_TARGET_RCODE(perr_elem); rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(sdata, target_addr); if (mpath) { struct sta_info *sta; @@ -751,9 +751,10 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata, spin_unlock_bh(&mpath->state_lock); if (!ifmsh->mshcfg.dot11MeshForwarding) goto endperr; - mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn), + mesh_path_error_tx(sdata, ttl, target_addr, + cpu_to_le32(target_sn), cpu_to_le16(target_rcode), - broadcast_addr, sdata); + broadcast_addr); } else spin_unlock_bh(&mpath->state_lock); } @@ -801,10 +802,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, metric_txsta = airtime_link_metric_get(local, sta); - mpath = mesh_path_lookup(orig_addr, sdata); + mpath = mesh_path_lookup(sdata, orig_addr); if (!mpath) { - mesh_path_add(orig_addr, sdata); - mpath = mesh_path_lookup(orig_addr, sdata); + mesh_path_add(sdata, orig_addr); + mpath = mesh_path_lookup(sdata, orig_addr); if (!mpath) { rcu_read_unlock(); sdata->u.mesh.mshstats.dropped_frames_no_route++; @@ -861,8 +862,7 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; size_t baselen; @@ -1006,7 +1006,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); rcu_read_lock(); - mpath = mesh_path_lookup(preq_node->dst, sdata); + mpath = mesh_path_lookup(sdata, preq_node->dst); if (!mpath) goto enddiscovery; @@ -1076,8 +1076,8 @@ enddiscovery: * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. * skb is freeed here if no mpath could be allocated. */ -int mesh_nexthop_resolve(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1091,17 +1091,17 @@ int mesh_nexthop_resolve(struct sk_buff *skb, return 0; rcu_read_lock(); - err = mesh_nexthop_lookup(skb, sdata); + err = mesh_nexthop_lookup(sdata, skb); if (!err) goto endlookup; /* no nexthop found, start resolving */ - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(sdata, target_addr); if (!mpath) { - mesh_path_add(target_addr, sdata); - mpath = mesh_path_lookup(target_addr, sdata); + mesh_path_add(sdata, target_addr); + mpath = mesh_path_lookup(sdata, target_addr); if (!mpath) { - mesh_path_discard_frame(skb, sdata); + mesh_path_discard_frame(sdata, skb); err = -ENOSPC; goto endlookup; } @@ -1118,12 +1118,13 @@ int mesh_nexthop_resolve(struct sk_buff *skb, skb_queue_tail(&mpath->frame_queue, skb); err = -ENOENT; if (skb_to_free) - mesh_path_discard_frame(skb_to_free, sdata); + mesh_path_discard_frame(sdata, skb_to_free); endlookup: rcu_read_unlock(); return err; } + /** * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling * this function is considered "using" the associated mpath, so preempt a path @@ -1134,8 +1135,8 @@ endlookup: * * Returns: 0 if the next hop was found. Nonzero otherwise. */ -int mesh_nexthop_lookup(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { struct mesh_path *mpath; struct sta_info *next_hop; @@ -1144,7 +1145,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb, int err = -ENOENT; rcu_read_lock(); - mpath = mesh_path_lookup(target_addr, sdata); + mpath = mesh_path_lookup(sdata, target_addr); if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) goto endlookup; @@ -1203,8 +1204,7 @@ void mesh_path_timer(unsigned long data) } } -void -mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) +void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 2ce4c4023a97..6b3c4e119c63 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -24,9 +24,12 @@ /* Keep the mean chain length below this constant */ #define MEAN_CHAIN_LEN 2 -#define MPATH_EXPIRED(mpath) ((mpath->flags & MESH_PATH_ACTIVE) && \ - time_after(jiffies, mpath->exp_time) && \ - !(mpath->flags & MESH_PATH_FIXED)) +static inline bool mpath_expired(struct mesh_path *mpath) +{ + return (mpath->flags & MESH_PATH_ACTIVE) && + time_after(jiffies, mpath->exp_time) && + !(mpath->flags & MESH_PATH_FIXED); +} struct mpath_node { struct hlist_node list; @@ -185,8 +188,8 @@ static u32 mesh_table_hash(const u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl) { /* Use last four bytes of hw addr and interface index as hash index */ - return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, tbl->hash_rnd) - & tbl->hash_mask; + return jhash_2words(*(u32 *)(addr+2), sdata->dev->ifindex, + tbl->hash_rnd) & tbl->hash_mask; } @@ -339,7 +342,7 @@ static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst, mpath = node->mpath; if (mpath->sdata == sdata && ether_addr_equal(dst, mpath->dst)) { - if (MPATH_EXPIRED(mpath)) { + if (mpath_expired(mpath)) { spin_lock_bh(&mpath->state_lock); mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&mpath->state_lock); @@ -352,20 +355,21 @@ static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst, /** * mesh_path_lookup - look up a path in the mesh path table - * @dst: hardware address (ETH_ALEN length) of destination * @sdata: local subif + * @dst: hardware address (ETH_ALEN length) of destination * * Returns: pointer to the mesh path structure, or NULL if not found * * Locking: must be called within a read rcu section. */ -struct mesh_path *mesh_path_lookup(const u8 *dst, - struct ieee80211_sub_if_data *sdata) +struct mesh_path * +mesh_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) { return mpath_lookup(rcu_dereference(mesh_paths), dst, sdata); } -struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) +struct mesh_path * +mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) { return mpath_lookup(rcu_dereference(mpp_paths), dst, sdata); } @@ -380,7 +384,8 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) * * Locking: must be called within a read rcu section. */ -struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) +struct mesh_path * +mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) { struct mesh_table *tbl = rcu_dereference(mesh_paths); struct mpath_node *node; @@ -392,7 +397,7 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data if (sdata && node->mpath->sdata != sdata) continue; if (j++ == idx) { - if (MPATH_EXPIRED(node->mpath)) { + if (mpath_expired(node->mpath)) { spin_lock_bh(&node->mpath->state_lock); node->mpath->flags &= ~MESH_PATH_ACTIVE; spin_unlock_bh(&node->mpath->state_lock); @@ -436,11 +441,10 @@ int mesh_path_add_gate(struct mesh_path *mpath) spin_lock_bh(&tbl->gates_lock); hlist_add_head_rcu(&new_gate->list, tbl->known_gates); spin_unlock_bh(&tbl->gates_lock); - rcu_read_unlock(); mpath_dbg(mpath->sdata, "Mesh path: Recorded new gate: %pM. %d known gates\n", mpath->dst, mpath->sdata->u.mesh.num_gates); - return 0; + err = 0; err_rcu: rcu_read_unlock(); return err; @@ -451,30 +455,27 @@ err_rcu: * @tbl: table which holds our list of known gates * @mpath: gate mpath * - * Returns: 0 on success - * * Locking: must be called inside rcu_read_lock() section */ -static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) +static void mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) { struct mpath_node *gate; struct hlist_node *p, *q; - hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) - if (gate->mpath == mpath) { - spin_lock_bh(&tbl->gates_lock); - hlist_del_rcu(&gate->list); - kfree_rcu(gate, rcu); - spin_unlock_bh(&tbl->gates_lock); - mpath->sdata->u.mesh.num_gates--; - mpath->is_gate = false; - mpath_dbg(mpath->sdata, - "Mesh path: Deleted gate: %pM. %d known gates\n", - mpath->dst, mpath->sdata->u.mesh.num_gates); - break; - } - - return 0; + hlist_for_each_entry_safe(gate, p, q, tbl->known_gates, list) { + if (gate->mpath != mpath) + continue; + spin_lock_bh(&tbl->gates_lock); + hlist_del_rcu(&gate->list); + kfree_rcu(gate, rcu); + spin_unlock_bh(&tbl->gates_lock); + mpath->sdata->u.mesh.num_gates--; + mpath->is_gate = false; + mpath_dbg(mpath->sdata, + "Mesh path: Deleted gate: %pM. %d known gates\n", + mpath->dst, mpath->sdata->u.mesh.num_gates); + break; + } } /** @@ -488,14 +489,14 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata) /** * mesh_path_add - allocate and add a new path to the mesh path table - * @addr: destination address of the path (ETH_ALEN length) + * @dst: destination address of the path (ETH_ALEN length) * @sdata: local subif * * Returns: 0 on success * * State: the initial state of the new path is set to 0 */ -int mesh_path_add(const u8 *dst, struct ieee80211_sub_if_data *sdata) +int mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; @@ -630,7 +631,8 @@ void mesh_mpp_table_grow(void) write_unlock_bh(&pathtbl_resize_lock); } -int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) +int mpp_path_add(struct ieee80211_sub_if_data *sdata, + const u8 *dst, const u8 *mpp) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; @@ -739,9 +741,10 @@ void mesh_plink_broken(struct sta_info *sta) mpath->flags &= ~MESH_PATH_ACTIVE; ++mpath->sn; spin_unlock_bh(&mpath->state_lock); - mesh_path_error_tx(sdata->u.mesh.mshcfg.element_ttl, - mpath->dst, cpu_to_le32(mpath->sn), - reason, bcast, sdata); + mesh_path_error_tx(sdata, + sdata->u.mesh.mshcfg.element_ttl, + mpath->dst, cpu_to_le32(mpath->sn), + reason, bcast); } } rcu_read_unlock(); @@ -856,7 +859,7 @@ void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) * * Returns: 0 if successful */ -int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) +int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct mesh_table *tbl; struct mesh_path *mpath; @@ -965,8 +968,8 @@ int mesh_path_send_to_gates(struct mesh_path *mpath) * * Locking: the function must me called within a rcu_read_lock region */ -void mesh_path_discard_frame(struct sk_buff *skb, - struct ieee80211_sub_if_data *sdata) +void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; @@ -984,7 +987,7 @@ void mesh_path_flush_pending(struct mesh_path *mpath) struct sk_buff *skb; while ((skb = skb_dequeue(&mpath->frame_queue)) != NULL) - mesh_path_discard_frame(skb, mpath->sdata); + mesh_path_discard_frame(mpath->sdata, skb); } /** @@ -1105,7 +1108,7 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata) if ((!(mpath->flags & MESH_PATH_RESOLVING)) && (!(mpath->flags & MESH_PATH_FIXED)) && time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) - mesh_path_del(mpath->dst, mpath->sdata); + mesh_path_del(mpath->sdata, mpath->dst); } rcu_read_unlock(); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f7526e509aa8..0b58e8139937 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -38,8 +38,8 @@ enum plink_event { }; static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, - enum ieee80211_self_protected_actioncode action, - u8 *da, __le16 llid, __le16 plid, __le16 reason); + enum ieee80211_self_protected_actioncode action, + u8 *da, __le16 llid, __le16 plid, __le16 reason); /** * mesh_plink_fsm_restart - restart a mesh peer link finite state machine @@ -231,8 +231,9 @@ u32 mesh_plink_deactivate(struct sta_info *sta) } static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, - enum ieee80211_self_protected_actioncode action, - u8 *da, __le16 llid, __le16 plid, __le16 reason) { + enum ieee80211_self_protected_actioncode action, + u8 *da, __le16 llid, __le16 plid, __le16 reason) +{ struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_tx_info *info; @@ -283,13 +284,13 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, } if (ieee80211_add_srates_ie(sdata, skb, true, band) || ieee80211_add_ext_srates_ie(sdata, skb, true, band) || - mesh_add_rsn_ie(skb, sdata) || - mesh_add_meshid_ie(skb, sdata) || - mesh_add_meshconf_ie(skb, sdata)) + mesh_add_rsn_ie(sdata, skb) || + mesh_add_meshid_ie(sdata, skb) || + mesh_add_meshconf_ie(sdata, skb)) goto free; } else { /* WLAN_SP_MESH_PEERING_CLOSE */ info->flags |= IEEE80211_TX_CTL_NO_ACK; - if (mesh_add_meshid_ie(skb, sdata)) + if (mesh_add_meshid_ie(sdata, skb)) goto free; } @@ -333,12 +334,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, } if (action != WLAN_SP_MESH_PEERING_CLOSE) { - if (mesh_add_ht_cap_ie(skb, sdata) || - mesh_add_ht_oper_ie(skb, sdata)) + if (mesh_add_ht_cap_ie(sdata, skb) || + mesh_add_ht_oper_ie(sdata, skb)) goto free; } - if (mesh_add_vendor_ies(skb, sdata)) + if (mesh_add_vendor_ies(sdata, skb)) goto free; ieee80211_tx_skb(sdata, skb); @@ -666,8 +667,9 @@ u32 mesh_plink_block(struct sta_info *sta) } -void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - size_t len, struct ieee80211_rx_status *rx_status) +void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) { struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; struct ieee802_11_elems elems; @@ -680,7 +682,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m u8 *baseaddr; u32 changed = 0; __le16 plid, llid, reason; - static const char *mplstates[] = { + static const char * const mplstates[] = { [NL80211_PLINK_LISTEN] = "LISTEN", [NL80211_PLINK_OPN_SNT] = "OPN-SNT", [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", @@ -708,13 +710,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); + if (!elems.peering) { mpl_dbg(sdata, "Mesh plink: missing necessary peer link ie\n"); return; } + if (elems.rsn_len && - sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { + sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { mpl_dbg(sdata, "Mesh plink: can't establish link with secure peer\n"); return; @@ -733,7 +737,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } if (ftype != WLAN_SP_MESH_PEERING_CLOSE && - (!elems.mesh_id || !elems.mesh_config)) { + (!elems.mesh_id || !elems.mesh_config)) { mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); return; } diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index aa8d1e437385..05a256b38e24 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -43,7 +43,7 @@ struct sync_method { static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & - IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; } void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) @@ -112,7 +112,8 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); - msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", sta->sta.addr); + msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", + sta->sta.addr); goto no_sync; } @@ -129,18 +130,15 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, sta->t_offset = t_t - t_r; if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { - s64 t_clockdrift = sta->t_offset_setpoint - - sta->t_offset; + s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset; msync_dbg(sdata, "STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n", - sta->sta.addr, - (long long) sta->t_offset, - (long long) - sta->t_offset_setpoint, + sta->sta.addr, (long long) sta->t_offset, + (long long) sta->t_offset_setpoint, (long long) t_clockdrift); if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || - t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { + t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { msync_dbg(sdata, "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", sta->sta.addr, @@ -149,15 +147,10 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, goto no_sync; } - rcu_read_unlock(); - spin_lock_bh(&ifmsh->sync_offset_lock); - if (t_clockdrift > - ifmsh->sync_offset_clockdrift_max) - ifmsh->sync_offset_clockdrift_max - = t_clockdrift; + if (t_clockdrift > ifmsh->sync_offset_clockdrift_max) + ifmsh->sync_offset_clockdrift_max = t_clockdrift; spin_unlock_bh(&ifmsh->sync_offset_lock); - } else { sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); @@ -165,9 +158,7 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, "STA %pM : offset was invalid, sta->t_offset=%lld\n", sta->sta.addr, (long long) sta->t_offset); - rcu_read_unlock(); } - return; no_sync: rcu_read_unlock(); @@ -177,14 +168,12 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - WARN_ON(ifmsh->mesh_sp_id - != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); + WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); BUG_ON(!rcu_read_lock_held()); spin_lock_bh(&ifmsh->sync_offset_lock); - if (ifmsh->sync_offset_clockdrift_max > - TOFFSET_MINIMUM_ADJUSTMENT) { + if (ifmsh->sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT) { /* Since ajusting the tsf here would * require a possibly blocking call * to the driver tsf setter, we punt @@ -193,8 +182,7 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) msync_dbg(sdata, "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", ifmsh->sync_offset_clockdrift_max); - set_bit(MESH_WORK_DRIFT_ADJUST, - &ifmsh->wrkq_flags); + set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); ifmsh->adjusting_tbtt = true; } else { @@ -220,14 +208,11 @@ static const struct sync_method sync_methods[] = { const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) { - const struct ieee80211_mesh_sync_ops *ops = NULL; - u8 i; + int i; for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { - if (sync_methods[i].method == method) { - ops = &sync_methods[i].ops; - break; - } + if (sync_methods[i].method == method) + return &sync_methods[i].ops; } - return ops; + return NULL; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3acb70b73e22..bb73ed2d20b9 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2027,7 +2027,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) /* frame is in RMC, don't forward */ if (ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1) && - mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata)) + mesh_rmc_check(rx->sdata, hdr->addr3, mesh_hdr)) return RX_DROP_MONITOR; if (!ieee80211_is_data(hdr->frame_control) || @@ -2054,9 +2054,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } rcu_read_lock(); - mppath = mpp_path_lookup(proxied_addr, sdata); + mppath = mpp_path_lookup(sdata, proxied_addr); if (!mppath) { - mpp_path_add(proxied_addr, mpp_addr, sdata); + mpp_path_add(sdata, proxied_addr, mpp_addr); } else { spin_lock_bh(&mppath->state_lock); if (!ether_addr_equal(mppath->mpp, mpp_addr)) @@ -2104,13 +2104,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN); /* update power mode indication when forwarding */ ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr); - } else if (!mesh_nexthop_lookup(fwd_skb, sdata)) { + } else if (!mesh_nexthop_lookup(sdata, fwd_skb)) { /* mesh power mode flags updated in mesh_nexthop_lookup */ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast); } else { /* unable to resolve next hop */ - mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, - 0, reason, fwd_hdr->addr2, sdata); + mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl, + fwd_hdr->addr3, 0, reason, fwd_hdr->addr2); IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); kfree_skb(fwd_skb); return RX_DROP_MONITOR; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index fe644f91ae05..5b9602b62405 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1495,7 +1495,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_is_data(hdr->frame_control) && is_unicast_ether_addr(hdr->addr1)) { - if (mesh_nexthop_resolve(skb, sdata)) + if (mesh_nexthop_resolve(sdata, skb)) return; /* skb queued: don't free */ } else { ieee80211_mps_set_frame_flags(sdata, NULL, hdr); @@ -1844,9 +1844,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, } if (!is_multicast_ether_addr(skb->data)) { - mpath = mesh_path_lookup(skb->data, sdata); + mpath = mesh_path_lookup(sdata, skb->data); if (!mpath) - mppath = mpp_path_lookup(skb->data, sdata); + mppath = mpp_path_lookup(sdata, skb->data); } /* @@ -1859,8 +1859,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, !(mppath && !ether_addr_equal(mppath->mpp, skb->data))) { hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc, skb->data, skb->data + ETH_ALEN); - meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, - sdata, NULL, NULL); + meshhdrlen = ieee80211_new_mesh_header(sdata, &mesh_hdr, + NULL, NULL); } else { /* DS -> MBSS (802.11-2012 13.11.3.3). * For unicast with unknown forwarding information, @@ -1879,18 +1879,14 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, mesh_da, sdata->vif.addr); if (is_multicast_ether_addr(mesh_da)) /* DA TA mSA AE:SA */ - meshhdrlen = - ieee80211_new_mesh_header(&mesh_hdr, - sdata, - skb->data + ETH_ALEN, - NULL); + meshhdrlen = ieee80211_new_mesh_header( + sdata, &mesh_hdr, + skb->data + ETH_ALEN, NULL); else /* RA TA mDA mSA AE:DA SA */ - meshhdrlen = - ieee80211_new_mesh_header(&mesh_hdr, - sdata, - skb->data, - skb->data + ETH_ALEN); + meshhdrlen = ieee80211_new_mesh_header( + sdata, &mesh_hdr, skb->data, + skb->data + ETH_ALEN); } chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); -- cgit v1.2.3 From 8ffb5c001a6d1ef142ef550861e0b97e291f6469 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Fri, 15 Feb 2013 10:18:34 -0500 Subject: mac80211: enable vif.cab_queue for mesh Since mesh powersaving was added, pending bcast/mcast frames may go out the CAB queue now. Unfortunately, the queue was only set up for AP mode, so we would try to tx on the IEEE80211_INVAL_HW_QUEUE. Allow cab_queue for mesh interfaces as well. Fixes the following warning (or crash without MAC80211_VERBOSE_DEBUG): WARNING: at net/mac80211/tx.c:1223 __ieee80211_tx+0x162/0x35f [mac80211]() Modules linked in: mac80211_hwsim mac80211 cfg80211 [...] Pid: 3085, comm: avahi-daemon Tainted: G W 3.8.0-rc1+ #377 Call Trace: [] warn_slowpath_common+0x83/0x9c [] warn_slowpath_null+0x1a/0x1c [] __ieee80211_tx+0x162/0x35f [mac80211] [] ieee80211_tx+0xd3/0xf9 [mac80211] [] ieee80211_xmit+0xcc/0xd5 [mac80211] [] ieee80211_subif_start_xmit+0xc53/0xcd8 [mac80211] [] dev_hard_start_xmit+0x259/0x3ce [] sch_direct_xmit+0x74/0x17d [] dev_queue_xmit+0x230/0x414 [] ip_finish_output2+0x348/0x3aa [] ip_finish_output+0x6c/0x71 [] NF_HOOK_COND.constprop.44+0x18/0x58 [] ip_mc_output+0x134/0x13c [] dst_output+0x18/0x1c [] ip_local_out+0x20/0x24 [] ip_send_skb+0x16/0x3c [] udp_send_skb+0x254/0x2b9 [] udp_sendmsg+0x5a8/0x7d4 Signed-off-by: Bob Copeland Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 86c83084542a..a1f7194295fe 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -294,7 +294,8 @@ static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata) } } - if ((sdata->vif.type != NL80211_IFTYPE_AP) || + if ((sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) || !(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) { sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; return 0; -- cgit v1.2.3 From 15ac7c478fd227257e4be94519c6f965a9e6159d Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 15 Feb 2013 13:15:48 -0600 Subject: mac80211: Fix incorrect use of STA_PR_FMT in trace points Several tracepoints are using STA_PR_FMT where STA_PR_ARG should be used, resulting in messages like "phy0 sta:ARG TYPE NOT FIELD BUT 1". Change these to STA_PR_ARG. Signed-off-by: Seth Forshee Signed-off-by: Johannes Berg --- net/mac80211/trace.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 1183c4a4fee5..3d7cd2a0582f 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -479,7 +479,7 @@ TRACE_EVENT(drv_set_tim, TP_printk( LOCAL_PR_FMT STA_PR_FMT " set:%d", - LOCAL_PR_ARG, STA_PR_FMT, __entry->set + LOCAL_PR_ARG, STA_PR_ARG, __entry->set ) ); @@ -1684,7 +1684,7 @@ TRACE_EVENT(api_sta_block_awake, TP_printk( LOCAL_PR_FMT STA_PR_FMT " block:%d", - LOCAL_PR_ARG, STA_PR_FMT, __entry->block + LOCAL_PR_ARG, STA_PR_ARG, __entry->block ) ); @@ -1782,7 +1782,7 @@ TRACE_EVENT(api_eosp, TP_printk( LOCAL_PR_FMT STA_PR_FMT, - LOCAL_PR_ARG, STA_PR_FMT + LOCAL_PR_ARG, STA_PR_ARG ) ); -- cgit v1.2.3 From 14bbd6a565e1bcdc240d44687edb93f721cfdf99 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 14 Feb 2013 09:44:49 +0000 Subject: net: Add skb_unclone() helper function. This function will be used in next GRE_GSO patch. This patch does not change any functionality. Signed-off-by: Pravin B Shelar Acked-by: Eric Dumazet --- drivers/net/ppp/ppp_generic.c | 3 +-- include/linux/skbuff.h | 10 ++++++++++ net/ipv4/ah4.c | 3 +-- net/ipv4/ip_fragment.c | 2 +- net/ipv4/tcp_output.c | 2 +- net/ipv4/xfrm4_input.c | 2 +- net/ipv4/xfrm4_mode_tunnel.c | 3 +-- net/ipv6/ah6.c | 3 +-- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/reassembly.c | 2 +- net/ipv6/xfrm6_mode_tunnel.c | 3 +-- net/sched/act_ipt.c | 6 ++---- net/sched/act_pedit.c | 3 +-- 13 files changed, 23 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 0b2706abe3e3..4fd754e74eb2 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1805,8 +1805,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) /* the filter instructions are constructed assuming a four-byte PPP header on each packet */ if (ppp->pass_filter || ppp->active_filter) { - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) goto err; *skb_push(skb, 2) = 0; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9da99520ccd5..ca6ee7d93edb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -804,6 +804,16 @@ static inline int skb_cloned(const struct sk_buff *skb) (atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1; } +static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) +{ + might_sleep_if(pri & __GFP_WAIT); + + if (skb_cloned(skb)) + return pskb_expand_head(skb, 0, 0, pri); + + return 0; +} + /** * skb_header_cloned - is the header a clone * @skb: buffer to check diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index a69b4e4a02b5..2e7f1948216f 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -321,8 +321,7 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb) /* We are going to _remove_ AH header to keep sockets happy, * so... Later this can change. */ - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) goto out; skb->ip_summed = CHECKSUM_NONE; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 1211613c6c34..b6d30acb600c 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -590,7 +590,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, goto out_oversize; /* Head of list must not be cloned. */ - if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) + if (skb_unclone(head, GFP_ATOMIC)) goto out_nomem; /* If the first fragment is fragmented itself, we split diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 6182d90e97b0..fd0cea114b5d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1331,7 +1331,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) /* Remove acked data from a packet in the transmit queue. */ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) { - if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) return -ENOMEM; __pskb_trim_head(skb, len); diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 06814b6216dc..1f12c8b45864 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -132,7 +132,7 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) * header and optional ESP marker bytes) and then modify the * protocol to ESP, and then call into the transform receiver. */ - if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) goto drop; /* Now we can update and verify the packet length... */ diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index ddee0a099a2c..1162ace30838 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -142,8 +142,7 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) for_each_input_rcu(rcv_notify_handlers, handler) handler->handler(skb); - if (skb_cloned(skb) && - (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + if (err = skb_unclone(skb, GFP_ATOMIC)) goto out; if (x->props.flags & XFRM_STATE_DECAP_DSCP) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 384233188ac1..bb02e176cb70 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -521,8 +521,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) /* We are going to _remove_ AH header to keep sockets happy, * so... Later this can change. */ - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) goto out; skb->ip_summed = CHECKSUM_NONE; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index c674f158efa8..b89a8c3186cd 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -368,7 +368,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) } /* Head of list must not be cloned. */ - if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) { + if (skb_unclone(head, GFP_ATOMIC)) { pr_debug("skb is cloned but can't expand head"); goto out_oom; } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index bab2c270f292..e354743ed426 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -404,7 +404,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, goto out_oversize; /* Head of list must not be cloned. */ - if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) + if (skb_unclone(head, GFP_ATOMIC)) goto out_oom; /* If the first fragment is fragmented itself, we split diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 9f2095b19ad0..93c41a81c4c3 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -69,8 +69,7 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; - if (skb_cloned(skb) && - (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + if (err = skb_unclone(skb, GFP_ATOMIC)) goto out; if (x->props.flags & XFRM_STATE_DECAP_DSCP) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 0fb9e3f567e6..e0f6de64afec 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -207,10 +207,8 @@ static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a, struct tcf_ipt *ipt = a->priv; struct xt_action_param par; - if (skb_cloned(skb)) { - if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - return TC_ACT_UNSPEC; - } + if (skb_unclone(skb, GFP_ATOMIC)) + return TC_ACT_UNSPEC; spin_lock(&ipt->tcf_lock); diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 0c3faddf3f2c..7ed78c9e505c 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -131,8 +131,7 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a, int i, munged = 0; unsigned int off; - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + if (skb_unclone(skb, GFP_ATOMIC)) return p->tcf_action; off = skb_network_offset(skb); -- cgit v1.2.3 From 05e8ef4ab2d8087d360e814d14da20b9f7fb2283 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 14 Feb 2013 09:44:55 +0000 Subject: net: factor out skb_mac_gso_segment() from skb_gso_segment() This function will be used in next GRE_GSO patch. This patch does not change any functionality. It only exports skb_mac_gso_segment() function. [ Use skb_reset_mac_len() -DaveM ] Signed-off-by: Pravin B Shelar Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 79 ++++++++++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9deb672d999f..920361bc27e7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2671,6 +2671,8 @@ extern void netdev_upper_dev_unlink(struct net_device *dev, extern int skb_checksum_help(struct sk_buff *skb); extern struct sk_buff *__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path); +extern struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, + netdev_features_t features); static inline struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) diff --git a/net/core/dev.c b/net/core/dev.c index f44473696b8b..67deae60214c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2327,37 +2327,20 @@ out: } EXPORT_SYMBOL(skb_checksum_help); -/* openvswitch calls this on rx path, so we need a different check. - */ -static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) -{ - if (tx_path) - return skb->ip_summed != CHECKSUM_PARTIAL; - else - return skb->ip_summed == CHECKSUM_NONE; -} - /** - * __skb_gso_segment - Perform segmentation on skb. + * skb_mac_gso_segment - mac layer segmentation handler. * @skb: buffer to segment * @features: features for the output path (see dev->features) - * @tx_path: whether it is called in TX path - * - * This function segments the given skb and returns a list of segments. - * - * It may return NULL if the skb requires no segmentation. This is - * only possible when GSO is used for verifying header integrity. */ -struct sk_buff *__skb_gso_segment(struct sk_buff *skb, - netdev_features_t features, bool tx_path) +struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, + netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); struct packet_offload *ptype; __be16 type = skb->protocol; - int vlan_depth = ETH_HLEN; - int err; while (type == htons(ETH_P_8021Q)) { + int vlan_depth = ETH_HLEN; struct vlan_hdr *vh; if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) @@ -2368,22 +2351,14 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, vlan_depth += VLAN_HLEN; } - skb_reset_mac_header(skb); - skb->mac_len = skb->network_header - skb->mac_header; __skb_pull(skb, skb->mac_len); - if (unlikely(skb_needs_check(skb, tx_path))) { - skb_warn_bad_offload(skb); - - if (skb_header_cloned(skb) && - (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) - return ERR_PTR(err); - } - rcu_read_lock(); list_for_each_entry_rcu(ptype, &offload_base, list) { if (ptype->type == type && ptype->callbacks.gso_segment) { if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) { + int err; + err = ptype->callbacks.gso_send_check(skb); segs = ERR_PTR(err); if (err || skb_gso_ok(skb, features)) @@ -2401,6 +2376,48 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, return segs; } +EXPORT_SYMBOL(skb_mac_gso_segment); + + +/* openvswitch calls this on rx path, so we need a different check. + */ +static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) +{ + if (tx_path) + return skb->ip_summed != CHECKSUM_PARTIAL; + else + return skb->ip_summed == CHECKSUM_NONE; +} + +/** + * __skb_gso_segment - Perform segmentation on skb. + * @skb: buffer to segment + * @features: features for the output path (see dev->features) + * @tx_path: whether it is called in TX path + * + * This function segments the given skb and returns a list of segments. + * + * It may return NULL if the skb requires no segmentation. This is + * only possible when GSO is used for verifying header integrity. + */ +struct sk_buff *__skb_gso_segment(struct sk_buff *skb, + netdev_features_t features, bool tx_path) +{ + if (unlikely(skb_needs_check(skb, tx_path))) { + int err; + + skb_warn_bad_offload(skb); + + if (skb_header_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + return ERR_PTR(err); + } + + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + + return skb_mac_gso_segment(skb, features); +} EXPORT_SYMBOL(__skb_gso_segment); /* Take action when hardware reception checksum errors are detected. */ -- cgit v1.2.3 From 68c331631143f5f039baac99a650e0b9e1ea02b6 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 14 Feb 2013 14:02:41 +0000 Subject: v4 GRE: Add TCP segmentation offload for GRE Following patch adds GRE protocol offload handler so that skb_gso_segment() can segment GRE packets. SKB GSO CB is added to keep track of total header length so that skb_segment can push entire header. e.g. in case of GRE, skb_segment need to push inner and outer headers to every segment. New NETIF_F_GRE_GSO feature is added for devices which support HW GRE TSO offload. Currently none of devices support it therefore GRE GSO always fall backs to software GSO. [ Compute pkt_len before ip_local_out() invocation. -DaveM ] Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/linux/netdev_features.h | 3 +- include/linux/skbuff.h | 17 ++++++ net/core/dev.c | 1 + net/core/ethtool.c | 1 + net/core/skbuff.c | 6 +- net/ipv4/af_inet.c | 1 + net/ipv4/gre.c | 118 ++++++++++++++++++++++++++++++++++++++++ net/ipv4/ip_gre.c | 82 +++++++++++++++++++++++++--- net/ipv4/tcp.c | 1 + net/ipv4/udp.c | 3 +- net/ipv6/ip6_offload.c | 1 + net/ipv6/udp_offload.c | 3 +- 12 files changed, 226 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 5ac32123035a..3dd39340430e 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -41,7 +41,7 @@ enum { NETIF_F_TSO_ECN_BIT, /* ... TCP ECN support */ NETIF_F_TSO6_BIT, /* ... TCPv6 segmentation */ NETIF_F_FSO_BIT, /* ... FCoE segmentation */ - NETIF_F_GSO_RESERVED1, /* ... free (fill GSO_MASK to 8 bits) */ + NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */ /**/NETIF_F_GSO_LAST, /* [can't be last bit, see GSO_MASK] */ NETIF_F_GSO_RESERVED2 /* ... free (fill GSO_MASK to 8 bits) */ = NETIF_F_GSO_LAST, @@ -102,6 +102,7 @@ enum { #define NETIF_F_VLAN_CHALLENGED __NETIF_F(VLAN_CHALLENGED) #define NETIF_F_RXFCS __NETIF_F(RXFCS) #define NETIF_F_RXALL __NETIF_F(RXALL) +#define NETIF_F_GRE_GSO __NETIF_F(GSO_GRE) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ca6ee7d93edb..821c7f45d2a7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -314,6 +314,8 @@ enum { SKB_GSO_TCPV6 = 1 << 4, SKB_GSO_FCOE = 1 << 5, + + SKB_GSO_GRE = 1 << 6, }; #if BITS_PER_LONG > 32 @@ -2732,6 +2734,21 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb) } #endif +/* Keeps track of mac header offset relative to skb->head. + * It is useful for TSO of Tunneling protocol. e.g. GRE. + * For non-tunnel skb it points to skb_mac_header() and for + * tunnel skb it points to outer mac header. */ +struct skb_gso_cb { + int mac_offset; +}; +#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) + +static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) +{ + return (skb_mac_header(inner_skb) - inner_skb->head) - + SKB_GSO_CB(inner_skb)->mac_offset; +} + static inline bool skb_is_gso(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_size; diff --git a/net/core/dev.c b/net/core/dev.c index 67deae60214c..1cd6297fd34b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2413,6 +2413,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, return ERR_PTR(err); } + SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb); skb_reset_mac_header(skb); skb_reset_mac_len(skb); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index d9d55209db67..3e9b2c3e30f0 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -77,6 +77,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation", [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", + [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6c1ad09f8796..2a3ca33c30aa 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2738,6 +2738,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) unsigned int mss = skb_shinfo(skb)->gso_size; unsigned int doffset = skb->data - skb_mac_header(skb); unsigned int offset = doffset; + unsigned int tnl_hlen = skb_tnl_header_len(skb); unsigned int headroom; unsigned int len; int sg = !!(features & NETIF_F_SG); @@ -2814,7 +2815,10 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) skb_set_network_header(nskb, skb->mac_len); nskb->transport_header = (nskb->network_header + skb_network_header_len(skb)); - skb_copy_from_linear_data(skb, nskb->data, doffset); + + skb_copy_from_linear_data_offset(skb, -tnl_hlen, + nskb->data - tnl_hlen, + doffset + tnl_hlen); if (fskb != skb_shinfo(skb)->frag_list) continue; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e6e5d8506336..e225a4e5b572 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1287,6 +1287,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | + SKB_GSO_GRE | 0))) goto out; diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c index 42a491055c76..7a4c710c4cdd 100644 --- a/net/ipv4/gre.c +++ b/net/ipv4/gre.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,11 @@ static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; static DEFINE_SPINLOCK(gre_proto_lock); +struct gre_base_hdr { + __be16 flags; + __be16 protocol; +}; +#define GRE_HEADER_SECTION 4 int gre_add_protocol(const struct gre_protocol *proto, u8 version) { @@ -112,12 +118,117 @@ static void gre_err(struct sk_buff *skb, u32 info) rcu_read_unlock(); } +static struct sk_buff *gre_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + netdev_features_t enc_features; + int ghl = GRE_HEADER_SECTION; + struct gre_base_hdr *greh; + int mac_len = skb->mac_len; + int tnl_hlen; + bool csum; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_TCPV6 | + SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_GRE))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*greh)))) + goto out; + + greh = (struct gre_base_hdr *)skb_transport_header(skb); + + if (greh->flags & GRE_KEY) + ghl += GRE_HEADER_SECTION; + if (greh->flags & GRE_SEQ) + ghl += GRE_HEADER_SECTION; + if (greh->flags & GRE_CSUM) { + ghl += GRE_HEADER_SECTION; + csum = true; + } else + csum = false; + + /* setup inner skb. */ + if (greh->protocol == htons(ETH_P_TEB)) { + struct ethhdr *eth = eth_hdr(skb); + skb->protocol = eth->h_proto; + } else { + skb->protocol = greh->protocol; + } + + skb->encapsulation = 0; + + if (unlikely(!pskb_may_pull(skb, ghl))) + goto out; + __skb_pull(skb, ghl); + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb_inner_network_offset(skb)); + skb->mac_len = skb_inner_network_offset(skb); + + /* segment inner packet. */ + enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); + segs = skb_mac_gso_segment(skb, enc_features); + if (!segs || IS_ERR(segs)) + goto out; + + skb = segs; + tnl_hlen = skb_tnl_header_len(skb); + do { + __skb_push(skb, ghl); + if (csum) { + __be32 *pcsum; + + if (skb_has_shared_frag(skb)) { + int err; + + err = __skb_linearize(skb); + if (err) { + kfree_skb(segs); + segs = ERR_PTR(err); + goto out; + } + } + + greh = (struct gre_base_hdr *)(skb->data); + pcsum = (__be32 *)(greh + 1); + *pcsum = 0; + *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0)); + } + __skb_push(skb, tnl_hlen - ghl); + + skb_reset_mac_header(skb); + skb_set_network_header(skb, mac_len); + skb->mac_len = mac_len; + } while ((skb = skb->next)); +out: + return segs; +} + +static int gre_gso_send_check(struct sk_buff *skb) +{ + if (!skb->encapsulation) + return -EINVAL; + return 0; +} + static const struct net_protocol net_gre_protocol = { .handler = gre_rcv, .err_handler = gre_err, .netns_ok = 1, }; +static const struct net_offload gre_offload = { + .callbacks = { + .gso_send_check = gre_gso_send_check, + .gso_segment = gre_gso_segment, + }, +}; + static int __init gre_init(void) { pr_info("GRE over IPv4 demultiplexor driver\n"); @@ -127,11 +238,18 @@ static int __init gre_init(void) return -EAGAIN; } + if (inet_add_offload(&gre_offload, IPPROTO_GRE)) { + pr_err("can't add protocol offload\n"); + inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); + return -EAGAIN; + } + return 0; } static void __exit gre_exit(void) { + inet_del_offload(&gre_offload, IPPROTO_GRE); inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); } diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 00a14b9864ea..a56f1182c176 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -735,8 +735,33 @@ drop: return 0; } +static struct sk_buff *handle_offloads(struct sk_buff *skb) +{ + int err; + + if (skb_is_gso(skb)) { + err = skb_unclone(skb, GFP_ATOMIC); + if (unlikely(err)) + goto error; + skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; + return skb; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + err = skb_checksum_help(skb); + if (unlikely(err)) + goto error; + } + skb->ip_summed = CHECKSUM_NONE; + + return skb; + +error: + kfree_skb(skb); + return ERR_PTR(err); +} + static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { + struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); struct ip_tunnel *tunnel = netdev_priv(dev); const struct iphdr *old_iph; const struct iphdr *tiph; @@ -751,10 +776,19 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev __be32 dst; int mtu; u8 ttl; + int err; + int pkt_len; - if (skb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(skb)) - goto tx_error; + skb = handle_offloads(skb); + if (IS_ERR(skb)) { + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + if (!skb->encapsulation) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } old_iph = ip_hdr(skb); @@ -855,7 +889,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev if (skb->protocol == htons(ETH_P_IP)) { df |= (old_iph->frag_off&htons(IP_DF)); - if ((old_iph->frag_off&htons(IP_DF)) && + if (!skb_is_gso(skb) && + (old_iph->frag_off&htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) { icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); ip_rt_put(rt); @@ -875,7 +910,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } } - if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) { + if (!skb_is_gso(skb) && + mtu >= IPV6_MIN_MTU && + mtu < skb->len - tunnel->hlen + gre_hlen) { icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); ip_rt_put(rt); goto tx_error; @@ -936,6 +973,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev iph->daddr = fl4.daddr; iph->saddr = fl4.saddr; iph->ttl = ttl; + iph->id = 0; if (ttl == 0) { if (skb->protocol == htons(ETH_P_IP)) @@ -964,9 +1002,19 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev *ptr = tunnel->parms.o_key; ptr--; } - if (tunnel->parms.o_flags&GRE_CSUM) { + /* Skip GRE checksum if skb is getting offloaded. */ + if (!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE) && + (tunnel->parms.o_flags&GRE_CSUM)) { int offset = skb_transport_offset(skb); + if (skb_has_shared_frag(skb)) { + err = __skb_linearize(skb); + if (err) { + ip_rt_put(rt); + goto tx_error; + } + } + *ptr = 0; *(__sum16 *)ptr = csum_fold(skb_checksum(skb, offset, skb->len - offset, @@ -974,7 +1022,19 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } } - iptunnel_xmit(skb, dev); + nf_reset(skb); + + pkt_len = skb->len - skb_transport_offset(skb); + err = ip_local_out(skb); + if (likely(net_xmit_eval(err) == 0)) { + u64_stats_update_begin(&tstats->syncp); + tstats->tx_bytes += pkt_len; + tstats->tx_packets++; + u64_stats_update_end(&tstats->syncp); + } else { + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; + } return NETDEV_TX_OK; #if IS_ENABLED(CONFIG_IPV6) @@ -1044,6 +1104,11 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) mtu = 68; tunnel->hlen = addend; + /* TCP offload with GRE SEQ is not supported. */ + if (!(tunnel->parms.o_flags & GRE_SEQ)) { + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->hw_features |= NETIF_F_GSO_SOFTWARE; + } return mtu; } @@ -1593,6 +1658,9 @@ static void ipgre_tap_setup(struct net_device *dev) dev->iflink = 0; dev->features |= NETIF_F_NETNS_LOCAL; + + dev->features |= GRE_FEATURES; + dev->hw_features |= GRE_FEATURES; } static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1f0bedb8622f..7a5ba48c2cc9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3043,6 +3043,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | + SKB_GSO_GRE | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) goto out; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6791aac06ea9..39a5e7a9a77f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2305,7 +2305,8 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, /* Packet is from an untrusted source, reset gso_segs. */ int type = skb_shinfo(skb)->gso_type; - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index f26f0da7f095..8234c1dcdf72 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -99,6 +99,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | + SKB_GSO_GRE | SKB_GSO_TCPV6 | 0))) goto out; diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 0c8934a317c2..cf05cf073c51 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -56,7 +56,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, /* Packet is from an untrusted source, reset gso_segs. */ int type = skb_shinfo(skb)->gso_type; - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) || + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; -- cgit v1.2.3 From efd9450e7e36717f24dff3bd584faa80a85231d6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 Feb 2013 17:31:48 +0000 Subject: net: use skb_reset_mac_len() in dev_gro_receive() We no longer need to use mac_len, lets cleanup things. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 1cd6297fd34b..6ad37896a324 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3802,7 +3802,6 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff __be16 type = skb->protocol; struct list_head *head = &offload_base; int same_flow; - int mac_len; enum gro_result ret; if (!(skb->dev->features & NETIF_F_GRO) || netpoll_rx_on(skb)) @@ -3819,8 +3818,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff continue; skb_set_network_header(skb, skb_gro_offset(skb)); - mac_len = skb->network_header - skb->mac_header; - skb->mac_len = mac_len; + skb_reset_mac_len(skb); NAPI_GRO_CB(skb)->same_flow = 0; NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; -- cgit v1.2.3 From c5c73dca596894c47760e4e955877b731ffabf57 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Thu, 14 Feb 2013 14:43:33 +0000 Subject: tipc: fix missing spinlock init in broadcast code After commit 3c294cb3 "tipc: remove the bearer congestion mechanism", we try to grab the broadcast bearer lock when sending multicast messages over the broadcast link. This will cause an oops because the lock is never initialized. This is an old bug, but the lock was never actually used before commit 3c294cb3, so that why it was not visible until now. The oops will look something like: BUG: spinlock bad magic on CPU#2, daemon/147 lock: bcast_bearer+0x48/0xffffffffffffd19a [tipc], .magic: 00000000, .owner: /-1, .owner_cpu: 0 Pid: 147, comm: daemon Not tainted 3.8.0-rc3+ #206 Call Trace: spin_dump+0x8a/0x8f spin_bug+0x21/0x26 do_raw_spin_lock+0x114/0x150 _raw_spin_lock_bh+0x19/0x20 tipc_bearer_blocked+0x1f/0x40 [tipc] tipc_link_send_buf+0x82/0x280 [tipc] ? __alloc_skb+0x9f/0x2b0 tipc_bclink_send_msg+0x77/0xa0 [tipc] tipc_multicast+0x11b/0x1b0 [tipc] send_msg+0x225/0x530 [tipc] sock_sendmsg+0xca/0xe0 The above can be triggered by running the multicast demo program. Signed-off-by: Erik Hugne Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- net/tipc/bcast.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 54f89f90ac33..2655c9f4ecad 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -774,6 +774,7 @@ void tipc_bclink_init(void) bcl->owner = &bclink->node; bcl->max_pkt = MAX_PKT_DEFAULT_MCAST; tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT); + spin_lock_init(&bcbearer->bearer.lock); bcl->b_ptr = &bcbearer->bearer; bcl->state = WORKING_WORKING; strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME); -- cgit v1.2.3 From 57467e56293796f780e91a24600a732516f534ac Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Sun, 20 Jan 2013 23:30:08 +0100 Subject: tipc: eliminate duplicated discard_rx_queue routine The tipc function discard_rx_queue() is just a duplicated implementation of __skb_queue_purge(). Remove the former and directly invoke __skb_queue_purge(). In doing so, the underscores convey to the code reader, more information about the current locking state that is assumed. Signed-off-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: Paul Gortmaker --- net/tipc/socket.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9b4e4833a484..f6ceecd44749 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -128,19 +128,6 @@ static void advance_rx_queue(struct sock *sk) kfree_skb(__skb_dequeue(&sk->sk_receive_queue)); } -/** - * discard_rx_queue - discard all buffers in socket receive queue - * - * Caller must hold socket lock - */ -static void discard_rx_queue(struct sock *sk) -{ - struct sk_buff *buf; - - while ((buf = __skb_dequeue(&sk->sk_receive_queue))) - kfree_skb(buf); -} - /** * reject_rx_queue - reject all buffers in socket receive queue * @@ -292,7 +279,7 @@ static int release(struct socket *sock) res = tipc_deleteport(tport->ref); /* Discard any remaining (connection-based) messages in receive queue */ - discard_rx_queue(sk); + __skb_queue_purge(&sk->sk_receive_queue); /* Reject any messages that accumulated in backlog queue */ sock->state = SS_DISCONNECTING; @@ -1637,7 +1624,7 @@ restart: case SS_DISCONNECTING: /* Discard any unreceived messages */ - discard_rx_queue(sk); + __skb_queue_purge(&sk->sk_receive_queue); /* Wake up anyone sleeping in poll */ sk->sk_state_change(sk); -- cgit v1.2.3 From aba79f332f46ca8529f3e62a9fc2926c8fe75e44 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Sun, 20 Jan 2013 23:30:09 +0100 Subject: tipc: byte-based overload control on socket receive queue Change overload control to be purely byte-based, using sk->sk_rmem_alloc as byte counter, and compare it to a calculated upper limit for the socket receive queue. For all connection messages, irrespective of message importance, the overload limit is set to a constant value (i.e, 67MB). This limit should normally never be reached because of the lower limit used by the flow control algorithm, and is there only as a last resort in case a faulty peer doesn't respect the send window limit. For datagram messages, message importance is taken into account when calculating the overload limit. The calculation is based on sk->sk_rcvbuf, and is hence configurable via the socket option SO_RCVBUF. Cc: Neil Horman Signed-off-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: Paul Gortmaker --- net/tipc/socket.c | 77 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f6ceecd44749..cbe2f6ecf07a 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -43,7 +43,8 @@ #define SS_LISTENING -1 /* socket is listening */ #define SS_READY -2 /* socket is connectionless */ -#define OVERLOAD_LIMIT_BASE 10000 +#define CONN_OVERLOAD_LIMIT ((TIPC_FLOW_CONTROL_WIN * 2 + 1) * \ + SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE)) #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ struct tipc_sock { @@ -202,7 +203,6 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol, sock_init_data(sock, sk); sk->sk_backlog_rcv = backlog_rcv; - sk->sk_rcvbuf = TIPC_FLOW_CONTROL_WIN * 2 * TIPC_MAX_USER_MSG_SIZE * 2; sk->sk_data_ready = tipc_data_ready; sk->sk_write_space = tipc_write_space; tipc_sk(sk)->p = tp_ptr; @@ -1141,34 +1141,6 @@ static void tipc_data_ready(struct sock *sk, int len) rcu_read_unlock(); } -/** - * rx_queue_full - determine if receive queue can accept another message - * @msg: message to be added to queue - * @queue_size: current size of queue - * @base: nominal maximum size of queue - * - * Returns 1 if queue is unable to accept message, 0 otherwise - */ -static int rx_queue_full(struct tipc_msg *msg, u32 queue_size, u32 base) -{ - u32 threshold; - u32 imp = msg_importance(msg); - - if (imp == TIPC_LOW_IMPORTANCE) - threshold = base; - else if (imp == TIPC_MEDIUM_IMPORTANCE) - threshold = base * 2; - else if (imp == TIPC_HIGH_IMPORTANCE) - threshold = base * 100; - else - return 0; - - if (msg_connected(msg)) - threshold *= 4; - - return queue_size >= threshold; -} - /** * filter_connect - Handle all incoming messages for a connection-based socket * @tsock: TIPC socket @@ -1246,6 +1218,36 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) return retval; } +/** + * rcvbuf_limit - get proper overload limit of socket receive queue + * @sk: socket + * @buf: message + * + * For all connection oriented messages, irrespective of importance, + * the default overload value (i.e. 67MB) is set as limit. + * + * For all connectionless messages, by default new queue limits are + * as belows: + * + * TIPC_LOW_IMPORTANCE (5MB) + * TIPC_MEDIUM_IMPORTANCE (10MB) + * TIPC_HIGH_IMPORTANCE (20MB) + * TIPC_CRITICAL_IMPORTANCE (40MB) + * + * Returns overload limit according to corresponding message importance + */ +static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + unsigned int limit; + + if (msg_connected(msg)) + limit = CONN_OVERLOAD_LIMIT; + else + limit = sk->sk_rcvbuf << (msg_importance(msg) + 5); + return limit; +} + /** * filter_rcv - validate incoming message * @sk: socket @@ -1262,7 +1264,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) { struct socket *sock = sk->sk_socket; struct tipc_msg *msg = buf_msg(buf); - u32 recv_q_len; + unsigned int limit = rcvbuf_limit(sk, buf); u32 res = TIPC_OK; /* Reject message if it is wrong sort of message for socket */ @@ -1279,15 +1281,13 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) } /* Reject message if there isn't room to queue it */ - recv_q_len = skb_queue_len(&sk->sk_receive_queue); - if (unlikely(recv_q_len >= (OVERLOAD_LIMIT_BASE / 2))) { - if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE / 2)) - return TIPC_ERR_OVERLOAD; - } + if (sk_rmem_alloc_get(sk) + buf->truesize >= limit) + return TIPC_ERR_OVERLOAD; - /* Enqueue message (finally!) */ + /* Enqueue message */ TIPC_SKB_CB(buf)->handle = 0; __skb_queue_tail(&sk->sk_receive_queue, buf); + skb_set_owner_r(buf, sk); sk->sk_data_ready(sk, 0); return TIPC_OK; @@ -1336,7 +1336,7 @@ static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf) if (!sock_owned_by_user(sk)) { res = filter_rcv(sk, buf); } else { - if (sk_add_backlog(sk, buf, sk->sk_rcvbuf)) + if (sk_add_backlog(sk, buf, rcvbuf_limit(sk, buf))) res = TIPC_ERR_OVERLOAD; else res = TIPC_OK; @@ -1570,6 +1570,7 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags) } else { __skb_dequeue(&sk->sk_receive_queue); __skb_queue_head(&new_sk->sk_receive_queue, buf); + skb_set_owner_r(buf, new_sk); } release_sock(new_sk); -- cgit v1.2.3 From 97f8b87e9108485a0b7070645662253561304458 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Thu, 31 Jan 2013 21:51:47 +0100 Subject: tipc: remove redundant checking for the number of iovecs in a send request As the number of iovecs in a send request is already limited within UIO_MAXIOV(i.e. 1024) in __sys_sendmsg(), it's unnecessary to check it again in TIPC stack. Signed-off-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: Paul Gortmaker --- net/tipc/socket.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/tipc/socket.c b/net/tipc/socket.c index cbe2f6ecf07a..a9622b6cd916 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -503,8 +503,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, if (unlikely((m->msg_namelen < sizeof(*dest)) || (dest->family != AF_TIPC))) return -EINVAL; - if ((total_len > TIPC_MAX_USER_MSG_SIZE) || - (m->msg_iovlen > (unsigned int)INT_MAX)) + if (total_len > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE; if (iocb) @@ -612,8 +611,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, if (unlikely(dest)) return send_msg(iocb, sock, m, total_len); - if ((total_len > TIPC_MAX_USER_MSG_SIZE) || - (m->msg_iovlen > (unsigned int)INT_MAX)) + if (total_len > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE; if (iocb) @@ -698,8 +696,7 @@ static int send_stream(struct kiocb *iocb, struct socket *sock, goto exit; } - if ((total_len > (unsigned int)INT_MAX) || - (m->msg_iovlen > (unsigned int)INT_MAX)) { + if (total_len > (unsigned int)INT_MAX) { res = -EMSGSIZE; goto exit; } -- cgit v1.2.3 From 572078be54856bf751af9a43d501ffb8d09b1ea7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 11:47:50 +0100 Subject: mac80211: fix harmless station flush warning If an interface is set down while authenticating or associating, there's a station entry that will be removed by the flushing in do_stop() and that will cause a warning. It's otherwise harmless, but avoid the warning by calling ieee80211_mgd_stop() first. Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a1f7194295fe..2c059e54e885 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -696,6 +696,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_roc_purge(sdata); + if (sdata->vif.type == NL80211_IFTYPE_STATION) + ieee80211_mgd_stop(sdata); + /* * Remove all stations associated with this interface. * @@ -783,8 +786,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, } } spin_unlock_irqrestore(&ps->bc_buf.lock, flags); - } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { - ieee80211_mgd_stop(sdata); } if (going_down) -- cgit v1.2.3 From 52ac8c488774b7ac6d1b0ed4c323b5de90d39d6e Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 16 Feb 2013 10:41:04 -0800 Subject: mac80211: clean up mesh HT operation ieee80211_ht_cap_ie_to_sta_ht_cap() will clean up the ht_supported flag and station bandwidth field for us if the peer beacon doesn't have an HT capability element (is operating as non-HT). Also, we don't really need a special station ch_width member to track the station operating mode any more so use sta.bandwidth instead. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 62 ++++++++++++++++++----------------------------- net/mac80211/sta_info.h | 2 -- 2 files changed, 24 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 0b58e8139937..d74bdf94c8a4 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -129,7 +129,6 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; - u32 changed = 0; u16 ht_opmode; bool non_ht_sta = false, ht20_sta = false; @@ -142,23 +141,19 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) sta->plink_state != NL80211_PLINK_ESTAB) continue; - switch (sta->ch_width) { - case NL80211_CHAN_WIDTH_20_NOHT: - mpl_dbg(sdata, - "mesh_plink %pM: nonHT sta (%pM) is present\n", - sdata->vif.addr, sta->sta.addr); + if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) + continue; + + if (!sta->sta.ht_cap.ht_supported) { + mpl_dbg(sdata, "nonHT sta (%pM) is present\n", + sta->sta.addr); non_ht_sta = true; - goto out; - case NL80211_CHAN_WIDTH_20: - mpl_dbg(sdata, - "mesh_plink %pM: HT20 sta (%pM) is present\n", - sdata->vif.addr, sta->sta.addr); - ht20_sta = true; - default: break; } + + mpl_dbg(sdata, "HT20 sta (%pM) is present\n", sta->sta.addr); + ht20_sta = true; } -out: rcu_read_unlock(); if (non_ht_sta) @@ -169,16 +164,13 @@ out: else ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; - if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { - sdata->vif.bss_conf.ht_operation_mode = ht_opmode; - sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; - changed = BSS_CHANGED_HT; - mpl_dbg(sdata, - "mesh_plink %pM: protection mode changed to %d\n", - sdata->vif.addr, ht_opmode); - } + if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode) + return 0; - return changed; + sdata->vif.bss_conf.ht_operation_mode = ht_opmode; + sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; + mpl_dbg(sdata, "selected new HT protection mode %d\n", ht_opmode); + return BSS_CHANGED_HT; } /** @@ -371,24 +363,18 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, if (sta->sta.supp_rates[band] != rates) changed |= IEEE80211_RC_SUPP_RATES_CHANGED; sta->sta.supp_rates[band] = rates; - if (elems->ht_cap_elem && - sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, - elems->ht_cap_elem, sta); - else - memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); - if (elems->ht_operation) { - struct cfg80211_chan_def chandef; + if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + elems->ht_cap_elem, sta)) + changed |= IEEE80211_RC_BW_CHANGED; - if (!(elems->ht_operation->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) - sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; - ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, - elems->ht_operation, &chandef); - if (sta->ch_width != chandef.width) + /* HT peer is operating 20MHz-only */ + if (elems->ht_operation && + !(elems->ht_operation->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { + if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20) changed |= IEEE80211_RC_BW_CHANGED; - sta->ch_width = chandef.width; + sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; } if (insert) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 63dfdb5e91da..4947341a2a82 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -285,7 +285,6 @@ struct sta_ampdu_mlme { * @t_offset: timing offset relative to this host * @t_offset_setpoint: reference timing offset of this sta to be used when * calculating clockdrift - * @ch_width: peer's channel width * @local_pm: local link-specific power save mode * @peer_pm: peer-specific power save mode towards local STA * @nonpeer_pm: STA power save mode towards non-peer neighbors @@ -386,7 +385,6 @@ struct sta_info { struct timer_list plink_timer; s64 t_offset; s64 t_offset_setpoint; - enum nl80211_chan_width ch_width; /* mesh power save */ enum nl80211_mesh_power_mode local_pm; enum nl80211_mesh_power_mode peer_pm; -- cgit v1.2.3 From 95e48addba6f9271aed51d9d3eaccbfc99a914b4 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 16 Feb 2013 10:41:05 -0800 Subject: mac80211: stringify mesh peering events Convert mesh peering events into strings and make the debug output a little easier to read. Also stop printing the llid and plid since these don't change across peering states and are random numbers anyway so they just amount to noise. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index d74bdf94c8a4..07d396d57079 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -37,6 +37,28 @@ enum plink_event { CLS_IGNR }; +static const char * const mplstates[] = { + [NL80211_PLINK_LISTEN] = "LISTEN", + [NL80211_PLINK_OPN_SNT] = "OPN-SNT", + [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", + [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", + [NL80211_PLINK_ESTAB] = "ESTAB", + [NL80211_PLINK_HOLDING] = "HOLDING", + [NL80211_PLINK_BLOCKED] = "BLOCKED" +}; + +static const char * const mplevents[] = { + [PLINK_UNDEFINED] = "NONE", + [OPN_ACPT] = "OPN_ACPT", + [OPN_RJCT] = "OPN_RJCT", + [OPN_IGNR] = "OPN_IGNR", + [CNF_ACPT] = "CNF_ACPT", + [CNF_RJCT] = "CNF_RJCT", + [CNF_IGNR] = "CNF_IGNR", + [CLS_ACPT] = "CLS_ACPT", + [CLS_IGNR] = "CLS_IGNR" +}; + static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason); @@ -668,15 +690,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, u8 *baseaddr; u32 changed = 0; __le16 plid, llid, reason; - static const char * const mplstates[] = { - [NL80211_PLINK_LISTEN] = "LISTEN", - [NL80211_PLINK_OPN_SNT] = "OPN-SNT", - [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", - [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", - [NL80211_PLINK_ESTAB] = "ESTAB", - [NL80211_PLINK_HOLDING] = "HOLDING", - [NL80211_PLINK_BLOCKED] = "BLOCKED" - }; /* need action_code, aux */ if (len < IEEE80211_MIN_ACTION_SIZE + 3) @@ -849,11 +862,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, } } - mpl_dbg(sdata, - "Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", - mgmt->sa, mplstates[sta->plink_state], - le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), - event); + mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa, + mplstates[sta->plink_state], mplevents[event]); reason = 0; spin_lock_bh(&sta->lock); switch (sta->plink_state) { -- cgit v1.2.3 From c39ac036add6b0696fb61b4d4733d671ca031723 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Sat, 16 Feb 2013 10:41:06 -0800 Subject: mac80211: don't spam mesh probe response messages If mesh plink debugging is enabled, this gets annoying in a crowded environment, fast. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index b0223326d9cd..29ce2aa87e7b 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -925,7 +925,6 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); memcpy(hdr->da, mgmt->sa, ETH_ALEN); - mpl_dbg(sdata, "sending probe resp. to %pM\n", hdr->da); IEEE80211_SKB_CB(presp)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, presp); out: -- cgit v1.2.3 From 279e9f2ff7351a7908f964332e778049712cbd5b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 15 Feb 2013 18:14:48 +0000 Subject: ipv6: optimize inet6_hash_frag() Use ipv6_addr_hash() and a single jhash invocation. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv6/reassembly.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'net') diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index e354743ed426..9a6428aaa2d8 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -79,20 +79,7 @@ unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, { u32 c; - c = jhash_3words((__force u32)saddr->s6_addr32[0], - (__force u32)saddr->s6_addr32[1], - (__force u32)saddr->s6_addr32[2], - rnd); - - c = jhash_3words((__force u32)saddr->s6_addr32[3], - (__force u32)daddr->s6_addr32[0], - (__force u32)daddr->s6_addr32[1], - c); - - c = jhash_3words((__force u32)daddr->s6_addr32[2], - (__force u32)daddr->s6_addr32[3], - (__force u32)id, - c); + c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), id, rnd); return c & (INETFRAGS_HASHSZ - 1); } -- cgit v1.2.3 From 8c6d59ee3509acd474365c34db291b950d2e66bf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Feb 2013 14:57:42 +0100 Subject: cfg80211: fix station change if TDLS isn't supported Larry noticed (and bisected) that commit df881293c6ba9a12868491a717b25 "cfg80211: Pass TDLS peer's QoS/HT/VHT information during set_station" broke secure connections. This is is the case only for drivers that don't support TDLS, where any kind of change, even just the change of authorized flag that is required for normal operation, was rejected now. To fix this, remove the checks. I have some patches that will add proper verification for all the different cases later. Cc: Jouni Malinen Bisected-by: Larry Finger Tested-by: Larry Finger Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 580ffeaef3d5..35545ccc30fd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3418,19 +3418,10 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { static int nl80211_set_station_tdls(struct genl_info *info, struct station_parameters *params) { - struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *tb[NL80211_STA_WME_MAX + 1]; struct nlattr *nla; int err; - /* Can only set if TDLS ... */ - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -EOPNOTSUPP; - - /* ... with external setup is supported */ - if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) - return -EOPNOTSUPP; - /* Dummy STA entry gets updated once the peer capabilities are known */ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) params->ht_capa = -- cgit v1.2.3 From 96b45cbd956ce83908378d87d009b05645353f22 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 15 Feb 2013 22:20:46 +0000 Subject: net: move ioctl functions into a separated file They well deserve a separated unit. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/core/Makefile | 2 +- net/core/dev.c | 573 -------------------------------------------------- net/core/dev_ioctl.c | 576 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 577 insertions(+), 574 deletions(-) create mode 100644 net/core/dev_ioctl.c (limited to 'net') diff --git a/net/core/Makefile b/net/core/Makefile index 674641b13aea..0c5e3618c80b 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.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 \ - sock_diag.o + sock_diag.o dev_ioctl.o obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o diff --git a/net/core/dev.c b/net/core/dev.c index 6ad37896a324..1932d351ed7c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -106,7 +106,6 @@ #include #include #include -#include #include #include #include @@ -132,7 +131,6 @@ #include #include #include -#include #include #include "net-sysfs.h" @@ -1226,36 +1224,6 @@ void netdev_notify_peers(struct net_device *dev) } EXPORT_SYMBOL(netdev_notify_peers); -/** - * dev_load - load a network module - * @net: the applicable net namespace - * @name: name of interface - * - * If a network interface is not present and the process has suitable - * privileges this function loads the module. If module loading is not - * available in this kernel then it becomes a nop. - */ - -void dev_load(struct net *net, const char *name) -{ - struct net_device *dev; - int no_module; - - rcu_read_lock(); - dev = dev_get_by_name_rcu(net, name); - rcu_read_unlock(); - - no_module = !dev; - if (no_module && capable(CAP_NET_ADMIN)) - no_module = request_module("netdev-%s", name); - if (no_module && capable(CAP_SYS_MODULE)) { - if (!request_module("%s", name)) - pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", - name); - } -} -EXPORT_SYMBOL(dev_load); - static int __dev_open(struct net_device *dev) { const struct net_device_ops *ops = dev->netdev_ops; @@ -1645,57 +1613,6 @@ static inline void net_timestamp_set(struct sk_buff *skb) __net_timestamp(SKB); \ } \ -static int net_hwtstamp_validate(struct ifreq *ifr) -{ - struct hwtstamp_config cfg; - enum hwtstamp_tx_types tx_type; - enum hwtstamp_rx_filters rx_filter; - int tx_type_valid = 0; - int rx_filter_valid = 0; - - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) - return -EFAULT; - - if (cfg.flags) /* reserved for future extensions */ - return -EINVAL; - - tx_type = cfg.tx_type; - rx_filter = cfg.rx_filter; - - switch (tx_type) { - case HWTSTAMP_TX_OFF: - case HWTSTAMP_TX_ON: - case HWTSTAMP_TX_ONESTEP_SYNC: - tx_type_valid = 1; - break; - } - - switch (rx_filter) { - case HWTSTAMP_FILTER_NONE: - case HWTSTAMP_FILTER_ALL: - case HWTSTAMP_FILTER_SOME: - case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: - case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - rx_filter_valid = 1; - break; - } - - if (!tx_type_valid || !rx_filter_valid) - return -ERANGE; - - return 0; -} - static inline bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb) { @@ -4300,127 +4217,6 @@ softnet_break: goto out; } -static gifconf_func_t *gifconf_list[NPROTO]; - -/** - * register_gifconf - register a SIOCGIF handler - * @family: Address family - * @gifconf: Function handler - * - * Register protocol dependent address dumping routines. The handler - * that is passed must not be freed or reused until it has been replaced - * by another handler. - */ -int register_gifconf(unsigned int family, gifconf_func_t *gifconf) -{ - if (family >= NPROTO) - return -EINVAL; - gifconf_list[family] = gifconf; - return 0; -} -EXPORT_SYMBOL(register_gifconf); - - -/* - * Map an interface index to its name (SIOCGIFNAME) - */ - -/* - * We need this ioctl for efficient implementation of the - * if_indextoname() function required by the IPv6 API. Without - * it, we would have to search all the interfaces to find a - * match. --pb - */ - -static int dev_ifname(struct net *net, struct ifreq __user *arg) -{ - struct net_device *dev; - struct ifreq ifr; - unsigned seq; - - /* - * Fetch the caller's info block. - */ - - if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) - return -EFAULT; - -retry: - seq = read_seqcount_begin(&devnet_rename_seq); - rcu_read_lock(); - dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); - if (!dev) { - rcu_read_unlock(); - return -ENODEV; - } - - strcpy(ifr.ifr_name, dev->name); - rcu_read_unlock(); - if (read_seqcount_retry(&devnet_rename_seq, seq)) - goto retry; - - if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) - return -EFAULT; - return 0; -} - -/* - * Perform a SIOCGIFCONF call. This structure will change - * size eventually, and there is nothing I can do about it. - * Thus we will need a 'compatibility mode'. - */ - -static int dev_ifconf(struct net *net, char __user *arg) -{ - struct ifconf ifc; - struct net_device *dev; - char __user *pos; - int len; - int total; - int i; - - /* - * Fetch the caller's info block. - */ - - if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) - return -EFAULT; - - pos = ifc.ifc_buf; - len = ifc.ifc_len; - - /* - * Loop over the interfaces, and write an info block for each. - */ - - total = 0; - for_each_netdev(net, dev) { - for (i = 0; i < NPROTO; i++) { - if (gifconf_list[i]) { - int done; - if (!pos) - done = gifconf_list[i](dev, NULL, 0); - else - done = gifconf_list[i](dev, pos + total, - len - total); - if (done < 0) - return -EFAULT; - total += done; - } - } - } - - /* - * All done. Write the updated control block back to the caller. - */ - ifc.ifc_len = total; - - /* - * Both BSD and Solaris return 0 here, so we do too. - */ - return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; -} - #ifdef CONFIG_PROC_FS #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) @@ -5381,375 +5177,6 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier) } EXPORT_SYMBOL(dev_change_carrier); -/* - * Perform the SIOCxIFxxx calls, inside rcu_read_lock() - */ -static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd) -{ - int err; - struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name); - - if (!dev) - return -ENODEV; - - switch (cmd) { - case SIOCGIFFLAGS: /* Get interface flags */ - ifr->ifr_flags = (short) dev_get_flags(dev); - return 0; - - case SIOCGIFMETRIC: /* Get the metric on the interface - (currently unused) */ - ifr->ifr_metric = 0; - return 0; - - case SIOCGIFMTU: /* Get the MTU of a device */ - ifr->ifr_mtu = dev->mtu; - return 0; - - case SIOCGIFHWADDR: - if (!dev->addr_len) - memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); - else - memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); - ifr->ifr_hwaddr.sa_family = dev->type; - return 0; - - case SIOCGIFSLAVE: - err = -EINVAL; - break; - - case SIOCGIFMAP: - ifr->ifr_map.mem_start = dev->mem_start; - ifr->ifr_map.mem_end = dev->mem_end; - ifr->ifr_map.base_addr = dev->base_addr; - ifr->ifr_map.irq = dev->irq; - ifr->ifr_map.dma = dev->dma; - ifr->ifr_map.port = dev->if_port; - return 0; - - case SIOCGIFINDEX: - ifr->ifr_ifindex = dev->ifindex; - return 0; - - case SIOCGIFTXQLEN: - ifr->ifr_qlen = dev->tx_queue_len; - return 0; - - default: - /* dev_ioctl() should ensure this case - * is never reached - */ - WARN_ON(1); - err = -ENOTTY; - break; - - } - return err; -} - -/* - * Perform the SIOCxIFxxx calls, inside rtnl_lock() - */ -static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) -{ - int err; - struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); - const struct net_device_ops *ops; - - if (!dev) - return -ENODEV; - - ops = dev->netdev_ops; - - switch (cmd) { - case SIOCSIFFLAGS: /* Set interface flags */ - return dev_change_flags(dev, ifr->ifr_flags); - - case SIOCSIFMETRIC: /* Set the metric on the interface - (currently unused) */ - return -EOPNOTSUPP; - - case SIOCSIFMTU: /* Set the MTU of a device */ - return dev_set_mtu(dev, ifr->ifr_mtu); - - case SIOCSIFHWADDR: - return dev_set_mac_address(dev, &ifr->ifr_hwaddr); - - case SIOCSIFHWBROADCAST: - if (ifr->ifr_hwaddr.sa_family != dev->type) - return -EINVAL; - memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); - call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); - return 0; - - case SIOCSIFMAP: - if (ops->ndo_set_config) { - if (!netif_device_present(dev)) - return -ENODEV; - return ops->ndo_set_config(dev, &ifr->ifr_map); - } - return -EOPNOTSUPP; - - case SIOCADDMULTI: - if (!ops->ndo_set_rx_mode || - ifr->ifr_hwaddr.sa_family != AF_UNSPEC) - return -EINVAL; - if (!netif_device_present(dev)) - return -ENODEV; - return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); - - case SIOCDELMULTI: - if (!ops->ndo_set_rx_mode || - ifr->ifr_hwaddr.sa_family != AF_UNSPEC) - return -EINVAL; - if (!netif_device_present(dev)) - return -ENODEV; - return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); - - case SIOCSIFTXQLEN: - if (ifr->ifr_qlen < 0) - return -EINVAL; - dev->tx_queue_len = ifr->ifr_qlen; - return 0; - - case SIOCSIFNAME: - ifr->ifr_newname[IFNAMSIZ-1] = '\0'; - return dev_change_name(dev, ifr->ifr_newname); - - case SIOCSHWTSTAMP: - err = net_hwtstamp_validate(ifr); - if (err) - return err; - /* fall through */ - - /* - * Unknown or private ioctl - */ - default: - if ((cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) || - cmd == SIOCBONDENSLAVE || - cmd == SIOCBONDRELEASE || - cmd == SIOCBONDSETHWADDR || - cmd == SIOCBONDSLAVEINFOQUERY || - cmd == SIOCBONDINFOQUERY || - cmd == SIOCBONDCHANGEACTIVE || - cmd == SIOCGMIIPHY || - cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG || - cmd == SIOCBRADDIF || - cmd == SIOCBRDELIF || - cmd == SIOCSHWTSTAMP || - cmd == SIOCWANDEV) { - err = -EOPNOTSUPP; - if (ops->ndo_do_ioctl) { - if (netif_device_present(dev)) - err = ops->ndo_do_ioctl(dev, ifr, cmd); - else - err = -ENODEV; - } - } else - err = -EINVAL; - - } - return err; -} - -/* - * This function handles all "interface"-type I/O control requests. The actual - * 'doing' part of this is dev_ifsioc above. - */ - -/** - * dev_ioctl - network device ioctl - * @net: the applicable net namespace - * @cmd: command to issue - * @arg: pointer to a struct ifreq in user space - * - * Issue ioctl functions to devices. This is normally called by the - * user space syscall interfaces but can sometimes be useful for - * other purposes. The return value is the return from the syscall if - * positive or a negative errno code on error. - */ - -int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) -{ - struct ifreq ifr; - int ret; - char *colon; - - /* One special case: SIOCGIFCONF takes ifconf argument - and requires shared lock, because it sleeps writing - to user space. - */ - - if (cmd == SIOCGIFCONF) { - rtnl_lock(); - ret = dev_ifconf(net, (char __user *) arg); - rtnl_unlock(); - return ret; - } - if (cmd == SIOCGIFNAME) - return dev_ifname(net, (struct ifreq __user *)arg); - - if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) - return -EFAULT; - - ifr.ifr_name[IFNAMSIZ-1] = 0; - - colon = strchr(ifr.ifr_name, ':'); - if (colon) - *colon = 0; - - /* - * See which interface the caller is talking about. - */ - - switch (cmd) { - /* - * These ioctl calls: - * - can be done by all. - * - atomic and do not require locking. - * - return a value - */ - case SIOCGIFFLAGS: - case SIOCGIFMETRIC: - case SIOCGIFMTU: - case SIOCGIFHWADDR: - case SIOCGIFSLAVE: - case SIOCGIFMAP: - case SIOCGIFINDEX: - case SIOCGIFTXQLEN: - dev_load(net, ifr.ifr_name); - rcu_read_lock(); - ret = dev_ifsioc_locked(net, &ifr, cmd); - rcu_read_unlock(); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; - - case SIOCETHTOOL: - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ethtool(net, &ifr); - rtnl_unlock(); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; - - /* - * These ioctl calls: - * - require superuser power. - * - require strict serialization. - * - return a value - */ - case SIOCGMIIPHY: - case SIOCGMIIREG: - case SIOCSIFNAME: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) - return -EPERM; - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ifsioc(net, &ifr, cmd); - rtnl_unlock(); - if (!ret) { - if (colon) - *colon = ':'; - if (copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - } - return ret; - - /* - * These ioctl calls: - * - require superuser power. - * - require strict serialization. - * - do not return a value - */ - case SIOCSIFMAP: - case SIOCSIFTXQLEN: - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - /* fall through */ - /* - * These ioctl calls: - * - require local superuser power. - * - require strict serialization. - * - do not return a value - */ - case SIOCSIFFLAGS: - case SIOCSIFMETRIC: - case SIOCSIFMTU: - case SIOCSIFHWADDR: - case SIOCSIFSLAVE: - case SIOCADDMULTI: - case SIOCDELMULTI: - case SIOCSIFHWBROADCAST: - case SIOCSMIIREG: - case SIOCBONDENSLAVE: - case SIOCBONDRELEASE: - case SIOCBONDSETHWADDR: - case SIOCBONDCHANGEACTIVE: - case SIOCBRADDIF: - case SIOCBRDELIF: - case SIOCSHWTSTAMP: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) - return -EPERM; - /* fall through */ - case SIOCBONDSLAVEINFOQUERY: - case SIOCBONDINFOQUERY: - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ifsioc(net, &ifr, cmd); - rtnl_unlock(); - return ret; - - case SIOCGIFMEM: - /* Get the per device memory space. We can add this but - * currently do not support it */ - case SIOCSIFMEM: - /* Set the per device memory buffer space. - * Not applicable in our case */ - case SIOCSIFLINK: - return -ENOTTY; - - /* - * Unknown or private ioctl. - */ - default: - if (cmd == SIOCWANDEV || - (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15)) { - dev_load(net, ifr.ifr_name); - rtnl_lock(); - ret = dev_ifsioc(net, &ifr, cmd); - rtnl_unlock(); - if (!ret && copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - return ret; - } - /* Take care of Wireless Extensions */ - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) - return wext_handle_ioctl(net, &ifr, cmd, arg); - return -ENOTTY; - } -} - - /** * dev_new_index - allocate an ifindex * @net: the applicable net namespace diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c new file mode 100644 index 000000000000..6cc0481faade --- /dev/null +++ b/net/core/dev_ioctl.c @@ -0,0 +1,576 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * Map an interface index to its name (SIOCGIFNAME) + */ + +/* + * We need this ioctl for efficient implementation of the + * if_indextoname() function required by the IPv6 API. Without + * it, we would have to search all the interfaces to find a + * match. --pb + */ + +static int dev_ifname(struct net *net, struct ifreq __user *arg) +{ + struct net_device *dev; + struct ifreq ifr; + unsigned seq; + + /* + * Fetch the caller's info block. + */ + + if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) + return -EFAULT; + +retry: + seq = read_seqcount_begin(&devnet_rename_seq); + rcu_read_lock(); + dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); + if (!dev) { + rcu_read_unlock(); + return -ENODEV; + } + + strcpy(ifr.ifr_name, dev->name); + rcu_read_unlock(); + if (read_seqcount_retry(&devnet_rename_seq, seq)) + goto retry; + + if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) + return -EFAULT; + return 0; +} + +static gifconf_func_t *gifconf_list[NPROTO]; + +/** + * register_gifconf - register a SIOCGIF handler + * @family: Address family + * @gifconf: Function handler + * + * Register protocol dependent address dumping routines. The handler + * that is passed must not be freed or reused until it has been replaced + * by another handler. + */ +int register_gifconf(unsigned int family, gifconf_func_t *gifconf) +{ + if (family >= NPROTO) + return -EINVAL; + gifconf_list[family] = gifconf; + return 0; +} +EXPORT_SYMBOL(register_gifconf); + +/* + * Perform a SIOCGIFCONF call. This structure will change + * size eventually, and there is nothing I can do about it. + * Thus we will need a 'compatibility mode'. + */ + +static int dev_ifconf(struct net *net, char __user *arg) +{ + struct ifconf ifc; + struct net_device *dev; + char __user *pos; + int len; + int total; + int i; + + /* + * Fetch the caller's info block. + */ + + if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) + return -EFAULT; + + pos = ifc.ifc_buf; + len = ifc.ifc_len; + + /* + * Loop over the interfaces, and write an info block for each. + */ + + total = 0; + for_each_netdev(net, dev) { + for (i = 0; i < NPROTO; i++) { + if (gifconf_list[i]) { + int done; + if (!pos) + done = gifconf_list[i](dev, NULL, 0); + else + done = gifconf_list[i](dev, pos + total, + len - total); + if (done < 0) + return -EFAULT; + total += done; + } + } + } + + /* + * All done. Write the updated control block back to the caller. + */ + ifc.ifc_len = total; + + /* + * Both BSD and Solaris return 0 here, so we do too. + */ + return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; +} + +/* + * Perform the SIOCxIFxxx calls, inside rcu_read_lock() + */ +static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd) +{ + int err; + struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name); + + if (!dev) + return -ENODEV; + + switch (cmd) { + case SIOCGIFFLAGS: /* Get interface flags */ + ifr->ifr_flags = (short) dev_get_flags(dev); + return 0; + + case SIOCGIFMETRIC: /* Get the metric on the interface + (currently unused) */ + ifr->ifr_metric = 0; + return 0; + + case SIOCGIFMTU: /* Get the MTU of a device */ + ifr->ifr_mtu = dev->mtu; + return 0; + + case SIOCGIFHWADDR: + if (!dev->addr_len) + memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); + else + memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, + min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + ifr->ifr_hwaddr.sa_family = dev->type; + return 0; + + case SIOCGIFSLAVE: + err = -EINVAL; + break; + + case SIOCGIFMAP: + ifr->ifr_map.mem_start = dev->mem_start; + ifr->ifr_map.mem_end = dev->mem_end; + ifr->ifr_map.base_addr = dev->base_addr; + ifr->ifr_map.irq = dev->irq; + ifr->ifr_map.dma = dev->dma; + ifr->ifr_map.port = dev->if_port; + return 0; + + case SIOCGIFINDEX: + ifr->ifr_ifindex = dev->ifindex; + return 0; + + case SIOCGIFTXQLEN: + ifr->ifr_qlen = dev->tx_queue_len; + return 0; + + default: + /* dev_ioctl() should ensure this case + * is never reached + */ + WARN_ON(1); + err = -ENOTTY; + break; + + } + return err; +} + +static int net_hwtstamp_validate(struct ifreq *ifr) +{ + struct hwtstamp_config cfg; + enum hwtstamp_tx_types tx_type; + enum hwtstamp_rx_filters rx_filter; + int tx_type_valid = 0; + int rx_filter_valid = 0; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + if (cfg.flags) /* reserved for future extensions */ + return -EINVAL; + + tx_type = cfg.tx_type; + rx_filter = cfg.rx_filter; + + switch (tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + case HWTSTAMP_TX_ONESTEP_SYNC: + tx_type_valid = 1; + break; + } + + switch (rx_filter) { + case HWTSTAMP_FILTER_NONE: + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + rx_filter_valid = 1; + break; + } + + if (!tx_type_valid || !rx_filter_valid) + return -ERANGE; + + return 0; +} + +/* + * Perform the SIOCxIFxxx calls, inside rtnl_lock() + */ +static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) +{ + int err; + struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); + const struct net_device_ops *ops; + + if (!dev) + return -ENODEV; + + ops = dev->netdev_ops; + + switch (cmd) { + case SIOCSIFFLAGS: /* Set interface flags */ + return dev_change_flags(dev, ifr->ifr_flags); + + case SIOCSIFMETRIC: /* Set the metric on the interface + (currently unused) */ + return -EOPNOTSUPP; + + case SIOCSIFMTU: /* Set the MTU of a device */ + return dev_set_mtu(dev, ifr->ifr_mtu); + + case SIOCSIFHWADDR: + return dev_set_mac_address(dev, &ifr->ifr_hwaddr); + + case SIOCSIFHWBROADCAST: + if (ifr->ifr_hwaddr.sa_family != dev->type) + return -EINVAL; + memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, + min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + return 0; + + case SIOCSIFMAP: + if (ops->ndo_set_config) { + if (!netif_device_present(dev)) + return -ENODEV; + return ops->ndo_set_config(dev, &ifr->ifr_map); + } + return -EOPNOTSUPP; + + case SIOCADDMULTI: + if (!ops->ndo_set_rx_mode || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); + + case SIOCDELMULTI: + if (!ops->ndo_set_rx_mode || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) + return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); + + case SIOCSIFTXQLEN: + if (ifr->ifr_qlen < 0) + return -EINVAL; + dev->tx_queue_len = ifr->ifr_qlen; + return 0; + + case SIOCSIFNAME: + ifr->ifr_newname[IFNAMSIZ-1] = '\0'; + return dev_change_name(dev, ifr->ifr_newname); + + case SIOCSHWTSTAMP: + err = net_hwtstamp_validate(ifr); + if (err) + return err; + /* fall through */ + + /* + * Unknown or private ioctl + */ + default: + if ((cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) || + cmd == SIOCBONDENSLAVE || + cmd == SIOCBONDRELEASE || + cmd == SIOCBONDSETHWADDR || + cmd == SIOCBONDSLAVEINFOQUERY || + cmd == SIOCBONDINFOQUERY || + cmd == SIOCBONDCHANGEACTIVE || + cmd == SIOCGMIIPHY || + cmd == SIOCGMIIREG || + cmd == SIOCSMIIREG || + cmd == SIOCBRADDIF || + cmd == SIOCBRDELIF || + cmd == SIOCSHWTSTAMP || + cmd == SIOCWANDEV) { + err = -EOPNOTSUPP; + if (ops->ndo_do_ioctl) { + if (netif_device_present(dev)) + err = ops->ndo_do_ioctl(dev, ifr, cmd); + else + err = -ENODEV; + } + } else + err = -EINVAL; + + } + return err; +} + +/** + * dev_load - load a network module + * @net: the applicable net namespace + * @name: name of interface + * + * If a network interface is not present and the process has suitable + * privileges this function loads the module. If module loading is not + * available in this kernel then it becomes a nop. + */ + +void dev_load(struct net *net, const char *name) +{ + struct net_device *dev; + int no_module; + + rcu_read_lock(); + dev = dev_get_by_name_rcu(net, name); + rcu_read_unlock(); + + no_module = !dev; + if (no_module && capable(CAP_NET_ADMIN)) + no_module = request_module("netdev-%s", name); + if (no_module && capable(CAP_SYS_MODULE)) { + if (!request_module("%s", name)) + pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", + name); + } +} +EXPORT_SYMBOL(dev_load); + +/* + * This function handles all "interface"-type I/O control requests. The actual + * 'doing' part of this is dev_ifsioc above. + */ + +/** + * dev_ioctl - network device ioctl + * @net: the applicable net namespace + * @cmd: command to issue + * @arg: pointer to a struct ifreq in user space + * + * Issue ioctl functions to devices. This is normally called by the + * user space syscall interfaces but can sometimes be useful for + * other purposes. The return value is the return from the syscall if + * positive or a negative errno code on error. + */ + +int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) +{ + struct ifreq ifr; + int ret; + char *colon; + + /* One special case: SIOCGIFCONF takes ifconf argument + and requires shared lock, because it sleeps writing + to user space. + */ + + if (cmd == SIOCGIFCONF) { + rtnl_lock(); + ret = dev_ifconf(net, (char __user *) arg); + rtnl_unlock(); + return ret; + } + if (cmd == SIOCGIFNAME) + return dev_ifname(net, (struct ifreq __user *)arg); + + if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) + return -EFAULT; + + ifr.ifr_name[IFNAMSIZ-1] = 0; + + colon = strchr(ifr.ifr_name, ':'); + if (colon) + *colon = 0; + + /* + * See which interface the caller is talking about. + */ + + switch (cmd) { + /* + * These ioctl calls: + * - can be done by all. + * - atomic and do not require locking. + * - return a value + */ + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFHWADDR: + case SIOCGIFSLAVE: + case SIOCGIFMAP: + case SIOCGIFINDEX: + case SIOCGIFTXQLEN: + dev_load(net, ifr.ifr_name); + rcu_read_lock(); + ret = dev_ifsioc_locked(net, &ifr, cmd); + rcu_read_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; + + case SIOCETHTOOL: + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ethtool(net, &ifr); + rtnl_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; + + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - return a value + */ + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSIFNAME: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + return -EPERM; + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ifsioc(net, &ifr, cmd); + rtnl_unlock(); + if (!ret) { + if (colon) + *colon = ':'; + if (copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + } + return ret; + + /* + * These ioctl calls: + * - require superuser power. + * - require strict serialization. + * - do not return a value + */ + case SIOCSIFMAP: + case SIOCSIFTXQLEN: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* fall through */ + /* + * These ioctl calls: + * - require local superuser power. + * - require strict serialization. + * - do not return a value + */ + case SIOCSIFFLAGS: + case SIOCSIFMETRIC: + case SIOCSIFMTU: + case SIOCSIFHWADDR: + case SIOCSIFSLAVE: + case SIOCADDMULTI: + case SIOCDELMULTI: + case SIOCSIFHWBROADCAST: + case SIOCSMIIREG: + case SIOCBONDENSLAVE: + case SIOCBONDRELEASE: + case SIOCBONDSETHWADDR: + case SIOCBONDCHANGEACTIVE: + case SIOCBRADDIF: + case SIOCBRDELIF: + case SIOCSHWTSTAMP: + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + return -EPERM; + /* fall through */ + case SIOCBONDSLAVEINFOQUERY: + case SIOCBONDINFOQUERY: + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ifsioc(net, &ifr, cmd); + rtnl_unlock(); + return ret; + + case SIOCGIFMEM: + /* Get the per device memory space. We can add this but + * currently do not support it */ + case SIOCSIFMEM: + /* Set the per device memory buffer space. + * Not applicable in our case */ + case SIOCSIFLINK: + return -ENOTTY; + + /* + * Unknown or private ioctl. + */ + default: + if (cmd == SIOCWANDEV || + (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15)) { + dev_load(net, ifr.ifr_name); + rtnl_lock(); + ret = dev_ifsioc(net, &ifr, cmd); + rtnl_unlock(); + if (!ret && copy_to_user(arg, &ifr, + sizeof(struct ifreq))) + ret = -EFAULT; + return ret; + } + /* Take care of Wireless Extensions */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) + return wext_handle_ioctl(net, &ifr, cmd, arg); + return -ENOTTY; + } +} -- cgit v1.2.3 From 0d4bfa297c3f7efb71367449ee44f9d3fb0f5871 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sat, 16 Feb 2013 07:38:15 +0000 Subject: ipv6: fix warning in xfrm6_mode_tunnel_input Should not use assignment in conditional: warning: suggest parentheses around assignment used as truth value [-Wparentheses] Problem introduced by: commit 14bbd6a565e1bcdc240d44687edb93f721cfdf99 Author: Pravin B Shelar Date: Thu Feb 14 09:44:49 2013 +0000 net: Add skb_unclone() helper function. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv6/xfrm6_mode_tunnel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 93c41a81c4c3..9bf6a74a71d2 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -69,7 +69,8 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; - if (err = skb_unclone(skb, GFP_ATOMIC)) + err = skb_unclone(skb, GFP_ATOMIC); + if (err) goto out; if (x->props.flags & XFRM_STATE_DECAP_DSCP) -- cgit v1.2.3 From 9aac22deb11a3da4df5b868fc3d30b07185b0d71 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sat, 16 Feb 2013 07:40:29 +0000 Subject: ip: fix warning in xfrm4_mode_tunnel_input Same problem as IPv6 Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv4/xfrm4_mode_tunnel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 1162ace30838..fe5189e2e114 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -142,7 +142,8 @@ static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) for_each_input_rcu(rcv_notify_handlers, handler) handler->handler(skb); - if (err = skb_unclone(skb, GFP_ATOMIC)) + err = skb_unclone(skb, GFP_ATOMIC); + if (err) goto out; if (x->props.flags & XFRM_STATE_DECAP_DSCP) -- cgit v1.2.3 From d4beaa66add8aebf83ab16d2fde4e4de8dac36df Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 18 Feb 2013 01:34:54 +0000 Subject: net: proc: change proc_net_fops_create to proc_create Right now, some modules such as bonding use proc_create to create proc entries under /proc/net/, and other modules such as ipv4 use proc_net_fops_create. It looks a little chaos.this patch changes all of proc_net_fops_create to proc_create. we can remove proc_net_fops_create after this patch. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- drivers/connector/connector.c | 2 +- drivers/net/hamradio/bpqether.c | 3 ++- drivers/net/hamradio/scc.c | 2 +- drivers/net/hamradio/yam.c | 2 +- drivers/net/ppp/pppoe.c | 2 +- net/ax25/af_ax25.c | 7 ++++--- net/bluetooth/af_bluetooth.c | 2 +- net/core/dev.c | 7 ++++--- net/core/dev_addr_lists.c | 2 +- net/core/sock.c | 2 +- net/dccp/probe.c | 2 +- net/decnet/af_decnet.c | 2 +- net/decnet/dn_dev.c | 2 +- net/decnet/dn_neigh.c | 3 ++- net/decnet/dn_route.c | 3 ++- net/ipv4/arp.c | 2 +- net/ipv4/fib_trie.c | 8 ++++---- net/ipv4/igmp.c | 5 +++-- net/ipv4/ipconfig.c | 2 +- net/ipv4/ipmr.c | 4 ++-- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | 6 +++--- net/ipv4/ping.c | 2 +- net/ipv4/proc.c | 7 ++++--- net/ipv4/raw.c | 2 +- net/ipv4/route.c | 4 ++-- net/ipv4/tcp_probe.c | 2 +- net/ipv6/addrconf.c | 2 +- net/ipv6/anycast.c | 2 +- net/ipv6/ip6_flowlabel.c | 4 ++-- net/ipv6/ip6mr.c | 4 ++-- net/ipv6/mcast.c | 6 +++--- net/ipv6/proc.c | 6 +++--- net/ipv6/raw.c | 2 +- net/ipv6/route.c | 4 ++-- net/key/af_key.c | 2 +- net/l2tp/l2tp_ppp.c | 3 ++- net/netfilter/ipvs/ip_vs_app.c | 2 +- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 8 ++++---- net/netfilter/nf_conntrack_expect.c | 3 ++- net/netfilter/nf_conntrack_standalone.c | 2 +- net/netlink/af_netlink.c | 2 +- net/netrom/af_netrom.c | 6 +++--- net/packet/af_packet.c | 2 +- net/phonet/pn_dev.c | 4 ++-- net/rose/af_rose.c | 11 +++++++---- net/rxrpc/af_rxrpc.c | 5 +++-- net/sched/sch_api.c | 2 +- net/sctp/probe.c | 4 ++-- net/unix/af_unix.c | 2 +- net/wireless/wext-proc.c | 3 ++- net/xfrm/xfrm_proc.c | 4 ++-- 52 files changed, 100 insertions(+), 86 deletions(-) (limited to 'net') diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 7b695913cb30..ed17a57ab3cb 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -276,7 +276,7 @@ static int cn_init(void) cn_already_initialized = 1; - proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops); + proc_create("connector", S_IRUGO, init_net.proc_net, &cn_file_ops); return 0; } diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index c2e5497397d5..61f28842163d 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -586,7 +586,8 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi static int __init bpq_init_driver(void) { #ifdef CONFIG_PROC_FS - if (!proc_net_fops_create(&init_net, "bpqether", S_IRUGO, &bpq_info_fops)) { + if (!proc_create("bpqether", S_IRUGO, init_net.proc_net, + &bpq_info_fops)) { printk(KERN_ERR "bpq: cannot create /proc/net/bpqether entry.\n"); return -ENOENT; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index 1b4a47bd32b7..cab13f4d2942 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -2118,7 +2118,7 @@ static int __init scc_init_driver (void) } rtnl_unlock(); - proc_net_fops_create(&init_net, "z8530drv", 0, &scc_net_seq_fops); + proc_create("z8530drv", 0, init_net.proc_net, &scc_net_seq_fops); return 0; } diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index c6645f1017af..92847d27b2e8 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1167,7 +1167,7 @@ static int __init yam_init_driver(void) yam_timer.expires = jiffies + HZ / 100; add_timer(&yam_timer); - proc_net_fops_create(&init_net, "yam", S_IRUGO, &yam_info_fops); + proc_create("yam", S_IRUGO, init_net.proc_net, &yam_info_fops); return 0; error: while (--i >= 0) { diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 20f31d0d1536..d7fa96c42ae7 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1134,7 +1134,7 @@ static __net_init int pppoe_init_net(struct net *net) rwlock_init(&pn->hash_lock); - pde = proc_net_fops_create(net, "pppoe", S_IRUGO, &pppoe_seq_fops); + pde = proc_create("pppoe", S_IRUGO, net->proc_net, &pppoe_seq_fops); #ifdef CONFIG_PROC_FS if (!pde) return -ENOMEM; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 779095ded689..b6ccbea3477e 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1992,9 +1992,10 @@ static int __init ax25_init(void) dev_add_pack(&ax25_packet_type); register_netdevice_notifier(&ax25_dev_notifier); - proc_net_fops_create(&init_net, "ax25_route", S_IRUGO, &ax25_route_fops); - proc_net_fops_create(&init_net, "ax25", S_IRUGO, &ax25_info_fops); - proc_net_fops_create(&init_net, "ax25_calls", S_IRUGO, &ax25_uid_fops); + proc_create("ax25_route", S_IRUGO, init_net.proc_net, + &ax25_route_fops); + proc_create("ax25", S_IRUGO, init_net.proc_net, &ax25_info_fops); + proc_create("ax25_calls", S_IRUGO, init_net.proc_net, &ax25_uid_fops); out: return rc; } diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 5355df63d39b..8412b637407b 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -641,7 +641,7 @@ int bt_procfs_init(struct module* module, struct net *net, const char *name, sk_list->fops.llseek = seq_lseek; sk_list->fops.release = seq_release_private; - pde = proc_net_fops_create(net, name, 0, &sk_list->fops); + pde = proc_create(name, 0, net->proc_net, &sk_list->fops); if (!pde) return -ENOMEM; diff --git a/net/core/dev.c b/net/core/dev.c index 1932d351ed7c..f2f81ef5bbd6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4518,11 +4518,12 @@ static int __net_init dev_proc_net_init(struct net *net) { int rc = -ENOMEM; - if (!proc_net_fops_create(net, "dev", S_IRUGO, &dev_seq_fops)) + if (!proc_create("dev", S_IRUGO, net->proc_net, &dev_seq_fops)) goto out; - if (!proc_net_fops_create(net, "softnet_stat", S_IRUGO, &softnet_seq_fops)) + if (!proc_create("softnet_stat", S_IRUGO, net->proc_net, + &softnet_seq_fops)) goto out_dev; - if (!proc_net_fops_create(net, "ptype", S_IRUGO, &ptype_seq_fops)) + if (!proc_create("ptype", S_IRUGO, net->proc_net, &ptype_seq_fops)) goto out_softnet; if (wext_proc_init(net)) diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index b079c7bbc157..7c62ec449f3c 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -780,7 +780,7 @@ static const struct file_operations dev_mc_seq_fops = { static int __net_init dev_mc_net_init(struct net *net) { - if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops)) + if (!proc_create("dev_mcast", 0, net->proc_net, &dev_mc_seq_fops)) return -ENOMEM; return 0; } diff --git a/net/core/sock.c b/net/core/sock.c index f1e14e20d181..b4d562ef36fb 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2836,7 +2836,7 @@ static const struct file_operations proto_seq_fops = { static __net_init int proto_init_net(struct net *net) { - if (!proc_net_fops_create(net, "protocols", S_IRUGO, &proto_seq_fops)) + if (!proc_create("protocols", S_IRUGO, net->proc_net, &proto_seq_fops)) return -ENOMEM; return 0; diff --git a/net/dccp/probe.c b/net/dccp/probe.c index 0a8d6ebd9b45..d183589b77e9 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -171,7 +171,7 @@ static __init int dccpprobe_init(void) spin_lock_init(&dccpw.lock); if (kfifo_alloc(&dccpw.fifo, bufsize, GFP_KERNEL)) return ret; - if (!proc_net_fops_create(&init_net, procname, S_IRUSR, &dccpprobe_fops)) + if (!proc_create(procname, S_IRUSR, init_net.proc_net, &dccpprobe_fops)) goto err0; ret = setup_jprobe(); diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 64d9843f9e04..f916013d9074 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -2384,7 +2384,7 @@ static int __init decnet_init(void) dev_add_pack(&dn_dix_packet_type); register_netdevice_notifier(&dn_dev_notifier); - proc_net_fops_create(&init_net, "decnet", S_IRUGO, &dn_socket_seq_fops); + proc_create("decnet", S_IRUGO, init_net.proc_net, &dn_socket_seq_fops); dn_register_sysctl(); out: return rc; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index e47ba9fc4a0e..a04894a2c26e 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -1412,7 +1412,7 @@ void __init dn_dev_init(void) rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL); rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL); - proc_net_fops_create(&init_net, "decnet_dev", S_IRUGO, &dn_dev_seq_fops); + proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops); #ifdef CONFIG_SYSCTL { diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 856636addd76..f3408f369fe3 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -590,7 +590,8 @@ static const struct file_operations dn_neigh_seq_fops = { void __init dn_neigh_init(void) { neigh_table_init(&dn_neigh_table); - proc_net_fops_create(&init_net, "decnet_neigh", S_IRUGO, &dn_neigh_seq_fops); + proc_create("decnet_neigh", S_IRUGO, init_net.proc_net, + &dn_neigh_seq_fops); } void __exit dn_neigh_cleanup(void) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 1550028fcd8e..8afb257f279f 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1901,7 +1901,8 @@ void __init dn_route_init(void) dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1); - proc_net_fops_create(&init_net, "decnet_cache", S_IRUGO, &dn_rt_cache_seq_fops); + proc_create("decnet_cache", S_IRUGO, init_net.proc_net, + &dn_rt_cache_seq_fops); #ifdef CONFIG_DECNET_ROUTER rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index ded146b217f1..b04c114ef055 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1405,7 +1405,7 @@ static const struct file_operations arp_seq_fops = { static int __net_init arp_net_init(struct net *net) { - if (!proc_net_fops_create(net, "arp", S_IRUGO, &arp_seq_fops)) + if (!proc_create("arp", S_IRUGO, net->proc_net, &arp_seq_fops)) return -ENOMEM; return 0; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 31d771ca9a70..67c1d4b641d4 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2607,14 +2607,14 @@ static const struct file_operations fib_route_fops = { int __net_init fib_proc_init(struct net *net) { - if (!proc_net_fops_create(net, "fib_trie", S_IRUGO, &fib_trie_fops)) + if (!proc_create("fib_trie", S_IRUGO, net->proc_net, &fib_trie_fops)) goto out1; - if (!proc_net_fops_create(net, "fib_triestat", S_IRUGO, - &fib_triestat_fops)) + if (!proc_create("fib_triestat", S_IRUGO, net->proc_net, + &fib_triestat_fops)) goto out2; - if (!proc_net_fops_create(net, "route", S_IRUGO, &fib_route_fops)) + if (!proc_create("route", S_IRUGO, net->proc_net, &fib_route_fops)) goto out3; return 0; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 736ab70fd179..be85628e6a83 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2646,10 +2646,11 @@ static int __net_init igmp_net_init(struct net *net) { struct proc_dir_entry *pde; - pde = proc_net_fops_create(net, "igmp", S_IRUGO, &igmp_mc_seq_fops); + pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops); if (!pde) goto out_igmp; - pde = proc_net_fops_create(net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops); + pde = proc_create("mcfilter", S_IRUGO, net->proc_net, + &igmp_mcf_seq_fops); if (!pde) goto out_mcfilter; return 0; diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index a2e50ae80b53..98cbc6877019 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1394,7 +1394,7 @@ static int __init ip_auto_config(void) unsigned int i; #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "pnp", S_IRUGO, &pnp_seq_fops); + proc_create("pnp", S_IRUGO, init_net.proc_net, &pnp_seq_fops); #endif /* CONFIG_PROC_FS */ if (!ic_enable) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 7085b9b51e7f..160a8b8e9cae 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2703,9 +2703,9 @@ static int __net_init ipmr_net_init(struct net *net) #ifdef CONFIG_PROC_FS err = -ENOMEM; - if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops)) + if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops)) goto proc_vif_fail; - if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops)) + if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops)) goto proc_cache_fail; #endif return 0; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 9682b36df38c..48bc681f5d32 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -417,12 +417,12 @@ static int __net_init ip_conntrack_net_init(struct net *net) { struct proc_dir_entry *proc, *proc_exp, *proc_stat; - proc = proc_net_fops_create(net, "ip_conntrack", 0440, &ct_file_ops); + proc = proc_create("ip_conntrack", 0440, net->proc_net, &ct_file_ops); if (!proc) goto err1; - proc_exp = proc_net_fops_create(net, "ip_conntrack_expect", 0440, - &ip_exp_file_ops); + proc_exp = proc_create("ip_conntrack_expect", 0440, net->proc_net, + &ip_exp_file_ops); if (!proc_exp) goto err2; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 6f9c07268cf6..fc62a13b5eef 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -889,7 +889,7 @@ static int ping_proc_register(struct net *net) struct proc_dir_entry *p; int rc = 0; - p = proc_net_fops_create(net, "icmp", S_IRUGO, &ping_seq_fops); + p = proc_create("icmp", S_IRUGO, net->proc_net, &ping_seq_fops); if (!p) rc = -ENOMEM; return rc; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 8de53e1ddd54..954bcd42d50e 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -471,11 +471,12 @@ static const struct file_operations netstat_seq_fops = { static __net_init int ip_proc_init_net(struct net *net) { - if (!proc_net_fops_create(net, "sockstat", S_IRUGO, &sockstat_seq_fops)) + if (!proc_create("sockstat", S_IRUGO, net->proc_net, + &sockstat_seq_fops)) goto out_sockstat; - if (!proc_net_fops_create(net, "netstat", S_IRUGO, &netstat_seq_fops)) + if (!proc_create("netstat", S_IRUGO, net->proc_net, &netstat_seq_fops)) goto out_netstat; - if (!proc_net_fops_create(net, "snmp", S_IRUGO, &snmp_seq_fops)) + if (!proc_create("snmp", S_IRUGO, net->proc_net, &snmp_seq_fops)) goto out_snmp; return 0; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6f08991409c3..5043a7d0c64e 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -1050,7 +1050,7 @@ static const struct file_operations raw_seq_fops = { static __net_init int raw_init_net(struct net *net) { - if (!proc_net_fops_create(net, "raw", S_IRUGO, &raw_seq_fops)) + if (!proc_create("raw", S_IRUGO, net->proc_net, &raw_seq_fops)) return -ENOMEM; return 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a0fcc47fee73..3bdd1b1ad1b3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -384,8 +384,8 @@ static int __net_init ip_rt_do_proc_init(struct net *net) { struct proc_dir_entry *pde; - pde = proc_net_fops_create(net, "rt_cache", S_IRUGO, - &rt_cache_seq_fops); + pde = proc_create("rt_cache", S_IRUGO, net->proc_net, + &rt_cache_seq_fops); if (!pde) goto err1; diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 4526fe68e60e..1e3a1fb3c0cd 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -234,7 +234,7 @@ static __init int tcpprobe_init(void) if (!tcp_probe.log) goto err0; - if (!proc_net_fops_create(&init_net, procname, S_IRUSR, &tcpprobe_fops)) + if (!proc_create(procname, S_IRUSR, init_net.proc_net, &tcpprobe_fops)) goto err0; ret = register_jprobe(&tcp_jprobe); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 86c235d05aba..bad71083db86 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3325,7 +3325,7 @@ static const struct file_operations if6_fops = { static int __net_init if6_proc_net_init(struct net *net) { - if (!proc_net_fops_create(net, "if_inet6", S_IRUGO, &if6_fops)) + if (!proc_create("if_inet6", S_IRUGO, net->proc_net, &if6_fops)) return -ENOMEM; return 0; } diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 921b8b398a8c..5a8c00983de5 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -509,7 +509,7 @@ static const struct file_operations ac6_seq_fops = { int __net_init ac6_proc_init(struct net *net) { - if (!proc_net_fops_create(net, "anycast6", S_IRUGO, &ac6_seq_fops)) + if (!proc_create("anycast6", S_IRUGO, net->proc_net, &ac6_seq_fops)) return -ENOMEM; return 0; diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index a7da2f472479..900d79eda51e 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -806,8 +806,8 @@ static const struct file_operations ip6fl_seq_fops = { static int __net_init ip6_flowlabel_proc_init(struct net *net) { - if (!proc_net_fops_create(net, "ip6_flowlabel", - S_IRUGO, &ip6fl_seq_fops)) + if (!proc_create("ip6_flowlabel", S_IRUGO, net->proc_net, + &ip6fl_seq_fops)) return -ENOMEM; return 0; } diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 351ce98e90d9..fdfd96d288ed 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1359,9 +1359,9 @@ static int __net_init ip6mr_net_init(struct net *net) #ifdef CONFIG_PROC_FS err = -ENOMEM; - if (!proc_net_fops_create(net, "ip6_mr_vif", 0, &ip6mr_vif_fops)) + if (!proc_create("ip6_mr_vif", 0, net->proc_net, &ip6mr_vif_fops)) goto proc_vif_fail; - if (!proc_net_fops_create(net, "ip6_mr_cache", 0, &ip6mr_mfc_fops)) + if (!proc_create("ip6_mr_cache", 0, net->proc_net, &ip6mr_mfc_fops)) goto proc_cache_fail; #endif diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 3a2849f660ca..de7d4b9a58b0 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2599,10 +2599,10 @@ static int __net_init igmp6_proc_init(struct net *net) int err; err = -ENOMEM; - if (!proc_net_fops_create(net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops)) + if (!proc_create("igmp6", S_IRUGO, net->proc_net, &igmp6_mc_seq_fops)) goto out; - if (!proc_net_fops_create(net, "mcfilter6", S_IRUGO, - &igmp6_mcf_seq_fops)) + if (!proc_create("mcfilter6", S_IRUGO, net->proc_net, + &igmp6_mcf_seq_fops)) goto out_proc_net_igmp6; err = 0; diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 745a32042950..cf8901dc653e 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -295,11 +295,11 @@ int snmp6_unregister_dev(struct inet6_dev *idev) static int __net_init ipv6_proc_init_net(struct net *net) { - if (!proc_net_fops_create(net, "sockstat6", S_IRUGO, - &sockstat6_seq_fops)) + if (!proc_create("sockstat6", S_IRUGO, net->proc_net, + &sockstat6_seq_fops)) return -ENOMEM; - if (!proc_net_fops_create(net, "snmp6", S_IRUGO, &snmp6_seq_fops)) + if (!proc_create("snmp6", S_IRUGO, net->proc_net, &snmp6_seq_fops)) goto proc_snmp6_fail; net->mib.proc_net_devsnmp6 = proc_mkdir("dev_snmp6", net->proc_net); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 70fa81449997..ff42fb309eec 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1292,7 +1292,7 @@ static const struct file_operations raw6_seq_fops = { static int __net_init raw6_init_net(struct net *net) { - if (!proc_net_fops_create(net, "raw6", S_IRUGO, &raw6_seq_fops)) + if (!proc_create("raw6", S_IRUGO, net->proc_net, &raw6_seq_fops)) return -ENOMEM; return 0; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index f3328bc1174f..e1228d839ebf 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2995,8 +2995,8 @@ static void __net_exit ip6_route_net_exit(struct net *net) static int __net_init ip6_route_net_init_late(struct net *net) { #ifdef CONFIG_PROC_FS - proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); - proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); + proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); + proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); #endif return 0; } diff --git a/net/key/af_key.c b/net/key/af_key.c index 7b3ba32ca678..021a429312b1 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3740,7 +3740,7 @@ static int __net_init pfkey_init_proc(struct net *net) { struct proc_dir_entry *e; - e = proc_net_fops_create(net, "pfkey", 0, &pfkey_proc_ops); + e = proc_create("pfkey", 0, net->proc_net, &pfkey_proc_ops); if (e == NULL) return -ENOMEM; diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 716605c241f4..3ca270b4d47c 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1783,7 +1783,8 @@ 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); + pde = proc_create("pppol2tp", S_IRUGO, net->proc_net, + &pppol2tp_proc_fops); if (!pde) { err = -ENOMEM; goto out; diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 9713e6e86d47..1db8a25fe4ba 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -605,7 +605,7 @@ int __net_init ip_vs_app_net_init(struct net *net) struct netns_ipvs *ipvs = net_ipvs(net); INIT_LIST_HEAD(&ipvs->app_list); - proc_net_fops_create(net, "ip_vs_app", 0, &ip_vs_app_fops); + proc_create("ip_vs_app", 0, net->proc_net, &ip_vs_app_fops); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 68e368a4beed..91ae11280c24 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1291,8 +1291,8 @@ int __net_init ip_vs_conn_net_init(struct net *net) atomic_set(&ipvs->conn_count, 0); - proc_net_fops_create(net, "ip_vs_conn", 0, &ip_vs_conn_fops); - proc_net_fops_create(net, "ip_vs_conn_sync", 0, &ip_vs_conn_sync_fops); + proc_create("ip_vs_conn", 0, net->proc_net, &ip_vs_conn_fops); + proc_create("ip_vs_conn_sync", 0, net->proc_net, &ip_vs_conn_sync_fops); return 0; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ec664cbb119f..7d69181f8daa 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3800,10 +3800,10 @@ int __net_init ip_vs_control_net_init(struct net *net) spin_lock_init(&ipvs->tot_stats.lock); - proc_net_fops_create(net, "ip_vs", 0, &ip_vs_info_fops); - proc_net_fops_create(net, "ip_vs_stats", 0, &ip_vs_stats_fops); - proc_net_fops_create(net, "ip_vs_stats_percpu", 0, - &ip_vs_stats_percpu_fops); + proc_create("ip_vs", 0, net->proc_net, &ip_vs_info_fops); + proc_create("ip_vs_stats", 0, net->proc_net, &ip_vs_stats_fops); + proc_create("ip_vs_stats_percpu", 0, net->proc_net, + &ip_vs_stats_percpu_fops); if (ip_vs_control_net_init_sysctl(net)) goto err; diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index bdd341899ed3..4c15f8d0b741 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -571,7 +571,8 @@ static int exp_proc_init(struct net *net) #ifdef CONFIG_NF_CONNTRACK_PROCFS struct proc_dir_entry *proc; - proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops); + proc = proc_create("nf_conntrack_expect", 0440, net->proc_net, + &exp_file_ops); if (!proc) return -ENOMEM; #endif /* CONFIG_NF_CONNTRACK_PROCFS */ diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 7936bf7f90ba..bee9aecc7ba7 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -366,7 +366,7 @@ static int nf_conntrack_standalone_init_proc(struct net *net) { struct proc_dir_entry *pde; - pde = proc_net_fops_create(net, "nf_conntrack", 0440, &ct_file_ops); + pde = proc_create("nf_conntrack", 0440, net->proc_net, &ct_file_ops); if (!pde) goto out_nf_conntrack; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 74827e3b26a1..50084c40a129 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2145,7 +2145,7 @@ static const struct net_proto_family netlink_family_ops = { static int __net_init netlink_net_init(struct net *net) { #ifdef CONFIG_PROC_FS - if (!proc_net_fops_create(net, "netlink", 0, &netlink_seq_fops)) + if (!proc_create("netlink", 0, net->proc_net, &netlink_seq_fops)) return -ENOMEM; #endif return 0; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 7261eb81974f..71b861338407 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1452,9 +1452,9 @@ static int __init nr_proto_init(void) nr_loopback_init(); - proc_net_fops_create(&init_net, "nr", S_IRUGO, &nr_info_fops); - proc_net_fops_create(&init_net, "nr_neigh", S_IRUGO, &nr_neigh_fops); - proc_net_fops_create(&init_net, "nr_nodes", S_IRUGO, &nr_nodes_fops); + proc_create("nr", S_IRUGO, init_net.proc_net, &nr_info_fops); + proc_create("nr_neigh", S_IRUGO, init_net.proc_net, &nr_neigh_fops); + proc_create("nr_nodes", S_IRUGO, init_net.proc_net, &nr_nodes_fops); out: return rc; fail: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index c111bd0e083a..b7b2d56126c4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3828,7 +3828,7 @@ static int __net_init packet_net_init(struct net *net) mutex_init(&net->packet.sklist_lock); INIT_HLIST_HEAD(&net->packet.sklist); - if (!proc_net_fops_create(net, "packet", 0, &packet_seq_fops)) + if (!proc_create("packet", 0, net->proc_net, &packet_seq_fops)) return -ENOMEM; return 0; diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 5bf6341e2dd4..0e527c9dd041 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -320,7 +320,7 @@ static int __net_init phonet_init_net(struct net *net) { struct phonet_net *pnn = phonet_pernet(net); - if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) + if (!proc_create("phonet", 0, net->proc_net, &pn_sock_seq_fops)) return -ENOMEM; INIT_LIST_HEAD(&pnn->pndevs.list); @@ -348,7 +348,7 @@ int __init phonet_device_init(void) if (err) return err; - proc_net_fops_create(&init_net, "pnresource", 0, &pn_res_seq_fops); + proc_create("pnresource", 0, init_net.proc_net, &pn_res_seq_fops); register_netdevice_notifier(&phonet_device_notifier); err = phonet_netlink_register(); if (err) diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index c4719ce604c2..5fd8aacbb5ae 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1575,10 +1575,13 @@ static int __init rose_proto_init(void) rose_add_loopback_neigh(); - proc_net_fops_create(&init_net, "rose", S_IRUGO, &rose_info_fops); - proc_net_fops_create(&init_net, "rose_neigh", S_IRUGO, &rose_neigh_fops); - proc_net_fops_create(&init_net, "rose_nodes", S_IRUGO, &rose_nodes_fops); - proc_net_fops_create(&init_net, "rose_routes", S_IRUGO, &rose_routes_fops); + proc_create("rose", S_IRUGO, init_net.proc_net, &rose_info_fops); + proc_create("rose_neigh", S_IRUGO, init_net.proc_net, + &rose_neigh_fops); + proc_create("rose_nodes", S_IRUGO, init_net.proc_net, + &rose_nodes_fops); + proc_create("rose_routes", S_IRUGO, init_net.proc_net, + &rose_routes_fops); out: return rc; fail: diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 5b0fd291babb..59c7c7d59402 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -839,8 +839,9 @@ static int __init af_rxrpc_init(void) } #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "rxrpc_calls", 0, &rxrpc_call_seq_fops); - proc_net_fops_create(&init_net, "rxrpc_conns", 0, &rxrpc_connection_seq_fops); + proc_create("rxrpc_calls", 0, init_net.proc_net, &rxrpc_call_seq_fops); + proc_create("rxrpc_conns", 0, init_net.proc_net, + &rxrpc_connection_seq_fops); #endif return 0; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index fe1ba54b93f7..d8cf7d3dfe8c 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1768,7 +1768,7 @@ static int __net_init psched_net_init(struct net *net) { struct proc_dir_entry *e; - e = proc_net_fops_create(net, "psched", 0, &psched_fops); + e = proc_create("psched", 0, net->proc_net, &psched_fops); if (e == NULL) return -ENOMEM; diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 261b7b9858a4..dd507f5ca8e7 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -195,8 +195,8 @@ static __init int sctpprobe_init(void) if (kfifo_alloc(&sctpw.fifo, bufsize, GFP_KERNEL)) return ret; - if (!proc_net_fops_create(&init_net, procname, S_IRUSR, - &sctpprobe_fops)) + if (!proc_create(procname, S_IRUSR, init_net.proc_net, + &sctpprobe_fops)) goto free_kfifo; ret = register_jprobe(&sctp_recv_probe); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 0c612361c153..0c479b6f407c 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2402,7 +2402,7 @@ static int __net_init unix_net_init(struct net *net) goto out; #ifdef CONFIG_PROC_FS - if (!proc_net_fops_create(net, "unix", 0, &unix_seq_fops)) { + if (!proc_create("unix", 0, net->proc_net, &unix_seq_fops)) { unix_sysctl_unregister(net); goto out; } diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c index 8bafa31fa9f8..00fc5bb3fb63 100644 --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c @@ -143,7 +143,8 @@ static const struct file_operations wireless_seq_fops = { int __net_init wext_proc_init(struct net *net) { /* Create /proc/net/wireless entry */ - if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) + if (!proc_create("wireless", S_IRUGO, net->proc_net, + &wireless_seq_fops)) return -ENOMEM; return 0; diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index 603903853e89..9abe14fb59b5 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -74,8 +74,8 @@ static const struct file_operations xfrm_statistics_seq_fops = { int __net_init xfrm_proc_init(struct net *net) { - if (!proc_net_fops_create(net, "xfrm_stat", S_IRUGO, - &xfrm_statistics_seq_fops)) + if (!proc_create("xfrm_stat", S_IRUGO, net->proc_net, + &xfrm_statistics_seq_fops)) return -ENOMEM; return 0; } -- cgit v1.2.3 From ece31ffd539e8e2b586b1ca5f50bc4f4591e3893 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 18 Feb 2013 01:34:56 +0000 Subject: net: proc: change proc_net_remove to remove_proc_entry proc_net_remove is only used to remove proc entries that under /proc/net,it's not a general function for removing proc entries of netns. if we want to remove some proc entries which under /proc/net/stat/, we still need to call remove_proc_entry. this patch use remove_proc_entry to replace proc_net_remove. we can remove proc_net_remove after this patch. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- drivers/connector/connector.c | 2 +- drivers/net/hamradio/bpqether.c | 2 +- drivers/net/hamradio/scc.c | 2 +- drivers/net/hamradio/yam.c | 2 +- drivers/net/ppp/pppoe.c | 2 +- net/8021q/vlanproc.c | 2 +- net/atm/proc.c | 2 +- net/ax25/af_ax25.c | 6 +++--- net/bluetooth/af_bluetooth.c | 2 +- net/can/bcm.c | 2 +- net/can/proc.c | 2 +- net/core/dev.c | 12 ++++++------ net/core/dev_addr_lists.c | 2 +- net/core/pktgen.c | 4 ++-- net/core/sock.c | 2 +- net/dccp/probe.c | 4 ++-- net/decnet/af_decnet.c | 2 +- net/decnet/dn_dev.c | 2 +- net/decnet/dn_neigh.c | 2 +- net/decnet/dn_route.c | 2 +- net/ipv4/arp.c | 2 +- net/ipv4/fib_trie.c | 10 +++++----- net/ipv4/igmp.c | 6 +++--- net/ipv4/ipmr.c | 6 +++--- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | 8 ++++---- net/ipv4/ping.c | 2 +- net/ipv4/proc.c | 10 +++++----- net/ipv4/raw.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_probe.c | 4 ++-- net/ipv4/udp.c | 2 +- net/ipv6/addrconf.c | 2 +- net/ipv6/anycast.c | 2 +- net/ipv6/ip6_flowlabel.c | 2 +- net/ipv6/ip6mr.c | 6 +++--- net/ipv6/mcast.c | 6 +++--- net/ipv6/proc.c | 10 +++++----- net/ipv6/raw.c | 2 +- net/ipv6/route.c | 4 ++-- net/key/af_key.c | 2 +- net/l2tp/l2tp_ppp.c | 2 +- net/netfilter/ipvs/ip_vs_app.c | 2 +- net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 6 +++--- net/netfilter/nf_conntrack_expect.c | 2 +- net/netfilter/nf_conntrack_standalone.c | 4 ++-- net/netfilter/x_tables.c | 10 +++++----- net/netfilter/xt_hashlimit.c | 6 +++--- net/netfilter/xt_recent.c | 2 +- net/netlink/af_netlink.c | 2 +- net/netrom/af_netrom.c | 6 +++--- net/packet/af_packet.c | 2 +- net/phonet/pn_dev.c | 4 ++-- net/rose/af_rose.c | 8 ++++---- net/rxrpc/af_rxrpc.c | 4 ++-- net/sched/sch_api.c | 2 +- net/sctp/probe.c | 4 ++-- net/unix/af_unix.c | 2 +- net/wireless/wext-proc.c | 2 +- net/xfrm/xfrm_proc.c | 2 +- 60 files changed, 112 insertions(+), 112 deletions(-) (limited to 'net') diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index ed17a57ab3cb..f1b7e244bfc1 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -287,7 +287,7 @@ static void cn_fini(void) cn_already_initialized = 0; - proc_net_remove(&init_net, "connector"); + remove_proc_entry("connector", init_net.proc_net); cn_queue_free_dev(dev->cbdev); netlink_kernel_release(dev->nls); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 61f28842163d..02de6c891670 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -611,7 +611,7 @@ static void __exit bpq_cleanup_driver(void) unregister_netdevice_notifier(&bpq_dev_notifier); - proc_net_remove(&init_net, "bpqether"); + remove_proc_entry("bpqether", init_net.proc_net); rtnl_lock(); while (!list_empty(&bpq_devices)) { diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index cab13f4d2942..bc1d52170389 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -2173,7 +2173,7 @@ static void __exit scc_cleanup_driver(void) if (Vector_Latch) release_region(Vector_Latch, 1); - proc_net_remove(&init_net, "z8530drv"); + remove_proc_entry("z8530drv", init_net.proc_net); } MODULE_AUTHOR("Joerg Reuter "); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 92847d27b2e8..4cf8f1017aad 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1199,7 +1199,7 @@ static void __exit yam_cleanup_driver(void) kfree(p); } - proc_net_remove(&init_net, "yam"); + remove_proc_entry("yam", init_net.proc_net); } /* --------------------------------------------------------------------- */ diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index d7fa96c42ae7..bb07ba94c3aa 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1145,7 +1145,7 @@ static __net_init int pppoe_init_net(struct net *net) static __net_exit void pppoe_exit_net(struct net *net) { - proc_net_remove(net, "pppoe"); + remove_proc_entry("pppoe", net->proc_net); } static struct pernet_operations pppoe_net_ops = { diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index 4de77ea5fa37..dc526ec965e4 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -131,7 +131,7 @@ void vlan_proc_cleanup(struct net *net) remove_proc_entry(name_conf, vn->proc_vlan_dir); if (vn->proc_vlan_dir) - proc_net_remove(net, name_root); + remove_proc_entry(name_root, net->proc_net); /* Dynamically added entries should be cleaned up as their vlan_device * is removed, so we should not have to take care of it here... diff --git a/net/atm/proc.c b/net/atm/proc.c index 0d020de8d233..b4e75340b162 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -460,7 +460,7 @@ static void atm_proc_dirs_remove(void) if (e->dirent) remove_proc_entry(e->name, atm_proc_root); } - proc_net_remove(&init_net, "atm"); + remove_proc_entry("atm", init_net.proc_net); } int __init atm_proc_init(void) diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index b6ccbea3477e..69a06c47b648 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -2009,9 +2009,9 @@ MODULE_ALIAS_NETPROTO(PF_AX25); static void __exit ax25_exit(void) { - proc_net_remove(&init_net, "ax25_route"); - proc_net_remove(&init_net, "ax25"); - proc_net_remove(&init_net, "ax25_calls"); + remove_proc_entry("ax25_route", init_net.proc_net); + remove_proc_entry("ax25", init_net.proc_net); + remove_proc_entry("ax25_calls", init_net.proc_net); unregister_netdevice_notifier(&ax25_dev_notifier); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 8412b637407b..d3ee69b35a78 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -652,7 +652,7 @@ int bt_procfs_init(struct module* module, struct net *net, const char *name, void bt_procfs_cleanup(struct net *net, const char *name) { - proc_net_remove(net, name); + remove_proc_entry(name, net->proc_net); } #else int bt_procfs_init(struct module* module, struct net *net, const char *name, diff --git a/net/can/bcm.c b/net/can/bcm.c index 28e12d18f0f1..5dcb20076f39 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1633,7 +1633,7 @@ static void __exit bcm_module_exit(void) can_proto_unregister(&bcm_can_proto); if (proc_dir) - proc_net_remove(&init_net, "can-bcm"); + remove_proc_entry("can-bcm", init_net.proc_net); } module_init(bcm_module_init); diff --git a/net/can/proc.c b/net/can/proc.c index ae566902d2bf..497335892146 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -531,5 +531,5 @@ void can_remove_proc(void) can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF); if (can_dir) - proc_net_remove(&init_net, "can"); + remove_proc_entry("can", init_net.proc_net); } diff --git a/net/core/dev.c b/net/core/dev.c index f2f81ef5bbd6..decf55f9ad80 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4532,11 +4532,11 @@ static int __net_init dev_proc_net_init(struct net *net) out: return rc; out_ptype: - proc_net_remove(net, "ptype"); + remove_proc_entry("ptype", net->proc_net); out_softnet: - proc_net_remove(net, "softnet_stat"); + remove_proc_entry("softnet_stat", net->proc_net); out_dev: - proc_net_remove(net, "dev"); + remove_proc_entry("dev", net->proc_net); goto out; } @@ -4544,9 +4544,9 @@ static void __net_exit dev_proc_net_exit(struct net *net) { wext_proc_exit(net); - proc_net_remove(net, "ptype"); - proc_net_remove(net, "softnet_stat"); - proc_net_remove(net, "dev"); + remove_proc_entry("ptype", net->proc_net); + remove_proc_entry("softnet_stat", net->proc_net); + remove_proc_entry("dev", net->proc_net); } static struct pernet_operations __net_initdata dev_proc_ops = { diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 7c62ec449f3c..89562529df45 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -787,7 +787,7 @@ static int __net_init dev_mc_net_init(struct net *net) static void __net_exit dev_mc_net_exit(struct net *net) { - proc_net_remove(net, "dev_mcast"); + remove_proc_entry("dev_mcast", net->proc_net); } static struct pernet_operations __net_initdata dev_mc_net_ops = { diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 2201e699ad67..6048fc1da1c2 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3650,7 +3650,7 @@ static int __net_init pg_net_init(struct net *net) remove_entry: remove_proc_entry(PGCTRL, pn->proc_dir); remove: - proc_net_remove(pn->net, PG_PROC_DIR); + remove_proc_entry(PG_PROC_DIR, pn->net->proc_net); return ret; } @@ -3676,7 +3676,7 @@ static void __net_exit pg_net_exit(struct net *net) } remove_proc_entry(PGCTRL, pn->proc_dir); - proc_net_remove(pn->net, PG_PROC_DIR); + remove_proc_entry(PG_PROC_DIR, pn->net->proc_net); } static struct pernet_operations pg_net_ops = { diff --git a/net/core/sock.c b/net/core/sock.c index b4d562ef36fb..fe96c5d34299 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2844,7 +2844,7 @@ static __net_init int proto_init_net(struct net *net) static __net_exit void proto_exit_net(struct net *net) { - proc_net_remove(net, "protocols"); + remove_proc_entry("protocols", net->proc_net); } diff --git a/net/dccp/probe.c b/net/dccp/probe.c index d183589b77e9..4c6bdf97a657 100644 --- a/net/dccp/probe.c +++ b/net/dccp/probe.c @@ -181,7 +181,7 @@ static __init int dccpprobe_init(void) pr_info("DCCP watch registered (port=%d)\n", port); return 0; err1: - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); err0: kfifo_free(&dccpw.fifo); return ret; @@ -191,7 +191,7 @@ module_init(dccpprobe_init); static __exit void dccpprobe_exit(void) { kfifo_free(&dccpw.fifo); - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); unregister_jprobe(&dccp_send_probe); } diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index f916013d9074..c4a2def5b7bd 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -2413,7 +2413,7 @@ static void __exit decnet_exit(void) dn_neigh_cleanup(); dn_fib_cleanup(); - proc_net_remove(&init_net, "decnet"); + remove_proc_entry("decnet", init_net.proc_net); proto_unregister(&dn_proto); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index a04894a2c26e..c8da116d84a4 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -1433,7 +1433,7 @@ void __exit dn_dev_cleanup(void) } #endif /* CONFIG_SYSCTL */ - proc_net_remove(&init_net, "decnet_dev"); + remove_proc_entry("decnet_dev", init_net.proc_net); dn_dev_devices_off(); } diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index f3408f369fe3..f8637f93d318 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -596,6 +596,6 @@ void __init dn_neigh_init(void) void __exit dn_neigh_cleanup(void) { - proc_net_remove(&init_net, "decnet_neigh"); + remove_proc_entry("decnet_neigh", init_net.proc_net); neigh_table_clear(&dn_neigh_table); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 8afb257f279f..5ac0e153ef83 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1918,7 +1918,7 @@ void __exit dn_route_cleanup(void) del_timer(&dn_route_timer); dn_run_flush(0); - proc_net_remove(&init_net, "decnet_cache"); + remove_proc_entry("decnet_cache", init_net.proc_net); dst_entries_destroy(&dn_dst_ops); } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index b04c114ef055..fea4929f6200 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1412,7 +1412,7 @@ static int __net_init arp_net_init(struct net *net) static void __net_exit arp_net_exit(struct net *net) { - proc_net_remove(net, "arp"); + remove_proc_entry("arp", net->proc_net); } static struct pernet_operations arp_net_ops = { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 67c1d4b641d4..61e03da3e1f5 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2620,18 +2620,18 @@ int __net_init fib_proc_init(struct net *net) return 0; out3: - proc_net_remove(net, "fib_triestat"); + remove_proc_entry("fib_triestat", net->proc_net); out2: - proc_net_remove(net, "fib_trie"); + remove_proc_entry("fib_trie", net->proc_net); out1: return -ENOMEM; } void __net_exit fib_proc_exit(struct net *net) { - proc_net_remove(net, "fib_trie"); - proc_net_remove(net, "fib_triestat"); - proc_net_remove(net, "route"); + remove_proc_entry("fib_trie", net->proc_net); + remove_proc_entry("fib_triestat", net->proc_net); + remove_proc_entry("route", net->proc_net); } #endif /* CONFIG_PROC_FS */ diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index be85628e6a83..d8c232794bcb 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2656,15 +2656,15 @@ static int __net_init igmp_net_init(struct net *net) return 0; out_mcfilter: - proc_net_remove(net, "igmp"); + remove_proc_entry("igmp", net->proc_net); out_igmp: return -ENOMEM; } static void __net_exit igmp_net_exit(struct net *net) { - proc_net_remove(net, "mcfilter"); - proc_net_remove(net, "igmp"); + remove_proc_entry("mcfilter", net->proc_net); + remove_proc_entry("igmp", net->proc_net); } static struct pernet_operations igmp_net_ops = { diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 160a8b8e9cae..5f95b3aa579e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2712,7 +2712,7 @@ static int __net_init ipmr_net_init(struct net *net) #ifdef CONFIG_PROC_FS proc_cache_fail: - proc_net_remove(net, "ip_mr_vif"); + remove_proc_entry("ip_mr_vif", net->proc_net); proc_vif_fail: ipmr_rules_exit(net); #endif @@ -2723,8 +2723,8 @@ fail: static void __net_exit ipmr_net_exit(struct net *net) { #ifdef CONFIG_PROC_FS - proc_net_remove(net, "ip_mr_cache"); - proc_net_remove(net, "ip_mr_vif"); + remove_proc_entry("ip_mr_cache", net->proc_net); + remove_proc_entry("ip_mr_vif", net->proc_net); #endif ipmr_rules_exit(net); } diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 48bc681f5d32..f2ca12794081 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -433,9 +433,9 @@ static int __net_init ip_conntrack_net_init(struct net *net) return 0; err3: - proc_net_remove(net, "ip_conntrack_expect"); + remove_proc_entry("ip_conntrack_expect", net->proc_net); err2: - proc_net_remove(net, "ip_conntrack"); + remove_proc_entry("ip_conntrack", net->proc_net); err1: return -ENOMEM; } @@ -443,8 +443,8 @@ err1: static void __net_exit ip_conntrack_net_exit(struct net *net) { remove_proc_entry("ip_conntrack", net->proc_net_stat); - proc_net_remove(net, "ip_conntrack_expect"); - proc_net_remove(net, "ip_conntrack"); + remove_proc_entry("ip_conntrack_expect", net->proc_net); + remove_proc_entry("ip_conntrack", net->proc_net); } static struct pernet_operations ip_conntrack_net_ops = { diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index fc62a13b5eef..55c4ee1bba06 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -897,7 +897,7 @@ static int ping_proc_register(struct net *net) static void ping_proc_unregister(struct net *net) { - proc_net_remove(net, "icmp"); + remove_proc_entry("icmp", net->proc_net); } diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 954bcd42d50e..32030a24e776 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -482,18 +482,18 @@ static __net_init int ip_proc_init_net(struct net *net) return 0; out_snmp: - proc_net_remove(net, "netstat"); + remove_proc_entry("netstat", net->proc_net); out_netstat: - proc_net_remove(net, "sockstat"); + remove_proc_entry("sockstat", net->proc_net); out_sockstat: return -ENOMEM; } static __net_exit void ip_proc_exit_net(struct net *net) { - proc_net_remove(net, "snmp"); - proc_net_remove(net, "netstat"); - proc_net_remove(net, "sockstat"); + remove_proc_entry("snmp", net->proc_net); + remove_proc_entry("netstat", net->proc_net); + remove_proc_entry("sockstat", net->proc_net); } static __net_initdata struct pernet_operations ip_proc_ops = { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 5043a7d0c64e..53ddebc292b6 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -1058,7 +1058,7 @@ static __net_init int raw_init_net(struct net *net) static __net_exit void raw_exit_net(struct net *net) { - proc_net_remove(net, "raw"); + remove_proc_entry("raw", net->proc_net); } static __net_initdata struct pernet_operations raw_net_ops = { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 77f5050efc8d..145d3bf8df86 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2616,7 +2616,7 @@ EXPORT_SYMBOL(tcp_proc_register); void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo) { - proc_net_remove(net, afinfo->name); + remove_proc_entry(afinfo->name, net->proc_net); } EXPORT_SYMBOL(tcp_proc_unregister); diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 1e3a1fb3c0cd..d4943f67aff2 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -244,7 +244,7 @@ static __init int tcpprobe_init(void) pr_info("probe registered (port=%d) bufsize=%u\n", port, bufsize); return 0; err1: - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); err0: kfree(tcp_probe.log); return ret; @@ -253,7 +253,7 @@ module_init(tcpprobe_init); static __exit void tcpprobe_exit(void) { - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); unregister_jprobe(&tcp_jprobe); kfree(tcp_probe.log); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 39a5e7a9a77f..265c42cf963c 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2122,7 +2122,7 @@ EXPORT_SYMBOL(udp_proc_register); void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo) { - proc_net_remove(net, afinfo->name); + remove_proc_entry(afinfo->name, net->proc_net); } EXPORT_SYMBOL(udp_proc_unregister); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index bad71083db86..4dc0d44a5d31 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3332,7 +3332,7 @@ static int __net_init if6_proc_net_init(struct net *net) static void __net_exit if6_proc_net_exit(struct net *net) { - proc_net_remove(net, "if_inet6"); + remove_proc_entry("if_inet6", net->proc_net); } static struct pernet_operations if6_proc_net_ops = { diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 5a8c00983de5..5a80f15a9de2 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -517,7 +517,7 @@ int __net_init ac6_proc_init(struct net *net) void ac6_proc_exit(struct net *net) { - proc_net_remove(net, "anycast6"); + remove_proc_entry("anycast6", net->proc_net); } #endif diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 900d79eda51e..b973ed3d06cf 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -814,7 +814,7 @@ static int __net_init ip6_flowlabel_proc_init(struct net *net) static void __net_exit ip6_flowlabel_proc_fini(struct net *net) { - proc_net_remove(net, "ip6_flowlabel"); + remove_proc_entry("ip6_flowlabel", net->proc_net); } #else static inline int ip6_flowlabel_proc_init(struct net *net) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index fdfd96d288ed..96bfb4e4b820 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1369,7 +1369,7 @@ static int __net_init ip6mr_net_init(struct net *net) #ifdef CONFIG_PROC_FS proc_cache_fail: - proc_net_remove(net, "ip6_mr_vif"); + remove_proc_entry("ip6_mr_vif", net->proc_net); proc_vif_fail: ip6mr_rules_exit(net); #endif @@ -1380,8 +1380,8 @@ fail: static void __net_exit ip6mr_net_exit(struct net *net) { #ifdef CONFIG_PROC_FS - proc_net_remove(net, "ip6_mr_cache"); - proc_net_remove(net, "ip6_mr_vif"); + remove_proc_entry("ip6_mr_cache", net->proc_net); + remove_proc_entry("ip6_mr_vif", net->proc_net); #endif ip6mr_rules_exit(net); } diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index de7d4b9a58b0..bfa6cc36ef2a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2610,14 +2610,14 @@ out: return err; out_proc_net_igmp6: - proc_net_remove(net, "igmp6"); + remove_proc_entry("igmp6", net->proc_net); goto out; } static void __net_exit igmp6_proc_exit(struct net *net) { - proc_net_remove(net, "mcfilter6"); - proc_net_remove(net, "igmp6"); + remove_proc_entry("mcfilter6", net->proc_net); + remove_proc_entry("igmp6", net->proc_net); } #else static inline int igmp6_proc_init(struct net *net) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index cf8901dc653e..bbbe53a99b57 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -308,17 +308,17 @@ static int __net_init ipv6_proc_init_net(struct net *net) return 0; proc_dev_snmp6_fail: - proc_net_remove(net, "snmp6"); + remove_proc_entry("snmp6", net->proc_net); proc_snmp6_fail: - proc_net_remove(net, "sockstat6"); + remove_proc_entry("sockstat6", net->proc_net); return -ENOMEM; } static void __net_exit ipv6_proc_exit_net(struct net *net) { - proc_net_remove(net, "sockstat6"); - proc_net_remove(net, "dev_snmp6"); - proc_net_remove(net, "snmp6"); + remove_proc_entry("sockstat6", net->proc_net); + remove_proc_entry("dev_snmp6", net->proc_net); + remove_proc_entry("snmp6", net->proc_net); } static struct pernet_operations ipv6_proc_ops = { diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ff42fb309eec..c65907db8c44 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1300,7 +1300,7 @@ static int __net_init raw6_init_net(struct net *net) static void __net_exit raw6_exit_net(struct net *net) { - proc_net_remove(net, "raw6"); + remove_proc_entry("raw6", net->proc_net); } static struct pernet_operations raw6_net_ops = { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e1228d839ebf..515bb51e05a8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3004,8 +3004,8 @@ static int __net_init ip6_route_net_init_late(struct net *net) static void __net_exit ip6_route_net_exit_late(struct net *net) { #ifdef CONFIG_PROC_FS - proc_net_remove(net, "ipv6_route"); - proc_net_remove(net, "rt6_stats"); + remove_proc_entry("ipv6_route", net->proc_net); + remove_proc_entry("rt6_stats", net->proc_net); #endif } diff --git a/net/key/af_key.c b/net/key/af_key.c index 021a429312b1..9ef79851f297 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3749,7 +3749,7 @@ static int __net_init pfkey_init_proc(struct net *net) static void __net_exit pfkey_exit_proc(struct net *net) { - proc_net_remove(net, "pfkey"); + remove_proc_entry("pfkey", net->proc_net); } #else static inline int pfkey_init_proc(struct net *net) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 3ca270b4d47c..3f4e3afc191a 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1796,7 +1796,7 @@ out: static __net_exit void pppol2tp_exit_net(struct net *net) { - proc_net_remove(net, "pppol2tp"); + remove_proc_entry("pppol2tp", net->proc_net); } static struct pernet_operations pppol2tp_net_ops = { diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 1db8a25fe4ba..0b779d7df881 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -612,5 +612,5 @@ int __net_init ip_vs_app_net_init(struct net *net) void __net_exit ip_vs_app_net_cleanup(struct net *net) { unregister_ip_vs_app(net, NULL /* all */); - proc_net_remove(net, "ip_vs_app"); + remove_proc_entry("ip_vs_app", net->proc_net); } diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 91ae11280c24..9f00db7e03f2 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1300,8 +1300,8 @@ void __net_exit ip_vs_conn_net_cleanup(struct net *net) { /* flush all the connection entries first */ ip_vs_conn_flush(net); - proc_net_remove(net, "ip_vs_conn"); - proc_net_remove(net, "ip_vs_conn_sync"); + remove_proc_entry("ip_vs_conn", net->proc_net); + remove_proc_entry("ip_vs_conn_sync", net->proc_net); } int __init ip_vs_conn_init(void) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 7d69181f8daa..c68198bf9128 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3822,9 +3822,9 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net) ip_vs_trash_cleanup(net); ip_vs_stop_estimator(net, &ipvs->tot_stats); ip_vs_control_net_cleanup_sysctl(net); - proc_net_remove(net, "ip_vs_stats_percpu"); - proc_net_remove(net, "ip_vs_stats"); - proc_net_remove(net, "ip_vs"); + remove_proc_entry("ip_vs_stats_percpu", net->proc_net); + remove_proc_entry("ip_vs_stats", net->proc_net); + remove_proc_entry("ip_vs", net->proc_net); free_percpu(ipvs->tot_stats.cpustats); } diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 4c15f8d0b741..3921e5bc1235 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -582,7 +582,7 @@ static int exp_proc_init(struct net *net) static void exp_proc_remove(struct net *net) { #ifdef CONFIG_NF_CONNTRACK_PROCFS - proc_net_remove(net, "nf_conntrack_expect"); + remove_proc_entry("nf_conntrack_expect", net->proc_net); #endif /* CONFIG_NF_CONNTRACK_PROCFS */ } diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index bee9aecc7ba7..6bcce401fd1c 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -377,7 +377,7 @@ static int nf_conntrack_standalone_init_proc(struct net *net) return 0; out_stat_nf_conntrack: - proc_net_remove(net, "nf_conntrack"); + remove_proc_entry("nf_conntrack", net->proc_net); out_nf_conntrack: return -ENOMEM; } @@ -385,7 +385,7 @@ out_nf_conntrack: static void nf_conntrack_standalone_fini_proc(struct net *net) { remove_proc_entry("nf_conntrack", net->proc_net_stat); - proc_net_remove(net, "nf_conntrack"); + remove_proc_entry("nf_conntrack", net->proc_net); } #else static int nf_conntrack_standalone_init_proc(struct net *net) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 7b3a9e5999c0..686c7715d777 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1323,12 +1323,12 @@ int xt_proto_init(struct net *net, u_int8_t af) out_remove_matches: strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_MATCHES, sizeof(buf)); - proc_net_remove(net, buf); + remove_proc_entry(buf, net->proc_net); out_remove_tables: strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TABLES, sizeof(buf)); - proc_net_remove(net, buf); + remove_proc_entry(buf, net->proc_net); out: return -1; #endif @@ -1342,15 +1342,15 @@ void xt_proto_fini(struct net *net, u_int8_t af) strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TABLES, sizeof(buf)); - proc_net_remove(net, buf); + remove_proc_entry(buf, net->proc_net); strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_TARGETS, sizeof(buf)); - proc_net_remove(net, buf); + remove_proc_entry(buf, net->proc_net); strlcpy(buf, xt_prefix[af], sizeof(buf)); strlcat(buf, FORMAT_MATCHES, sizeof(buf)); - proc_net_remove(net, buf); + remove_proc_entry(buf, net->proc_net); #endif /*CONFIG_PROC_FS*/ } EXPORT_SYMBOL_GPL(xt_proto_fini); diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index a9d7af953ceb..98218c896d2e 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -867,7 +867,7 @@ static int __net_init hashlimit_proc_net_init(struct net *net) #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) hashlimit_net->ip6t_hashlimit = proc_mkdir("ip6t_hashlimit", net->proc_net); if (!hashlimit_net->ip6t_hashlimit) { - proc_net_remove(net, "ipt_hashlimit"); + remove_proc_entry("ipt_hashlimit", net->proc_net); return -ENOMEM; } #endif @@ -897,9 +897,9 @@ static void __net_exit hashlimit_proc_net_exit(struct net *net) hashlimit_net->ip6t_hashlimit = NULL; mutex_unlock(&hashlimit_mutex); - proc_net_remove(net, "ipt_hashlimit"); + remove_proc_entry("ipt_hashlimit", net->proc_net); #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - proc_net_remove(net, "ip6t_hashlimit"); + remove_proc_entry("ip6t_hashlimit", net->proc_net); #endif } diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 978efc9b555a..31bf233dae97 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -643,7 +643,7 @@ static void __net_exit recent_proc_net_exit(struct net *net) recent_net->xt_recent = NULL; spin_unlock_bh(&recent_lock); - proc_net_remove(net, "xt_recent"); + remove_proc_entry("xt_recent", net->proc_net); } #else static inline int recent_proc_net_init(struct net *net) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 50084c40a129..3d55e0c713e2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2154,7 +2154,7 @@ static int __net_init netlink_net_init(struct net *net) static void __net_exit netlink_net_exit(struct net *net) { #ifdef CONFIG_PROC_FS - proc_net_remove(net, "netlink"); + remove_proc_entry("netlink", net->proc_net); #endif } diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 71b861338407..297b07a029de 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1482,9 +1482,9 @@ static void __exit nr_exit(void) { int i; - proc_net_remove(&init_net, "nr"); - proc_net_remove(&init_net, "nr_neigh"); - proc_net_remove(&init_net, "nr_nodes"); + remove_proc_entry("nr", init_net.proc_net); + remove_proc_entry("nr_neigh", init_net.proc_net); + remove_proc_entry("nr_nodes", init_net.proc_net); nr_loopback_clear(); nr_rt_free(); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index b7b2d56126c4..c7bfeff10767 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3836,7 +3836,7 @@ static int __net_init packet_net_init(struct net *net) static void __net_exit packet_net_exit(struct net *net) { - proc_net_remove(net, "packet"); + remove_proc_entry("packet", net->proc_net); } static struct pernet_operations packet_net_ops = { diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 0e527c9dd041..45a7df6575de 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -331,7 +331,7 @@ static int __net_init phonet_init_net(struct net *net) static void __net_exit phonet_exit_net(struct net *net) { - proc_net_remove(net, "phonet"); + remove_proc_entry("phonet", net->proc_net); } static struct pernet_operations phonet_net_ops = { @@ -361,7 +361,7 @@ void phonet_device_exit(void) rtnl_unregister_all(PF_PHONET); unregister_netdevice_notifier(&phonet_device_notifier); unregister_pernet_subsys(&phonet_net_ops); - proc_net_remove(&init_net, "pnresource"); + remove_proc_entry("pnresource", init_net.proc_net); } int phonet_route_add(struct net_device *dev, u8 daddr) diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 5fd8aacbb5ae..b768fe9d5e7a 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1608,10 +1608,10 @@ static void __exit rose_exit(void) { int i; - proc_net_remove(&init_net, "rose"); - proc_net_remove(&init_net, "rose_neigh"); - proc_net_remove(&init_net, "rose_nodes"); - proc_net_remove(&init_net, "rose_routes"); + remove_proc_entry("rose", init_net.proc_net); + remove_proc_entry("rose_neigh", init_net.proc_net); + remove_proc_entry("rose_nodes", init_net.proc_net); + remove_proc_entry("rose_routes", init_net.proc_net); rose_loopback_clear(); rose_rt_free(); diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 59c7c7d59402..e61aa6001c65 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -879,8 +879,8 @@ static void __exit af_rxrpc_exit(void) _debug("flush scheduled work"); flush_workqueue(rxrpc_workqueue); - proc_net_remove(&init_net, "rxrpc_conns"); - proc_net_remove(&init_net, "rxrpc_calls"); + remove_proc_entry("rxrpc_conns", init_net.proc_net); + remove_proc_entry("rxrpc_calls", init_net.proc_net); destroy_workqueue(rxrpc_workqueue); kmem_cache_destroy(rxrpc_call_jar); _leave(""); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index d8cf7d3dfe8c..a181b484812a 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1777,7 +1777,7 @@ static int __net_init psched_net_init(struct net *net) static void __net_exit psched_net_exit(struct net *net) { - proc_net_remove(net, "psched"); + remove_proc_entry("psched", net->proc_net); } #else static int __net_init psched_net_init(struct net *net) diff --git a/net/sctp/probe.c b/net/sctp/probe.c index dd507f5ca8e7..ad0dba870341 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -208,7 +208,7 @@ static __init int sctpprobe_init(void) return 0; remove_proc: - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); free_kfifo: kfifo_free(&sctpw.fifo); return ret; @@ -217,7 +217,7 @@ free_kfifo: static __exit void sctpprobe_exit(void) { kfifo_free(&sctpw.fifo); - proc_net_remove(&init_net, procname); + remove_proc_entry(procname, init_net.proc_net); unregister_jprobe(&sctp_recv_probe); } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 0c479b6f407c..87d284289012 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2415,7 +2415,7 @@ out: static void __net_exit unix_net_exit(struct net *net) { unix_sysctl_unregister(net); - proc_net_remove(net, "unix"); + remove_proc_entry("unix", net->proc_net); } static struct pernet_operations unix_net_ops = { diff --git a/net/wireless/wext-proc.c b/net/wireless/wext-proc.c index 00fc5bb3fb63..e98a01c1034f 100644 --- a/net/wireless/wext-proc.c +++ b/net/wireless/wext-proc.c @@ -152,5 +152,5 @@ int __net_init wext_proc_init(struct net *net) void __net_exit wext_proc_exit(struct net *net) { - proc_net_remove(net, "wireless"); + remove_proc_entry("wireless", net->proc_net); } diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index 9abe14fb59b5..c721b0d9ab8b 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -82,5 +82,5 @@ int __net_init xfrm_proc_init(struct net *net) void xfrm_proc_fini(struct net *net) { - proc_net_remove(net, "xfrm_stat"); + remove_proc_entry("xfrm_stat", net->proc_net); } -- cgit v1.2.3 From 18cf0d0784b4a634472ed24d0d7ca1c721d93e90 Mon Sep 17 00:00:00 2001 From: Romain KUNTZ Date: Mon, 18 Feb 2013 02:36:24 +0000 Subject: xfrm: release neighbor upon dst destruction Neighbor is cloned in xfrm6_fill_dst but seems to never be released. Neighbor entry should be released when XFRM6 dst entry is destroyed in xfrm6_dst_destroy, otherwise references may be kept forever on the device pointed by the neighbor entry. I may not have understood all the subtleties of XFRM & dst so I would be happy to receive comments on this patch. Signed-off-by: Romain Kuntz Signed-off-by: David S. Miller --- net/ipv6/xfrm6_policy.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c9844135c9ca..8f32718677bd 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -236,6 +236,8 @@ static void xfrm6_dst_destroy(struct dst_entry *dst) { struct xfrm_dst *xdst = (struct xfrm_dst *)dst; + if (likely(xdst->u.rt6.n)) + neigh_release(xdst->u.rt6.n); if (likely(xdst->u.rt6.rt6i_idev)) in6_dev_put(xdst->u.rt6.rt6i_idev); dst_destroy_metrics_generic(dst); -- cgit v1.2.3 From 7777ac3860327da557665f6e53cd82fbe40f151b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 18 Feb 2013 06:04:10 +0000 Subject: VSOCK: get rid of EXPORT_SYMTAB This is the default behavior for a looooooong time. Acked-by: Andy King Signed-off-by: Dmitry Torokhov Signed-off-by: David S. Miller --- net/vmw_vsock/af_vsock.c | 2 -- net/vmw_vsock/vmci_transport.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'net') diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 54bb7bdf92d3..a8efe5aaf221 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -76,8 +76,6 @@ */ #include - -#define EXPORT_SYMTAB #include #include #include diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index e8a87cf37072..a70ace83a153 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -14,8 +14,6 @@ */ #include - -#define EXPORT_SYMTAB #include #include #include -- cgit v1.2.3 From 7ccd7de691bcc7194deb68b1db391861ada5427e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 18 Feb 2013 06:04:11 +0000 Subject: VSOCK: get rid of vsock_version.h There isn't really a need to have a separate file for it. Acked-by: Andy King Signed-off-by: Dmitry Torokhov Signed-off-by: David S. Miller --- net/vmw_vsock/af_vsock.c | 3 +-- net/vmw_vsock/vsock_version.h | 22 ---------------------- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 net/vmw_vsock/vsock_version.h (limited to 'net') diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index a8efe5aaf221..c1b9e55e338e 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -98,7 +98,6 @@ #include #include "af_vsock.h" -#include "vsock_version.h" static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr); static void vsock_sk_destruct(struct sock *sk); @@ -2009,5 +2008,5 @@ EXPORT_SYMBOL_GPL(vsock_core_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Socket Family"); -MODULE_VERSION(VSOCK_DRIVER_VERSION_STRING); +MODULE_VERSION("1.0.0.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/net/vmw_vsock/vsock_version.h b/net/vmw_vsock/vsock_version.h deleted file mode 100644 index 4df7f5e2151c..000000000000 --- a/net/vmw_vsock/vsock_version.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * VMware vSockets Driver - * - * Copyright (C) 2011-2012 VMware, Inc. All rights reserved. - * - * 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 version 2 and no 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 _VSOCK_VERSION_H_ -#define _VSOCK_VERSION_H_ - -#define VSOCK_DRIVER_VERSION_PARTS { 1, 0, 0, 0 } -#define VSOCK_DRIVER_VERSION_STRING "1.0.0.0-k" - -#endif /* _VSOCK_VERSION_H_ */ -- cgit v1.2.3 From 6cf1c5fc26c6507bcb0edced6fcda876a79b5a6d Mon Sep 17 00:00:00 2001 From: Andy King Date: Mon, 18 Feb 2013 06:04:13 +0000 Subject: VSOCK: Don't reject PF_VSOCK protocol Allow our own family as the protocol value for socket creation. Reported-by: Gerd Hoffmann Signed-off-by: Andy King Signed-off-by: David S. Miller --- net/vmw_vsock/af_vsock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index c1b9e55e338e..ca511c4f388a 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1860,7 +1860,7 @@ static int vsock_create(struct net *net, struct socket *sock, if (!sock) return -EINVAL; - if (protocol) + if (protocol && protocol != PF_VSOCK) return -EPROTONOSUPPORT; switch (sock->type) { -- cgit v1.2.3 From 8064b3cf750e71fdaf306abb4433a93d0f45f4c9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 18 Feb 2013 10:18:52 +0000 Subject: ipv6: fix a sparse warning net/ipv6/reassembly.c:82:72: warning: incorrect type in argument 3 (different base types) net/ipv6/reassembly.c:82:72: expected unsigned int [unsigned] [usertype] c net/ipv6/reassembly.c:82:72: got restricted __be32 [usertype] id Signed-off-by: Eric Dumazet Reported-by: Fengguang Wu Signed-off-by: David S. Miller --- net/ipv6/reassembly.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 9a6428aaa2d8..3c6a77290c6e 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -79,7 +79,8 @@ unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, { u32 c; - c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), id, rnd); + c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), + (__force u32)id, rnd); return c & (INETFRAGS_HASHSZ - 1); } -- cgit v1.2.3 From b20ab9cc63ca4605aec154cf54faa8455749f3f6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 10 Feb 2013 18:56:56 +0100 Subject: netfilter: nf_ct_helper: better logging for dropped packets Connection tracking helpers have to drop packets under exceptional situations. Currently, the user gets the following logging message in case that happens: nf_ct_%s: dropping packet ... However, depending on the helper, there are different reasons why a packet can be dropped. This patch modifies the existing code to provide more specific error message in the scope of each helper to help users to debug the reason why the packet has been dropped, ie: nf_ct_%s: dropping packet: reason ... Thanks to Joe Perches for many formatting suggestions. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 4 ++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 10 +--- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 8 +-- net/netfilter/nf_conntrack_amanda.c | 5 +- net/netfilter/nf_conntrack_ftp.c | 10 ++-- net/netfilter/nf_conntrack_h323_main.c | 6 +-- net/netfilter/nf_conntrack_helper.c | 19 +++++++ net/netfilter/nf_conntrack_irc.c | 7 ++- net/netfilter/nf_conntrack_sane.c | 5 +- net/netfilter/nf_conntrack_sip.c | 73 +++++++++++++++++++------- net/netfilter/nf_conntrack_tftp.c | 8 ++- net/netfilter/nf_nat_amanda.c | 8 ++- net/netfilter/nf_nat_ftp.c | 5 +- net/netfilter/nf_nat_irc.c | 8 ++- net/netfilter/nf_nat_sip.c | 49 ++++++++++++----- net/netfilter/nf_nat_tftp.c | 4 +- 16 files changed, 164 insertions(+), 65 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index ce27edf57570..26c4ae5bfbb8 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -100,6 +100,10 @@ struct nf_ct_helper_expectfn { void (*expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); }; +__printf(3,4) +void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, + const char *fmt, ...); + void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n); void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n); struct nf_ct_helper_expectfn * diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 48990ada0e1e..2820aa18b542 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -100,7 +100,6 @@ static unsigned int ipv4_helper(unsigned int hooknum, enum ip_conntrack_info ctinfo; const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; - unsigned int ret; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); @@ -116,13 +115,8 @@ static unsigned int ipv4_helper(unsigned int hooknum, if (!helper) return NF_ACCEPT; - ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), - ct, ctinfo); - if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) { - nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL, - "nf_ct_%s: dropping packet", helper->name); - } - return ret; + return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), + ct, ctinfo); } static unsigned int ipv4_confirm(unsigned int hooknum, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 8a45bb20bedb..2b6c226f5198 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -104,7 +104,6 @@ static unsigned int ipv6_helper(unsigned int hooknum, const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; enum ip_conntrack_info ctinfo; - unsigned int ret; __be16 frag_off; int protoff; u8 nexthdr; @@ -130,12 +129,7 @@ static unsigned int ipv6_helper(unsigned int hooknum, return NF_ACCEPT; } - ret = helper->help(skb, protoff, ct, ctinfo); - if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) { - nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL, - "nf_ct_%s: dropping packet", helper->name); - } - return ret; + return helper->help(skb, protoff, ct, ctinfo); } static unsigned int ipv6_confirm(unsigned int hooknum, diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index c514fe6033d2..dbdaa1149260 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -145,6 +145,7 @@ static int amanda_help(struct sk_buff *skb, exp = nf_ct_expect_alloc(ct); if (exp == NULL) { + nf_ct_helper_log(skb, ct, "cannot alloc expectation"); ret = NF_DROP; goto out; } @@ -158,8 +159,10 @@ static int amanda_help(struct sk_buff *skb, if (nf_nat_amanda && ct->status & IPS_NAT_MASK) ret = nf_nat_amanda(skb, ctinfo, protoff, off - dataoff, len, exp); - else if (nf_ct_expect_related(exp) != 0) + else if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; + } nf_ct_expect_put(exp); } diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 1ce3befb7c8a..62fb8faedb80 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -435,8 +435,8 @@ skip_nl_seq: connection tracking, not packet filtering. However, it is necessary for accurate tracking in this case. */ - pr_debug("conntrack_ftp: partial %s %u+%u\n", - search[dir][i].pattern, ntohl(th->seq), datalen); + nf_ct_helper_log(skb, ct, "partial matching of `%s'", + search[dir][i].pattern); ret = NF_DROP; goto out; } else if (found == 0) { /* No match */ @@ -450,6 +450,7 @@ skip_nl_seq: exp = nf_ct_expect_alloc(ct); if (exp == NULL) { + nf_ct_helper_log(skb, ct, "cannot alloc expectation"); ret = NF_DROP; goto out; } @@ -500,9 +501,10 @@ skip_nl_seq: protoff, matchoff, matchlen, exp); else { /* Can't expect this? Best to drop packet now. */ - if (nf_ct_expect_related(exp) != 0) + if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; - else + } else ret = NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 962795e839ab..7df7b36d2e24 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -623,7 +623,7 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff, drop: spin_unlock_bh(&nf_h323_lock); - net_info_ratelimited("nf_ct_h245: packet dropped\n"); + nf_ct_helper_log(skb, ct, "cannot process H.245 message"); return NF_DROP; } @@ -1197,7 +1197,7 @@ static int q931_help(struct sk_buff *skb, unsigned int protoff, drop: spin_unlock_bh(&nf_h323_lock); - net_info_ratelimited("nf_ct_q931: packet dropped\n"); + nf_ct_helper_log(skb, ct, "cannot process Q.931 message"); return NF_DROP; } @@ -1795,7 +1795,7 @@ static int ras_help(struct sk_buff *skb, unsigned int protoff, drop: spin_unlock_bh(&nf_h323_lock); - net_info_ratelimited("nf_ct_ras: packet dropped\n"); + nf_ct_helper_log(skb, ct, "cannot process RAS message"); return NF_DROP; } diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 2f380f73c4c0..c08768da7936 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -28,6 +28,7 @@ #include #include #include +#include static DEFINE_MUTEX(nf_ct_helper_mutex); struct hlist_head *nf_ct_helper_hash __read_mostly; @@ -332,6 +333,24 @@ nf_ct_helper_expectfn_find_by_symbol(const void *symbol) } EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); +__printf(3, 4) +void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct, + const char *fmt, ...) +{ + const struct nf_conn_help *help; + const struct nf_conntrack_helper *helper; + + /* Called from the helper function, this call never fails */ + help = nfct_help(ct); + + /* rcu_read_lock()ed by nf_hook_slow */ + helper = rcu_dereference(help->helper); + + nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL, + "nf_ct_%s: dropping packet: %s ", helper->name, fmt); +} +EXPORT_SYMBOL_GPL(nf_ct_helper_log); + int nf_conntrack_helper_register(struct nf_conntrack_helper *me) { int ret = 0; diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 3b20aa77cfc8..70985c5d0ffa 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -194,6 +194,8 @@ static int help(struct sk_buff *skb, unsigned int protoff, exp = nf_ct_expect_alloc(ct); if (exp == NULL) { + nf_ct_helper_log(skb, ct, + "cannot alloc expectation"); ret = NF_DROP; goto out; } @@ -210,8 +212,11 @@ static int help(struct sk_buff *skb, unsigned int protoff, addr_beg_p - ib_ptr, addr_end_p - addr_beg_p, exp); - else if (nf_ct_expect_related(exp) != 0) + else if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, + "cannot add expectation"); ret = NF_DROP; + } nf_ct_expect_put(exp); goto out; } diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 295429f39088..4a2134fd3fcb 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -138,6 +138,7 @@ static int help(struct sk_buff *skb, exp = nf_ct_expect_alloc(ct); if (exp == NULL) { + nf_ct_helper_log(skb, ct, "cannot alloc expectation"); ret = NF_DROP; goto out; } @@ -151,8 +152,10 @@ static int help(struct sk_buff *skb, nf_ct_dump_tuple(&exp->tuple); /* Can't expect this? Best to drop packet now. */ - if (nf_ct_expect_related(exp) != 0) + if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; + } nf_ct_expect_put(exp); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 72a67bbe3518..069229d919b6 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1095,8 +1095,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, port = simple_strtoul(*dptr + mediaoff, NULL, 10); if (port == 0) continue; - if (port < 1024 || port > 65535) + if (port < 1024 || port > 65535) { + nf_ct_helper_log(skb, ct, "wrong port %u", port); return NF_DROP; + } /* The media description overrides the session description. */ maddr_len = 0; @@ -1107,15 +1109,20 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); } else if (caddr_len) memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); - else + else { + nf_ct_helper_log(skb, ct, "cannot parse SDP message"); return NF_DROP; + } ret = set_expected_rtp_rtcp(skb, protoff, dataoff, dptr, datalen, &rtp_addr, htons(port), t->class, mediaoff, medialen); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_ct_helper_log(skb, ct, + "cannot add expectation for voice"); return ret; + } /* Update media connection address if present */ if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { @@ -1123,8 +1130,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, dptr, datalen, mediaoff, SDP_HDR_CONNECTION, SDP_HDR_MEDIA, &rtp_addr); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_ct_helper_log(skb, ct, "cannot mangle SDP"); return ret; + } } i++; } @@ -1258,9 +1267,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, SIP_HDR_CONTACT, NULL, &matchoff, &matchlen, &daddr, &port); - if (ret < 0) + if (ret < 0) { + nf_ct_helper_log(skb, ct, "cannot parse contact"); return NF_DROP; - else if (ret == 0) + } else if (ret == 0) return NF_ACCEPT; /* We don't support third-party registrations */ @@ -1273,8 +1283,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, if (ct_sip_parse_numerical_param(ct, *dptr, matchoff + matchlen, *datalen, - "expires=", NULL, NULL, &expires) < 0) + "expires=", NULL, NULL, &expires) < 0) { + nf_ct_helper_log(skb, ct, "cannot parse expires"); return NF_DROP; + } if (expires == 0) { ret = NF_ACCEPT; @@ -1282,8 +1294,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, } exp = nf_ct_expect_alloc(ct); - if (!exp) + if (!exp) { + nf_ct_helper_log(skb, ct, "cannot alloc expectation"); return NF_DROP; + } saddr = NULL; if (sip_direct_signalling) @@ -1300,9 +1314,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, exp, matchoff, matchlen); else { - if (nf_ct_expect_related(exp) != 0) + if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; - else + } else ret = NF_ACCEPT; } nf_ct_expect_put(exp); @@ -1356,9 +1371,10 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff, SIP_HDR_CONTACT, &in_contact, &matchoff, &matchlen, &addr, &port); - if (ret < 0) + if (ret < 0) { + nf_ct_helper_log(skb, ct, "cannot parse contact"); return NF_DROP; - else if (ret == 0) + } else if (ret == 0) break; /* We don't support third-party registrations */ @@ -1373,8 +1389,10 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff, matchoff + matchlen, *datalen, "expires=", NULL, NULL, &c_expires); - if (ret < 0) + if (ret < 0) { + nf_ct_helper_log(skb, ct, "cannot parse expires"); return NF_DROP; + } if (c_expires == 0) break; if (refresh_signalling_expectation(ct, &addr, proto, port, @@ -1408,15 +1426,21 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff, if (*datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); - if (!code) + if (!code) { + nf_ct_helper_log(skb, ct, "cannot get code"); return NF_DROP; + } if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, - &matchoff, &matchlen) <= 0) + &matchoff, &matchlen) <= 0) { + nf_ct_helper_log(skb, ct, "cannot parse cseq"); return NF_DROP; + } cseq = simple_strtoul(*dptr + matchoff, NULL, 10); - if (!cseq) + if (!cseq) { + nf_ct_helper_log(skb, ct, "cannot get cseq"); return NF_DROP; + } matchend = matchoff + matchlen + 1; for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { @@ -1471,11 +1495,15 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, continue; if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ, - &matchoff, &matchlen) <= 0) + &matchoff, &matchlen) <= 0) { + nf_ct_helper_log(skb, ct, "cannot parse cseq"); return NF_DROP; + } cseq = simple_strtoul(*dptr + matchoff, NULL, 10); - if (!cseq) + if (!cseq) { + nf_ct_helper_log(skb, ct, "cannot get cseq"); return NF_DROP; + } return handler->request(skb, protoff, dataoff, dptr, datalen, cseq); @@ -1498,8 +1526,10 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { nf_nat_sip = rcu_dereference(nf_nat_sip_hook); if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, - dptr, datalen)) + dptr, datalen)) { + nf_ct_helper_log(skb, ct, "cannot NAT SIP message"); ret = NF_DROP; + } } return ret; @@ -1563,11 +1593,14 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, end += strlen("\r\n\r\n") + clen; msglen = origlen = end - dptr; - if (msglen > datalen) + if (msglen > datalen) { + nf_ct_helper_log(skb, ct, "incomplete/bad SIP message"); return NF_DROP; + } ret = process_sip_msg(skb, ct, protoff, dataoff, &dptr, &msglen); + /* process_sip_* functions report why this packet is dropped */ if (ret != NF_ACCEPT) break; diff = msglen - origlen; diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 81fc61c05263..e9936c830208 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -60,8 +60,10 @@ static int tftp_help(struct sk_buff *skb, nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); exp = nf_ct_expect_alloc(ct); - if (exp == NULL) + if (exp == NULL) { + nf_ct_helper_log(skb, ct, "cannot alloc expectation"); return NF_DROP; + } tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), @@ -74,8 +76,10 @@ static int tftp_help(struct sk_buff *skb, nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook); if (nf_nat_tftp && ct->status & IPS_NAT_MASK) ret = nf_nat_tftp(skb, ctinfo, exp); - else if (nf_ct_expect_related(exp) != 0) + else if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; + } nf_ct_expect_put(exp); break; case TFTP_OPCODE_DATA: diff --git a/net/netfilter/nf_nat_amanda.c b/net/netfilter/nf_nat_amanda.c index 42d337881171..3b67c9d11273 100644 --- a/net/netfilter/nf_nat_amanda.c +++ b/net/netfilter/nf_nat_amanda.c @@ -56,15 +56,19 @@ static unsigned int help(struct sk_buff *skb, } } - if (port == 0) + if (port == 0) { + nf_ct_helper_log(skb, exp->master, "all ports in use"); return NF_DROP; + } sprintf(buffer, "%u", port); ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, protoff, matchoff, matchlen, buffer, strlen(buffer)); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_ct_helper_log(skb, exp->master, "cannot mangle packet"); nf_ct_unexpect_related(exp); + } return ret; } diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c index e839b97b2863..e84a578dbe35 100644 --- a/net/netfilter/nf_nat_ftp.c +++ b/net/netfilter/nf_nat_ftp.c @@ -96,8 +96,10 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, } } - if (port == 0) + if (port == 0) { + nf_ct_helper_log(skb, ct, "all ports in use"); return NF_DROP; + } buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer), &newaddr, port); @@ -113,6 +115,7 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb, return NF_ACCEPT; out: + nf_ct_helper_log(skb, ct, "cannot mangle packet"); nf_ct_unexpect_related(exp); return NF_DROP; } diff --git a/net/netfilter/nf_nat_irc.c b/net/netfilter/nf_nat_irc.c index 1fedee6e7fb6..f02b3605823e 100644 --- a/net/netfilter/nf_nat_irc.c +++ b/net/netfilter/nf_nat_irc.c @@ -56,14 +56,18 @@ static unsigned int help(struct sk_buff *skb, } } - if (port == 0) + if (port == 0) { + nf_ct_helper_log(skb, exp->master, "all ports in use"); return NF_DROP; + } ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo, protoff, matchoff, matchlen, buffer, strlen(buffer)); - if (ret != NF_ACCEPT) + if (ret != NF_ACCEPT) { + nf_ct_helper_log(skb, exp->master, "cannot mangle packet"); nf_ct_unexpect_related(exp); + } return ret; } diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index 5951146e7688..96ccdf78a29f 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -159,8 +159,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &matchoff, &matchlen, &addr, &port) > 0 && !map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, &addr, port)) + matchoff, matchlen, &addr, port)) { + nf_ct_helper_log(skb, ct, "cannot mangle SIP message"); return NF_DROP; + } request = 1; } else request = 0; @@ -193,8 +195,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, olen = *datalen; if (!map_addr(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, &addr, port)) + matchoff, matchlen, &addr, port)) { + nf_ct_helper_log(skb, ct, "cannot mangle Via header"); return NF_DROP; + } matchend = matchoff + matchlen + *datalen - olen; @@ -209,8 +213,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, &ct->tuplehash[!dir].tuple.dst.u3, true); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - poff, plen, buffer, buflen)) + poff, plen, buffer, buflen)) { + nf_ct_helper_log(skb, ct, "cannot mangle maddr"); return NF_DROP; + } } /* The received= parameter (RFC 2361) contains the address @@ -225,6 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, false); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, poff, plen, buffer, buflen)) + nf_ct_helper_log(skb, ct, "cannot mangle received"); return NF_DROP; } @@ -238,8 +245,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; buflen = sprintf(buffer, "%u", ntohs(p)); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - poff, plen, buffer, buflen)) + poff, plen, buffer, buflen)) { + nf_ct_helper_log(skb, ct, "cannot mangle rport"); return NF_DROP; + } } } @@ -253,27 +262,35 @@ next: &addr, &port) > 0) { if (!map_addr(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, - &addr, port)) + &addr, port)) { + nf_ct_helper_log(skb, ct, "cannot mangle contact"); return NF_DROP; + } } if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) || - !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) + !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) { + nf_ct_helper_log(skb, ct, "cannot mangle SIP from/to"); return NF_DROP; + } /* Mangle destination port for Cisco phones, then fix up checksums */ if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { struct udphdr *uh; - if (!skb_make_writable(skb, skb->len)) + if (!skb_make_writable(skb, skb->len)) { + nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; + } uh = (void *)skb->data + protoff; uh->dest = ct_sip_info->forced_dport; if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, - 0, 0, NULL, 0)) + 0, 0, NULL, 0)) { + nf_ct_helper_log(skb, ct, "cannot mangle packet"); return NF_DROP; + } } return NF_ACCEPT; @@ -372,15 +389,19 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, } } - if (port == 0) + if (port == 0) { + nf_ct_helper_log(skb, ct, "all ports in use for SIP"); return NF_DROP; + } if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, - matchoff, matchlen, buffer, buflen)) + matchoff, matchlen, buffer, buflen)) { + nf_ct_helper_log(skb, ct, "cannot mangle packet"); goto err; + } } return NF_ACCEPT; @@ -573,14 +594,18 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, } } - if (port == 0) + if (port == 0) { + nf_ct_helper_log(skb, ct, "all ports in use for SDP media"); goto err1; + } /* Update media port. */ if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, - mediaoff, medialen, port)) + mediaoff, medialen, port)) { + nf_ct_helper_log(skb, ct, "cannot mangle SDP message"); goto err2; + } return NF_ACCEPT; diff --git a/net/netfilter/nf_nat_tftp.c b/net/netfilter/nf_nat_tftp.c index ccabbda71a3e..7f67e1d5310d 100644 --- a/net/netfilter/nf_nat_tftp.c +++ b/net/netfilter/nf_nat_tftp.c @@ -28,8 +28,10 @@ static unsigned int help(struct sk_buff *skb, = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; exp->dir = IP_CT_DIR_REPLY; exp->expectfn = nf_nat_follow_master; - if (nf_ct_expect_related(exp) != 0) + if (nf_ct_expect_related(exp) != 0) { + nf_ct_helper_log(skb, exp->master, "cannot add expectation"); return NF_DROP; + } return NF_ACCEPT; } -- cgit v1.2.3 From aa0e51cdda005cd37e23751de54443b1e73e7def Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 18 Feb 2013 09:50:52 +0000 Subject: ip_gre: allow CSUM capable devices to handle packets If device is not able to handle checksumming it will be handled in dev_xmit Signed-off-by: Dmitry Kravkov Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index a56f1182c176..cdc31ac4a9df 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -745,12 +745,9 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb) goto error; skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; return skb; - } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - err = skb_checksum_help(skb); - if (unlikely(err)) - goto error; } - skb->ip_summed = CHECKSUM_NONE; + if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; return skb; -- cgit v1.2.3 From eb6b9a8cad65e820b145547844b108117cece3a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 18 Feb 2013 09:50:53 +0000 Subject: ip_gre: propogate target device GSO capability to the tunnel device Signed-off-by: Dmitry Kravkov Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index cdc31ac4a9df..31bc94152693 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1103,8 +1103,14 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) tunnel->hlen = addend; /* TCP offload with GRE SEQ is not supported. */ if (!(tunnel->parms.o_flags & GRE_SEQ)) { - dev->features |= NETIF_F_GSO_SOFTWARE; - dev->hw_features |= NETIF_F_GSO_SOFTWARE; + /* device supports enc gso offload*/ + if (tdev->hw_enc_features & NETIF_F_GRE_GSO) { + dev->features |= NETIF_F_TSO; + dev->hw_features |= NETIF_F_TSO; + } else { + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->hw_features |= NETIF_F_GSO_SOFTWARE; + } } return mtu; -- cgit v1.2.3 From 900ff8c6321418dafa03c22e215cb9646a2541b9 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 18 Feb 2013 19:20:33 +0000 Subject: net: move procfs code to net/core/net-procfs.c Similar to net/core/net-sysfs.c, group procfs code to a single unit. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 36 +++- net/core/Makefile | 1 + net/core/dev.c | 384 +----------------------------------------- net/core/dev_addr_lists.c | 74 --------- net/core/net-procfs.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 450 insertions(+), 459 deletions(-) create mode 100644 net/core/net-procfs.c (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 920361bc27e7..f111b4f038f3 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2692,9 +2692,9 @@ extern void net_enable_timestamp(void); extern void net_disable_timestamp(void); #ifdef CONFIG_PROC_FS -extern void *dev_seq_start(struct seq_file *seq, loff_t *pos); -extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos); -extern void dev_seq_stop(struct seq_file *seq, void *v); +extern int __init dev_proc_init(void); +#else +#define dev_proc_init() 0 #endif extern int netdev_class_create_file(struct class_attribute *class_attr); @@ -2896,4 +2896,34 @@ do { \ }) #endif +/* + * The list of packet types we will receive (as opposed to discard) + * and the routines to invoke. + * + * Why 16. Because with 16 the only overlap we get on a hash of the + * low nibble of the protocol value is RARP/SNAP/X.25. + * + * NOTE: That is no longer true with the addition of VLAN tags. Not + * sure which should go first, but I bet it won't make much + * difference if we are running VLANs. The good news is that + * this protocol won't be in the list unless compiled in, so + * the average user (w/out VLANs) will not be adversely affected. + * --BLG + * + * 0800 IP + * 8100 802.1Q VLAN + * 0001 802.3 + * 0002 AX.25 + * 0004 802.2 + * 8035 RARP + * 0005 SNAP + * 0805 X.25 + * 0806 ARP + * 8137 IPX + * 0009 Localtalk + * 86DD IPv6 + */ +#define PTYPE_HASH_SIZE (16) +#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1) + #endif /* _LINUX_NETDEVICE_H */ diff --git a/net/core/Makefile b/net/core/Makefile index 0c5e3618c80b..b33b996f5dd6 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -13,6 +13,7 @@ obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o +obj-$(CONFIG_PROC_FS) += net-procfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_NETPOLL) += netpoll.o obj-$(CONFIG_NET_DMA) += user_dma.o diff --git a/net/core/dev.c b/net/core/dev.c index decf55f9ad80..8d9ddb09f208 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -97,8 +97,6 @@ #include #include #include -#include -#include #include #include #include @@ -110,7 +108,6 @@ #include #include #include -#include #include #include #include @@ -141,41 +138,10 @@ /* This should be increased if a protocol with a bigger head is added. */ #define GRO_MAX_HEAD (MAX_HEADER + 128) -/* - * The list of packet types we will receive (as opposed to discard) - * and the routines to invoke. - * - * Why 16. Because with 16 the only overlap we get on a hash of the - * low nibble of the protocol value is RARP/SNAP/X.25. - * - * NOTE: That is no longer true with the addition of VLAN tags. Not - * sure which should go first, but I bet it won't make much - * difference if we are running VLANs. The good news is that - * this protocol won't be in the list unless compiled in, so - * the average user (w/out VLANs) will not be adversely affected. - * --BLG - * - * 0800 IP - * 8100 802.1Q VLAN - * 0001 802.3 - * 0002 AX.25 - * 0004 802.2 - * 8035 RARP - * 0005 SNAP - * 0805 X.25 - * 0806 ARP - * 8137 IPX - * 0009 Localtalk - * 86DD IPv6 - */ - -#define PTYPE_HASH_SIZE (16) -#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1) - static DEFINE_SPINLOCK(ptype_lock); static DEFINE_SPINLOCK(offload_lock); -static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; -static struct list_head ptype_all __read_mostly; /* Taps */ +struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; +struct list_head ptype_all __read_mostly; /* Taps */ static struct list_head offload_base __read_mostly; /* @@ -4217,352 +4183,6 @@ softnet_break: goto out; } -#ifdef CONFIG_PROC_FS - -#define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) - -#define get_bucket(x) ((x) >> BUCKET_SPACE) -#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) -#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) - -static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos) -{ - struct net *net = seq_file_net(seq); - struct net_device *dev; - struct hlist_node *p; - struct hlist_head *h; - unsigned int count = 0, offset = get_offset(*pos); - - h = &net->dev_name_head[get_bucket(*pos)]; - hlist_for_each_entry_rcu(dev, p, h, name_hlist) { - if (++count == offset) - return dev; - } - - return NULL; -} - -static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos) -{ - struct net_device *dev; - unsigned int bucket; - - do { - dev = dev_from_same_bucket(seq, pos); - if (dev) - return dev; - - bucket = get_bucket(*pos) + 1; - *pos = set_bucket_offset(bucket, 1); - } while (bucket < NETDEV_HASHENTRIES); - - return NULL; -} - -/* - * This is invoked by the /proc filesystem handler to display a device - * in detail. - */ -void *dev_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - rcu_read_lock(); - if (!*pos) - return SEQ_START_TOKEN; - - if (get_bucket(*pos) >= NETDEV_HASHENTRIES) - return NULL; - - return dev_from_bucket(seq, pos); -} - -void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return dev_from_bucket(seq, pos); -} - -void dev_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - -static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) -{ - struct rtnl_link_stats64 temp; - const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); - - seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " - "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", - dev->name, stats->rx_bytes, stats->rx_packets, - stats->rx_errors, - stats->rx_dropped + stats->rx_missed_errors, - stats->rx_fifo_errors, - stats->rx_length_errors + stats->rx_over_errors + - stats->rx_crc_errors + stats->rx_frame_errors, - stats->rx_compressed, stats->multicast, - stats->tx_bytes, stats->tx_packets, - stats->tx_errors, stats->tx_dropped, - stats->tx_fifo_errors, stats->collisions, - stats->tx_carrier_errors + - stats->tx_aborted_errors + - stats->tx_window_errors + - stats->tx_heartbeat_errors, - stats->tx_compressed); -} - -/* - * Called from the PROCfs module. This now uses the new arbitrary sized - * /proc/net interface to create /proc/net/dev - */ -static int dev_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, "Inter-| Receive " - " | Transmit\n" - " face |bytes packets errs drop fifo frame " - "compressed multicast|bytes packets errs " - "drop fifo colls carrier compressed\n"); - else - dev_seq_printf_stats(seq, v); - return 0; -} - -static struct softnet_data *softnet_get_online(loff_t *pos) -{ - struct softnet_data *sd = NULL; - - while (*pos < nr_cpu_ids) - if (cpu_online(*pos)) { - sd = &per_cpu(softnet_data, *pos); - break; - } else - ++*pos; - return sd; -} - -static void *softnet_seq_start(struct seq_file *seq, loff_t *pos) -{ - return softnet_get_online(pos); -} - -static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return softnet_get_online(pos); -} - -static void softnet_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int softnet_seq_show(struct seq_file *seq, void *v) -{ - struct softnet_data *sd = v; - - seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", - sd->processed, sd->dropped, sd->time_squeeze, 0, - 0, 0, 0, 0, /* was fastroute */ - sd->cpu_collision, sd->received_rps); - return 0; -} - -static const struct seq_operations dev_seq_ops = { - .start = dev_seq_start, - .next = dev_seq_next, - .stop = dev_seq_stop, - .show = dev_seq_show, -}; - -static int dev_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &dev_seq_ops, - sizeof(struct seq_net_private)); -} - -static const struct file_operations dev_seq_fops = { - .owner = THIS_MODULE, - .open = dev_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -static const struct seq_operations softnet_seq_ops = { - .start = softnet_seq_start, - .next = softnet_seq_next, - .stop = softnet_seq_stop, - .show = softnet_seq_show, -}; - -static int softnet_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &softnet_seq_ops); -} - -static const struct file_operations softnet_seq_fops = { - .owner = THIS_MODULE, - .open = softnet_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static void *ptype_get_idx(loff_t pos) -{ - struct packet_type *pt = NULL; - loff_t i = 0; - int t; - - list_for_each_entry_rcu(pt, &ptype_all, list) { - if (i == pos) - return pt; - ++i; - } - - for (t = 0; t < PTYPE_HASH_SIZE; t++) { - list_for_each_entry_rcu(pt, &ptype_base[t], list) { - if (i == pos) - return pt; - ++i; - } - } - return NULL; -} - -static void *ptype_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - rcu_read_lock(); - return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct packet_type *pt; - struct list_head *nxt; - int hash; - - ++*pos; - if (v == SEQ_START_TOKEN) - return ptype_get_idx(0); - - pt = v; - nxt = pt->list.next; - if (pt->type == htons(ETH_P_ALL)) { - if (nxt != &ptype_all) - goto found; - hash = 0; - nxt = ptype_base[0].next; - } else - hash = ntohs(pt->type) & PTYPE_HASH_MASK; - - while (nxt == &ptype_base[hash]) { - if (++hash >= PTYPE_HASH_SIZE) - return NULL; - nxt = ptype_base[hash].next; - } -found: - return list_entry(nxt, struct packet_type, list); -} - -static void ptype_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - -static int ptype_seq_show(struct seq_file *seq, void *v) -{ - struct packet_type *pt = v; - - if (v == SEQ_START_TOKEN) - seq_puts(seq, "Type Device Function\n"); - else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) { - if (pt->type == htons(ETH_P_ALL)) - seq_puts(seq, "ALL "); - else - seq_printf(seq, "%04x", ntohs(pt->type)); - - seq_printf(seq, " %-8s %pF\n", - pt->dev ? pt->dev->name : "", pt->func); - } - - return 0; -} - -static const struct seq_operations ptype_seq_ops = { - .start = ptype_seq_start, - .next = ptype_seq_next, - .stop = ptype_seq_stop, - .show = ptype_seq_show, -}; - -static int ptype_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &ptype_seq_ops, - sizeof(struct seq_net_private)); -} - -static const struct file_operations ptype_seq_fops = { - .owner = THIS_MODULE, - .open = ptype_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - - -static int __net_init dev_proc_net_init(struct net *net) -{ - int rc = -ENOMEM; - - if (!proc_create("dev", S_IRUGO, net->proc_net, &dev_seq_fops)) - goto out; - if (!proc_create("softnet_stat", S_IRUGO, net->proc_net, - &softnet_seq_fops)) - goto out_dev; - if (!proc_create("ptype", S_IRUGO, net->proc_net, &ptype_seq_fops)) - goto out_softnet; - - if (wext_proc_init(net)) - goto out_ptype; - rc = 0; -out: - return rc; -out_ptype: - remove_proc_entry("ptype", net->proc_net); -out_softnet: - remove_proc_entry("softnet_stat", net->proc_net); -out_dev: - remove_proc_entry("dev", net->proc_net); - goto out; -} - -static void __net_exit dev_proc_net_exit(struct net *net) -{ - wext_proc_exit(net); - - remove_proc_entry("ptype", net->proc_net); - remove_proc_entry("softnet_stat", net->proc_net); - remove_proc_entry("dev", net->proc_net); -} - -static struct pernet_operations __net_initdata dev_proc_ops = { - .init = dev_proc_net_init, - .exit = dev_proc_net_exit, -}; - -static int __init dev_proc_init(void) -{ - return register_pernet_subsys(&dev_proc_ops); -} -#else -#define dev_proc_init() 0 -#endif /* CONFIG_PROC_FS */ - - struct netdev_upper { struct net_device *dev; bool master; diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 89562529df45..bd2eb9d3e369 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -15,7 +15,6 @@ #include #include #include -#include /* * General list handling functions @@ -727,76 +726,3 @@ void dev_mc_init(struct net_device *dev) __hw_addr_init(&dev->mc); } EXPORT_SYMBOL(dev_mc_init); - -#ifdef CONFIG_PROC_FS -#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_create("dev_mcast", 0, net->proc_net, &dev_mc_seq_fops)) - return -ENOMEM; - return 0; -} - -static void __net_exit dev_mc_net_exit(struct net *net) -{ - remove_proc_entry("dev_mcast", net->proc_net); -} - -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/net-procfs.c b/net/core/net-procfs.c new file mode 100644 index 000000000000..ac87066491e9 --- /dev/null +++ b/net/core/net-procfs.c @@ -0,0 +1,414 @@ +#include +#include +#include +#include + +#define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) + +#define get_bucket(x) ((x) >> BUCKET_SPACE) +#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) +#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) + +extern struct list_head ptype_all __read_mostly; +extern struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly; + +static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos) +{ + struct net *net = seq_file_net(seq); + struct net_device *dev; + struct hlist_node *p; + struct hlist_head *h; + unsigned int count = 0, offset = get_offset(*pos); + + h = &net->dev_name_head[get_bucket(*pos)]; + hlist_for_each_entry_rcu(dev, p, h, name_hlist) { + if (++count == offset) + return dev; + } + + return NULL; +} + +static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos) +{ + struct net_device *dev; + unsigned int bucket; + + do { + dev = dev_from_same_bucket(seq, pos); + if (dev) + return dev; + + bucket = get_bucket(*pos) + 1; + *pos = set_bucket_offset(bucket, 1); + } while (bucket < NETDEV_HASHENTRIES); + + return NULL; +} + +/* + * This is invoked by the /proc filesystem handler to display a device + * in detail. + */ +static void *dev_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) +{ + rcu_read_lock(); + if (!*pos) + return SEQ_START_TOKEN; + + if (get_bucket(*pos) >= NETDEV_HASHENTRIES) + return NULL; + + return dev_from_bucket(seq, pos); +} + +static void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return dev_from_bucket(seq, pos); +} + +static void dev_seq_stop(struct seq_file *seq, void *v) + __releases(RCU) +{ + rcu_read_unlock(); +} + +static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) +{ + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); + + seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " + "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", + dev->name, stats->rx_bytes, stats->rx_packets, + stats->rx_errors, + stats->rx_dropped + stats->rx_missed_errors, + stats->rx_fifo_errors, + stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors, + stats->rx_compressed, stats->multicast, + stats->tx_bytes, stats->tx_packets, + stats->tx_errors, stats->tx_dropped, + stats->tx_fifo_errors, stats->collisions, + stats->tx_carrier_errors + + stats->tx_aborted_errors + + stats->tx_window_errors + + stats->tx_heartbeat_errors, + stats->tx_compressed); +} + +/* + * Called from the PROCfs module. This now uses the new arbitrary sized + * /proc/net interface to create /proc/net/dev + */ +static int dev_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Inter-| Receive " + " | Transmit\n" + " face |bytes packets errs drop fifo frame " + "compressed multicast|bytes packets errs " + "drop fifo colls carrier compressed\n"); + else + dev_seq_printf_stats(seq, v); + return 0; +} + +static struct softnet_data *softnet_get_online(loff_t *pos) +{ + struct softnet_data *sd = NULL; + + while (*pos < nr_cpu_ids) + if (cpu_online(*pos)) { + sd = &per_cpu(softnet_data, *pos); + break; + } else + ++*pos; + return sd; +} + +static void *softnet_seq_start(struct seq_file *seq, loff_t *pos) +{ + return softnet_get_online(pos); +} + +static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return softnet_get_online(pos); +} + +static void softnet_seq_stop(struct seq_file *seq, void *v) +{ +} + +static int softnet_seq_show(struct seq_file *seq, void *v) +{ + struct softnet_data *sd = v; + + seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + sd->processed, sd->dropped, sd->time_squeeze, 0, + 0, 0, 0, 0, /* was fastroute */ + sd->cpu_collision, sd->received_rps); + return 0; +} + +static const struct seq_operations dev_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = dev_seq_show, +}; + +static int dev_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &dev_seq_ops, + sizeof(struct seq_net_private)); +} + +static const struct file_operations dev_seq_fops = { + .owner = THIS_MODULE, + .open = dev_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +static const struct seq_operations softnet_seq_ops = { + .start = softnet_seq_start, + .next = softnet_seq_next, + .stop = softnet_seq_stop, + .show = softnet_seq_show, +}; + +static int softnet_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &softnet_seq_ops); +} + +static const struct file_operations softnet_seq_fops = { + .owner = THIS_MODULE, + .open = softnet_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *ptype_get_idx(loff_t pos) +{ + struct packet_type *pt = NULL; + loff_t i = 0; + int t; + + list_for_each_entry_rcu(pt, &ptype_all, list) { + if (i == pos) + return pt; + ++i; + } + + for (t = 0; t < PTYPE_HASH_SIZE; t++) { + list_for_each_entry_rcu(pt, &ptype_base[t], list) { + if (i == pos) + return pt; + ++i; + } + } + return NULL; +} + +static void *ptype_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) +{ + rcu_read_lock(); + return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct packet_type *pt; + struct list_head *nxt; + int hash; + + ++*pos; + if (v == SEQ_START_TOKEN) + return ptype_get_idx(0); + + pt = v; + nxt = pt->list.next; + if (pt->type == htons(ETH_P_ALL)) { + if (nxt != &ptype_all) + goto found; + hash = 0; + nxt = ptype_base[0].next; + } else + hash = ntohs(pt->type) & PTYPE_HASH_MASK; + + while (nxt == &ptype_base[hash]) { + if (++hash >= PTYPE_HASH_SIZE) + return NULL; + nxt = ptype_base[hash].next; + } +found: + return list_entry(nxt, struct packet_type, list); +} + +static void ptype_seq_stop(struct seq_file *seq, void *v) + __releases(RCU) +{ + rcu_read_unlock(); +} + +static int ptype_seq_show(struct seq_file *seq, void *v) +{ + struct packet_type *pt = v; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Type Device Function\n"); + else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) { + if (pt->type == htons(ETH_P_ALL)) + seq_puts(seq, "ALL "); + else + seq_printf(seq, "%04x", ntohs(pt->type)); + + seq_printf(seq, " %-8s %pF\n", + pt->dev ? pt->dev->name : "", pt->func); + } + + return 0; +} + +static const struct seq_operations ptype_seq_ops = { + .start = ptype_seq_start, + .next = ptype_seq_next, + .stop = ptype_seq_stop, + .show = ptype_seq_show, +}; + +static int ptype_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &ptype_seq_ops, + sizeof(struct seq_net_private)); +} + +static const struct file_operations ptype_seq_fops = { + .owner = THIS_MODULE, + .open = ptype_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + + +static int __net_init dev_proc_net_init(struct net *net) +{ + int rc = -ENOMEM; + + if (!proc_create("dev", S_IRUGO, net->proc_net, &dev_seq_fops)) + goto out; + if (!proc_create("softnet_stat", S_IRUGO, net->proc_net, + &softnet_seq_fops)) + goto out_dev; + if (!proc_create("ptype", S_IRUGO, net->proc_net, &ptype_seq_fops)) + goto out_softnet; + + if (wext_proc_init(net)) + goto out_ptype; + rc = 0; +out: + return rc; +out_ptype: + remove_proc_entry("ptype", net->proc_net); +out_softnet: + remove_proc_entry("softnet_stat", net->proc_net); +out_dev: + remove_proc_entry("dev", net->proc_net); + goto out; +} + +static void __net_exit dev_proc_net_exit(struct net *net) +{ + wext_proc_exit(net); + + remove_proc_entry("ptype", net->proc_net); + remove_proc_entry("softnet_stat", net->proc_net); + remove_proc_entry("dev", net->proc_net); +} + +static struct pernet_operations __net_initdata dev_proc_ops = { + .init = dev_proc_net_init, + .exit = dev_proc_net_exit, +}; + +int __init dev_proc_init(void) +{ + return register_pernet_subsys(&dev_proc_ops); +} + +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, +}; + +static int __net_init dev_mc_net_init(struct net *net) +{ + if (!proc_create("dev_mcast", 0, net->proc_net, &dev_mc_seq_fops)) + return -ENOMEM; + return 0; +} + +static void __net_exit dev_mc_net_exit(struct net *net) +{ + remove_proc_entry("dev_mcast", net->proc_net); +} + +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); +} -- cgit v1.2.3 From 082c7ca42b0bf33aa3fb5eeb619dfbcfaa20d216 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Tue, 19 Feb 2013 00:43:12 +0000 Subject: net: ipv4: fix waring -Wunused-variable the vars ip_rt_gc_timeout is used only when CONFIG_SYSCTL is selected. move these vars into CONFIG_SYSCTL. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- net/ipv4/route.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3bdd1b1ad1b3..6e2851464f8f 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -117,15 +117,11 @@ #define RT_GC_TIMEOUT (300*HZ) static int ip_rt_max_size; -static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT; -static int ip_rt_gc_interval __read_mostly = 60 * HZ; -static int ip_rt_gc_min_interval __read_mostly = HZ / 2; static int ip_rt_redirect_number __read_mostly = 9; static int ip_rt_redirect_load __read_mostly = HZ / 50; static int ip_rt_redirect_silence __read_mostly = ((HZ / 50) << (9 + 1)); static int ip_rt_error_cost __read_mostly = HZ; static int ip_rt_error_burst __read_mostly = 5 * HZ; -static int ip_rt_gc_elasticity __read_mostly = 8; static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ; static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20; static int ip_rt_min_advmss __read_mostly = 256; @@ -2423,6 +2419,11 @@ void ip_rt_multicast_event(struct in_device *in_dev) } #ifdef CONFIG_SYSCTL +static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT; +static int ip_rt_gc_interval __read_mostly = 60 * HZ; +static int ip_rt_gc_min_interval __read_mostly = HZ / 2; +static int ip_rt_gc_elasticity __read_mostly = 8; + static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) -- cgit v1.2.3 From cd0615746ba0f6643fb984345ae6ee0b73404ca6 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 19 Feb 2013 02:47:05 +0000 Subject: net: fix a build failure when !CONFIG_PROC_FS When !CONFIG_PROC_FS dev_mcast_init() is not defined, actually we can just merge dev_mcast_init() into dev_proc_init(). Reported-by: Gao feng Cc: Gao feng Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - net/core/dev.c | 1 - net/core/net-procfs.c | 12 +++++------- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f111b4f038f3..b3d00fa4b314 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2646,7 +2646,6 @@ extern void netdev_notify_peers(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); -extern void dev_mcast_init(void); extern struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); extern void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, diff --git a/net/core/dev.c b/net/core/dev.c index 8d9ddb09f208..17bc535115d3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6260,7 +6260,6 @@ static int __init net_dev_init(void) hotcpu_notifier(dev_cpu_callback, 0); dst_init(); - dev_mcast_init(); rc = 0; out: return rc; diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c index ac87066491e9..0f6bb6f8d391 100644 --- a/net/core/net-procfs.c +++ b/net/core/net-procfs.c @@ -341,11 +341,6 @@ static struct pernet_operations __net_initdata dev_proc_ops = { .exit = dev_proc_net_exit, }; -int __init dev_proc_init(void) -{ - return register_pernet_subsys(&dev_proc_ops); -} - static int dev_mc_seq_show(struct seq_file *seq, void *v) { struct netdev_hw_addr *ha; @@ -408,7 +403,10 @@ static struct pernet_operations __net_initdata dev_mc_net_ops = { .exit = dev_mc_net_exit, }; -void __init dev_mcast_init(void) +int __init dev_proc_init(void) { - register_pernet_subsys(&dev_mc_net_ops); + int ret = register_pernet_subsys(&dev_proc_ops); + if (!ret) + return register_pernet_subsys(&dev_mc_net_ops); + return ret; } -- cgit v1.2.3 From 4aa896c4baaab2b4f499c7434f85457f9f3d9fe4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 Feb 2013 12:14:10 +0000 Subject: ip_gre: remove an extra dst_release() commit 68c331631143 (v4 GRE: Add TCP segmentation offload for GRE) introduced a bug in error path. dst is attached to skb, so will be released when skb is freed. Signed-off-by: Eric Dumazet Cc: Pravin B Shelar Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 31bc94152693..5ef4da780ac1 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1006,10 +1006,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev if (skb_has_shared_frag(skb)) { err = __skb_linearize(skb); - if (err) { - ip_rt_put(rt); + if (err) goto tx_error; - } } *ptr = 0; -- cgit v1.2.3 From 68534c682e8f5c333f835818ca5a89d3e6288870 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Tue, 19 Feb 2013 22:51:30 +0000 Subject: net: fix a wrong assignment in skb_split() commit c9af6db4c11ccc6c3e7f1 (net: Fix possible wrong checksum generation) has a suspicous piece: - skb_shinfo(skb1)->gso_type = skb_shinfo(skb)->gso_type; - + skb_shinfo(skb)->tx_flags = skb_shinfo(skb1)->tx_flags & SKBTX_SHARED_FRAG; skb1 is the new skb, therefore should be on the left side of the assignment. This patch fixes it. Cc: Pravin B Shelar Cc: David S. Miller Signed-off-by: Cong Wang Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2a3ca33c30aa..33245ef54c3b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2326,7 +2326,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) { int pos = skb_headlen(skb); - skb_shinfo(skb)->tx_flags = skb_shinfo(skb1)->tx_flags & SKBTX_SHARED_FRAG; + skb_shinfo(skb1)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; if (len < pos) /* Split line is inside header. */ skb_split_inside_header(skb, skb1, len, pos); else /* Second chunk has no header, nothing to copy. */ -- cgit v1.2.3 From ecd9883724b78cc72ed92c98bcb1a46c764fff21 Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Wed, 20 Feb 2013 00:29:08 +0000 Subject: ipv6: fix race condition regarding dst->expires and dst->from. Eric Dumazet wrote: | Some strange crashes happen in rt6_check_expired(), with access | to random addresses. | | At first glance, it looks like the RTF_EXPIRES and | stuff added in commit 1716a96101c49186b | (ipv6: fix problem with expired dst cache) | are racy : same dst could be manipulated at the same time | on different cpus. | | At some point, our stack believes rt->dst.from contains a dst pointer, | while its really a jiffie value (as rt->dst.expires shares the same area | of memory) | | rt6_update_expires() should be fixed, or am I missing something ? | | CC Neil because of https://bugzilla.redhat.com/show_bug.cgi?id=892060 Because we do not have any locks for dst_entry, we cannot change essential structure in the entry; e.g., we cannot change reference to other entity. To fix this issue, split 'from' and 'expires' field in dst_entry out of union. Once it is 'from' is assigned in the constructor, keep the reference until the very last stage of the life time of the object. Of course, it is unsafe to change 'from', so make rt6_set_from simple just for fresh entries. Reported-by: Eric Dumazet Reported-by: Neil Horman CC: Gao Feng Signed-off-by: YOSHIFUJI Hideaki Reviewed-by: Eric Dumazet Reported-by: Steinar H. Gunderson Reviewed-by: Neil Horman Signed-off-by: David S. Miller --- include/net/dst.h | 8 ++------ include/net/ip6_fib.h | 39 ++++++++++++--------------------------- net/core/dst.c | 1 + net/ipv6/route.c | 8 +++----- 4 files changed, 18 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/include/net/dst.h b/include/net/dst.h index 3da47e0a4a1f..853cda11e518 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -36,13 +36,9 @@ struct dst_entry { struct net_device *dev; struct dst_ops *ops; unsigned long _metrics; - union { - unsigned long expires; - /* point to where the dst_entry copied from */ - struct dst_entry *from; - }; + unsigned long expires; struct dst_entry *path; - void *__pad0; + struct dst_entry *from; #ifdef CONFIG_XFRM struct xfrm_state *xfrm; #else diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 6919a501f99e..2a601e7da1bf 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -164,50 +164,35 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) static inline void rt6_clean_expires(struct rt6_info *rt) { - if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) - dst_release(rt->dst.from); - rt->rt6i_flags &= ~RTF_EXPIRES; - rt->dst.from = NULL; } static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) { - if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) - dst_release(rt->dst.from); - - rt->rt6i_flags |= RTF_EXPIRES; rt->dst.expires = expires; + rt->rt6i_flags |= RTF_EXPIRES; } -static inline void rt6_update_expires(struct rt6_info *rt, int timeout) +static inline void rt6_update_expires(struct rt6_info *rt0, int timeout) { - if (!(rt->rt6i_flags & RTF_EXPIRES)) { - if (rt->dst.from) - dst_release(rt->dst.from); - /* dst_set_expires relies on expires == 0 - * if it has not been set previously. - */ - rt->dst.expires = 0; - } - - dst_set_expires(&rt->dst, timeout); - rt->rt6i_flags |= RTF_EXPIRES; + struct rt6_info *rt; + + for (rt = rt0; rt && !(rt->rt6i_flags & RTF_EXPIRES); + rt = (struct rt6_info *)rt->dst.from); + if (rt && rt != rt0) + rt0->dst.expires = rt->dst.expires; + + dst_set_expires(&rt0->dst, timeout); + rt0->rt6i_flags |= RTF_EXPIRES; } static inline void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) { struct dst_entry *new = (struct dst_entry *) from; - if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) { - if (new == rt->dst.from) - return; - dst_release(rt->dst.from); - } - rt->rt6i_flags &= ~RTF_EXPIRES; - rt->dst.from = new; dst_hold(new); + rt->dst.from = new; } static inline void ip6_rt_put(struct rt6_info *rt) diff --git a/net/core/dst.c b/net/core/dst.c index ee6153e2cf43..35fd12f1a69c 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -179,6 +179,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, dst_init_metrics(dst, dst_default_metrics, true); dst->expires = 0UL; dst->path = dst; + dst->from = NULL; #ifdef CONFIG_XFRM dst->xfrm = NULL; #endif diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 515bb51e05a8..928266569689 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -287,6 +287,7 @@ static void ip6_dst_destroy(struct dst_entry *dst) { struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; + struct dst_entry *from = dst->from; if (!(rt->dst.flags & DST_HOST)) dst_destroy_metrics_generic(dst); @@ -296,8 +297,8 @@ static void ip6_dst_destroy(struct dst_entry *dst) in6_dev_put(idev); } - if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) - dst_release(dst->from); + dst->from = NULL; + dst_release(from); if (rt6_has_peer(rt)) { struct inet_peer *peer = rt6_peer_ptr(rt); @@ -1010,7 +1011,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori rt->rt6i_gateway = ort->rt6i_gateway; rt->rt6i_flags = ort->rt6i_flags; - rt6_clean_expires(rt); rt->rt6i_metric = 0; memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); @@ -1784,8 +1784,6 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == (RTF_DEFAULT | RTF_ADDRCONF)) rt6_set_from(rt, ort); - else - rt6_clean_expires(rt); rt->rt6i_metric = 0; #ifdef CONFIG_IPV6_SUBTREES -- cgit v1.2.3