xref: /OK3568_Linux_fs/kernel/net/wireless/wext-core.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * This file implement the Wireless Extensions core API.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5*4882a593Smuzhiyun  * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
6*4882a593Smuzhiyun  * Copyright	2009 Johannes Berg <johannes@sipsolutions.net>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * (As all part of the Linux kernel, this file is GPL)
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/netdevice.h>
12*4882a593Smuzhiyun #include <linux/rtnetlink.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/wireless.h>
15*4882a593Smuzhiyun #include <linux/uaccess.h>
16*4882a593Smuzhiyun #include <linux/export.h>
17*4882a593Smuzhiyun #include <net/cfg80211.h>
18*4882a593Smuzhiyun #include <net/iw_handler.h>
19*4882a593Smuzhiyun #include <net/netlink.h>
20*4882a593Smuzhiyun #include <net/wext.h>
21*4882a593Smuzhiyun #include <net/net_namespace.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
24*4882a593Smuzhiyun 			       unsigned int, struct iw_request_info *,
25*4882a593Smuzhiyun 			       iw_handler);
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * Meta-data about all the standard Wireless Extension request we
30*4882a593Smuzhiyun  * know about.
31*4882a593Smuzhiyun  */
32*4882a593Smuzhiyun static const struct iw_ioctl_description standard_ioctl[] = {
33*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
34*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_NULL,
35*4882a593Smuzhiyun 	},
36*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWNAME)] = {
37*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_CHAR,
38*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
39*4882a593Smuzhiyun 	},
40*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWNWID)] = {
41*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
42*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_EVENT,
43*4882a593Smuzhiyun 	},
44*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWNWID)] = {
45*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
46*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
47*4882a593Smuzhiyun 	},
48*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWFREQ)] = {
49*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_FREQ,
50*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_EVENT,
51*4882a593Smuzhiyun 	},
52*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWFREQ)] = {
53*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_FREQ,
54*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
55*4882a593Smuzhiyun 	},
56*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWMODE)] = {
57*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_UINT,
58*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_EVENT,
59*4882a593Smuzhiyun 	},
60*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWMODE)] = {
61*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_UINT,
62*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
63*4882a593Smuzhiyun 	},
64*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWSENS)] = {
65*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
66*4882a593Smuzhiyun 	},
67*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWSENS)] = {
68*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
69*4882a593Smuzhiyun 	},
70*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWRANGE)] = {
71*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_NULL,
72*4882a593Smuzhiyun 	},
73*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWRANGE)] = {
74*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
75*4882a593Smuzhiyun 		.token_size	= 1,
76*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_range),
77*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
78*4882a593Smuzhiyun 	},
79*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWPRIV)] = {
80*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_NULL,
81*4882a593Smuzhiyun 	},
82*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
83*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
84*4882a593Smuzhiyun 		.token_size	= sizeof(struct iw_priv_args),
85*4882a593Smuzhiyun 		.max_tokens	= 16,
86*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_NOMAX,
87*4882a593Smuzhiyun 	},
88*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWSTATS)] = {
89*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_NULL,
90*4882a593Smuzhiyun 	},
91*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
92*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
93*4882a593Smuzhiyun 		.token_size	= 1,
94*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_statistics),
95*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
96*4882a593Smuzhiyun 	},
97*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWSPY)] = {
98*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
99*4882a593Smuzhiyun 		.token_size	= sizeof(struct sockaddr),
100*4882a593Smuzhiyun 		.max_tokens	= IW_MAX_SPY,
101*4882a593Smuzhiyun 	},
102*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWSPY)] = {
103*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
104*4882a593Smuzhiyun 		.token_size	= sizeof(struct sockaddr) +
105*4882a593Smuzhiyun 				  sizeof(struct iw_quality),
106*4882a593Smuzhiyun 		.max_tokens	= IW_MAX_SPY,
107*4882a593Smuzhiyun 	},
108*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
109*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
110*4882a593Smuzhiyun 		.token_size	= sizeof(struct iw_thrspy),
111*4882a593Smuzhiyun 		.min_tokens	= 1,
112*4882a593Smuzhiyun 		.max_tokens	= 1,
113*4882a593Smuzhiyun 	},
114*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
115*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
116*4882a593Smuzhiyun 		.token_size	= sizeof(struct iw_thrspy),
117*4882a593Smuzhiyun 		.min_tokens	= 1,
118*4882a593Smuzhiyun 		.max_tokens	= 1,
119*4882a593Smuzhiyun 	},
120*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWAP)] = {
121*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_ADDR,
122*4882a593Smuzhiyun 	},
123*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWAP)] = {
124*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_ADDR,
125*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
126*4882a593Smuzhiyun 	},
127*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWMLME)] = {
128*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
129*4882a593Smuzhiyun 		.token_size	= 1,
130*4882a593Smuzhiyun 		.min_tokens	= sizeof(struct iw_mlme),
131*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_mlme),
132*4882a593Smuzhiyun 	},
133*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
134*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
135*4882a593Smuzhiyun 		.token_size	= sizeof(struct sockaddr) +
136*4882a593Smuzhiyun 				  sizeof(struct iw_quality),
137*4882a593Smuzhiyun 		.max_tokens	= IW_MAX_AP,
138*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_NOMAX,
139*4882a593Smuzhiyun 	},
140*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWSCAN)] = {
141*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
142*4882a593Smuzhiyun 		.token_size	= 1,
143*4882a593Smuzhiyun 		.min_tokens	= 0,
144*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_scan_req),
145*4882a593Smuzhiyun 	},
146*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWSCAN)] = {
147*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
148*4882a593Smuzhiyun 		.token_size	= 1,
149*4882a593Smuzhiyun 		.max_tokens	= IW_SCAN_MAX_DATA,
150*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_NOMAX,
151*4882a593Smuzhiyun 	},
152*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWESSID)] = {
153*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
154*4882a593Smuzhiyun 		.token_size	= 1,
155*4882a593Smuzhiyun 		.max_tokens	= IW_ESSID_MAX_SIZE,
156*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_EVENT,
157*4882a593Smuzhiyun 	},
158*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWESSID)] = {
159*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
160*4882a593Smuzhiyun 		.token_size	= 1,
161*4882a593Smuzhiyun 		.max_tokens	= IW_ESSID_MAX_SIZE,
162*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP,
163*4882a593Smuzhiyun 	},
164*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWNICKN)] = {
165*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
166*4882a593Smuzhiyun 		.token_size	= 1,
167*4882a593Smuzhiyun 		.max_tokens	= IW_ESSID_MAX_SIZE,
168*4882a593Smuzhiyun 	},
169*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWNICKN)] = {
170*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
171*4882a593Smuzhiyun 		.token_size	= 1,
172*4882a593Smuzhiyun 		.max_tokens	= IW_ESSID_MAX_SIZE,
173*4882a593Smuzhiyun 	},
174*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWRATE)] = {
175*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
176*4882a593Smuzhiyun 	},
177*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWRATE)] = {
178*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
179*4882a593Smuzhiyun 	},
180*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWRTS)] = {
181*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
182*4882a593Smuzhiyun 	},
183*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWRTS)] = {
184*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
185*4882a593Smuzhiyun 	},
186*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWFRAG)] = {
187*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
188*4882a593Smuzhiyun 	},
189*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWFRAG)] = {
190*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
191*4882a593Smuzhiyun 	},
192*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
193*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
194*4882a593Smuzhiyun 	},
195*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
196*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
197*4882a593Smuzhiyun 	},
198*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWRETRY)] = {
199*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
200*4882a593Smuzhiyun 	},
201*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWRETRY)] = {
202*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
203*4882a593Smuzhiyun 	},
204*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWENCODE)] = {
205*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
206*4882a593Smuzhiyun 		.token_size	= 1,
207*4882a593Smuzhiyun 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
208*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
209*4882a593Smuzhiyun 	},
210*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWENCODE)] = {
211*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
212*4882a593Smuzhiyun 		.token_size	= 1,
213*4882a593Smuzhiyun 		.max_tokens	= IW_ENCODING_TOKEN_MAX,
214*4882a593Smuzhiyun 		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
215*4882a593Smuzhiyun 	},
216*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWPOWER)] = {
217*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
218*4882a593Smuzhiyun 	},
219*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWPOWER)] = {
220*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
221*4882a593Smuzhiyun 	},
222*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWGENIE)] = {
223*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
224*4882a593Smuzhiyun 		.token_size	= 1,
225*4882a593Smuzhiyun 		.max_tokens	= IW_GENERIC_IE_MAX,
226*4882a593Smuzhiyun 	},
227*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWGENIE)] = {
228*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
229*4882a593Smuzhiyun 		.token_size	= 1,
230*4882a593Smuzhiyun 		.max_tokens	= IW_GENERIC_IE_MAX,
231*4882a593Smuzhiyun 	},
232*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWAUTH)] = {
233*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
234*4882a593Smuzhiyun 	},
235*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWAUTH)] = {
236*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_PARAM,
237*4882a593Smuzhiyun 	},
238*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
239*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
240*4882a593Smuzhiyun 		.token_size	= 1,
241*4882a593Smuzhiyun 		.min_tokens	= sizeof(struct iw_encode_ext),
242*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_encode_ext) +
243*4882a593Smuzhiyun 				  IW_ENCODING_TOKEN_MAX,
244*4882a593Smuzhiyun 	},
245*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
246*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
247*4882a593Smuzhiyun 		.token_size	= 1,
248*4882a593Smuzhiyun 		.min_tokens	= sizeof(struct iw_encode_ext),
249*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_encode_ext) +
250*4882a593Smuzhiyun 				  IW_ENCODING_TOKEN_MAX,
251*4882a593Smuzhiyun 	},
252*4882a593Smuzhiyun 	[IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
253*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
254*4882a593Smuzhiyun 		.token_size	= 1,
255*4882a593Smuzhiyun 		.min_tokens	= sizeof(struct iw_pmksa),
256*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_pmksa),
257*4882a593Smuzhiyun 	},
258*4882a593Smuzhiyun };
259*4882a593Smuzhiyun static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun /*
262*4882a593Smuzhiyun  * Meta-data about all the additional standard Wireless Extension events
263*4882a593Smuzhiyun  * we know about.
264*4882a593Smuzhiyun  */
265*4882a593Smuzhiyun static const struct iw_ioctl_description standard_event[] = {
266*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVTXDROP)] = {
267*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_ADDR,
268*4882a593Smuzhiyun 	},
269*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVQUAL)] = {
270*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_QUAL,
271*4882a593Smuzhiyun 	},
272*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVCUSTOM)] = {
273*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
274*4882a593Smuzhiyun 		.token_size	= 1,
275*4882a593Smuzhiyun 		.max_tokens	= IW_CUSTOM_MAX,
276*4882a593Smuzhiyun 	},
277*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVREGISTERED)] = {
278*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_ADDR,
279*4882a593Smuzhiyun 	},
280*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVEXPIRED)] = {
281*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_ADDR,
282*4882a593Smuzhiyun 	},
283*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVGENIE)] = {
284*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
285*4882a593Smuzhiyun 		.token_size	= 1,
286*4882a593Smuzhiyun 		.max_tokens	= IW_GENERIC_IE_MAX,
287*4882a593Smuzhiyun 	},
288*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
289*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
290*4882a593Smuzhiyun 		.token_size	= 1,
291*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_michaelmicfailure),
292*4882a593Smuzhiyun 	},
293*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVASSOCREQIE)] = {
294*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
295*4882a593Smuzhiyun 		.token_size	= 1,
296*4882a593Smuzhiyun 		.max_tokens	= IW_GENERIC_IE_MAX,
297*4882a593Smuzhiyun 	},
298*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
299*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
300*4882a593Smuzhiyun 		.token_size	= 1,
301*4882a593Smuzhiyun 		.max_tokens	= IW_GENERIC_IE_MAX,
302*4882a593Smuzhiyun 	},
303*4882a593Smuzhiyun 	[IW_EVENT_IDX(IWEVPMKIDCAND)] = {
304*4882a593Smuzhiyun 		.header_type	= IW_HEADER_TYPE_POINT,
305*4882a593Smuzhiyun 		.token_size	= 1,
306*4882a593Smuzhiyun 		.max_tokens	= sizeof(struct iw_pmkid_cand),
307*4882a593Smuzhiyun 	},
308*4882a593Smuzhiyun };
309*4882a593Smuzhiyun static const unsigned int standard_event_num = ARRAY_SIZE(standard_event);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun /* Size (in bytes) of various events */
312*4882a593Smuzhiyun static const int event_type_size[] = {
313*4882a593Smuzhiyun 	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */
314*4882a593Smuzhiyun 	0,
315*4882a593Smuzhiyun 	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */
316*4882a593Smuzhiyun 	0,
317*4882a593Smuzhiyun 	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */
318*4882a593Smuzhiyun 	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */
319*4882a593Smuzhiyun 	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */
320*4882a593Smuzhiyun 	0,
321*4882a593Smuzhiyun 	IW_EV_POINT_LEN,		/* Without variable payload */
322*4882a593Smuzhiyun 	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
323*4882a593Smuzhiyun 	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */
324*4882a593Smuzhiyun };
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
327*4882a593Smuzhiyun static const int compat_event_type_size[] = {
328*4882a593Smuzhiyun 	IW_EV_COMPAT_LCP_LEN,		/* IW_HEADER_TYPE_NULL */
329*4882a593Smuzhiyun 	0,
330*4882a593Smuzhiyun 	IW_EV_COMPAT_CHAR_LEN,		/* IW_HEADER_TYPE_CHAR */
331*4882a593Smuzhiyun 	0,
332*4882a593Smuzhiyun 	IW_EV_COMPAT_UINT_LEN,		/* IW_HEADER_TYPE_UINT */
333*4882a593Smuzhiyun 	IW_EV_COMPAT_FREQ_LEN,		/* IW_HEADER_TYPE_FREQ */
334*4882a593Smuzhiyun 	IW_EV_COMPAT_ADDR_LEN,		/* IW_HEADER_TYPE_ADDR */
335*4882a593Smuzhiyun 	0,
336*4882a593Smuzhiyun 	IW_EV_COMPAT_POINT_LEN,		/* Without variable payload */
337*4882a593Smuzhiyun 	IW_EV_COMPAT_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */
338*4882a593Smuzhiyun 	IW_EV_COMPAT_QUAL_LEN,		/* IW_HEADER_TYPE_QUAL */
339*4882a593Smuzhiyun };
340*4882a593Smuzhiyun #endif
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun /* IW event code */
344*4882a593Smuzhiyun 
wireless_nlevent_flush(void)345*4882a593Smuzhiyun void wireless_nlevent_flush(void)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun 	struct sk_buff *skb;
348*4882a593Smuzhiyun 	struct net *net;
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	down_read(&net_rwsem);
351*4882a593Smuzhiyun 	for_each_net(net) {
352*4882a593Smuzhiyun 		while ((skb = skb_dequeue(&net->wext_nlevents)))
353*4882a593Smuzhiyun 			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
354*4882a593Smuzhiyun 				    GFP_KERNEL);
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun 	up_read(&net_rwsem);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wireless_nlevent_flush);
359*4882a593Smuzhiyun 
wext_netdev_notifier_call(struct notifier_block * nb,unsigned long state,void * ptr)360*4882a593Smuzhiyun static int wext_netdev_notifier_call(struct notifier_block *nb,
361*4882a593Smuzhiyun 				     unsigned long state, void *ptr)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun 	/*
364*4882a593Smuzhiyun 	 * When a netdev changes state in any way, flush all pending messages
365*4882a593Smuzhiyun 	 * to avoid them going out in a strange order, e.g. RTM_NEWLINK after
366*4882a593Smuzhiyun 	 * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close()
367*4882a593Smuzhiyun 	 * or similar - all of which could otherwise happen due to delays from
368*4882a593Smuzhiyun 	 * schedule_work().
369*4882a593Smuzhiyun 	 */
370*4882a593Smuzhiyun 	wireless_nlevent_flush();
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return NOTIFY_OK;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun static struct notifier_block wext_netdev_notifier = {
376*4882a593Smuzhiyun 	.notifier_call = wext_netdev_notifier_call,
377*4882a593Smuzhiyun };
378*4882a593Smuzhiyun 
wext_pernet_init(struct net * net)379*4882a593Smuzhiyun static int __net_init wext_pernet_init(struct net *net)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun 	skb_queue_head_init(&net->wext_nlevents);
382*4882a593Smuzhiyun 	return 0;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
wext_pernet_exit(struct net * net)385*4882a593Smuzhiyun static void __net_exit wext_pernet_exit(struct net *net)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	skb_queue_purge(&net->wext_nlevents);
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun static struct pernet_operations wext_pernet_ops = {
391*4882a593Smuzhiyun 	.init = wext_pernet_init,
392*4882a593Smuzhiyun 	.exit = wext_pernet_exit,
393*4882a593Smuzhiyun };
394*4882a593Smuzhiyun 
wireless_nlevent_init(void)395*4882a593Smuzhiyun static int __init wireless_nlevent_init(void)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	int err = register_pernet_subsys(&wext_pernet_ops);
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	if (err)
400*4882a593Smuzhiyun 		return err;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	err = register_netdevice_notifier(&wext_netdev_notifier);
403*4882a593Smuzhiyun 	if (err)
404*4882a593Smuzhiyun 		unregister_pernet_subsys(&wext_pernet_ops);
405*4882a593Smuzhiyun 	return err;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun subsys_initcall(wireless_nlevent_init);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun /* Process events generated by the wireless layer or the driver. */
wireless_nlevent_process(struct work_struct * work)411*4882a593Smuzhiyun static void wireless_nlevent_process(struct work_struct *work)
412*4882a593Smuzhiyun {
413*4882a593Smuzhiyun 	wireless_nlevent_flush();
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
417*4882a593Smuzhiyun 
rtnetlink_ifinfo_prep(struct net_device * dev,struct sk_buff * skb)418*4882a593Smuzhiyun static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev,
419*4882a593Smuzhiyun 					      struct sk_buff *skb)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct ifinfomsg *r;
422*4882a593Smuzhiyun 	struct nlmsghdr  *nlh;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0);
425*4882a593Smuzhiyun 	if (!nlh)
426*4882a593Smuzhiyun 		return NULL;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	r = nlmsg_data(nlh);
429*4882a593Smuzhiyun 	r->ifi_family = AF_UNSPEC;
430*4882a593Smuzhiyun 	r->__ifi_pad = 0;
431*4882a593Smuzhiyun 	r->ifi_type = dev->type;
432*4882a593Smuzhiyun 	r->ifi_index = dev->ifindex;
433*4882a593Smuzhiyun 	r->ifi_flags = dev_get_flags(dev);
434*4882a593Smuzhiyun 	r->ifi_change = 0;	/* Wireless changes don't affect those flags */
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (nla_put_string(skb, IFLA_IFNAME, dev->name))
437*4882a593Smuzhiyun 		goto nla_put_failure;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	return nlh;
440*4882a593Smuzhiyun  nla_put_failure:
441*4882a593Smuzhiyun 	nlmsg_cancel(skb, nlh);
442*4882a593Smuzhiyun 	return NULL;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun /*
447*4882a593Smuzhiyun  * Main event dispatcher. Called from other parts and drivers.
448*4882a593Smuzhiyun  * Send the event on the appropriate channels.
449*4882a593Smuzhiyun  * May be called from interrupt context.
450*4882a593Smuzhiyun  */
wireless_send_event(struct net_device * dev,unsigned int cmd,union iwreq_data * wrqu,const char * extra)451*4882a593Smuzhiyun void wireless_send_event(struct net_device *	dev,
452*4882a593Smuzhiyun 			 unsigned int		cmd,
453*4882a593Smuzhiyun 			 union iwreq_data *	wrqu,
454*4882a593Smuzhiyun 			 const char *		extra)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	const struct iw_ioctl_description *	descr = NULL;
457*4882a593Smuzhiyun 	int extra_len = 0;
458*4882a593Smuzhiyun 	struct iw_event  *event;		/* Mallocated whole event */
459*4882a593Smuzhiyun 	int event_len;				/* Its size */
460*4882a593Smuzhiyun 	int hdr_len;				/* Size of the event header */
461*4882a593Smuzhiyun 	int wrqu_off = 0;			/* Offset in wrqu */
462*4882a593Smuzhiyun 	/* Don't "optimise" the following variable, it will crash */
463*4882a593Smuzhiyun 	unsigned int	cmd_index;		/* *MUST* be unsigned */
464*4882a593Smuzhiyun 	struct sk_buff *skb;
465*4882a593Smuzhiyun 	struct nlmsghdr *nlh;
466*4882a593Smuzhiyun 	struct nlattr *nla;
467*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
468*4882a593Smuzhiyun 	struct __compat_iw_event *compat_event;
469*4882a593Smuzhiyun 	struct compat_iw_point compat_wrqu;
470*4882a593Smuzhiyun 	struct sk_buff *compskb;
471*4882a593Smuzhiyun #endif
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	/*
474*4882a593Smuzhiyun 	 * Nothing in the kernel sends scan events with data, be safe.
475*4882a593Smuzhiyun 	 * This is necessary because we cannot fix up scan event data
476*4882a593Smuzhiyun 	 * for compat, due to being contained in 'extra', but normally
477*4882a593Smuzhiyun 	 * applications are required to retrieve the scan data anyway
478*4882a593Smuzhiyun 	 * and no data is included in the event, this codifies that
479*4882a593Smuzhiyun 	 * practice.
480*4882a593Smuzhiyun 	 */
481*4882a593Smuzhiyun 	if (WARN_ON(cmd == SIOCGIWSCAN && extra))
482*4882a593Smuzhiyun 		extra = NULL;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	/* Get the description of the Event */
485*4882a593Smuzhiyun 	if (cmd <= SIOCIWLAST) {
486*4882a593Smuzhiyun 		cmd_index = IW_IOCTL_IDX(cmd);
487*4882a593Smuzhiyun 		if (cmd_index < standard_ioctl_num)
488*4882a593Smuzhiyun 			descr = &(standard_ioctl[cmd_index]);
489*4882a593Smuzhiyun 	} else {
490*4882a593Smuzhiyun 		cmd_index = IW_EVENT_IDX(cmd);
491*4882a593Smuzhiyun 		if (cmd_index < standard_event_num)
492*4882a593Smuzhiyun 			descr = &(standard_event[cmd_index]);
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 	/* Don't accept unknown events */
495*4882a593Smuzhiyun 	if (descr == NULL) {
496*4882a593Smuzhiyun 		/* Note : we don't return an error to the driver, because
497*4882a593Smuzhiyun 		 * the driver would not know what to do about it. It can't
498*4882a593Smuzhiyun 		 * return an error to the user, because the event is not
499*4882a593Smuzhiyun 		 * initiated by a user request.
500*4882a593Smuzhiyun 		 * The best the driver could do is to log an error message.
501*4882a593Smuzhiyun 		 * We will do it ourselves instead...
502*4882a593Smuzhiyun 		 */
503*4882a593Smuzhiyun 		netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
504*4882a593Smuzhiyun 			   cmd);
505*4882a593Smuzhiyun 		return;
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	/* Check extra parameters and set extra_len */
509*4882a593Smuzhiyun 	if (descr->header_type == IW_HEADER_TYPE_POINT) {
510*4882a593Smuzhiyun 		/* Check if number of token fits within bounds */
511*4882a593Smuzhiyun 		if (wrqu->data.length > descr->max_tokens) {
512*4882a593Smuzhiyun 			netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n",
513*4882a593Smuzhiyun 				   cmd, wrqu->data.length);
514*4882a593Smuzhiyun 			return;
515*4882a593Smuzhiyun 		}
516*4882a593Smuzhiyun 		if (wrqu->data.length < descr->min_tokens) {
517*4882a593Smuzhiyun 			netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n",
518*4882a593Smuzhiyun 				   cmd, wrqu->data.length);
519*4882a593Smuzhiyun 			return;
520*4882a593Smuzhiyun 		}
521*4882a593Smuzhiyun 		/* Calculate extra_len - extra is NULL for restricted events */
522*4882a593Smuzhiyun 		if (extra != NULL)
523*4882a593Smuzhiyun 			extra_len = wrqu->data.length * descr->token_size;
524*4882a593Smuzhiyun 		/* Always at an offset in wrqu */
525*4882a593Smuzhiyun 		wrqu_off = IW_EV_POINT_OFF;
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	/* Total length of the event */
529*4882a593Smuzhiyun 	hdr_len = event_type_size[descr->header_type];
530*4882a593Smuzhiyun 	event_len = hdr_len + extra_len;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	/*
533*4882a593Smuzhiyun 	 * The problem for 64/32 bit.
534*4882a593Smuzhiyun 	 *
535*4882a593Smuzhiyun 	 * On 64-bit, a regular event is laid out as follows:
536*4882a593Smuzhiyun 	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
537*4882a593Smuzhiyun 	 *      | event.len | event.cmd |     p a d d i n g     |
538*4882a593Smuzhiyun 	 *      | wrqu data ... (with the correct size)         |
539*4882a593Smuzhiyun 	 *
540*4882a593Smuzhiyun 	 * This padding exists because we manipulate event->u,
541*4882a593Smuzhiyun 	 * and 'event' is not packed.
542*4882a593Smuzhiyun 	 *
543*4882a593Smuzhiyun 	 * An iw_point event is laid out like this instead:
544*4882a593Smuzhiyun 	 *      |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |
545*4882a593Smuzhiyun 	 *      | event.len | event.cmd |     p a d d i n g     |
546*4882a593Smuzhiyun 	 *      | iwpnt.len | iwpnt.flg |     p a d d i n g     |
547*4882a593Smuzhiyun 	 *      | extra data  ...
548*4882a593Smuzhiyun 	 *
549*4882a593Smuzhiyun 	 * The second padding exists because struct iw_point is extended,
550*4882a593Smuzhiyun 	 * but this depends on the platform...
551*4882a593Smuzhiyun 	 *
552*4882a593Smuzhiyun 	 * On 32-bit, all the padding shouldn't be there.
553*4882a593Smuzhiyun 	 */
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
556*4882a593Smuzhiyun 	if (!skb)
557*4882a593Smuzhiyun 		return;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	/* Send via the RtNetlink event channel */
560*4882a593Smuzhiyun 	nlh = rtnetlink_ifinfo_prep(dev, skb);
561*4882a593Smuzhiyun 	if (WARN_ON(!nlh)) {
562*4882a593Smuzhiyun 		kfree_skb(skb);
563*4882a593Smuzhiyun 		return;
564*4882a593Smuzhiyun 	}
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	/* Add the wireless events in the netlink packet */
567*4882a593Smuzhiyun 	nla = nla_reserve(skb, IFLA_WIRELESS, event_len);
568*4882a593Smuzhiyun 	if (!nla) {
569*4882a593Smuzhiyun 		kfree_skb(skb);
570*4882a593Smuzhiyun 		return;
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 	event = nla_data(nla);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	/* Fill event - first clear to avoid data leaking */
575*4882a593Smuzhiyun 	memset(event, 0, hdr_len);
576*4882a593Smuzhiyun 	event->len = event_len;
577*4882a593Smuzhiyun 	event->cmd = cmd;
578*4882a593Smuzhiyun 	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);
579*4882a593Smuzhiyun 	if (extra_len)
580*4882a593Smuzhiyun 		memcpy(((char *) event) + hdr_len, extra, extra_len);
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun 	nlmsg_end(skb, nlh);
583*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
584*4882a593Smuzhiyun 	hdr_len = compat_event_type_size[descr->header_type];
585*4882a593Smuzhiyun 	event_len = hdr_len + extra_len;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
588*4882a593Smuzhiyun 	if (!compskb) {
589*4882a593Smuzhiyun 		kfree_skb(skb);
590*4882a593Smuzhiyun 		return;
591*4882a593Smuzhiyun 	}
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	/* Send via the RtNetlink event channel */
594*4882a593Smuzhiyun 	nlh = rtnetlink_ifinfo_prep(dev, compskb);
595*4882a593Smuzhiyun 	if (WARN_ON(!nlh)) {
596*4882a593Smuzhiyun 		kfree_skb(skb);
597*4882a593Smuzhiyun 		kfree_skb(compskb);
598*4882a593Smuzhiyun 		return;
599*4882a593Smuzhiyun 	}
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	/* Add the wireless events in the netlink packet */
602*4882a593Smuzhiyun 	nla = nla_reserve(compskb, IFLA_WIRELESS, event_len);
603*4882a593Smuzhiyun 	if (!nla) {
604*4882a593Smuzhiyun 		kfree_skb(skb);
605*4882a593Smuzhiyun 		kfree_skb(compskb);
606*4882a593Smuzhiyun 		return;
607*4882a593Smuzhiyun 	}
608*4882a593Smuzhiyun 	compat_event = nla_data(nla);
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	compat_event->len = event_len;
611*4882a593Smuzhiyun 	compat_event->cmd = cmd;
612*4882a593Smuzhiyun 	if (descr->header_type == IW_HEADER_TYPE_POINT) {
613*4882a593Smuzhiyun 		compat_wrqu.length = wrqu->data.length;
614*4882a593Smuzhiyun 		compat_wrqu.flags = wrqu->data.flags;
615*4882a593Smuzhiyun 		memcpy(&compat_event->pointer,
616*4882a593Smuzhiyun 			((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF,
617*4882a593Smuzhiyun 			hdr_len - IW_EV_COMPAT_LCP_LEN);
618*4882a593Smuzhiyun 		if (extra_len)
619*4882a593Smuzhiyun 			memcpy(((char *) compat_event) + hdr_len,
620*4882a593Smuzhiyun 				extra, extra_len);
621*4882a593Smuzhiyun 	} else {
622*4882a593Smuzhiyun 		/* extra_len must be zero, so no if (extra) needed */
623*4882a593Smuzhiyun 		memcpy(&compat_event->pointer, wrqu,
624*4882a593Smuzhiyun 			hdr_len - IW_EV_COMPAT_LCP_LEN);
625*4882a593Smuzhiyun 	}
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	nlmsg_end(compskb, nlh);
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	skb_shinfo(skb)->frag_list = compskb;
630*4882a593Smuzhiyun #endif
631*4882a593Smuzhiyun 	skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
632*4882a593Smuzhiyun 	schedule_work(&wireless_nlevent_work);
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun EXPORT_SYMBOL(wireless_send_event);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun /* IW handlers */
639*4882a593Smuzhiyun 
get_wireless_stats(struct net_device * dev)640*4882a593Smuzhiyun struct iw_statistics *get_wireless_stats(struct net_device *dev)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun #ifdef CONFIG_WIRELESS_EXT
643*4882a593Smuzhiyun 	if ((dev->wireless_handlers != NULL) &&
644*4882a593Smuzhiyun 	   (dev->wireless_handlers->get_wireless_stats != NULL))
645*4882a593Smuzhiyun 		return dev->wireless_handlers->get_wireless_stats(dev);
646*4882a593Smuzhiyun #endif
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun #ifdef CONFIG_CFG80211_WEXT
649*4882a593Smuzhiyun 	if (dev->ieee80211_ptr &&
650*4882a593Smuzhiyun 	    dev->ieee80211_ptr->wiphy &&
651*4882a593Smuzhiyun 	    dev->ieee80211_ptr->wiphy->wext &&
652*4882a593Smuzhiyun 	    dev->ieee80211_ptr->wiphy->wext->get_wireless_stats)
653*4882a593Smuzhiyun 		return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev);
654*4882a593Smuzhiyun #endif
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	/* not found */
657*4882a593Smuzhiyun 	return NULL;
658*4882a593Smuzhiyun }
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun /* noinline to avoid a bogus warning with -O3 */
iw_handler_get_iwstats(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)661*4882a593Smuzhiyun static noinline int iw_handler_get_iwstats(struct net_device *	dev,
662*4882a593Smuzhiyun 				  struct iw_request_info *	info,
663*4882a593Smuzhiyun 				  union iwreq_data *		wrqu,
664*4882a593Smuzhiyun 				  char *			extra)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun 	/* Get stats from the driver */
667*4882a593Smuzhiyun 	struct iw_statistics *stats;
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	stats = get_wireless_stats(dev);
670*4882a593Smuzhiyun 	if (stats) {
671*4882a593Smuzhiyun 		/* Copy statistics to extra */
672*4882a593Smuzhiyun 		memcpy(extra, stats, sizeof(struct iw_statistics));
673*4882a593Smuzhiyun 		wrqu->data.length = sizeof(struct iw_statistics);
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 		/* Check if we need to clear the updated flag */
676*4882a593Smuzhiyun 		if (wrqu->data.flags != 0)
677*4882a593Smuzhiyun 			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
678*4882a593Smuzhiyun 		return 0;
679*4882a593Smuzhiyun 	} else
680*4882a593Smuzhiyun 		return -EOPNOTSUPP;
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun 
get_handler(struct net_device * dev,unsigned int cmd)683*4882a593Smuzhiyun static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun 	/* Don't "optimise" the following variable, it will crash */
686*4882a593Smuzhiyun 	unsigned int	index;		/* *MUST* be unsigned */
687*4882a593Smuzhiyun 	const struct iw_handler_def *handlers = NULL;
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun #ifdef CONFIG_CFG80211_WEXT
690*4882a593Smuzhiyun 	if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy)
691*4882a593Smuzhiyun 		handlers = dev->ieee80211_ptr->wiphy->wext;
692*4882a593Smuzhiyun #endif
693*4882a593Smuzhiyun #ifdef CONFIG_WIRELESS_EXT
694*4882a593Smuzhiyun 	if (dev->wireless_handlers)
695*4882a593Smuzhiyun 		handlers = dev->wireless_handlers;
696*4882a593Smuzhiyun #endif
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	if (!handlers)
699*4882a593Smuzhiyun 		return NULL;
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	/* Try as a standard command */
702*4882a593Smuzhiyun 	index = IW_IOCTL_IDX(cmd);
703*4882a593Smuzhiyun 	if (index < handlers->num_standard)
704*4882a593Smuzhiyun 		return handlers->standard[index];
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun #ifdef CONFIG_WEXT_PRIV
707*4882a593Smuzhiyun 	/* Try as a private command */
708*4882a593Smuzhiyun 	index = cmd - SIOCIWFIRSTPRIV;
709*4882a593Smuzhiyun 	if (index < handlers->num_private)
710*4882a593Smuzhiyun 		return handlers->private[index];
711*4882a593Smuzhiyun #endif
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	/* Not found */
714*4882a593Smuzhiyun 	return NULL;
715*4882a593Smuzhiyun }
716*4882a593Smuzhiyun 
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)717*4882a593Smuzhiyun static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
718*4882a593Smuzhiyun 				   const struct iw_ioctl_description *descr,
719*4882a593Smuzhiyun 				   iw_handler handler, struct net_device *dev,
720*4882a593Smuzhiyun 				   struct iw_request_info *info)
721*4882a593Smuzhiyun {
722*4882a593Smuzhiyun 	int err, extra_size, user_length = 0, essid_compat = 0;
723*4882a593Smuzhiyun 	char *extra;
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 	/* Calculate space needed by arguments. Always allocate
726*4882a593Smuzhiyun 	 * for max space.
727*4882a593Smuzhiyun 	 */
728*4882a593Smuzhiyun 	extra_size = descr->max_tokens * descr->token_size;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	/* Check need for ESSID compatibility for WE < 21 */
731*4882a593Smuzhiyun 	switch (cmd) {
732*4882a593Smuzhiyun 	case SIOCSIWESSID:
733*4882a593Smuzhiyun 	case SIOCGIWESSID:
734*4882a593Smuzhiyun 	case SIOCSIWNICKN:
735*4882a593Smuzhiyun 	case SIOCGIWNICKN:
736*4882a593Smuzhiyun 		if (iwp->length == descr->max_tokens + 1)
737*4882a593Smuzhiyun 			essid_compat = 1;
738*4882a593Smuzhiyun 		else if (IW_IS_SET(cmd) && (iwp->length != 0)) {
739*4882a593Smuzhiyun 			char essid[IW_ESSID_MAX_SIZE + 1];
740*4882a593Smuzhiyun 			unsigned int len;
741*4882a593Smuzhiyun 			len = iwp->length * descr->token_size;
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 			if (len > IW_ESSID_MAX_SIZE)
744*4882a593Smuzhiyun 				return -EFAULT;
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 			err = copy_from_user(essid, iwp->pointer, len);
747*4882a593Smuzhiyun 			if (err)
748*4882a593Smuzhiyun 				return -EFAULT;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 			if (essid[iwp->length - 1] == '\0')
751*4882a593Smuzhiyun 				essid_compat = 1;
752*4882a593Smuzhiyun 		}
753*4882a593Smuzhiyun 		break;
754*4882a593Smuzhiyun 	default:
755*4882a593Smuzhiyun 		break;
756*4882a593Smuzhiyun 	}
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun 	iwp->length -= essid_compat;
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 	/* Check what user space is giving us */
761*4882a593Smuzhiyun 	if (IW_IS_SET(cmd)) {
762*4882a593Smuzhiyun 		/* Check NULL pointer */
763*4882a593Smuzhiyun 		if (!iwp->pointer && iwp->length != 0)
764*4882a593Smuzhiyun 			return -EFAULT;
765*4882a593Smuzhiyun 		/* Check if number of token fits within bounds */
766*4882a593Smuzhiyun 		if (iwp->length > descr->max_tokens)
767*4882a593Smuzhiyun 			return -E2BIG;
768*4882a593Smuzhiyun 		if (iwp->length < descr->min_tokens)
769*4882a593Smuzhiyun 			return -EINVAL;
770*4882a593Smuzhiyun 	} else {
771*4882a593Smuzhiyun 		/* Check NULL pointer */
772*4882a593Smuzhiyun 		if (!iwp->pointer)
773*4882a593Smuzhiyun 			return -EFAULT;
774*4882a593Smuzhiyun 		/* Save user space buffer size for checking */
775*4882a593Smuzhiyun 		user_length = iwp->length;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 		/* Don't check if user_length > max to allow forward
778*4882a593Smuzhiyun 		 * compatibility. The test user_length < min is
779*4882a593Smuzhiyun 		 * implied by the test at the end.
780*4882a593Smuzhiyun 		 */
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 		/* Support for very large requests */
783*4882a593Smuzhiyun 		if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
784*4882a593Smuzhiyun 		    (user_length > descr->max_tokens)) {
785*4882a593Smuzhiyun 			/* Allow userspace to GET more than max so
786*4882a593Smuzhiyun 			 * we can support any size GET requests.
787*4882a593Smuzhiyun 			 * There is still a limit : -ENOMEM.
788*4882a593Smuzhiyun 			 */
789*4882a593Smuzhiyun 			extra_size = user_length * descr->token_size;
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 			/* Note : user_length is originally a __u16,
792*4882a593Smuzhiyun 			 * and token_size is controlled by us,
793*4882a593Smuzhiyun 			 * so extra_size won't get negative and
794*4882a593Smuzhiyun 			 * won't overflow...
795*4882a593Smuzhiyun 			 */
796*4882a593Smuzhiyun 		}
797*4882a593Smuzhiyun 	}
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	/* kzalloc() ensures NULL-termination for essid_compat. */
800*4882a593Smuzhiyun 	extra = kzalloc(extra_size, GFP_KERNEL);
801*4882a593Smuzhiyun 	if (!extra)
802*4882a593Smuzhiyun 		return -ENOMEM;
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	/* If it is a SET, get all the extra data in here */
805*4882a593Smuzhiyun 	if (IW_IS_SET(cmd) && (iwp->length != 0)) {
806*4882a593Smuzhiyun 		if (copy_from_user(extra, iwp->pointer,
807*4882a593Smuzhiyun 				   iwp->length *
808*4882a593Smuzhiyun 				   descr->token_size)) {
809*4882a593Smuzhiyun 			err = -EFAULT;
810*4882a593Smuzhiyun 			goto out;
811*4882a593Smuzhiyun 		}
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		if (cmd == SIOCSIWENCODEEXT) {
814*4882a593Smuzhiyun 			struct iw_encode_ext *ee = (void *) extra;
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 			if (iwp->length < sizeof(*ee) + ee->key_len) {
817*4882a593Smuzhiyun 				err = -EFAULT;
818*4882a593Smuzhiyun 				goto out;
819*4882a593Smuzhiyun 			}
820*4882a593Smuzhiyun 		}
821*4882a593Smuzhiyun 	}
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) {
824*4882a593Smuzhiyun 		/*
825*4882a593Smuzhiyun 		 * If this is a GET, but not NOMAX, it means that the extra
826*4882a593Smuzhiyun 		 * data is not bounded by userspace, but by max_tokens. Thus
827*4882a593Smuzhiyun 		 * set the length to max_tokens. This matches the extra data
828*4882a593Smuzhiyun 		 * allocation.
829*4882a593Smuzhiyun 		 * The driver should fill it with the number of tokens it
830*4882a593Smuzhiyun 		 * provided, and it may check iwp->length rather than having
831*4882a593Smuzhiyun 		 * knowledge of max_tokens. If the driver doesn't change the
832*4882a593Smuzhiyun 		 * iwp->length, this ioctl just copies back max_token tokens
833*4882a593Smuzhiyun 		 * filled with zeroes. Hopefully the driver isn't claiming
834*4882a593Smuzhiyun 		 * them to be valid data.
835*4882a593Smuzhiyun 		 */
836*4882a593Smuzhiyun 		iwp->length = descr->max_tokens;
837*4882a593Smuzhiyun 	}
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	err = handler(dev, info, (union iwreq_data *) iwp, extra);
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 	iwp->length += essid_compat;
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	/* If we have something to return to the user */
844*4882a593Smuzhiyun 	if (!err && IW_IS_GET(cmd)) {
845*4882a593Smuzhiyun 		/* Check if there is enough buffer up there */
846*4882a593Smuzhiyun 		if (user_length < iwp->length) {
847*4882a593Smuzhiyun 			err = -E2BIG;
848*4882a593Smuzhiyun 			goto out;
849*4882a593Smuzhiyun 		}
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 		if (copy_to_user(iwp->pointer, extra,
852*4882a593Smuzhiyun 				 iwp->length *
853*4882a593Smuzhiyun 				 descr->token_size)) {
854*4882a593Smuzhiyun 			err = -EFAULT;
855*4882a593Smuzhiyun 			goto out;
856*4882a593Smuzhiyun 		}
857*4882a593Smuzhiyun 	}
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	/* Generate an event to notify listeners of the change */
860*4882a593Smuzhiyun 	if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
861*4882a593Smuzhiyun 	    ((err == 0) || (err == -EIWCOMMIT))) {
862*4882a593Smuzhiyun 		union iwreq_data *data = (union iwreq_data *) iwp;
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 		if (descr->flags & IW_DESCR_FLAG_RESTRICT)
865*4882a593Smuzhiyun 			/* If the event is restricted, don't
866*4882a593Smuzhiyun 			 * export the payload.
867*4882a593Smuzhiyun 			 */
868*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, data, NULL);
869*4882a593Smuzhiyun 		else
870*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, data, extra);
871*4882a593Smuzhiyun 	}
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun out:
874*4882a593Smuzhiyun 	kfree(extra);
875*4882a593Smuzhiyun 	return err;
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun /*
879*4882a593Smuzhiyun  * Call the commit handler in the driver
880*4882a593Smuzhiyun  * (if exist and if conditions are right)
881*4882a593Smuzhiyun  *
882*4882a593Smuzhiyun  * Note : our current commit strategy is currently pretty dumb,
883*4882a593Smuzhiyun  * but we will be able to improve on that...
884*4882a593Smuzhiyun  * The goal is to try to agreagate as many changes as possible
885*4882a593Smuzhiyun  * before doing the commit. Drivers that will define a commit handler
886*4882a593Smuzhiyun  * are usually those that need a reset after changing parameters, so
887*4882a593Smuzhiyun  * we want to minimise the number of reset.
888*4882a593Smuzhiyun  * A cool idea is to use a timer : at each "set" command, we re-set the
889*4882a593Smuzhiyun  * timer, when the timer eventually fires, we call the driver.
890*4882a593Smuzhiyun  * Hopefully, more on that later.
891*4882a593Smuzhiyun  *
892*4882a593Smuzhiyun  * Also, I'm waiting to see how many people will complain about the
893*4882a593Smuzhiyun  * netif_running(dev) test. I'm open on that one...
894*4882a593Smuzhiyun  * Hopefully, the driver will remember to do a commit in "open()" ;-)
895*4882a593Smuzhiyun  */
call_commit_handler(struct net_device * dev)896*4882a593Smuzhiyun int call_commit_handler(struct net_device *dev)
897*4882a593Smuzhiyun {
898*4882a593Smuzhiyun #ifdef CONFIG_WIRELESS_EXT
899*4882a593Smuzhiyun 	if (netif_running(dev) &&
900*4882a593Smuzhiyun 	    dev->wireless_handlers &&
901*4882a593Smuzhiyun 	    dev->wireless_handlers->standard[0])
902*4882a593Smuzhiyun 		/* Call the commit handler on the driver */
903*4882a593Smuzhiyun 		return dev->wireless_handlers->standard[0](dev, NULL,
904*4882a593Smuzhiyun 							   NULL, NULL);
905*4882a593Smuzhiyun 	else
906*4882a593Smuzhiyun 		return 0;		/* Command completed successfully */
907*4882a593Smuzhiyun #else
908*4882a593Smuzhiyun 	/* cfg80211 has no commit */
909*4882a593Smuzhiyun 	return 0;
910*4882a593Smuzhiyun #endif
911*4882a593Smuzhiyun }
912*4882a593Smuzhiyun 
913*4882a593Smuzhiyun /*
914*4882a593Smuzhiyun  * Main IOCTl dispatcher.
915*4882a593Smuzhiyun  * Check the type of IOCTL and call the appropriate wrapper...
916*4882a593Smuzhiyun  */
wireless_process_ioctl(struct net * net,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,wext_ioctl_func standard,wext_ioctl_func private)917*4882a593Smuzhiyun static int wireless_process_ioctl(struct net *net, struct iwreq *iwr,
918*4882a593Smuzhiyun 				  unsigned int cmd,
919*4882a593Smuzhiyun 				  struct iw_request_info *info,
920*4882a593Smuzhiyun 				  wext_ioctl_func standard,
921*4882a593Smuzhiyun 				  wext_ioctl_func private)
922*4882a593Smuzhiyun {
923*4882a593Smuzhiyun 	struct net_device *dev;
924*4882a593Smuzhiyun 	iw_handler	handler;
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	/* Permissions are already checked in dev_ioctl() before calling us.
927*4882a593Smuzhiyun 	 * The copy_to/from_user() of ifr is also dealt with in there */
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun 	/* Make sure the device exist */
930*4882a593Smuzhiyun 	if ((dev = __dev_get_by_name(net, iwr->ifr_name)) == NULL)
931*4882a593Smuzhiyun 		return -ENODEV;
932*4882a593Smuzhiyun 
933*4882a593Smuzhiyun 	/* A bunch of special cases, then the generic case...
934*4882a593Smuzhiyun 	 * Note that 'cmd' is already filtered in dev_ioctl() with
935*4882a593Smuzhiyun 	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */
936*4882a593Smuzhiyun 	if (cmd == SIOCGIWSTATS)
937*4882a593Smuzhiyun 		return standard(dev, iwr, cmd, info,
938*4882a593Smuzhiyun 				&iw_handler_get_iwstats);
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun #ifdef CONFIG_WEXT_PRIV
941*4882a593Smuzhiyun 	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)
942*4882a593Smuzhiyun 		return standard(dev, iwr, cmd, info,
943*4882a593Smuzhiyun 				iw_handler_get_private);
944*4882a593Smuzhiyun #endif
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	/* Basic check */
947*4882a593Smuzhiyun 	if (!netif_device_present(dev))
948*4882a593Smuzhiyun 		return -ENODEV;
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun 	/* New driver API : try to find the handler */
951*4882a593Smuzhiyun 	handler = get_handler(dev, cmd);
952*4882a593Smuzhiyun 	if (handler) {
953*4882a593Smuzhiyun 		/* Standard and private are not the same */
954*4882a593Smuzhiyun 		if (cmd < SIOCIWFIRSTPRIV)
955*4882a593Smuzhiyun 			return standard(dev, iwr, cmd, info, handler);
956*4882a593Smuzhiyun 		else if (private)
957*4882a593Smuzhiyun 			return private(dev, iwr, cmd, info, handler);
958*4882a593Smuzhiyun 	}
959*4882a593Smuzhiyun 	return -EOPNOTSUPP;
960*4882a593Smuzhiyun }
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun /* If command is `set a parameter', or `get the encoding parameters',
963*4882a593Smuzhiyun  * check if the user has the right to do it.
964*4882a593Smuzhiyun  */
wext_permission_check(unsigned int cmd)965*4882a593Smuzhiyun static int wext_permission_check(unsigned int cmd)
966*4882a593Smuzhiyun {
967*4882a593Smuzhiyun 	if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE ||
968*4882a593Smuzhiyun 	     cmd == SIOCGIWENCODEEXT) &&
969*4882a593Smuzhiyun 	    !capable(CAP_NET_ADMIN))
970*4882a593Smuzhiyun 		return -EPERM;
971*4882a593Smuzhiyun 
972*4882a593Smuzhiyun 	return 0;
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun /* entry point from dev ioctl */
wext_ioctl_dispatch(struct net * net,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,wext_ioctl_func standard,wext_ioctl_func private)976*4882a593Smuzhiyun static int wext_ioctl_dispatch(struct net *net, struct iwreq *iwr,
977*4882a593Smuzhiyun 			       unsigned int cmd, struct iw_request_info *info,
978*4882a593Smuzhiyun 			       wext_ioctl_func standard,
979*4882a593Smuzhiyun 			       wext_ioctl_func private)
980*4882a593Smuzhiyun {
981*4882a593Smuzhiyun 	int ret = wext_permission_check(cmd);
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	if (ret)
984*4882a593Smuzhiyun 		return ret;
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun 	dev_load(net, iwr->ifr_name);
987*4882a593Smuzhiyun 	rtnl_lock();
988*4882a593Smuzhiyun 	ret = wireless_process_ioctl(net, iwr, cmd, info, standard, private);
989*4882a593Smuzhiyun 	rtnl_unlock();
990*4882a593Smuzhiyun 
991*4882a593Smuzhiyun 	return ret;
992*4882a593Smuzhiyun }
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun /*
995*4882a593Smuzhiyun  * Wrapper to call a standard Wireless Extension handler.
996*4882a593Smuzhiyun  * We do various checks and also take care of moving data between
997*4882a593Smuzhiyun  * user space and kernel space.
998*4882a593Smuzhiyun  */
ioctl_standard_call(struct net_device * dev,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,iw_handler handler)999*4882a593Smuzhiyun static int ioctl_standard_call(struct net_device *	dev,
1000*4882a593Smuzhiyun 			       struct iwreq		*iwr,
1001*4882a593Smuzhiyun 			       unsigned int		cmd,
1002*4882a593Smuzhiyun 			       struct iw_request_info	*info,
1003*4882a593Smuzhiyun 			       iw_handler		handler)
1004*4882a593Smuzhiyun {
1005*4882a593Smuzhiyun 	const struct iw_ioctl_description *	descr;
1006*4882a593Smuzhiyun 	int					ret = -EINVAL;
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	/* Get the description of the IOCTL */
1009*4882a593Smuzhiyun 	if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
1010*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1011*4882a593Smuzhiyun 	descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
1012*4882a593Smuzhiyun 
1013*4882a593Smuzhiyun 	/* Check if we have a pointer to user space data or not */
1014*4882a593Smuzhiyun 	if (descr->header_type != IW_HEADER_TYPE_POINT) {
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 		/* No extra arguments. Trivial to handle */
1017*4882a593Smuzhiyun 		ret = handler(dev, info, &(iwr->u), NULL);
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 		/* Generate an event to notify listeners of the change */
1020*4882a593Smuzhiyun 		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
1021*4882a593Smuzhiyun 		   ((ret == 0) || (ret == -EIWCOMMIT)))
1022*4882a593Smuzhiyun 			wireless_send_event(dev, cmd, &(iwr->u), NULL);
1023*4882a593Smuzhiyun 	} else {
1024*4882a593Smuzhiyun 		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
1025*4882a593Smuzhiyun 					      handler, dev, info);
1026*4882a593Smuzhiyun 	}
1027*4882a593Smuzhiyun 
1028*4882a593Smuzhiyun 	/* Call commit handler if needed and defined */
1029*4882a593Smuzhiyun 	if (ret == -EIWCOMMIT)
1030*4882a593Smuzhiyun 		ret = call_commit_handler(dev);
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun 	/* Here, we will generate the appropriate event if needed */
1033*4882a593Smuzhiyun 
1034*4882a593Smuzhiyun 	return ret;
1035*4882a593Smuzhiyun }
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun 
wext_handle_ioctl(struct net * net,unsigned int cmd,void __user * arg)1038*4882a593Smuzhiyun int wext_handle_ioctl(struct net *net, unsigned int cmd, void __user *arg)
1039*4882a593Smuzhiyun {
1040*4882a593Smuzhiyun 	struct iw_request_info info = { .cmd = cmd, .flags = 0 };
1041*4882a593Smuzhiyun 	struct iwreq iwr;
1042*4882a593Smuzhiyun 	int ret;
1043*4882a593Smuzhiyun 
1044*4882a593Smuzhiyun 	if (copy_from_user(&iwr, arg, sizeof(iwr)))
1045*4882a593Smuzhiyun 		return -EFAULT;
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	iwr.ifr_name[sizeof(iwr.ifr_name) - 1] = 0;
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 	ret = wext_ioctl_dispatch(net, &iwr, cmd, &info,
1050*4882a593Smuzhiyun 				  ioctl_standard_call,
1051*4882a593Smuzhiyun 				  ioctl_private_call);
1052*4882a593Smuzhiyun 	if (ret >= 0 &&
1053*4882a593Smuzhiyun 	    IW_IS_GET(cmd) &&
1054*4882a593Smuzhiyun 	    copy_to_user(arg, &iwr, sizeof(struct iwreq)))
1055*4882a593Smuzhiyun 		return -EFAULT;
1056*4882a593Smuzhiyun 
1057*4882a593Smuzhiyun 	return ret;
1058*4882a593Smuzhiyun }
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
compat_standard_call(struct net_device * dev,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,iw_handler handler)1061*4882a593Smuzhiyun static int compat_standard_call(struct net_device	*dev,
1062*4882a593Smuzhiyun 				struct iwreq		*iwr,
1063*4882a593Smuzhiyun 				unsigned int		cmd,
1064*4882a593Smuzhiyun 				struct iw_request_info	*info,
1065*4882a593Smuzhiyun 				iw_handler		handler)
1066*4882a593Smuzhiyun {
1067*4882a593Smuzhiyun 	const struct iw_ioctl_description *descr;
1068*4882a593Smuzhiyun 	struct compat_iw_point *iwp_compat;
1069*4882a593Smuzhiyun 	struct iw_point iwp;
1070*4882a593Smuzhiyun 	int err;
1071*4882a593Smuzhiyun 
1072*4882a593Smuzhiyun 	descr = standard_ioctl + IW_IOCTL_IDX(cmd);
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	if (descr->header_type != IW_HEADER_TYPE_POINT)
1075*4882a593Smuzhiyun 		return ioctl_standard_call(dev, iwr, cmd, info, handler);
1076*4882a593Smuzhiyun 
1077*4882a593Smuzhiyun 	iwp_compat = (struct compat_iw_point *) &iwr->u.data;
1078*4882a593Smuzhiyun 	iwp.pointer = compat_ptr(iwp_compat->pointer);
1079*4882a593Smuzhiyun 	iwp.length = iwp_compat->length;
1080*4882a593Smuzhiyun 	iwp.flags = iwp_compat->flags;
1081*4882a593Smuzhiyun 
1082*4882a593Smuzhiyun 	err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info);
1083*4882a593Smuzhiyun 
1084*4882a593Smuzhiyun 	iwp_compat->pointer = ptr_to_compat(iwp.pointer);
1085*4882a593Smuzhiyun 	iwp_compat->length = iwp.length;
1086*4882a593Smuzhiyun 	iwp_compat->flags = iwp.flags;
1087*4882a593Smuzhiyun 
1088*4882a593Smuzhiyun 	return err;
1089*4882a593Smuzhiyun }
1090*4882a593Smuzhiyun 
compat_wext_handle_ioctl(struct net * net,unsigned int cmd,unsigned long arg)1091*4882a593Smuzhiyun int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
1092*4882a593Smuzhiyun 			     unsigned long arg)
1093*4882a593Smuzhiyun {
1094*4882a593Smuzhiyun 	void __user *argp = (void __user *)arg;
1095*4882a593Smuzhiyun 	struct iw_request_info info;
1096*4882a593Smuzhiyun 	struct iwreq iwr;
1097*4882a593Smuzhiyun 	char *colon;
1098*4882a593Smuzhiyun 	int ret;
1099*4882a593Smuzhiyun 
1100*4882a593Smuzhiyun 	if (copy_from_user(&iwr, argp, sizeof(struct iwreq)))
1101*4882a593Smuzhiyun 		return -EFAULT;
1102*4882a593Smuzhiyun 
1103*4882a593Smuzhiyun 	iwr.ifr_name[IFNAMSIZ-1] = 0;
1104*4882a593Smuzhiyun 	colon = strchr(iwr.ifr_name, ':');
1105*4882a593Smuzhiyun 	if (colon)
1106*4882a593Smuzhiyun 		*colon = 0;
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun 	info.cmd = cmd;
1109*4882a593Smuzhiyun 	info.flags = IW_REQUEST_FLAG_COMPAT;
1110*4882a593Smuzhiyun 
1111*4882a593Smuzhiyun 	ret = wext_ioctl_dispatch(net, &iwr, cmd, &info,
1112*4882a593Smuzhiyun 				  compat_standard_call,
1113*4882a593Smuzhiyun 				  compat_private_call);
1114*4882a593Smuzhiyun 
1115*4882a593Smuzhiyun 	if (ret >= 0 &&
1116*4882a593Smuzhiyun 	    IW_IS_GET(cmd) &&
1117*4882a593Smuzhiyun 	    copy_to_user(argp, &iwr, sizeof(struct iwreq)))
1118*4882a593Smuzhiyun 		return -EFAULT;
1119*4882a593Smuzhiyun 
1120*4882a593Smuzhiyun 	return ret;
1121*4882a593Smuzhiyun }
1122*4882a593Smuzhiyun #endif
1123*4882a593Smuzhiyun 
iwe_stream_add_event(struct iw_request_info * info,char * stream,char * ends,struct iw_event * iwe,int event_len)1124*4882a593Smuzhiyun char *iwe_stream_add_event(struct iw_request_info *info, char *stream,
1125*4882a593Smuzhiyun 			   char *ends, struct iw_event *iwe, int event_len)
1126*4882a593Smuzhiyun {
1127*4882a593Smuzhiyun 	int lcp_len = iwe_stream_lcp_len(info);
1128*4882a593Smuzhiyun 
1129*4882a593Smuzhiyun 	event_len = iwe_stream_event_len_adjust(info, event_len);
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun 	/* Check if it's possible */
1132*4882a593Smuzhiyun 	if (likely((stream + event_len) < ends)) {
1133*4882a593Smuzhiyun 		iwe->len = event_len;
1134*4882a593Smuzhiyun 		/* Beware of alignement issues on 64 bits */
1135*4882a593Smuzhiyun 		memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
1136*4882a593Smuzhiyun 		memcpy(stream + lcp_len, &iwe->u,
1137*4882a593Smuzhiyun 		       event_len - lcp_len);
1138*4882a593Smuzhiyun 		stream += event_len;
1139*4882a593Smuzhiyun 	}
1140*4882a593Smuzhiyun 
1141*4882a593Smuzhiyun 	return stream;
1142*4882a593Smuzhiyun }
1143*4882a593Smuzhiyun EXPORT_SYMBOL(iwe_stream_add_event);
1144*4882a593Smuzhiyun 
iwe_stream_add_point(struct iw_request_info * info,char * stream,char * ends,struct iw_event * iwe,char * extra)1145*4882a593Smuzhiyun char *iwe_stream_add_point(struct iw_request_info *info, char *stream,
1146*4882a593Smuzhiyun 			   char *ends, struct iw_event *iwe, char *extra)
1147*4882a593Smuzhiyun {
1148*4882a593Smuzhiyun 	int event_len = iwe_stream_point_len(info) + iwe->u.data.length;
1149*4882a593Smuzhiyun 	int point_len = iwe_stream_point_len(info);
1150*4882a593Smuzhiyun 	int lcp_len   = iwe_stream_lcp_len(info);
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun 	/* Check if it's possible */
1153*4882a593Smuzhiyun 	if (likely((stream + event_len) < ends)) {
1154*4882a593Smuzhiyun 		iwe->len = event_len;
1155*4882a593Smuzhiyun 		memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN);
1156*4882a593Smuzhiyun 		memcpy(stream + lcp_len,
1157*4882a593Smuzhiyun 		       ((char *) &iwe->u) + IW_EV_POINT_OFF,
1158*4882a593Smuzhiyun 		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
1159*4882a593Smuzhiyun 		if (iwe->u.data.length && extra)
1160*4882a593Smuzhiyun 			memcpy(stream + point_len, extra, iwe->u.data.length);
1161*4882a593Smuzhiyun 		stream += event_len;
1162*4882a593Smuzhiyun 	}
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	return stream;
1165*4882a593Smuzhiyun }
1166*4882a593Smuzhiyun EXPORT_SYMBOL(iwe_stream_add_point);
1167*4882a593Smuzhiyun 
iwe_stream_add_value(struct iw_request_info * info,char * event,char * value,char * ends,struct iw_event * iwe,int event_len)1168*4882a593Smuzhiyun char *iwe_stream_add_value(struct iw_request_info *info, char *event,
1169*4882a593Smuzhiyun 			   char *value, char *ends, struct iw_event *iwe,
1170*4882a593Smuzhiyun 			   int event_len)
1171*4882a593Smuzhiyun {
1172*4882a593Smuzhiyun 	int lcp_len = iwe_stream_lcp_len(info);
1173*4882a593Smuzhiyun 
1174*4882a593Smuzhiyun 	/* Don't duplicate LCP */
1175*4882a593Smuzhiyun 	event_len -= IW_EV_LCP_LEN;
1176*4882a593Smuzhiyun 
1177*4882a593Smuzhiyun 	/* Check if it's possible */
1178*4882a593Smuzhiyun 	if (likely((value + event_len) < ends)) {
1179*4882a593Smuzhiyun 		/* Add new value */
1180*4882a593Smuzhiyun 		memcpy(value, &iwe->u, event_len);
1181*4882a593Smuzhiyun 		value += event_len;
1182*4882a593Smuzhiyun 		/* Patch LCP */
1183*4882a593Smuzhiyun 		iwe->len = value - event;
1184*4882a593Smuzhiyun 		memcpy(event, (char *) iwe, lcp_len);
1185*4882a593Smuzhiyun 	}
1186*4882a593Smuzhiyun 
1187*4882a593Smuzhiyun 	return value;
1188*4882a593Smuzhiyun }
1189*4882a593Smuzhiyun EXPORT_SYMBOL(iwe_stream_add_value);
1190