staticint wext_netdev_notifier_call(struct notifier_block *nb, unsignedlong 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();
staticint __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. */ staticvoid wireless_nlevent_process(struct work_struct *work)
{
wireless_nlevent_flush();
}
/* * 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, unsignedint cmd, union iwreq_data * wrqu, constchar * extra)
{ conststruct 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 */ unsignedint 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. * The best the driver could do is to log an error message. * We will do it ourselves instead...
*/
netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n",
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) {
netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too big (%d)\n",
cmd, wrqu->data.length); return;
} if (wrqu->data.length < descr->min_tokens) {
netdev_err(dev, "(WE) : Wireless Event (cmd=0x%04X) too small (%d)\n",
cmd, 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;
/* * The problem for 64/32 bit. * * On 64-bit, a regular event is laid out as follows: * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | event.len | event.cmd | p a d d i n g | * | wrqu data ... (with the correct size) | * * This padding exists because we manipulate event->u, * and 'event' is not packed. * * An iw_point event is laid out like this instead: * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * | event.len | event.cmd | p a d d i n g | * | iwpnt.len | iwpnt.flg | p a d d i n g | * | extra data ... * * The second padding exists because struct iw_point is extended, * but this depends on the platform... * * On 32-bit, all the padding shouldn't be there.
*/
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!skb) return;
/* Send via the RtNetlink event channel */
nlh = rtnetlink_ifinfo_prep(dev, skb); if (WARN_ON(!nlh)) {
kfree_skb(skb); return;
}
/* Add the wireless events in the netlink packet */
nla = nla_reserve(skb, IFLA_WIRELESS, event_len); if (!nla) {
kfree_skb(skb); return;
}
event = nla_data(nla);
/* Fill event - first clear to avoid data leaking */
memset(event, 0, hdr_len);
event->len = event_len;
event->cmd = cmd;
memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); if (extra_len)
memcpy(((char *) event) + hdr_len, extra, extra_len);
#ifdef CONFIG_CFG80211_WEXT staticvoid wireless_warn_cfg80211_wext(void)
{
pr_warn_once("warning: `%s' uses wireless extensions which will stop working for Wi-Fi 7 hardware; use nl80211\n",
current->comm);
} #endif
/* noinline to avoid a bogus warning with -O3 */ static noinline 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;
}
static iw_handler get_handler(struct net_device *dev, unsignedint cmd)
{ /* Don't "optimise" the following variable, it will crash */ unsignedint index; /* *MUST* be unsigned */ conststruct iw_handler_def *handlers = NULL;
#ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) {
wireless_warn_cfg80211_wext(); if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO |
WIPHY_FLAG_DISABLE_WEXT)) return NULL;
handlers = dev->ieee80211_ptr->wiphy->wext;
} #endif #ifdef CONFIG_WIRELESS_EXT if (dev->wireless_handlers)
handlers = dev->wireless_handlers; #endif
if (!handlers) return NULL;
/* Try as a standard command */
index = IW_IOCTL_IDX(cmd); if (index < handlers->num_standard) return handlers->standard[index];
#ifdef CONFIG_WEXT_PRIV /* Try as a private command */
index = cmd - SIOCIWFIRSTPRIV; if (index < handlers->num_private) return handlers->private[index]; #endif
/* Calculate space needed by arguments. Always allocate * for max space.
*/
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 (iwp->length == descr->max_tokens + 1)
essid_compat = 1; elseif (IW_IS_SET(cmd) && (iwp->length != 0)) { char essid[IW_ESSID_MAX_SIZE + 1]; unsignedint len;
len = iwp->length * descr->token_size;
if (len > IW_ESSID_MAX_SIZE) return -EFAULT;
err = copy_from_user(essid, iwp->pointer, len); if (err) return -EFAULT;
/* Check what user space is giving us */ if (IW_IS_SET(cmd)) { /* Check NULL pointer */ if (!iwp->pointer && iwp->length != 0) return -EFAULT; /* Check if number of token fits within bounds */ if (iwp->length > descr->max_tokens) return -E2BIG; if (iwp->length < descr->min_tokens) return -EINVAL;
} else { /* Check NULL pointer */ if (!iwp->pointer) return -EFAULT; /* Save user space buffer size for checking */
user_length = iwp->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...
*/
}
}
/* Sanity-check to ensure we never end up _allocating_ zero * bytes of data for extra.
*/ if (extra_size <= 0) return -EFAULT;
/* kzalloc() ensures NULL-termination for essid_compat. */
extra = kzalloc(extra_size, GFP_KERNEL); if (!extra) return -ENOMEM;
/* If it is a SET, get all the extra data in here */ if (IW_IS_SET(cmd) && (iwp->length != 0)) { if (copy_from_user(extra, iwp->pointer,
iwp->length *
descr->token_size)) {
err = -EFAULT; goto out;
}
if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { /* * If this is a GET, but not NOMAX, it means that the extra * data is not bounded by userspace, but by max_tokens. Thus * set the length to max_tokens. This matches the extra data * allocation. * The driver should fill it with the number of tokens it * provided, and it may check iwp->length rather than having * knowledge of max_tokens. If the driver doesn't change the * iwp->length, this ioctl just copies back max_token tokens * filled with zeroes. Hopefully the driver isn't claiming * them to be valid data.
*/
iwp->length = descr->max_tokens;
}
/* If we have something to return to the user */ if (!err && IW_IS_GET(cmd)) { /* Check if there is enough buffer up there */ if (user_length < iwp->length) {
err = -E2BIG; goto out;
}
/* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) &&
((err == 0) || (err == -EIWCOMMIT))) { union iwreq_data *data = (union iwreq_data *) iwp;
if (descr->flags & IW_DESCR_FLAG_RESTRICT) /* If the event is restricted, don't * export the payload.
*/
wireless_send_event(dev, cmd, data, NULL); else
wireless_send_event(dev, cmd, data, extra);
}
out:
kfree(extra); return err;
}
/* * Call the commit handler in the driver * (if exist and if conditions are right) * * Note : our current commit strategy is currently pretty dumb, * but we will be able to improve on that... * The goal is to try to agreagate as many changes as possible * before doing the commit. Drivers that will define a commit handler * are usually those that need a reset after changing parameters, so * we want to minimise the number of reset. * A cool idea is to use a timer : at each "set" command, we re-set the * timer, when the timer eventually fires, we call the driver. * Hopefully, more on that later. * * Also, I'm waiting to see how many people will complain about the * netif_running(dev) test. I'm open on that one... * Hopefully, the driver will remember to do a commit in "open()" ;-)
*/ int call_commit_handler(struct net_device *dev)
{ #ifdef CONFIG_WIRELESS_EXT if (netif_running(dev) &&
dev->wireless_handlers &&
dev->wireless_handlers->standard[0]) /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL,
NULL, NULL); else return 0; /* Command completed successfully */ #else /* cfg80211 has no commit */ return 0; #endif
}
/* * Main IOCTl dispatcher. * Check the type of IOCTL and call the appropriate wrapper...
*/ staticint wireless_process_ioctl(struct net *net, struct iwreq *iwr, unsignedint cmd, struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{ 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, iwr->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 standard(dev, iwr, cmd, info,
&iw_handler_get_iwstats);
/* 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 standard(dev, iwr, cmd, info, handler); elseif (private) returnprivate(dev, iwr, cmd, info, handler);
} return -EOPNOTSUPP;
}
/* If command is `set a parameter', or `get the encoding parameters', * check if the user has the right to do it.
*/ staticint wext_permission_check(unsignedint cmd)
{ if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE ||
cmd == SIOCGIWENCODEEXT) &&
!capable(CAP_NET_ADMIN)) return -EPERM;
return 0;
}
/* entry point from dev ioctl */ staticint wext_ioctl_dispatch(struct net *net, struct iwreq *iwr, unsignedint cmd, struct iw_request_info *info,
wext_ioctl_func standard,
wext_ioctl_func private)
{ int ret = wext_permission_check(cmd);
/* * 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.
*/ staticint ioctl_standard_call(struct net_device * dev, struct iwreq *iwr, unsignedint cmd, struct iw_request_info *info,
iw_handler handler)
{ conststruct iw_ioctl_description * descr; int ret = -EINVAL;
/* Get the description of the IOCTL */ if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) return -EOPNOTSUPP;
descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
/* 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 */ if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
/* Here, we will generate the appropriate event if needed */
return ret;
}
int wext_handle_ioctl(struct net *net, unsignedint cmd, void __user *arg)
{ struct iw_request_info info = { .cmd = cmd, .flags = 0 }; struct iwreq iwr; int ret;
if (copy_from_user(&iwr, arg, sizeof(iwr))) return -EFAULT;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.