#include <linux/if_arp.h> /* ARPHRD_ETHER */
#include <linux/etherdevice.h> /* compare_ether_addr */
#include <linux/interrupt.h>
+#include <net/net_namespace.h>
#include <linux/wireless.h> /* Pretty obvious */
#include <net/iw_handler.h> /* New driver API */
#include <net/netlink.h>
+#include <net/wext.h>
#include <asm/uaccess.h> /* copy_to_user() */
-/**************************** CONSTANTS ****************************/
-
-/* Debugging stuff */
-#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
-#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
-#undef WE_SPY_DEBUG /* Debug enhanced spy support */
-
-/* Options */
-#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
-#define WE_SET_EVENT /* Generate an event on some set commands */
-
/************************* GLOBAL VARIABLES *************************/
/*
* You should not use global variables, because of re-entrancy.
IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
};
-/* Size (in bytes) of various events, as packed */
-static const int event_type_pk_size[] = {
- IW_EV_LCP_PK_LEN, /* IW_HEADER_TYPE_NULL */
- 0,
- IW_EV_CHAR_PK_LEN, /* IW_HEADER_TYPE_CHAR */
- 0,
- IW_EV_UINT_PK_LEN, /* IW_HEADER_TYPE_UINT */
- IW_EV_FREQ_PK_LEN, /* IW_HEADER_TYPE_FREQ */
- IW_EV_ADDR_PK_LEN, /* IW_HEADER_TYPE_ADDR */
- 0,
- IW_EV_POINT_PK_LEN, /* Without variable payload */
- IW_EV_PARAM_PK_LEN, /* IW_HEADER_TYPE_PARAM */
- IW_EV_QUAL_PK_LEN, /* IW_HEADER_TYPE_QUAL */
-};
/************************ COMMON SUBROUTINES ************************/
/*
/* ---------------------------------------------------------------- */
/*
* Return the driver handler associated with a specific Wireless Extension.
- * Called from various place, so make sure it remains efficient.
*/
-static inline iw_handler get_handler(struct net_device *dev,
- unsigned int cmd)
+static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
{
/* Don't "optimise" the following variable, it will crash */
unsigned int index; /* *MUST* be unsigned */
/*
* Get statistics out of the driver
*/
-static inline struct iw_statistics *get_wireless_stats(struct net_device *dev)
+static struct iw_statistics *get_wireless_stats(struct net_device *dev)
{
/* New location */
if ((dev->wireless_handlers != NULL) &&
return dev->wireless_handlers->get_wireless_stats(dev);
/* Not found */
- return (struct iw_statistics *) NULL;
+ return NULL;
}
/* ---------------------------------------------------------------- */
* netif_running(dev) test. I'm open on that one...
* Hopefully, the driver will remember to do a commit in "open()" ;-)
*/
-static inline int call_commit_handler(struct net_device * dev)
+static int call_commit_handler(struct net_device *dev)
{
if ((netif_running(dev)) &&
- (dev->wireless_handlers->standard[0] != NULL)) {
+ (dev->wireless_handlers->standard[0] != NULL))
/* Call the commit handler on the driver */
return dev->wireless_handlers->standard[0](dev, NULL,
NULL, NULL);
- } else
+ else
return 0; /* Command completed successfully */
}
/*
* Calculate size of private arguments
*/
-static inline int get_priv_size(__u16 args)
+static int get_priv_size(__u16 args)
{
int num = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
/*
* Re-calculate the size of private arguments
*/
-static inline int adjust_priv_size(__u16 args,
- union iwreq_data * wrqu)
+static int adjust_priv_size(__u16 args, struct iw_point *iwp)
{
- int num = wrqu->data.length;
+ int num = iwp->length;
int max = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
struct iw_statistics *stats;
stats = get_wireless_stats(dev);
- if (stats != (struct iw_statistics *) NULL) {
-
+ if (stats) {
/* Copy statistics to extra */
memcpy(extra, stats, sizeof(struct iw_statistics));
wrqu->data.length = sizeof(struct iw_statistics);
/*
* Print one entry (line) of /proc/net/wireless
*/
-static __inline__ void wireless_seq_printf_stats(struct seq_file *seq,
- struct net_device *dev)
+static void wireless_seq_printf_stats(struct seq_file *seq,
+ struct net_device *dev)
{
/* Get stats from the driver */
struct iw_statistics *stats = get_wireless_stats(dev);
static int wireless_seq_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &wireless_seq_ops);
+ return seq_open_net(inode, file, &wireless_seq_ops,
+ sizeof(struct seq_net_private));
}
static const struct file_operations wireless_seq_fops = {
.open = wireless_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
-int __init wireless_proc_init(void)
+int wext_proc_init(struct net *net)
{
/* Create /proc/net/wireless entry */
- if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops))
+ if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops))
return -ENOMEM;
return 0;
}
+
+void wext_proc_exit(struct net *net)
+{
+ proc_net_remove(net, "wireless");
+}
#endif /* CONFIG_PROC_FS */
/************************** IOCTL SUPPORT **************************/
*/
/* ---------------------------------------------------------------- */
+static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
+ const struct iw_ioctl_description *descr,
+ iw_handler handler, struct net_device *dev,
+ struct iw_request_info *info)
+{
+ int err, extra_size, user_length = 0, essid_compat = 0;
+ char *extra;
+
+ /* Calculate space needed by arguments. Always allocate
+ * for max space.
+ */
+ extra_size = descr->max_tokens * descr->token_size;
+
+ /* Check need for ESSID compatibility for WE < 21 */
+ switch (cmd) {
+ case SIOCSIWESSID:
+ case SIOCGIWESSID:
+ case SIOCSIWNICKN:
+ case SIOCGIWNICKN:
+ if (iwp->length == descr->max_tokens + 1)
+ essid_compat = 1;
+ else if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ char essid[IW_ESSID_MAX_SIZE + 1];
+
+ err = copy_from_user(essid, iwp->pointer,
+ iwp->length *
+ descr->token_size);
+ if (err)
+ return -EFAULT;
+
+ if (essid[iwp->length - 1] == '\0')
+ essid_compat = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ iwp->length -= essid_compat;
+
+ /* Check what user space is giving us */
+ if (IW_IS_SET(cmd)) {
+ /* Check NULL pointer */
+ if (!iwp->pointer && iwp->length != 0)
+ return -EFAULT;
+ /* Check if number of token fits within bounds */
+ if (iwp->length > descr->max_tokens)
+ return -E2BIG;
+ if (iwp->length < descr->min_tokens)
+ return -EINVAL;
+ } else {
+ /* Check NULL pointer */
+ if (!iwp->pointer)
+ return -EFAULT;
+ /* Save user space buffer size for checking */
+ user_length = iwp->length;
+
+ /* Don't check if user_length > max to allow forward
+ * compatibility. The test user_length < min is
+ * implied by the test at the end.
+ */
+
+ /* Support for very large requests */
+ if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ (user_length > descr->max_tokens)) {
+ /* Allow userspace to GET more than max so
+ * we can support any size GET requests.
+ * There is still a limit : -ENOMEM.
+ */
+ extra_size = user_length * descr->token_size;
+
+ /* Note : user_length is originally a __u16,
+ * and token_size is controlled by us,
+ * so extra_size won't get negative and
+ * won't overflow...
+ */
+ }
+ }
+
+ /* kzalloc() ensures NULL-termination for essid_compat. */
+ extra = kzalloc(extra_size, GFP_KERNEL);
+ if (!extra)
+ return -ENOMEM;
+
+ /* If it is a SET, get all the extra data in here */
+ if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ if (copy_from_user(extra, iwp->pointer,
+ iwp->length *
+ descr->token_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ err = handler(dev, info, (union iwreq_data *) iwp, extra);
+
+ iwp->length += essid_compat;
+
+ /* If we have something to return to the user */
+ if (!err && IW_IS_GET(cmd)) {
+ /* Check if there is enough buffer up there */
+ if (user_length < iwp->length) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ if (copy_to_user(iwp->pointer, extra,
+ iwp->length *
+ descr->token_size)) {
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Generate an event to notify listeners of the change */
+ if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) {
+ union iwreq_data *data = (union iwreq_data *) iwp;
+
+ if (descr->flags & IW_DESCR_FLAG_RESTRICT)
+ /* If the event is restricted, don't
+ * export the payload.
+ */
+ wireless_send_event(dev, cmd, data, NULL);
+ else
+ wireless_send_event(dev, cmd, data, extra);
+ }
+
+out:
+ kfree(extra);
+ return err;
+}
+
/*
* Wrapper to call a standard Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
*/
static int ioctl_standard_call(struct net_device * dev,
- struct ifreq * ifr,
+ struct iwreq *iwr,
unsigned int cmd,
+ struct iw_request_info *info,
iw_handler handler)
{
- struct iwreq * iwr = (struct iwreq *) ifr;
const struct iw_ioctl_description * descr;
- struct iw_request_info info;
int ret = -EINVAL;
/* Get the description of the IOCTL */
return -EOPNOTSUPP;
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n",
- ifr->ifr_name, cmd);
- printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif /* WE_IOCTL_DEBUG */
-
- /* Prepare the call */
- info.cmd = cmd;
- info.flags = 0;
-
/* Check if we have a pointer to user space data or not */
if (descr->header_type != IW_HEADER_TYPE_POINT) {
/* No extra arguments. Trivial to handle */
- ret = handler(dev, &info, &(iwr->u), NULL);
+ ret = handler(dev, info, &(iwr->u), NULL);
-#ifdef WE_SET_EVENT
/* Generate an event to notify listeners of the change */
if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((ret == 0) || (ret == -EIWCOMMIT)))
wireless_send_event(dev, cmd, &(iwr->u), NULL);
-#endif /* WE_SET_EVENT */
} else {
- char * extra;
- int extra_size;
- int user_length = 0;
- int err;
- int essid_compat = 0;
-
- /* Calculate space needed by arguments. Always allocate
- * for max space. Easier, and won't last long... */
- extra_size = descr->max_tokens * descr->token_size;
-
- /* Check need for ESSID compatibility for WE < 21 */
- switch (cmd) {
- case SIOCSIWESSID:
- case SIOCGIWESSID:
- case SIOCSIWNICKN:
- case SIOCGIWNICKN:
- if (iwr->u.data.length == descr->max_tokens + 1)
- essid_compat = 1;
- else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
- char essid[IW_ESSID_MAX_SIZE + 1];
-
- err = copy_from_user(essid, iwr->u.data.pointer,
- iwr->u.data.length *
- descr->token_size);
- if (err)
- return -EFAULT;
-
- if (essid[iwr->u.data.length - 1] == '\0')
- essid_compat = 1;
- }
- break;
- default:
- break;
- }
-
- iwr->u.data.length -= essid_compat;
-
- /* Check what user space is giving us */
- if (IW_IS_SET(cmd)) {
- /* Check NULL pointer */
- if ((iwr->u.data.pointer == NULL) &&
- (iwr->u.data.length != 0))
- return -EFAULT;
- /* Check if number of token fits within bounds */
- if (iwr->u.data.length > descr->max_tokens)
- return -E2BIG;
- if (iwr->u.data.length < descr->min_tokens)
- return -EINVAL;
- } else {
- /* Check NULL pointer */
- if (iwr->u.data.pointer == NULL)
- return -EFAULT;
- /* Save user space buffer size for checking */
- user_length = iwr->u.data.length;
-
- /* Don't check if user_length > max to allow forward
- * compatibility. The test user_length < min is
- * implied by the test at the end. */
-
- /* Support for very large requests */
- if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
- (user_length > descr->max_tokens)) {
- /* Allow userspace to GET more than max so
- * we can support any size GET requests.
- * There is still a limit : -ENOMEM. */
- extra_size = user_length * descr->token_size;
- /* Note : user_length is originally a __u16,
- * and token_size is controlled by us,
- * so extra_size won't get negative and
- * won't overflow... */
- }
- }
-
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
-
- /* Create the kernel buffer */
- /* kzalloc ensures NULL-termination for essid_compat */
- extra = kzalloc(extra_size, GFP_KERNEL);
- if (extra == NULL) {
- return -ENOMEM;
- }
-
- /* If it is a SET, get all the extra data in here */
- if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
- err = copy_from_user(extra, iwr->u.data.pointer,
- iwr->u.data.length *
- descr->token_size);
- if (err) {
- kfree(extra);
- return -EFAULT;
- }
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Got %d bytes\n",
- dev->name,
- iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
- }
-
- /* Call the handler */
- ret = handler(dev, &info, &(iwr->u), extra);
-
- iwr->u.data.length += essid_compat;
-
- /* If we have something to return to the user */
- if (!ret && IW_IS_GET(cmd)) {
- /* Check if there is enough buffer up there */
- if (user_length < iwr->u.data.length) {
- kfree(extra);
- return -E2BIG;
- }
-
- err = copy_to_user(iwr->u.data.pointer, extra,
- iwr->u.data.length *
- descr->token_size);
- if (err)
- ret = -EFAULT;
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n",
- dev->name,
- iwr->u.data.length * descr->token_size);
-#endif /* WE_IOCTL_DEBUG */
- }
-
-#ifdef WE_SET_EVENT
- /* Generate an event to notify listeners of the change */
- if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
- ((ret == 0) || (ret == -EIWCOMMIT))) {
- if (descr->flags & IW_DESCR_FLAG_RESTRICT)
- /* If the event is restricted, don't
- * export the payload */
- wireless_send_event(dev, cmd, &(iwr->u), NULL);
- else
- wireless_send_event(dev, cmd, &(iwr->u),
- extra);
- }
-#endif /* WE_SET_EVENT */
-
- /* Cleanup - I told you it wasn't that long ;-) */
- kfree(extra);
+ ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
+ handler, dev, info);
}
/* Call commit handler if needed and defined */
* a iw_handler but process it in your ioctl handler (i.e. use the
* old driver API).
*/
-static inline int ioctl_private_call(struct net_device * dev,
- struct ifreq * ifr,
- unsigned int cmd,
- iw_handler handler)
+static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
+ const struct iw_priv_args **descrp)
{
- struct iwreq * iwr = (struct iwreq *) ifr;
- const struct iw_priv_args * descr = NULL;
- struct iw_request_info info;
- int extra_size = 0;
- int i;
- int ret = -EINVAL;
+ const struct iw_priv_args *descr;
+ int i, extra_size;
- /* Get the description of the IOCTL */
- for (i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ descr = NULL;
+ for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
if (cmd == dev->wireless_handlers->private_args[i].cmd) {
- descr = &(dev->wireless_handlers->private_args[i]);
+ descr = &dev->wireless_handlers->private_args[i];
break;
}
-
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n",
- ifr->ifr_name, cmd);
- if (descr) {
- printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n",
- dev->name, descr->name,
- descr->set_args, descr->get_args);
}
-#endif /* WE_IOCTL_DEBUG */
- /* Compute the size of the set/get arguments */
- if (descr != NULL) {
+ extra_size = 0;
+ if (descr) {
if (IW_IS_SET(cmd)) {
int offset = 0; /* For sub-ioctls */
/* Check for sub-ioctl handler */
extra_size = 0;
}
}
+ *descrp = descr;
+ return extra_size;
+}
- /* Prepare the call */
- info.cmd = cmd;
- info.flags = 0;
-
- /* Check if we have a pointer to user space data or not. */
- if (extra_size == 0) {
- /* No extra arguments. Trivial to handle */
- ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));
- } else {
- char * extra;
- int err;
+static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
+ const struct iw_priv_args *descr,
+ iw_handler handler, struct net_device *dev,
+ struct iw_request_info *info, int extra_size)
+{
+ char *extra;
+ int err;
- /* Check what user space is giving us */
- if (IW_IS_SET(cmd)) {
- /* Check NULL pointer */
- if ((iwr->u.data.pointer == NULL) &&
- (iwr->u.data.length != 0))
- return -EFAULT;
+ /* Check what user space is giving us */
+ if (IW_IS_SET(cmd)) {
+ if (!iwp->pointer && iwp->length != 0)
+ return -EFAULT;
- /* Does it fits within bounds ? */
- if (iwr->u.data.length > (descr->set_args &
- IW_PRIV_SIZE_MASK))
- return -E2BIG;
- } else {
- /* Check NULL pointer */
- if (iwr->u.data.pointer == NULL)
- return -EFAULT;
- }
+ if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+ } else if (!iwp->pointer)
+ return -EFAULT;
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n",
- dev->name, extra_size);
-#endif /* WE_IOCTL_DEBUG */
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (!extra)
+ return -ENOMEM;
- /* Always allocate for max space. Easier, and won't last
- * long... */
- extra = kmalloc(extra_size, GFP_KERNEL);
- if (extra == NULL) {
- return -ENOMEM;
+ /* If it is a SET, get all the extra data in here */
+ if (IW_IS_SET(cmd) && (iwp->length != 0)) {
+ if (copy_from_user(extra, iwp->pointer, extra_size)) {
+ err = -EFAULT;
+ goto out;
}
+ }
- /* If it is a SET, get all the extra data in here */
- if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
- err = copy_from_user(extra, iwr->u.data.pointer,
- extra_size);
- if (err) {
- kfree(extra);
- return -EFAULT;
- }
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Got %d elem\n",
- dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
- }
+ /* Call the handler */
+ err = handler(dev, info, (union iwreq_data *) iwp, extra);
- /* Call the handler */
- ret = handler(dev, &info, &(iwr->u), extra);
+ /* If we have something to return to the user */
+ if (!err && IW_IS_GET(cmd)) {
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside.
+ */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+ extra_size = adjust_priv_size(descr->get_args, iwp);
- /* If we have something to return to the user */
- if (!ret && IW_IS_GET(cmd)) {
+ if (copy_to_user(iwp->pointer, extra, extra_size))
+ err = -EFAULT;
+ }
- /* Adjust for the actual length if it's variable,
- * avoid leaking kernel bits outside. */
- if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {
- extra_size = adjust_priv_size(descr->get_args,
- &(iwr->u));
- }
+out:
+ kfree(extra);
+ return err;
+}
- err = copy_to_user(iwr->u.data.pointer, extra,
- extra_size);
- if (err)
- ret = -EFAULT;
-#ifdef WE_IOCTL_DEBUG
- printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n",
- dev->name, iwr->u.data.length);
-#endif /* WE_IOCTL_DEBUG */
- }
+static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
+ unsigned int cmd, struct iw_request_info *info,
+ iw_handler handler)
+{
+ int extra_size = 0, ret = -EINVAL;
+ const struct iw_priv_args *descr;
- /* Cleanup - I told you it wasn't that long ;-) */
- kfree(extra);
- }
+ extra_size = get_priv_descr_and_size(dev, cmd, &descr);
+ /* Check if we have a pointer to user space data or not. */
+ if (extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
+ handler, dev, info, extra_size);
+ }
/* Call commit handler if needed and defined */
if (ret == -EIWCOMMIT)
}
/* ---------------------------------------------------------------- */
+typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
+ unsigned int, struct iw_request_info *,
+ iw_handler);
+
/*
- * Main IOCTl dispatcher. Called from the main networking code
- * (dev_ioctl() in net/core/dev.c).
+ * Main IOCTl dispatcher.
* Check the type of IOCTL and call the appropriate wrapper...
*/
-int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
+static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
+ unsigned int cmd,
+ struct iw_request_info *info,
+ wext_ioctl_func standard,
+ wext_ioctl_func private)
{
+ struct iwreq *iwr = (struct iwreq *) ifr;
struct net_device *dev;
iw_handler handler;
* The copy_to/from_user() of ifr is also dealt with in there */
/* Make sure the device exist */
- if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+ if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)
return -ENODEV;
/* A bunch of special cases, then the generic case...
* Note that 'cmd' is already filtered in dev_ioctl() with
* (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
- switch (cmd) {
- case SIOCGIWSTATS:
- /* Get Wireless Stats */
- return ioctl_standard_call(dev,
- ifr,
- cmd,
- &iw_handler_get_iwstats);
-
- case SIOCGIWPRIV:
- /* Check if we have some wireless handlers defined */
- if (dev->wireless_handlers != NULL) {
- /* We export to user space the definition of
- * the private handler ourselves */
- return ioctl_standard_call(dev,
- ifr,
- cmd,
- &iw_handler_get_private);
- }
- // ## Fall-through for old API ##
- default:
- /* Generic IOCTL */
- /* Basic check */
- if (!netif_device_present(dev))
- return -ENODEV;
- /* New driver API : try to find the handler */
- handler = get_handler(dev, cmd);
- if (handler != NULL) {
- /* Standard and private are not the same */
- if (cmd < SIOCIWFIRSTPRIV)
- return ioctl_standard_call(dev,
- ifr,
- cmd,
- handler);
- else
- return ioctl_private_call(dev,
- ifr,
- cmd,
- handler);
- }
- /* Old driver API : call driver ioctl handler */
- if (dev->do_ioctl) {
- return dev->do_ioctl(dev, ifr, cmd);
- }
- return -EOPNOTSUPP;
+ if (cmd == SIOCGIWSTATS)
+ return standard(dev, iwr, cmd, info,
+ &iw_handler_get_iwstats);
+
+ if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
+ return standard(dev, iwr, cmd, info,
+ &iw_handler_get_private);
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, cmd);
+ if (handler) {
+ /* Standard and private are not the same */
+ if (cmd < SIOCIWFIRSTPRIV)
+ return standard(dev, iwr, cmd, info, handler);
+ else
+ return private(dev, iwr, cmd, info, handler);
}
- /* Not reached */
- return -EINVAL;
+ /* Old driver API : call driver ioctl handler */
+ if (dev->do_ioctl)
+ return dev->do_ioctl(dev, ifr, cmd);
+ return -EOPNOTSUPP;
+}
+
+/* If command is `set a parameter', or `get the encoding parameters',
+ * check if the user has the right to do it.
+ */
+static int wext_permission_check(unsigned int cmd)
+{
+ if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)
+ && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ return 0;
}
+/* entry point from dev ioctl */
+static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
+ unsigned int cmd, struct iw_request_info *info,
+ wext_ioctl_func standard,
+ wext_ioctl_func private)
+{
+ int ret = wext_permission_check(cmd);
+
+ if (ret)
+ return ret;
+
+ dev_load(net, ifr->ifr_name);
+ rtnl_lock();
+ ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
+ rtnl_unlock();
+
+ return ret;
+}
+
+int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
+ void __user *arg)
+{
+ struct iw_request_info info = { .cmd = cmd, .flags = 0 };
+ int ret;
+
+ ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
+ ioctl_standard_call,
+ ioctl_private_call);
+ if (ret >= 0 &&
+ IW_IS_GET(cmd) &&
+ copy_to_user(arg, ifr, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_standard_call(struct net_device *dev,
+ struct iwreq *iwr,
+ unsigned int cmd,
+ struct iw_request_info *info,
+ iw_handler handler)
+{
+ const struct iw_ioctl_description *descr;
+ struct compat_iw_point *iwp_compat;
+ struct iw_point iwp;
+ int err;
+
+ descr = standard_ioctl + (cmd - SIOCIWFIRST);
+
+ if (descr->header_type != IW_HEADER_TYPE_POINT)
+ return ioctl_standard_call(dev, iwr, cmd, info, handler);
+
+ iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+ iwp.pointer = compat_ptr(iwp_compat->pointer);
+ iwp.length = iwp_compat->length;
+ iwp.flags = iwp_compat->flags;
+
+ err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
+
+ iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+ iwp_compat->length = iwp.length;
+ iwp_compat->flags = iwp.flags;
+
+ return err;
+}
+
+static int compat_private_call(struct net_device *dev, struct iwreq *iwr,
+ unsigned int cmd, struct iw_request_info *info,
+ iw_handler handler)
+{
+ const struct iw_priv_args *descr;
+ int ret, extra_size;
+
+ extra_size = get_priv_descr_and_size(dev, cmd, &descr);
+
+ /* Check if we have a pointer to user space data or not. */
+ if (extra_size == 0) {
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
+ } else {
+ struct compat_iw_point *iwp_compat;
+ struct iw_point iwp;
+
+ iwp_compat = (struct compat_iw_point *) &iwr->u.data;
+ iwp.pointer = compat_ptr(iwp_compat->pointer);
+ iwp.length = iwp_compat->length;
+ iwp.flags = iwp_compat->flags;
+
+ ret = ioctl_private_iw_point(&iwp, cmd, descr,
+ handler, dev, info, extra_size);
+
+ iwp_compat->pointer = ptr_to_compat(iwp.pointer);
+ iwp_compat->length = iwp.length;
+ iwp_compat->flags = iwp.flags;
+ }
+
+ /* Call commit handler if needed and defined */
+ if (ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct iw_request_info info;
+ struct iwreq iwr;
+ char *colon;
+ int ret;
+
+ if (copy_from_user(&iwr, argp, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ iwr.ifr_name[IFNAMSIZ-1] = 0;
+ colon = strchr(iwr.ifr_name, ':');
+ if (colon)
+ *colon = 0;
+
+ info.cmd = cmd;
+ info.flags = IW_REQUEST_FLAG_COMPAT;
+
+ ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info,
+ compat_standard_call,
+ compat_private_call);
+
+ if (ret >= 0 &&
+ IW_IS_GET(cmd) &&
+ copy_to_user(argp, &iwr, sizeof(struct iwreq)))
+ return -EFAULT;
+
+ return ret;
+}
+#endif
/************************* EVENT PROCESSING *************************/
/*
* Most often, the event will be propagated through rtnetlink
*/
-#ifdef WE_EVENT_RTNETLINK
/* ---------------------------------------------------------------- */
/*
* Locking...
struct sk_buff *skb;
while ((skb = skb_dequeue(&wireless_nlevent_queue)))
- rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+ rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
}
static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
* current wireless config. Dumping the wireless config is far too
* expensive (for each parameter, the driver need to query the hardware).
*/
-static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
- struct net_device * dev,
- int type,
- char * event,
- int event_len)
+static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
+ int type, char *event, int event_len)
{
struct ifinfomsg *r;
struct nlmsghdr *nlh;
- unsigned char *b = skb_tail_pointer(skb);
- nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r));
- r = NLMSG_DATA(nlh);
+ nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ r = nlmsg_data(nlh);
r->ifi_family = AF_UNSPEC;
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_flags = dev_get_flags(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 */
- RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
+ NLA_PUT(skb, IFLA_WIRELESS, event_len, event);
- nlh->nlmsg_len = skb_tail_pointer(skb) - b;
- return skb->len;
+ return nlmsg_end(skb, nlh);
-nlmsg_failure:
-rtattr_failure:
- nlmsg_trim(skb, b);
- return -1;
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
}
/* ---------------------------------------------------------------- */
* Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field
* within a RTM_NEWLINK event.
*/
-static inline void rtmsg_iwinfo(struct net_device * dev,
- char * event,
- int event_len)
+static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
{
struct sk_buff *skb;
- int size = NLMSG_GOODSIZE;
+ int err;
+
+ if (!net_eq(dev_net(dev), &init_net))
+ return;
- skb = alloc_skb(size, GFP_ATOMIC);
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!skb)
return;
- if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK,
- event, event_len) < 0) {
+ 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);
}
-#endif /* WE_EVENT_RTNETLINK */
-
/* ---------------------------------------------------------------- */
/*
* Main event dispatcher. Called from other parts and drivers.
dev->name, cmd);
return;
}
-#ifdef WE_EVENT_DEBUG
- printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n",
- dev->name, cmd);
- printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
-#endif /* WE_EVENT_DEBUG */
/* Check extra parameters and set extra_len */
if (descr->header_type == IW_HEADER_TYPE_POINT) {
extra_len = wrqu->data.length * descr->token_size;
/* Always at an offset in wrqu */
wrqu_off = IW_EV_POINT_OFF;
-#ifdef WE_EVENT_DEBUG
- printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);
-#endif /* WE_EVENT_DEBUG */
}
/* Total length of the event */
hdr_len = event_type_size[descr->header_type];
event_len = hdr_len + extra_len;
-#ifdef WE_EVENT_DEBUG
- printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, wrqu_off %d, event_len %d\n", dev->name, cmd, hdr_len, wrqu_off, event_len);
-#endif /* WE_EVENT_DEBUG */
-
/* Create temporary buffer to hold the event */
event = kmalloc(event_len, GFP_ATOMIC);
if (event == NULL)
event->len = event_len;
event->cmd = cmd;
memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
- if (extra != NULL)
+ if (extra)
memcpy(((char *) event) + hdr_len, extra, extra_len);
-#ifdef WE_EVENT_RTNETLINK
/* Send via the RtNetlink event channel */
rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif /* WE_EVENT_RTNETLINK */
/* Cleanup */
kfree(event);
return; /* Always success, I guess ;-) */
}
+EXPORT_SYMBOL(wireless_send_event);
/********************** ENHANCED IWSPY SUPPORT **********************/
/*
* Because this is called on the Rx path via wireless_spy_update(),
* we want it to be efficient...
*/
-static inline struct iw_spy_data * get_spydata(struct net_device *dev)
+static inline struct iw_spy_data *get_spydata(struct net_device *dev)
{
/* This is the new way */
if (dev->wireless_data)
- return(dev->wireless_data->spy_data);
+ return dev->wireless_data->spy_data;
return NULL;
}
/* Reset stats */
memset(spydata->spy_stat, 0,
sizeof(struct iw_quality) * IW_MAX_SPY);
-
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_handler_set_spy() : wireless_data %p, spydata %p, num %d\n", dev->wireless_data, spydata, wrqu->data.length);
- for (i = 0; i < wrqu->data.length; i++)
- printk(KERN_DEBUG
- "%02X:%02X:%02X:%02X:%02X:%02X \n",
- spydata->spy_address[i][0],
- spydata->spy_address[i][1],
- spydata->spy_address[i][2],
- spydata->spy_address[i][3],
- spydata->spy_address[i][4],
- spydata->spy_address[i][5]);
-#endif /* WE_SPY_DEBUG */
}
/* Make sure above is updated before re-enabling */
return 0;
}
+EXPORT_SYMBOL(iw_handler_set_spy);
/*------------------------------------------------------------------*/
/*
spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
return 0;
}
+EXPORT_SYMBOL(iw_handler_get_spy);
/*------------------------------------------------------------------*/
/*
/* Clear flag */
memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);
-#endif /* WE_SPY_DEBUG */
-
return 0;
}
+EXPORT_SYMBOL(iw_handler_set_thrspy);
/*------------------------------------------------------------------*/
/*
return 0;
}
+EXPORT_SYMBOL(iw_handler_get_thrspy);
/*------------------------------------------------------------------*/
/*
memcpy(&(threshold.low), &(spydata->spy_thr_low),
2 * sizeof(struct iw_quality));
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n",
- threshold.addr.sa_data[0],
- threshold.addr.sa_data[1],
- threshold.addr.sa_data[2],
- threshold.addr.sa_data[3],
- threshold.addr.sa_data[4],
- threshold.addr.sa_data[5], threshold.qual.level);
-#endif /* WE_SPY_DEBUG */
-
/* Send event to user space */
wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
}
if (!spydata)
return;
-#ifdef WE_SPY_DEBUG
- printk(KERN_DEBUG "wireless_spy_update() : wireless_data %p, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_data, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);
-#endif /* WE_SPY_DEBUG */
-
/* Update all records that match */
for (i = 0; i < spydata->spy_number; i++)
if (!compare_ether_addr(address, spydata->spy_address[i])) {
}
}
}
-
-EXPORT_SYMBOL(iw_handler_get_spy);
-EXPORT_SYMBOL(iw_handler_get_thrspy);
-EXPORT_SYMBOL(iw_handler_set_spy);
-EXPORT_SYMBOL(iw_handler_set_thrspy);
-EXPORT_SYMBOL(wireless_send_event);
EXPORT_SYMBOL(wireless_spy_update);