Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84149b0f authored by David S. Miller's avatar David S. Miller Committed by David S. Miller
Browse files

wext: Extract standard call iw_point handling into seperate function.

parent 208887d4
Loading
Loading
Loading
Loading
+134 −124
Original line number Original line Diff line number Diff line
@@ -694,49 +694,17 @@ void wext_proc_exit(struct net *net)
 */
 */


/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/*
static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd,
 * Wrapper to call a standard Wireless Extension handler.
				   const struct iw_ioctl_description *descr,
 * We do various checks and also take care of moving data between
				   iw_handler handler, struct net_device *dev,
 * user space and kernel space.
				   struct iw_request_info *info)
 */
static int ioctl_standard_call(struct net_device *	dev,
			       struct ifreq *		ifr,
			       unsigned int		cmd,
			       iw_handler		handler)
{
{
	struct iwreq *				iwr = (struct iwreq *) ifr;
	int err, extra_size, user_length = 0, essid_compat = 0;
	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;
	char *extra;
		int	extra_size;
		int	user_length = 0;
		int	err;
		int	essid_compat = 0;


	/* Calculate space needed by arguments. Always allocate
	/* Calculate space needed by arguments. Always allocate
		 * for max space. Easier, and won't last long... */
	 * for max space.
	 */
	extra_size = descr->max_tokens * descr->token_size;
	extra_size = descr->max_tokens * descr->token_size;


	/* Check need for ESSID compatibility for WE < 21 */
	/* Check need for ESSID compatibility for WE < 21 */
@@ -745,18 +713,18 @@ static int ioctl_standard_call(struct net_device * dev,
	case SIOCGIWESSID:
	case SIOCGIWESSID:
	case SIOCSIWNICKN:
	case SIOCSIWNICKN:
	case SIOCGIWNICKN:
	case SIOCGIWNICKN:
			if (iwr->u.data.length == descr->max_tokens + 1)
		if (iwp->length == descr->max_tokens + 1)
			essid_compat = 1;
			essid_compat = 1;
			else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
		else if (IW_IS_SET(cmd) && (iwp->length != 0)) {
			char essid[IW_ESSID_MAX_SIZE + 1];
			char essid[IW_ESSID_MAX_SIZE + 1];


				err = copy_from_user(essid, iwr->u.data.pointer,
			err = copy_from_user(essid, iwp->pointer,
						     iwr->u.data.length *
					     iwp->length *
					     descr->token_size);
					     descr->token_size);
			if (err)
			if (err)
				return -EFAULT;
				return -EFAULT;


				if (essid[iwr->u.data.length - 1] == '\0')
			if (essid[iwp->length - 1] == '\0')
				essid_compat = 1;
				essid_compat = 1;
		}
		}
		break;
		break;
@@ -764,95 +732,137 @@ static int ioctl_standard_call(struct net_device * dev,
		break;
		break;
	}
	}


		iwr->u.data.length -= essid_compat;
	iwp->length -= essid_compat;


	/* Check what user space is giving us */
	/* Check what user space is giving us */
	if (IW_IS_SET(cmd)) {
	if (IW_IS_SET(cmd)) {
		/* Check NULL pointer */
		/* Check NULL pointer */
			if ((iwr->u.data.pointer == NULL) &&
		if (!iwp->pointer && iwp->length != 0)
			   (iwr->u.data.length != 0))
			return -EFAULT;
			return -EFAULT;
		/* Check if number of token fits within bounds */
		/* Check if number of token fits within bounds */
			if (iwr->u.data.length > descr->max_tokens)
		if (iwp->length > descr->max_tokens)
			return -E2BIG;
			return -E2BIG;
			if (iwr->u.data.length < descr->min_tokens)
		if (iwp->length < descr->min_tokens)
			return -EINVAL;
			return -EINVAL;
	} else {
	} else {
		/* Check NULL pointer */
		/* Check NULL pointer */
			if (iwr->u.data.pointer == NULL)
		if (!iwp->pointer)
			return -EFAULT;
			return -EFAULT;
		/* Save user space buffer size for checking */
		/* Save user space buffer size for checking */
			user_length = iwr->u.data.length;
		user_length = iwp->length;


		/* Don't check if user_length > max to allow forward
		/* Don't check if user_length > max to allow forward
		 * compatibility. The test user_length < min is
		 * compatibility. The test user_length < min is
			 * implied by the test at the end. */
		 * implied by the test at the end.
		 */


		/* Support for very large requests */
		/* Support for very large requests */
		if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
		if ((descr->flags & IW_DESCR_FLAG_NOMAX) &&
		    (user_length > descr->max_tokens)) {
		    (user_length > descr->max_tokens)) {
			/* Allow userspace to GET more than max so
			/* Allow userspace to GET more than max so
			 * we can support any size GET requests.
			 * we can support any size GET requests.
				 * There is still a limit : -ENOMEM. */
			 * There is still a limit : -ENOMEM.
			 */
			extra_size = user_length * descr->token_size;
			extra_size = user_length * descr->token_size;

			/* Note : user_length is originally a __u16,
			/* Note : user_length is originally a __u16,
			 * and token_size is controlled by us,
			 * and token_size is controlled by us,
			 * so extra_size won't get negative and
			 * so extra_size won't get negative and
				 * won't overflow... */
			 * won't overflow...
			 */
		}
		}
	}
	}


		/* Create the kernel buffer */
	/* kzalloc() ensures NULL-termination for essid_compat. */
		/*    kzalloc ensures NULL-termination for essid_compat */
	extra = kzalloc(extra_size, GFP_KERNEL);
	extra = kzalloc(extra_size, GFP_KERNEL);
		if (extra == NULL)
	if (!extra)
		return -ENOMEM;
		return -ENOMEM;


	/* If it is a SET, get all the extra data in here */
	/* If it is a SET, get all the extra data in here */
		if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) {
	if (IW_IS_SET(cmd) && (iwp->length != 0)) {
			err = copy_from_user(extra, iwr->u.data.pointer,
		if (copy_from_user(extra, iwp->pointer,
					     iwr->u.data.length *
				   iwp->length *
					     descr->token_size);
				   descr->token_size)) {
			if (err) {
			err = -EFAULT;
				kfree(extra);
			goto out;
				return -EFAULT;
		}
		}
	}
	}


		/* Call the handler */
	err = handler(dev, info, (union iwreq_data *) iwp, extra);
		ret = handler(dev, &info, &(iwr->u), extra);


		iwr->u.data.length += essid_compat;
	iwp->length += essid_compat;


	/* If we have something to return to the user */
	/* If we have something to return to the user */
		if (!ret && IW_IS_GET(cmd)) {
	if (!err && IW_IS_GET(cmd)) {
		/* Check if there is enough buffer up there */
		/* Check if there is enough buffer up there */
			if (user_length < iwr->u.data.length) {
		if (user_length < iwp->length) {
				kfree(extra);
			err = -E2BIG;
				return -E2BIG;
			goto out;
		}
		}


			err = copy_to_user(iwr->u.data.pointer, extra,
		if (copy_to_user(iwp->pointer, extra,
					   iwr->u.data.length *
				 iwp->length *
					   descr->token_size);
				 descr->token_size)) {
			if (err)
			err = -EFAULT;
				ret =  -EFAULT;
			goto out;
		}
	}
	}


	/* Generate an event to notify listeners of the change */
	/* Generate an event to notify listeners of the change */
		if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
	if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) {
		   ((ret == 0) || (ret == -EIWCOMMIT))) {
		union iwreq_data *data = (union iwreq_data *) iwp;

		if (descr->flags & IW_DESCR_FLAG_RESTRICT)
		if (descr->flags & IW_DESCR_FLAG_RESTRICT)
			/* If the event is restricted, don't
			/* If the event is restricted, don't
				 * export the payload */
			 * export the payload.
				wireless_send_event(dev, cmd, &(iwr->u), NULL);
			 */
			wireless_send_event(dev, cmd, data, NULL);
		else
		else
				wireless_send_event(dev, cmd, &(iwr->u),
			wireless_send_event(dev, cmd, data, extra);
						    extra);
	}
	}


		/* Cleanup - I told you it wasn't that long ;-) */
out:
	kfree(extra);
	kfree(extra);
	return err;
}

/*
 * 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 {
		ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr,
					      handler, dev, &info);
	}
	}


	/* Call commit handler if needed and defined */
	/* Call commit handler if needed and defined */