📄 wext.c
字号:
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 + -