linux/net/wireless/wext-spy.c
<<
>>
Prefs
   1/*
   2 * This file implement the Wireless Extensions spy API.
   3 *
   4 * Authors :    Jean Tourrilhes - HPL - <jt@hpl.hp.com>
   5 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
   6 *
   7 * (As all part of the Linux kernel, this file is GPL)
   8 */
   9
  10#include <linux/wireless.h>
  11#include <linux/netdevice.h>
  12#include <linux/etherdevice.h>
  13#include <net/iw_handler.h>
  14#include <net/arp.h>
  15#include <net/wext.h>
  16
  17static inline struct iw_spy_data *get_spydata(struct net_device *dev)
  18{
  19        /* This is the new way */
  20        if (dev->wireless_data)
  21                return dev->wireless_data->spy_data;
  22        return NULL;
  23}
  24
  25int iw_handler_set_spy(struct net_device *      dev,
  26                       struct iw_request_info * info,
  27                       union iwreq_data *       wrqu,
  28                       char *                   extra)
  29{
  30        struct iw_spy_data *    spydata = get_spydata(dev);
  31        struct sockaddr *       address = (struct sockaddr *) extra;
  32
  33        /* Make sure driver is not buggy or using the old API */
  34        if (!spydata)
  35                return -EOPNOTSUPP;
  36
  37        /* Disable spy collection while we copy the addresses.
  38         * While we copy addresses, any call to wireless_spy_update()
  39         * will NOP. This is OK, as anyway the addresses are changing. */
  40        spydata->spy_number = 0;
  41
  42        /* We want to operate without locking, because wireless_spy_update()
  43         * most likely will happen in the interrupt handler, and therefore
  44         * have its own locking constraints and needs performance.
  45         * The rtnl_lock() make sure we don't race with the other iw_handlers.
  46         * This make sure wireless_spy_update() "see" that the spy list
  47         * is temporarily disabled. */
  48        smp_wmb();
  49
  50        /* Are there are addresses to copy? */
  51        if (wrqu->data.length > 0) {
  52                int i;
  53
  54                /* Copy addresses */
  55                for (i = 0; i < wrqu->data.length; i++)
  56                        memcpy(spydata->spy_address[i], address[i].sa_data,
  57                               ETH_ALEN);
  58                /* Reset stats */
  59                memset(spydata->spy_stat, 0,
  60                       sizeof(struct iw_quality) * IW_MAX_SPY);
  61        }
  62
  63        /* Make sure above is updated before re-enabling */
  64        smp_wmb();
  65
  66        /* Enable addresses */
  67        spydata->spy_number = wrqu->data.length;
  68
  69        return 0;
  70}
  71EXPORT_SYMBOL(iw_handler_set_spy);
  72
  73int iw_handler_get_spy(struct net_device *      dev,
  74                       struct iw_request_info * info,
  75                       union iwreq_data *       wrqu,
  76                       char *                   extra)
  77{
  78        struct iw_spy_data *    spydata = get_spydata(dev);
  79        struct sockaddr *       address = (struct sockaddr *) extra;
  80        int                     i;
  81
  82        /* Make sure driver is not buggy or using the old API */
  83        if (!spydata)
  84                return -EOPNOTSUPP;
  85
  86        wrqu->data.length = spydata->spy_number;
  87
  88        /* Copy addresses. */
  89        for (i = 0; i < spydata->spy_number; i++)       {
  90                memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
  91                address[i].sa_family = AF_UNIX;
  92        }
  93        /* Copy stats to the user buffer (just after). */
  94        if (spydata->spy_number > 0)
  95                memcpy(extra  + (sizeof(struct sockaddr) *spydata->spy_number),
  96                       spydata->spy_stat,
  97                       sizeof(struct iw_quality) * spydata->spy_number);
  98        /* Reset updated flags. */
  99        for (i = 0; i < spydata->spy_number; i++)
 100                spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
 101        return 0;
 102}
 103EXPORT_SYMBOL(iw_handler_get_spy);
 104
 105/*------------------------------------------------------------------*/
 106/*
 107 * Standard Wireless Handler : set spy threshold
 108 */
 109int iw_handler_set_thrspy(struct net_device *   dev,
 110                          struct iw_request_info *info,
 111                          union iwreq_data *    wrqu,
 112                          char *                extra)
 113{
 114        struct iw_spy_data *    spydata = get_spydata(dev);
 115        struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
 116
 117        /* Make sure driver is not buggy or using the old API */
 118        if (!spydata)
 119                return -EOPNOTSUPP;
 120
 121        /* Just do it */
 122        memcpy(&(spydata->spy_thr_low), &(threshold->low),
 123               2 * sizeof(struct iw_quality));
 124
 125        /* Clear flag */
 126        memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
 127
 128        return 0;
 129}
 130EXPORT_SYMBOL(iw_handler_set_thrspy);
 131
 132/*------------------------------------------------------------------*/
 133/*
 134 * Standard Wireless Handler : get spy threshold
 135 */
 136int iw_handler_get_thrspy(struct net_device *   dev,
 137                          struct iw_request_info *info,
 138                          union iwreq_data *    wrqu,
 139                          char *                extra)
 140{
 141        struct iw_spy_data *    spydata = get_spydata(dev);
 142        struct iw_thrspy *      threshold = (struct iw_thrspy *) extra;
 143
 144        /* Make sure driver is not buggy or using the old API */
 145        if (!spydata)
 146                return -EOPNOTSUPP;
 147
 148        /* Just do it */
 149        memcpy(&(threshold->low), &(spydata->spy_thr_low),
 150               2 * sizeof(struct iw_quality));
 151
 152        return 0;
 153}
 154EXPORT_SYMBOL(iw_handler_get_thrspy);
 155
 156/*------------------------------------------------------------------*/
 157/*
 158 * Prepare and send a Spy Threshold event
 159 */
 160static void iw_send_thrspy_event(struct net_device *    dev,
 161                                 struct iw_spy_data *   spydata,
 162                                 unsigned char *        address,
 163                                 struct iw_quality *    wstats)
 164{
 165        union iwreq_data        wrqu;
 166        struct iw_thrspy        threshold;
 167
 168        /* Init */
 169        wrqu.data.length = 1;
 170        wrqu.data.flags = 0;
 171        /* Copy address */
 172        memcpy(threshold.addr.sa_data, address, ETH_ALEN);
 173        threshold.addr.sa_family = ARPHRD_ETHER;
 174        /* Copy stats */
 175        memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
 176        /* Copy also thresholds */
 177        memcpy(&(threshold.low), &(spydata->spy_thr_low),
 178               2 * sizeof(struct iw_quality));
 179
 180        /* Send event to user space */
 181        wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
 182}
 183
 184/* ---------------------------------------------------------------- */
 185/*
 186 * Call for the driver to update the spy data.
 187 * For now, the spy data is a simple array. As the size of the array is
 188 * small, this is good enough. If we wanted to support larger number of
 189 * spy addresses, we should use something more efficient...
 190 */
 191void wireless_spy_update(struct net_device *    dev,
 192                         unsigned char *        address,
 193                         struct iw_quality *    wstats)
 194{
 195        struct iw_spy_data *    spydata = get_spydata(dev);
 196        int                     i;
 197        int                     match = -1;
 198
 199        /* Make sure driver is not buggy or using the old API */
 200        if (!spydata)
 201                return;
 202
 203        /* Update all records that match */
 204        for (i = 0; i < spydata->spy_number; i++)
 205                if (!compare_ether_addr(address, spydata->spy_address[i])) {
 206                        memcpy(&(spydata->spy_stat[i]), wstats,
 207                               sizeof(struct iw_quality));
 208                        match = i;
 209                }
 210
 211        /* Generate an event if we cross the spy threshold.
 212         * To avoid event storms, we have a simple hysteresis : we generate
 213         * event only when we go under the low threshold or above the
 214         * high threshold. */
 215        if (match >= 0) {
 216                if (spydata->spy_thr_under[match]) {
 217                        if (wstats->level > spydata->spy_thr_high.level) {
 218                                spydata->spy_thr_under[match] = 0;
 219                                iw_send_thrspy_event(dev, spydata,
 220                                                     address, wstats);
 221                        }
 222                } else {
 223                        if (wstats->level < spydata->spy_thr_low.level) {
 224                                spydata->spy_thr_under[match] = 1;
 225                                iw_send_thrspy_event(dev, spydata,
 226                                                     address, wstats);
 227                        }
 228                }
 229        }
 230}
 231EXPORT_SYMBOL(wireless_spy_update);
 232