1216 lines · 32.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * This file implement the Wireless Extensions core API. * * Authors :	Jean Tourrilhes - HPL - <jt@hpl.hp.com> * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. * Copyright	2009 Johannes Berg <johannes@sipsolutions.net> * Copyright (C) 2024 Intel Corporation */#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/rtnetlink.h>#include <linux/slab.h>#include <linux/wireless.h>#include <linux/uaccess.h>#include <linux/export.h>#include <net/cfg80211.h>#include <net/iw_handler.h>#include <net/netlink.h>#include <net/wext.h>#include <net/net_namespace.h> typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,			       unsigned int, struct iw_request_info *,			       iw_handler);  /* * Meta-data about all the standard Wireless Extension request we * know about. */static const struct iw_ioctl_description standard_ioctl[] = {	[IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {		.header_type	= IW_HEADER_TYPE_NULL,	},	[IW_IOCTL_IDX(SIOCGIWNAME)] = {		.header_type	= IW_HEADER_TYPE_CHAR,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWNWID)] = {		.header_type	= IW_HEADER_TYPE_PARAM,		.flags		= IW_DESCR_FLAG_EVENT,	},	[IW_IOCTL_IDX(SIOCGIWNWID)] = {		.header_type	= IW_HEADER_TYPE_PARAM,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWFREQ)] = {		.header_type	= IW_HEADER_TYPE_FREQ,		.flags		= IW_DESCR_FLAG_EVENT,	},	[IW_IOCTL_IDX(SIOCGIWFREQ)] = {		.header_type	= IW_HEADER_TYPE_FREQ,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWMODE)] = {		.header_type	= IW_HEADER_TYPE_UINT,		.flags		= IW_DESCR_FLAG_EVENT,	},	[IW_IOCTL_IDX(SIOCGIWMODE)] = {		.header_type	= IW_HEADER_TYPE_UINT,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWSENS)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWSENS)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWRANGE)] = {		.header_type	= IW_HEADER_TYPE_NULL,	},	[IW_IOCTL_IDX(SIOCGIWRANGE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= sizeof(struct iw_range),		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWPRIV)] = {		.header_type	= IW_HEADER_TYPE_NULL,	},	[IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct iw_priv_args),		.max_tokens	= 16,		.flags		= IW_DESCR_FLAG_NOMAX,	},	[IW_IOCTL_IDX(SIOCSIWSTATS)] = {		.header_type	= IW_HEADER_TYPE_NULL,	},	[IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= sizeof(struct iw_statistics),		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWSPY)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct sockaddr),		.max_tokens	= IW_MAX_SPY,	},	[IW_IOCTL_IDX(SIOCGIWSPY)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct sockaddr) +				  sizeof(struct iw_quality),		.max_tokens	= IW_MAX_SPY,	},	[IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct iw_thrspy),		.min_tokens	= 1,		.max_tokens	= 1,	},	[IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct iw_thrspy),		.min_tokens	= 1,		.max_tokens	= 1,	},	[IW_IOCTL_IDX(SIOCSIWAP)] = {		.header_type	= IW_HEADER_TYPE_ADDR,	},	[IW_IOCTL_IDX(SIOCGIWAP)] = {		.header_type	= IW_HEADER_TYPE_ADDR,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWMLME)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.min_tokens	= sizeof(struct iw_mlme),		.max_tokens	= sizeof(struct iw_mlme),	},	[IW_IOCTL_IDX(SIOCGIWAPLIST)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= sizeof(struct sockaddr) +				  sizeof(struct iw_quality),		.max_tokens	= IW_MAX_AP,		.flags		= IW_DESCR_FLAG_NOMAX,	},	[IW_IOCTL_IDX(SIOCSIWSCAN)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.min_tokens	= 0,		.max_tokens	= sizeof(struct iw_scan_req),	},	[IW_IOCTL_IDX(SIOCGIWSCAN)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_SCAN_MAX_DATA,		.flags		= IW_DESCR_FLAG_NOMAX,	},	[IW_IOCTL_IDX(SIOCSIWESSID)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ESSID_MAX_SIZE,		.flags		= IW_DESCR_FLAG_EVENT,	},	[IW_IOCTL_IDX(SIOCGIWESSID)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ESSID_MAX_SIZE,		.flags		= IW_DESCR_FLAG_DUMP,	},	[IW_IOCTL_IDX(SIOCSIWNICKN)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ESSID_MAX_SIZE,	},	[IW_IOCTL_IDX(SIOCGIWNICKN)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ESSID_MAX_SIZE,	},	[IW_IOCTL_IDX(SIOCSIWRATE)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWRATE)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWRTS)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWRTS)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWFRAG)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWFRAG)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWTXPOW)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWTXPOW)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWRETRY)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWRETRY)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWENCODE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ENCODING_TOKEN_MAX,		.flags		= IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,	},	[IW_IOCTL_IDX(SIOCGIWENCODE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_ENCODING_TOKEN_MAX,		.flags		= IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,	},	[IW_IOCTL_IDX(SIOCSIWPOWER)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWPOWER)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWGENIE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_GENERIC_IE_MAX,	},	[IW_IOCTL_IDX(SIOCGIWGENIE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_GENERIC_IE_MAX,	},	[IW_IOCTL_IDX(SIOCSIWAUTH)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCGIWAUTH)] = {		.header_type	= IW_HEADER_TYPE_PARAM,	},	[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.min_tokens	= sizeof(struct iw_encode_ext),		.max_tokens	= sizeof(struct iw_encode_ext) +				  IW_ENCODING_TOKEN_MAX,	},	[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.min_tokens	= sizeof(struct iw_encode_ext),		.max_tokens	= sizeof(struct iw_encode_ext) +				  IW_ENCODING_TOKEN_MAX,	},	[IW_IOCTL_IDX(SIOCSIWPMKSA)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.min_tokens	= sizeof(struct iw_pmksa),		.max_tokens	= sizeof(struct iw_pmksa),	},};static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl); /* * Meta-data about all the additional standard Wireless Extension events * we know about. */static const struct iw_ioctl_description standard_event[] = {	[IW_EVENT_IDX(IWEVTXDROP)] = {		.header_type	= IW_HEADER_TYPE_ADDR,	},	[IW_EVENT_IDX(IWEVQUAL)] = {		.header_type	= IW_HEADER_TYPE_QUAL,	},	[IW_EVENT_IDX(IWEVCUSTOM)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_CUSTOM_MAX,	},	[IW_EVENT_IDX(IWEVREGISTERED)] = {		.header_type	= IW_HEADER_TYPE_ADDR,	},	[IW_EVENT_IDX(IWEVEXPIRED)] = {		.header_type	= IW_HEADER_TYPE_ADDR,	},	[IW_EVENT_IDX(IWEVGENIE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_GENERIC_IE_MAX,	},	[IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= sizeof(struct iw_michaelmicfailure),	},	[IW_EVENT_IDX(IWEVASSOCREQIE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_GENERIC_IE_MAX,	},	[IW_EVENT_IDX(IWEVASSOCRESPIE)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= IW_GENERIC_IE_MAX,	},	[IW_EVENT_IDX(IWEVPMKIDCAND)] = {		.header_type	= IW_HEADER_TYPE_POINT,		.token_size	= 1,		.max_tokens	= sizeof(struct iw_pmkid_cand),	},};static const unsigned int standard_event_num = ARRAY_SIZE(standard_event); /* Size (in bytes) of various events */static const int event_type_size[] = {	IW_EV_LCP_LEN,			/* IW_HEADER_TYPE_NULL */	0,	IW_EV_CHAR_LEN,			/* IW_HEADER_TYPE_CHAR */	0,	IW_EV_UINT_LEN,			/* IW_HEADER_TYPE_UINT */	IW_EV_FREQ_LEN,			/* IW_HEADER_TYPE_FREQ */	IW_EV_ADDR_LEN,			/* IW_HEADER_TYPE_ADDR */	0,	IW_EV_POINT_LEN,		/* Without variable payload */	IW_EV_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */	IW_EV_QUAL_LEN,			/* IW_HEADER_TYPE_QUAL */}; #ifdef CONFIG_COMPATstatic const int compat_event_type_size[] = {	IW_EV_COMPAT_LCP_LEN,		/* IW_HEADER_TYPE_NULL */	0,	IW_EV_COMPAT_CHAR_LEN,		/* IW_HEADER_TYPE_CHAR */	0,	IW_EV_COMPAT_UINT_LEN,		/* IW_HEADER_TYPE_UINT */	IW_EV_COMPAT_FREQ_LEN,		/* IW_HEADER_TYPE_FREQ */	IW_EV_COMPAT_ADDR_LEN,		/* IW_HEADER_TYPE_ADDR */	0,	IW_EV_COMPAT_POINT_LEN,		/* Without variable payload */	IW_EV_COMPAT_PARAM_LEN,		/* IW_HEADER_TYPE_PARAM */	IW_EV_COMPAT_QUAL_LEN,		/* IW_HEADER_TYPE_QUAL */};#endif  /* IW event code */ void wireless_nlevent_flush(void){	struct sk_buff *skb;	struct net *net; 	down_read(&net_rwsem);	for_each_net(net) {		while ((skb = skb_dequeue(&net->wext_nlevents)))			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,				    GFP_KERNEL);	}	up_read(&net_rwsem);}EXPORT_SYMBOL_GPL(wireless_nlevent_flush); static int wext_netdev_notifier_call(struct notifier_block *nb,				     unsigned long state, void *ptr){	/*	 * When a netdev changes state in any way, flush all pending messages	 * to avoid them going out in a strange order, e.g. RTM_NEWLINK after	 * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close()	 * or similar - all of which could otherwise happen due to delays from	 * schedule_work().	 */	wireless_nlevent_flush(); 	return NOTIFY_OK;} static struct notifier_block wext_netdev_notifier = {	.notifier_call = wext_netdev_notifier_call,}; static int __net_init wext_pernet_init(struct net *net){	skb_queue_head_init(&net->wext_nlevents);	return 0;} static void __net_exit wext_pernet_exit(struct net *net){	skb_queue_purge(&net->wext_nlevents);} static struct pernet_operations wext_pernet_ops = {	.init = wext_pernet_init,	.exit = wext_pernet_exit,}; static int __init wireless_nlevent_init(void){	int err = register_pernet_subsys(&wext_pernet_ops); 	if (err)		return err; 	err = register_netdevice_notifier(&wext_netdev_notifier);	if (err)		unregister_pernet_subsys(&wext_pernet_ops);	return err;} subsys_initcall(wireless_nlevent_init); /* Process events generated by the wireless layer or the driver. */static void wireless_nlevent_process(struct work_struct *work){	wireless_nlevent_flush();} static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev,					      struct sk_buff *skb){	struct ifinfomsg *r;	struct nlmsghdr  *nlh; 	nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0);	if (!nlh)		return NULL; 	r = nlmsg_data(nlh);	r->ifi_family = AF_UNSPEC;	r->__ifi_pad = 0;	r->ifi_type = dev->type;	r->ifi_index = dev->ifindex;	r->ifi_flags = netif_get_flags(dev);	r->ifi_change = 0;	/* Wireless changes don't affect those flags */ 	if (nla_put_string(skb, IFLA_IFNAME, dev->name))		goto nla_put_failure; 	return nlh; nla_put_failure:	nlmsg_cancel(skb, nlh);	return NULL;}  /* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. * May be called from interrupt context. */void wireless_send_event(struct net_device *	dev,			 unsigned int		cmd,			 union iwreq_data *	wrqu,			 const char *		extra){	const struct iw_ioctl_description *	descr = NULL;	int extra_len = 0;	struct iw_event  *event;		/* Mallocated whole event */	int event_len;				/* Its size */	int hdr_len;				/* Size of the event header */	int wrqu_off = 0;			/* Offset in wrqu */	/* Don't "optimise" the following variable, it will crash */	unsigned int	cmd_index;		/* *MUST* be unsigned */	struct sk_buff *skb;	struct nlmsghdr *nlh;	struct nlattr *nla;#ifdef CONFIG_COMPAT	struct __compat_iw_event *compat_event;	struct compat_iw_point compat_wrqu;	struct sk_buff *compskb;	int ptr_len;#endif 	/*	 * Nothing in the kernel sends scan events with data, be safe.	 * This is necessary because we cannot fix up scan event data	 * for compat, due to being contained in 'extra', but normally	 * applications are required to retrieve the scan data anyway	 * and no data is included in the event, this codifies that	 * practice.	 */	if (WARN_ON(cmd == SIOCGIWSCAN && extra))		extra = NULL; 	/* Get the description of the Event */	if (cmd <= SIOCIWLAST) {		cmd_index = IW_IOCTL_IDX(cmd);		if (cmd_index < standard_ioctl_num)			descr = &(standard_ioctl[cmd_index]);	} else {		cmd_index = IW_EVENT_IDX(cmd);		if (cmd_index < standard_event_num)			descr = &(standard_event[cmd_index]);	}	/* Don't accept unknown events */	if (descr == NULL) {		/* Note : we don't return an error to the driver, because		 * the driver would not know what to do about it. It can't		 * return an error to the user, because the event is not		 * initiated by a user request.

Lines 1–500 of 1216.