Release 4.11 net/wireless/wext-core.c
/*
* This file implement the Wireless Extensions core API.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*
* (As all part of the Linux kernel, this file is GPL)
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/wireless.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#include <net/cfg80211.h>
#include <net/iw_handler.h>
#include <net/netlink.h>
#include <net/wext.h>
#include <net/net_namespace.h>
typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
unsigned int, struct iw_request_info *,
iw_handler);
/*
* Meta-data about all the standard Wireless Extension request we
* know about.
*/
static const struct iw_ioctl_description standard_ioctl[] = {
[IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[IW_IOCTL_IDX(SIOCGIWNAME)] = {
.header_type = IW_HEADER_TYPE_CHAR,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWNWID)] = {
.header_type = IW_HEADER_TYPE_PARAM,
.flags = IW_DESCR_FLAG_EVENT,
},
[IW_IOCTL_IDX(SIOCGIWNWID)] = {
.header_type = IW_HEADER_TYPE_PARAM,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWFREQ)] = {
.header_type = IW_HEADER_TYPE_FREQ,
.flags = IW_DESCR_FLAG_EVENT,
},
[IW_IOCTL_IDX(SIOCGIWFREQ)] = {
.header_type = IW_HEADER_TYPE_FREQ,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWMODE)] = {
.header_type = IW_HEADER_TYPE_UINT,
.flags = IW_DESCR_FLAG_EVENT,
},
[IW_IOCTL_IDX(SIOCGIWMODE)] = {
.header_type = IW_HEADER_TYPE_UINT,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWSENS)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWSENS)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWRANGE)] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[IW_IOCTL_IDX(SIOCGIWRANGE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_range),
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWPRIV)] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_priv_args),
.max_tokens = 16,
.flags = IW_DESCR_FLAG_NOMAX,
},
[IW_IOCTL_IDX(SIOCSIWSTATS)] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_statistics),
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWSPY)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr),
.max_tokens = IW_MAX_SPY,
},
[IW_IOCTL_IDX(SIOCGIWSPY)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr) +
sizeof(struct iw_quality),
.max_tokens = IW_MAX_SPY,
},
[IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_thrspy),
.min_tokens = 1,
.max_tokens = 1,
},
[IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_thrspy),
.min_tokens = 1,
.max_tokens = 1,
},
[IW_IOCTL_IDX(SIOCSIWAP)] = {
.header_type = IW_HEADER_TYPE_ADDR,
},
[IW_IOCTL_IDX(SIOCGIWAP)] = {
.header_type = IW_HEADER_TYPE_ADDR,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWMLME)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.min_tokens = sizeof(struct iw_mlme),
.max_tokens = sizeof(struct iw_mlme),
},
[IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr) +
sizeof(struct iw_quality),
.max_tokens = IW_MAX_AP,
.flags = IW_DESCR_FLAG_NOMAX,
},
[IW_IOCTL_IDX(SIOCSIWSCAN)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.min_tokens = 0,
.max_tokens = sizeof(struct iw_scan_req),
},
[IW_IOCTL_IDX(SIOCGIWSCAN)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_SCAN_MAX_DATA,
.flags = IW_DESCR_FLAG_NOMAX,
},
[IW_IOCTL_IDX(SIOCSIWESSID)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_EVENT,
},
[IW_IOCTL_IDX(SIOCGIWESSID)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_DUMP,
},
[IW_IOCTL_IDX(SIOCSIWNICKN)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE,
},
[IW_IOCTL_IDX(SIOCGIWNICKN)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE,
},
[IW_IOCTL_IDX(SIOCSIWRATE)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWRATE)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWRTS)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWRTS)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWFRAG)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWFRAG)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWRETRY)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWRETRY)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWENCODE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ENCODING_TOKEN_MAX,
.flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
},
[IW_IOCTL_IDX(SIOCGIWENCODE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_ENCODING_TOKEN_MAX,
.flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
},
[IW_IOCTL_IDX(SIOCSIWPOWER)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWPOWER)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IW_IOCTL_IDX(SIOCGIWGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IW_IOCTL_IDX(SIOCSIWAUTH)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCGIWAUTH)] = {
.header_type = IW_HEADER_TYPE_PARAM,
},
[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.min_tokens = sizeof(struct iw_encode_ext),
.max_tokens = sizeof(struct iw_encode_ext) +
IW_ENCODING_TOKEN_MAX,
},
[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.min_tokens = sizeof(struct iw_encode_ext),
.max_tokens = sizeof(struct iw_encode_ext) +
IW_ENCODING_TOKEN_MAX,
},
[IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.min_tokens = sizeof(struct iw_pmksa),
.max_tokens = sizeof(struct iw_pmksa),
},
};
static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
/*
* Meta-data about all the additional standard Wireless Extension events
* we know about.
*/
static const struct iw_ioctl_description standard_event[] = {
[IW_EVENT_IDX(IWEVTXDROP)] = {
.header_type = IW_HEADER_TYPE_ADDR,
},
[IW_EVENT_IDX(IWEVQUAL)] = {
.header_type = IW_HEADER_TYPE_QUAL,
},
[IW_EVENT_IDX(IWEVCUSTOM)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_CUSTOM_MAX,
},
[IW_EVENT_IDX(IWEVREGISTERED)] = {
.header_type = IW_HEADER_TYPE_ADDR,
},
[IW_EVENT_IDX(IWEVEXPIRED)] = {
.header_type = IW_HEADER_TYPE_ADDR,
},
[IW_EVENT_IDX(IWEVGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_michaelmicfailure),
},
[IW_EVENT_IDX(IWEVASSOCREQIE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX,
},
[IW_EVENT_IDX(IWEVPMKIDCAND)] = {
.header_type = IW_HEADER_TYPE_POINT,
.token_size = 1,
.max_tokens = sizeof(struct iw_pmkid_cand),
},
};
static const unsigned int standard_event_num = ARRAY_SIZE(standard_event);
/* Size (in bytes) of various events */
static const int event_type_size[] = {
IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
0,
IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
0,
IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */
IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
0,
IW_EV_POINT_LEN, /* Without variable payload */
IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
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
/* IW event code */
void wireless_nlevent_flush(void)
{
struct sk_buff *skb;
struct net *net;
ASSERT_RTNL();
for_each_net(net) {
while ((skb = skb_dequeue(&net->wext_nlevents)))
rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
GFP_KERNEL);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 55 | 100.00% | 1 | 100.00% |
Total | 55 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(wireless_nlevent_flush);
static int wext_netdev_notifier_call(struct notifier_block *nb,
unsigned long state, void *ptr)
{
/*
* When a netdev changes state in any way, flush all pending messages
* to avoid them going out in a strange order, e.g. RTM_NEWLINK after
* RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close()
* or similar - all of which could otherwise happen due to delays from
* schedule_work().
*/
wireless_nlevent_flush();
return NOTIFY_OK;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 26 | 100.00% | 1 | 100.00% |
Total | 26 | 100.00% | 1 | 100.00% |
static struct notifier_block wext_netdev_notifier = {
.notifier_call = wext_netdev_notifier_call,
};
static int __net_init wext_pernet_init(struct net *net)
{
skb_queue_head_init(&net->wext_nlevents);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 12 | 52.17% | 1 | 50.00% |
Linus Torvalds | 11 | 47.83% | 1 | 50.00% |
Total | 23 | 100.00% | 2 | 100.00% |
static void __net_exit wext_pernet_exit(struct net *net)
{
skb_queue_purge(&net->wext_nlevents);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 12 | 60.00% | 1 | 33.33% |
Linus Torvalds | 6 | 30.00% | 1 | 33.33% |
Andrew Morton | 2 | 10.00% | 1 | 33.33% |
Total | 20 | 100.00% | 3 | 100.00% |
static struct pernet_operations wext_pernet_ops = {
.init = wext_pernet_init,
.exit = wext_pernet_exit,
};
static int __init wireless_nlevent_init(void)
{
int err = register_pernet_subsys(&wext_pernet_ops);
if (err)
return err;
err = register_netdevice_notifier(&wext_netdev_notifier);
if (err)
unregister_pernet_subsys(&wext_pernet_ops);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 39 | 84.78% | 3 | 60.00% |
Linus Torvalds | 5 | 10.87% | 1 | 20.00% |
Andrew Morton | 2 | 4.35% | 1 | 20.00% |
Total | 46 | 100.00% | 5 | 100.00% |
subsys_initcall(wireless_nlevent_init);
/* Process events generated by the wireless layer or the driver. */
static void wireless_nlevent_process(struct work_struct *work)
{
rtnl_lock();
wireless_nlevent_flush();
rtnl_unlock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 12 | 60.00% | 2 | 40.00% |
Andrew Morton | 6 | 30.00% | 1 | 20.00% |
Linus Torvalds | 1 | 5.00% | 1 | 20.00% |
David S. Miller | 1 | 5.00% | 1 | 20.00% |
Total | 20 | 100.00% | 5 | 100.00% |
static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
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, RTM_NEWLINK, sizeof(*r), 0);
if (!nlh)
return NULL;
r = nlmsg_data(nlh);
r->ifi_family = AF_UNSPEC;
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0; /* Wireless changes don't affect those flags */
if (nla_put_string(skb, IFLA_IFNAME, dev->name))
goto nla_put_failure;
return nlh;
nla_put_failure:
nlmsg_cancel(skb, nlh);
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 79 | 56.83% | 1 | 16.67% |
Jean Tourrilhes | 40 | 28.78% | 2 | 33.33% |
Linus Torvalds | 12 | 8.63% | 1 | 16.67% |
David S. Miller | 7 | 5.04% | 1 | 16.67% |
Arnaldo Carvalho de Melo | 1 | 0.72% | 1 | 16.67% |
Total | 139 | 100.00% | 6 | 100.00% |
/*
* Main event dispatcher. Called from other parts and drivers.
* Send the event on the appropriate channels.
* May be called from interrupt context.
*/
void wireless_send_event(struct net_device * dev,
unsigned int cmd,
union iwreq_data * wrqu,
const char * extra)
{
const struct iw_ioctl_description * descr = NULL;
int extra_len = 0;
struct iw_event *event; /* Mallocated whole event */
int event_len; /* Its size */
int hdr_len; /* Size of the event header */
int wrqu_off = 0; /* Offset in wrqu */
/* Don't "optimise" the following variable, it will crash */
unsigned int 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) {
cmd_index = IW_IOCTL_IDX(cmd)