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

📄 wext.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Calculate size of private arguments */static inline int get_priv_size(__u16	args){	int	num = args & IW_PRIV_SIZE_MASK;	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;	return num * iw_priv_type_size[type];}/* ---------------------------------------------------------------- *//* * Re-calculate the size of private arguments */static inline int adjust_priv_size(__u16		args,				   union iwreq_data *	wrqu){	int	num = wrqu->data.length;	int	max = args & IW_PRIV_SIZE_MASK;	int	type = (args & IW_PRIV_TYPE_MASK) >> 12;	/* Make sure the driver doesn't goof up */	if (max < num)		num = max;	return num * iw_priv_type_size[type];}/* ---------------------------------------------------------------- *//* * Standard Wireless Handler : get wireless stats *	Allow programatic access to /proc/net/wireless even if /proc *	doesn't exist... Also more efficient... */static int iw_handler_get_iwstats(struct net_device *		dev,				  struct iw_request_info *	info,				  union iwreq_data *		wrqu,				  char *			extra){	/* Get stats from the driver */	struct iw_statistics *stats;	stats = get_wireless_stats(dev);	if (stats) {		/* Copy statistics to extra */		memcpy(extra, stats, sizeof(struct iw_statistics));		wrqu->data.length = sizeof(struct iw_statistics);		/* Check if we need to clear the updated flag */		if (wrqu->data.flags != 0)			stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;		return 0;	} else		return -EOPNOTSUPP;}/* ---------------------------------------------------------------- *//* * Standard Wireless Handler : get iwpriv definitions * Export the driver private handler definition * They will be picked up by tools like iwpriv... */static int iw_handler_get_private(struct net_device *		dev,				  struct iw_request_info *	info,				  union iwreq_data *		wrqu,				  char *			extra){	/* Check if the driver has something to export */	if ((dev->wireless_handlers->num_private_args == 0) ||	   (dev->wireless_handlers->private_args == NULL))		return -EOPNOTSUPP;	/* Check if there is enough buffer up there */	if (wrqu->data.length < dev->wireless_handlers->num_private_args) {		/* User space can't know in advance how large the buffer		 * needs to be. Give it a hint, so that we can support		 * any size buffer we want somewhat efficiently... */		wrqu->data.length = dev->wireless_handlers->num_private_args;		return -E2BIG;	}	/* Set the number of available ioctls. */	wrqu->data.length = dev->wireless_handlers->num_private_args;	/* Copy structure to the user buffer. */	memcpy(extra, dev->wireless_handlers->private_args,	       sizeof(struct iw_priv_args) * wrqu->data.length);	return 0;}/******************** /proc/net/wireless SUPPORT ********************//* * The /proc/net/wireless file is a human readable user-space interface * exporting various wireless specific statistics from the wireless devices. * This is the most popular part of the Wireless Extensions ;-) * * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). * The content of the file is basically the content of "struct iw_statistics". */#ifdef CONFIG_PROC_FS/* ---------------------------------------------------------------- *//* * Print one entry (line) of /proc/net/wireless */static void wireless_seq_printf_stats(struct seq_file *seq,				      struct net_device *dev){	/* Get stats from the driver */	struct iw_statistics *stats = get_wireless_stats(dev);	if (stats) {		seq_printf(seq, "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d "				"%6d %6d   %6d\n",			   dev->name, stats->status, stats->qual.qual,			   stats->qual.updated & IW_QUAL_QUAL_UPDATED			   ? '.' : ' ',			   ((__s32) stats->qual.level) -			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),			   stats->qual.updated & IW_QUAL_LEVEL_UPDATED			   ? '.' : ' ',			   ((__s32) stats->qual.noise) -			   ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),			   stats->qual.updated & IW_QUAL_NOISE_UPDATED			   ? '.' : ' ',			   stats->discard.nwid, stats->discard.code,			   stats->discard.fragment, stats->discard.retries,			   stats->discard.misc, stats->miss.beacon);		stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;	}}/* ---------------------------------------------------------------- *//* * Print info for /proc/net/wireless (print all entries) */static int wireless_seq_show(struct seq_file *seq, void *v){	if (v == SEQ_START_TOKEN)		seq_printf(seq, "Inter-| sta-|   Quality        |   Discarded "				"packets               | Missed | WE\n"				" face | tus | link level noise |  nwid  "				"crypt   frag  retry   misc | beacon | %d\n",			   WIRELESS_EXT);	else		wireless_seq_printf_stats(seq, v);	return 0;}static const struct seq_operations wireless_seq_ops = {	.start = dev_seq_start,	.next  = dev_seq_next,	.stop  = dev_seq_stop,	.show  = wireless_seq_show,};static int wireless_seq_open(struct inode *inode, struct file *file){	struct seq_file *seq;	int res;	res = seq_open(file, &wireless_seq_ops);	if (!res) {		seq = file->private_data;		seq->private = get_proc_net(inode);		if (!seq->private) {			seq_release(inode, file);			res = -ENXIO;		}	}	return res;}static int wireless_seq_release(struct inode *inode, struct file *file){	struct seq_file *seq = file->private_data;	struct net *net = seq->private;	put_net(net);	return seq_release(inode, file);}static const struct file_operations wireless_seq_fops = {	.owner	 = THIS_MODULE,	.open    = wireless_seq_open,	.read    = seq_read,	.llseek  = seq_lseek,	.release = wireless_seq_release,};int wext_proc_init(struct net *net){	/* Create /proc/net/wireless entry */	if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops))		return -ENOMEM;	return 0;}void wext_proc_exit(struct net *net){	proc_net_remove(net, "wireless");}#endif	/* CONFIG_PROC_FS *//************************** IOCTL SUPPORT **************************//* * The original user space API to configure all those Wireless Extensions * is through IOCTLs. * In there, we check if we need to call the new driver API (iw_handler) * or just call the driver ioctl handler. *//* ---------------------------------------------------------------- *//* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. */static int ioctl_standard_call(struct net_device *	dev,			       struct ifreq *		ifr,			       unsigned int		cmd,			       iw_handler		handler){	struct iwreq *				iwr = (struct iwreq *) ifr;	const struct iw_ioctl_description *	descr;	struct iw_request_info			info;	int					ret = -EINVAL;	/* Get the description of the IOCTL */	if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)		return -EOPNOTSUPP;	descr = &(standard_ioctl[cmd - SIOCIWFIRST]);	/* Prepare the call */	info.cmd = cmd;	info.flags = 0;	/* Check if we have a pointer to user space data or not */	if (descr->header_type != IW_HEADER_TYPE_POINT) {		/* No extra arguments. Trivial to handle */		ret = handler(dev, &info, &(iwr->u), NULL);		/* Generate an event to notify listeners of the change */		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&		   ((ret == 0) || (ret == -EIWCOMMIT)))			wireless_send_event(dev, cmd, &(iwr->u), NULL);	} else {		char *	extra;		int	extra_size;		int	user_length = 0;		int	err;		int	essid_compat = 0;		/* Calculate space needed by arguments. Always allocate		 * for max space. Easier, and won't last long... */		extra_size = descr->max_tokens * descr->token_size;		/* Check need for ESSID compatibility for WE < 21 */		switch (cmd) {		case SIOCSIWESSID:		case SIOCGIWESSID:		case SIOCSIWNICKN:		case SIOCGIWNICKN:			if (iwr->u.data.length == descr->max_tokens + 1)				essid_compat = 1;			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {				char essid[IW_ESSID_MAX_SIZE + 1];				err = copy_from_user(essid, iwr->u.data.pointer,						     iwr->u.data.length *						     descr->token_size);				if (err)					return -EFAULT;				if (essid[iwr->u.data.length - 1] == '\0')					essid_compat = 1;			}			break;		default:			break;		}		iwr->u.data.length -= essid_compat;		/* Check what user space is giving us */		if (IW_IS_SET(cmd)) {			/* Check NULL pointer */			if ((iwr->u.data.pointer == NULL) &&			   (iwr->u.data.length != 0))				return -EFAULT;			/* Check if number of token fits within bounds */			if (iwr->u.data.length > descr->max_tokens)				return -E2BIG;			if (iwr->u.data.length < descr->min_tokens)				return -EINVAL;		} else {			/* Check NULL pointer */			if (iwr->u.data.pointer == NULL)				return -EFAULT;			/* Save user space buffer size for checking */			user_length = iwr->u.data.length;			/* Don't check if user_length > max to allow forward			 * compatibility. The test user_length < min is			 * implied by the test at the end. */			/* Support for very large requests */			if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&			   (user_length > descr->max_tokens)) {				/* Allow userspace to GET more than max so				 * we can support any size GET requests.				 * There is still a limit : -ENOMEM. */				extra_size = user_length * descr->token_size;				/* Note : user_length is originally a __u16,				 * and token_size is controlled by us,				 * so extra_size won't get negative and				 * won't overflow... */			}		}		/* Create the kernel buffer */		/*    kzalloc ensures NULL-termination for essid_compat */		extra = kzalloc(extra_size, GFP_KERNEL);		if (extra == NULL)			return -ENOMEM;		/* If it is a SET, get all the extra data in here */		if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {			err = copy_from_user(extra, iwr->u.data.pointer,					     iwr->u.data.length *					     descr->token_size);			if (err) {				kfree(extra);				return -EFAULT;			}		}		/* Call the handler */		ret = handler(dev, &info, &(iwr->u), extra);		iwr->u.data.length += essid_compat;		/* If we have something to return to the user */		if (!ret && IW_IS_GET(cmd)) {			/* Check if there is enough buffer up there */			if (user_length < iwr->u.data.length) {				kfree(extra);				return -E2BIG;			}			err = copy_to_user(iwr->u.data.pointer, extra,					   iwr->u.data.length *					   descr->token_size);			if (err)				ret =  -EFAULT;		}		/* Generate an event to notify listeners of the change */		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&		   ((ret == 0) || (ret == -EIWCOMMIT))) {			if (descr->flags & IW_DESCR_FLAG_RESTRICT)				/* If the event is restricted, don't				 * export the payload */				wireless_send_event(dev, cmd, &(iwr->u), NULL);			else				wireless_send_event(dev, cmd, &(iwr->u),						    extra);		}		/* Cleanup - I told you it wasn't that long ;-) */		kfree(extra);	}	/* Call commit handler if needed and defined */	if (ret == -EIWCOMMIT)		ret = call_commit_handler(dev);	/* Here, we will generate the appropriate event if needed */	return ret;}/* ---------------------------------------------------------------- *//* * Wrapper to call a private Wireless Extension handler. * We do various checks and also take care of moving data between * user space and kernel space. * It's not as nice and slimline as the standard wrapper. The cause * is struct iw_priv_args, which was not really designed for the * job we are going here. * * IMPORTANT : This function prevent to set and get data on the same * IOCTL and enforce the SET/GET convention. Not doing it would be * far too hairy... * If you need to set and get data at the same time, please don't use * a iw_handler but process it in your ioctl handler (i.e. use the * old driver API). */static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr,			      unsigned int cmd, iw_handler handler){	struct iwreq *			iwr = (struct iwreq *) ifr;	const struct iw_priv_args *	descr = NULL;	struct iw_request_info		info;	int				extra_size = 0;	int				i;	int				ret = -EINVAL;	/* Get the description of the IOCTL */	for (i = 0; i < dev->wireless_handlers->num_private_args; i++)		if (cmd == dev->wireless_handlers->private_args[i].cmd) {			descr = &(dev->wireless_handlers->private_args[i]);			break;		}	/* Compute the size of the set/get arguments */	if (descr != NULL) {		if (IW_IS_SET(cmd)) {			int	offset = 0;	/* For sub-ioctls */			/* Check for sub-ioctl handler */			if (descr->name[0] == '\0')				/* Reserve one int for sub-ioctl index */				offset = sizeof(__u32);			/* Size of set arguments */			extra_size = get_priv_size(descr->set_args);			/* Does it fits in iwr ? */			if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&			   ((extra_size + offset) <= IFNAMSIZ))				extra_size = 0;		} else {			/* Size of get arguments */			extra_size = get_priv_size(descr->get_args);			/* Does it fits in iwr ? */			if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&			   (extra_size <= IFNAMSIZ))				extra_size = 0;		}	}	/* Prepare the call */	info.cmd = cmd;	info.flags = 0;	/* Check if we have a pointer to user space data or not. */	if (extra_size == 0) {		/* No extra arguments. Trivial to handle */		ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u));	} else {		char *	extra;		int	err;		/* Check what user space is giving us */		if (IW_IS_SET(cmd)) {			/* Check NULL pointer */			if ((iwr->u.data.pointer == NULL) &&			   (iwr->u.data.length != 0))				return -EFAULT;			/* Does it fits within bounds ? */			if (iwr->u.data.length > (descr->set_args &						 IW_PRIV_SIZE_MASK))				return -E2BIG;		} else if (iwr->u.data.pointer == NULL)			return -EFAULT;		/* Always allocate for max space. Easier, and won't last		 * long... */		extra = kmalloc(extra_size, GFP_KERNEL);		if (extra == NULL)			return -ENOMEM;		/* If it is a SET, get all the extra data in here */		if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {			err = copy_from_user(extra, iwr->u.data.pointer,					     extra_size);			if (err) {				kfree(extra);				return -EFAULT;			}		}		/* Call the handler */		ret = handler(dev, &info, &(iwr->u), extra);		/* If we have something to return to the user */		if (!ret && IW_IS_GET(cmd)) {			/* Adjust for the actual length if it's variable,			 * avoid leaking kernel bits outside. */			if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) {				extra_size = adjust_priv_size(descr->get_args,							      &(iwr->u));			}			err = copy_to_user(iwr->u.data.pointer, extra,					   extra_size);			if (err)				ret =  -EFAULT;		}		/* Cleanup - I told you it wasn't that long ;-) */		kfree(extra);	}	/* Call commit handler if needed and defined */

⌨️ 快捷键说明

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