⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wext.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	if (ret == -EIWCOMMIT)		ret = call_commit_handler(dev);	return ret;}/* ---------------------------------------------------------------- *//* * Main IOCTl dispatcher. * Check the type of IOCTL and call the appropriate wrapper... */static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd){	struct net_device *dev;	iw_handler	handler;	/* Permissions are already checked in dev_ioctl() before calling us.	 * The copy_to/from_user() of ifr is also dealt with in there */	/* Make sure the device exist */	if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL)		return -ENODEV;	/* A bunch of special cases, then the generic case...	 * Note that 'cmd' is already filtered in dev_ioctl() with	 * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */	if (cmd == SIOCGIWSTATS)		return ioctl_standard_call(dev, ifr, cmd,					   &iw_handler_get_iwstats);	if (cmd == SIOCGIWPRIV && dev->wireless_handlers)		return ioctl_standard_call(dev, ifr, cmd,					   &iw_handler_get_private);	/* Basic check */	if (!netif_device_present(dev))		return -ENODEV;	/* New driver API : try to find the handler */	handler = get_handler(dev, cmd);	if (handler) {		/* Standard and private are not the same */		if (cmd < SIOCIWFIRSTPRIV)			return ioctl_standard_call(dev, ifr, cmd, handler);		else			return ioctl_private_call(dev, ifr, cmd, handler);	}	/* Old driver API : call driver ioctl handler */	if (dev->do_ioctl)		return dev->do_ioctl(dev, ifr, cmd);	return -EOPNOTSUPP;}/* entry point from dev ioctl */int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd,		      void __user *arg){	int ret;	/* If command is `set a parameter', or	 * `get the encoding parameters', check if	 * the user has the right to do it */	if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT)	    && !capable(CAP_NET_ADMIN))		return -EPERM;	dev_load(net, ifr->ifr_name);	rtnl_lock();	ret = wireless_process_ioctl(net, ifr, cmd);	rtnl_unlock();	if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq)))		return -EFAULT;	return ret;}/************************* EVENT PROCESSING *************************//* * Process events generated by the wireless layer or the driver. * Most often, the event will be propagated through rtnetlink *//* ---------------------------------------------------------------- *//* * Locking... * ---------- * * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing * the locking issue in here and implementing this code ! * * The issue : wireless_send_event() is often called in interrupt context, * while the Netlink layer can never be called in interrupt context. * The fully formed RtNetlink events are queued, and then a tasklet is run * to feed those to Netlink. * The skb_queue is interrupt safe, and its lock is not held while calling * Netlink, so there is no possibility of dealock. * Jean II */static struct sk_buff_head wireless_nlevent_queue;static int __init wireless_nlevent_init(void){	skb_queue_head_init(&wireless_nlevent_queue);	return 0;}subsys_initcall(wireless_nlevent_init);static void wireless_nlevent_process(unsigned long data){	struct sk_buff *skb;	while ((skb = skb_dequeue(&wireless_nlevent_queue)))		rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);}static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);/* ---------------------------------------------------------------- *//* * Fill a rtnetlink message with our event data. * Note that we propage only the specified event and don't dump the * current wireless config. Dumping the wireless config is far too * expensive (for each parameter, the driver need to query the hardware). */static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev,				 int type, char *event, int event_len){	struct ifinfomsg *r;	struct nlmsghdr  *nlh;	nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0);	if (nlh == NULL)		return -EMSGSIZE;	r = nlmsg_data(nlh);	r->ifi_family = AF_UNSPEC;	r->__ifi_pad = 0;	r->ifi_type = dev->type;	r->ifi_index = dev->ifindex;	r->ifi_flags = dev_get_flags(dev);	r->ifi_change = 0;	/* Wireless changes don't affect those flags */	/* Add the wireless events in the netlink packet */	NLA_PUT(skb, IFLA_WIRELESS, event_len, event);	return nlmsg_end(skb, nlh);nla_put_failure:	nlmsg_cancel(skb, nlh);	return -EMSGSIZE;}/* ---------------------------------------------------------------- *//* * Create and broadcast and send it on the standard rtnetlink socket * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field * within a RTM_NEWLINK event. */static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len){	struct sk_buff *skb;	int err;	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);	if (!skb)		return;	err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len);	if (err < 0) {		WARN_ON(err == -EMSGSIZE);		kfree_skb(skb);		return;	}	NETLINK_CB(skb).dst_group = RTNLGRP_LINK;	skb_queue_tail(&wireless_nlevent_queue, skb);	tasklet_schedule(&wireless_nlevent_tasklet);}/* ---------------------------------------------------------------- *//* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. * May be called from interrupt context. */void wireless_send_event(struct net_device *	dev,			 unsigned int		cmd,			 union iwreq_data *	wrqu,			 char *			extra){	const struct iw_ioctl_description *	descr = NULL;	int extra_len = 0;	struct iw_event  *event;		/* Mallocated whole event */	int event_len;				/* Its size */	int hdr_len;				/* Size of the event header */	int wrqu_off = 0;			/* Offset in wrqu */	/* Don't "optimise" the following variable, it will crash */	unsigned	cmd_index;		/* *MUST* be unsigned */	/* Get the description of the Event */	if (cmd <= SIOCIWLAST) {		cmd_index = cmd - SIOCIWFIRST;		if (cmd_index < standard_ioctl_num)			descr = &(standard_ioctl[cmd_index]);	} else {		cmd_index = cmd - IWEVFIRST;		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.		 * The best the driver could do is to log an error message.		 * We will do it ourselves instead...		 */		printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n",		       dev->name, cmd);		return;	}	/* Check extra parameters and set extra_len */	if (descr->header_type == IW_HEADER_TYPE_POINT) {		/* Check if number of token fits within bounds */		if (wrqu->data.length > descr->max_tokens) {			printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length);			return;		}		if (wrqu->data.length < descr->min_tokens) {			printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length);			return;		}		/* Calculate extra_len - extra is NULL for restricted events */		if (extra != NULL)			extra_len = wrqu->data.length * descr->token_size;		/* Always at an offset in wrqu */		wrqu_off = IW_EV_POINT_OFF;	}	/* Total length of the event */	hdr_len = event_type_size[descr->header_type];	event_len = hdr_len + extra_len;	/* Create temporary buffer to hold the event */	event = kmalloc(event_len, GFP_ATOMIC);	if (event == NULL)		return;	/* Fill event */	event->len = event_len;	event->cmd = cmd;	memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN);	if (extra)		memcpy(((char *) event) + hdr_len, extra, extra_len);	/* Send via the RtNetlink event channel */	rtmsg_iwinfo(dev, (char *) event, event_len);	/* Cleanup */	kfree(event);	return;		/* Always success, I guess ;-) */}EXPORT_SYMBOL(wireless_send_event);/********************** ENHANCED IWSPY SUPPORT **********************//* * In the old days, the driver was handling spy support all by itself. * Now, the driver can delegate this task to Wireless Extensions. * It needs to use those standard spy iw_handler in struct iw_handler_def, * push data to us via wireless_spy_update() and include struct iw_spy_data * in its private part (and export it in net_device->wireless_data->spy_data). * One of the main advantage of centralising spy support here is that * it becomes much easier to improve and extend it without having to touch * the drivers. One example is the addition of the Spy-Threshold events. *//* ---------------------------------------------------------------- *//* * Return the pointer to the spy data in the driver. * Because this is called on the Rx path via wireless_spy_update(), * we want it to be efficient... */static inline struct iw_spy_data *get_spydata(struct net_device *dev){	/* This is the new way */	if (dev->wireless_data)		return dev->wireless_data->spy_data;	return NULL;}/*------------------------------------------------------------------*//* * Standard Wireless Handler : set Spy List */int iw_handler_set_spy(struct net_device *	dev,		       struct iw_request_info *	info,		       union iwreq_data *	wrqu,		       char *			extra){	struct iw_spy_data *	spydata = get_spydata(dev);	struct sockaddr *	address = (struct sockaddr *) extra;	/* Make sure driver is not buggy or using the old API */	if (!spydata)		return -EOPNOTSUPP;	/* Disable spy collection while we copy the addresses.	 * While we copy addresses, any call to wireless_spy_update()	 * will NOP. This is OK, as anyway the addresses are changing. */	spydata->spy_number = 0;	/* We want to operate without locking, because wireless_spy_update()	 * most likely will happen in the interrupt handler, and therefore	 * have its own locking constraints and needs performance.	 * The rtnl_lock() make sure we don't race with the other iw_handlers.	 * This make sure wireless_spy_update() "see" that the spy list	 * is temporarily disabled. */	smp_wmb();	/* Are there are addresses to copy? */	if (wrqu->data.length > 0) {		int i;		/* Copy addresses */		for (i = 0; i < wrqu->data.length; i++)			memcpy(spydata->spy_address[i], address[i].sa_data,			       ETH_ALEN);		/* Reset stats */		memset(spydata->spy_stat, 0,		       sizeof(struct iw_quality) * IW_MAX_SPY);	}	/* Make sure above is updated before re-enabling */	smp_wmb();	/* Enable addresses */	spydata->spy_number = wrqu->data.length;	return 0;}EXPORT_SYMBOL(iw_handler_set_spy);/*------------------------------------------------------------------*//* * Standard Wireless Handler : get Spy List */int iw_handler_get_spy(struct net_device *	dev,		       struct iw_request_info *	info,		       union iwreq_data *	wrqu,		       char *			extra){	struct iw_spy_data *	spydata = get_spydata(dev);	struct sockaddr *	address = (struct sockaddr *) extra;	int			i;	/* Make sure driver is not buggy or using the old API */	if (!spydata)		return -EOPNOTSUPP;	wrqu->data.length = spydata->spy_number;	/* Copy addresses. */	for (i = 0; i < spydata->spy_number; i++) 	{		memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);		address[i].sa_family = AF_UNIX;	}	/* Copy stats to the user buffer (just after). */	if (spydata->spy_number > 0)		memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),		       spydata->spy_stat,		       sizeof(struct iw_quality) * spydata->spy_number);	/* Reset updated flags. */	for (i = 0; i < spydata->spy_number; i++)		spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;	return 0;}EXPORT_SYMBOL(iw_handler_get_spy);/*------------------------------------------------------------------*//* * Standard Wireless Handler : set spy threshold */int iw_handler_set_thrspy(struct net_device *	dev,			  struct iw_request_info *info,			  union iwreq_data *	wrqu,			  char *		extra){	struct iw_spy_data *	spydata = get_spydata(dev);	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;	/* Make sure driver is not buggy or using the old API */	if (!spydata)		return -EOPNOTSUPP;	/* Just do it */	memcpy(&(spydata->spy_thr_low), &(threshold->low),	       2 * sizeof(struct iw_quality));	/* Clear flag */	memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));	return 0;}EXPORT_SYMBOL(iw_handler_set_thrspy);/*------------------------------------------------------------------*//* * Standard Wireless Handler : get spy threshold */int iw_handler_get_thrspy(struct net_device *	dev,			  struct iw_request_info *info,			  union iwreq_data *	wrqu,			  char *		extra){	struct iw_spy_data *	spydata = get_spydata(dev);	struct iw_thrspy *	threshold = (struct iw_thrspy *) extra;	/* Make sure driver is not buggy or using the old API */	if (!spydata)		return -EOPNOTSUPP;	/* Just do it */	memcpy(&(threshold->low), &(spydata->spy_thr_low),	       2 * sizeof(struct iw_quality));	return 0;}EXPORT_SYMBOL(iw_handler_get_thrspy);/*------------------------------------------------------------------*//* * Prepare and send a Spy Threshold event */static void iw_send_thrspy_event(struct net_device *	dev,				 struct iw_spy_data *	spydata,				 unsigned char *	address,				 struct iw_quality *	wstats){	union iwreq_data	wrqu;	struct iw_thrspy	threshold;	/* Init */	wrqu.data.length = 1;	wrqu.data.flags = 0;	/* Copy address */	memcpy(threshold.addr.sa_data, address, ETH_ALEN);	threshold.addr.sa_family = ARPHRD_ETHER;	/* Copy stats */	memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));	/* Copy also thresholds */	memcpy(&(threshold.low), &(spydata->spy_thr_low),	       2 * sizeof(struct iw_quality));	/* Send event to user space */	wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);}/* ---------------------------------------------------------------- *//* * Call for the driver to update the spy data. * For now, the spy data is a simple array. As the size of the array is * small, this is good enough. If we wanted to support larger number of * spy addresses, we should use something more efficient... */void wireless_spy_update(struct net_device *	dev,			 unsigned char *	address,			 struct iw_quality *	wstats){	struct iw_spy_data *	spydata = get_spydata(dev);	int			i;	int			match = -1;	/* Make sure driver is not buggy or using the old API */	if (!spydata)		return;	/* Update all records that match */	for (i = 0; i < spydata->spy_number; i++)		if (!compare_ether_addr(address, spydata->spy_address[i])) {			memcpy(&(spydata->spy_stat[i]), wstats,			       sizeof(struct iw_quality));			match = i;		}	/* Generate an event if we cross the spy threshold.	 * To avoid event storms, we have a simple hysteresis : we generate	 * event only when we go under the low threshold or above the	 * high threshold. */	if (match >= 0) {		if (spydata->spy_thr_under[match]) {			if (wstats->level > spydata->spy_thr_high.level) {				spydata->spy_thr_under[match] = 0;				iw_send_thrspy_event(dev, spydata,						     address, wstats);			}		} else {			if (wstats->level < spydata->spy_thr_low.level) {				spydata->spy_thr_under[match] = 1;				iw_send_thrspy_event(dev, spydata,						     address, wstats);			}		}	}}EXPORT_SYMBOL(wireless_spy_update);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -