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