wext.c 47.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 * This file implement the Wireless Extensions APIs.
 *
 * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5
 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
L
Linus Torvalds 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 *
 * (As all part of the Linux kernel, this file is GPL)
 */

/************************** DOCUMENTATION **************************/
/*
 * API definition :
 * --------------
 * See <linux/wireless.h> for details of the APIs and the rest.
 *
 * History :
 * -------
 *
 * v1 - 5.12.01 - Jean II
 *	o Created this file.
 *
 * v2 - 13.12.01 - Jean II
 *	o Move /proc/net/wireless stuff from net/core/dev.c to here
 *	o Make Wireless Extension IOCTLs go through here
 *	o Added iw_handler handling ;-)
 *	o Added standard ioctl description
 *	o Initial dumb commit strategy based on orinoco.c
 *
 * v3 - 19.12.01 - Jean II
 *	o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call
 *	o Add event dispatcher function
 *	o Add event description
 *	o Propagate events as rtnetlink IFLA_WIRELESS option
 *	o Generate event on selected SET requests
 *
 * v4 - 18.04.02 - Jean II
 *	o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1
 *
 * v5 - 21.06.02 - Jean II
 *	o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup)
 *	o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
 *	o Add IWEVCUSTOM for driver specific event/scanning token
 *	o Turn on WE_STRICT_WRITE by default + kernel warning
 *	o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num)
 *	o Fix off-by-one in test (extra_size <= IFNAMSIZ)
 *
 * v6 - 9.01.03 - Jean II
 *	o Add common spy support : iw_handler_set_spy(), wireless_spy_update()
 *	o Add enhanced spy support : iw_handler_set_thrspy() and event.
 *	o Add WIRELESS_EXT version display in /proc/net/wireless
 *
 * v6 - 18.06.04 - Jean II
 *	o Change get_spydata() method for added safety
 *	o Remove spy #ifdef, they are always on -> cleaner code
 *	o Allow any size GET request if user specifies length > max
 *		and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV
 *	o Start migrating get_wireless_stats to struct iw_handler_def
 *	o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus
 * Based on patch from Pavel Roskin <proski@gnu.org> :
 *	o Fix kernel data leak to user space in private handler handling
61 62 63 64 65 66
 *
 * v7 - 18.3.05 - Jean II
 *	o Remove (struct iw_point *)->pointer from events and streams
 *	o Remove spy_offset from struct iw_handler_def
 *	o Start deprecating dev->get_wireless_stats, output a warning
 *	o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
67
 *	o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats)
68 69 70
 *
 * v8 - 17.02.06 - Jean II
 *	o RtNetlink requests support (SET/GET)
71 72 73 74 75 76 77 78
 *
 * v8b - 03.08.06 - Herbert Xu
 *	o Fix Wireless Event locking issues.
 *
 * v9 - 14.3.06 - Jean II
 *	o Change length in ESSID and NICK to strlen() instead of strlen()+1
 *	o Make standard_ioctl_num and standard_event_num unsigned
 *	o Remove (struct net_device *)->get_wireless_stats()
79 80 81
 *
 * v10 - 16.3.07 - Jean II
 *	o Prevent leaking of kernel space in stream on 64 bits.
L
Linus Torvalds 已提交
82 83 84 85 86 87 88 89 90 91 92 93
 */

/***************************** INCLUDES *****************************/

#include <linux/module.h>
#include <linux/types.h>		/* off_t */
#include <linux/netdevice.h>		/* struct ifreq, dev_get_by_name() */
#include <linux/proc_fs.h>
#include <linux/rtnetlink.h>		/* rtnetlink stuff */
#include <linux/seq_file.h>
#include <linux/init.h>			/* for __init */
#include <linux/if_arp.h>		/* ARPHRD_ETHER */
94
#include <linux/etherdevice.h>		/* compare_ether_addr */
95
#include <linux/interrupt.h>
96
#include <net/net_namespace.h>
L
Linus Torvalds 已提交
97 98 99

#include <linux/wireless.h>		/* Pretty obvious */
#include <net/iw_handler.h>		/* New driver API */
100
#include <net/netlink.h>
101
#include <net/wext.h>
L
Linus Torvalds 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

#include <asm/uaccess.h>		/* copy_to_user() */

/************************* GLOBAL VARIABLES *************************/
/*
 * You should not use global variables, because of re-entrancy.
 * On our case, it's only const, so it's OK...
 */
/*
 * Meta-data about all the standard Wireless Extension request we
 * know about.
 */
static const struct iw_ioctl_description standard_ioctl[] = {
	[SIOCSIWCOMMIT	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWNAME	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_CHAR,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWNWID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWNWID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWFREQ	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_FREQ,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWFREQ	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_FREQ,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWMODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_UINT,
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWMODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_UINT,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWSENS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWSENS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRANGE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWRANGE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_range),
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWPRIV	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWPRIV	- SIOCIWFIRST] = { /* (handled directly by us) */
165 166 167 168
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct iw_priv_args),
		.max_tokens	= 16,
		.flags		= IW_DESCR_FLAG_NOMAX,
L
Linus Torvalds 已提交
169 170 171 172 173
	},
	[SIOCSIWSTATS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_NULL,
	},
	[SIOCGIWSTATS	- SIOCIWFIRST] = { /* (handled directly by us) */
174 175 176
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_statistics),
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct sockaddr),
		.max_tokens	= IW_MAX_SPY,
	},
	[SIOCGIWSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct sockaddr) +
				  sizeof(struct iw_quality),
		.max_tokens	= IW_MAX_SPY,
	},
	[SIOCSIWTHRSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct iw_thrspy),
		.min_tokens	= 1,
		.max_tokens	= 1,
	},
	[SIOCGIWTHRSPY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= sizeof(struct iw_thrspy),
		.min_tokens	= 1,
		.max_tokens	= 1,
	},
	[SIOCSIWAP	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[SIOCGIWAP	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
		.flags		= IW_DESCR_FLAG_DUMP,
	},
已提交
209 210 211 212 213 214
	[SIOCSIWMLME	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_mlme),
		.max_tokens	= sizeof(struct iw_mlme),
	},
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222
	[SIOCGIWAPLIST	- SIOCIWFIRST] = {
		.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,
	},
	[SIOCSIWSCAN	- SIOCIWFIRST] = {
已提交
223 224 225 226
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= 0,
		.max_tokens	= sizeof(struct iw_scan_req),
L
Linus Torvalds 已提交
227 228 229 230 231 232 233 234 235 236
	},
	[SIOCGIWSCAN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_SCAN_MAX_DATA,
		.flags		= IW_DESCR_FLAG_NOMAX,
	},
	[SIOCSIWESSID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
237
		.max_tokens	= IW_ESSID_MAX_SIZE,
L
Linus Torvalds 已提交
238 239 240 241 242
		.flags		= IW_DESCR_FLAG_EVENT,
	},
	[SIOCGIWESSID	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
243
		.max_tokens	= IW_ESSID_MAX_SIZE,
L
Linus Torvalds 已提交
244 245 246 247 248
		.flags		= IW_DESCR_FLAG_DUMP,
	},
	[SIOCSIWNICKN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
249
		.max_tokens	= IW_ESSID_MAX_SIZE,
L
Linus Torvalds 已提交
250 251 252 253
	},
	[SIOCGIWNICKN	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
254
		.max_tokens	= IW_ESSID_MAX_SIZE,
L
Linus Torvalds 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	},
	[SIOCSIWRATE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRATE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRTS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRTS	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWFRAG	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWFRAG	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWTXPOW	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWTXPOW	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWRETRY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWRETRY	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWENCODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ENCODING_TOKEN_MAX,
		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
	},
	[SIOCGIWENCODE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_ENCODING_TOKEN_MAX,
		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
	},
	[SIOCSIWPOWER	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWPOWER	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	[SIOCSIWGENIE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[SIOCGIWGENIE	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[SIOCSIWAUTH	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCGIWAUTH	- SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_PARAM,
	},
	[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
		.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,
	},
	[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
		.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,
	},
	[SIOCSIWPMKSA - SIOCIWFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.min_tokens	= sizeof(struct iw_pmksa),
		.max_tokens	= sizeof(struct iw_pmksa),
	},
L
Linus Torvalds 已提交
340
};
S
Stephen Hemminger 已提交
341
static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
L
Linus Torvalds 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362

/*
 * Meta-data about all the additional standard Wireless Extension events
 * we know about.
 */
static const struct iw_ioctl_description standard_event[] = {
	[IWEVTXDROP	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[IWEVQUAL	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_QUAL,
	},
	[IWEVCUSTOM	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_CUSTOM_MAX,
	},
	[IWEVREGISTERED	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_ADDR,
	},
	[IWEVEXPIRED	- IWEVFIRST] = {
363
		.header_type	= IW_HEADER_TYPE_ADDR,
L
Linus Torvalds 已提交
364
	},
已提交
365 366 367 368 369 370
	[IWEVGENIE	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVMICHAELMICFAILURE	- IWEVFIRST] = {
371
		.header_type	= IW_HEADER_TYPE_POINT,
已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_michaelmicfailure),
	},
	[IWEVASSOCREQIE	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVASSOCRESPIE	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= IW_GENERIC_IE_MAX,
	},
	[IWEVPMKIDCAND	- IWEVFIRST] = {
		.header_type	= IW_HEADER_TYPE_POINT,
		.token_size	= 1,
		.max_tokens	= sizeof(struct iw_pmkid_cand),
	},
L
Linus Torvalds 已提交
390
};
S
Stephen Hemminger 已提交
391
static const unsigned standard_event_num = ARRAY_SIZE(standard_event);
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

/* Size (in bytes) of the various private data types */
static const char iw_priv_type_size[] = {
	0,				/* IW_PRIV_TYPE_NONE */
	1,				/* IW_PRIV_TYPE_BYTE */
	1,				/* IW_PRIV_TYPE_CHAR */
	0,				/* Not defined */
	sizeof(__u32),			/* IW_PRIV_TYPE_INT */
	sizeof(struct iw_freq),		/* IW_PRIV_TYPE_FLOAT */
	sizeof(struct sockaddr),	/* IW_PRIV_TYPE_ADDR */
	0,				/* Not defined */
};

/* 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 */
};

420

L
Linus Torvalds 已提交
421 422 423 424 425 426 427 428 429 430
/************************ COMMON SUBROUTINES ************************/
/*
 * Stuff that may be used in various place or doesn't fit in one
 * of the section below.
 */

/* ---------------------------------------------------------------- */
/*
 * Return the driver handler associated with a specific Wireless Extension.
 */
J
Johannes Berg 已提交
431
static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
L
Linus Torvalds 已提交
432 433 434 435 436
{
	/* Don't "optimise" the following variable, it will crash */
	unsigned int	index;		/* *MUST* be unsigned */

	/* Check if we have some wireless handlers defined */
437
	if (dev->wireless_handlers == NULL)
L
Linus Torvalds 已提交
438 439 440 441
		return NULL;

	/* Try as a standard command */
	index = cmd - SIOCIWFIRST;
442
	if (index < dev->wireless_handlers->num_standard)
L
Linus Torvalds 已提交
443 444 445 446
		return dev->wireless_handlers->standard[index];

	/* Try as a private command */
	index = cmd - SIOCIWFIRSTPRIV;
447
	if (index < dev->wireless_handlers->num_private)
L
Linus Torvalds 已提交
448 449 450 451 452 453 454 455 456 457
		return dev->wireless_handlers->private[index];

	/* Not found */
	return NULL;
}

/* ---------------------------------------------------------------- */
/*
 * Get statistics out of the driver
 */
J
Johannes Berg 已提交
458
static struct iw_statistics *get_wireless_stats(struct net_device *dev)
L
Linus Torvalds 已提交
459 460
{
	/* New location */
461
	if ((dev->wireless_handlers != NULL) &&
L
Linus Torvalds 已提交
462 463 464
	   (dev->wireless_handlers->get_wireless_stats != NULL))
		return dev->wireless_handlers->get_wireless_stats(dev);

465
	/* Not found */
J
Johannes Berg 已提交
466
	return NULL;
L
Linus Torvalds 已提交
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
}

/* ---------------------------------------------------------------- */
/*
 * Call the commit handler in the driver
 * (if exist and if conditions are right)
 *
 * Note : our current commit strategy is currently pretty dumb,
 * but we will be able to improve on that...
 * The goal is to try to agreagate as many changes as possible
 * before doing the commit. Drivers that will define a commit handler
 * are usually those that need a reset after changing parameters, so
 * we want to minimise the number of reset.
 * A cool idea is to use a timer : at each "set" command, we re-set the
 * timer, when the timer eventually fires, we call the driver.
 * Hopefully, more on that later.
 *
 * Also, I'm waiting to see how many people will complain about the
 * netif_running(dev) test. I'm open on that one...
 * Hopefully, the driver will remember to do a commit in "open()" ;-)
 */
J
Johannes Berg 已提交
488
static int call_commit_handler(struct net_device *dev)
L
Linus Torvalds 已提交
489
{
490
	if ((netif_running(dev)) &&
J
Johannes Berg 已提交
491
	   (dev->wireless_handlers->standard[0] != NULL))
L
Linus Torvalds 已提交
492 493 494
		/* Call the commit handler on the driver */
		return dev->wireless_handlers->standard[0](dev, NULL,
							   NULL, NULL);
J
Johannes Berg 已提交
495
	else
L
Linus Torvalds 已提交
496 497 498 499 500 501 502
		return 0;		/* Command completed successfully */
}

/* ---------------------------------------------------------------- */
/*
 * Calculate size of private arguments
 */
503
static int get_priv_size(__u16 args)
L
Linus Torvalds 已提交
504 505 506 507 508 509 510 511 512 513 514
{
	int	num = args & IW_PRIV_SIZE_MASK;
	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;

	return num * iw_priv_type_size[type];
}

/* ---------------------------------------------------------------- */
/*
 * Re-calculate the size of private arguments
 */
515
static int adjust_priv_size(__u16 args, struct iw_point *iwp)
L
Linus Torvalds 已提交
516
{
517
	int	num = iwp->length;
L
Linus Torvalds 已提交
518 519 520 521 522 523 524 525 526 527
	int	max = args & IW_PRIV_SIZE_MASK;
	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;

	/* Make sure the driver doesn't goof up */
	if (max < num)
		num = max;

	return num * iw_priv_type_size[type];
}

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
/* ---------------------------------------------------------------- */
/*
 * Standard Wireless Handler : get wireless stats
 *	Allow programatic access to /proc/net/wireless even if /proc
 *	doesn't exist... Also more efficient...
 */
static int iw_handler_get_iwstats(struct net_device *		dev,
				  struct iw_request_info *	info,
				  union iwreq_data *		wrqu,
				  char *			extra)
{
	/* Get stats from the driver */
	struct iw_statistics *stats;

	stats = get_wireless_stats(dev);
J
Johannes Berg 已提交
543
	if (stats) {
544 545 546 547 548
		/* Copy statistics to extra */
		memcpy(extra, stats, sizeof(struct iw_statistics));
		wrqu->data.length = sizeof(struct iw_statistics);

		/* Check if we need to clear the updated flag */
549
		if (wrqu->data.flags != 0)
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
		return 0;
	} else
		return -EOPNOTSUPP;
}

/* ---------------------------------------------------------------- */
/*
 * Standard Wireless Handler : get iwpriv definitions
 * Export the driver private handler definition
 * They will be picked up by tools like iwpriv...
 */
static int iw_handler_get_private(struct net_device *		dev,
				  struct iw_request_info *	info,
				  union iwreq_data *		wrqu,
				  char *			extra)
{
	/* Check if the driver has something to export */
568
	if ((dev->wireless_handlers->num_private_args == 0) ||
569 570 571 572
	   (dev->wireless_handlers->private_args == NULL))
		return -EOPNOTSUPP;

	/* Check if there is enough buffer up there */
573
	if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
		/* User space can't know in advance how large the buffer
		 * needs to be. Give it a hint, so that we can support
		 * any size buffer we want somewhat efficiently... */
		wrqu->data.length = dev->wireless_handlers->num_private_args;
		return -E2BIG;
	}

	/* Set the number of available ioctls. */
	wrqu->data.length = dev->wireless_handlers->num_private_args;

	/* Copy structure to the user buffer. */
	memcpy(extra, dev->wireless_handlers->private_args,
	       sizeof(struct iw_priv_args) * wrqu->data.length);

	return 0;
}

L
Linus Torvalds 已提交
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607

/******************** /proc/net/wireless SUPPORT ********************/
/*
 * The /proc/net/wireless file is a human readable user-space interface
 * exporting various wireless specific statistics from the wireless devices.
 * This is the most popular part of the Wireless Extensions ;-)
 *
 * This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
 * The content of the file is basically the content of "struct iw_statistics".
 */

#ifdef CONFIG_PROC_FS

/* ---------------------------------------------------------------- */
/*
 * Print one entry (line) of /proc/net/wireless
 */
J
Johannes Berg 已提交
608 609
static void wireless_seq_printf_stats(struct seq_file *seq,
				      struct net_device *dev)
L
Linus Torvalds 已提交
610 611 612 613 614 615 616 617 618 619
{
	/* Get stats from the driver */
	struct iw_statistics *stats = get_wireless_stats(dev);

	if (stats) {
		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "
				"%6d %6d   %6d\n",
			   dev->name, stats->status, stats->qual.qual,
			   stats->qual.updated & IW_QUAL_QUAL_UPDATED
			   ? '.' : ' ',
620
			   ((__s32) stats->qual.level) -
621
			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
L
Linus Torvalds 已提交
622 623
			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED
			   ? '.' : ' ',
624
			   ((__s32) stats->qual.noise) -
625
			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
L
Linus Torvalds 已提交
626 627 628 629 630
			   stats->qual.updated & IW_QUAL_NOISE_UPDATED
			   ? '.' : ' ',
			   stats->discard.nwid, stats->discard.code,
			   stats->discard.fragment, stats->discard.retries,
			   stats->discard.misc, stats->miss.beacon);
631
		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
L
Linus Torvalds 已提交
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	}
}

/* ---------------------------------------------------------------- */
/*
 * Print info for /proc/net/wireless (print all entries)
 */
static int wireless_seq_show(struct seq_file *seq, void *v)
{
	if (v == SEQ_START_TOKEN)
		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "
				"packets               | Missed | WE\n"
				" face | tus | link level noise |  nwid  "
				"crypt   frag  retry   misc | beacon | %d\n",
			   WIRELESS_EXT);
	else
		wireless_seq_printf_stats(seq, v);
	return 0;
}

652
static const struct seq_operations wireless_seq_ops = {
L
Linus Torvalds 已提交
653 654 655 656 657 658 659 660
	.start = dev_seq_start,
	.next  = dev_seq_next,
	.stop  = dev_seq_stop,
	.show  = wireless_seq_show,
};

static int wireless_seq_open(struct inode *inode, struct file *file)
{
661 662
	return seq_open_net(inode, file, &wireless_seq_ops,
			    sizeof(struct seq_net_private));
L
Linus Torvalds 已提交
663 664
}

665
static const struct file_operations wireless_seq_fops = {
L
Linus Torvalds 已提交
666 667 668 669
	.owner	 = THIS_MODULE,
	.open    = wireless_seq_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
670
	.release = seq_release_net,
L
Linus Torvalds 已提交
671 672
};

673
int wext_proc_init(struct net *net)
L
Linus Torvalds 已提交
674
{
675
	/* Create /proc/net/wireless entry */
676
	if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops))
L
Linus Torvalds 已提交
677 678 679 680
		return -ENOMEM;

	return 0;
}
681 682 683 684 685

void wext_proc_exit(struct net *net)
{
	proc_net_remove(net, "wireless");
}
L
Linus Torvalds 已提交
686 687 688 689 690 691 692 693 694 695 696
#endif	/* CONFIG_PROC_FS */

/************************** IOCTL SUPPORT **************************/
/*
 * The original user space API to configure all those Wireless Extensions
 * is through IOCTLs.
 * In there, we check if we need to call the new driver API (iw_handler)
 * or just call the driver ioctl handler.
 */

/* ---------------------------------------------------------------- */
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
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;
		}
789 790 791 792 793 794 795

		if (cmd == SIOCSIWENCODEEXT) {
			struct iw_encode_ext *ee = (void *) extra;

			if (iwp->length < sizeof(*ee) + ee->key_len)
				return -EFAULT;
		}
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
	}

	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;
}

L
Linus Torvalds 已提交
836 837 838 839 840
/*
 * 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.
 */
841
static int ioctl_standard_call(struct net_device *	dev,
842
			       struct iwreq		*iwr,
843
			       unsigned int		cmd,
844
			       struct iw_request_info	*info,
845
			       iw_handler		handler)
L
Linus Torvalds 已提交
846 847 848 849 850
{
	const struct iw_ioctl_description *	descr;
	int					ret = -EINVAL;

	/* Get the description of the IOCTL */
851
	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
L
Linus Torvalds 已提交
852 853 854 855
		return -EOPNOTSUPP;
	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);

	/* Check if we have a pointer to user space data or not */
856
	if (descr->header_type != IW_HEADER_TYPE_POINT) {
L
Linus Torvalds 已提交
857 858

		/* No extra arguments. Trivial to handle */
859
		ret = handler(dev, info, &(iwr->u), NULL);
L
Linus Torvalds 已提交
860 861

		/* Generate an event to notify listeners of the change */
862
		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
L
Linus Torvalds 已提交
863 864 865
		   ((ret == 0) || (ret == -EIWCOMMIT)))
			wireless_send_event(dev, cmd, &(iwr->u), NULL);
	} else {
866
		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
867
					      handler, dev, info);
L
Linus Torvalds 已提交
868 869 870
	}

	/* Call commit handler if needed and defined */
871
	if (ret == -EIWCOMMIT)
L
Linus Torvalds 已提交
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
		ret = call_commit_handler(dev);

	/* Here, we will generate the appropriate event if needed */

	return ret;
}

/* ---------------------------------------------------------------- */
/*
 * Wrapper to call a private Wireless Extension handler.
 * We do various checks and also take care of moving data between
 * user space and kernel space.
 * It's not as nice and slimline as the standard wrapper. The cause
 * is struct iw_priv_args, which was not really designed for the
 * job we are going here.
 *
 * IMPORTANT : This function prevent to set and get data on the same
 * IOCTL and enforce the SET/GET convention. Not doing it would be
 * far too hairy...
 * If you need to set and get data at the same time, please don't use
 * a iw_handler but process it in your ioctl handler (i.e. use the
 * old driver API).
 */
895 896
static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
				   const struct iw_priv_args **descrp)
L
Linus Torvalds 已提交
897
{
898 899
	const struct iw_priv_args *descr;
	int i, extra_size;
L
Linus Torvalds 已提交
900

901 902
	descr = NULL;
	for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
903
		if (cmd == dev->wireless_handlers->private_args[i].cmd) {
904
			descr = &dev->wireless_handlers->private_args[i];
L
Linus Torvalds 已提交
905 906
			break;
		}
907
	}
L
Linus Torvalds 已提交
908

909 910
	extra_size = 0;
	if (descr) {
911
		if (IW_IS_SET(cmd)) {
L
Linus Torvalds 已提交
912 913
			int	offset = 0;	/* For sub-ioctls */
			/* Check for sub-ioctl handler */
914
			if (descr->name[0] == '\0')
L
Linus Torvalds 已提交
915 916 917 918 919 920 921
				/* Reserve one int for sub-ioctl index */
				offset = sizeof(__u32);

			/* Size of set arguments */
			extra_size = get_priv_size(descr->set_args);

			/* Does it fits in iwr ? */
922
			if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
L
Linus Torvalds 已提交
923 924 925 926 927 928 929
			   ((extra_size + offset) <= IFNAMSIZ))
				extra_size = 0;
		} else {
			/* Size of get arguments */
			extra_size = get_priv_size(descr->get_args);

			/* Does it fits in iwr ? */
930
			if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
L
Linus Torvalds 已提交
931 932 933 934
			   (extra_size <= IFNAMSIZ))
				extra_size = 0;
		}
	}
935 936 937
	*descrp = descr;
	return extra_size;
}
L
Linus Torvalds 已提交
938

939 940 941 942 943 944 945
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;
L
Linus Torvalds 已提交
946

947 948 949 950
	/* Check what user space is giving us */
	if (IW_IS_SET(cmd)) {
		if (!iwp->pointer && iwp->length != 0)
			return -EFAULT;
L
Linus Torvalds 已提交
951

952 953 954 955
		if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
			return -E2BIG;
	} else if (!iwp->pointer)
		return -EFAULT;
L
Linus Torvalds 已提交
956

957 958 959
	extra = kmalloc(extra_size, GFP_KERNEL);
	if (!extra)
		return -ENOMEM;
L
Linus Torvalds 已提交
960

961 962 963 964 965
	/* 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;
L
Linus Torvalds 已提交
966
		}
967
	}
L
Linus Torvalds 已提交
968

969 970
	/* Call the handler */
	err = handler(dev, info, (union iwreq_data *) iwp, extra);
L
Linus Torvalds 已提交
971

972 973 974 975 976 977 978
	/* 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);
L
Linus Torvalds 已提交
979

980 981 982
		if (copy_to_user(iwp->pointer, extra, extra_size))
			err =  -EFAULT;
	}
L
Linus Torvalds 已提交
983

984 985 986 987
out:
	kfree(extra);
	return err;
}
L
Linus Torvalds 已提交
988

989
static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
990 991
			      unsigned int cmd, struct iw_request_info *info,
			      iw_handler handler)
992 993 994 995 996 997 998 999 1000
{
	int extra_size = 0, ret = -EINVAL;
	const struct iw_priv_args *descr;

	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 */
1001
		ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
1002 1003
	} else {
		ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
1004
					     handler, dev, info, extra_size);
1005
	}
L
Linus Torvalds 已提交
1006 1007

	/* Call commit handler if needed and defined */
1008
	if (ret == -EIWCOMMIT)
L
Linus Torvalds 已提交
1009 1010 1011 1012 1013 1014
		ret = call_commit_handler(dev);

	return ret;
}

/* ---------------------------------------------------------------- */
1015
typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
1016 1017
			       unsigned int, struct iw_request_info *,
			       iw_handler);
1018

L
Linus Torvalds 已提交
1019
/*
1020
 * Main IOCTl dispatcher.
L
Linus Torvalds 已提交
1021 1022
 * Check the type of IOCTL and call the appropriate wrapper...
 */
1023 1024
static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
				  unsigned int cmd,
1025
				  struct iw_request_info *info,
1026 1027
				  wext_ioctl_func standard,
				  wext_ioctl_func private)
L
Linus Torvalds 已提交
1028
{
1029
	struct iwreq *iwr = (struct iwreq *) ifr;
L
Linus Torvalds 已提交
1030 1031 1032 1033 1034 1035 1036
	struct net_device *dev;
	iw_handler	handler;

	/* Permissions are already checked in dev_ioctl() before calling us.
	 * The copy_to/from_user() of ifr is also dealt with in there */

	/* Make sure the device exist */
1037
	if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)
L
Linus Torvalds 已提交
1038 1039 1040 1041 1042
		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) */
1043
	if (cmd == SIOCGIWSTATS)
1044
		return standard(dev, iwr, cmd, info,
1045
				&iw_handler_get_iwstats);
1046

1047
	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
1048
		return standard(dev, iwr, cmd, info,
1049
				&iw_handler_get_private);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059

	/* 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)
1060
			return standard(dev, iwr, cmd, info, handler);
1061
		else
1062
			return private(dev, iwr, cmd, info, handler);
L
Linus Torvalds 已提交
1063
	}
1064
	/* Old driver API : call driver ioctl handler */
1065 1066
	if (dev->netdev_ops->ndo_do_ioctl)
		return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
1067
	return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1068 1069
}

1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
/* 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;
}

1082
/* entry point from dev ioctl */
1083
static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr,
1084
			       unsigned int cmd, struct iw_request_info *info,
1085 1086
			       wext_ioctl_func standard,
			       wext_ioctl_func private)
1087
{
1088
	int ret = wext_permission_check(cmd);
1089

1090 1091
	if (ret)
		return ret;
1092

1093
	dev_load(net, ifr->ifr_name);
1094
	rtnl_lock();
1095
	ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private);
1096
	rtnl_unlock();
1097 1098 1099 1100 1101 1102 1103

	return ret;
}

int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,
		      void __user *arg)
{
1104 1105
	struct iw_request_info info = { .cmd = cmd, .flags = 0 };
	int ret;
1106

1107 1108 1109
	ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
				  ioctl_standard_call,
				  ioctl_private_call);
1110 1111 1112
	if (ret >= 0 &&
	    IW_IS_GET(cmd) &&
	    copy_to_user(arg, ifr, sizeof(struct iwreq)))
1113
		return -EFAULT;
1114

1115 1116
	return ret;
}
1117

1118 1119 1120 1121
#ifdef CONFIG_COMPAT
static int compat_standard_call(struct net_device	*dev,
				struct iwreq		*iwr,
				unsigned int		cmd,
1122
				struct iw_request_info	*info,
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
				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)
1133
		return ioctl_standard_call(dev, iwr, cmd, info, handler);
1134 1135 1136 1137 1138 1139

	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;

1140
	err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
1141 1142 1143 1144 1145 1146 1147 1148 1149

	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,
1150 1151
			       unsigned int cmd, struct iw_request_info *info,
			       iw_handler handler)
1152 1153 1154 1155 1156 1157 1158 1159 1160
{
	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 */
1161
		ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
	} 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,
1172
					     handler, dev, info, extra_size);
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189

		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;
1190
	struct iw_request_info info;
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
	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;

1203 1204 1205 1206
	info.cmd = cmd;
	info.flags = IW_REQUEST_FLAG_COMPAT;

	ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info,
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
				  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

L
Linus Torvalds 已提交
1219 1220 1221 1222 1223 1224
/************************* EVENT PROCESSING *************************/
/*
 * Process events generated by the wireless layer or the driver.
 * Most often, the event will be propagated through rtnetlink
 */

1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
/* ---------------------------------------------------------------- */
/*
 * 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
 */

1242 1243
static struct sk_buff_head wireless_nlevent_queue;

1244 1245 1246 1247 1248 1249 1250 1251
static int __init wireless_nlevent_init(void)
{
	skb_queue_head_init(&wireless_nlevent_queue);
	return 0;
}

subsys_initcall(wireless_nlevent_init);

1252 1253 1254 1255 1256
static void wireless_nlevent_process(unsigned long data)
{
	struct sk_buff *skb;

	while ((skb = skb_dequeue(&wireless_nlevent_queue)))
1257
		rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
1258 1259 1260 1261
}

static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);

L
Linus Torvalds 已提交
1262 1263 1264 1265 1266 1267 1268
/* ---------------------------------------------------------------- */
/*
 * 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).
 */
J
Johannes Berg 已提交
1269 1270
static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,
				 int type, char *event, int event_len)
L
Linus Torvalds 已提交
1271 1272 1273 1274
{
	struct ifinfomsg *r;
	struct nlmsghdr  *nlh;

1275 1276 1277 1278 1279
	nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);
	if (nlh == NULL)
		return -EMSGSIZE;

	r = nlmsg_data(nlh);
L
Linus Torvalds 已提交
1280
	r->ifi_family = AF_UNSPEC;
1281
	r->__ifi_pad = 0;
L
Linus Torvalds 已提交
1282 1283
	r->ifi_type = dev->type;
	r->ifi_index = dev->ifindex;
1284
	r->ifi_flags = dev_get_flags(dev);
L
Linus Torvalds 已提交
1285 1286
	r->ifi_change = 0;	/* Wireless changes don't affect those flags */

J
Jamal Hadi Salim 已提交
1287
	NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name);
L
Linus Torvalds 已提交
1288
	/* Add the wireless events in the netlink packet */
1289
	NLA_PUT(skb, IFLA_WIRELESS, event_len, event);
L
Linus Torvalds 已提交
1290

1291
	return nlmsg_end(skb, nlh);
L
Linus Torvalds 已提交
1292

1293 1294 1295
nla_put_failure:
	nlmsg_cancel(skb, nlh);
	return -EMSGSIZE;
L
Linus Torvalds 已提交
1296 1297 1298 1299 1300 1301 1302 1303 1304
}

/* ---------------------------------------------------------------- */
/*
 * 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.
 */
J
Johannes Berg 已提交
1305
static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
L
Linus Torvalds 已提交
1306 1307
{
	struct sk_buff *skb;
1308
	int err;
L
Linus Torvalds 已提交
1309

1310
	if (!net_eq(dev_net(dev), &init_net))
1311 1312
		return;

1313
	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
L
Linus Torvalds 已提交
1314 1315 1316
	if (!skb)
		return;

1317 1318 1319
	err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len);
	if (err < 0) {
		WARN_ON(err == -EMSGSIZE);
L
Linus Torvalds 已提交
1320 1321 1322
		kfree_skb(skb);
		return;
	}
1323

1324
	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
1325 1326 1327 1328
	skb_queue_tail(&wireless_nlevent_queue, skb);
	tasklet_schedule(&wireless_nlevent_tasklet);
}

L
Linus Torvalds 已提交
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
/* ---------------------------------------------------------------- */
/*
 * 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,
			 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 */
1345
	int wrqu_off = 0;			/* Offset in wrqu */
L
Linus Torvalds 已提交
1346 1347 1348
	/* Don't "optimise" the following variable, it will crash */
	unsigned	cmd_index;		/* *MUST* be unsigned */

1349
	/* Get the description of the Event */
1350
	if (cmd <= SIOCIWLAST) {
L
Linus Torvalds 已提交
1351
		cmd_index = cmd - SIOCIWFIRST;
1352
		if (cmd_index < standard_ioctl_num)
L
Linus Torvalds 已提交
1353 1354 1355
			descr = &(standard_ioctl[cmd_index]);
	} else {
		cmd_index = cmd - IWEVFIRST;
1356
		if (cmd_index < standard_event_num)
L
Linus Torvalds 已提交
1357 1358 1359
			descr = &(standard_event[cmd_index]);
	}
	/* Don't accept unknown events */
1360
	if (descr == NULL) {
L
Linus Torvalds 已提交
1361 1362 1363 1364 1365 1366 1367
		/* Note : we don't return an error to the driver, because
		 * the driver would not know what to do about it. It can't
		 * return an error to the user, because the event is not
		 * initiated by a user request.
		 * The best the driver could do is to log an error message.
		 * We will do it ourselves instead...
		 */
1368
		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
L
Linus Torvalds 已提交
1369 1370 1371 1372 1373
		       dev->name, cmd);
		return;
	}

	/* Check extra parameters and set extra_len */
1374
	if (descr->header_type == IW_HEADER_TYPE_POINT) {
L
Linus Torvalds 已提交
1375
		/* Check if number of token fits within bounds */
1376
		if (wrqu->data.length > descr->max_tokens) {
1377
			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);
L
Linus Torvalds 已提交
1378 1379
			return;
		}
1380
		if (wrqu->data.length < descr->min_tokens) {
1381
			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);
L
Linus Torvalds 已提交
1382 1383 1384
			return;
		}
		/* Calculate extra_len - extra is NULL for restricted events */
1385
		if (extra != NULL)
L
Linus Torvalds 已提交
1386
			extra_len = wrqu->data.length * descr->token_size;
1387 1388
		/* Always at an offset in wrqu */
		wrqu_off = IW_EV_POINT_OFF;
L
Linus Torvalds 已提交
1389 1390 1391 1392 1393 1394 1395 1396
	}

	/* Total length of the event */
	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);
1397
	if (event == NULL)
L
Linus Torvalds 已提交
1398 1399 1400 1401 1402
		return;

	/* Fill event */
	event->len = event_len;
	event->cmd = cmd;
1403
	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
J
Johannes Berg 已提交
1404
	if (extra)
L
Linus Torvalds 已提交
1405 1406
		memcpy(((char *) event) + hdr_len, extra, extra_len);

1407
	/* Send via the RtNetlink event channel */
L
Linus Torvalds 已提交
1408 1409 1410 1411 1412 1413 1414
	rtmsg_iwinfo(dev, (char *) event, event_len);

	/* Cleanup */
	kfree(event);

	return;		/* Always success, I guess ;-) */
}
1415
EXPORT_SYMBOL(wireless_send_event);
L
Linus Torvalds 已提交
1416 1417 1418 1419 1420 1421 1422

/********************** ENHANCED IWSPY SUPPORT **********************/
/*
 * In the old days, the driver was handling spy support all by itself.
 * Now, the driver can delegate this task to Wireless Extensions.
 * It needs to use those standard spy iw_handler in struct iw_handler_def,
 * push data to us via wireless_spy_update() and include struct iw_spy_data
1423
 * in its private part (and export it in net_device->wireless_data->spy_data).
L
Linus Torvalds 已提交
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
 * One of the main advantage of centralising spy support here is that
 * it becomes much easier to improve and extend it without having to touch
 * the drivers. One example is the addition of the Spy-Threshold events.
 */

/* ---------------------------------------------------------------- */
/*
 * Return the pointer to the spy data in the driver.
 * Because this is called on the Rx path via wireless_spy_update(),
 * we want it to be efficient...
 */
J
Johannes Berg 已提交
1435
static inline struct iw_spy_data *get_spydata(struct net_device *dev)
L
Linus Torvalds 已提交
1436 1437
{
	/* This is the new way */
1438
	if (dev->wireless_data)
J
Johannes Berg 已提交
1439
		return dev->wireless_data->spy_data;
1440
	return NULL;
L
Linus Torvalds 已提交
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
}

/*------------------------------------------------------------------*/
/*
 * Standard Wireless Handler : set Spy List
 */
int iw_handler_set_spy(struct net_device *	dev,
		       struct iw_request_info *	info,
		       union iwreq_data *	wrqu,
		       char *			extra)
{
	struct iw_spy_data *	spydata = get_spydata(dev);
	struct sockaddr *	address = (struct sockaddr *) extra;

	/* Make sure driver is not buggy or using the old API */
1456
	if (!spydata)
L
Linus Torvalds 已提交
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
		return -EOPNOTSUPP;

	/* Disable spy collection while we copy the addresses.
	 * While we copy addresses, any call to wireless_spy_update()
	 * will NOP. This is OK, as anyway the addresses are changing. */
	spydata->spy_number = 0;

	/* We want to operate without locking, because wireless_spy_update()
	 * most likely will happen in the interrupt handler, and therefore
	 * have its own locking constraints and needs performance.
	 * The rtnl_lock() make sure we don't race with the other iw_handlers.
	 * This make sure wireless_spy_update() "see" that the spy list
	 * is temporarily disabled. */
R
Ralf Baechle 已提交
1470
	smp_wmb();
L
Linus Torvalds 已提交
1471 1472

	/* Are there are addresses to copy? */
1473
	if (wrqu->data.length > 0) {
L
Linus Torvalds 已提交
1474 1475 1476
		int i;

		/* Copy addresses */
1477
		for (i = 0; i < wrqu->data.length; i++)
L
Linus Torvalds 已提交
1478 1479 1480 1481 1482 1483 1484 1485
			memcpy(spydata->spy_address[i], address[i].sa_data,
			       ETH_ALEN);
		/* Reset stats */
		memset(spydata->spy_stat, 0,
		       sizeof(struct iw_quality) * IW_MAX_SPY);
	}

	/* Make sure above is updated before re-enabling */
R
Ralf Baechle 已提交
1486
	smp_wmb();
L
Linus Torvalds 已提交
1487 1488 1489 1490 1491 1492

	/* Enable addresses */
	spydata->spy_number = wrqu->data.length;

	return 0;
}
1493
EXPORT_SYMBOL(iw_handler_set_spy);
L
Linus Torvalds 已提交
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508

/*------------------------------------------------------------------*/
/*
 * Standard Wireless Handler : get Spy List
 */
int iw_handler_get_spy(struct net_device *	dev,
		       struct iw_request_info *	info,
		       union iwreq_data *	wrqu,
		       char *			extra)
{
	struct iw_spy_data *	spydata = get_spydata(dev);
	struct sockaddr *	address = (struct sockaddr *) extra;
	int			i;

	/* Make sure driver is not buggy or using the old API */
1509
	if (!spydata)
L
Linus Torvalds 已提交
1510 1511 1512 1513 1514
		return -EOPNOTSUPP;

	wrqu->data.length = spydata->spy_number;

	/* Copy addresses. */
1515
	for (i = 0; i < spydata->spy_number; i++) 	{
L
Linus Torvalds 已提交
1516 1517 1518 1519
		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
		address[i].sa_family = AF_UNIX;
	}
	/* Copy stats to the user buffer (just after). */
1520
	if (spydata->spy_number > 0)
L
Linus Torvalds 已提交
1521 1522 1523 1524
		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
		       spydata->spy_stat,
		       sizeof(struct iw_quality) * spydata->spy_number);
	/* Reset updated flags. */
1525
	for (i = 0; i < spydata->spy_number; i++)
1526
		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
L
Linus Torvalds 已提交
1527 1528
	return 0;
}
1529
EXPORT_SYMBOL(iw_handler_get_spy);
L
Linus Torvalds 已提交
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543

/*------------------------------------------------------------------*/
/*
 * Standard Wireless Handler : set spy threshold
 */
int iw_handler_set_thrspy(struct net_device *	dev,
			  struct iw_request_info *info,
			  union iwreq_data *	wrqu,
			  char *		extra)
{
	struct iw_spy_data *	spydata = get_spydata(dev);
	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;

	/* Make sure driver is not buggy or using the old API */
1544
	if (!spydata)
L
Linus Torvalds 已提交
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
		return -EOPNOTSUPP;

	/* Just do it */
	memcpy(&(spydata->spy_thr_low), &(threshold->low),
	       2 * sizeof(struct iw_quality));

	/* Clear flag */
	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));

	return 0;
}
1556
EXPORT_SYMBOL(iw_handler_set_thrspy);
L
Linus Torvalds 已提交
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570

/*------------------------------------------------------------------*/
/*
 * Standard Wireless Handler : get spy threshold
 */
int iw_handler_get_thrspy(struct net_device *	dev,
			  struct iw_request_info *info,
			  union iwreq_data *	wrqu,
			  char *		extra)
{
	struct iw_spy_data *	spydata = get_spydata(dev);
	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;

	/* Make sure driver is not buggy or using the old API */
1571
	if (!spydata)
L
Linus Torvalds 已提交
1572 1573 1574 1575 1576 1577 1578 1579
		return -EOPNOTSUPP;

	/* Just do it */
	memcpy(&(threshold->low), &(spydata->spy_thr_low),
	       2 * sizeof(struct iw_quality));

	return 0;
}
1580
EXPORT_SYMBOL(iw_handler_get_thrspy);
L
Linus Torvalds 已提交
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625

/*------------------------------------------------------------------*/
/*
 * Prepare and send a Spy Threshold event
 */
static void iw_send_thrspy_event(struct net_device *	dev,
				 struct iw_spy_data *	spydata,
				 unsigned char *	address,
				 struct iw_quality *	wstats)
{
	union iwreq_data	wrqu;
	struct iw_thrspy	threshold;

	/* Init */
	wrqu.data.length = 1;
	wrqu.data.flags = 0;
	/* Copy address */
	memcpy(threshold.addr.sa_data, address, ETH_ALEN);
	threshold.addr.sa_family = ARPHRD_ETHER;
	/* Copy stats */
	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
	/* Copy also thresholds */
	memcpy(&(threshold.low), &(spydata->spy_thr_low),
	       2 * sizeof(struct iw_quality));

	/* Send event to user space */
	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
}

/* ---------------------------------------------------------------- */
/*
 * Call for the driver to update the spy data.
 * For now, the spy data is a simple array. As the size of the array is
 * small, this is good enough. If we wanted to support larger number of
 * spy addresses, we should use something more efficient...
 */
void wireless_spy_update(struct net_device *	dev,
			 unsigned char *	address,
			 struct iw_quality *	wstats)
{
	struct iw_spy_data *	spydata = get_spydata(dev);
	int			i;
	int			match = -1;

	/* Make sure driver is not buggy or using the old API */
1626
	if (!spydata)
L
Linus Torvalds 已提交
1627 1628 1629
		return;

	/* Update all records that match */
1630 1631
	for (i = 0; i < spydata->spy_number; i++)
		if (!compare_ether_addr(address, spydata->spy_address[i])) {
L
Linus Torvalds 已提交
1632 1633 1634 1635 1636 1637 1638 1639 1640
			memcpy(&(spydata->spy_stat[i]), wstats,
			       sizeof(struct iw_quality));
			match = i;
		}

	/* Generate an event if we cross the spy threshold.
	 * To avoid event storms, we have a simple hysteresis : we generate
	 * event only when we go under the low threshold or above the
	 * high threshold. */
1641 1642 1643
	if (match >= 0) {
		if (spydata->spy_thr_under[match]) {
			if (wstats->level > spydata->spy_thr_high.level) {
L
Linus Torvalds 已提交
1644 1645 1646 1647 1648
				spydata->spy_thr_under[match] = 0;
				iw_send_thrspy_event(dev, spydata,
						     address, wstats);
			}
		} else {
1649
			if (wstats->level < spydata->spy_thr_low.level) {
L
Linus Torvalds 已提交
1650 1651 1652 1653 1654 1655 1656 1657
				spydata->spy_thr_under[match] = 1;
				iw_send_thrspy_event(dev, spydata,
						     address, wstats);
			}
		}
	}
}
EXPORT_SYMBOL(wireless_spy_update);