diff options
Diffstat (limited to 'net')
80 files changed, 5593 insertions, 2821 deletions
diff --git a/net/Kconfig b/net/Kconfig index 7051b9710675..041c35edb763 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -23,6 +23,26 @@ menuconfig NET if NET +config WANT_COMPAT_NETLINK_MESSAGES + bool + help + This option can be selected by other options that need compat + netlink messages. + +config COMPAT_NETLINK_MESSAGES + def_bool y + depends on COMPAT + depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES + help + This option makes it possible to send different netlink messages + to tasks depending on whether the task is a compat task or not. To + achieve this, you need to set skb_shinfo(skb)->frag_list to the + compat skb before sending the skb, the netlink code will sort out + which message to actually pass to the task. + + Newly written code should NEVER need this option but do + compat-independent messages instead! + menu "Networking options" source "net/packet/Kconfig" diff --git a/net/Makefile b/net/Makefile index ba324aefda73..1542e7268a7b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -24,7 +24,6 @@ obj-y += ipv6/ endif obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ -obj-$(CONFIG_NET_SCHED) += sched/ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 2912665fc58c..848af113ba2a 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -238,7 +238,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) /* netif_stop_queue(dev); */ dev_kfree_skb(skb); read_unlock(&devs_lock); - return 0; + return NETDEV_TX_OK; } if (!br2684_xmit_vcc(skb, dev, brvcc)) { /* @@ -252,7 +252,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_fifo_errors++; } read_unlock(&devs_lock); - return 0; + return NETDEV_TX_OK; } /* diff --git a/net/atm/clip.c b/net/atm/clip.c index e65a3b1477f8..64910bb86089 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -373,7 +373,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_ERR "clip_start_xmit: skb_dst(skb) == NULL\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } if (!skb_dst(skb)->neighbour) { #if 0 @@ -387,7 +387,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n"); dev_kfree_skb(skb); dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } entry = NEIGH2ENTRY(skb_dst(skb)->neighbour); if (!entry->vccs) { @@ -402,7 +402,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb); dev->stats.tx_dropped++; } - return 0; + return NETDEV_TX_OK; } pr_debug("neigh %p, vccs %p\n", entry, entry->vccs); ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc; @@ -421,14 +421,14 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */ if (old) { printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n"); - return 0; + return NETDEV_TX_OK; } dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; vcc->send(vcc, skb); if (atm_may_send(vcc, 0)) { entry->vccs->xoff = 0; - return 0; + return NETDEV_TX_OK; } spin_lock_irqsave(&clip_priv->xoff_lock, flags); netif_stop_queue(dev); /* XOFF -> throttle immediately */ @@ -440,7 +440,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev) of the brief netif_stop_queue. If this isn't true or if it changes, use netif_wake_queue instead. */ spin_unlock_irqrestore(&clip_priv->xoff_lock, flags); - return 0; + return NETDEV_TX_OK; } static int clip_mkip(struct atm_vcc *vcc, int timeout) diff --git a/net/atm/lec.c b/net/atm/lec.c index ff2e594dca9b..c463868c993b 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -289,7 +289,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); kfree_skb(skb); if (skb2 == NULL) - return 0; + return NETDEV_TX_OK; skb = skb2; } skb_push(skb, 2); @@ -307,7 +307,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN); kfree_skb(skb); if (skb2 == NULL) - return 0; + return NETDEV_TX_OK; skb = skb2; } #endif @@ -345,7 +345,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb); if (skb2 == NULL) { dev->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } skb = skb2; } @@ -416,7 +416,7 @@ out: if (entry) lec_arp_put(entry); dev->trans_start = jiffies; - return 0; + return NETDEV_TX_OK; } /* The inverse routine to net_open(). */ diff --git a/net/atm/mpc.c b/net/atm/mpc.c index e5bf11453a18..1ac4b94bf626 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -554,7 +554,7 @@ static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev) while (i < mpc->number_of_mps_macs) { if (!compare_ether_addr(eth->h_dest, (mpc->mps_macs + i*ETH_ALEN))) if ( send_via_shortcut(skb, mpc) == 0 ) /* try shortcut */ - return 0; /* success! */ + return NETDEV_TX_OK; /* success! */ i++; } diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index d7a0e9722def..9c42990126a0 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -175,14 +175,14 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef CONFIG_BT_BNEP_MC_FILTER if (bnep_net_mc_filter(skb, s)) { kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } #endif #ifdef CONFIG_BT_BNEP_PROTO_FILTER if (bnep_net_proto_filter(skb, s)) { kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } #endif @@ -203,7 +203,7 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); } - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops bnep_netdev_ops = { diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 18538d7460d7..15d43ba86b53 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -39,7 +39,7 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) else br_flood_deliver(br, skb); - return 0; + return NETDEV_TX_OK; } static int br_dev_open(struct net_device *dev) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index d22f611e4004..4fde7425077d 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -905,46 +905,62 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb, * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because * ip_refrag() can return NF_STOLEN. */ static struct nf_hook_ops br_nf_ops[] __read_mostly = { - { .hook = br_nf_pre_routing, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_PRE_ROUTING, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_local_in, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_LOCAL_IN, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_forward_ip, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_FORWARD, - .priority = NF_BR_PRI_BRNF - 1, }, - { .hook = br_nf_forward_arp, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_FORWARD, - .priority = NF_BR_PRI_BRNF, }, - { .hook = br_nf_local_out, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_LOCAL_OUT, - .priority = NF_BR_PRI_FIRST, }, - { .hook = br_nf_post_routing, - .owner = THIS_MODULE, - .pf = PF_BRIDGE, - .hooknum = NF_BR_POST_ROUTING, - .priority = NF_BR_PRI_LAST, }, - { .hook = ip_sabotage_in, - .owner = THIS_MODULE, - .pf = PF_INET, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP_PRI_FIRST, }, - { .hook = ip_sabotage_in, - .owner = THIS_MODULE, - .pf = PF_INET6, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP6_PRI_FIRST, }, + { + .hook = br_nf_pre_routing, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_PRE_ROUTING, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_local_in, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_LOCAL_IN, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_forward_ip, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_FORWARD, + .priority = NF_BR_PRI_BRNF - 1, + }, + { + .hook = br_nf_forward_arp, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_FORWARD, + .priority = NF_BR_PRI_BRNF, + }, + { + .hook = br_nf_local_out, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_LOCAL_OUT, + .priority = NF_BR_PRI_FIRST, + }, + { + .hook = br_nf_post_routing, + .owner = THIS_MODULE, + .pf = PF_BRIDGE, + .hooknum = NF_BR_POST_ROUTING, + .priority = NF_BR_PRI_LAST, + }, + { + .hook = ip_sabotage_in, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_FIRST, + }, + { + .hook = ip_sabotage_in, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP6_PRI_FIRST, + }, }; #ifdef CONFIG_SYSCTL diff --git a/net/compat.c b/net/compat.c index 8d739053afe4..12728b17a226 100644 --- a/net/compat.c +++ b/net/compat.c @@ -743,6 +743,18 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } +asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags) +{ + return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT); +} + +asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len, + unsigned flags, struct sockaddr __user *addr, + int __user *addrlen) +{ + return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); +} + asmlinkage long compat_sys_socketcall(int call, u32 __user *args) { int ret; @@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args) ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]); break; case SYS_RECV: - ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]); + ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]); break; case SYS_RECVFROM: - ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5])); + ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], + compat_ptr(a[4]), compat_ptr(a[5])); break; case SYS_SHUTDOWN: ret = sys_shutdown(a0,a1); diff --git a/net/core/dev.c b/net/core/dev.c index 70c27e0c7c32..dca8b5000d3b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1704,7 +1704,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, skb_dst_drop(skb); rc = ops->ndo_start_xmit(skb, dev); - if (rc == 0) + if (rc == NETDEV_TX_OK) txq_trans_update(txq); /* * TODO: if skb_orphan() was called by @@ -1730,7 +1730,7 @@ gso: skb->next = nskb->next; nskb->next = NULL; rc = ops->ndo_start_xmit(nskb, dev); - if (unlikely(rc)) { + if (unlikely(rc != NETDEV_TX_OK)) { nskb->next = skb->next; skb->next = nskb; return rc; @@ -1744,7 +1744,7 @@ gso: out_kfree_skb: kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static u32 skb_tx_hashrnd; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 163b4f5b0365..c6f9ad8e4c7a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1316,7 +1316,7 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, } EXPORT_SYMBOL(pneigh_enqueue); -static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl, +static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, struct net *net, int ifindex) { struct neigh_parms *p; @@ -1337,7 +1337,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct net *net = dev_net(dev); const struct net_device_ops *ops = dev->netdev_ops; - ref = lookup_neigh_params(tbl, net, 0); + ref = lookup_neigh_parms(tbl, net, 0); if (!ref) return NULL; @@ -1906,7 +1906,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) if (tbp[NDTPA_IFINDEX]) ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); - p = lookup_neigh_params(tbl, net, ifindex); + p = lookup_neigh_parms(tbl, net, ifindex); if (p == NULL) { err = -ENOENT; goto errout_tbl_lock; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index b7292a2719dc..ddd2cd2b1775 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -6,6 +6,8 @@ #include <linux/delay.h> #include <linux/sched.h> #include <linux/idr.h> +#include <linux/rculist.h> +#include <linux/nsproxy.h> #include <net/net_namespace.h> #include <net/netns/generic.h> @@ -127,7 +129,7 @@ static struct net *net_create(void) rv = setup_net(net); if (rv == 0) { rtnl_lock(); - list_add_tail(&net->list, &net_namespace_list); + list_add_tail_rcu(&net->list, &net_namespace_list); rtnl_unlock(); } mutex_unlock(&net_mutex); @@ -156,9 +158,16 @@ static void cleanup_net(struct work_struct *work) /* Don't let anyone else find us. */ rtnl_lock(); - list_del(&net->list); + list_del_rcu(&net->list); rtnl_unlock(); + /* + * Another CPU might be rcu-iterating the list, wait for it. + * This needs to be before calling the exit() notifiers, so + * the rcu_barrier() below isn't sufficient alone. + */ + synchronize_rcu(); + /* Run all of the network namespace exit methods */ list_for_each_entry_reverse(ops, &pernet_list, list) { if (ops->exit) @@ -193,6 +202,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) } #endif +struct net *get_net_ns_by_pid(pid_t pid) +{ + struct task_struct *tsk; + struct net *net; + + /* Lookup the network namespace */ + net = ERR_PTR(-ESRCH); + rcu_read_lock(); + tsk = find_task_by_vpid(pid); + if (tsk) { + struct nsproxy *nsproxy; + nsproxy = task_nsproxy(tsk); + if (nsproxy) + net = get_net(nsproxy->net_ns); + } + rcu_read_unlock(); + return net; +} +EXPORT_SYMBOL_GPL(get_net_ns_by_pid); + static int __init net_ns_init(void) { struct net_generic *ng; @@ -219,7 +248,7 @@ static int __init net_ns_init(void) panic("Could not setup the initial network namespace"); rtnl_lock(); - list_add_tail(&init_net.list, &net_namespace_list); + list_add_tail_rcu(&init_net.list, &net_namespace_list); rtnl_unlock(); mutex_unlock(&net_mutex); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index df30feb2fc72..0ac309154b0d 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -9,6 +9,7 @@ * Copyright (C) 2002 Red Hat, Inc. */ +#include <linux/moduleparam.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/string.h> @@ -50,6 +51,9 @@ static atomic_t trapped; static void zap_completion_queue(void); static void arp_reply(struct sk_buff *skb); +static unsigned int carrier_timeout = 4; +module_param(carrier_timeout, uint, 0644); + static void queue_process(struct work_struct *work) { struct netpoll_info *npinfo = @@ -732,7 +736,7 @@ int netpoll_setup(struct netpoll *np) } atleast = jiffies + HZ/10; - atmost = jiffies + 4*HZ; + atmost = jiffies + carrier_timeout * HZ; while (!netif_carrier_ok(ndev)) { if (time_after(jiffies, atmost)) { printk(KERN_NOTICE diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d78030f88bd0..b44775f9f2bf 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -35,7 +35,6 @@ #include <linux/security.h> #include <linux/mutex.h> #include <linux/if_addr.h> -#include <linux/nsproxy.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -52,6 +51,7 @@ #include <net/pkt_sched.h> #include <net/fib_rules.h> #include <net/rtnetlink.h> +#include <net/net_namespace.h> struct rtnl_link { @@ -725,25 +725,6 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { [IFLA_INFO_DATA] = { .type = NLA_NESTED }, }; -static struct net *get_net_ns_by_pid(pid_t pid) -{ - struct task_struct *tsk; - struct net *net; - - /* Lookup the network namespace */ - net = ERR_PTR(-ESRCH); - rcu_read_lock(); - tsk = find_task_by_vpid(pid); - if (tsk) { - struct nsproxy *nsproxy; - nsproxy = task_nsproxy(tsk); - if (nsproxy) - net = get_net(nsproxy->net_ns); - } - rcu_read_unlock(); - return net; -} - static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[]) { if (dev) { diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 1d6ca8a98dc6..9383d3e5a1ab 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -774,7 +774,7 @@ static int dn_rt_bug(struct sk_buff *skb) kfree_skb(skb); - return NET_RX_BAD; + return NET_RX_DROP; } static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 2e1f836d4240..e114da7ca9b8 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -1072,7 +1072,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet skb->protocol = htons(ETH_P_IP); skb_pull(skb, sizeof(struct ec_framehdr)); netif_rx(skb); - return 0; + return NET_RX_SUCCESS; } sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net); @@ -1083,7 +1083,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet hdr->port)) goto drop; - return 0; + return NET_RX_SUCCESS; drop: kfree_skb(skb); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 566ea6c4321d..197d024b2536 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1187,6 +1187,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) int proto; int ihl; int id; + unsigned int offset = 0; if (!(features & NETIF_F_V4_CSUM)) features &= ~NETIF_F_SG; @@ -1229,7 +1230,14 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) skb = segs; do { iph = ip_hdr(skb); - iph->id = htons(id++); + if (proto == IPPROTO_UDP) { + iph->id = htons(id); + iph->frag_off = htons(offset >> 3); + if (skb->next != NULL) + iph->frag_off |= htons(IP_MF); + offset += (skb->len - skb->mac_len - iph->ihl * 4); + } else + iph->id = htons(id++); iph->tot_len = htons(skb->len - skb->mac_len); iph->check = 0; iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); @@ -1425,6 +1433,8 @@ static struct net_protocol tcp_protocol = { static struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = udp_err, + .gso_send_check = udp4_ufo_send_check, + .gso_segment = udp4_ufo_fragment, .no_policy = 1, .netns_ok = 1, }; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index cb4a0f4bd5e5..b902ef55be7f 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -821,7 +821,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -889,7 +889,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -898,7 +898,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static int ipgre_tunnel_bind_dev(struct net_device *dev) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 93e2b787da20..98075b6d619c 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -486,7 +486,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -524,7 +524,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -532,7 +532,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static void ipip_tunnel_bind_dev(struct net_device *dev) diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 9a8da5ed92b7..06c33fb6b321 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -212,7 +212,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) IGMPMSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops reg_vif_netdev_ops = { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 80e3812837ad..7bc2d082a49e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1816,6 +1816,67 @@ void __init udp_init(void) sysctl_udp_wmem_min = SK_MEM_QUANTUM; } +int udp4_ufo_send_check(struct sk_buff *skb) +{ + const struct iphdr *iph; + struct udphdr *uh; + + if (!pskb_may_pull(skb, sizeof(*uh))) + return -EINVAL; + + iph = ip_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + return 0; +} + +struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + int offset; + __wsum csum; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* 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) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot + * do checksum of UDP packets sent as multiple IP fragments. + */ + offset = skb->csum_start - skb_headroom(skb); + csum = skb_checksum(skb, offset, skb->len- offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + /* Fragment the skb. IP headers of the fragments are updated in + * inet_gso_segment() + */ + segs = skb_segment(skb, features); +out: + return segs; +} + EXPORT_SYMBOL(udp_disconnect); EXPORT_SYMBOL(udp_ioctl); EXPORT_SYMBOL(udp_prot); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index caa0278d30a9..bf85d5f97032 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -772,6 +772,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) struct sk_buff *segs = ERR_PTR(-EINVAL); struct ipv6hdr *ipv6h; struct inet6_protocol *ops; + int proto; + struct frag_hdr *fptr; + unsigned int unfrag_ip6hlen; + u8 *prevhdr; + int offset = 0; if (!(features & NETIF_F_V6_CSUM)) features &= ~NETIF_F_SG; @@ -791,10 +796,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) __skb_pull(skb, sizeof(*ipv6h)); segs = ERR_PTR(-EPROTONOSUPPORT); + proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); rcu_read_lock(); - ops = rcu_dereference(inet6_protos[ - ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); - + ops = rcu_dereference(inet6_protos[proto]); if (likely(ops && ops->gso_segment)) { skb_reset_transport_header(skb); segs = ops->gso_segment(skb, features); @@ -808,6 +812,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) ipv6h = ipv6_hdr(skb); ipv6h->payload_len = htons(skb->len - skb->mac_len - sizeof(*ipv6h)); + if (proto == IPPROTO_UDP) { + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + fptr = (struct frag_hdr *)(skb_network_header(skb) + + unfrag_ip6hlen); + fptr->frag_off = htons(offset); + if (skb->next != NULL) + fptr->frag_off |= htons(IP6_MF); + offset += (ntohs(ipv6h->payload_len) - + sizeof(struct frag_hdr)); + } } out: diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 6d6a4277c677..2d9cbaa67edb 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -63,7 +63,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); - return 0; + return NET_RX_DROP; } rcu_read_lock(); @@ -133,7 +133,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (ipv6_parse_hopopts(skb) < 0) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); rcu_read_unlock(); - return 0; + return NET_RX_DROP; } } @@ -149,7 +149,7 @@ err: drop: rcu_read_unlock(); kfree_skb(skb); - return 0; + return NET_RX_DROP; } /* diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 87f8419a68fd..93beee944657 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -57,18 +57,6 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); -static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) -{ - static u32 ipv6_fragmentation_id = 1; - static DEFINE_SPINLOCK(ip6_id_lock); - - spin_lock_bh(&ip6_id_lock); - fhdr->identification = htonl(ipv6_fragmentation_id); - if (++ipv6_fragmentation_id == 0) - ipv6_fragmentation_id = 1; - spin_unlock_bh(&ip6_id_lock); -} - int __ip6_local_out(struct sk_buff *skb) { int len; @@ -706,7 +694,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); - ipv6_select_ident(skb, fh); + ipv6_select_ident(fh); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); @@ -844,7 +832,7 @@ slow_path: fh->nexthdr = nexthdr; fh->reserved = 0; if (!frag_id) { - ipv6_select_ident(skb, fh); + ipv6_select_ident(fh); frag_id = fh->identification; } else fh->identification = frag_id; @@ -1087,11 +1075,13 @@ static inline int ip6_ufo_append_data(struct sock *sk, if (!err) { struct frag_hdr fhdr; - /* specify the length of each IP datagram fragment*/ - skb_shinfo(skb)->gso_size = mtu - fragheaderlen - - sizeof(struct frag_hdr); + /* Specify the length of each IPv6 datagram fragment. + * It has to be a multiple of 8. + */ + skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - + sizeof(struct frag_hdr)) & ~7; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - ipv6_select_ident(skb, &fhdr); + ipv6_select_ident(&fhdr); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; __skb_queue_tail(&sk->sk_write_queue, skb); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 51f410e7775a..a1d6045c4694 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1063,14 +1063,14 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_err; t->recursion--; - return 0; + return NETDEV_TX_OK; tx_err: stats->tx_errors++; stats->tx_dropped++; kfree_skb(skb); t->recursion--; - return 0; + return NETDEV_TX_OK; } static void ip6_tnl_set_cap(struct ip6_tnl *t) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index c769f155c698..07ded5075b33 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -427,7 +427,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) MRT6MSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static const struct net_device_ops reg_vif_netdev_ops = { diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 98b7327d0949..d335a306a4db 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -753,7 +753,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_dropped++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } if (skb->sk) skb_set_owner_w(new_skb, skb->sk); @@ -794,7 +794,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) IPTUNNEL_XMIT(); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; tx_error_icmp: dst_link_failure(skb); @@ -802,7 +802,7 @@ tx_error: stats->tx_errors++; dev_kfree_skb(skb); tunnel->recursion--; - return 0; + return NETDEV_TX_OK; } static void ipip6_tunnel_bind_dev(struct net_device *dev) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 33b59bd92c4d..d79fa6724451 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -638,6 +638,47 @@ static void udp_v6_flush_pending_frames(struct sock *sk) } } +/** + * udp6_hwcsum_outgoing - handle outgoing HW checksumming + * @sk: socket we are sending on + * @skb: sk_buff containing the filled-in UDP header + * (checksum field must be zeroed out) + */ +static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, + const struct in6_addr *saddr, + const struct in6_addr *daddr, int len) +{ + unsigned int offset; + struct udphdr *uh = udp_hdr(skb); + __wsum csum = 0; + + if (skb_queue_len(&sk->sk_write_queue) == 1) { + /* Only one fragment on the socket. */ + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + uh->check = ~csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, 0); + } else { + /* + * HW-checksum won't work as there are two or more + * fragments on the socket so that all csums of sk_buffs + * should be together + */ + offset = skb_transport_offset(skb); + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + + skb->ip_summed = CHECKSUM_NONE; + + skb_queue_walk(&sk->sk_write_queue, skb) { + csum = csum_add(csum, skb->csum); + } + + uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, + csum); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + } +} + /* * Sending */ @@ -668,7 +709,11 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (is_udplite) csum = udplite_csum_outgoing(sk, skb); - else + else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ + udp6_hwcsum_outgoing(sk, skb, &fl->fl6_src, &fl->fl6_dst, + up->len); + goto send; + } else csum = udp_csum_outgoing(sk, skb); /* add protocol-dependent pseudo-header */ @@ -677,6 +722,7 @@ static int udp_v6_push_pending_frames(struct sock *sk) if (uh->check == 0) uh->check = CSUM_MANGLED_0; +send: err = ip6_push_pending_frames(sk); out: up->len = 0; @@ -1032,9 +1078,102 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif +static int udp6_ufo_send_check(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct udphdr *uh; + + if (!pskb_may_pull(skb, sizeof(*uh))) + return -EINVAL; + + ipv6h = ipv6_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + return 0; +} + +static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + unsigned int unfrag_ip6hlen, unfrag_len; + struct frag_hdr *fptr; + u8 *mac_start, *prevhdr; + u8 nexthdr; + u8 frag_hdr_sz = sizeof(struct frag_hdr); + int offset; + __wsum csum; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* 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) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot + * do checksum of UDP packets sent as multiple IP fragments. + */ + offset = skb->csum_start - skb_headroom(skb); + csum = skb_checksum(skb, offset, skb->len- offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + /* Check if there is enough headroom to insert fragment header. */ + if ((skb_headroom(skb) < frag_hdr_sz) && + pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) + goto out; + + /* Find the unfragmentable header and shift it left by frag_hdr_sz + * bytes to insert fragment header. + */ + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); + nexthdr = *prevhdr; + *prevhdr = NEXTHDR_FRAGMENT; + unfrag_len = skb_network_header(skb) - skb_mac_header(skb) + + unfrag_ip6hlen; + mac_start = skb_mac_header(skb); + memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len); + + skb->mac_header -= frag_hdr_sz; + skb->network_header -= frag_hdr_sz; + + fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); + fptr->nexthdr = nexthdr; + fptr->reserved = 0; + ipv6_select_ident(fptr); + + /* Fragment the skb. ipv6 header and the remaining fields of the + * fragment header are updated in ipv6_gso_segment() + */ + segs = skb_segment(skb, features); + +out: + return segs; +} + static struct inet6_protocol udpv6_protocol = { .handler = udpv6_rcv, .err_handler = udpv6_err, + .gso_send_check = udp6_ufo_send_check, + .gso_segment = udp6_ufo_fragment, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 724bcf951b80..64230cffcfee 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -177,7 +177,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) /* Did the realloc succeed? */ if (new_skb == NULL) - return 0; + return NETDEV_TX_OK; /* Use the new skb instead */ skb = new_skb; @@ -209,7 +209,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) self->stats.tx_bytes += skb->len; } - return 0; + return NETDEV_TX_OK; } /* diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index 8dd7ed7e7c1f..476b307bd801 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -115,7 +115,7 @@ static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) genlmsg_end(msg, hdr); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); err_out: nlmsg_free(msg); diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index 2ba1bc4f3c3a..bda96d18fd98 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -407,7 +407,7 @@ int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) return lapb->callbacks.data_indication(lapb->dev, skb); kfree_skb(skb); - return NET_RX_CN_HIGH; /* For now; must be != NET_RX_DROP */ + return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */ } int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index ba2643a43c73..41a32cd919af 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -14,22 +14,6 @@ config MAC80211 comment "CFG80211 needs to be enabled for MAC80211" depends on CFG80211=n -config MAC80211_DEFAULT_PS - bool "enable powersave by default" - depends on MAC80211 - default y - help - This option enables powersave mode by default. - - If this causes your applications to misbehave you should fix your - applications instead -- they need to register their network - latency requirement, see Documentation/power/pm_qos_interface.txt. - -config MAC80211_DEFAULT_PS_VALUE - int - default 1 if MAC80211_DEFAULT_PS - default 0 - menu "Rate control algorithm selection" depends on MAC80211 != n diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3f47276caeb8..36f8f245fa4c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -74,19 +74,14 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) return 0; } -static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, +static int ieee80211_change_iface(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct net_device *dev; struct ieee80211_sub_if_data *sdata; int ret; - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - if (!nl80211_type_check(type)) return -EINVAL; @@ -1177,123 +1172,29 @@ static int ieee80211_scan(struct wiphy *wiphy, static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_auth_request *req) { - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - switch (req->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY; - break; - case NL80211_AUTHTYPE_FT: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP; - break; - default: - return -EOPNOTSUPP; - } - - memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN); - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; - - /* TODO: req->chan */ - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; - - if (req->ssid) { - sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; - } - - kfree(sdata->u.mgd.sme_auth_ie); - sdata->u.mgd.sme_auth_ie = NULL; - sdata->u.mgd.sme_auth_ie_len = 0; - if (req->ie) { - sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL); - if (sdata->u.mgd.sme_auth_ie == NULL) - return -ENOMEM; - memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len); - sdata->u.mgd.sme_auth_ie_len = req->ie_len; - } - - sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; - sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE; - ieee80211_sta_req_auth(sdata); - return 0; + return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_assoc_request *req) { - struct ieee80211_sub_if_data *sdata; - int ret; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 || - !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED)) - return -ENOLINK; /* not authenticated */ - - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET; - - /* TODO: req->chan */ - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL; - - if (req->ssid) { - sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET; - memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len); - sdata->u.mgd.ssid_len = req->ssid_len; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; - } else - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; - - ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len); - if (ret && ret != -EALREADY) - return ret; - - if (req->use_mfp) { - sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; - sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED; - } else { - sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; - sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; - } - - if (req->control_port) - sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - - sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME; - sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE; - ieee80211_sta_req_auth(sdata); - return 0; + return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req); } static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_deauth_request *req) + struct cfg80211_deauth_request *req, + void *cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - /* TODO: req->ie, req->peer_addr */ - return ieee80211_sta_deauthenticate(sdata, req->reason_code); + return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_disassoc_request *req) + struct cfg80211_disassoc_request *req, + void *cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - /* TODO: req->ie, req->peer_addr */ - return ieee80211_sta_disassociate(sdata, req->reason_code); + return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), + req, cookie); } static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, @@ -1374,6 +1275,16 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) return 0; } +static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev, + u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN); + + return 0; +} + static void ieee80211_rfkill_poll(struct wiphy *wiphy) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -1381,6 +1292,85 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy) drv_rfkill_poll(local); } +#ifdef CONFIG_NL80211_TESTMODE +int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + + if (!local->ops->testmode_cmd) + return -EOPNOTSUPP; + + return local->ops->testmode_cmd(&local->hw, data, len); +} +#endif + +static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) + return -EOPNOTSUPP; + + if (enabled == sdata->u.mgd.powersave && + timeout == conf->dynamic_ps_timeout) + return 0; + + sdata->u.mgd.powersave = enabled; + conf->dynamic_ps_timeout = timeout; + + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + + ieee80211_recalc_ps(local, -1); + + return 0; +} + +static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, + const struct cfg80211_bitrate_mask *mask) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int i, err = -EINVAL; + u32 target_rate; + struct ieee80211_supported_band *sband; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + + /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates + * target_rate = X, rate->fixed = 1 means only rate X + * target_rate = X, rate->fixed = 0 means all rates <= X */ + sdata->max_ratectrl_rateidx = -1; + sdata->force_unicast_rateidx = -1; + + if (mask->fixed) + target_rate = mask->fixed / 100; + else if (mask->maxrate) + target_rate = mask->maxrate / 100; + else + return 0; + + for (i=0; i< sband->n_bitrates; i++) { + struct ieee80211_rate *brate = &sband->bitrates[i]; + int this_rate = brate->bitrate; + + if (target_rate == this_rate) { + sdata->max_ratectrl_rateidx = i; + if (mask->fixed) + sdata->force_unicast_rateidx = i; + err = 0; + break; + } + } + + return err; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1422,5 +1412,9 @@ struct cfg80211_ops mac80211_config_ops = { .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, + .set_wds_peer = ieee80211_set_wds_peer, .rfkill_poll = ieee80211_rfkill_poll, + CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd) + .set_power_mgmt = ieee80211_set_power_mgmt, + .set_bitrate_mask = ieee80211_set_bitrate_mask, }; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index e3420329f4e6..e9ec6cae2d39 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -95,33 +95,9 @@ IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC); IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC); /* STA attributes */ -IEEE80211_IF_FILE(state, u.mgd.state, DEC); IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); -IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC); -IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE); IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); -IEEE80211_IF_FILE(ap_capab, u.mgd.ap_capab, HEX); IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); -IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE); -IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC); -IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC); -IEEE80211_IF_FILE(auth_algs, u.mgd.auth_algs, HEX); -IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC); -IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC); - -static ssize_t ieee80211_if_fmt_flags( - const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) -{ - return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n", - sdata->u.mgd.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "", - sdata->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "", - sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : ""); -} -__IEEE80211_IF_FILE(flags); /* AP attributes */ IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); @@ -184,20 +160,9 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(force_unicast_rateidx, sta); DEBUGFS_ADD(max_ratectrl_rateidx, sta); - DEBUGFS_ADD(state, sta); DEBUGFS_ADD(bssid, sta); - DEBUGFS_ADD(prev_bssid, sta); - DEBUGFS_ADD(ssid_len, sta); DEBUGFS_ADD(aid, sta); - DEBUGFS_ADD(ap_capab, sta); DEBUGFS_ADD(capab, sta); - DEBUGFS_ADD(extra_ie_len, sta); - DEBUGFS_ADD(auth_tries, sta); - DEBUGFS_ADD(assoc_tries, sta); - DEBUGFS_ADD(auth_algs, sta); - DEBUGFS_ADD(auth_alg, sta); - DEBUGFS_ADD(auth_transaction, sta); - DEBUGFS_ADD(flags, sta); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) @@ -317,20 +282,9 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_DEL(force_unicast_rateidx, sta); DEBUGFS_DEL(max_ratectrl_rateidx, sta); - DEBUGFS_DEL(state, sta); DEBUGFS_DEL(bssid, sta); - DEBUGFS_DEL(prev_bssid, sta); - DEBUGFS_DEL(ssid_len, sta); DEBUGFS_DEL(aid, sta); - DEBUGFS_DEL(ap_capab, sta); DEBUGFS_DEL(capab, sta); - DEBUGFS_DEL(extra_ie_len, sta); - DEBUGFS_DEL(auth_tries, sta); - DEBUGFS_DEL(assoc_tries, sta); - DEBUGFS_DEL(auth_algs, sta); - DEBUGFS_DEL(auth_alg, sta); - DEBUGFS_DEL(auth_transaction, sta); - DEBUGFS_DEL(flags, sta); } static void del_ap_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 90230c718b5b..33a2e892115b 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -120,45 +120,38 @@ STA_OPS(last_seq_ctrl); static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[768], *p = buf; + char buf[30 + STA_TID_NUM * 70], *p = buf; int i; struct sta_info *sta = file->private_data; - p += scnprintf(p, sizeof(buf)+buf-p, "Agg state for STA is:\n"); - p += scnprintf(p, sizeof(buf)+buf-p, " STA next dialog_token is %d \n " - "TIDs info is: \n TID :", - (sta->ampdu_mlme.dialog_token_allocator + 1)); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", i); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n RX :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_rx[i]); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_rx[i] ? - sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i]); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i] ? - sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); - - p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :"); - for (i = 0; i < STA_TID_NUM; i++) - p += scnprintf(p, sizeof(buf)+buf-p, "%5d", - sta->ampdu_mlme.tid_state_tx[i] ? - sta->ampdu_mlme.tid_tx[i]->ssn : 0); - p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + spin_lock_bh(&sta->lock); + p += scnprintf(p, sizeof(buf)+buf-p, "next dialog_token is %#02x\n", + sta->ampdu_mlme.dialog_token_allocator + 1); + for (i = 0; i < STA_TID_NUM; i++) { + p += scnprintf(p, sizeof(buf)+buf-p, "TID %02d:", i); + p += scnprintf(p, sizeof(buf)+buf-p, " RX=%x", + sta->ampdu_mlme.tid_state_rx[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x", + sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x", + sta->ampdu_mlme.tid_state_rx[i] ? + sta->ampdu_mlme.tid_rx[i]->ssn : 0); + + p += scnprintf(p, sizeof(buf)+buf-p, " TX=%x", + sta->ampdu_mlme.tid_state_tx[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x", + sta->ampdu_mlme.tid_state_tx[i] ? + sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x", + sta->ampdu_mlme.tid_state_tx[i] ? + sta->ampdu_mlme.tid_tx[i]->ssn : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "/pending=%03d", + sta->ampdu_mlme.tid_state_tx[i] ? + skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); + p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + } + spin_unlock_bh(&sta->lock); return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); } @@ -203,6 +196,22 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(inactive_ms); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); + DEBUGFS_ADD(dev); + DEBUGFS_ADD(rx_packets); + DEBUGFS_ADD(tx_packets); + DEBUGFS_ADD(rx_bytes); + DEBUGFS_ADD(tx_bytes); + DEBUGFS_ADD(rx_duplicates); + DEBUGFS_ADD(rx_fragments); + DEBUGFS_ADD(rx_dropped); + DEBUGFS_ADD(tx_fragments); + DEBUGFS_ADD(tx_filtered); + DEBUGFS_ADD(tx_retry_failed); + DEBUGFS_ADD(tx_retry_count); + DEBUGFS_ADD(last_signal); + DEBUGFS_ADD(last_qual); + DEBUGFS_ADD(last_noise); + DEBUGFS_ADD(wep_weak_iv_count); } void ieee80211_sta_debugfs_remove(struct sta_info *sta) @@ -212,6 +221,23 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta) DEBUGFS_DEL(inactive_ms); DEBUGFS_DEL(last_seq_ctrl); DEBUGFS_DEL(agg_status); + DEBUGFS_DEL(aid); + DEBUGFS_DEL(dev); + DEBUGFS_DEL(rx_packets); + DEBUGFS_DEL(tx_packets); + DEBUGFS_DEL(rx_bytes); + DEBUGFS_DEL(tx_bytes); + DEBUGFS_DEL(rx_duplicates); + DEBUGFS_DEL(rx_fragments); + DEBUGFS_DEL(rx_dropped); + DEBUGFS_DEL(tx_fragments); + DEBUGFS_DEL(tx_filtered); + DEBUGFS_DEL(tx_retry_failed); + DEBUGFS_DEL(tx_retry_count); + DEBUGFS_DEL(last_signal); + DEBUGFS_DEL(last_qual); + DEBUGFS_DEL(last_noise); + DEBUGFS_DEL(wep_weak_iv_count); debugfs_remove(sta->debugfs.dir); sta->debugfs.dir = NULL; diff --git a/net/mac80211/event.c b/net/mac80211/event.c index f288d01a6344..01ae759518f6 100644 --- a/net/mac80211/event.c +++ b/net/mac80211/event.c @@ -7,8 +7,7 @@ * * mac80211 - events */ - -#include <net/iw_handler.h> +#include <net/cfg80211.h> #include "ieee80211_i.h" /* @@ -17,26 +16,12 @@ * driver or is still in the frame), it should provide that information. */ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, - struct ieee80211_hdr *hdr, const u8 *tsc) + struct ieee80211_hdr *hdr, const u8 *tsc, + gfp_t gfp) { - union iwreq_data wrqu; - char *buf = kmalloc(128, GFP_ATOMIC); - - if (buf) { - /* TODO: needed parameters: count, key type, TSC */ - sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" - "keyid=%d %scast addr=%pM)", - keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", - hdr->addr2); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = strlen(buf); - wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); - kfree(buf); - } - cfg80211_michael_mic_failure(sdata->dev, hdr->addr2, (hdr->addr1[0] & 0x01) ? NL80211_KEYTYPE_GROUP : NL80211_KEYTYPE_PAIRWISE, - keyidx, tsc); + keyidx, tsc, gfp); } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 0b30277eb366..15d5a53b59a8 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -705,7 +705,7 @@ static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; u16 fc; - rx_status = (struct ieee80211_rx_status *) skb->cb; + rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); @@ -836,8 +836,7 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) } ieee80211_rx_result -ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; @@ -852,7 +851,6 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_AUTH: skb_queue_tail(&sdata->u.ibss.skb_queue, skb); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 68eb5052179a..327aabc07abe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -227,35 +227,44 @@ struct mesh_preq_queue { u8 flags; }; +enum ieee80211_mgd_state { + IEEE80211_MGD_STATE_IDLE, + IEEE80211_MGD_STATE_PROBE, + IEEE80211_MGD_STATE_AUTH, + IEEE80211_MGD_STATE_ASSOC, +}; + +struct ieee80211_mgd_work { + struct list_head list; + struct ieee80211_bss *bss; + int ie_len; + u8 prev_bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + unsigned long timeout; + enum ieee80211_mgd_state state; + u16 auth_alg, auth_transaction; + + int tries; + + /* must be last */ + u8 ie[0]; /* for auth or assoc frame, not probe */ +}; + /* flags used in struct ieee80211_if_managed.flags */ -#define IEEE80211_STA_SSID_SET BIT(0) -#define IEEE80211_STA_BSSID_SET BIT(1) -#define IEEE80211_STA_PREV_BSSID_SET BIT(2) -#define IEEE80211_STA_AUTHENTICATED BIT(3) -#define IEEE80211_STA_ASSOCIATED BIT(4) -#define IEEE80211_STA_PROBEREQ_POLL BIT(5) -#define IEEE80211_STA_CREATE_IBSS BIT(6) -#define IEEE80211_STA_CONTROL_PORT BIT(7) -#define IEEE80211_STA_WMM_ENABLED BIT(8) -/* hole at 9, please re-use */ -#define IEEE80211_STA_AUTO_SSID_SEL BIT(10) -#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11) -#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) -#define IEEE80211_STA_PRIVACY_INVOKED BIT(13) -#define IEEE80211_STA_TKIP_WEP_USED BIT(14) -#define IEEE80211_STA_CSA_RECEIVED BIT(15) -#define IEEE80211_STA_MFP_ENABLED BIT(16) -#define IEEE80211_STA_EXT_SME BIT(17) -/* flags for MLME request */ -#define IEEE80211_STA_REQ_SCAN 0 -#define IEEE80211_STA_REQ_AUTH 1 -#define IEEE80211_STA_REQ_RUN 2 +enum ieee80211_sta_flags { + IEEE80211_STA_PROBEREQ_POLL = BIT(3), + IEEE80211_STA_CONTROL_PORT = BIT(4), + IEEE80211_STA_WMM_ENABLED = BIT(5), + IEEE80211_STA_DISABLE_11N = BIT(6), + IEEE80211_STA_CSA_RECEIVED = BIT(7), + IEEE80211_STA_MFP_ENABLED = BIT(8), +}; -/* bitfield of allowed auth algs */ -#define IEEE80211_AUTH_ALG_OPEN BIT(0) -#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) -#define IEEE80211_AUTH_ALG_LEAP BIT(2) -#define IEEE80211_AUTH_ALG_FT BIT(3) +/* flags for MLME request */ +enum ieee80211_sta_request { + IEEE80211_STA_REQ_SCAN, +}; struct ieee80211_if_managed { struct timer_list timer; @@ -264,49 +273,26 @@ struct ieee80211_if_managed { struct work_struct chswitch_work; struct work_struct beacon_loss_work; - u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; - - u8 ssid[IEEE80211_MAX_SSID_LEN]; - size_t ssid_len; + struct mutex mtx; + struct ieee80211_bss *associated; + struct list_head work_list; - enum { - IEEE80211_STA_MLME_DISABLED, - IEEE80211_STA_MLME_DIRECT_PROBE, - IEEE80211_STA_MLME_AUTHENTICATE, - IEEE80211_STA_MLME_ASSOCIATE, - IEEE80211_STA_MLME_ASSOCIATED, - } state; + u8 bssid[ETH_ALEN]; u16 aid; - u16 ap_capab, capab; - u8 *extra_ie; /* to be added to the end of AssocReq */ - size_t extra_ie_len; - - /* The last AssocReq/Resp IEs */ - u8 *assocreq_ies, *assocresp_ies; - size_t assocreq_ies_len, assocresp_ies_len; + u16 capab; struct sk_buff_head skb_queue; - int assoc_scan_tries; /* number of scans done pre-association */ - int direct_probe_tries; /* retries for direct probes */ - int auth_tries; /* retries for auth req */ - int assoc_tries; /* retries for assoc req */ - unsigned long timers_running; /* used for quiesce/restart */ bool powersave; /* powersave requested for this iface */ unsigned long request; - unsigned long last_probe; unsigned long last_beacon; unsigned int flags; - unsigned int auth_algs; /* bitfield of allowed auth algs */ - int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ - int auth_transaction; - u32 beacon_crc; enum { @@ -316,10 +302,6 @@ struct ieee80211_if_managed { } mfp; /* management frame protection */ int wmm_last_param_set; - - /* Extra IE data for management frames */ - u8 *sme_auth_ie; - size_t sme_auth_ie_len; }; enum ieee80211_ibss_request { @@ -478,20 +460,9 @@ struct ieee80211_sub_if_data { union { struct { struct dentry *drop_unencrypted; - struct dentry *state; struct dentry *bssid; - struct dentry *prev_bssid; - struct dentry *ssid_len; struct dentry *aid; - struct dentry *ap_capab; struct dentry *capab; - struct dentry *extra_ie_len; - struct dentry *auth_tries; - struct dentry *assoc_tries; - struct dentry *auth_algs; - struct dentry *auth_alg; - struct dentry *auth_transaction; - struct dentry *flags; struct dentry *force_unicast_rateidx; struct dentry *max_ratectrl_rateidx; } sta; @@ -942,16 +913,18 @@ extern const struct iw_handler_def ieee80211_iw_handler_def; /* STA code */ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata); +int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_auth_request *req); +int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_assoc_request *req); +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_deauth_request *req, + void *cookie); +int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_disassoc_request *req, + void *cookie); ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); -int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata); -int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len); -int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len); -int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid); -void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata); -int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason); -int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason); + struct sk_buff *skb); void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency); @@ -967,8 +940,7 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result -ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); +ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 *addr, u32 supp_rates); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, @@ -983,16 +955,9 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); -int ieee80211_scan_results(struct ieee80211_local *local, - struct iw_request_info *info, - char *buf, size_t len); void ieee80211_scan_cancel(struct ieee80211_local *local); ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - const char *ie, size_t len); +ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); struct ieee80211_bss * @@ -1008,8 +973,6 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, u8 *ssid, u8 ssid_len); void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); -void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid, - int freq, u8 *ssid, u8 ssid_len); /* interface handling */ int ieee80211_if_add(struct ieee80211_local *local, const char *name, @@ -1092,7 +1055,8 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, int rate, int erp, int short_preamble); void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx, - struct ieee80211_hdr *hdr, const u8 *tsc); + struct ieee80211_hdr *hdr, const u8 *tsc, + gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int encrypt); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b7c8a4484298..4839a2d97a3b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -233,9 +233,6 @@ static int ieee80211_open(struct net_device *dev) ieee80211_configure_filter(local); netif_addr_unlock_bh(local->mdev); break; - case NL80211_IFTYPE_STATION: - sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET; - /* fall through */ default: conf.vif = &sdata->vif; conf.type = sdata->vif.type; @@ -366,18 +363,6 @@ static int ieee80211_stop(struct net_device *dev) rcu_read_unlock(); /* - * Announce that we are leaving the network, in case we are a - * station interface type. This must be done before removing - * all stations associated with sta_info_flush, otherwise STA - * information will be gone and no announce being done. - */ - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.state != IEEE80211_STA_MLME_DISABLED) - ieee80211_sta_deauthenticate(sdata, - WLAN_REASON_DEAUTH_LEAVING); - } - - /* * Remove all stations associated with this interface. * * This must be done before calling ops->remove_interface() @@ -462,7 +447,6 @@ static int ieee80211_stop(struct net_device *dev) netif_addr_unlock_bh(local->mdev); break; case NL80211_IFTYPE_STATION: - memset(sdata->u.mgd.bssid, 0, ETH_ALEN); del_timer_sync(&sdata->u.mgd.chswitch_timer); del_timer_sync(&sdata->u.mgd.timer); /* @@ -485,12 +469,6 @@ static int ieee80211_stop(struct net_device *dev) */ synchronize_rcu(); skb_queue_purge(&sdata->u.mgd.skb_queue); - - sdata->u.mgd.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED | - IEEE80211_STA_TKIP_WEP_USED); - kfree(sdata->u.mgd.extra_ie); - sdata->u.mgd.extra_ie = NULL; - sdata->u.mgd.extra_ie_len = 0; /* fall through */ case NL80211_IFTYPE_ADHOC: if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { @@ -652,11 +630,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree_skb(sdata->u.ibss.presp); break; case NL80211_IFTYPE_STATION: - kfree(sdata->u.mgd.extra_ie); - kfree(sdata->u.mgd.assocreq_ies); - kfree(sdata->u.mgd.assocresp_ies); - kfree(sdata->u.mgd.sme_auth_ie); - break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: @@ -939,7 +912,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) continue; /* do not count disabled managed interfaces */ if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.state == IEEE80211_STA_MLME_DISABLED) + !sdata->u.mgd.associated && + list_empty(&sdata->u.mgd.work_list)) continue; /* do not count unused IBSS interfaces */ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ce267565e180..659a42d529e3 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -67,6 +67,8 @@ static DECLARE_WORK(todo_work, key_todo); * * @key: key to add to do item for * @flag: todo flag(s) + * + * Must be called with IRQs or softirqs disabled. */ static void add_todo(struct ieee80211_key *key, u32 flag) { @@ -140,9 +142,9 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf); if (!ret) { - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) @@ -164,12 +166,12 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) if (!key || !key->local->ops->set_key) return; - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); return; } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); sta = get_sta_for_key(key); sdata = key->sdata; @@ -188,9 +190,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) wiphy_name(key->local->hw.wiphy), key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, @@ -437,14 +439,14 @@ void ieee80211_key_link(struct ieee80211_key *key, __ieee80211_key_replace(sdata, sta, old_key, key); - spin_unlock_irqrestore(&sdata->local->key_lock, flags); - /* free old key later */ add_todo(old_key, KEY_FLAG_TODO_DELETE); add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); if (netif_running(sdata->dev)) add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD); + + spin_unlock_irqrestore(&sdata->local->key_lock, flags); } static void __ieee80211_key_free(struct ieee80211_key *key) @@ -547,7 +549,7 @@ static void __ieee80211_key_todo(void) */ synchronize_rcu(); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); while (!list_empty(&todo_list)) { key = list_first_entry(&todo_list, struct ieee80211_key, todo); list_del_init(&key->todo); @@ -558,7 +560,7 @@ static void __ieee80211_key_todo(void) KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); key->flags &= ~todoflags; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); work_done = false; @@ -591,9 +593,9 @@ static void __ieee80211_key_todo(void) WARN_ON(!work_done); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } void ieee80211_key_todo(void) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 092a017b237e..5b69f5f07299 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -330,19 +330,16 @@ static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sk_buff *skb; - struct ieee80211_rx_status rx_status; struct ieee80211_ra_tid *ra_tid; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { switch (skb->pkt_type) { case IEEE80211_RX_MSG: - /* status is in skb->cb */ - memcpy(&rx_status, skb->cb, sizeof(rx_status)); /* Clear skb->pkt_type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; - __ieee80211_rx(local_to_hw(local), skb, &rx_status); + ieee80211_rx(local_to_hw(local), skb); break; case IEEE80211_TX_STATUS_MSG: skb->pkt_type = 0; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 11cf45bce38a..542ea025494e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -568,7 +568,7 @@ static void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ifmsh = &sdata->u.mesh; - rx_status = (struct ieee80211_rx_status *) skb->cb; + rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; @@ -671,8 +671,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) } ieee80211_rx_result -ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; @@ -689,7 +688,6 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: case IEEE80211_STYPE_ACTION: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); skb_queue_tail(&ifmsh->skb_queue, skb); queue_work(local->hw.workqueue, &ifmsh->work); return RX_QUEUED; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c7d72819cdd2..2a2ed182cb7e 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -208,8 +208,7 @@ void ieee80211s_init(void); void ieee80211s_stop(void); void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); ieee80211_rx_result -ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status); +ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aca22b00b6a3..c9db9646025b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -27,43 +27,54 @@ #include "rate.h" #include "led.h" -#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2 #define IEEE80211_AUTH_TIMEOUT (HZ / 5) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 #define IEEE80211_MONITORING_INTERVAL (2 * HZ) #define IEEE80211_PROBE_WAIT (HZ / 5) -#define IEEE80211_PROBE_IDLE_TIME (60 * HZ) -#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 +/* + * All cfg80211 functions have to be called outside a locked + * section so that they can acquire a lock themselves... This + * is much simpler than queuing up things in cfg80211, but we + * do need some indirection for that here. + */ +enum rx_mgmt_action { + /* no action required */ + RX_MGMT_NONE, + + /* caller must call cfg80211_send_rx_auth() */ + RX_MGMT_CFG80211_AUTH, + + /* caller must call cfg80211_send_rx_assoc() */ + RX_MGMT_CFG80211_ASSOC, + + /* caller must call cfg80211_send_deauth() */ + RX_MGMT_CFG80211_DEAUTH, + + /* caller must call cfg80211_send_disassoc() */ + RX_MGMT_CFG80211_DISASSOC, + + /* caller must call cfg80211_auth_timeout() & free work */ + RX_MGMT_CFG80211_AUTH_TO, + + /* caller must call cfg80211_assoc_timeout() & free work */ + RX_MGMT_CFG80211_ASSOC_TO, +}; + /* utils */ -static int ecw2cw(int ecw) +static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) { - return (1 << ecw) - 1; + WARN_ON(!mutex_is_locked(&ifmgd->mtx)); } -static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie) +static int ecw2cw(int ecw) { - u8 *end, *pos; - - pos = bss->cbss.information_elements; - if (pos == NULL) - return NULL; - end = pos + bss->cbss.len_information_elements; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return (1 << ecw) - 1; } static int ieee80211_compatible_rates(struct ieee80211_bss *bss, @@ -94,11 +105,10 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss, */ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_ht_info *hti, - u16 ap_ht_cap_flags) + const u8 *bssid, u16 ap_ht_cap_flags) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct sta_info *sta; u32 changed = 0; u16 ht_opmode; @@ -147,12 +157,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, ieee80211_hw_config(local, 0); rcu_read_lock(); - - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (sta) rate_control_rate_update(local, sband, sta, IEEE80211_RC_HT_CHANGED); - rcu_read_unlock(); } @@ -175,23 +183,24 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, /* frame sending functions */ -static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) +static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie; + u8 *pos; + const u8 *ies, *ht_ie; int i, len, count, rates_len, supp_rates_len; u16 capab; - struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u32 rates = 0; skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 200 + ifmgd->extra_ie_len + - ifmgd->ssid_len); + sizeof(*mgmt) + 200 + wk->ie_len + + wk->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -210,45 +219,35 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; } - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY) - capab |= WLAN_CAPABILITY_PRIVACY; - if (bss->wmm_used) - wmm = 1; + if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + if (wk->bss->wmm_used) + wmm = 1; - /* get all rates supported by the device and the AP as - * some APs don't like getting a superset of their rates - * in the association request (e.g. D-Link DAP 1353 in - * b-only mode) */ - rates_len = ieee80211_compatible_rates(bss, sband, &rates); + /* get all rates supported by the device and the AP as + * some APs don't like getting a superset of their rates + * in the association request (e.g. D-Link DAP 1353 in + * b-only mode) */ + rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates); - if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && - (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) - capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - - ieee80211_rx_bss_put(local, bss); - } else { - rates = ~0; - rates_len = sband->n_bitrates; - } + if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && + (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); - memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN); - if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) { + if (!is_zero_ether_addr(wk->prev_bssid)) { skb_put(skb, 10); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); mgmt->u.reassoc_req.listen_interval = cpu_to_le16(local->hw.conf.listen_interval); - memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid, + memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid, ETH_ALEN); } else { skb_put(skb, 4); @@ -260,10 +259,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } /* SSID */ - ies = pos = skb_put(skb, 2 + ifmgd->ssid_len); + ies = pos = skb_put(skb, 2 + wk->ssid_len); *pos++ = WLAN_EID_SSID; - *pos++ = ifmgd->ssid_len; - memcpy(pos, ifmgd->ssid, ifmgd->ssid_len); + *pos++ = wk->ssid_len; + memcpy(pos, wk->ssid, wk->ssid_len); /* add all rates which were marked to be used above */ supp_rates_len = rates_len; @@ -318,9 +317,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } } - if (ifmgd->extra_ie) { - pos = skb_put(skb, ifmgd->extra_ie_len); - memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len); + if (wk->ie_len && wk->ie) { + pos = skb_put(skb, wk->ie_len); + memcpy(pos, wk->ie, wk->ie_len); } if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) { @@ -345,9 +344,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) */ if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && sband->ht_cap.ht_supported && - (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) && + (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) && ht_ie[1] >= sizeof(struct ieee80211_ht_info) && - (!(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))) { + (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) { struct ieee80211_ht_info *ht_info = (struct ieee80211_ht_info *)(ht_ie + 2); u16 cap = sband->ht_cap.cap; @@ -382,18 +381,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } - kfree(ifmgd->assocreq_ies); - ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies; - ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL); - if (ifmgd->assocreq_ies) - memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len); - ieee80211_tx_skb(sdata, skb, 0); } static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, - u16 stype, u16 reason) + const u8 *bssid, u16 stype, u16 reason, + void *cookie) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -410,18 +404,18 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); - memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->da, bssid, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); - memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN); + memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); skb_put(skb, 2); /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); if (stype == IEEE80211_STYPE_DEAUTH) - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie); else - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len); + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie); ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED); } @@ -494,28 +488,26 @@ static void ieee80211_chswitch_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); - struct ieee80211_bss *bss; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (!netif_running(sdata->dev)) return; - bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (!bss) - goto exit; + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) + goto out; sdata->local->oper_channel = sdata->local->csa_channel; + ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); + /* XXX: shouldn't really modify cfg80211-owned data! */ - if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) - bss->cbss.channel = sdata->local->oper_channel; + ifmgd->associated->cbss.channel = sdata->local->oper_channel; - ieee80211_rx_bss_put(sdata->local, bss); -exit: - ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); + out: + ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + mutex_unlock(&ifmgd->mtx); } static void ieee80211_chswitch_timer(unsigned long data) @@ -540,7 +532,9 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); - if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED) + ASSERT_MGD_MTX(ifmgd); + + if (!ifmgd->associated) return; if (sdata->local->sw_scanning || sdata->local->hw_scanning) @@ -651,7 +645,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) } if (count == 1 && found->u.mgd.powersave && - (found->u.mgd.flags & IEEE80211_STA_ASSOCIATED) && + found->u.mgd.associated && list_empty(&found->u.mgd.work_list) && !(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) { s32 beaconint_us; @@ -806,9 +800,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, u16 capab, bool erp_valid, u8 erp) { struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -#endif u32 changed = 0; bool use_protection; bool use_short_preamble; @@ -825,42 +816,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); if (use_protection != bss_conf->use_cts_prot) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n", - sdata->dev->name, - use_protection ? "enabled" : "disabled", - ifmgd->bssid); - } -#endif bss_conf->use_cts_prot = use_protection; changed |= BSS_CHANGED_ERP_CTS_PROT; } if (use_short_preamble != bss_conf->use_short_preamble) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: switched to %s barker preamble" - " (BSSID=%pM)\n", - sdata->dev->name, - use_short_preamble ? "short" : "long", - ifmgd->bssid); - } -#endif bss_conf->use_short_preamble = use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } if (use_short_slot != bss_conf->use_short_slot) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: switched to %s slot time" - " (BSSID=%pM)\n", - sdata->dev->name, - use_short_slot ? "short" : "long", - ifmgd->bssid); - } -#endif bss_conf->use_short_slot = use_short_slot; changed |= BSS_CHANGED_ERP_SLOT; } @@ -868,105 +833,25 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, return changed; } -static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata) -{ - union iwreq_data wrqu; - - memset(&wrqu, 0, sizeof(wrqu)); - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) - memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL); -} - -static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - char *buf; - size_t len; - int i; - union iwreq_data wrqu; - - if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies) - return; - - buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len + - ifmgd->assocresp_ies_len), GFP_KERNEL); - if (!buf) - return; - - len = sprintf(buf, "ASSOCINFO("); - if (ifmgd->assocreq_ies) { - len += sprintf(buf + len, "ReqIEs="); - for (i = 0; i < ifmgd->assocreq_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocreq_ies[i]); - } - } - if (ifmgd->assocresp_ies) { - if (ifmgd->assocreq_ies) - len += sprintf(buf + len, " "); - len += sprintf(buf + len, "RespIEs="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - len += sprintf(buf + len, ")"); - - if (len > IW_CUSTOM_MAX) { - len = sprintf(buf, "ASSOCRESPIE="); - for (i = 0; i < ifmgd->assocresp_ies_len; i++) { - len += sprintf(buf + len, "%02x", - ifmgd->assocresp_ies[i]); - } - } - - if (len <= IW_CUSTOM_MAX) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = len; - wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); - } - - kfree(buf); -} - - static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, + struct ieee80211_bss *bss, u32 bss_info_changed) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - struct ieee80211_conf *conf = &local_to_hw(local)->conf; - - struct ieee80211_bss *bss; bss_info_changed |= BSS_CHANGED_ASSOC; - ifmgd->flags |= IEEE80211_STA_ASSOCIATED; + /* set timing information */ + sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval; + sdata->vif.bss_conf.timestamp = bss->cbss.tsf; + sdata->vif.bss_conf.dtim_period = bss->dtim_period; - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - conf->channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (bss) { - /* set timing information */ - sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval; - sdata->vif.bss_conf.timestamp = bss->cbss.tsf; - sdata->vif.bss_conf.dtim_period = bss->dtim_period; + bss_info_changed |= BSS_CHANGED_BEACON_INT; + bss_info_changed |= ieee80211_handle_bss_capability(sdata, + bss->cbss.capability, bss->has_erp_value, bss->erp_value); - bss_info_changed |= BSS_CHANGED_BEACON_INT; - bss_info_changed |= ieee80211_handle_bss_capability(sdata, - bss->cbss.capability, bss->has_erp_value, bss->erp_value); + sdata->u.mgd.associated = bss; + memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN); - cfg80211_hold_bss(&bss->cbss); - - ieee80211_rx_bss_put(local, bss); - } - - ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET; - memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN); - ieee80211_sta_send_associnfo(sdata); - - ifmgd->last_probe = jiffies; ieee80211_led_assoc(local, 1); sdata->vif.bss_conf.assoc = 1; @@ -991,167 +876,135 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); - - ieee80211_sta_send_apinfo(sdata); } -static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - ifmgd->direct_probe_tries++; - if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid); + sdata->dev->name, wk->bss->cbss.bssid); /* * Most likely AP is not in the range so remove the - * bss information associated to the AP + * bss struct for that AP. */ - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_DIRECT_PROBE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_AUTH_TO; } - printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n", - sdata->dev->name, ifmgd->bssid, - ifmgd->direct_probe_tries); - - ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; + printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, + wk->tries); - /* Direct probe is sent to broadcast address as some APs + /* + * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. */ - ieee80211_send_probe_req(sdata, NULL, - ifmgd->ssid, ifmgd->ssid_len, NULL, 0); + ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0); + + wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); + return RX_MGMT_NONE; } -static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - u8 *ies; - size_t ies_len; - ifmgd->auth_tries++; - if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_AUTH_MAX_TRIES) { printk(KERN_DEBUG "%s: authentication with AP %pM" " timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid); - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + sdata->dev->name, wk->bss->cbss.bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_AUTHENTICATE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_AUTH_TO; } - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - printk(KERN_DEBUG "%s: authenticate with AP %pM\n", - sdata->dev->name, ifmgd->bssid); + printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, wk->tries); - if (ifmgd->flags & IEEE80211_STA_EXT_SME) { - ies = ifmgd->sme_auth_ie; - ies_len = ifmgd->sme_auth_ie_len; - } else { - ies = NULL; - ies_len = 0; - } - ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len, - ifmgd->bssid, 0); - ifmgd->auth_transaction = 2; + ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len, + wk->bss->cbss.bssid, 0); + wk->auth_transaction = 2; - mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT); + wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); + + return RX_MGMT_NONE; } -/* - * The disassoc 'reason' argument can be either our own reason - * if self disconnected or a reason code from the AP. - */ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, - bool deauth, bool self_disconnected, - u16 reason) + const u8 *bssid, bool deauth) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - struct ieee80211_conf *conf = &local_to_hw(local)->conf; - struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = 0, config_changed = 0; - if (deauth) { - ifmgd->direct_probe_tries = 0; - ifmgd->auth_tries = 0; - } - ifmgd->assoc_scan_tries = 0; - ifmgd->assoc_tries = 0; + ASSERT_MGD_MTX(ifmgd); + + ifmgd->associated = NULL; + memset(ifmgd->bssid, 0, ETH_ALEN); + + /* + * we need to commit the associated = NULL change because the + * scan code uses that to determine whether this iface should + * go to/wake up from powersave or not -- and could otherwise + * wake the queues erroneously. + */ + smp_mb(); + + /* + * Thus, we can only afterwards stop the queues -- to account + * for the case where another CPU is finishing a scan at this + * time -- we don't want the scan code to enable queues. + */ netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (sta) ieee80211_sta_tear_down_BA_sessions(sta); rcu_read_unlock(); - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - conf->channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - - if (bss) { - cfg80211_unhold_bss(&bss->cbss); - ieee80211_rx_bss_put(local, bss); - } - - if (self_disconnected) { - if (deauth) - ieee80211_send_deauth_disassoc(sdata, - IEEE80211_STYPE_DEAUTH, reason); - else - ieee80211_send_deauth_disassoc(sdata, - IEEE80211_STYPE_DISASSOC, reason); - } - - ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED; changed |= ieee80211_reset_erp_info(sdata); ieee80211_led_assoc(local, 0); changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; - ieee80211_sta_send_apinfo(sdata); - - if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) { - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - } - ieee80211_set_wmm_default(sdata); ieee80211_recalc_idle(local); @@ -1180,7 +1033,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, bssid); if (!sta) { rcu_read_unlock(); return; @@ -1193,83 +1046,42 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sta_info_destroy(sta); } -static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata) -{ - if (!sdata || !sdata->default_key || - sdata->default_key->conf.alg != ALG_WEP) - return 0; - return 1; -} - -static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct ieee80211_bss *bss; - int bss_privacy; - int wep_privacy; - int privacy_invoked; - - if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME)) - return 0; - - bss = ieee80211_rx_bss_get(local, ifmgd->bssid, - local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); - if (!bss) - return 0; - - bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY); - wep_privacy = !!ieee80211_sta_wep_configured(sdata); - privacy_invoked = !!(ifmgd->flags & IEEE80211_STA_PRIVACY_INVOKED); - - ieee80211_rx_bss_put(local, bss); - - if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked)) - return 0; - - return 1; -} - -static void ieee80211_associate(struct ieee80211_sub_if_data *sdata) +static enum rx_mgmt_action __must_check +ieee80211_associate(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - ifmgd->assoc_tries++; - if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { + wk->tries++; + if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with AP %pM" " timed out\n", - sdata->dev->name, ifmgd->bssid); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid); - ieee80211_rx_bss_remove(sdata, ifmgd->bssid, - sdata->local->hw.conf.channel->center_freq, - ifmgd->ssid, ifmgd->ssid_len); + sdata->dev->name, wk->bss->cbss.bssid); + + /* + * Most likely AP is not in the range so remove the + * bss struct for that AP. + */ + cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss); + /* * We might have a pending scan which had no chance to run yet - * due to state == IEEE80211_STA_MLME_ASSOCIATE. - * Hence, queue the STAs work again + * due to work needing to be done. Hence, queue the STAs work + * again for that. */ queue_work(local->hw.workqueue, &ifmgd->work); - return; + return RX_MGMT_CFG80211_ASSOC_TO; } - ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; - printk(KERN_DEBUG "%s: associate with AP %pM\n", - sdata->dev->name, ifmgd->bssid); - if (ieee80211_privacy_mismatch(sdata)) { - printk(KERN_DEBUG "%s: mismatch in privacy configuration and " - "mixed-cell disabled - abort association\n", sdata->dev->name); - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - return; - } + printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n", + sdata->dev->name, wk->bss->cbss.bssid, wk->tries); + ieee80211_send_assoc(sdata, wk); - ieee80211_send_assoc(sdata); + wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; + mod_timer(&ifmgd->timer, wk->timeout); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); + return RX_MGMT_NONE; } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -1294,6 +1106,7 @@ void ieee80211_beacon_loss_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.beacon_loss_work); struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ssid; /* * The driver has already reported this event and we have @@ -1306,12 +1119,15 @@ void ieee80211_beacon_loss_work(struct work_struct *work) if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) return; + mutex_lock(&ifmgd->mtx); + + if (!ifmgd->associated) + goto out; + #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " - "- sending probe request\n", sdata->dev->name, - sdata->u.mgd.bssid); - } + if (net_ratelimit()) + printk(KERN_DEBUG "%s: driver reports beacon loss from AP " + "- sending probe request\n", sdata->dev->name); #endif ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; @@ -1320,10 +1136,13 @@ void ieee80211_beacon_loss_work(struct work_struct *work) ieee80211_recalc_ps(sdata->local, -1); mutex_unlock(&sdata->local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); + ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID); + ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid, + ssid + 2, ssid[1], NULL, 0); mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT); + out: + mutex_unlock(&ifmgd->mtx); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) @@ -1335,105 +1154,16 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_beacon_loss); -static void ieee80211_associated(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct sta_info *sta; - unsigned long last_rx; - bool disassoc = false; - - /* TODO: start monitoring current AP signal quality and number of - * missed beacons. Scan other channels every now and then and search - * for better APs. */ - /* TODO: remove expired BSSes */ - - ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED; - - rcu_read_lock(); - - sta = sta_info_get(local, ifmgd->bssid); - if (!sta) { - printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n", - sdata->dev->name, ifmgd->bssid); - disassoc = true; - rcu_read_unlock(); - goto out; - } - - last_rx = sta->last_rx; - rcu_read_unlock(); - - if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) && - time_after(jiffies, last_rx + IEEE80211_PROBE_WAIT)) { - printk(KERN_DEBUG "%s: no probe response from AP %pM " - "- disassociating\n", - sdata->dev->name, ifmgd->bssid); - disassoc = true; - ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; - goto out; - } - - /* - * Beacon filtering is only enabled with power save and then the - * stack should not check for beacon loss. - */ - if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) && - (local->hw.conf.flags & IEEE80211_CONF_PS)) && - time_after(jiffies, - ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: beacon loss from AP %pM " - "- sending probe request\n", - sdata->dev->name, ifmgd->bssid); - } -#endif - ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); - mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT); - goto out; - } - - if (time_after(jiffies, last_rx + IEEE80211_PROBE_IDLE_TIME)) { - ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL; - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); - ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid, - ifmgd->ssid_len, NULL, 0); - } - - out: - if (!disassoc) - mod_timer(&ifmgd->timer, - jiffies + IEEE80211_MONITORING_INTERVAL); - else - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_PREV_AUTH_NOT_VALID); -} - - -static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata) +static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - + wk->state = IEEE80211_MGD_STATE_IDLE; printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name); - ifmgd->flags |= IEEE80211_STA_AUTHENTICATED; - if (ifmgd->flags & IEEE80211_STA_EXT_SME) { - /* Wait for SME to request association */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(sdata->local); - } else - ieee80211_associate(sdata); } static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, struct ieee80211_mgmt *mgmt, size_t len) { @@ -1444,161 +1174,132 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (!elems.challenge) return; - ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg, + ieee80211_send_auth(sdata, 3, wk->auth_alg, elems.challenge - 2, elems.challenge_len + 2, - sdata->u.mgd.bssid, 1); - sdata->u.mgd.auth_transaction = 4; + wk->bss->cbss.bssid, 1); + wk->auth_transaction = 4; } -static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 auth_alg, auth_transaction, status_code; - if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE) - return; + if (wk->state != IEEE80211_MGD_STATE_AUTH) + return RX_MGMT_NONE; if (len < 24 + 6) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0) + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0) + return RX_MGMT_NONE; auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); status_code = le16_to_cpu(mgmt->u.auth.status_code); - if (auth_alg != ifmgd->auth_alg || - auth_transaction != ifmgd->auth_transaction) - return; + if (auth_alg != wk->auth_alg || + auth_transaction != wk->auth_transaction) + return RX_MGMT_NONE; if (status_code != WLAN_STATUS_SUCCESS) { - if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - u8 algs[3]; - const int num_algs = ARRAY_SIZE(algs); - int i, pos; - algs[0] = algs[1] = algs[2] = 0xff; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN) - algs[0] = WLAN_AUTH_OPEN; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) - algs[1] = WLAN_AUTH_SHARED_KEY; - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) - algs[2] = WLAN_AUTH_LEAP; - if (ifmgd->auth_alg == WLAN_AUTH_OPEN) - pos = 0; - else if (ifmgd->auth_alg == WLAN_AUTH_SHARED_KEY) - pos = 1; - else - pos = 2; - for (i = 0; i < num_algs; i++) { - pos++; - if (pos >= num_algs) - pos = 0; - if (algs[pos] == ifmgd->auth_alg || - algs[pos] == 0xff) - continue; - if (algs[pos] == WLAN_AUTH_SHARED_KEY && - !ieee80211_sta_wep_configured(sdata)) - continue; - ifmgd->auth_alg = algs[pos]; - break; - } - } - return; + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_AUTH; } - switch (ifmgd->auth_alg) { + switch (wk->auth_alg) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: case WLAN_AUTH_FT: - ieee80211_auth_completed(sdata); - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); - break; + ieee80211_auth_completed(sdata, wk); + return RX_MGMT_CFG80211_AUTH; case WLAN_AUTH_SHARED_KEY: - if (ifmgd->auth_transaction == 4) { - ieee80211_auth_completed(sdata); - cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len); + if (wk->auth_transaction == 4) { + ieee80211_auth_completed(sdata, wk); + return RX_MGMT_CFG80211_AUTH; } else - ieee80211_auth_challenge(sdata, mgmt, len); + ieee80211_auth_challenge(sdata, wk, mgmt, len); break; } + + return RX_MGMT_NONE; } -static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *bssid = NULL; u16 reason_code; if (len < 24 + 2) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN)) - return; + ASSERT_MGD_MTX(ifmgd); + + if (wk) + bssid = wk->bss->cbss.bssid; + else + bssid = ifmgd->associated->cbss.bssid; reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED) - printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n", - sdata->dev->name, reason_code); + printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", + sdata->dev->name, bssid, reason_code); - if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && - (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) { - ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; - mod_timer(&ifmgd->timer, jiffies + - IEEE80211_RETRY_AUTH_INTERVAL); + if (!wk) { + ieee80211_set_disassoc(sdata, bssid, true); + } else { + list_del(&wk->list); + kfree(wk); } - ieee80211_set_disassoc(sdata, true, false, 0); - ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED; - cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len); + return RX_MGMT_CFG80211_DEAUTH; } -static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; if (len < 24 + 2) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN)) - return; + ASSERT_MGD_MTX(ifmgd); - reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + if (WARN_ON(!ifmgd->associated)) + return RX_MGMT_NONE; - if (ifmgd->flags & IEEE80211_STA_ASSOCIATED) - printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", - sdata->dev->name, reason_code); + if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN))) + return RX_MGMT_NONE; - if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) { - ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE; - mod_timer(&ifmgd->timer, jiffies + - IEEE80211_RETRY_AUTH_INTERVAL); - } + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n", + sdata->dev->name, reason_code); - ieee80211_set_disassoc(sdata, false, false, reason_code); - cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len); + ieee80211_set_disassoc(sdata, ifmgd->associated->cbss.bssid, false); + return RX_MGMT_CFG80211_DISASSOC; } -static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, - int reassoc) +static enum rx_mgmt_action __must_check +ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len, + bool reassoc) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -1614,17 +1315,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bool have_higher_than_11mbit = false, newsta = false; u16 ap_ht_cap_flags; - /* AssocResp and ReassocResp have identical structure, so process both - * of them in this function. */ - - if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) - return; + /* + * AssocResp and ReassocResp have identical structure, so process both + * of them in this function. + */ if (len < 24 + 6) - return; + return RX_MGMT_NONE; - if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0) - return; + if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0) + return RX_MGMT_NONE; capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -1647,26 +1347,19 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: AP rejected association temporarily; " "comeback duration %u TU (%u ms)\n", sdata->dev->name, tu, ms); + wk->timeout = jiffies + msecs_to_jiffies(ms); if (ms > IEEE80211_ASSOC_TIMEOUT) mod_timer(&ifmgd->timer, jiffies + msecs_to_jiffies(ms)); - return; + return RX_MGMT_NONE; } if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", sdata->dev->name, status_code); - /* if this was a reassociation, ensure we try a "full" - * association next time. This works around some broken APs - * which do not correctly reject reassociation requests. */ - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); - if (ifmgd->flags & IEEE80211_STA_EXT_SME) { - /* Wait for SME to decide what to do next */ - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - } - return; + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_ASSOC; } if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) @@ -1677,51 +1370,38 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (!elems.supp_rates) { printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", sdata->dev->name); - return; + return RX_MGMT_NONE; } printk(KERN_DEBUG "%s: associated\n", sdata->dev->name); ifmgd->aid = aid; - ifmgd->ap_capab = capab_info; - - kfree(ifmgd->assocresp_ies); - ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt); - ifmgd->assocresp_ies = kmalloc(ifmgd->assocresp_ies_len, GFP_KERNEL); - if (ifmgd->assocresp_ies) - memcpy(ifmgd->assocresp_ies, pos, ifmgd->assocresp_ies_len); rcu_read_lock(); /* Add STA entry for the AP */ - sta = sta_info_get(local, ifmgd->bssid); + sta = sta_info_get(local, wk->bss->cbss.bssid); if (!sta) { newsta = true; - sta = sta_info_alloc(sdata, ifmgd->bssid, GFP_ATOMIC); + rcu_read_unlock(); + + sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL); if (!sta) { printk(KERN_DEBUG "%s: failed to alloc STA entry for" " the AP\n", sdata->dev->name); - rcu_read_unlock(); - return; + return RX_MGMT_NONE; } /* update new sta with its last rx activity */ sta->last_rx = jiffies; - } - /* - * FIXME: Do we really need to update the sta_info's information here? - * We already know about the AP (we found it in our list) so it - * should already be filled with the right info, no? - * As is stands, all this is racy because typically we assume - * the information that is filled in here (except flags) doesn't - * change while a STA structure is alive. As such, it should move - * to between the sta_info_alloc() and sta_info_insert() above. - */ + set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_AP); + if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) + set_sta_flags(sta, WLAN_STA_AUTHORIZED); - set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP); - if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flags(sta, WLAN_STA_AUTHORIZED); + rcu_read_lock(); + } rates = 0; basic_rates = 0; @@ -1771,8 +1451,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; - /* If TKIP/WEP is used, no need to parse AP's HT capabilities */ - if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) + if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) ieee80211_ht_cap_ie_to_sta_ht_cap(sband, elems.ht_cap_elem, &sta->sta.ht_cap); @@ -1792,7 +1471,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: failed to insert STA entry for" " the AP (error %d)\n", sdata->dev->name, err); rcu_read_unlock(); - return; + return RX_MGMT_NONE; } } @@ -1806,15 +1485,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, if (elems.ht_info_elem && elems.wmm_param && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) && - !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) + !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, + wk->bss->cbss.bssid, ap_ht_cap_flags); /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; bss_conf->assoc_capability = capab_info; - ieee80211_set_associated(sdata, changed); + ieee80211_set_associated(sdata, wk->bss, changed); /* * initialise the time of last beacon to be the association time, @@ -1822,8 +1502,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, */ ifmgd->last_beacon = jiffies; - ieee80211_associated(sdata); - cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len); + list_del(&wk->list); + kfree(wk); + return RX_MGMT_CFG80211_ASSOC; } @@ -1851,23 +1532,25 @@ 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); - if (!bss) + if (bss) + ieee80211_rx_bss_put(local, bss); + + if (!sdata->u.mgd.associated) return; if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && - (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) { + (memcmp(mgmt->bssid, sdata->u.mgd.associated->cbss.bssid, + ETH_ALEN) == 0)) { struct ieee80211_channel_sw_ie *sw_elem = (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); } - - ieee80211_rx_bss_put(local, bss); } static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, - size_t len, + struct ieee80211_mgd_work *wk, + struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd; @@ -1876,6 +1559,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ifmgd = &sdata->u.mgd; + ASSERT_MGD_MTX(ifmgd); + if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ @@ -1889,13 +1574,17 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false); /* direct probe may be part of the association flow */ - if (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE) { + if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) { printk(KERN_DEBUG "%s direct probe responded\n", sdata->dev->name); - ieee80211_authenticate(sdata); + wk->tries = 0; + wk->state = IEEE80211_MGD_STATE_AUTH; + WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE); } - if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { + if (ifmgd->associated && + memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 && + ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL; mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local, -1); @@ -1937,6 +1626,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, bool erp_valid, directed_tim = false; u8 erp_value = 0; u32 ncrc; + u8 *bssid; + + ASSERT_MGD_MTX(ifmgd); /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; @@ -1946,8 +1638,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (rx_status->freq != local->hw.conf.channel->center_freq) return; - if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED) || - memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0) + if (WARN_ON(!ifmgd->associated)) + return; + + bssid = ifmgd->associated->cbss.bssid; + + if (WARN_ON(memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)) return; if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) { @@ -2019,15 +1715,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) { + !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { struct sta_info *sta; struct ieee80211_supported_band *sband; u16 ap_ht_cap_flags; rcu_read_lock(); - sta = sta_info_get(local, ifmgd->bssid); - if (!sta) { + sta = sta_info_get(local, bssid); + if (WARN_ON(!sta)) { rcu_read_unlock(); return; } @@ -2042,7 +1738,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - ap_ht_cap_flags); + bssid, ap_ht_cap_flags); } if (elems.country_elem) { @@ -2063,8 +1759,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) + struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_mgmt *mgmt; @@ -2080,12 +1775,12 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_PROBE_REQ: case IEEE80211_STYPE_PROBE_RESP: case IEEE80211_STYPE_BEACON: - memcpy(skb->cb, rx_status, sizeof(*rx_status)); case IEEE80211_STYPE_AUTH: case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: case IEEE80211_STYPE_DEAUTH: case IEEE80211_STYPE_DISASSOC: + case IEEE80211_STYPE_ACTION: skb_queue_tail(&sdata->u.mgd.skb_queue, skb); queue_work(local->hw.workqueue, &sdata->u.mgd.work); return RX_QUEUED; @@ -2097,40 +1792,116 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; + struct ieee80211_mgd_work *wk; + enum rx_mgmt_action rma = RX_MGMT_NONE; u16 fc; rx_status = (struct ieee80211_rx_status *) skb->cb; mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - switch (fc & IEEE80211_FCTL_STYPE) { - 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); - break; - case IEEE80211_STYPE_AUTH: - ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); - break; - case IEEE80211_STYPE_ASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 0); + mutex_lock(&ifmgd->mtx); + + if (ifmgd->associated && + memcmp(ifmgd->associated->cbss.bssid, mgmt->bssid, + ETH_ALEN) == 0) { + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, + rx_status); + break; + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt, + skb->len, rx_status); + break; + case IEEE80211_STYPE_DEAUTH: + rma = ieee80211_rx_mgmt_deauth(sdata, NULL, + mgmt, skb->len); + break; + case IEEE80211_STYPE_DISASSOC: + rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + break; + case IEEE80211_STYPE_ACTION: + /* XXX: differentiate, can only happen for CSA now! */ + ieee80211_sta_process_chanswitch(sdata, + &mgmt->u.action.u.chan_switch.sw_elem, + ifmgd->associated); + break; + } + mutex_unlock(&ifmgd->mtx); + + switch (rma) { + case RX_MGMT_NONE: + /* no action */ + break; + case RX_MGMT_CFG80211_DEAUTH: + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, + NULL); + break; + case RX_MGMT_CFG80211_DISASSOC: + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, + NULL); + break; + default: + WARN(1, "unexpected: %d", rma); + } + goto out; + } + + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0) + continue; + + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len, + rx_status); + break; + case IEEE80211_STYPE_AUTH: + rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len); + break; + case IEEE80211_STYPE_ASSOC_RESP: + rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, + skb->len, false); + break; + case IEEE80211_STYPE_REASSOC_RESP: + rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt, + skb->len, true); + break; + case IEEE80211_STYPE_DEAUTH: + rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt, + skb->len); + break; + } + /* + * We've processed this frame for that work, so it can't + * belong to another work struct. + * NB: this is also required for correctness because the + * called functions can free 'wk', and for 'rma'! + */ break; - case IEEE80211_STYPE_REASSOC_RESP: - ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 1); + } + + mutex_unlock(&ifmgd->mtx); + + switch (rma) { + case RX_MGMT_NONE: + /* no action */ break; - case IEEE80211_STYPE_DEAUTH: - ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); + case RX_MGMT_CFG80211_AUTH: + cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len); break; - case IEEE80211_STYPE_DISASSOC: - ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + case RX_MGMT_CFG80211_ASSOC: + cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len); break; + default: + WARN(1, "unexpected: %d", rma); } + out: kfree_skb(skb); } @@ -2146,125 +1917,9 @@ static void ieee80211_sta_timer(unsigned long data) return; } - set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); queue_work(local->hw.workqueue, &ifmgd->work); } -static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - - /* Reset own TSF to allow time synchronization work. */ - drv_reset_tsf(local); - - ifmgd->wmm_last_param_set = -1; /* allow any WMM update */ - - - if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN) - ifmgd->auth_alg = WLAN_AUTH_OPEN; - else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) - ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY; - else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP) - ifmgd->auth_alg = WLAN_AUTH_LEAP; - else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT) - ifmgd->auth_alg = WLAN_AUTH_FT; - else - ifmgd->auth_alg = WLAN_AUTH_OPEN; - ifmgd->auth_transaction = -1; - ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED; - ifmgd->assoc_scan_tries = 0; - ifmgd->direct_probe_tries = 0; - ifmgd->auth_tries = 0; - ifmgd->assoc_tries = 0; - netif_tx_stop_all_queues(sdata->dev); - netif_carrier_off(sdata->dev); -} - -static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - struct ieee80211_bss *bss; - u8 *bssid = ifmgd->bssid, *ssid = ifmgd->ssid; - u8 ssid_len = ifmgd->ssid_len; - u16 capa_mask = WLAN_CAPABILITY_ESS; - u16 capa_val = WLAN_CAPABILITY_ESS; - struct ieee80211_channel *chan = local->oper_channel; - - if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) && - ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL | - IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL)) { - capa_mask |= WLAN_CAPABILITY_PRIVACY; - if (sdata->default_key) - capa_val |= WLAN_CAPABILITY_PRIVACY; - } - - if (ifmgd->flags & IEEE80211_STA_AUTO_CHANNEL_SEL) - chan = NULL; - - if (ifmgd->flags & IEEE80211_STA_AUTO_BSSID_SEL) - bssid = NULL; - - if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL) { - ssid = NULL; - ssid_len = 0; - } - - bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, - bssid, ssid, ssid_len, - capa_mask, capa_val); - - if (bss) { - local->oper_channel = bss->cbss.channel; - local->oper_channel_type = NL80211_CHAN_NO_HT; - ieee80211_hw_config(local, 0); - - if (!(ifmgd->flags & IEEE80211_STA_SSID_SET)) - ieee80211_sta_set_ssid(sdata, bss->ssid, - bss->ssid_len); - ieee80211_sta_set_bssid(sdata, bss->cbss.bssid); - ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len, - bss->supp_rates); - if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED) - sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; - - /* Send out direct probe if no probe resp was received or - * the one we have is outdated - */ - if (!bss->last_probe_resp || - time_after(jiffies, bss->last_probe_resp - + IEEE80211_SCAN_RESULT_EXPIRE)) - ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE; - else - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - - ieee80211_rx_bss_put(local, bss); - ieee80211_sta_reset_auth(sdata); - return 0; - } else { - if (ifmgd->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { - - ifmgd->assoc_scan_tries++; - - ieee80211_request_internal_scan(sdata, ifmgd->ssid, - ssid_len); - - ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE; - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); - } else { - ifmgd->assoc_scan_tries = 0; - ifmgd->state = IEEE80211_STA_MLME_DISABLED; - ieee80211_recalc_idle(local); - } - } - return -1; -} - - static void ieee80211_sta_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = @@ -2272,6 +1927,10 @@ static void ieee80211_sta_work(struct work_struct *work) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd; struct sk_buff *skb; + struct ieee80211_mgd_work *wk, *tmp; + LIST_HEAD(free_work); + enum rx_mgmt_action rma; + bool anybusy = false; if (!netif_running(sdata->dev)) return; @@ -2294,54 +1953,91 @@ static void ieee80211_sta_work(struct work_struct *work) ifmgd = &sdata->u.mgd; + /* first process frames to avoid timing out while a frame is pending */ while ((skb = skb_dequeue(&ifmgd->skb_queue))) ieee80211_sta_rx_queued_mgmt(sdata, skb); - if (ifmgd->state != IEEE80211_STA_MLME_DIRECT_PROBE && - ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE && - ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE && - test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) { - queue_delayed_work(local->hw.workqueue, &local->scan_work, - round_jiffies_relative(0)); - return; + /* then process the rest of the work */ + mutex_lock(&ifmgd->mtx); + + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (wk->state != IEEE80211_MGD_STATE_IDLE) { + anybusy = true; + break; + } } - if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request)) { - if (ieee80211_sta_config_auth(sdata)) - return; - clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); - } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request)) + ieee80211_recalc_idle(local); + + if (!anybusy) { + mutex_unlock(&ifmgd->mtx); + + if (test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) + queue_delayed_work(local->hw.workqueue, + &local->scan_work, + round_jiffies_relative(0)); return; + } - ieee80211_recalc_idle(local); + list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) { + if (time_before(jiffies, wk->timeout)) + continue; - switch (ifmgd->state) { - case IEEE80211_STA_MLME_DISABLED: - break; - case IEEE80211_STA_MLME_DIRECT_PROBE: - ieee80211_direct_probe(sdata); - break; - case IEEE80211_STA_MLME_AUTHENTICATE: - ieee80211_authenticate(sdata); - break; - case IEEE80211_STA_MLME_ASSOCIATE: - ieee80211_associate(sdata); - break; - case IEEE80211_STA_MLME_ASSOCIATED: - ieee80211_associated(sdata); - break; - default: - WARN_ON(1); - break; + switch (wk->state) { + default: + WARN_ON(1); + /* fall through */ + case IEEE80211_MGD_STATE_IDLE: + /* nothing */ + rma = RX_MGMT_NONE; + break; + case IEEE80211_MGD_STATE_PROBE: + rma = ieee80211_direct_probe(sdata, wk); + break; + case IEEE80211_MGD_STATE_AUTH: + rma = ieee80211_authenticate(sdata, wk); + break; + case IEEE80211_MGD_STATE_ASSOC: + rma = ieee80211_associate(sdata, wk); + break; + } + + switch (rma) { + case RX_MGMT_NONE: + /* no action required */ + break; + case RX_MGMT_CFG80211_AUTH_TO: + case RX_MGMT_CFG80211_ASSOC_TO: + list_del(&wk->list); + list_add(&wk->list, &free_work); + wk->tries = rma; /* small abuse but only local */ + break; + default: + WARN(1, "unexpected: %d", rma); + } } - if (ieee80211_privacy_mismatch(sdata)) { - printk(KERN_DEBUG "%s: privacy configuration mismatch and " - "mixed-cell disabled - disassociate\n", sdata->dev->name); + mutex_unlock(&ifmgd->mtx); - ieee80211_set_disassoc(sdata, false, true, - WLAN_REASON_UNSPECIFIED); + list_for_each_entry_safe(wk, tmp, &free_work, list) { + switch (wk->tries) { + case RX_MGMT_CFG80211_AUTH_TO: + cfg80211_send_auth_timeout(sdata->dev, + wk->bss->cbss.bssid); + break; + case RX_MGMT_CFG80211_ASSOC_TO: + cfg80211_send_assoc_timeout(sdata->dev, + wk->bss->cbss.bssid); + break; + default: + WARN(1, "unexpected: %d", wk->tries); + } + + list_del(&wk->list); + kfree(wk); } + + ieee80211_recalc_idle(local); } static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) @@ -2353,7 +2049,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) */ sdata->u.mgd.last_beacon = jiffies; - queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); } @@ -2395,7 +2090,6 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd; - u32 hw_flags; ifmgd = &sdata->u.mgd; INIT_WORK(&ifmgd->work, ieee80211_sta_work); @@ -2407,198 +2101,243 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); skb_queue_head_init(&ifmgd->skb_queue); + INIT_LIST_HEAD(&ifmgd->work_list); + ifmgd->capab = WLAN_CAPABILITY_ESS; - ifmgd->auth_algs = IEEE80211_AUTH_ALG_OPEN | - IEEE80211_AUTH_ALG_SHARED_KEY; - ifmgd->flags |= IEEE80211_STA_CREATE_IBSS | - IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL; + ifmgd->flags = 0; if (sdata->local->hw.queues >= 4) ifmgd->flags |= IEEE80211_STA_WMM_ENABLED; - hw_flags = sdata->local->hw.flags; - - if (hw_flags & IEEE80211_HW_SUPPORTS_PS) { - ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE; - sdata->local->hw.conf.dynamic_ps_timeout = 500; - } + mutex_init(&ifmgd->mtx); } -/* configuration hooks */ -void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata) +/* scan finished notification */ +void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - struct ieee80211_local *local = sdata->local; - - if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) - return; - - if ((ifmgd->flags & (IEEE80211_STA_BSSID_SET | - IEEE80211_STA_AUTO_BSSID_SEL)) && - (ifmgd->flags & (IEEE80211_STA_SSID_SET | - IEEE80211_STA_AUTO_SSID_SEL))) { - - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); - - if (ifmgd->ssid_len == 0) { - /* - * Only allow association to be started if a valid SSID - * is configured. - */ - return; - } + struct ieee80211_sub_if_data *sdata = local->scan_sdata; - if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) || - ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE) - set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request); - else if (ifmgd->flags & IEEE80211_STA_EXT_SME) - set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request); - queue_work(local->hw.workqueue, &ifmgd->work); - } + /* Restart STA timers */ + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) + ieee80211_restart_sta_timer(sdata); + rcu_read_unlock(); } -int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata) +int ieee80211_max_network_latency(struct notifier_block *nb, + unsigned long data, void *dummy) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + s32 latency_usec = (s32) data; + struct ieee80211_local *local = + container_of(nb, struct ieee80211_local, + network_latency_notifier); - if (ifmgd->ssid_len) - ifmgd->flags |= IEEE80211_STA_SSID_SET; - else - ifmgd->flags &= ~IEEE80211_STA_SSID_SET; + mutex_lock(&local->iflist_mtx); + ieee80211_recalc_ps(local, latency_usec); + mutex_unlock(&local->iflist_mtx); return 0; } -int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len) +/* config hooks */ +int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_auth_request *req) { - struct ieee80211_if_managed *ifmgd; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + const u8 *ssid; + struct ieee80211_mgd_work *wk; + u16 auth_alg; - if (len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + auth_alg = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg = WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg = WLAN_AUTH_LEAP; + break; + default: + return -EOPNOTSUPP; + } - ifmgd = &sdata->u.mgd; + wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL); + if (!wk) + return -ENOMEM; - if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) { - if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); + wk->bss = (void *)req->bss; - /* - * Do not use reassociation if SSID is changed (different ESS). - */ - ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET; - memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid)); - memcpy(ifmgd->ssid, ssid, len); - ifmgd->ssid_len = len; + if (req->ie && req->ie_len) { + memcpy(wk->ie, req->ie, req->ie_len); + wk->ie_len = req->ie_len; } - return ieee80211_sta_commit(sdata); -} + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + memcpy(wk->ssid, ssid + 2, ssid[1]); + wk->ssid_len = ssid[1]; -int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - memcpy(ssid, ifmgd->ssid, ifmgd->ssid_len); - *len = ifmgd->ssid_len; + wk->state = IEEE80211_MGD_STATE_PROBE; + wk->auth_alg = auth_alg; + + /* + * XXX: if still associated need to tell AP that we're going + * to sleep and then change channel etc. + */ + sdata->local->oper_channel = req->bss->channel; + ieee80211_hw_config(sdata->local, 0); + + mutex_lock(&ifmgd->mtx); + list_add(&wk->list, &sdata->u.mgd.work_list); + mutex_unlock(&ifmgd->mtx); + + queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); return 0; } -int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) +int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_assoc_request *req) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_work *wk, *found = NULL; + int i, err; - if (compare_ether_addr(bssid, ifmgd->bssid) != 0 && - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) - ieee80211_set_disassoc(sdata, true, true, - WLAN_REASON_DEAUTH_LEAVING); + mutex_lock(&ifmgd->mtx); - if (is_valid_ether_addr(bssid)) { - memcpy(ifmgd->bssid, bssid, ETH_ALEN); - ifmgd->flags |= IEEE80211_STA_BSSID_SET; - } else { - memset(ifmgd->bssid, 0, ETH_ALEN); - ifmgd->flags &= ~IEEE80211_STA_BSSID_SET; + list_for_each_entry(wk, &ifmgd->work_list, list) { + if (&wk->bss->cbss == req->bss && + wk->state == IEEE80211_MGD_STATE_IDLE) { + found = wk; + break; + } } - return ieee80211_sta_commit(sdata); -} + if (!found) { + err = -ENOLINK; + goto out; + } -int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, - const char *ie, size_t len) -{ - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + list_del(&found->list); - if (len == 0 && ifmgd->extra_ie_len == 0) - return -EALREADY; + wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL); + if (!wk) { + list_add(&found->list, &ifmgd->work_list); + err = -ENOMEM; + goto out; + } - if (len == ifmgd->extra_ie_len && ifmgd->extra_ie && - memcmp(ifmgd->extra_ie, ie, len) == 0) - return -EALREADY; + list_add(&wk->list, &ifmgd->work_list); - kfree(ifmgd->extra_ie); - if (len == 0) { - ifmgd->extra_ie = NULL; - ifmgd->extra_ie_len = 0; - return 0; - } - ifmgd->extra_ie = kmalloc(len, GFP_KERNEL); - if (!ifmgd->extra_ie) { - ifmgd->extra_ie_len = 0; - return -ENOMEM; + ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N; + + for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) + if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || + req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) + ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + + sdata->local->oper_channel = req->bss->channel; + ieee80211_hw_config(sdata->local, 0); + + if (req->ie && req->ie_len) { + memcpy(wk->ie, req->ie, req->ie_len); + wk->ie_len = req->ie_len; + } else + wk->ie_len = 0; + + if (req->prev_bssid) + memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN); + + wk->state = IEEE80211_MGD_STATE_ASSOC; + wk->tries = 0; + + if (req->use_mfp) { + ifmgd->mfp = IEEE80211_MFP_REQUIRED; + ifmgd->flags |= IEEE80211_STA_MFP_ENABLED; + } else { + ifmgd->mfp = IEEE80211_MFP_DISABLED; + ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; } - memcpy(ifmgd->extra_ie, ie, len); - ifmgd->extra_ie_len = len; - return 0; -} -int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason) -{ - printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n", - sdata->dev->name, reason); + if (req->crypto.control_port) + ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; + else + ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_set_disassoc(sdata, true, true, reason); - return 0; + queue_work(sdata->local->hw.workqueue, &sdata->u.mgd.work); + + err = 0; + + out: + mutex_unlock(&ifmgd->mtx); + return err; } -int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason) +int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, + struct cfg80211_deauth_request *req, + void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_mgd_work *wk; + const u8 *bssid = NULL; - printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n", - sdata->dev->name, reason); + printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n", + sdata->dev->name, req->reason_code); + + mutex_lock(&ifmgd->mtx); + + if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) { + bssid = req->bss->bssid; + ieee80211_set_disassoc(sdata, bssid, true); + } else list_for_each_entry(wk, &ifmgd->work_list, list) { + if (&wk->bss->cbss == req->bss) { + bssid = req->bss->bssid; + list_del(&wk->list); + kfree(wk); + break; + } + } - if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED)) + /* cfg80211 should catch this... */ + if (WARN_ON(!bssid)) { + mutex_unlock(&ifmgd->mtx); return -ENOLINK; + } + + mutex_unlock(&ifmgd->mtx); + + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, req->reason_code, + cookie); - ieee80211_set_disassoc(sdata, false, true, reason); return 0; } -/* scan finished notification */ -void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) +int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, + struct cfg80211_disassoc_request *req, + void *cookie) { - struct ieee80211_sub_if_data *sdata = local->scan_sdata; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - /* Restart STA timers */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) - ieee80211_restart_sta_timer(sdata); - rcu_read_unlock(); -} + printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n", + sdata->dev->name, req->reason_code); -int ieee80211_max_network_latency(struct notifier_block *nb, - unsigned long data, void *dummy) -{ - s32 latency_usec = (s32) data; - struct ieee80211_local *local = - container_of(nb, struct ieee80211_local, - network_latency_notifier); + mutex_lock(&ifmgd->mtx); - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, latency_usec); - mutex_unlock(&local->iflist_mtx); + /* cfg80211 should catch that */ + if (WARN_ON(&ifmgd->associated->cbss != req->bss)) { + mutex_unlock(&ifmgd->mtx); + return -ENOLINK; + } + + ieee80211_set_disassoc(sdata, req->bss->bssid, false); + + mutex_unlock(&ifmgd->mtx); + ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, + IEEE80211_STYPE_DISASSOC, req->reason_code, + cookie); return 0; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index de5bba7f910a..fe6b99059531 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -30,7 +30,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *skb, - struct ieee80211_rx_status *status, u16 mpdu_seq_num, int bar_req); /* @@ -59,11 +58,11 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, return skb; } -static inline int should_drop_frame(struct ieee80211_rx_status *status, - struct sk_buff *skb, +static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len, int radiotap_len) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) @@ -111,10 +110,10 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, static void ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate, int rtap_len) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_radiotap_header *rthdr; unsigned char *pos; @@ -220,9 +219,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, */ static struct sk_buff * ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; int needed_headroom = 0; struct sk_buff *skb, *skb2; @@ -248,8 +247,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, present_fcs_len = FCS_LEN; if (!local->monitors) { - if (should_drop_frame(status, origskb, present_fcs_len, - rtap_len)) { + if (should_drop_frame(origskb, present_fcs_len, rtap_len)) { dev_kfree_skb(origskb); return NULL; } @@ -257,7 +255,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return remove_monitor_info(local, origskb, rtap_len); } - if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) { + if (should_drop_frame(origskb, present_fcs_len, rtap_len)) { /* only need to expand headroom if necessary */ skb = origskb; origskb = NULL; @@ -289,7 +287,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, /* if necessary, prepend radiotap information */ if (!(status->flag & RX_FLAG_RADIOTAP)) - ieee80211_add_rx_radiotap_header(local, skb, status, rate, + ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom); skb_reset_mac_header(skb); @@ -421,12 +419,11 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) struct sk_buff *skb = rx->skb; if (unlikely(local->hw_scanning)) - return ieee80211_scan_rx(rx->sdata, skb, rx->status); + return ieee80211_scan_rx(rx->sdata, skb); if (unlikely(local->sw_scanning)) { /* drop all the other packets during a software scan anyway */ - if (ieee80211_scan_rx(rx->sdata, skb, rx->status) - != RX_QUEUED) + if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) dev_kfree_skb(skb); return RX_QUEUED; } @@ -1620,7 +1617,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) /* manage reordering buffer according to requested */ /* sequence number */ rcu_read_lock(); - ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, NULL, + ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, start_seq_num, 1); rcu_read_unlock(); return RX_DROP_UNUSABLE; @@ -1644,12 +1641,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 || compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) { - /* Not from the current AP. */ - return; - } - - if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) { - /* Association in progress; ignore SA Query */ + /* Not from the current AP or not associated yet. */ return; } @@ -1686,7 +1678,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; - struct ieee80211_bss *bss; int len = rx->skb->len; if (!ieee80211_is_action(mgmt->frame_control)) @@ -1764,17 +1755,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN)) return RX_DROP_MONITOR; - bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid, - local->hw.conf.channel->center_freq, - sdata->u.mgd.ssid, - sdata->u.mgd.ssid_len); - if (!bss) - return RX_DROP_MONITOR; - - ieee80211_sta_process_chanswitch(sdata, - &mgmt->u.action.u.chan_switch.sw_elem, bss); - ieee80211_rx_bss_put(local, bss); - break; + return ieee80211_sta_rx_mgmt(sdata, rx->skb); } break; case WLAN_CATEGORY_SA_QUERY: @@ -1817,13 +1798,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; if (ieee80211_vif_is_mesh(&sdata->vif)) - return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_mesh_rx_mgmt(sdata, rx->skb); if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_ibss_rx_mgmt(sdata, rx->skb); if (sdata->vif.type == NL80211_IFTYPE_STATION) - return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status); + return ieee80211_sta_rx_mgmt(sdata, rx->skb); return RX_DROP_MONITOR; } @@ -1866,7 +1847,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev, !ieee80211_is_auth(hdr->frame_control)) goto ignore; - mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL); + mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL, + GFP_ATOMIC); ignore: dev_kfree_skb(rx->skb); rx->skb = NULL; @@ -2028,13 +2010,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_STATION: if (!bssid) return 0; - if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) { - if (!(rx->flags & IEEE80211_RX_IN_SCAN)) - return 0; - rx->flags &= ~IEEE80211_RX_RA_MATCH; - } else if (!multicast && - compare_ether_addr(sdata->dev->dev_addr, - hdr->addr1) != 0) { + if (!multicast && + compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) { if (!(sdata->dev->flags & IFF_PROMISC)) return 0; rx->flags &= ~IEEE80211_RX_RA_MATCH; @@ -2114,9 +2091,9 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, */ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status, struct ieee80211_rate *rate) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr; @@ -2227,20 +2204,21 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw, { struct ieee80211_supported_band *sband; struct ieee80211_rate *rate; - struct ieee80211_rx_status status; + struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; + struct ieee80211_rx_status *status; - if (!tid_agg_rx->reorder_buf[index]) + if (!skb) goto no_frame; + status = IEEE80211_SKB_RXCB(skb); + /* release the reordered frames to stack */ - memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status)); - sband = hw->wiphy->bands[status.band]; - if (status.flag & RX_FLAG_HT) + sband = hw->wiphy->bands[status->band]; + if (status->flag & RX_FLAG_HT) rate = sband->bitrates; /* TODO: HT rates */ else - rate = &sband->bitrates[status.rate_idx]; - __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index], - &status, rate); + rate = &sband->bitrates[status->rate_idx]; + __ieee80211_rx_handle_packet(hw, skb, rate); tid_agg_rx->stored_mpdu_num--; tid_agg_rx->reorder_buf[index] = NULL; @@ -2265,7 +2243,6 @@ no_frame: static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff *skb, - struct ieee80211_rx_status *rxstatus, u16 mpdu_seq_num, int bar_req) { @@ -2324,8 +2301,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, /* put the frame in the reordering buffer */ tid_agg_rx->reorder_buf[index] = skb; tid_agg_rx->reorder_time[index] = jiffies; - memcpy(tid_agg_rx->reorder_buf[index]->cb, rxstatus, - sizeof(*rxstatus)); tid_agg_rx->stored_mpdu_num++; /* release the buffer until next missing frame */ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) @@ -2374,8 +2349,7 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw, } static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, - struct sk_buff *skb, - struct ieee80211_rx_status *status) + struct sk_buff *skb) { struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; @@ -2424,7 +2398,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, /* according to mpdu sequence number deal with reordering buffer */ mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; - ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, status, + ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, mpdu_seq_num, 0); end_reorder: return ret; @@ -2434,12 +2408,12 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ -void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status) +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_rate *rate = NULL; struct ieee80211_supported_band *sband; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); if (status->band < 0 || status->band >= IEEE80211_NUM_BANDS) { @@ -2482,7 +2456,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, * if it was previously present. * Also, frames with less than 16 bytes are dropped. */ - skb = ieee80211_rx_monitor(local, skb, status, rate); + skb = ieee80211_rx_monitor(local, skb, rate); if (!skb) { rcu_read_unlock(); return; @@ -2500,8 +2474,8 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, * frames from other than operational channel), but that should not * happen in normal networks. */ - if (!ieee80211_rx_reorder_ampdu(local, skb, status)) - __ieee80211_rx_handle_packet(hw, skb, status, rate); + if (!ieee80211_rx_reorder_ampdu(local, skb)) + __ieee80211_rx_handle_packet(hw, skb, rate); rcu_read_unlock(); } @@ -2509,16 +2483,13 @@ EXPORT_SYMBOL(__ieee80211_rx); /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ -void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_rx_status *status) +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); skb->dev = local->mdev; - /* copy status into skb->cb for use by tasklet */ - memcpy(skb->cb, status, sizeof(*status)); skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 2a8d09ad17ff..5f4f7869d050 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -26,7 +26,7 @@ #define IEEE80211_PROBE_DELAY (HZ / 33) #define IEEE80211_CHANNEL_TIME (HZ / 33) -#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) +#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) struct ieee80211_bss * ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, @@ -121,23 +121,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local, return bss; } -void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid, - int freq, u8 *ssid, u8 ssid_len) -{ - struct ieee80211_bss *bss; - struct ieee80211_local *local = sdata->local; - - bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len); - if (bss) { - cfg80211_unlink_bss(local->hw.wiphy, (void *)bss); - ieee80211_rx_bss_put(local, bss); - } -} - ieee80211_rx_result -ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct ieee80211_rx_status *rx_status) +ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_mgmt *mgmt; struct ieee80211_bss *bss; u8 *elements; @@ -327,7 +314,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) /* Tell AP we're back */ if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { + if (sdata->u.mgd.associated) { ieee80211_scan_ps_disable(sdata); netif_tx_wake_all_queues(sdata->dev); } @@ -383,7 +370,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) sdata, BSS_CHANGED_BEACON_ENABLED); if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { + if (sdata->u.mgd.associated) { netif_tx_stop_all_queues(sdata->dev); ieee80211_scan_ps_enable(sdata); } @@ -443,10 +430,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (req != &local->int_scan_req && sdata->vif.type == NL80211_IFTYPE_STATION && - (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE || - ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE)) { - /* actually wait for the assoc to finish/time out */ + !list_empty(&ifmgd->work_list)) { + /* actually wait for the work it's doing to finish/time out */ set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request); return 0; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 49a1a1f76511..4ecf10a9bd00 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -308,6 +308,23 @@ struct sta_info { struct dentry *inactive_ms; struct dentry *last_seq_ctrl; struct dentry *agg_status; + struct dentry *aid; + struct dentry *dev; + struct dentry *rx_packets; + struct dentry *tx_packets; + struct dentry *rx_bytes; + struct dentry *tx_bytes; + struct dentry *rx_duplicates; + struct dentry *rx_fragments; + struct dentry *rx_dropped; + struct dentry *tx_fragments; + struct dentry *tx_filtered; + struct dentry *tx_retry_failed; + struct dentry *tx_retry_count; + struct dentry *last_signal; + struct dentry *last_qual; + struct dentry *last_noise; + struct dentry *wep_weak_iv_count; bool add_has_run; } debugfs; #endif diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d238a8939a09..2ffb35d3f566 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1627,7 +1627,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, u32 sta_flags = 0; if (unlikely(skb->len < ETH_HLEN)) { - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1664,7 +1664,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, if (!sdata->u.mesh.mshcfg.dot11MeshTTL) { /* Do not send frames with mesh_ttl == 0 */ sdata->u.mesh.mshstats.dropped_frames_ttl++; - ret = 0; + ret = NETDEV_TX_OK; goto fail; } memset(&mesh_hdr, 0, sizeof(mesh_hdr)); @@ -1724,7 +1724,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, hdrlen = 24; break; default: - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1766,7 +1766,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -1858,10 +1858,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, dev->trans_start = jiffies; dev_queue_xmit(skb); - return 0; + return NETDEV_TX_OK; fail: - if (!ret) + if (ret == NETDEV_TX_OK) dev_kfree_skb(skb); return ret; diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index ef73105b3061..4fafb2d27c84 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -67,10 +67,10 @@ static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) static void ieee80211_wep_get_iv(struct ieee80211_local *local, - struct ieee80211_key *key, u8 *iv) + int keylen, int keyidx, u8 *iv) { local->wep_iv++; - if (ieee80211_wep_weak_iv(local->wep_iv, key->conf.keylen)) + if (ieee80211_wep_weak_iv(local->wep_iv, keylen)) local->wep_iv += 0x0100; if (!iv) @@ -79,13 +79,13 @@ static void ieee80211_wep_get_iv(struct ieee80211_local *local, *iv++ = (local->wep_iv >> 16) & 0xff; *iv++ = (local->wep_iv >> 8) & 0xff; *iv++ = local->wep_iv & 0xff; - *iv++ = key->conf.keyidx << 6; + *iv++ = keyidx << 6; } static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) + int keylen, int keyidx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; unsigned int hdrlen; @@ -100,7 +100,7 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, hdrlen = ieee80211_hdrlen(hdr->frame_control); newhdr = skb_push(skb, WEP_IV_LEN); memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); - ieee80211_wep_get_iv(local, key, newhdr + hdrlen); + ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } @@ -144,26 +144,17 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) */ -int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) +static int ieee80211_wep_encrypt(struct ieee80211_local *local, + struct sk_buff *skb, + const u8 *key, int keylen, int keyidx) { - u32 klen; - u8 *rc4key, *iv; + u8 *iv; size_t len; + u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; - if (!key || key->conf.alg != ALG_WEP) - return -1; - - klen = 3 + key->conf.keylen; - rc4key = kmalloc(klen, GFP_ATOMIC); - if (!rc4key) - return -1; - - iv = ieee80211_wep_add_iv(local, skb, key); - if (!iv) { - kfree(rc4key); + iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx); + if (!iv) return -1; - } len = skb->len - (iv + WEP_IV_LEN - skb->data); @@ -171,16 +162,14 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, memcpy(rc4key, iv, 3); /* Copy rest of the WEP key (the secret part) */ - memcpy(rc4key + 3, key->conf.key, key->conf.keylen); + memcpy(rc4key + 3, key, keylen); /* Add room for ICV */ skb_put(skb, WEP_ICV_LEN); - ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, klen, + ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3, iv + WEP_IV_LEN, len); - kfree(rc4key); - return 0; } @@ -216,8 +205,9 @@ int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload * is moved to the beginning of the skb and skb length will be reduced. */ -int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key) +static int ieee80211_wep_decrypt(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key) { u32 klen; u8 *rc4key; @@ -314,12 +304,16 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { - if (ieee80211_wep_encrypt(tx->local, skb, tx->key)) + if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key, + tx->key->conf.keylen, + tx->key->conf.keyidx)) return -1; } else { info->control.hw_key = &tx->key->conf; if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) { - if (!ieee80211_wep_add_iv(tx->local, skb, tx->key)) + if (!ieee80211_wep_add_iv(tx->local, skb, + tx->key->conf.keylen, + tx->key->conf.keyidx)) return -1; } } diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h index d3f0db48314e..85219ded8703 100644 --- a/net/mac80211/wep.h +++ b/net/mac80211/wep.h @@ -22,10 +22,6 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, size_t klen, u8 *data, size_t data_len); -int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key); -int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, - struct ieee80211_key *key); bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); ieee80211_rx_result diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 1da81f456744..5acb8140ee58 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -27,29 +27,6 @@ #include "aes_ccm.h" -static int ieee80211_ioctl_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length); - if (ret && ret != -EALREADY) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - if (ret != -EALREADY) - ieee80211_sta_req_auth(sdata); - return 0; - } - - return -EOPNOTSUPP; -} - static int ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) @@ -61,16 +38,13 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); else if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL; + return cfg80211_mgd_wext_siwfreq(dev, info, freq, extra); /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ if (freq->e == 0) { - if (freq->m < 0) { - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.flags |= - IEEE80211_STA_AUTO_CHANNEL_SEL; - return 0; - } else + if (freq->m < 0) + return -EINVAL; + else chan = ieee80211_get_channel(local->hw.wiphy, ieee80211_channel_to_frequency(freq->m)); } else { @@ -95,9 +69,6 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev, if (local->oper_channel == chan) return 0; - if (sdata->vif.type == NL80211_IFTYPE_STATION) - ieee80211_sta_req_auth(sdata); - local->oper_channel = chan; local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, 0); @@ -115,6 +86,8 @@ static int ieee80211_ioctl_giwfreq(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); freq->m = local->oper_channel->center_freq; freq->e = 6; @@ -128,31 +101,11 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_point *data, char *ssid) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - size_t len = data->length; - int ret; if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->flags) - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL; - else - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL; - - ret = ieee80211_sta_set_ssid(sdata, ssid, len); - if (ret) - return ret; - - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -162,23 +115,14 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) { - size_t len; struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int res = ieee80211_sta_get_ssid(sdata, ssid, &len); - if (res == 0) { - data->length = len; - data->flags = 1; - } else - data->flags = 0; - return res; - } + else if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); return -EOPNOTSUPP; } @@ -193,40 +137,11 @@ static int ieee80211_ioctl_siwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - int ret; - - if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL | - IEEE80211_STA_AUTO_CHANNEL_SEL; - else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) - sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL; - else - sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL; - ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data); - if (ret) - return ret; - sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME; - sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; - ieee80211_sta_req_auth(sdata); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { - /* - * If it is necessary to update the WDS peer address - * while the interface is running, then we need to do - * more work here, namely if it is running we need to - * add a new and remove the old STA entry, this is - * normally handled by _open() and _stop(). - */ - if (netif_running(dev)) - return -EBUSY; - - memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, - ETH_ALEN); - - return 0; - } + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); + if (sdata->vif.type == NL80211_IFTYPE_WDS) + return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); return -EOPNOTSUPP; } @@ -240,326 +155,13 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, if (sdata->vif.type == NL80211_IFTYPE_ADHOC) return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN); - } else - memset(&ap_addr->sa_data, 0, ETH_ALEN); - return 0; - } else if (sdata->vif.type == NL80211_IFTYPE_WDS) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); - return 0; - } - - return -EOPNOTSUPP; -} - - -static int ieee80211_ioctl_siwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - int i, err = -EINVAL; - u32 target_rate = rate->value / 100000; - struct ieee80211_sub_if_data *sdata; - struct ieee80211_supported_band *sband; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates - * target_rate = X, rate->fixed = 1 means only rate X - * target_rate = X, rate->fixed = 0 means all rates <= X */ - sdata->max_ratectrl_rateidx = -1; - sdata->force_unicast_rateidx = -1; - if (rate->value < 0) - return 0; - - for (i=0; i< sband->n_bitrates; i++) { - struct ieee80211_rate *brate = &sband->bitrates[i]; - int this_rate = brate->bitrate; - - if (target_rate == this_rate) { - sdata->max_ratectrl_rateidx = i; - if (rate->fixed) - sdata->force_unicast_rateidx = i; - err = 0; - break; - } - } - return err; -} - -static int ieee80211_ioctl_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct sta_info *sta; - struct ieee80211_sub_if_data *sdata; - struct ieee80211_supported_band *sband; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - rcu_read_lock(); - - sta = sta_info_get(local, sdata->u.mgd.bssid); - - if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) - rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; - else - rate->value = 0; - - rcu_read_unlock(); - - if (!sta) - return -ENODEV; - - rate->value *= 100000; - - return 0; -} - -static int ieee80211_ioctl_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, - char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_conf *conf = &local->hw.conf; - int timeout = 0; - bool ps; - - if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) - return -EOPNOTSUPP; - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (wrq->disabled) { - ps = false; - timeout = 0; - goto set; - } - - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitely state all */ - ps = true; - break; - default: /* Otherwise we ignore */ - return -EINVAL; - } - - if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) - return -EINVAL; - - if (wrq->flags & IW_POWER_TIMEOUT) - timeout = wrq->value / 1000; - - set: - if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout) - return 0; - - sdata->u.mgd.powersave = ps; - conf->dynamic_ps_timeout = timeout; - - if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); - - ieee80211_recalc_ps(local, -1); - - return 0; -} - -static int ieee80211_ioctl_giwpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - wrqu->power.disabled = !sdata->u.mgd.powersave; - - return 0; -} - -static int ieee80211_ioctl_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_WPA_ENABLED: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_CIPHER_GROUP_MGMT: - break; - case IW_AUTH_CIPHER_PAIRWISE: - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - if (data->value & (IW_AUTH_CIPHER_WEP40 | - IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP)) - sdata->u.mgd.flags |= - IEEE80211_STA_TKIP_WEP_USED; - else - sdata->u.mgd.flags &= - ~IEEE80211_STA_TKIP_WEP_USED; - } - break; - case IW_AUTH_DROP_UNENCRYPTED: - sdata->drop_unencrypted = !!data->value; - break; - case IW_AUTH_PRIVACY_INVOKED: - if (sdata->vif.type != NL80211_IFTYPE_STATION) - ret = -EINVAL; - else { - sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; - /* - * Privacy invoked by wpa_supplicant, store the - * value and allow associating to a protected - * network without having a key up front. - */ - if (data->value) - sdata->u.mgd.flags |= - IEEE80211_STA_PRIVACY_INVOKED; - } - break; - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sdata->u.mgd.auth_algs = data->value; - else - ret = -EOPNOTSUPP; - break; - case IW_AUTH_MFP: - if (!(sdata->local->hw.flags & IEEE80211_HW_MFP_CAPABLE)) { - ret = -EOPNOTSUPP; - break; - } - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - switch (data->value) { - case IW_AUTH_MFP_DISABLED: - sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED; - break; - case IW_AUTH_MFP_OPTIONAL: - sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL; - break; - case IW_AUTH_MFP_REQUIRED: - sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED; - break; - default: - ret = -EINVAL; - } - } else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; -} - -/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ -static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct iw_statistics *wstats = &local->wstats; - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct sta_info *sta = NULL; - - rcu_read_lock(); - if (sdata->vif.type == NL80211_IFTYPE_STATION) - sta = sta_info_get(local, sdata->u.mgd.bssid); - - if (!sta) { - wstats->discard.fragment = 0; - wstats->discard.misc = 0; - wstats->qual.qual = 0; - wstats->qual.level = 0; - wstats->qual.noise = 0; - wstats->qual.updated = IW_QUAL_ALL_INVALID; - } else { - wstats->qual.updated = 0; - /* - * mirror what cfg80211 does for iwrange/scan results, - * otherwise userspace gets confused. - */ - if (local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC | - IEEE80211_HW_SIGNAL_DBM)) { - wstats->qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats->qual.updated |= IW_QUAL_QUAL_UPDATED; - } else { - wstats->qual.updated |= IW_QUAL_LEVEL_INVALID; - wstats->qual.updated |= IW_QUAL_QUAL_INVALID; - } - - if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { - wstats->qual.level = sta->last_signal; - wstats->qual.qual = sta->last_signal; - } else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { - int sig = sta->last_signal; - - wstats->qual.updated |= IW_QUAL_DBM; - wstats->qual.level = sig; - if (sig < -110) - sig = -110; - else if (sig > -40) - sig = -40; - wstats->qual.qual = sig + 110; - } - - if (local->hw.flags & IEEE80211_HW_NOISE_DBM) { - /* - * This assumes that if driver reports noise, it also - * reports signal in dBm. - */ - wstats->qual.noise = sta->last_noise; - wstats->qual.updated |= IW_QUAL_NOISE_UPDATED; - } else { - wstats->qual.updated |= IW_QUAL_NOISE_INVALID; - } - } + return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); - rcu_read_unlock(); - - return wstats; -} + if (sdata->vif.type == NL80211_IFTYPE_WDS) + return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); -static int ieee80211_ioctl_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret = 0; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_80211_AUTH_ALG: - if (sdata->vif.type == NL80211_IFTYPE_STATION) - data->value = sdata->u.mgd.auth_algs; - else - ret = -EOPNOTSUPP; - break; - default: - ret = -EOPNOTSUPP; - break; - } - return ret; + return -EOPNOTSUPP; } @@ -599,8 +201,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) NULL, /* SIOCGIWNICKN */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ - (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) cfg80211_wext_siwrate, /* SIOCSIWRATE */ + (iw_handler) cfg80211_wext_giwrate, /* SIOCGIWRATE */ (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ @@ -611,14 +213,14 @@ static const iw_handler ieee80211_handler[] = (iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */ (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */ (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */ - (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */ - (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ + (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */ + (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ - (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) cfg80211_wext_siwgenie, /* SIOCSIWGENIE */ (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ - (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) cfg80211_wext_siwauth, /* SIOCSIWAUTH */ + (iw_handler) cfg80211_wext_giwauth, /* SIOCGIWAUTH */ (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */ (iw_handler) NULL, /* SIOCGIWENCODEEXT */ (iw_handler) NULL, /* SIOCSIWPMKSA */ @@ -629,5 +231,5 @@ const struct iw_handler_def ieee80211_iw_handler_def = { .num_standard = ARRAY_SIZE(ieee80211_handler), .standard = (iw_handler *) ieee80211_handler, - .get_wireless_stats = ieee80211_get_wireless_stats, + .get_wireless_stats = cfg80211_wireless_stats, }; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index dcfae8884b86..70778694877b 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -122,7 +122,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx, - (void *) skb->data, NULL); + (void *) skb->data, NULL, + GFP_ATOMIC); return RX_DROP_UNUSABLE; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 7c1333c67ff3..2d24d81474ce 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3231,7 +3231,7 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) } genlmsg_end(msg, reply); - ret = genlmsg_unicast(msg, info->snd_pid); + ret = genlmsg_reply(msg, info); goto out; nla_put_failure: diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2936fa3b6dc8..da3163d15ef0 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -83,6 +83,11 @@ struct netlink_sock { struct module *module; }; +struct listeners_rcu_head { + struct rcu_head rcu_head; + void *ptr; +}; + #define NETLINK_KERNEL_SOCKET 0x1 #define NETLINK_RECV_PKTINFO 0x2 #define NETLINK_BROADCAST_SEND_ERROR 0x4 @@ -1356,7 +1361,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, struct netlink_sock *nlk = nlk_sk(sk); int noblock = flags&MSG_DONTWAIT; size_t copied; - struct sk_buff *skb; + struct sk_buff *skb, *frag __maybe_unused = NULL; int err; if (flags&MSG_OOB) @@ -1368,6 +1373,35 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, if (skb == NULL) goto out; +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES + if (unlikely(skb_shinfo(skb)->frag_list)) { + bool need_compat = !!(flags & MSG_CMSG_COMPAT); + + /* + * If this skb has a frag_list, then here that means that + * we will have to use the frag_list skb for compat tasks + * and the regular skb for non-compat tasks. + * + * The skb might (and likely will) be cloned, so we can't + * just reset frag_list and go on with things -- we need to + * keep that. For the compat case that's easy -- simply get + * a reference to the compat skb and free the regular one + * including the frag. For the non-compat case, we need to + * avoid sending the frag to the user -- so assign NULL but + * restore it below before freeing the skb. + */ + if (need_compat) { + struct sk_buff *compskb = skb_shinfo(skb)->frag_list; + skb_get(compskb); + kfree_skb(skb); + skb = compskb; + } else { + frag = skb_shinfo(skb)->frag_list; + skb_shinfo(skb)->frag_list = NULL; + } + } +#endif + msg->msg_namelen = 0; copied = skb->len; @@ -1398,6 +1432,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, siocb->scm->creds = *NETLINK_CREDS(skb); if (flags & MSG_TRUNC) copied = skb->len; + +#ifdef CONFIG_COMPAT_NETLINK_MESSAGES + skb_shinfo(skb)->frag_list = frag; +#endif + skb_free_datagram(sk, skb); if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) @@ -1453,7 +1492,8 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, if (groups < 32) groups = 32; - listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL); + listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head), + GFP_KERNEL); if (!listeners) goto out_sock_release; @@ -1501,6 +1541,14 @@ netlink_kernel_release(struct sock *sk) EXPORT_SYMBOL(netlink_kernel_release); +static void netlink_free_old_listeners(struct rcu_head *rcu_head) +{ + struct listeners_rcu_head *lrh; + + lrh = container_of(rcu_head, struct listeners_rcu_head, rcu_head); + kfree(lrh->ptr); +} + /** * netlink_change_ngroups - change number of multicast groups * @@ -1516,6 +1564,7 @@ EXPORT_SYMBOL(netlink_kernel_release); int netlink_change_ngroups(struct sock *sk, unsigned int groups) { unsigned long *listeners, *old = NULL; + struct listeners_rcu_head *old_rcu_head; struct netlink_table *tbl = &nl_table[sk->sk_protocol]; int err = 0; @@ -1524,7 +1573,9 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) netlink_table_grab(); if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { - listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC); + listeners = kzalloc(NLGRPSZ(groups) + + sizeof(struct listeners_rcu_head), + GFP_ATOMIC); if (!listeners) { err = -ENOMEM; goto out_ungrab; @@ -1532,16 +1583,24 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups) old = tbl->listeners; memcpy(listeners, old, NLGRPSZ(tbl->groups)); rcu_assign_pointer(tbl->listeners, listeners); + /* + * Free the old memory after an RCU grace period so we + * don't leak it. We use call_rcu() here in order to be + * able to call this function from atomic contexts. The + * allocation of this memory will have reserved enough + * space for struct listeners_rcu_head at the end. + */ + old_rcu_head = (void *)(tbl->listeners + + NLGRPLONGS(tbl->groups)); + old_rcu_head->ptr = old; + call_rcu(&old_rcu_head->rcu_head, netlink_free_old_listeners); } tbl->groups = groups; out_ungrab: netlink_table_ungrab(); - synchronize_rcu(); - kfree(old); return err; } -EXPORT_SYMBOL(netlink_change_ngroups); /** * netlink_clear_multicast_users - kick off multicast listeners @@ -1564,7 +1623,6 @@ void netlink_clear_multicast_users(struct sock *ksk, unsigned int group) netlink_table_ungrab(); } -EXPORT_SYMBOL(netlink_clear_multicast_users); void netlink_set_nonroot(int protocol, unsigned int flags) { diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index eed4c6a8afc0..575c64341508 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -18,8 +18,6 @@ #include <net/sock.h> #include <net/genetlink.h> -struct sock *genl_sock = NULL; - static DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ static inline void genl_lock(void) @@ -175,10 +173,31 @@ int genl_register_mc_group(struct genl_family *family, mc_groups_longs++; } - err = netlink_change_ngroups(genl_sock, - mc_groups_longs * BITS_PER_LONG); - if (err) - goto out; + if (family->netnsok) { + struct net *net; + + rcu_read_lock(); + for_each_net_rcu(net) { + err = netlink_change_ngroups(net->genl_sock, + mc_groups_longs * BITS_PER_LONG); + if (err) { + /* + * No need to roll back, can only fail if + * memory allocation fails and then the + * number of _possible_ groups has been + * increased on some sockets which is ok. + */ + rcu_read_unlock(); + goto out; + } + } + rcu_read_unlock(); + } else { + err = netlink_change_ngroups(init_net.genl_sock, + mc_groups_longs * BITS_PER_LONG); + if (err) + goto out; + } grp->id = id; set_bit(id, mc_groups); @@ -195,8 +214,14 @@ EXPORT_SYMBOL(genl_register_mc_group); static void __genl_unregister_mc_group(struct genl_family *family, struct genl_multicast_group *grp) { + struct net *net; BUG_ON(grp->family != family); - netlink_clear_multicast_users(genl_sock, grp->id); + + rcu_read_lock(); + for_each_net_rcu(net) + netlink_clear_multicast_users(net->genl_sock, grp->id); + rcu_read_unlock(); + clear_bit(grp->id, mc_groups); list_del(&grp->list); genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, grp); @@ -467,6 +492,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { struct genl_ops *ops; struct genl_family *family; + struct net *net = sock_net(skb->sk); struct genl_info info; struct genlmsghdr *hdr = nlmsg_data(nlh); int hdrlen, err; @@ -475,6 +501,10 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (family == NULL) return -ENOENT; + /* this family doesn't exist in this netns */ + if (!family->netnsok && !net_eq(net, &init_net)) + return -ENOENT; + hdrlen = GENL_HDRLEN + family->hdrsize; if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) return -EINVAL; @@ -492,7 +522,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) return -EOPNOTSUPP; genl_unlock(); - err = netlink_dump_start(genl_sock, skb, nlh, + err = netlink_dump_start(net->genl_sock, skb, nlh, ops->dumpit, ops->done); genl_lock(); return err; @@ -514,6 +544,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = family->attrbuf; + genl_info_net_set(&info, net); return ops->doit(skb, &info); } @@ -534,6 +565,7 @@ static struct genl_family genl_ctrl = { .name = "nlctrl", .version = 0x2, .maxattr = CTRL_ATTR_MAX, + .netnsok = true, }; static int ctrl_fill_info(struct genl_family *family, u32 pid, u32 seq, @@ -650,6 +682,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) int i, n = 0; struct genl_family *rt; + struct net *net = sock_net(skb->sk); int chains_to_skip = cb->args[0]; int fams_to_skip = cb->args[1]; @@ -658,6 +691,8 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) continue; n = 0; list_for_each_entry(rt, genl_family_chain(i), family_list) { + if (!rt->netnsok && !net_eq(net, &init_net)) + continue; if (++n < fams_to_skip) continue; if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).pid, @@ -729,6 +764,7 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) if (info->attrs[CTRL_ATTR_FAMILY_ID]) { u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); res = genl_family_find_byid(id); + err = -ENOENT; } if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { @@ -736,49 +772,61 @@ static int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); res = genl_family_find_byname(name); + err = -ENOENT; } - if (res == NULL) { - err = -ENOENT; - goto errout; + if (res == NULL) + return err; + + if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { + /* family doesn't exist here */ + return -ENOENT; } msg = ctrl_build_family_msg(res, info->snd_pid, info->snd_seq, CTRL_CMD_NEWFAMILY); - if (IS_ERR(msg)) { - err = PTR_ERR(msg); - goto errout; - } + if (IS_ERR(msg)) + return PTR_ERR(msg); - err = genlmsg_reply(msg, info); -errout: - return err; + return genlmsg_reply(msg, info); } static int genl_ctrl_event(int event, void *data) { struct sk_buff *msg; + struct genl_family *family; + struct genl_multicast_group *grp; - if (genl_sock == NULL) + /* genl is still initialising */ + if (!init_net.genl_sock) return 0; switch (event) { case CTRL_CMD_NEWFAMILY: case CTRL_CMD_DELFAMILY: - msg = ctrl_build_family_msg(data, 0, 0, event); - if (IS_ERR(msg)) - return PTR_ERR(msg); - - genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); + family = data; + msg = ctrl_build_family_msg(family, 0, 0, event); break; case CTRL_CMD_NEWMCAST_GRP: case CTRL_CMD_DELMCAST_GRP: + grp = data; + family = grp->family; msg = ctrl_build_mcgrp_msg(data, 0, 0, event); - if (IS_ERR(msg)) - return PTR_ERR(msg); - - genlmsg_multicast(msg, 0, GENL_ID_CTRL, GFP_KERNEL); break; + default: + return -EINVAL; + } + + if (IS_ERR(msg)) + return PTR_ERR(msg); + + if (!family->netnsok) { + genlmsg_multicast_netns(&init_net, msg, 0, + GENL_ID_CTRL, GFP_KERNEL); + } else { + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, GENL_ID_CTRL, GFP_ATOMIC); + rcu_read_unlock(); } return 0; @@ -795,6 +843,33 @@ static struct genl_multicast_group notify_grp = { .name = "notify", }; +static int __net_init genl_pernet_init(struct net *net) +{ + /* we'll bump the group number right afterwards */ + net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0, + genl_rcv, &genl_mutex, + THIS_MODULE); + + if (!net->genl_sock && net_eq(net, &init_net)) + panic("GENL: Cannot initialize generic netlink\n"); + + if (!net->genl_sock) + return -ENOMEM; + + return 0; +} + +static void __net_exit genl_pernet_exit(struct net *net) +{ + netlink_kernel_release(net->genl_sock); + net->genl_sock = NULL; +} + +static struct pernet_operations genl_pernet_ops = { + .init = genl_pernet_init, + .exit = genl_pernet_exit, +}; + static int __init genl_init(void) { int i, err; @@ -804,36 +879,67 @@ static int __init genl_init(void) err = genl_register_family(&genl_ctrl); if (err < 0) - goto errout; + goto problem; err = genl_register_ops(&genl_ctrl, &genl_ctrl_ops); if (err < 0) - goto errout_register; + goto problem; netlink_set_nonroot(NETLINK_GENERIC, NL_NONROOT_RECV); - /* we'll bump the group number right afterwards */ - genl_sock = netlink_kernel_create(&init_net, NETLINK_GENERIC, 0, - genl_rcv, &genl_mutex, THIS_MODULE); - if (genl_sock == NULL) - panic("GENL: Cannot initialize generic netlink\n"); + err = register_pernet_subsys(&genl_pernet_ops); + if (err) + goto problem; err = genl_register_mc_group(&genl_ctrl, ¬ify_grp); if (err < 0) - goto errout_register; + goto problem; return 0; -errout_register: - genl_unregister_family(&genl_ctrl); -errout: +problem: panic("GENL: Cannot register controller: %d\n", err); } subsys_initcall(genl_init); -EXPORT_SYMBOL(genl_sock); EXPORT_SYMBOL(genl_register_ops); EXPORT_SYMBOL(genl_unregister_ops); EXPORT_SYMBOL(genl_register_family); EXPORT_SYMBOL(genl_unregister_family); + +static int genlmsg_mcast(struct sk_buff *skb, u32 pid, unsigned long group, + gfp_t flags) +{ + struct sk_buff *tmp; + struct net *net, *prev = NULL; + int err; + + for_each_net_rcu(net) { + if (prev) { + tmp = skb_clone(skb, flags); + if (!tmp) { + err = -ENOMEM; + goto error; + } + err = nlmsg_multicast(prev->genl_sock, tmp, + pid, group, flags); + if (err) + goto error; + } + + prev = net; + } + + return nlmsg_multicast(prev->genl_sock, skb, pid, group, flags); + error: + kfree_skb(skb); + return err; +} + +int genlmsg_multicast_allns(struct sk_buff *skb, u32 pid, unsigned int group, + gfp_t flags) +{ + return genlmsg_mcast(skb, pid, group, flags); +} +EXPORT_SYMBOL(genlmsg_multicast_allns); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 351372463fed..c7b7838a0519 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -177,13 +177,13 @@ static int nr_xmit(struct sk_buff *skb, struct net_device *dev) if (!nr_route_frame(skb, NULL)) { kfree_skb(skb); stats->tx_errors++; - return 0; + return NETDEV_TX_OK; } stats->tx_packets++; stats->tx_bytes += len; - return 0; + return NETDEV_TX_OK; } static const struct header_ops nr_header_ops = { diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index 480839dfc560..4667af51ed71 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -195,7 +195,7 @@ static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) break; default: dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } skb_orphan(skb); @@ -215,7 +215,7 @@ static int gprs_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); if (pep_writeable(sk)) netif_wake_queue(dev); - return 0; + return NETDEV_TX_OK; } static int gprs_set_mtu(struct net_device *dev, int new_mtu) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 79693fe2001e..47497c97c8d9 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1076,10 +1076,16 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf, struct rfkill_event ev; /* we don't need the 'hard' variable but accept it */ - if (count < sizeof(ev) - 1) + if (count < RFKILL_EVENT_SIZE_V1 - 1) return -EINVAL; - if (copy_from_user(&ev, buf, sizeof(ev) - 1)) + /* + * Copy as much data as we can accept into our 'ev' buffer, + * but tell userspace how much we've copied so it can determine + * our API version even in a write() call, if it cares. + */ + count = min(count, sizeof(ev)); + if (copy_from_user(&ev, buf, count)) return -EFAULT; if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL) diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 389d6e0d7740..c711e2edf5ce 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -141,7 +141,7 @@ static int rose_xmit(struct sk_buff *skb, struct net_device *dev) } dev_kfree_skb(skb); stats->tx_errors++; - return 0; + return NETDEV_TX_OK; } static const struct header_ops rose_header_ops = { diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 9c002b6e0533..12434b6c2042 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -314,7 +314,7 @@ restart: netif_wake_queue(dev); txq->tx_packets++; txq->tx_bytes += length; - return 0; + return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); } @@ -323,7 +323,7 @@ restart: break; case 1: master->slaves = NEXT_SLAVE(q); - return 0; + return NETDEV_TX_OK; default: nores = 1; break; @@ -345,7 +345,7 @@ restart: drop: txq->tx_dropped++; dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 3c57005e44d1..7bda8e3d1398 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -62,7 +62,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; - genlmsg_unicast(rep_buf, NETLINK_CB(skb).pid); + genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).pid); } return 0; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 1848693ebb82..e8254e809b79 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1748,6 +1748,12 @@ static int getsockopt(struct socket *sock, value = jiffies_to_msecs(sk->sk_rcvtimeo); /* no need to set "res", since already 0 at this point */ break; + case TIPC_NODE_RECVQ_DEPTH: + value = (u32)atomic_read(&tipc_queue_size); + break; + case TIPC_SOCK_RECVQ_DEPTH: + value = skb_queue_len(&sk->sk_receive_queue); + break; default: res = -EINVAL; } diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 4428dd5e911d..c6031d5b135f 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -2,6 +2,21 @@ config CFG80211 tristate "Improved wireless configuration API" depends on RFKILL || !RFKILL +config NL80211_TESTMODE + bool "nl80211 testmode command" + depends on CFG80211 + help + The nl80211 testmode command helps implementing things like + factory calibration or validation tools for wireless chips. + + Select this option ONLY for kernels that are specifically + built for such purposes. + + Debugging tools that are supposed to end up in the hands of + users should better be implemented with debugfs. + + Say N. + config CFG80211_REG_DEBUG bool "cfg80211 regulatory debugging" depends on CFG80211 @@ -11,6 +26,22 @@ config CFG80211_REG_DEBUG If unsure, say N. +config CFG80211_DEFAULT_PS + bool "enable powersave by default" + depends on CFG80211 + default y + help + This option enables powersave mode by default. + + If this causes your applications to misbehave you should fix your + applications instead -- they need to register their network + latency requirement, see Documentation/power/pm_qos_interface.txt. + +config CFG80211_DEFAULT_PS_VALUE + int + default 1 if CFG80211_DEFAULT_PS + default 0 + config CFG80211_DEBUGFS bool "cfg80211 DebugFS entries" depends on CFG80211 && DEBUG_FS @@ -35,19 +66,13 @@ config WIRELESS_OLD_REGULATORY config WIRELESS_EXT bool "Wireless extensions" - default n + default y ---help--- This option enables the legacy wireless extensions (wireless network interface configuration via ioctls.) - Wireless extensions will be replaced by cfg80211 and - will be required only by legacy drivers that implement - wireless extension handlers. This option does not - affect the wireless-extension backward compatibility - code in cfg80211. - - Say N (if you can) unless you know you need wireless - extensions for external modules. + Say Y unless you've upgraded all your userspace to use + nl80211 instead of wireless extensions. config WIRELESS_EXT_SYSFS bool "Wireless extensions sysfs files" diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f78c4832a9ca..d74cc77fa57a 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,8 +5,8 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o +cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o wext-sme.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/wireless/core.c b/net/wireless/core.c index d5850292b3df..1a78b3c70cf2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -30,10 +30,10 @@ MODULE_DESCRIPTION("wireless configuration support"); /* RCU might be appropriate here since we usually * only read the list, and that can happen quite * often because we need to do it for each command */ -LIST_HEAD(cfg80211_drv_list); +LIST_HEAD(cfg80211_rdev_list); /* - * This is used to protect the cfg80211_drv_list, cfg80211_regdomain, + * This is used to protect the cfg80211_rdev_list, cfg80211_regdomain, * country_ie_regdomain, the reg_beacon_list and the the last regulatory * request receipt (last_request). */ @@ -43,18 +43,18 @@ DEFINE_MUTEX(cfg80211_mutex); static struct dentry *ieee80211_debugfs_dir; /* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { - struct cfg80211_registered_device *result = NULL, *drv; + struct cfg80211_registered_device *result = NULL, *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - list_for_each_entry(drv, &cfg80211_drv_list, list) { - if (drv->wiphy_idx == wiphy_idx) { - result = drv; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy_idx == wiphy_idx) { + result = rdev; break; } } @@ -64,32 +64,32 @@ struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx) int get_wiphy_idx(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy) return WIPHY_IDX_STALE; - drv = wiphy_to_dev(wiphy); - return drv->wiphy_idx; + rdev = wiphy_to_dev(wiphy); + return rdev->wiphy_idx; } -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; if (!wiphy_idx_valid(wiphy_idx)) return NULL; assert_cfg80211_lock(); - drv = cfg80211_drv_by_wiphy_idx(wiphy_idx); - if (!drv) + rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); + if (!rdev) return NULL; - return &drv->wiphy; + return &rdev->wiphy; } /* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info) +__cfg80211_rdev_from_info(struct genl_info *info) { int ifindex; struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; @@ -99,7 +99,7 @@ __cfg80211_drv_from_info(struct genl_info *info) assert_cfg80211_lock(); if (info->attrs[NL80211_ATTR_WIPHY]) { - bywiphyidx = cfg80211_drv_by_wiphy_idx( + bywiphyidx = cfg80211_rdev_by_wiphy_idx( nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); err = -ENODEV; } @@ -134,26 +134,26 @@ __cfg80211_drv_from_info(struct genl_info *info) struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; mutex_lock(&cfg80211_mutex); - drv = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); /* if it is not an error we grab the lock on * it to assure it won't be going away while * we operate on it */ - if (!IS_ERR(drv)) - mutex_lock(&drv->mtx); + if (!IS_ERR(rdev)) + mutex_lock(&rdev->mtx); mutex_unlock(&cfg80211_mutex); - return drv; + return rdev; } struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex) { - struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV); + struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); @@ -161,27 +161,21 @@ cfg80211_get_dev_from_ifindex(int ifindex) if (!dev) goto out; if (dev->ieee80211_ptr) { - drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&drv->mtx); + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); } else - drv = ERR_PTR(-ENODEV); + rdev = ERR_PTR(-ENODEV); dev_put(dev); out: mutex_unlock(&cfg80211_mutex); - return drv; -} - -void cfg80211_put_dev(struct cfg80211_registered_device *drv) -{ - BUG_ON(IS_ERR(drv)); - mutex_unlock(&drv->mtx); + return rdev; } /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, result, digits; assert_cfg80211_lock(); @@ -207,8 +201,8 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; /* Ensure another device does not already have this name. */ - list_for_each_entry(drv, &cfg80211_drv_list, list) - if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) return -EINVAL; result = device_rename(&rdev->wiphy.dev, newname); @@ -230,26 +224,26 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; - drv->ops->rfkill_poll(&drv->wiphy); + rdev->ops->rfkill_poll(&rdev->wiphy); } static int cfg80211_rfkill_set_block(void *data, bool blocked) { - struct cfg80211_registered_device *drv = data; + struct cfg80211_registered_device *rdev = data; struct wireless_dev *wdev; if (!blocked) return 0; rtnl_lock(); - mutex_lock(&drv->devlist_mtx); + mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &drv->netdev_list, list) + list_for_each_entry(wdev, &rdev->netdev_list, list) dev_close(wdev->netdev); - mutex_unlock(&drv->devlist_mtx); + mutex_unlock(&rdev->devlist_mtx); rtnl_unlock(); return 0; @@ -257,10 +251,75 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) static void cfg80211_rfkill_sync_work(struct work_struct *work) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); + cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); +} + +static void cfg80211_process_events(struct wireless_dev *wdev) +{ + struct cfg80211_event *ev; + unsigned long flags; + + spin_lock_irqsave(&wdev->event_lock, flags); + while (!list_empty(&wdev->event_list)) { + ev = list_first_entry(&wdev->event_list, + struct cfg80211_event, list); + list_del(&ev->list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + + wdev_lock(wdev); + switch (ev->type) { + case EVENT_CONNECT_RESULT: + __cfg80211_connect_result( + wdev->netdev, ev->cr.bssid, + ev->cr.req_ie, ev->cr.req_ie_len, + ev->cr.resp_ie, ev->cr.resp_ie_len, + ev->cr.status, + ev->cr.status == WLAN_STATUS_SUCCESS); + break; + case EVENT_ROAMED: + __cfg80211_roamed(wdev, ev->rm.bssid, + ev->rm.req_ie, ev->rm.req_ie_len, + ev->rm.resp_ie, ev->rm.resp_ie_len); + break; + case EVENT_DISCONNECTED: + __cfg80211_disconnected(wdev->netdev, + ev->dc.ie, ev->dc.ie_len, + ev->dc.reason, true); + break; + case EVENT_IBSS_JOINED: + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + break; + } + wdev_unlock(wdev); + + kfree(ev); - drv = container_of(work, struct cfg80211_registered_device, rfkill_sync); - cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill)); + spin_lock_irqsave(&wdev->event_lock, flags); + } + spin_unlock_irqrestore(&wdev->event_lock, flags); +} + +static void cfg80211_event_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + rdev = container_of(work, struct cfg80211_registered_device, + event_work); + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) + cfg80211_process_events(wdev); + + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); } /* exported functions */ @@ -269,76 +328,84 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { static int wiphy_counter; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int alloc_size; - WARN_ON(!ops->add_key && ops->del_key); - WARN_ON(ops->add_key && !ops->del_key); + WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); + WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); + WARN_ON(ops->connect && !ops->disconnect); + WARN_ON(ops->join_ibss && !ops->leave_ibss); + WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); + WARN_ON(ops->add_station && !ops->del_station); + WARN_ON(ops->add_mpath && !ops->del_mpath); - alloc_size = sizeof(*drv) + sizeof_priv; + alloc_size = sizeof(*rdev) + sizeof_priv; - drv = kzalloc(alloc_size, GFP_KERNEL); - if (!drv) + rdev = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev) return NULL; - drv->ops = ops; + rdev->ops = ops; mutex_lock(&cfg80211_mutex); - drv->wiphy_idx = wiphy_counter++; + rdev->wiphy_idx = wiphy_counter++; - if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) { + if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { wiphy_counter--; mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ - kfree(drv); + kfree(rdev); return NULL; } mutex_unlock(&cfg80211_mutex); /* give it a proper name */ - dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx); - - mutex_init(&drv->mtx); - mutex_init(&drv->devlist_mtx); - INIT_LIST_HEAD(&drv->netdev_list); - spin_lock_init(&drv->bss_lock); - INIT_LIST_HEAD(&drv->bss_list); - - device_initialize(&drv->wiphy.dev); - drv->wiphy.dev.class = &ieee80211_class; - drv->wiphy.dev.platform_data = drv; - - drv->rfkill_ops.set_block = cfg80211_rfkill_set_block; - drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev), - &drv->wiphy.dev, RFKILL_TYPE_WLAN, - &drv->rfkill_ops, drv); - - if (!drv->rfkill) { - kfree(drv); + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + + mutex_init(&rdev->mtx); + mutex_init(&rdev->devlist_mtx); + INIT_LIST_HEAD(&rdev->netdev_list); + spin_lock_init(&rdev->bss_lock); + INIT_LIST_HEAD(&rdev->bss_list); + INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); + + device_initialize(&rdev->wiphy.dev); + rdev->wiphy.dev.class = &ieee80211_class; + rdev->wiphy.dev.platform_data = rdev; + + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; + rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), + &rdev->wiphy.dev, RFKILL_TYPE_WLAN, + &rdev->rfkill_ops, rdev); + + if (!rdev->rfkill) { + kfree(rdev); return NULL; } - INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); + INIT_WORK(&rdev->conn_work, cfg80211_conn_work); + INIT_WORK(&rdev->event_work, cfg80211_event_work); /* * Initialize wiphy parameters to IEEE 802.11 MIB default values. * Fragmentation and RTS threshold are disabled by default with the * special -1 value. */ - drv->wiphy.retry_short = 7; - drv->wiphy.retry_long = 4; - drv->wiphy.frag_threshold = (u32) -1; - drv->wiphy.rts_threshold = (u32) -1; + rdev->wiphy.retry_short = 7; + rdev->wiphy.retry_long = 4; + rdev->wiphy.frag_threshold = (u32) -1; + rdev->wiphy.rts_threshold = (u32) -1; - return &drv->wiphy; + return &rdev->wiphy; } EXPORT_SYMBOL(wiphy_new); int wiphy_register(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); int res; enum ieee80211_band band; struct ieee80211_supported_band *sband; @@ -346,9 +413,6 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; - if (WARN_ON(wiphy->max_scan_ssids < 1)) - return -EINVAL; - /* sanity check ifmodes */ WARN_ON(!ifmodes); ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; @@ -395,11 +459,11 @@ int wiphy_register(struct wiphy *wiphy) /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); - res = device_add(&drv->wiphy.dev); + res = device_add(&rdev->wiphy.dev); if (res) return res; - res = rfkill_register(drv->rfkill); + res = rfkill_register(rdev->rfkill); if (res) goto out_rm_dev; @@ -408,16 +472,16 @@ int wiphy_register(struct wiphy *wiphy) /* set up regulatory info */ wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); - list_add(&drv->list, &cfg80211_drv_list); + list_add(&rdev->list, &cfg80211_rdev_list); mutex_unlock(&cfg80211_mutex); /* add to debugfs */ - drv->wiphy.debugfsdir = - debugfs_create_dir(wiphy_name(&drv->wiphy), + rdev->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&rdev->wiphy), ieee80211_debugfs_dir); - if (IS_ERR(drv->wiphy.debugfsdir)) - drv->wiphy.debugfsdir = NULL; + if (IS_ERR(rdev->wiphy.debugfsdir)) + rdev->wiphy.debugfsdir = NULL; if (wiphy->custom_regulatory) { struct regulatory_request request; @@ -430,48 +494,48 @@ int wiphy_register(struct wiphy *wiphy) nl80211_send_reg_change_event(&request); } - cfg80211_debugfs_drv_add(drv); + cfg80211_debugfs_rdev_add(rdev); return 0; out_rm_dev: - device_del(&drv->wiphy.dev); + device_del(&rdev->wiphy.dev); return res; } EXPORT_SYMBOL(wiphy_register); void wiphy_rfkill_start_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (!drv->ops->rfkill_poll) + if (!rdev->ops->rfkill_poll) return; - drv->rfkill_ops.poll = cfg80211_rfkill_poll; - rfkill_resume_polling(drv->rfkill); + rdev->rfkill_ops.poll = cfg80211_rfkill_poll; + rfkill_resume_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_start_polling); void wiphy_rfkill_stop_polling(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_pause_polling(drv->rfkill); + rfkill_pause_polling(rdev->rfkill); } EXPORT_SYMBOL(wiphy_rfkill_stop_polling); void wiphy_unregister(struct wiphy *wiphy) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rfkill_unregister(drv->rfkill); + rfkill_unregister(rdev->rfkill); /* protect the device list */ mutex_lock(&cfg80211_mutex); - BUG_ON(!list_empty(&drv->netdev_list)); + BUG_ON(!list_empty(&rdev->netdev_list)); /* - * Try to grab drv->mtx. If a command is still in progress, + * Try to grab rdev->mtx. If a command is still in progress, * hopefully the driver will refuse it since it's tearing * down the device already. We wait for this command to complete * before unlinking the item from the list. @@ -480,33 +544,38 @@ void wiphy_unregister(struct wiphy *wiphy) * get to lock contention here if userspace issues a command * that identified the hardware by wiphy index. */ - mutex_lock(&drv->mtx); + mutex_lock(&rdev->mtx); /* unlock again before freeing */ - mutex_unlock(&drv->mtx); + mutex_unlock(&rdev->mtx); - cfg80211_debugfs_drv_del(drv); + cancel_work_sync(&rdev->conn_work); + cancel_work_sync(&rdev->scan_done_wk); + kfree(rdev->scan_req); + flush_work(&rdev->event_work); + + cfg80211_debugfs_rdev_del(rdev); /* If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint */ reg_device_remove(wiphy); - list_del(&drv->list); - device_del(&drv->wiphy.dev); - debugfs_remove(drv->wiphy.debugfsdir); + list_del(&rdev->list); + device_del(&rdev->wiphy.dev); + debugfs_remove(rdev->wiphy.debugfsdir); mutex_unlock(&cfg80211_mutex); } EXPORT_SYMBOL(wiphy_unregister); -void cfg80211_dev_free(struct cfg80211_registered_device *drv) +void cfg80211_dev_free(struct cfg80211_registered_device *rdev) { struct cfg80211_internal_bss *scan, *tmp; - rfkill_destroy(drv->rfkill); - mutex_destroy(&drv->mtx); - mutex_destroy(&drv->devlist_mtx); - list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) + rfkill_destroy(rdev->rfkill); + mutex_destroy(&rdev->mtx); + mutex_destroy(&rdev->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); - kfree(drv); + kfree(rdev); } void wiphy_free(struct wiphy *wiphy) @@ -517,10 +586,10 @@ EXPORT_SYMBOL(wiphy_free); void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) { - struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - if (rfkill_set_hw_state(drv->rfkill, blocked)) - schedule_work(&drv->rfkill_sync); + if (rfkill_set_hw_state(rdev->rfkill, blocked)) + schedule_work(&rdev->rfkill_sync); } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); @@ -529,56 +598,102 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, void *ndev) { struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; - if (!dev->ieee80211_ptr) + if (!wdev) return NOTIFY_DONE; - rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + rdev = wiphy_to_dev(wdev->wiphy); - WARN_ON(dev->ieee80211_ptr->iftype == NL80211_IFTYPE_UNSPECIFIED); + WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); switch (state) { case NETDEV_REGISTER: + mutex_init(&wdev->mtx); + INIT_LIST_HEAD(&wdev->event_list); + spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); - list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list); + list_add(&wdev->list, &rdev->netdev_list); if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { printk(KERN_ERR "wireless: failed to add phy80211 " "symlink to netdev!\n"); } - dev->ieee80211_ptr->netdev = dev; + wdev->netdev = dev; + wdev->sme_state = CFG80211_SME_IDLE; + mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_WIRELESS_EXT - dev->ieee80211_ptr->wext.default_key = -1; - dev->ieee80211_ptr->wext.default_mgmt_key = -1; + wdev->wext.default_key = -1; + wdev->wext.default_mgmt_key = -1; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + wdev->wext.ps = CONFIG_CFG80211_DEFAULT_PS_VALUE; + wdev->wext.ps_timeout = 500; + if (rdev->ops->set_power_mgmt) + if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, + wdev->wext.ps, + wdev->wext.ps_timeout)) { + /* assume this means it's off */ + wdev->wext.ps = false; + } #endif - mutex_unlock(&rdev->devlist_mtx); break; case NETDEV_GOING_DOWN: - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + cfg80211_leave_ibss(rdev, dev, true); + break; + case NL80211_IFTYPE_STATION: + wdev_lock(wdev); +#ifdef CONFIG_WIRELESS_EXT + kfree(wdev->wext.ie); + wdev->wext.ie = NULL; + wdev->wext.ie_len = 0; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; +#endif + __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_mlme_down(rdev, dev); + wdev_unlock(wdev); break; - if (!dev->ieee80211_ptr->ssid_len) + default: break; - cfg80211_leave_ibss(rdev, dev, true); + } break; case NETDEV_UP: #ifdef CONFIG_WIRELESS_EXT - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + if (wdev->wext.ibss.ssid_len) + __cfg80211_join_ibss(rdev, dev, + &wdev->wext.ibss); break; - if (!dev->ieee80211_ptr->wext.ibss.ssid_len) + case NL80211_IFTYPE_STATION: + if (wdev->wext.connect.ssid_len) + __cfg80211_connect(rdev, dev, + &wdev->wext.connect); break; - cfg80211_join_ibss(rdev, dev, &dev->ieee80211_ptr->wext.ibss); - break; + default: + break; + } + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); #endif + break; case NETDEV_UNREGISTER: mutex_lock(&rdev->devlist_mtx); - if (!list_empty(&dev->ieee80211_ptr->list)) { + if (!list_empty(&wdev->list)) { sysfs_remove_link(&dev->dev.kobj, "phy80211"); - list_del_init(&dev->ieee80211_ptr->list); + list_del_init(&wdev->list); } mutex_unlock(&rdev->devlist_mtx); + mutex_destroy(&wdev->mtx); break; case NETDEV_PRE_UP: + if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) + return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); break; diff --git a/net/wireless/core.h b/net/wireless/core.h index bfa340c7abb5..e46cd6eb61d7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -57,6 +57,14 @@ struct cfg80211_registered_device { u32 bss_generation; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ unsigned long suspend_at; + struct work_struct scan_done_wk; + +#ifdef CONFIG_NL80211_TESTMODE + struct genl_info *testmode_info; +#endif + + struct work_struct conn_work; + struct work_struct event_work; #ifdef CONFIG_CFG80211_DEBUGFS /* Debugfs entries */ @@ -89,13 +97,13 @@ bool wiphy_idx_valid(int wiphy_idx) } extern struct mutex cfg80211_mutex; -extern struct list_head cfg80211_drv_list; +extern struct list_head cfg80211_rdev_list; #define assert_cfg80211_lock() WARN_ON(!mutex_is_locked(&cfg80211_mutex)) /* * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_drv_by_wiphy_idx(wiphy_idx) will return NULL + * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL */ #define WIPHY_IDX_STALE -1 @@ -104,17 +112,35 @@ struct cfg80211_internal_bss { struct rb_node rbn; unsigned long ts; struct kref ref; - bool hold, ies_allocated; + atomic_t hold; + bool ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; }; -struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); +static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) +{ + return container_of(pub, struct cfg80211_internal_bss, pub); +} + +static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) +{ + atomic_inc(&bss->hold); +} + +static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) +{ + int r = atomic_dec_return(&bss->hold); + WARN_ON(r < 0); +} + + +struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); struct cfg80211_registered_device * -__cfg80211_drv_from_info(struct genl_info *info); +__cfg80211_rdev_from_info(struct genl_info *info); /* * This function returns a pointer to the driver @@ -122,12 +148,12 @@ __cfg80211_drv_from_info(struct genl_info *info); * If successful, it returns non-NULL and also locks * the driver's mutex! * - * This means that you need to call cfg80211_put_dev() + * This means that you need to call cfg80211_unlock_rdev() * before being allowed to acquire &cfg80211_mutex! * * This is necessary because we need to lock the global * mutex to get an item off the list safely, and then - * we lock the drv mutex so it doesn't go away under us. + * we lock the rdev mutex so it doesn't go away under us. * * We don't want to keep cfg80211_mutex locked * for all the time in order to allow requests on @@ -139,19 +165,84 @@ __cfg80211_drv_from_info(struct genl_info *info); extern struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct genl_info *info); -/* requires cfg80211_drv_mutex to be held! */ +/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(int ifindex); -extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); +static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) +{ + mutex_lock(&rdev->mtx); +} + +static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) +{ + BUG_ON(IS_ERR(rdev) || !rdev); + mutex_unlock(&rdev->mtx); +} + +static inline void wdev_lock(struct wireless_dev *wdev) + __acquires(wdev) +{ + mutex_lock(&wdev->mtx); + __acquire(wdev->mtx); +} + +static inline void wdev_unlock(struct wireless_dev *wdev) + __releases(wdev) +{ + __release(wdev->mtx); + mutex_unlock(&wdev->mtx); +} + +#define ASSERT_RDEV_LOCK(rdev) WARN_ON(!mutex_is_locked(&(rdev)->mtx)); +#define ASSERT_WDEV_LOCK(wdev) WARN_ON(!mutex_is_locked(&(wdev)->mtx)); + +enum cfg80211_event_type { + EVENT_CONNECT_RESULT, + EVENT_ROAMED, + EVENT_DISCONNECTED, + EVENT_IBSS_JOINED, +}; + +struct cfg80211_event { + struct list_head list; + enum cfg80211_event_type type; + + union { + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + u16 status; + } cr; + struct { + u8 bssid[ETH_ALEN]; + const u8 *req_ie; + const u8 *resp_ie; + size_t req_ie_len; + size_t resp_ie_len; + } rm; + struct { + const u8 *ie; + size_t ie_len; + u16 reason; + } dc; + struct { + u8 bssid[ETH_ALEN]; + } ij; + }; +}; + /* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); +extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); -extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, +extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); @@ -163,15 +254,86 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs); /* IBSS */ +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params); int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ibss_params *params); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); + +/* MLME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len); +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt); +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason); +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev); +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev); + +/* SME */ +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect); +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, + bool wextev); +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len); + +void cfg80211_conn_work(struct work_struct *work); /* internal helpers */ int cfg80211_validate_key_settings(struct key_params *params, int key_idx, const u8 *mac_addr); +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, + size_t ie_len, u16 reason, bool from_ap); +void cfg80211_sme_scan_done(struct net_device *dev); +void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); +void cfg80211_sme_disassoc(struct net_device *dev, int idx); +void __cfg80211_scan_done(struct work_struct *wk); #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 679ddfcec1ee..13d93d84f902 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -104,15 +104,15 @@ static const struct file_operations ht40allow_map_ops = { }; #define DEBUGFS_ADD(name) \ - drv->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ - &drv->wiphy, &name## _ops); + rdev->debugfs.name = debugfs_create_file(#name, S_IRUGO, phyd, \ + &rdev->wiphy, &name## _ops); #define DEBUGFS_DEL(name) \ - debugfs_remove(drv->debugfs.name); \ - drv->debugfs.name = NULL; + debugfs_remove(rdev->debugfs.name); \ + rdev->debugfs.name = NULL; -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) { - struct dentry *phyd = drv->wiphy.debugfsdir; + struct dentry *phyd = rdev->wiphy.debugfsdir; DEBUGFS_ADD(rts_threshold); DEBUGFS_ADD(fragmentation_threshold); @@ -121,7 +121,7 @@ void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) DEBUGFS_ADD(ht40allow_map); } -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) { DEBUGFS_DEL(rts_threshold); DEBUGFS_DEL(fragmentation_threshold); diff --git a/net/wireless/debugfs.h b/net/wireless/debugfs.h index c226983ae66b..6419b6d6ce3e 100644 --- a/net/wireless/debugfs.h +++ b/net/wireless/debugfs.h @@ -2,13 +2,13 @@ #define __CFG80211_DEBUGFS_H #ifdef CONFIG_CFG80211_DEBUGFS -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv); -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv); +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev); #else static inline -void cfg80211_debugfs_drv_add(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} static inline -void cfg80211_debugfs_drv_del(struct cfg80211_registered_device *drv) {} +void cfg80211_debugfs_rdev_del(struct cfg80211_registered_device *rdev) {} #endif #endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index a4a1c3498ff2..99ef9364b7e8 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -10,7 +10,7 @@ #include "nl80211.h" -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -24,9 +24,6 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) if (WARN_ON(!wdev->ssid_len)) return; - if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0) - return; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); @@ -36,29 +33,51 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } - cfg80211_hold_bss(bss); - wdev->current_bss = bss; - memcpy(wdev->bssid, bssid, ETH_ALEN); + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); - nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); + nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, + GFP_KERNEL); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } + +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_IBSS_JOINED; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} EXPORT_SYMBOL(cfg80211_ibss_joined); -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params) +int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + if (wdev->ssid_len) return -EALREADY; @@ -76,39 +95,82 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return 0; } -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) +int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_ibss(rdev, dev, params); + wdev_unlock(wdev); + + return err; +} + +static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; + ASSERT_WDEV_LOCK(wdev); + if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); } wdev->current_bss = NULL; wdev->ssid_len = 0; - memset(wdev->bssid, 0, ETH_ALEN); #ifdef CONFIG_WIRELESS_EXT if (!nowext) wdev->wext.ibss.ssid_len = 0; #endif } -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) +void cfg80211_clear_ibss(struct net_device *dev, bool nowext) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_clear_ibss(dev, nowext); + wdev_unlock(wdev); +} + +static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; int err; + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->ssid_len) + return -ENOLINK; + err = rdev->ops->leave_ibss(&rdev->wiphy, dev); if (err) return err; - cfg80211_clear_ibss(dev, nowext); + __cfg80211_clear_ibss(dev, nowext); return 0; } +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ibss(rdev, dev, nowext); + wdev_unlock(wdev); + + return err; +} + #ifdef CONFIG_WIRELESS_EXT static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -185,12 +247,15 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, if (wdev->wext.ibss.channel == chan) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (chan) { wdev->wext.ibss.channel = chan; @@ -216,10 +281,12 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; + wdev_lock(wdev); if (wdev->current_bss) - chan = wdev->current_bss->channel; + chan = wdev->current_bss->pub.channel; else if (wdev->wext.ibss.channel) chan = wdev->wext.ibss.channel; + wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -248,12 +315,15 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) return -EOPNOTSUPP; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; /* iwconfig uses nul termination in SSID.. */ if (len > 0 && ssid[len - 1] == '\0') @@ -280,6 +350,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->flags = 0; + wdev_lock(wdev); if (wdev->ssid_len) { data->flags = 1; data->length = wdev->ssid_len; @@ -289,6 +360,7 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->length = wdev->wext.ibss.ssid_len; memcpy(ssid, wdev->wext.ibss.ssid, data->length); } + wdev_unlock(wdev); return 0; } @@ -326,12 +398,15 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) return 0; - if (wdev->ssid_len) { - err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), - dev, true); - if (err) - return err; - } + wdev_lock(wdev); + err = 0; + if (wdev->ssid_len) + err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), + dev, true); + wdev_unlock(wdev); + + if (err) + return err; if (bssid) { memcpy(wdev->wext.bssid, bssid, ETH_ALEN); @@ -356,12 +431,13 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; - if (wdev->wext.ibss.bssid) { + wdev_lock(wdev); + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); + else memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); - return 0; - } + wdev_unlock(wdev); - memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN); return 0; } /* temporary symbol - mark GPL - in the future the handler won't be */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 42184361a109..1b2ca1fea7a1 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -14,69 +14,600 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_rx_auth(rdev, dev, buf, len); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *bssid = mgmt->bssid; + int i; + u16 status = le16_to_cpu(mgmt->u.auth.status_code); + bool done = false; + + wdev_lock(wdev); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0) { + if (status == WLAN_STATUS_SUCCESS) { + wdev->auth_bsses[i] = wdev->authtry_bsses[i]; + } else { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + } + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + + nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); + cfg80211_sme_rx_auth(dev, buf, len); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + u16 status_code; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_rx_assoc(rdev, dev, buf, len); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u8 *ie = mgmt->u.assoc_resp.variable; + int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + bool done; + + wdev_lock(wdev); + + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); + + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, + status_code, + status_code == WLAN_STATUS_SUCCESS); + + if (status_code == WLAN_STATUS_SUCCESS) { + for (i = 0; wdev->current_bss && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] == wdev->current_bss) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + } + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_deauth(struct net_device *dev, + const u8 *buf, size_t len) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_deauth(rdev, dev, buf, len); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + bool done = false; + + ASSERT_WDEV_LOCK(wdev); + + nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); + + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + done = true; + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + + if (wdev->sme_state == CFG80211_SME_CONNECTED) { + u16 reason_code; + bool from_ap; + + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); + } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { + __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + } +} + + +void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_deauth(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_deauth(dev, buf, len); + wdev_unlock(wdev); + } } EXPORT_SYMBOL(cfg80211_send_deauth); -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) +static void __cfg80211_send_disassoc(struct net_device *dev, + const u8 *buf, size_t len) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_disassoc(rdev, dev, buf, len); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + const u8 *bssid = mgmt->bssid; + int i; + u16 reason_code; + bool from_ap; + bool done = false; + + wdev_lock(wdev); + + nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); + + if (!wdev->sme_state == CFG80211_SME_CONNECTED) + goto out; + + if (wdev->current_bss && + memcmp(wdev->current_bss, bssid, ETH_ALEN) == 0) { + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) + continue; + wdev->auth_bsses[i] = wdev->current_bss; + wdev->current_bss = NULL; + done = true; + cfg80211_sme_disassoc(dev, i); + break; + } + WARN_ON(!done); + } else + WARN_ON(1); + + + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; + __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); + out: + wdev_unlock(wdev); } -EXPORT_SYMBOL(cfg80211_send_disassoc); -static void cfg80211_wext_disconnected(struct net_device *dev) +void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, + void *cookie) { -#ifdef CONFIG_WIRELESS_EXT - union iwreq_data wrqu; - memset(&wrqu, 0, sizeof(wrqu)); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -#endif + struct wireless_dev *wdev = dev->ieee80211_ptr; + + BUG_ON(cookie && wdev != cookie); + + if (cookie) { + /* called within callback */ + __cfg80211_send_disassoc(dev, buf, len); + } else { + wdev_lock(wdev); + __cfg80211_send_disassoc(dev, buf, len); + wdev_unlock(wdev); + } } +EXPORT_SYMBOL(cfg80211_send_disassoc); void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_auth_timeout(rdev, dev, addr); - cfg80211_wext_disconnected(dev); + int i; + bool done = false; + + wdev_lock(wdev); + + nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(wdev->authtry_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_assoc_timeout(rdev, dev, addr); - cfg80211_wext_disconnected(dev); + int i; + bool done = false; + + wdev_lock(wdev); + + nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); + if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + + for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(wdev->auth_bsses[i]->pub.bssid, + addr, ETH_ALEN) == 0) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + done = true; + break; + } + } + + WARN_ON(!done); + + wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, enum nl80211_key_type key_type, int key_id, - const u8 *tsc) + const u8 *tsc, gfp_t gfp) { struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc); +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; + char *buf = kmalloc(128, gfp); + + if (buf) { + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" + "keyid=%d %scast addr=%pM)", key_id, + key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", + addr); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); + } +#endif + + nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); } EXPORT_SYMBOL(cfg80211_michael_mic_failure); + +/* some MLME handling for userspace SME */ +int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_auth_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1, nfree = 0; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->current_bss && + memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) + return -EALREADY; + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, + ETH_ALEN) == 0) + return -EALREADY; + } + + memset(&req, 0, sizeof(req)); + + req.ie = ie; + req.ie_len = ie_len; + req.auth_type = auth_type; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { + slot = i; + nfree++; + } + } + + /* we need one free slot for disassoc and one for this auth */ + if (nfree < 2) { + err = -ENOSPC; + goto out; + } + + wdev->authtry_bsses[slot] = bss; + cfg80211_hold_bss(bss); + + err = rdev->ops->auth(&rdev->wiphy, dev, &req); + if (err) { + wdev->authtry_bsses[slot] = NULL; + cfg80211_unhold_bss(bss); + } + + out: + if (err) + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_assoc_request req; + struct cfg80211_internal_bss *bss; + int i, err, slot = -1; + + ASSERT_WDEV_LOCK(wdev); + + memset(&req, 0, sizeof(req)); + + if (wdev->current_bss) + return -EALREADY; + + req.ie = ie; + req.ie_len = ie_len; + memcpy(&req.crypto, crypt, sizeof(req.crypto)); + req.use_mfp = use_mfp; + req.prev_bssid = prev_bssid; + req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + if (!req.bss) + return -ENOENT; + + bss = bss_from_pub(req.bss); + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (bss == wdev->auth_bsses[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + err = -ENOTCONN; + goto out; + } + + err = rdev->ops->assoc(&rdev->wiphy, dev, &req); + out: + /* still a reference in wdev->auth_bsses[slot] */ + cfg80211_put_bss(req.bss); + return err; +} + +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, const u8 *prev_bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, bool use_mfp, + struct cfg80211_crypto_settings *crypt) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, crypt); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + ASSERT_WDEV_LOCK(wdev); + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (wdev->current_bss && + memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { + req.bss = &wdev->current_bss->pub; + } else for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i] && + memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->auth_bsses[i]->pub; + break; + } + if (wdev->authtry_bsses[i] && + memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { + req.bss = &wdev->authtry_bsses[i]->pub; + break; + } + } + + if (!req.bss) + return -ENOTCONN; + + return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; +} + +static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_disassoc_request req; + + ASSERT_WDEV_LOCK(wdev); + + memset(&req, 0, sizeof(req)); + req.reason_code = reason; + req.ie = ie; + req.ie_len = ie_len; + if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) + req.bss = &wdev->current_bss->pub; + else + return -ENOTCONN; + + return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); +} + +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason); + wdev_unlock(wdev); + + return err; +} + +void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_deauth_request req; + int i; + + ASSERT_WDEV_LOCK(wdev); + + if (!rdev->ops->deauth) + return; + + memset(&req, 0, sizeof(req)); + req.reason_code = WLAN_REASON_DEAUTH_LEAVING; + req.ie = NULL; + req.ie_len = 0; + + if (wdev->current_bss) { + req.bss = &wdev->current_bss->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + } + + for (i = 0; i < MAX_AUTH_BSSES; i++) { + if (wdev->auth_bsses[i]) { + req.bss = &wdev->auth_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->auth_bsses[i]) { + cfg80211_unhold_bss(wdev->auth_bsses[i]); + cfg80211_put_bss(&wdev->auth_bsses[i]->pub); + wdev->auth_bsses[i] = NULL; + } + } + if (wdev->authtry_bsses[i]) { + req.bss = &wdev->authtry_bsses[i]->pub; + rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); + if (wdev->authtry_bsses[i]) { + cfg80211_unhold_bss(wdev->authtry_bsses[i]); + cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); + wdev->authtry_bsses[i] = NULL; + } + } + } +} diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 43bdb1372cae..2a04beba4369 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -29,9 +29,9 @@ static struct genl_family nl80211_fam = { .maxattr = NL80211_ATTR_MAX, }; -/* internal helper: get drv and dev */ -static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, - struct cfg80211_registered_device **drv, +/* internal helper: get rdev and dev */ +static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, + struct cfg80211_registered_device **rdev, struct net_device **dev) { int ifindex; @@ -44,10 +44,10 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs, if (!*dev) return -ENODEV; - *drv = cfg80211_get_dev_from_ifindex(ifindex); - if (IS_ERR(*drv)) { + *rdev = cfg80211_get_dev_from_ifindex(ifindex); + if (IS_ERR(*rdev)) { dev_put(*dev); - return PTR_ERR(*drv); + return PTR_ERR(*rdev); } return 0; @@ -71,6 +71,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, @@ -128,6 +129,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = sizeof(struct nl80211_sta_flag_update), }, [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, + [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, + [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, }; /* IE validation */ @@ -347,6 +351,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(join_ibss, JOIN_IBSS); #undef CMD + + if (dev->ops->connect || dev->ops->auth) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); + } + + if (dev->ops->disconnect || dev->ops->deauth) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); + } + nla_nest_end(msg, nl_cmds); return genlmsg_end(msg, hdr); @@ -363,7 +378,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) struct cfg80211_registered_device *dev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -396,14 +411,14 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) goto out_free; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); out_free: nlmsg_free(msg); out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -445,7 +460,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_drv_from_info(info); + rdev = __cfg80211_rdev_from_info(info); if (IS_ERR(rdev)) { mutex_unlock(&cfg80211_mutex); result = PTR_ERR(rdev); @@ -668,7 +683,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * struct wireless_dev *wdev; mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_drv_list, list) { + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (wp_idx < wp_start) { wp_idx++; continue; @@ -709,7 +724,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); if (err) return err; @@ -722,15 +737,15 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) goto out_free; dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); - return genlmsg_unicast(msg, info->snd_pid); + return genlmsg_reply(msg, info); out_free: nlmsg_free(msg); out_err: dev_put(netdev); - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); return -ENOBUFS; } @@ -765,9 +780,9 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; - int err, ifindex; + int err; enum nl80211_iftype otype, ntype; struct net_device *dev; u32 _flags, *flags = NULL; @@ -777,13 +792,11 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - ifindex = dev->ifindex; otype = ntype = dev->ieee80211_ptr->iftype; - dev_put(dev); if (info->attrs[NL80211_ATTR_IFTYPE]) { ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); @@ -795,8 +808,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } } - if (!drv->ops->change_virtual_intf || - !(drv->wiphy.interface_modes & (1 << ntype))) { + if (!rdev->ops->change_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << ntype))) { err = -EOPNOTSUPP; goto unlock; } @@ -826,21 +839,21 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (change) - err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, + err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, ntype, flags, ¶ms); else err = 0; - dev = __dev_get_by_index(&init_net, ifindex); - WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype)); + WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - if (dev && !err && (ntype != otype)) { + if (!err && (ntype != otype)) { if (otype == NL80211_IFTYPE_ADHOC) cfg80211_clear_ibss(dev, false); } unlock: - cfg80211_put_dev(drv); + dev_put(dev); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -848,7 +861,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct vif_params params; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; @@ -867,14 +880,14 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - drv = cfg80211_get_dev_from_info(info); - if (IS_ERR(drv)) { - err = PTR_ERR(drv); + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); goto unlock_rtnl; } - if (!drv->ops->add_virtual_intf || - !(drv->wiphy.interface_modes & (1 << type))) { + if (!rdev->ops->add_virtual_intf || + !(rdev->wiphy.interface_modes & (1 << type))) { err = -EOPNOTSUPP; goto unlock; } @@ -888,12 +901,12 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - err = drv->ops->add_virtual_intf(&drv->wiphy, + err = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); unlock: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -901,27 +914,27 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int ifindex, err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; ifindex = dev->ifindex; dev_put(dev); - if (!drv->ops->del_virtual_intf) { + if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); unlock_rtnl: rtnl_unlock(); return err; @@ -955,7 +968,7 @@ static void get_key_callback(void *c, struct key_params *params) static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -977,11 +990,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->get_key) { + if (!rdev->ops->get_key) { err = -EOPNOTSUPP; goto out; } @@ -1007,7 +1020,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (mac_addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, + err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr, &cookie, get_key_callback); if (err) @@ -1017,14 +1030,14 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) goto nla_put_failure; genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: err = -ENOBUFS; nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1034,7 +1047,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx; @@ -1059,24 +1072,24 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) - func = drv->ops->set_default_key; + func = rdev->ops->set_default_key; else - func = drv->ops->set_default_mgmt_key; + func = rdev->ops->set_default_mgmt_key; if (!func) { err = -EOPNOTSUPP; goto out; } - err = func(&drv->wiphy, dev, key_idx); + err = func(&rdev->wiphy, dev, key_idx); #ifdef CONFIG_WIRELESS_EXT if (!err) { - if (func == drv->ops->set_default_key) + if (func == rdev->ops->set_default_key) dev->ieee80211_ptr->wext.default_key = key_idx; else dev->ieee80211_ptr->wext.default_mgmt_key = key_idx; @@ -1084,7 +1097,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1095,7 +1108,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err, i; struct net_device *dev; struct key_params params; @@ -1130,27 +1143,27 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - for (i = 0; i < drv->wiphy.n_cipher_suites; i++) - if (params.cipher == drv->wiphy.cipher_suites[i]) + for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) + if (params.cipher == rdev->wiphy.cipher_suites[i]) break; - if (i == drv->wiphy.n_cipher_suites) { + if (i == rdev->wiphy.n_cipher_suites) { err = -EINVAL; goto out; } - if (!drv->ops->add_key) { + if (!rdev->ops->add_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); + err = rdev->ops->add_key(&rdev->wiphy, dev, key_idx, mac_addr, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1160,7 +1173,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 key_idx = 0; @@ -1177,16 +1190,16 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_key) { + if (!rdev->ops->del_key) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); + err = rdev->ops->del_key(&rdev->wiphy, dev, key_idx, mac_addr); #ifdef CONFIG_WIRELESS_EXT if (!err) { @@ -1198,7 +1211,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) #endif out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: @@ -1211,7 +1224,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) { int (*call)(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info); - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct beacon_parameters params; @@ -1222,7 +1235,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1241,10 +1254,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - call = drv->ops->add_beacon; + call = rdev->ops->add_beacon; break; case NL80211_CMD_SET_BEACON: - call = drv->ops->set_beacon; + call = rdev->ops->set_beacon; break; default: WARN_ON(1); @@ -1290,10 +1303,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) goto out; } - err = call(&drv->wiphy, dev, ¶ms); + err = call(&rdev->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1303,17 +1316,17 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->del_beacon) { + if (!rdev->ops->del_beacon) { err = -EOPNOTSUPP; goto out; } @@ -1322,10 +1335,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_beacon(&drv->wiphy, dev); + err = rdev->ops->del_beacon(&rdev->wiphy, dev); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -1559,7 +1572,7 @@ static int nl80211_dump_station(struct sk_buff *skb, cb->args[1] = sta_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -1568,7 +1581,7 @@ static int nl80211_dump_station(struct sk_buff *skb, static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_info sinfo; @@ -1584,16 +1597,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_station) { + if (!rdev->ops->get_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); + err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); if (err) goto out; @@ -1605,13 +1618,13 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) dev, mac_addr, &sinfo) < 0) goto out_free; - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1642,7 +1655,7 @@ static int get_vlan(struct nlattr *vlanattr, static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1684,11 +1697,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1737,17 +1750,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->change_station) { + if (!rdev->ops->change_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1757,7 +1770,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct station_parameters params; @@ -1797,11 +1810,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); + err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); if (err) goto out; @@ -1837,7 +1850,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!drv->ops->add_station) { + if (!rdev->ops->add_station) { err = -EOPNOTSUPP; goto out; } @@ -1847,12 +1860,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); + err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); out: if (params.vlan) dev_put(params.vlan); - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -1862,7 +1875,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *mac_addr = NULL; @@ -1872,7 +1885,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; @@ -1883,15 +1896,15 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) goto out; } - if (!drv->ops->del_station) { + if (!rdev->ops->del_station) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); + err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2022,7 +2035,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, cb->args[1] = path_idx; err = skb->len; out_err: - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_rtnl: rtnl_unlock(); @@ -2031,7 +2044,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb, static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct mpath_info pinfo; @@ -2048,11 +2061,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mpath) { + if (!rdev->ops->get_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2062,7 +2075,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); + err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); if (err) goto out; @@ -2074,13 +2087,13 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) dev, dst, next_hop, &pinfo) < 0) goto out_free; - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; out_free: nlmsg_free(msg); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2090,7 +2103,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2107,11 +2120,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_mpath) { + if (!rdev->ops->change_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2126,10 +2139,10 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2138,7 +2151,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) } static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2155,11 +2168,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->add_mpath) { + if (!rdev->ops->add_mpath) { err = -EOPNOTSUPP; goto out; } @@ -2174,10 +2187,10 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); + err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2187,7 +2200,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; u8 *dst = NULL; @@ -2197,19 +2210,19 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->del_mpath) { + if (!rdev->ops->del_mpath) { err = -EOPNOTSUPP; goto out; } - err = drv->ops->del_mpath(&drv->wiphy, dev, dst); + err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2219,7 +2232,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; int err; struct net_device *dev; struct bss_parameters params; @@ -2248,11 +2261,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->change_bss) { + if (!rdev->ops->change_bss) { err = -EOPNOTSUPP; goto out; } @@ -2262,10 +2275,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); + err = rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2356,7 +2369,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) static int nl80211_get_mesh_params(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct mesh_config cur_params; int err; struct net_device *dev; @@ -2367,17 +2380,17 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->get_mesh_params) { + if (!rdev->ops->get_mesh_params) { err = -EOPNOTSUPP; goto out; } /* Get the mesh params */ - err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); + err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params); if (err) goto out; @@ -2423,7 +2436,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, cur_params.dot11MeshHWMPnetDiameterTraversalTime); nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: @@ -2431,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, err = -EMSGSIZE; out: /* Cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2469,7 +2482,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) { int err; u32 mask; - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct mesh_config cfg; struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; @@ -2484,11 +2497,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - if (!drv->ops->set_mesh_params) { + if (!rdev->ops->set_mesh_params) { err = -EOPNOTSUPP; goto out; } @@ -2533,11 +2546,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) nla_get_u16); /* Apply changes */ - err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); + err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); out: /* cleanup */ - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2611,7 +2624,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) nla_nest_end(msg, nl_reg_rules); genlmsg_end(msg, hdr); - err = genlmsg_unicast(msg, info->snd_pid); + err = genlmsg_reply(msg, info); goto out; nla_put_failure: @@ -2697,16 +2710,41 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return r; } +static int validate_scan_freqs(struct nlattr *freqs) +{ + struct nlattr *attr1, *attr2; + int n_channels = 0, tmp1, tmp2; + + nla_for_each_nested(attr1, freqs, tmp1) { + n_channels++; + /* + * Some hardware has a limited channel list for + * scanning, and it is pretty much nonsensical + * to scan for a channel twice, so disallow that + * and don't require drivers to check that the + * channel list they get isn't longer than what + * they can scan, as long as they can scan all + * the channels they registered at once. + */ + nla_for_each_nested(attr2, freqs, tmp2) + if (attr1 != attr2 && + nla_get_u32(attr1) == nla_get_u32(attr2)) + return 0; + } + + return n_channels; +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_scan_request *request; struct cfg80211_ssid *ssid; struct ieee80211_channel *channel; struct nlattr *attr; struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_channels = 0, i; + int err, tmp, n_ssids = 0, n_channels, i; enum ieee80211_band band; size_t ie_len; @@ -2715,13 +2753,13 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto out_rtnl; - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; - if (!drv->ops->scan) { + if (!rdev->ops->scan) { err = -EOPNOTSUPP; goto out; } @@ -2731,19 +2769,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) goto out; } - if (drv->scan_req) { + if (rdev->scan_req) { err = -EBUSY; goto out; } if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) - n_channels++; + n_channels = validate_scan_freqs( + info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); if (!n_channels) { err = -EINVAL; goto out; } } else { + n_channels = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) if (wiphy->bands[band]) n_channels += wiphy->bands[band]->n_channels; @@ -2836,18 +2876,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } request->ifidx = dev->ifindex; - request->wiphy = &drv->wiphy; + request->wiphy = &rdev->wiphy; + + rdev->scan_req = request; + err = rdev->ops->scan(&rdev->wiphy, dev, request); - drv->scan_req = request; - err = drv->ops->scan(&drv->wiphy, dev, request); + if (!err) + nl80211_send_scan_start(rdev, dev); out_free: if (err) { - drv->scan_req = NULL; + rdev->scan_req = NULL; kfree(request); } out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); out_rtnl: rtnl_unlock(); @@ -2964,7 +3007,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[1] = idx; err = skb->len; - cfg80211_put_dev(dev); + cfg80211_unlock_rdev(dev); out_put_netdev: dev_put(netdev); @@ -2973,19 +3016,39 @@ static int nl80211_dump_scan(struct sk_buff *skb, static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) { - return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || - auth_type == NL80211_AUTHTYPE_SHARED_KEY || - auth_type == NL80211_AUTHTYPE_FT || - auth_type == NL80211_AUTHTYPE_NETWORK_EAP; + return auth_type <= NL80211_AUTHTYPE_MAX; +} + +static bool nl80211_valid_wpa_versions(u32 wpa_versions) +{ + return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)); +} + +static bool nl80211_valid_akm_suite(u32 akm) +{ + return akm == WLAN_AKM_SUITE_8021X || + akm == WLAN_AKM_SUITE_PSK; } +static bool nl80211_valid_cipher_suite(u32 cipher) +{ + return cipher == WLAN_CIPHER_SUITE_WEP40 || + cipher == WLAN_CIPHER_SUITE_WEP104 || + cipher == WLAN_CIPHER_SUITE_TKIP || + cipher == WLAN_CIPHER_SUITE_CCMP || + cipher == WLAN_CIPHER_SUITE_AES_CMAC; +} + + static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_auth_request req; - struct wiphy *wiphy; - int err; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL; + int err, ssid_len, ie_len = 0; + enum nl80211_auth_type auth_type; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -2996,13 +3059,19 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) return -EINVAL; + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; + rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->auth) { + if (!rdev->ops->auth) { err = -EOPNOTSUPP; goto out; } @@ -3017,69 +3086,127 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - req.chan = ieee80211_get_channel( - wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!req.chan) { - err = -EINVAL; - goto out; - } + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + err = -EINVAL; + goto out; } - if (info->attrs[NL80211_ATTR_SSID]) { - req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - } + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(req.auth_type)) { + auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(auth_type)) { err = -EINVAL; goto out; } - err = drv->ops->auth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); return err; } +static int nl80211_crypto_settings(struct genl_info *info, + struct cfg80211_crypto_settings *settings, + int cipher_limit) +{ + settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; + + if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); + settings->n_ciphers_pairwise = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + if (settings->n_ciphers_pairwise > cipher_limit) + return -EINVAL; + + memcpy(settings->ciphers_pairwise, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_cipher_suite( + settings->ciphers_pairwise[i])) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { + settings->cipher_group = + nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); + if (!nl80211_valid_cipher_suite(settings->cipher_group)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { + settings->wpa_versions = + nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); + if (!nl80211_valid_wpa_versions(settings->wpa_versions)) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_AKM_SUITES]) { + void *data; + int len, i; + + data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); + len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); + settings->n_akm_suites = len / sizeof(u32); + + if (len % sizeof(u32)) + return -EINVAL; + + memcpy(settings->akm_suites, data, len); + + for (i = 0; i < settings->n_ciphers_pairwise; i++) + if (!nl80211_valid_akm_suite(settings->akm_suites[i])) + return -EINVAL; + } + + return 0; +} + static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_assoc_request req; - struct wiphy *wiphy; - int err; + struct cfg80211_crypto_settings crypto; + struct ieee80211_channel *chan; + const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; + int err, ssid_len, ie_len = 0; + bool use_mfp = false; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; if (!info->attrs[NL80211_ATTR_MAC] || - !info->attrs[NL80211_ATTR_SSID]) + !info->attrs[NL80211_ATTR_SSID] || + !info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->assoc) { + if (!rdev->ops->assoc) { err = -EOPNOTSUPP; goto out; } @@ -3094,46 +3221,45 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - req.chan = ieee80211_get_channel( - wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!req.chan) { - err = -EINVAL; - goto out; - } + chan = ieee80211_get_channel(&rdev->wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) { + err = -EINVAL; + goto out; } - req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_USE_MFP]) { - enum nl80211_mfp use_mfp = + enum nl80211_mfp mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); - if (use_mfp == NL80211_MFP_REQUIRED) - req.use_mfp = true; - else if (use_mfp != NL80211_MFP_NO) { + if (mfp == NL80211_MFP_REQUIRED) + use_mfp = true; + else if (mfp != NL80211_MFP_NO) { err = -EINVAL; goto out; } } - req.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; + if (info->attrs[NL80211_ATTR_PREV_BSSID]) + prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); - err = drv->ops->assoc(&drv->wiphy, dev, &req); + err = nl80211_crypto_settings(info, &crypto, 1); + if (!err) + err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, + ssid, ssid_len, ie, ie_len, use_mfp, + &crypto); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3142,11 +3268,11 @@ unlock_rtnl: static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_deauth_request req; - struct wiphy *wiphy; - int err; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3159,11 +3285,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->deauth) { + if (!rdev->ops->deauth) { err = -EOPNOTSUPP; goto out; } @@ -3178,27 +3304,24 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); - - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->deauth(&drv->wiphy, dev, &req); + err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3207,11 +3330,11 @@ unlock_rtnl: static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; - struct cfg80211_disassoc_request req; - struct wiphy *wiphy; - int err; + const u8 *ie = NULL, *bssid; + int err, ie_len = 0; + u16 reason_code; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -3224,11 +3347,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->disassoc) { + if (!rdev->ops->disassoc) { err = -EOPNOTSUPP; goto out; } @@ -3243,27 +3366,24 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; - memset(&req, 0, sizeof(req)); + bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (req.reason_code == 0) { + reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (reason_code == 0) { /* Reason Code 0 is reserved */ err = -EINVAL; goto out; } if (info->attrs[NL80211_ATTR_IE]) { - req.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - err = drv->ops->disassoc(&drv->wiphy, dev, &req); + err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3272,7 +3392,7 @@ unlock_rtnl: static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; struct cfg80211_ibss_params ibss; struct wiphy *wiphy; @@ -3299,11 +3419,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->join_ibss) { + if (!rdev->ops->join_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3318,7 +3438,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - wiphy = &drv->wiphy; + wiphy = &rdev->wiphy; if (info->attrs[NL80211_ATTR_MAC]) ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -3341,10 +3461,10 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; - err = cfg80211_join_ibss(drv, dev, &ibss); + err = cfg80211_join_ibss(rdev, dev, &ibss); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3353,17 +3473,17 @@ unlock_rtnl: static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct net_device *dev; int err; rtnl_lock(); - err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); if (err) goto unlock_rtnl; - if (!drv->ops->leave_ibss) { + if (!rdev->ops->leave_ibss) { err = -EOPNOTSUPP; goto out; } @@ -3378,10 +3498,257 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) goto out; } - err = cfg80211_leave_ibss(drv, dev, false); + err = cfg80211_leave_ibss(rdev, dev, false); + +out: + cfg80211_unlock_rdev(rdev); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +#ifdef CONFIG_NL80211_TESTMODE +static struct genl_multicast_group nl80211_testmode_mcgrp = { + .name = "testmode", +}; + +static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int err; + + if (!info->attrs[NL80211_ATTR_TESTDATA]) + return -EINVAL; + + rtnl_lock(); + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + goto unlock_rtnl; + } + + err = -EOPNOTSUPP; + if (rdev->ops->testmode_cmd) { + rdev->testmode_info = info; + err = rdev->ops->testmode_cmd(&rdev->wiphy, + nla_data(info->attrs[NL80211_ATTR_TESTDATA]), + nla_len(info->attrs[NL80211_ATTR_TESTDATA])); + rdev->testmode_info = NULL; + } + + cfg80211_unlock_rdev(rdev); + + unlock_rtnl: + rtnl_unlock(); + return err; +} + +static struct sk_buff * +__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, + int approxlen, u32 pid, u32 seq, gfp_t gfp) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *data; + + skb = nlmsg_new(approxlen + 100, gfp); + if (!skb) + return NULL; + + hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); + if (!hdr) { + kfree_skb(skb); + return NULL; + } + + NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); + + ((void **)skb->cb)[0] = rdev; + ((void **)skb->cb)[1] = hdr; + ((void **)skb->cb)[2] = data; + + return skb; + + nla_put_failure: + kfree_skb(skb); + return NULL; +} + +struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, + int approxlen) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!rdev->testmode_info)) + return NULL; + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, + rdev->testmode_info->snd_pid, + rdev->testmode_info->snd_seq, + GFP_KERNEL); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); + +int cfg80211_testmode_reply(struct sk_buff *skb) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + if (WARN_ON(!rdev->testmode_info)) { + kfree_skb(skb); + return -EINVAL; + } + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, rdev->testmode_info); +} +EXPORT_SYMBOL(cfg80211_testmode_reply); + +struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, + int approxlen, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); + +void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) +{ + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); +} +EXPORT_SYMBOL(cfg80211_testmode_event); +#endif + +static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net_device *dev; + struct cfg80211_connect_params connect; + struct wiphy *wiphy; + int err; + + memset(&connect, 0, sizeof(connect)); + + if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_SSID] || + !nla_len(info->attrs[NL80211_ATTR_SSID])) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { + connect.auth_type = + nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); + if (!nl80211_valid_auth_type(connect.auth_type)) + return -EINVAL; + } else + connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; + + err = nl80211_crypto_settings(info, &connect.crypto, + NL80211_MAX_NR_CIPHER_SUITES); + if (err) + return err; + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + wiphy = &rdev->wiphy; + + connect.bssid = NULL; + connect.channel = NULL; + connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + + if (info->attrs[NL80211_ATTR_MAC]) + connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + connect.channel = + ieee80211_get_channel(wiphy, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); + if (!connect.channel || + connect.channel->flags & IEEE80211_CHAN_DISABLED) { + err = -EINVAL; + goto out; + } + } + + err = cfg80211_connect(rdev, dev, &connect); out: - cfg80211_put_dev(drv); + cfg80211_unlock_rdev(rdev); + dev_put(dev); +unlock_rtnl: + rtnl_unlock(); + return err; +} + +static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net_device *dev; + int err; + u16 reason; + + if (!info->attrs[NL80211_ATTR_REASON_CODE]) + reason = WLAN_REASON_DEAUTH_LEAVING; + else + reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + + if (reason == 0) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + if (err) + goto unlock_rtnl; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + err = cfg80211_disconnect(rdev, dev, reason, true); + +out: + cfg80211_unlock_rdev(rdev); dev_put(dev); unlock_rtnl: rtnl_unlock(); @@ -3601,6 +3968,26 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, +#ifdef CONFIG_NL80211_TESTMODE + { + .cmd = NL80211_CMD_TESTMODE, + .doit = nl80211_testmode_do, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +#endif + { + .cmd = NL80211_CMD_CONNECT, + .doit = nl80211_connect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISCONNECT, + .doit = nl80211_disconnect, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -3642,6 +4029,8 @@ static int nl80211_add_scan_req(struct sk_buff *msg, struct nlattr *nest; int i; + ASSERT_RDEV_LOCK(rdev); + if (WARN_ON(!req)) return 0; @@ -3667,11 +4056,11 @@ static int nl80211_add_scan_req(struct sk_buff *msg, return -ENOBUFS; } -static int nl80211_send_scan_donemsg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 pid, u32 seq, int flags, - u32 cmd) +static int nl80211_send_scan_msg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, + u32 cmd) { void *hdr; @@ -3692,6 +4081,24 @@ static int nl80211_send_scan_donemsg(struct sk_buff *msg, return -EMSGSIZE; } +void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_TRIGGER_SCAN) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct net_device *netdev) { @@ -3701,8 +4108,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_NEW_SCAN_RESULTS) < 0) { + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_NEW_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; } @@ -3719,8 +4126,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCAN_ABORTED) < 0) { + if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCAN_ABORTED) < 0) { nlmsg_free(msg); return; } @@ -3786,12 +4193,12 @@ nla_put_failure: static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, - enum nl80211_commands cmd) + enum nl80211_commands cmd, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -3810,7 +4217,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -3819,42 +4226,45 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, } void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, size_t len) + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE); + NL80211_CMD_AUTHENTICATE, gfp); } void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len) + size_t len, gfp_t gfp) { - nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE); + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_ASSOCIATE, gfp); } void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, size_t len) + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE); + NL80211_CMD_DEAUTHENTICATE, gfp); } void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, - size_t len) + size_t len, gfp_t gfp) { nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE); + NL80211_CMD_DISASSOCIATE, gfp); } static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, int cmd, - const u8 *addr) + const u8 *addr, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -3874,7 +4284,7 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -3883,16 +4293,142 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, } void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr) + struct net_device *netdev, const u8 *addr, + gfp_t gfp) { nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, - addr); + addr, gfp); } void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr) + struct net_device *netdev, const u8 *addr, + gfp_t gfp) { - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, addr); + nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, + addr, gfp); +} + +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + if (req_ie) + NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); + if (resp_ie) + NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + +} + +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + const u8 *ie, size_t ie_len, bool from_ap) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + if (from_ap && reason) + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); + if (from_ap) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); + if (ie) + NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + } void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, @@ -3932,12 +4468,12 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, int key_id, - const u8 *tsc) + const u8 *tsc, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -3961,7 +4497,7 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC); + genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4050,6 +4586,12 @@ int nl80211_init(void) if (err) goto err_out; +#ifdef CONFIG_NL80211_TESTMODE + err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); + if (err) + goto err_out; +#endif + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5c12ad13499b..44cc2a76a1b0 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -3,39 +3,54 @@ #include "core.h" -extern int nl80211_init(void); -extern void nl80211_exit(void); -extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); -extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -extern void nl80211_send_reg_change_event(struct regulatory_request *request); -extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_deauth(struct cfg80211_registered_device *rdev, +int nl80211_init(void); +void nl80211_exit(void); +void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +void nl80211_send_reg_change_event(struct regulatory_request *request); +void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *addr, gfp_t gfp); +void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len); -extern void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr); -extern void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr); -extern void + const u8 *addr, gfp_t gfp); +void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp); +void nl80211_send_roamed(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u16 reason, + const u8 *ie, size_t ie_len, bool from_ap); + +void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, enum nl80211_key_type key_type, - int key_id, const u8 *tsc); + int key_id, const u8 *tsc, gfp_t gfp); -extern void +void nl80211_send_beacon_hint_event(struct wiphy *wiphy, struct ieee80211_channel *channel_before, struct ieee80211_channel *channel_after); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 5e14371cda70..2b4a6c66f5ae 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1061,10 +1061,10 @@ static bool ignore_reg_update(struct wiphy *wiphy, static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; - list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_regulatory(&drv->wiphy, initiator); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_regulatory(&rdev->wiphy, initiator); } static void handle_reg_beacon(struct wiphy *wiphy, @@ -1614,7 +1614,7 @@ static void reg_process_pending_hints(void) /* Processes beacon hints -- this has nothing to do with country IEs */ static void reg_process_pending_beacon_hints(void) { - struct cfg80211_registered_device *drv; + struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; mutex_lock(&cfg80211_mutex); @@ -1633,8 +1633,8 @@ static void reg_process_pending_beacon_hints(void) list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ - list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_new_beacon(&drv->wiphy, pending_beacon); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); /* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); @@ -1814,23 +1814,23 @@ void regulatory_hint_11d(struct wiphy *wiphy, if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy_idx_valid(last_request->wiphy_idx))) { - struct cfg80211_registered_device *drv_last_ie; + struct cfg80211_registered_device *rdev_last_ie; - drv_last_ie = - cfg80211_drv_by_wiphy_idx(last_request->wiphy_idx); + rdev_last_ie = + cfg80211_rdev_by_wiphy_idx(last_request->wiphy_idx); /* * Lets keep this simple -- we trust the first AP * after we intersect with CRDA */ - if (likely(&drv_last_ie->wiphy == wiphy)) { + if (likely(&rdev_last_ie->wiphy == wiphy)) { /* * Ignore IEs coming in on this wiphy with * the same alpha2 and environment cap */ - if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* @@ -1846,9 +1846,9 @@ void regulatory_hint_11d(struct wiphy *wiphy, * Ignore IEs coming in on two separate wiphys with * the same alpha2 and environment cap */ - if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, + if (likely(alpha2_equal(rdev_last_ie->country_ie_alpha2, alpha2) && - env == drv_last_ie->env)) { + env == rdev_last_ie->env)) { goto out; } /* We could potentially intersect though */ @@ -1995,14 +1995,14 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - struct cfg80211_registered_device *drv; - drv = cfg80211_drv_by_wiphy_idx( + struct cfg80211_registered_device *rdev; + rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); - if (drv) { + if (rdev) { printk(KERN_INFO "cfg80211: Current regulatory " "domain updated by AP to: %c%c\n", - drv->country_ie_alpha2[0], - drv->country_ie_alpha2[1]); + rdev->country_ie_alpha2[0], + rdev->country_ie_alpha2[1]); } else printk(KERN_INFO "cfg80211: Current regulatory " "domain intersected: \n"); @@ -2063,7 +2063,7 @@ static inline void reg_country_ie_process_debug( static int __set_regdom(const struct ieee80211_regdomain *rd) { const struct ieee80211_regdomain *intersected_rd = NULL; - struct cfg80211_registered_device *drv = NULL; + struct cfg80211_registered_device *rdev = NULL; struct wiphy *request_wiphy; /* Some basic sanity checks first */ @@ -2202,11 +2202,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!intersected_rd) return -EINVAL; - drv = wiphy_to_dev(request_wiphy); + rdev = wiphy_to_dev(request_wiphy); - drv->country_ie_alpha2[0] = rd->alpha2[0]; - drv->country_ie_alpha2[1] = rd->alpha2[1]; - drv->env = last_request->country_ie_env; + rdev->country_ie_alpha2[0] = rd->alpha2[0]; + rdev->country_ie_alpha2[1] = rd->alpha2[1]; + rdev->env = last_request->country_ie_env; BUG_ON(intersected_rd == rd); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index f8e71b300001..4f552c3f29a3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -17,20 +17,33 @@ #define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +void __cfg80211_scan_done(struct work_struct *wk) { + struct cfg80211_registered_device *rdev; + struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif + rdev = container_of(wk, struct cfg80211_registered_device, + scan_done_wk); + + mutex_lock(&rdev->mtx); + request = rdev->scan_req; + dev = dev_get_by_index(&init_net, request->ifidx); if (!dev) goto out; - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + /* + * This must be before sending the other events! + * Otherwise, wpa_supplicant gets completely confused with + * wext events. + */ + cfg80211_sme_scan_done(dev); - if (aborted) + if (request->aborted) nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); else nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); @@ -38,7 +51,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) wiphy_to_dev(request->wiphy)->scan_req = NULL; #ifdef CONFIG_WIRELESS_EXT - if (!aborted) { + if (!request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); @@ -48,8 +61,24 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) dev_put(dev); out: + cfg80211_unlock_rdev(rdev); kfree(request); } + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); + if (WARN_ON(!dev)) { + kfree(request); + return; + } + + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + + request->aborted = aborted; + schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); + dev_put(dev); +} EXPORT_SYMBOL(cfg80211_scan_done); static void bss_release(struct kref *ref) @@ -63,6 +92,8 @@ static void bss_release(struct kref *ref) if (bss->ies_allocated) kfree(bss->pub.information_elements); + BUG_ON(atomic_read(&bss->hold)); + kfree(bss); } @@ -85,8 +116,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (bss->hold || - !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + if (atomic_read(&bss->hold)) + continue; + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; list_del(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); @@ -546,30 +578,6 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) } EXPORT_SYMBOL(cfg80211_unlink_bss); -void cfg80211_hold_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss->hold = true; -} -EXPORT_SYMBOL(cfg80211_hold_bss); - -void cfg80211_unhold_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - bss->hold = false; -} -EXPORT_SYMBOL(cfg80211_unhold_bss); - #ifdef CONFIG_WIRELESS_EXT int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, @@ -647,9 +655,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (err) { rdev->scan_req = NULL; kfree(creq); - } + } else + nl80211_send_scan_start(rdev, dev); out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); @@ -958,7 +967,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, } out: - cfg80211_put_dev(rdev); + cfg80211_unlock_rdev(rdev); return res; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 000000000000..df9173f73604 --- /dev/null +++ b/net/wireless/sme.c @@ -0,0 +1,792 @@ +/* + * SME code for cfg80211's connect emulation. + * + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <linux/workqueue.h> +#include <net/cfg80211.h> +#include <net/rtnetlink.h> +#include "nl80211.h" + +struct cfg80211_conn { + struct cfg80211_connect_params params; + /* these are sub-states of the _CONNECTING sme_state */ + enum { + CFG80211_CONN_IDLE, + CFG80211_CONN_SCANNING, + CFG80211_CONN_SCAN_AGAIN, + CFG80211_CONN_AUTHENTICATE_NEXT, + CFG80211_CONN_AUTHENTICATING, + CFG80211_CONN_ASSOCIATE_NEXT, + CFG80211_CONN_ASSOCIATING, + } state; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ie_len; + bool auto_auth; +}; + + +static int cfg80211_conn_scan(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_scan_request *request; + int n_channels, err; + + ASSERT_RTNL(); + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + + if (rdev->scan_req) + return -EBUSY; + + if (wdev->conn->params.channel) { + n_channels = 1; + } else { + enum ieee80211_band band; + n_channels = 0; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + n_channels += wdev->wiphy->bands[band]->n_channels; + } + } + request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + + sizeof(request->channels[0]) * n_channels, + GFP_KERNEL); + if (!request) + return -ENOMEM; + + request->channels = (void *)((char *)request + sizeof(*request)); + if (wdev->conn->params.channel) + request->channels[0] = wdev->conn->params.channel; + else { + int i = 0, j; + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wdev->wiphy->bands[band]) + continue; + for (j = 0; j < wdev->wiphy->bands[band]->n_channels; + i++, j++) + request->channels[i] = + &wdev->wiphy->bands[band]->channels[j]; + } + } + request->n_channels = n_channels; + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = 1; + + memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, + wdev->conn->params.ssid_len); + request->ssids[0].ssid_len = wdev->conn->params.ssid_len; + + request->ifidx = wdev->netdev->ifindex; + request->wiphy = &rdev->wiphy; + + rdev->scan_req = request; + + err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); + if (!err) { + wdev->conn->state = CFG80211_CONN_SCANNING; + nl80211_send_scan_start(rdev, wdev->netdev); + } else { + rdev->scan_req = NULL; + kfree(request); + } + return err; +} + +static int cfg80211_conn_do_work(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_connect_params *params; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->conn) + return 0; + + params = &wdev->conn->params; + + switch (wdev->conn->state) { + case CFG80211_CONN_SCAN_AGAIN: + return cfg80211_conn_scan(wdev); + case CFG80211_CONN_AUTHENTICATE_NEXT: + BUG_ON(!rdev->ops->auth); + wdev->conn->state = CFG80211_CONN_AUTHENTICATING; + return __cfg80211_mlme_auth(rdev, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0); + case CFG80211_CONN_ASSOCIATE_NEXT: + BUG_ON(!rdev->ops->assoc); + wdev->conn->state = CFG80211_CONN_ASSOCIATING; + /* + * We could, later, implement roaming here and then actually + * set prev_bssid to non-NULL. But then we need to be aware + * that some APs don't like that -- so we'd need to retry + * the association. + */ + err = __cfg80211_mlme_assoc(rdev, wdev->netdev, + params->channel, params->bssid, + NULL, + params->ssid, params->ssid_len, + params->ie, params->ie_len, + false, ¶ms->crypto); + if (err) + __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING); + return err; + default: + return 0; + } +} + +void cfg80211_conn_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev = + container_of(work, struct cfg80211_registered_device, conn_work); + struct wireless_dev *wdev; + + rtnl_lock(); + cfg80211_lock_rdev(rdev); + mutex_lock(&rdev->devlist_mtx); + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev_lock(wdev); + if (!netif_running(wdev->netdev)) { + wdev_unlock(wdev); + continue; + } + if (wdev->sme_state != CFG80211_SME_CONNECTING) { + wdev_unlock(wdev); + continue; + } + if (cfg80211_conn_do_work(wdev)) + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + wdev_unlock(wdev); + } + + mutex_unlock(&rdev->devlist_mtx); + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); +} + +static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bss *bss; + u16 capa = WLAN_CAPABILITY_ESS; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->conn->params.privacy) + capa |= WLAN_CAPABILITY_PRIVACY; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, + wdev->conn->params.ssid, + wdev->conn->params.ssid_len, + WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, + capa); + if (!bss) + return false; + + memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); + wdev->conn->params.bssid = wdev->conn->bssid; + wdev->conn->params.channel = bss->channel; + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + + cfg80211_put_bss(bss); + return true; +} + +static void __cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (wdev->conn->state != CFG80211_CONN_SCANNING && + wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) + return; + + if (!cfg80211_get_conn_bss(wdev)) { + /* not found */ + if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) + schedule_work(&rdev->conn_work); + else + __cfg80211_connect_result( + wdev->netdev, + wdev->conn->params.bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + false); + } +} + +void cfg80211_sme_scan_done(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + __cfg80211_sme_scan_done(dev); + wdev_unlock(wdev); +} + +void cfg80211_sme_rx_auth(struct net_device *dev, + const u8 *buf, size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; + u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); + + ASSERT_WDEV_LOCK(wdev); + + /* should only RX auth frames when connecting */ + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (WARN_ON(!wdev->conn)) + return; + + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + wdev->conn->auto_auth && + wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { + /* select automatically between only open, shared, leap */ + switch (wdev->conn->params.auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_SHARED_KEY; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_NETWORK_EAP; + break; + default: + /* huh? */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + } + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + schedule_work(&rdev->conn_work); + } else if (status_code != WLAN_STATUS_SUCCESS) { + wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; + } else if (wdev->sme_state == CFG80211_SME_CONNECTING && + wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { + wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; + schedule_work(&rdev->conn_work); + } +} + +void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, bool wextev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (wdev->sme_state == CFG80211_SME_CONNECTED) + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, + bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, GFP_KERNEL); + else + nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, + bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, + status, GFP_KERNEL); + +#ifdef CONFIG_WIRELESS_EXT + if (wextev) { + if (req_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); + } + + if (resp_ie && status == WLAN_STATUS_SUCCESS) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid && status == WLAN_STATUS_SUCCESS) + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + } +#endif + + if (status == WLAN_STATUS_SUCCESS && + wdev->sme_state == CFG80211_SME_IDLE) { + wdev->sme_state = CFG80211_SME_CONNECTED; + return; + } + + if (wdev->sme_state != CFG80211_SME_CONNECTING) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + } + + if (wdev->conn) + wdev->conn->state = CFG80211_CONN_IDLE; + + if (status == WLAN_STATUS_SUCCESS) { + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + wdev->sme_state = CFG80211_SME_CONNECTED; + } else { + wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; + } +} + +void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, + u16 status, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_CONNECT_RESULT; + memcpy(ev->cr.bssid, bssid, ETH_ALEN); + ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->cr.req_ie_len = req_ie_len; + memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->cr.resp_ie_len = resp_ie_len; + memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + ev->cr.status = status; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_connect_result); + +void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len) +{ + struct cfg80211_bss *bss; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + /* internal error -- how did we get to CONNECTED w/o BSS? */ + if (WARN_ON(!wdev->current_bss)) { + return; + } + + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + wdev->current_bss = NULL; + + bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, + wdev->ssid, wdev->ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); + + if (WARN_ON(!bss)) + return; + + cfg80211_hold_bss(bss_from_pub(bss)); + wdev->current_bss = bss_from_pub(bss); + + nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, + req_ie, req_ie_len, resp_ie, resp_ie_len, + GFP_KERNEL); + +#ifdef CONFIG_WIRELESS_EXT + if (req_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = req_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, req_ie); + } + + if (resp_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = resp_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, resp_ie); + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); +#endif +} + +void cfg80211_roamed(struct net_device *dev, const u8 *bssid, + const u8 *req_ie, size_t req_ie_len, + const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_ROAMED; + memcpy(ev->rm.bssid, bssid, ETH_ALEN); + ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); + ev->rm.req_ie_len = req_ie_len; + memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); + ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; + ev->rm.resp_ie_len = resp_ie_len; + memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_roamed); + +void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, + size_t ie_len, u16 reason, bool from_ap) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) + return; + + if (wdev->current_bss) { + cfg80211_unhold_bss(wdev->current_bss); + cfg80211_put_bss(&wdev->current_bss->pub); + } + + wdev->current_bss = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + + if (wdev->conn) { + kfree(wdev->conn->ie); + wdev->conn->ie = NULL; + kfree(wdev->conn); + wdev->conn = NULL; + } + + nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, + reason, ie, ie_len, from_ap); + +#ifdef CONFIG_WIRELESS_EXT + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +#endif +} + +void cfg80211_disconnected(struct net_device *dev, u16 reason, + u8 *ie, size_t ie_len, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + ev = kzalloc(sizeof(*ev) + ie_len, gfp); + if (!ev) + return; + + ev->type = EVENT_DISCONNECTED; + ev->dc.ie = ((u8 *)ev) + sizeof(*ev); + ev->dc.ie_len = ie_len; + memcpy((void *)ev->dc.ie, ie, ie_len); + ev->dc.reason = reason; + + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + schedule_work(&rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_disconnected); + +int __cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state != CFG80211_SME_IDLE) + return -EALREADY; + + if (!rdev->ops->connect) { + if (!rdev->ops->auth || !rdev->ops->assoc) + return -EOPNOTSUPP; + + if (WARN_ON(wdev->conn)) + return -EINPROGRESS; + + wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); + if (!wdev->conn) + return -ENOMEM; + + /* + * Copy all parameters, and treat explicitly IEs, BSSID, SSID. + */ + memcpy(&wdev->conn->params, connect, sizeof(*connect)); + if (connect->bssid) { + wdev->conn->params.bssid = wdev->conn->bssid; + memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); + } + + if (connect->ie) { + wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, + GFP_KERNEL); + wdev->conn->params.ie = wdev->conn->ie; + if (!wdev->conn->ie) { + kfree(wdev->conn); + wdev->conn = NULL; + return -ENOMEM; + } + } + + if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + wdev->conn->auto_auth = true; + /* start with open system ... should mostly work */ + wdev->conn->params.auth_type = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } else { + wdev->conn->auto_auth = false; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + wdev->conn->params.ssid = wdev->ssid; + wdev->conn->params.ssid_len = connect->ssid_len; + + /* don't care about result -- but fill bssid & channel */ + if (!wdev->conn->params.bssid || !wdev->conn->params.channel) + cfg80211_get_conn_bss(wdev); + + wdev->sme_state = CFG80211_SME_CONNECTING; + + /* we're good if we have both BSSID and channel */ + if (wdev->conn->params.bssid && wdev->conn->params.channel) { + wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; + err = cfg80211_conn_do_work(wdev); + } else { + /* otherwise we'll need to scan for the AP first */ + err = cfg80211_conn_scan(wdev); + /* + * If we can't scan right now, then we need to scan again + * after the current scan finished, since the parameters + * changed (unless we find a good AP anyway). + */ + if (err == -EBUSY) { + err = 0; + wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; + } + } + if (err) { + kfree(wdev->conn); + wdev->conn = NULL; + wdev->sme_state = CFG80211_SME_IDLE; + } + + return err; + } else { + wdev->sme_state = CFG80211_SME_CONNECTING; + err = rdev->ops->connect(&rdev->wiphy, dev, connect); + if (err) { + wdev->sme_state = CFG80211_SME_IDLE; + return err; + } + + memcpy(wdev->ssid, connect->ssid, connect->ssid_len); + wdev->ssid_len = connect->ssid_len; + + return 0; + } +} + +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_connect(rdev, dev, connect); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, bool wextev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (wdev->sme_state == CFG80211_SME_IDLE) + return -EINVAL; + + if (!rdev->ops->disconnect) { + if (!rdev->ops->deauth) + return -EOPNOTSUPP; + + /* was it connected by userspace SME? */ + if (!wdev->conn) { + cfg80211_mlme_down(rdev, dev); + return 0; + } + + if (wdev->sme_state == CFG80211_SME_CONNECTING && + (wdev->conn->state == CFG80211_CONN_SCANNING || + wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { + wdev->sme_state = CFG80211_SME_IDLE; + kfree(wdev->conn); + wdev->conn = NULL; + return 0; + } + + /* wdev->conn->params.bssid must be set if > SCANNING */ + err = __cfg80211_mlme_deauth(rdev, dev, + wdev->conn->params.bssid, + NULL, 0, reason); + if (err) + return err; + } else { + err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); + if (err) + return err; + } + + if (wdev->sme_state == CFG80211_SME_CONNECTED) + __cfg80211_disconnected(dev, NULL, 0, 0, false); + else if (wdev->sme_state == CFG80211_SME_CONNECTING) + __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + wextev); + + return 0; +} + +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + u16 reason, bool wextev) +{ + int err; + + wdev_lock(dev->ieee80211_ptr); + err = __cfg80211_disconnect(rdev, dev, reason, wextev); + wdev_unlock(dev->ieee80211_ptr); + + return err; +} + +void cfg80211_sme_disassoc(struct net_device *dev, int idx) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 bssid[ETH_ALEN]; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->conn) + return; + + if (wdev->conn->state == CFG80211_CONN_IDLE) + return; + + /* + * Ok, so the association was made by this SME -- we don't + * want it any more so deauthenticate too. + */ + + if (!wdev->auth_bsses[idx]) + return; + + memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); + if (cfg80211_mlme_deauth(rdev, dev, bssid, + NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { + /* whatever -- assume gone anyway */ + cfg80211_unhold_bss(wdev->auth_bsses[idx]); + cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); + wdev->auth_bsses[idx] = NULL; + } +} diff --git a/net/wireless/util.c b/net/wireless/util.c index 25550692dda6..28f8f96801d4 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -502,3 +502,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb) return dscp >> 5; } EXPORT_SYMBOL(cfg80211_classify8021d); + +const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +{ + u8 *end, *pos; + + pos = bss->information_elements; + if (pos == NULL) + return NULL; + end = pos + bss->len_information_elements; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} +EXPORT_SYMBOL(ieee80211_bss_get_ie); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index d030c5315672..9d101d566bb1 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -103,7 +103,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type, + ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev, type, NULL, &vifparams); WARN_ON(!ret && wdev->iftype != type); @@ -154,7 +154,7 @@ int cfg80211_wext_giwrange(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct iw_range *range = (struct iw_range *) extra; enum ieee80211_band band; - int c = 0; + int i, c = 0; if (!wdev) return -EOPNOTSUPP; @@ -173,9 +173,6 @@ int cfg80211_wext_giwrange(struct net_device *dev, range->min_frag = 256; range->max_frag = 2346; - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; range->max_encoding_tokens = 4; range->max_qual.updated = IW_QUAL_NOISE_INVALID; @@ -204,11 +201,31 @@ int cfg80211_wext_giwrange(struct net_device *dev, range->avg_qual.noise = range->max_qual.noise / 2; range->avg_qual.updated = range->max_qual.updated; - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { + switch (wdev->wiphy->cipher_suites[i]) { + case WLAN_CIPHER_SUITE_TKIP: + range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | + IW_ENC_CAPA_WPA); + break; + + case WLAN_CIPHER_SUITE_CCMP: + range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | + IW_ENC_CAPA_WPA2); + break; + + case WLAN_CIPHER_SUITE_WEP40: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP40; + break; + + case WLAN_CIPHER_SUITE_WEP104: + range->encoding_size[range->num_encoding_sizes++] = + WLAN_KEY_LEN_WEP104; + break; + } + } for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { - int i; struct ieee80211_supported_band *sband; sband = wdev->wiphy->bands[band]; @@ -236,56 +253,13 @@ int cfg80211_wext_giwrange(struct net_device *dev, IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - range->scan_capa |= IW_SCAN_CAPA_ESSID; + if (wdev->wiphy->max_scan_ssids > 0) + range->scan_capa |= IW_SCAN_CAPA_ESSID; return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_mlme *mlme = (struct iw_mlme *)extra; - struct cfg80211_registered_device *rdev; - union { - struct cfg80211_disassoc_request disassoc; - struct cfg80211_deauth_request deauth; - } cmd; - - if (!wdev) - return -EOPNOTSUPP; - - rdev = wiphy_to_dev(wdev->wiphy); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (mlme->addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - cmd.deauth.peer_addr = mlme->addr.sa_data; - cmd.deauth.reason_code = mlme->reason_code; - return rdev->ops->deauth(wdev->wiphy, dev, &cmd.deauth); - case IW_MLME_DISASSOC: - if (!rdev->ops->disassoc) - return -EOPNOTSUPP; - cmd.disassoc.peer_addr = mlme->addr.sa_data; - cmd.disassoc.reason_code = mlme->reason_code; - return rdev->ops->disassoc(wdev->wiphy, dev, &cmd.disassoc); - default: - return -EOPNOTSUPP; - } -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); - /** * cfg80211_wext_freq - get wext frequency for non-"auto" @@ -827,3 +801,419 @@ int cfg80211_wext_giwtxpower(struct net_device *dev, return 0; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwtxpower); + +static int cfg80211_set_auth_alg(struct wireless_dev *wdev, + s32 auth_alg) +{ + int nr_alg = 0; + + if (!auth_alg) + return -EINVAL; + + if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | + IW_AUTH_ALG_SHARED_KEY | + IW_AUTH_ALG_LEAP)) + return -EINVAL; + + if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; + } + + if (auth_alg & IW_AUTH_ALG_LEAP) { + nr_alg++; + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; + } + + if (nr_alg > 1) + wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; + + return 0; +} + +static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) +{ + wdev->wext.connect.crypto.wpa_versions = 0; + + if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | + IW_AUTH_WPA_VERSION_WPA2)) + return -EINVAL; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_1; + + if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WPA_VERSION_2; + + return 0; +} + +static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) +{ + wdev->wext.connect.crypto.cipher_group = 0; + + if (cipher & IW_AUTH_CIPHER_WEP40) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP40; + else if (cipher & IW_AUTH_CIPHER_WEP104) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_WEP104; + else if (cipher & IW_AUTH_CIPHER_TKIP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_TKIP; + else if (cipher & IW_AUTH_CIPHER_CCMP) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_CCMP; + else if (cipher & IW_AUTH_CIPHER_AES_CMAC) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_AES_CMAC; + else + return -EINVAL; + + return 0; +} + +static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) +{ + int nr_ciphers = 0; + u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; + + if (cipher & IW_AUTH_CIPHER_WEP40) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_WEP104) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_TKIP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_CCMP) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; + nr_ciphers++; + } + + if (cipher & IW_AUTH_CIPHER_AES_CMAC) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; + nr_ciphers++; + } + + BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); + + wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; + + return 0; +} + + +static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) +{ + int nr_akm_suites = 0; + + if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | + IW_AUTH_KEY_MGMT_PSK)) + return -EINVAL; + + if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_8021X; + nr_akm_suites++; + } + + if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_PSK; + nr_akm_suites++; + } + + wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; + + return 0; +} + +int cfg80211_wext_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_PRIVACY_INVOKED: + wdev->wext.connect.privacy = data->value; + return 0; + case IW_AUTH_WPA_VERSION: + return cfg80211_set_wpa_version(wdev, data->value); + case IW_AUTH_CIPHER_GROUP: + return cfg80211_set_cipher_group(wdev, data->value); + case IW_AUTH_KEY_MGMT: + return cfg80211_set_key_mgt(wdev, data->value); + case IW_AUTH_CIPHER_PAIRWISE: + return cfg80211_set_cipher_pairwise(wdev, data->value); + case IW_AUTH_80211_AUTH_ALG: + return cfg80211_set_auth_alg(wdev, data->value); + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_MFP: + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwauth); + +int cfg80211_wext_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + /* XXX: what do we need? */ + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwauth); + +int cfg80211_wext_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + bool ps = wdev->wext.ps; + int timeout = wdev->wext.ps_timeout; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (!rdev->ops->set_power_mgmt) + return -EOPNOTSUPP; + + if (wrq->disabled) { + ps = false; + } else { + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_ON: /* If not specified */ + case IW_POWER_MODE: /* If set all mask */ + case IW_POWER_ALL_R: /* If explicitely state all */ + ps = true; + break; + default: /* Otherwise we ignore */ + return -EINVAL; + } + + if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) + return -EINVAL; + + if (wrq->flags & IW_POWER_TIMEOUT) + timeout = wrq->value / 1000; + } + + err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); + if (err) + return err; + + wdev->wext.ps = ps; + wdev->wext.ps_timeout = timeout; + + return 0; + +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwpower); + +int cfg80211_wext_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wrq->disabled = !wdev->wext.ps; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwpower); + +int cfg80211_wds_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + int err; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + if (addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (netif_running(dev)) + return -EBUSY; + + if (!rdev->ops->set_wds_peer) + return -EOPNOTSUPP; + + err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); + if (err) + return err; + + memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wds_wext_siwap); + +int cfg80211_wds_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) + return -EINVAL; + + addr->sa_family = ARPHRD_ETHER; + memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wds_wext_giwap); + +int cfg80211_wext_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_bitrate_mask mask; + + if (!rdev->ops->set_bitrate_mask) + return -EOPNOTSUPP; + + mask.fixed = 0; + mask.maxrate = 0; + + if (rate->value < 0) { + /* nothing */ + } else if (rate->fixed) { + mask.fixed = rate->value / 1000; /* kbps */ + } else { + mask.maxrate = rate->value / 1000; /* kbps */ + } + + return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); + +int cfg80211_wext_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rate, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use a static struct */ + static struct station_info sinfo; + u8 *addr; + int err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!rdev->ops->get_station) + return -EOPNOTSUPP; + + if (wdev->current_bss) + addr = wdev->current_bss->pub.bssid; + else if (wdev->wext.connect.bssid) + addr = wdev->wext.connect.bssid; + else + return -EOPNOTSUPP; + + err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); + if (err) + return err; + + if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) + return -EOPNOTSUPP; + + rate->value = 0; + + if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS)) + rate->value = 100000 * sinfo.txrate.legacy; + + return 0; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_giwrate); + +/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ +struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + /* we are under RTNL - globally locked - so can use static structs */ + static struct iw_statistics wstats; + static struct station_info sinfo; + u8 *addr; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) + return NULL; + + if (!rdev->ops->get_station) + return NULL; + + addr = wdev->wext.connect.bssid; + if (!addr) + return NULL; + + if (rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo)) + return NULL; + + memset(&wstats, 0, sizeof(wstats)); + + switch (rdev->wiphy.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + if (sinfo.filled & STATION_INFO_SIGNAL) { + int sig = sinfo.signal; + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.updated |= IW_QUAL_DBM; + wstats.qual.level = sig; + if (sig < -110) + sig = -110; + else if (sig > -40) + sig = -40; + wstats.qual.qual = sig + 110; + break; + } + case CFG80211_SIGNAL_TYPE_UNSPEC: + if (sinfo.filled & STATION_INFO_SIGNAL) { + wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + wstats.qual.level = sinfo.signal; + wstats.qual.qual = sinfo.signal; + break; + } + default: + wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; + wstats.qual.updated |= IW_QUAL_QUAL_INVALID; + } + + wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + + return &wstats; +} +EXPORT_SYMBOL_GPL(cfg80211_wireless_stats); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c new file mode 100644 index 000000000000..6f75aaa7f795 --- /dev/null +++ b/net/wireless/wext-sme.c @@ -0,0 +1,380 @@ +/* + * cfg80211 wext compat for managed mode. + * + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright (C) 2009 Intel Corporation. All rights reserved. + */ + +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <net/cfg80211.h> +#include "nl80211.h" + +static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) +{ + int err; + + ASSERT_RDEV_LOCK(rdev); + ASSERT_WDEV_LOCK(wdev); + + if (!netif_running(wdev->netdev)) + return 0; + + wdev->wext.connect.ie = wdev->wext.ie; + wdev->wext.connect.ie_len = wdev->wext.ie_len; + wdev->wext.connect.privacy = wdev->wext.default_key != -1; + + err = 0; + if (wdev->wext.connect.ssid_len != 0) + err = __cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect); + + return err; +} + +int cfg80211_mgd_wext_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct ieee80211_channel *chan; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + chan = cfg80211_wext_freq(wdev->wiphy, freq); + if (chan && IS_ERR(chan)) + return PTR_ERR(chan); + + if (chan && (chan->flags & IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + cfg80211_lock_rdev(rdev); + wdev_lock(wdev); + + if (wdev->wext.connect.channel == chan) { + err = 0; + goto out; + } + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set, we'll try right again, avoid event */ + if (wdev->wext.connect.ssid_len) + event = false; + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + goto out; + } + + + wdev->wext.connect.channel = chan; + + /* SSID is not set, we just want to switch channel */ + if (wdev->wext.connect.ssid_len && chan) { + err = -EOPNOTSUPP; + if (rdev->ops->set_channel) + err = rdev->ops->set_channel(wdev->wiphy, chan, + NL80211_CHAN_NO_HT); + goto out; + } + + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(rdev); + return err; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq); + +int cfg80211_mgd_wext_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *chan = NULL; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + wdev_lock(wdev); + if (wdev->current_bss) + chan = wdev->current_bss->pub.channel; + else if (wdev->wext.connect.channel) + chan = wdev->wext.connect.channel; + wdev_unlock(wdev); + + if (chan) { + freq->m = chan->center_freq; + freq->e = 6; + return 0; + } + + /* no channel if not joining */ + return -EINVAL; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq); + +int cfg80211_mgd_wext_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + size_t len = data->length; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (!data->flags) + len = 0; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; + + if (wdev->wext.connect.ssid && len && + len == wdev->wext.connect.ssid_len && + memcmp(wdev->wext.connect.ssid, ssid, len)) + goto out; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + bool event = true; + /* if SSID set now, we'll try to connect, avoid event */ + if (len) + event = false; + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + event); + if (err) + goto out; + } + + wdev->wext.connect.ssid = wdev->wext.ssid; + memcpy(wdev->wext.ssid, ssid, len); + wdev->wext.connect.ssid_len = len; + + wdev->wext.connect.crypto.control_port = false; + + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid); + +int cfg80211_mgd_wext_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + data->flags = 0; + + wdev_lock(wdev); + if (wdev->ssid_len) { + data->flags = 1; + data->length = wdev->ssid_len; + memcpy(ssid, wdev->ssid, data->length); + } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { + data->flags = 1; + data->length = wdev->wext.connect.ssid_len; + memcpy(ssid, wdev->wext.connect.ssid, data->length); + } else + data->flags = 0; + wdev_unlock(wdev); + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid); + +int cfg80211_mgd_wext_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 *bssid = ap_addr->sa_data; + int err; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + if (ap_addr->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* automatic mode */ + if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) + bssid = NULL; + + cfg80211_lock_rdev(wiphy_to_dev(wdev->wiphy)); + wdev_lock(wdev); + + err = 0; + /* both automatic */ + if (!bssid && !wdev->wext.connect.bssid) + goto out; + + /* fixed already - and no change */ + if (wdev->wext.connect.bssid && bssid && + compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) + goto out; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = __cfg80211_disconnect(wiphy_to_dev(wdev->wiphy), + dev, WLAN_REASON_DEAUTH_LEAVING, + false); + if (err) + goto out; + } + + if (bssid) { + memcpy(wdev->wext.bssid, bssid, ETH_ALEN); + wdev->wext.connect.bssid = wdev->wext.bssid; + } else + wdev->wext.connect.bssid = NULL; + + err = cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev); + out: + wdev_unlock(wdev); + cfg80211_unlock_rdev(wiphy_to_dev(wdev->wiphy)); + return err; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap); + +int cfg80211_mgd_wext_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + /* call only for station! */ + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return -EINVAL; + + ap_addr->sa_family = ARPHRD_ETHER; + + wdev_lock(wdev); + if (wdev->current_bss) + memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); + else if (wdev->wext.connect.bssid) + memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN); + else + memset(ap_addr->sa_data, 0, ETH_ALEN); + wdev_unlock(wdev); + + return 0; +} +/* temporary symbol - mark GPL - in the future the handler won't be */ +EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap); + +int cfg80211_wext_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + u8 *ie = extra; + int ie_len = data->length, err; + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!ie_len) + ie = NULL; + + wdev_lock(wdev); + + /* no change */ + err = 0; + if (wdev->wext.ie_len == ie_len && + memcmp(wdev->wext.ie, ie, ie_len) == 0) + goto out; + + if (ie_len) { + ie = kmemdup(extra, ie_len, GFP_KERNEL); + if (!ie) { + err = -ENOMEM; + goto out; + } + } else + ie = NULL; + + kfree(wdev->wext.ie); + wdev->wext.ie = ie; + wdev->wext.ie_len = ie_len; + + if (wdev->sme_state != CFG80211_SME_IDLE) { + err = __cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); + if (err) + goto out; + } + + /* userspace better not think we'll reconnect */ + err = 0; + out: + wdev_unlock(wdev); + return err; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie); + +int cfg80211_wext_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + struct cfg80211_registered_device *rdev; + int err; + + if (!wdev) + return -EOPNOTSUPP; + + rdev = wiphy_to_dev(wdev->wiphy); + + if (wdev->iftype != NL80211_IFTYPE_STATION) + return -EINVAL; + + if (mlme->addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + wdev_lock(wdev); + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + case IW_MLME_DISASSOC: + err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, + true); + break; + default: + err = -EOPNOTSUPP; + break; + } + wdev_unlock(wdev); + + return err; +} +EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme); diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 252c2010c2e2..3fe3c2c0ce11 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -417,6 +417,21 @@ static const int event_type_size[] = { IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ }; +#ifdef CONFIG_COMPAT +static const int compat_event_type_size[] = { + IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ + IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; +#endif /************************ COMMON SUBROUTINES ************************/ /* @@ -610,6 +625,11 @@ static void wireless_seq_printf_stats(struct seq_file *seq, { /* Get stats from the driver */ struct iw_statistics *stats = get_wireless_stats(dev); + static struct iw_statistics nullstats = {}; + + /* show device if it's wireless regardless of current stats */ + if (!stats && dev->wireless_handlers) + stats = &nullstats; if (stats) { seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " @@ -628,7 +648,9 @@ static void wireless_seq_printf_stats(struct seq_file *seq, stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + + if (stats != &nullstats) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; } } @@ -1250,65 +1272,58 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, } #endif -/************************* EVENT PROCESSING *************************/ -/* - * Process events generated by the wireless layer or the driver. - * Most often, the event will be propagated through rtnetlink - */ +static int __net_init wext_pernet_init(struct net *net) +{ + skb_queue_head_init(&net->wext_nlevents); + return 0; +} -/* ---------------------------------------------------------------- */ -/* - * Locking... - * ---------- - * - * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing - * the locking issue in here and implementing this code ! - * - * The issue : wireless_send_event() is often called in interrupt context, - * while the Netlink layer can never be called in interrupt context. - * The fully formed RtNetlink events are queued, and then a tasklet is run - * to feed those to Netlink. - * The skb_queue is interrupt safe, and its lock is not held while calling - * Netlink, so there is no possibility of dealock. - * Jean II - */ +static void __net_exit wext_pernet_exit(struct net *net) +{ + skb_queue_purge(&net->wext_nlevents); +} -static struct sk_buff_head wireless_nlevent_queue; +static struct pernet_operations wext_pernet_ops = { + .init = wext_pernet_init, + .exit = wext_pernet_exit, +}; static int __init wireless_nlevent_init(void) { - skb_queue_head_init(&wireless_nlevent_queue); + return register_pernet_subsys(&wext_pernet_ops); return 0; } subsys_initcall(wireless_nlevent_init); -static void wireless_nlevent_process(unsigned long data) +/* Process events generated by the wireless layer or the driver. */ +static void wireless_nlevent_process(struct work_struct *work) { struct sk_buff *skb; + struct net *net; + + rtnl_lock(); + + for_each_net(net) { + while ((skb = skb_dequeue(&net->wext_nlevents))) + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, + GFP_KERNEL); + } - while ((skb = skb_dequeue(&wireless_nlevent_queue))) - rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + rtnl_unlock(); } -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); +static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); -/* ---------------------------------------------------------------- */ -/* - * Fill a rtnetlink message with our event data. - * Note that we propage only the specified event and don't dump the - * current wireless config. Dumping the wireless config is far too - * expensive (for each parameter, the driver need to query the hardware). - */ -static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, - int type, char *event, int event_len) +static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, + struct sk_buff *skb) { struct ifinfomsg *r; struct nlmsghdr *nlh; - nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0); - if (nlh == NULL) - return -EMSGSIZE; + nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); + if (!nlh) + return NULL; r = nlmsg_data(nlh); r->ifi_family = AF_UNSPEC; @@ -1319,48 +1334,14 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, r->ifi_change = 0; /* Wireless changes don't affect those flags */ NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); - /* Add the wireless events in the netlink packet */ - NLA_PUT(skb, IFLA_WIRELESS, event_len, event); - return nlmsg_end(skb, nlh); - -nla_put_failure: + return nlh; + nla_put_failure: nlmsg_cancel(skb, nlh); - return -EMSGSIZE; + return NULL; } -/* ---------------------------------------------------------------- */ -/* - * Create and broadcast and send it on the standard rtnetlink socket - * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c - * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field - * within a RTM_NEWLINK event. - */ -static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) -{ - struct sk_buff *skb; - int err; - - if (!net_eq(dev_net(dev), &init_net)) - return; - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) - return; - - err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len); - if (err < 0) { - WARN_ON(err == -EMSGSIZE); - kfree_skb(skb); - return; - } - - NETLINK_CB(skb).dst_group = RTNLGRP_LINK; - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); -} -/* ---------------------------------------------------------------- */ /* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. @@ -1369,7 +1350,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) void wireless_send_event(struct net_device * dev, unsigned int cmd, union iwreq_data * wrqu, - char * extra) + const char * extra) { const struct iw_ioctl_description * descr = NULL; int extra_len = 0; @@ -1379,6 +1360,25 @@ void wireless_send_event(struct net_device * dev, int wrqu_off = 0; /* Offset in wrqu */ /* Don't "optimise" the following variable, it will crash */ unsigned cmd_index; /* *MUST* be unsigned */ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct nlattr *nla; +#ifdef CONFIG_COMPAT + struct __compat_iw_event *compat_event; + struct compat_iw_point compat_wrqu; + struct sk_buff *compskb; +#endif + + /* + * Nothing in the kernel sends scan events with data, be safe. + * This is necessary because we cannot fix up scan event data + * for compat, due to being contained in 'extra', but normally + * applications are required to retrieve the scan data anyway + * and no data is included in the event, this codifies that + * practice. + */ + if (WARN_ON(cmd == SIOCGIWSCAN && extra)) + extra = NULL; /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { @@ -1426,25 +1426,107 @@ void wireless_send_event(struct net_device * dev, hdr_len = event_type_size[descr->header_type]; event_len = hdr_len + extra_len; - /* Create temporary buffer to hold the event */ - event = kmalloc(event_len, GFP_ATOMIC); - if (event == NULL) + /* + * The problem for 64/32 bit. + * + * On 64-bit, a regular event is laid out as follows: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | wrqu data ... (with the correct size) | + * + * This padding exists because we manipulate event->u, + * and 'event' is not packed. + * + * An iw_point event is laid out like this instead: + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * | event.len | event.cmd | p a d d i n g | + * | iwpnt.len | iwpnt.flg | p a d d i n g | + * | extra data ... + * + * The second padding exists because struct iw_point is extended, + * but this depends on the platform... + * + * On 32-bit, all the padding shouldn't be there. + */ + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!skb) + return; + + /* Send via the RtNetlink event channel */ + nlh = rtnetlink_ifinfo_prep(dev, skb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + return; + } + + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(skb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); return; + } + event = nla_data(nla); - /* Fill event */ + /* Fill event - first clear to avoid data leaking */ + memset(event, 0, hdr_len); event->len = event_len; event->cmd = cmd; memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); - if (extra) + if (extra_len) memcpy(((char *) event) + hdr_len, extra, extra_len); + nlmsg_end(skb, nlh); +#ifdef CONFIG_COMPAT + hdr_len = compat_event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); + if (!compskb) { + kfree_skb(skb); + return; + } + /* Send via the RtNetlink event channel */ - rtmsg_iwinfo(dev, (char *) event, event_len); + nlh = rtnetlink_ifinfo_prep(dev, compskb); + if (WARN_ON(!nlh)) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } - /* Cleanup */ - kfree(event); + /* Add the wireless events in the netlink packet */ + nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); + if (!nla) { + kfree_skb(skb); + kfree_skb(compskb); + return; + } + compat_event = nla_data(nla); - return; /* Always success, I guess ;-) */ + compat_event->len = event_len; + compat_event->cmd = cmd; + if (descr->header_type == IW_HEADER_TYPE_POINT) { + compat_wrqu.length = wrqu->data.length; + compat_wrqu.flags = wrqu->data.flags; + memcpy(&compat_event->pointer, + ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, + hdr_len - IW_EV_COMPAT_LCP_LEN); + if (extra_len) + memcpy(((char *) compat_event) + hdr_len, + extra, extra_len); + } else { + /* extra_len must be zero, so no if (extra) needed */ + memcpy(&compat_event->pointer, wrqu, + hdr_len - IW_EV_COMPAT_LCP_LEN); + } + + nlmsg_end(compskb, nlh); + + skb_shinfo(skb)->frag_list = compskb; +#endif + skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); + schedule_work(&wireless_nlevent_work); } EXPORT_SYMBOL(wireless_send_event); |