linux/drivers/staging/csr/csr_wifi_hip_ta_sampling.c
<<
>>
Prefs
   1/*****************************************************************************
   2
   3            (c) Cambridge Silicon Radio Limited 2012
   4            All rights reserved and confidential information of CSR
   5
   6            Refer to LICENSE.txt included with this source for details
   7            on the license terms.
   8
   9*****************************************************************************/
  10
  11/*
  12 * ---------------------------------------------------------------------------
  13 *  FILE:     csr_wifi_hip_ta_sampling.c
  14 *
  15 *  PURPOSE:
  16 *      The traffic analysis sampling module.
  17 *      This gathers data which is sent to the SME and used to analyse
  18 *      the traffic behaviour.
  19 *
  20 * Provides:
  21 *      unifi_ta_sampling_init - Initialise the internal state
  22 *      unifi_ta_sample        - Sampling function, call this for every data packet
  23 *
  24 * Calls these external functions which must be provided:
  25 *      unifi_ta_indicate_sampling - Pass sample data to the SME.
  26 *      unifi_ta_indicate_protocol - Report certain data packet types to the SME.
  27 * ---------------------------------------------------------------------------
  28 */
  29
  30#include "csr_wifi_hip_card_sdio.h"
  31
  32/* Maximum number of Tx frames we store each CYCLE_1, for detecting period */
  33#define TA_MAX_INTERVALS_IN_C1          100
  34
  35/* Number of intervals in CYCLE_1 (one second), for detecting periodic */
  36/* Must match size of unifi_TrafficStats.intervals - 1 */
  37#define TA_INTERVALS_NUM               10
  38
  39/* Step (in msecs) between intervals, for detecting periodic */
  40/* We are only interested in periods up to 100ms, i.e. between beacons */
  41/* This is correct for TA_INTERVALS_NUM=10 */
  42#define TA_INTERVALS_STEP               10
  43
  44
  45enum ta_frame_identity
  46{
  47    TA_FRAME_UNKNOWN,
  48    TA_FRAME_ETHERNET_UNINTERESTING,
  49    TA_FRAME_ETHERNET_INTERESTING
  50};
  51
  52
  53#define TA_ETHERNET_TYPE_OFFSET     6
  54#define TA_LLC_HEADER_SIZE          8
  55#define TA_IP_TYPE_OFFSET           17
  56#define TA_UDP_SOURCE_PORT_OFFSET   28
  57#define TA_UDP_DEST_PORT_OFFSET     (TA_UDP_SOURCE_PORT_OFFSET + 2)
  58#define TA_BOOTP_CLIENT_MAC_ADDR_OFFSET 64
  59#define TA_DHCP_MESSAGE_TYPE_OFFSET 278
  60#define TA_DHCP_MESSAGE_TYPE_ACK    0x05
  61#define TA_PROTO_TYPE_IP            0x0800
  62#define TA_PROTO_TYPE_EAP           0x888E
  63#define TA_PROTO_TYPE_WAI           0x8864
  64#define TA_PROTO_TYPE_ARP           0x0806
  65#define TA_IP_TYPE_TCP              0x06
  66#define TA_IP_TYPE_UDP              0x11
  67#define TA_UDP_PORT_BOOTPC          0x0044
  68#define TA_UDP_PORT_BOOTPS          0x0043
  69#define TA_EAPOL_TYPE_OFFSET        9
  70#define TA_EAPOL_TYPE_START         0x01
  71
  72#define snap_802_2                  0xAAAA0300
  73#define oui_rfc1042                 0x00000000
  74#define oui_8021h                   0x0000f800
  75static const u8 aironet_snap[5] = { 0x00, 0x40, 0x96, 0x00, 0x00 };
  76
  77
  78/*
  79 * ---------------------------------------------------------------------------
  80 *  ta_detect_protocol
  81 *
  82 *      Internal only.
  83 *      Detects a specific protocol in a frame and indicates a TA event.
  84 *
  85 *  Arguments:
  86 *      ta              The pointer to the TA module.
  87 *      direction       The direction of the frame (tx or rx).
  88 *      data            Pointer to the structure that contains the data.
  89 *
  90 *  Returns:
  91 *      None
  92 * ---------------------------------------------------------------------------
  93 */
  94static enum ta_frame_identity ta_detect_protocol(card_t *card, CsrWifiRouterCtrlProtocolDirection direction,
  95                                                 const bulk_data_desc_t *data,
  96                                                 const u8 *saddr,
  97                                                 const u8 *sta_macaddr)
  98{
  99    ta_data_t *tad = &card->ta_sampling;
 100    u16 proto;
 101    u16 source_port, dest_port;
 102    CsrWifiMacAddress srcAddress;
 103    u32 snap_hdr, oui_hdr;
 104
 105    if (data->data_length < TA_LLC_HEADER_SIZE)
 106    {
 107        return TA_FRAME_UNKNOWN;
 108    }
 109
 110    snap_hdr = (((u32)data->os_data_ptr[0]) << 24) |
 111               (((u32)data->os_data_ptr[1]) << 16) |
 112               (((u32)data->os_data_ptr[2]) << 8);
 113    if (snap_hdr != snap_802_2)
 114    {
 115        return TA_FRAME_UNKNOWN;
 116    }
 117
 118    if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
 119    {
 120        /*
 121         * Here we would use the custom filter to detect interesting frames.
 122         */
 123    }
 124
 125    oui_hdr = (((u32)data->os_data_ptr[3]) << 24) |
 126              (((u32)data->os_data_ptr[4]) << 16) |
 127              (((u32)data->os_data_ptr[5]) << 8);
 128    if ((oui_hdr == oui_rfc1042) || (oui_hdr == oui_8021h))
 129    {
 130        proto = (data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET] * 256) +
 131                data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET + 1];
 132
 133        /* The only interesting IP frames are the DHCP */
 134        if (proto == TA_PROTO_TYPE_IP)
 135        {
 136            if (data->data_length > TA_IP_TYPE_OFFSET)
 137            {
 138                if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
 139                {
 140                    ta_l4stats_t *ta_l4stats = &tad->ta_l4stats;
 141                    u8 l4proto = data->os_data_ptr[TA_IP_TYPE_OFFSET];
 142
 143                    if (l4proto == TA_IP_TYPE_TCP)
 144                    {
 145                        if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
 146                        {
 147                            ta_l4stats->txTcpBytesCount += data->data_length;
 148                        }
 149                        else
 150                        {
 151                            ta_l4stats->rxTcpBytesCount += data->data_length;
 152                        }
 153                    }
 154                    else if (l4proto == TA_IP_TYPE_UDP)
 155                    {
 156                        if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
 157                        {
 158                            ta_l4stats->txUdpBytesCount += data->data_length;
 159                        }
 160                        else
 161                        {
 162                            ta_l4stats->rxUdpBytesCount += data->data_length;
 163                        }
 164                    }
 165                }
 166
 167                /* detect DHCP frames */
 168                if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP)
 169                {
 170                    /* DHCP frames are UDP frames with BOOTP ports */
 171                    if (data->os_data_ptr[TA_IP_TYPE_OFFSET] == TA_IP_TYPE_UDP)
 172                    {
 173                        if (data->data_length > TA_UDP_DEST_PORT_OFFSET)
 174                        {
 175                            source_port = (data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET] * 256) +
 176                                          data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET + 1];
 177                            dest_port = (data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET] * 256) +
 178                                        data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET + 1];
 179
 180                            if (((source_port == TA_UDP_PORT_BOOTPC) && (dest_port == TA_UDP_PORT_BOOTPS)) ||
 181                                ((source_port == TA_UDP_PORT_BOOTPS) && (dest_port == TA_UDP_PORT_BOOTPC)))
 182                            {
 183                                /* The DHCP should have at least a message type (request, ack, nack, etc) */
 184                                if (data->data_length > TA_DHCP_MESSAGE_TYPE_OFFSET + 6)
 185                                {
 186                                    UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
 187
 188                                    if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
 189                                    {
 190                                        unifi_ta_indicate_protocol(card->ospriv,
 191                                                                   CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
 192                                                                   direction,
 193                                                                   &srcAddress);
 194                                        return TA_FRAME_ETHERNET_UNINTERESTING;
 195                                    }
 196
 197                                    /* DHCPACK is a special indication */
 198                                    if (UNIFI_MAC_ADDRESS_CMP(data->os_data_ptr + TA_BOOTP_CLIENT_MAC_ADDR_OFFSET, sta_macaddr) == TRUE)
 199                                    {
 200                                        if (data->os_data_ptr[TA_DHCP_MESSAGE_TYPE_OFFSET] == TA_DHCP_MESSAGE_TYPE_ACK)
 201                                        {
 202                                            unifi_ta_indicate_protocol(card->ospriv,
 203                                                                       CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP_ACK,
 204                                                                       direction,
 205                                                                       &srcAddress);
 206                                        }
 207                                        else
 208                                        {
 209                                            unifi_ta_indicate_protocol(card->ospriv,
 210                                                                       CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
 211                                                                       direction,
 212                                                                       &srcAddress);
 213                                        }
 214                                    }
 215                                }
 216                            }
 217                        }
 218                    }
 219                }
 220            }
 221
 222            return TA_FRAME_ETHERNET_INTERESTING;
 223        }
 224
 225        /* detect protocol type EAPOL or WAI (treated as equivalent here) */
 226        if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL)
 227        {
 228            if (TA_PROTO_TYPE_EAP == proto || TA_PROTO_TYPE_WAI == proto)
 229            {
 230                if ((TA_PROTO_TYPE_WAI == proto) || (direction != CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) ||
 231                    (data->os_data_ptr[TA_EAPOL_TYPE_OFFSET] == TA_EAPOL_TYPE_START))
 232                {
 233                    UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
 234                    unifi_ta_indicate_protocol(card->ospriv,
 235                                               CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL,
 236                                               direction, &srcAddress);
 237                }
 238                return TA_FRAME_ETHERNET_UNINTERESTING;
 239            }
 240        }
 241
 242        /* detect protocol type 0x0806 (ARP) */
 243        if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP)
 244        {
 245            if (proto == TA_PROTO_TYPE_ARP)
 246            {
 247                UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
 248                unifi_ta_indicate_protocol(card->ospriv,
 249                                           CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP,
 250                                           direction, &srcAddress);
 251                return TA_FRAME_ETHERNET_UNINTERESTING;
 252            }
 253        }
 254
 255        return TA_FRAME_ETHERNET_INTERESTING;
 256    }
 257    else if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET)
 258    {
 259        /* detect Aironet frames */
 260        if (!memcmp(data->os_data_ptr + 3, aironet_snap, 5))
 261        {
 262            UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
 263            unifi_ta_indicate_protocol(card->ospriv, CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET,
 264                                       direction, &srcAddress);
 265        }
 266    }
 267
 268    return TA_FRAME_ETHERNET_UNINTERESTING;
 269} /* ta_detect_protocol() */
 270
 271
 272static void tas_reset_data(ta_data_t *tad)
 273{
 274    s16 i;
 275
 276    for (i = 0; i < (TA_INTERVALS_NUM + 1); i++)
 277    {
 278        tad->stats.intervals[i] = 0;
 279    }
 280
 281    tad->stats.rxFramesNum = 0;
 282    tad->stats.txFramesNum = 0;
 283    tad->stats.rxBytesCount = 0;
 284    tad->stats.txBytesCount = 0;
 285    tad->stats.rxMeanRate = 0;
 286
 287    tad->rx_sum_rate = 0;
 288
 289    tad->ta_l4stats.rxTcpBytesCount = 0;
 290    tad->ta_l4stats.txTcpBytesCount = 0;
 291    tad->ta_l4stats.rxUdpBytesCount = 0;
 292    tad->ta_l4stats.txUdpBytesCount = 0;
 293} /* tas_reset_data() */
 294
 295
 296/*
 297 * ---------------------------------------------------------------------------
 298 *  API.
 299 *  unifi_ta_sampling_init
 300 *
 301 *      (Re)Initialise the Traffic Analysis sampling module.
 302 *      Resets the counters and timestamps.
 303 *
 304 *  Arguments:
 305 *      tad             Pointer to a ta_data_t structure containing the
 306 *                      context for this device instance.
 307 *      drv_priv        An opaque pointer that the TA sampling module will
 308 *                      pass in call-outs.
 309 *
 310 *  Returns:
 311 *      None.
 312 * ---------------------------------------------------------------------------
 313 */
 314void unifi_ta_sampling_init(card_t *card)
 315{
 316    (void)unifi_ta_configure(card, CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET, NULL);
 317
 318    card->ta_sampling.packet_filter = CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE;
 319    card->ta_sampling.traffic_type = CSR_WIFI_ROUTER_CTRL_TRAFFIC_TYPE_OCCASIONAL;
 320} /* unifi_ta_sampling_init() */
 321
 322
 323/*
 324 * ---------------------------------------------------------------------------
 325 *  API.
 326 *  unifi_ta_sample
 327 *
 328 *      Sample a data frame for the TA module.
 329 *      This function stores all the useful information it can extract from
 330 *      the frame and detects any specific protocols.
 331 *
 332 *  Arguments:
 333 *      tad             The pointer to the TA sampling context struct.
 334 *      direction       The direction of the frame (rx, tx)
 335 *      data            Pointer to the frame data
 336 *      saddr           Source MAC address of frame.
 337 *      timestamp       Time (in msecs) that the frame was received.
 338 *      rate            Reported data rate for the rx frame (0 for tx frames)
 339 *
 340 *  Returns:
 341 *      None
 342 * ---------------------------------------------------------------------------
 343 */
 344void unifi_ta_sample(card_t                            *card,
 345                     CsrWifiRouterCtrlProtocolDirection direction,
 346                     const bulk_data_desc_t            *data,
 347                     const u8                    *saddr,
 348                     const u8                    *sta_macaddr,
 349                     u32                          timestamp,
 350                     u16                          rate)
 351{
 352    ta_data_t *tad = &card->ta_sampling;
 353    enum ta_frame_identity identity;
 354    u32 time_delta;
 355
 356
 357
 358    /* Step1: Check for specific frames */
 359    if (tad->packet_filter != CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE)
 360    {
 361        identity = ta_detect_protocol(card, direction, data, saddr, sta_macaddr);
 362    }
 363    else
 364    {
 365        identity = TA_FRAME_ETHERNET_INTERESTING;
 366    }
 367
 368
 369    /* Step2: Update the information in the current record */
 370    if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_RX)
 371    {
 372        /* Update the Rx packet count and the throughput count */
 373        tad->stats.rxFramesNum++;
 374        tad->stats.rxBytesCount += data->data_length;
 375
 376        /* Accumulate packet Rx rates for later averaging */
 377        tad->rx_sum_rate += rate;
 378    }
 379    else
 380    {
 381        if (identity == TA_FRAME_ETHERNET_INTERESTING)
 382        {
 383            /*
 384             * Store the period between the last and the current frame.
 385             * There is not point storing more than TA_MAX_INTERVALS_IN_C1 periods,
 386             * the traffic will be bursty or continuous.
 387             */
 388            if (tad->stats.txFramesNum < TA_MAX_INTERVALS_IN_C1)
 389            {
 390                u32 interval;
 391                u32 index_in_intervals;
 392
 393                interval = timestamp - tad->tx_last_ts;
 394                tad->tx_last_ts = timestamp;
 395                index_in_intervals = (interval + TA_INTERVALS_STEP / 2 - 1) / TA_INTERVALS_STEP;
 396
 397                /* If the interval is interesting, update the t1_intervals count */
 398                if (index_in_intervals <= TA_INTERVALS_NUM)
 399                {
 400                    unifi_trace(card->ospriv, UDBG5,
 401                                "unifi_ta_sample: TX interval=%d index=%d\n",
 402                                interval, index_in_intervals);
 403                    tad->stats.intervals[index_in_intervals]++;
 404                }
 405            }
 406        }
 407
 408        /* Update the Tx packet count... */
 409        tad->stats.txFramesNum++;
 410        /* ... and the number of bytes for throughput. */
 411        tad->stats.txBytesCount += data->data_length;
 412    }
 413
 414    /*
 415     * If more than one second has elapsed since the last report, send
 416     * another one.
 417     */
 418    /* Unsigned subtraction handles wrap-around from 0xFFFFFFFF to 0 */
 419    time_delta = timestamp - tad->last_indication_time;
 420    if (time_delta >= 1000)
 421    {
 422        /*
 423         * rxFramesNum can be flashed in tas_reset_data() by another thread.
 424         * Use a temp to avoid division by zero.
 425         */
 426        u32 temp_rxFramesNum;
 427        temp_rxFramesNum = tad->stats.rxFramesNum;
 428
 429        /* Calculate this interval's mean frame Rx rate from the sum */
 430        if (temp_rxFramesNum)
 431        {
 432            tad->stats.rxMeanRate = tad->rx_sum_rate / temp_rxFramesNum;
 433        }
 434        unifi_trace(card->ospriv, UDBG5,
 435                    "unifi_ta_sample: RX fr=%lu, r=%u, sum=%lu, av=%lu\n",
 436                    tad->stats.rxFramesNum, rate,
 437                    tad->rx_sum_rate, tad->stats.rxMeanRate);
 438
 439        /*
 440         * Send the information collected in the stats struct
 441         * to the SME and reset the counters.
 442         */
 443        if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
 444        {
 445            u32 rxTcpThroughput = tad->ta_l4stats.rxTcpBytesCount / time_delta;
 446            u32 txTcpThroughput = tad->ta_l4stats.txTcpBytesCount / time_delta;
 447            u32 rxUdpThroughput = tad->ta_l4stats.rxUdpBytesCount / time_delta;
 448            u32 txUdpThroughput = tad->ta_l4stats.txUdpBytesCount / time_delta;
 449
 450            unifi_ta_indicate_l4stats(card->ospriv,
 451                                      rxTcpThroughput,
 452                                      txTcpThroughput,
 453                                      rxUdpThroughput,
 454                                      txUdpThroughput
 455                                      );
 456        }
 457        unifi_ta_indicate_sampling(card->ospriv, &tad->stats);
 458        tas_reset_data(tad);
 459        tad->last_indication_time = timestamp;
 460    }
 461} /* unifi_ta_sample() */
 462
 463
 464/*
 465 * ---------------------------------------------------------------------------
 466 *  External API.
 467 *  unifi_ta_configure
 468 *
 469 *      Configures the TA module parameters.
 470 *
 471 *  Arguments:
 472 *      ta              The pointer to the TA module.
 473 *      config_type     The type of the configuration request
 474 *      config          Pointer to the configuration parameters.
 475 *
 476 *  Returns:
 477 *      CSR_RESULT_SUCCESS on success, CSR error code otherwise
 478 * ---------------------------------------------------------------------------
 479 */
 480CsrResult unifi_ta_configure(card_t                               *card,
 481                             CsrWifiRouterCtrlTrafficConfigType    config_type,
 482                             const CsrWifiRouterCtrlTrafficConfig *config)
 483{
 484    ta_data_t *tad = &card->ta_sampling;
 485
 486    /* Reinitialise our data when we are reset */
 487    if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET)
 488    {
 489        /* Reset the stats to zero */
 490        tas_reset_data(tad);
 491
 492        /* Reset the timer variables */
 493        tad->tx_last_ts = 0;
 494        tad->last_indication_time = 0;
 495
 496        return CSR_RESULT_SUCCESS;
 497    }
 498
 499    if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_FILTER)
 500    {
 501        tad->packet_filter = config->packetFilter;
 502
 503        if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
 504        {
 505            tad->custom_filter = config->customFilter;
 506        }
 507
 508        return CSR_RESULT_SUCCESS;
 509    }
 510
 511    return CSR_RESULT_SUCCESS;
 512} /* unifi_ta_configure() */
 513
 514
 515/*
 516 * ---------------------------------------------------------------------------
 517 *  External API.
 518 *  unifi_ta_classification
 519 *
 520 *      Configures the current TA classification.
 521 *
 522 *  Arguments:
 523 *      ta              The pointer to the TA module.
 524 *      traffic_type    The classification type
 525 *      period          The traffic period if the type is periodic
 526 *
 527 *  Returns:
 528 *      None
 529 * ---------------------------------------------------------------------------
 530 */
 531void unifi_ta_classification(card_t                      *card,
 532                             CsrWifiRouterCtrlTrafficType traffic_type,
 533                             u16                    period)
 534{
 535    unifi_trace(card->ospriv, UDBG3,
 536                "Changed current ta classification to: %d\n", traffic_type);
 537
 538    card->ta_sampling.traffic_type = traffic_type;
 539}
 540
 541
 542