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