| /***************************************************************************** |
| |
| (c) Cambridge Silicon Radio Limited 2012 |
| All rights reserved and confidential information of CSR |
| |
| Refer to LICENSE.txt included with this source for details |
| on the license terms. |
| |
| *****************************************************************************/ |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * FILE: csr_wifi_hip_ta_sampling.c |
| * |
| * PURPOSE: |
| * The traffic analysis sampling module. |
| * This gathers data which is sent to the SME and used to analyse |
| * the traffic behaviour. |
| * |
| * Provides: |
| * unifi_ta_sampling_init - Initialise the internal state |
| * unifi_ta_sample - Sampling function, call this for every data packet |
| * |
| * Calls these external functions which must be provided: |
| * unifi_ta_indicate_sampling - Pass sample data to the SME. |
| * unifi_ta_indicate_protocol - Report certain data packet types to the SME. |
| * --------------------------------------------------------------------------- |
| */ |
| |
| #include "csr_wifi_hip_card_sdio.h" |
| |
| /* Maximum number of Tx frames we store each CYCLE_1, for detecting period */ |
| #define TA_MAX_INTERVALS_IN_C1 100 |
| |
| /* Number of intervals in CYCLE_1 (one second), for detecting periodic */ |
| /* Must match size of unifi_TrafficStats.intervals - 1 */ |
| #define TA_INTERVALS_NUM 10 |
| |
| /* Step (in msecs) between intervals, for detecting periodic */ |
| /* We are only interested in periods up to 100ms, i.e. between beacons */ |
| /* This is correct for TA_INTERVALS_NUM=10 */ |
| #define TA_INTERVALS_STEP 10 |
| |
| |
| enum ta_frame_identity |
| { |
| TA_FRAME_UNKNOWN, |
| TA_FRAME_ETHERNET_UNINTERESTING, |
| TA_FRAME_ETHERNET_INTERESTING |
| }; |
| |
| |
| #define TA_ETHERNET_TYPE_OFFSET 6 |
| #define TA_LLC_HEADER_SIZE 8 |
| #define TA_IP_TYPE_OFFSET 17 |
| #define TA_UDP_SOURCE_PORT_OFFSET 28 |
| #define TA_UDP_DEST_PORT_OFFSET (TA_UDP_SOURCE_PORT_OFFSET + 2) |
| #define TA_BOOTP_CLIENT_MAC_ADDR_OFFSET 64 |
| #define TA_DHCP_MESSAGE_TYPE_OFFSET 278 |
| #define TA_DHCP_MESSAGE_TYPE_ACK 0x05 |
| #define TA_PROTO_TYPE_IP 0x0800 |
| #define TA_PROTO_TYPE_EAP 0x888E |
| #define TA_PROTO_TYPE_WAI 0x8864 |
| #define TA_PROTO_TYPE_ARP 0x0806 |
| #define TA_IP_TYPE_TCP 0x06 |
| #define TA_IP_TYPE_UDP 0x11 |
| #define TA_UDP_PORT_BOOTPC 0x0044 |
| #define TA_UDP_PORT_BOOTPS 0x0043 |
| #define TA_EAPOL_TYPE_OFFSET 9 |
| #define TA_EAPOL_TYPE_START 0x01 |
| |
| #define snap_802_2 0xAAAA0300 |
| #define oui_rfc1042 0x00000000 |
| #define oui_8021h 0x0000f800 |
| static const u8 aironet_snap[5] = { 0x00, 0x40, 0x96, 0x00, 0x00 }; |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * ta_detect_protocol |
| * |
| * Internal only. |
| * Detects a specific protocol in a frame and indicates a TA event. |
| * |
| * Arguments: |
| * ta The pointer to the TA module. |
| * direction The direction of the frame (tx or rx). |
| * data Pointer to the structure that contains the data. |
| * |
| * Returns: |
| * None |
| * --------------------------------------------------------------------------- |
| */ |
| static enum ta_frame_identity ta_detect_protocol(card_t *card, CsrWifiRouterCtrlProtocolDirection direction, |
| const bulk_data_desc_t *data, |
| const u8 *saddr, |
| const u8 *sta_macaddr) |
| { |
| ta_data_t *tad = &card->ta_sampling; |
| u16 proto; |
| u16 source_port, dest_port; |
| CsrWifiMacAddress srcAddress; |
| u32 snap_hdr, oui_hdr; |
| |
| if (data->data_length < TA_LLC_HEADER_SIZE) |
| { |
| return TA_FRAME_UNKNOWN; |
| } |
| |
| snap_hdr = (((u32)data->os_data_ptr[0]) << 24) | |
| (((u32)data->os_data_ptr[1]) << 16) | |
| (((u32)data->os_data_ptr[2]) << 8); |
| if (snap_hdr != snap_802_2) |
| { |
| return TA_FRAME_UNKNOWN; |
| } |
| |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM) |
| { |
| /* |
| * Here we would use the custom filter to detect interesting frames. |
| */ |
| } |
| |
| oui_hdr = (((u32)data->os_data_ptr[3]) << 24) | |
| (((u32)data->os_data_ptr[4]) << 16) | |
| (((u32)data->os_data_ptr[5]) << 8); |
| if ((oui_hdr == oui_rfc1042) || (oui_hdr == oui_8021h)) |
| { |
| proto = (data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET] * 256) + |
| data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET + 1]; |
| |
| /* The only interesting IP frames are the DHCP */ |
| if (proto == TA_PROTO_TYPE_IP) |
| { |
| if (data->data_length > TA_IP_TYPE_OFFSET) |
| { |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM) |
| { |
| ta_l4stats_t *ta_l4stats = &tad->ta_l4stats; |
| u8 l4proto = data->os_data_ptr[TA_IP_TYPE_OFFSET]; |
| |
| if (l4proto == TA_IP_TYPE_TCP) |
| { |
| if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) |
| { |
| ta_l4stats->txTcpBytesCount += data->data_length; |
| } |
| else |
| { |
| ta_l4stats->rxTcpBytesCount += data->data_length; |
| } |
| } |
| else if (l4proto == TA_IP_TYPE_UDP) |
| { |
| if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) |
| { |
| ta_l4stats->txUdpBytesCount += data->data_length; |
| } |
| else |
| { |
| ta_l4stats->rxUdpBytesCount += data->data_length; |
| } |
| } |
| } |
| |
| /* detect DHCP frames */ |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP) |
| { |
| /* DHCP frames are UDP frames with BOOTP ports */ |
| if (data->os_data_ptr[TA_IP_TYPE_OFFSET] == TA_IP_TYPE_UDP) |
| { |
| if (data->data_length > TA_UDP_DEST_PORT_OFFSET) |
| { |
| source_port = (data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET] * 256) + |
| data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET + 1]; |
| dest_port = (data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET] * 256) + |
| data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET + 1]; |
| |
| if (((source_port == TA_UDP_PORT_BOOTPC) && (dest_port == TA_UDP_PORT_BOOTPS)) || |
| ((source_port == TA_UDP_PORT_BOOTPS) && (dest_port == TA_UDP_PORT_BOOTPC))) |
| { |
| /* The DHCP should have at least a message type (request, ack, nack, etc) */ |
| if (data->data_length > TA_DHCP_MESSAGE_TYPE_OFFSET + 6) |
| { |
| UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr); |
| |
| if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) |
| { |
| unifi_ta_indicate_protocol(card->ospriv, |
| CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP, |
| direction, |
| &srcAddress); |
| return TA_FRAME_ETHERNET_UNINTERESTING; |
| } |
| |
| /* DHCPACK is a special indication */ |
| if (UNIFI_MAC_ADDRESS_CMP(data->os_data_ptr + TA_BOOTP_CLIENT_MAC_ADDR_OFFSET, sta_macaddr) == TRUE) |
| { |
| if (data->os_data_ptr[TA_DHCP_MESSAGE_TYPE_OFFSET] == TA_DHCP_MESSAGE_TYPE_ACK) |
| { |
| unifi_ta_indicate_protocol(card->ospriv, |
| CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP_ACK, |
| direction, |
| &srcAddress); |
| } |
| else |
| { |
| unifi_ta_indicate_protocol(card->ospriv, |
| CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP, |
| direction, |
| &srcAddress); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return TA_FRAME_ETHERNET_INTERESTING; |
| } |
| |
| /* detect protocol type EAPOL or WAI (treated as equivalent here) */ |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL) |
| { |
| if (TA_PROTO_TYPE_EAP == proto || TA_PROTO_TYPE_WAI == proto) |
| { |
| if ((TA_PROTO_TYPE_WAI == proto) || (direction != CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) || |
| (data->os_data_ptr[TA_EAPOL_TYPE_OFFSET] == TA_EAPOL_TYPE_START)) |
| { |
| UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr); |
| unifi_ta_indicate_protocol(card->ospriv, |
| CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL, |
| direction, &srcAddress); |
| } |
| return TA_FRAME_ETHERNET_UNINTERESTING; |
| } |
| } |
| |
| /* detect protocol type 0x0806 (ARP) */ |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP) |
| { |
| if (proto == TA_PROTO_TYPE_ARP) |
| { |
| UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr); |
| unifi_ta_indicate_protocol(card->ospriv, |
| CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP, |
| direction, &srcAddress); |
| return TA_FRAME_ETHERNET_UNINTERESTING; |
| } |
| } |
| |
| return TA_FRAME_ETHERNET_INTERESTING; |
| } |
| else if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET) |
| { |
| /* detect Aironet frames */ |
| if (!memcmp(data->os_data_ptr + 3, aironet_snap, 5)) |
| { |
| UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr); |
| unifi_ta_indicate_protocol(card->ospriv, CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET, |
| direction, &srcAddress); |
| } |
| } |
| |
| return TA_FRAME_ETHERNET_UNINTERESTING; |
| } /* ta_detect_protocol() */ |
| |
| |
| static void tas_reset_data(ta_data_t *tad) |
| { |
| s16 i; |
| |
| for (i = 0; i < (TA_INTERVALS_NUM + 1); i++) |
| { |
| tad->stats.intervals[i] = 0; |
| } |
| |
| tad->stats.rxFramesNum = 0; |
| tad->stats.txFramesNum = 0; |
| tad->stats.rxBytesCount = 0; |
| tad->stats.txBytesCount = 0; |
| tad->stats.rxMeanRate = 0; |
| |
| tad->rx_sum_rate = 0; |
| |
| tad->ta_l4stats.rxTcpBytesCount = 0; |
| tad->ta_l4stats.txTcpBytesCount = 0; |
| tad->ta_l4stats.rxUdpBytesCount = 0; |
| tad->ta_l4stats.txUdpBytesCount = 0; |
| } /* tas_reset_data() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * API. |
| * unifi_ta_sampling_init |
| * |
| * (Re)Initialise the Traffic Analysis sampling module. |
| * Resets the counters and timestamps. |
| * |
| * Arguments: |
| * tad Pointer to a ta_data_t structure containing the |
| * context for this device instance. |
| * drv_priv An opaque pointer that the TA sampling module will |
| * pass in call-outs. |
| * |
| * Returns: |
| * None. |
| * --------------------------------------------------------------------------- |
| */ |
| void unifi_ta_sampling_init(card_t *card) |
| { |
| (void)unifi_ta_configure(card, CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET, NULL); |
| |
| card->ta_sampling.packet_filter = CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE; |
| card->ta_sampling.traffic_type = CSR_WIFI_ROUTER_CTRL_TRAFFIC_TYPE_OCCASIONAL; |
| } /* unifi_ta_sampling_init() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * API. |
| * unifi_ta_sample |
| * |
| * Sample a data frame for the TA module. |
| * This function stores all the useful information it can extract from |
| * the frame and detects any specific protocols. |
| * |
| * Arguments: |
| * tad The pointer to the TA sampling context struct. |
| * direction The direction of the frame (rx, tx) |
| * data Pointer to the frame data |
| * saddr Source MAC address of frame. |
| * timestamp Time (in msecs) that the frame was received. |
| * rate Reported data rate for the rx frame (0 for tx frames) |
| * |
| * Returns: |
| * None |
| * --------------------------------------------------------------------------- |
| */ |
| void unifi_ta_sample(card_t *card, |
| CsrWifiRouterCtrlProtocolDirection direction, |
| const bulk_data_desc_t *data, |
| const u8 *saddr, |
| const u8 *sta_macaddr, |
| u32 timestamp, |
| u16 rate) |
| { |
| ta_data_t *tad = &card->ta_sampling; |
| enum ta_frame_identity identity; |
| u32 time_delta; |
| |
| |
| |
| /* Step1: Check for specific frames */ |
| if (tad->packet_filter != CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE) |
| { |
| identity = ta_detect_protocol(card, direction, data, saddr, sta_macaddr); |
| } |
| else |
| { |
| identity = TA_FRAME_ETHERNET_INTERESTING; |
| } |
| |
| |
| /* Step2: Update the information in the current record */ |
| if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_RX) |
| { |
| /* Update the Rx packet count and the throughput count */ |
| tad->stats.rxFramesNum++; |
| tad->stats.rxBytesCount += data->data_length; |
| |
| /* Accumulate packet Rx rates for later averaging */ |
| tad->rx_sum_rate += rate; |
| } |
| else |
| { |
| if (identity == TA_FRAME_ETHERNET_INTERESTING) |
| { |
| /* |
| * Store the period between the last and the current frame. |
| * There is not point storing more than TA_MAX_INTERVALS_IN_C1 periods, |
| * the traffic will be bursty or continuous. |
| */ |
| if (tad->stats.txFramesNum < TA_MAX_INTERVALS_IN_C1) |
| { |
| u32 interval; |
| u32 index_in_intervals; |
| |
| interval = timestamp - tad->tx_last_ts; |
| tad->tx_last_ts = timestamp; |
| index_in_intervals = (interval + TA_INTERVALS_STEP / 2 - 1) / TA_INTERVALS_STEP; |
| |
| /* If the interval is interesting, update the t1_intervals count */ |
| if (index_in_intervals <= TA_INTERVALS_NUM) |
| { |
| unifi_trace(card->ospriv, UDBG5, |
| "unifi_ta_sample: TX interval=%d index=%d\n", |
| interval, index_in_intervals); |
| tad->stats.intervals[index_in_intervals]++; |
| } |
| } |
| } |
| |
| /* Update the Tx packet count... */ |
| tad->stats.txFramesNum++; |
| /* ... and the number of bytes for throughput. */ |
| tad->stats.txBytesCount += data->data_length; |
| } |
| |
| /* |
| * If more than one second has elapsed since the last report, send |
| * another one. |
| */ |
| /* Unsigned subtraction handles wrap-around from 0xFFFFFFFF to 0 */ |
| time_delta = timestamp - tad->last_indication_time; |
| if (time_delta >= 1000) |
| { |
| /* |
| * rxFramesNum can be flashed in tas_reset_data() by another thread. |
| * Use a temp to avoid division by zero. |
| */ |
| u32 temp_rxFramesNum; |
| temp_rxFramesNum = tad->stats.rxFramesNum; |
| |
| /* Calculate this interval's mean frame Rx rate from the sum */ |
| if (temp_rxFramesNum) |
| { |
| tad->stats.rxMeanRate = tad->rx_sum_rate / temp_rxFramesNum; |
| } |
| unifi_trace(card->ospriv, UDBG5, |
| "unifi_ta_sample: RX fr=%lu, r=%u, sum=%lu, av=%lu\n", |
| tad->stats.rxFramesNum, rate, |
| tad->rx_sum_rate, tad->stats.rxMeanRate); |
| |
| /* |
| * Send the information collected in the stats struct |
| * to the SME and reset the counters. |
| */ |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM) |
| { |
| u32 rxTcpThroughput = tad->ta_l4stats.rxTcpBytesCount / time_delta; |
| u32 txTcpThroughput = tad->ta_l4stats.txTcpBytesCount / time_delta; |
| u32 rxUdpThroughput = tad->ta_l4stats.rxUdpBytesCount / time_delta; |
| u32 txUdpThroughput = tad->ta_l4stats.txUdpBytesCount / time_delta; |
| |
| unifi_ta_indicate_l4stats(card->ospriv, |
| rxTcpThroughput, |
| txTcpThroughput, |
| rxUdpThroughput, |
| txUdpThroughput |
| ); |
| } |
| unifi_ta_indicate_sampling(card->ospriv, &tad->stats); |
| tas_reset_data(tad); |
| tad->last_indication_time = timestamp; |
| } |
| } /* unifi_ta_sample() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * External API. |
| * unifi_ta_configure |
| * |
| * Configures the TA module parameters. |
| * |
| * Arguments: |
| * ta The pointer to the TA module. |
| * config_type The type of the configuration request |
| * config Pointer to the configuration parameters. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, CSR error code otherwise |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_ta_configure(card_t *card, |
| CsrWifiRouterCtrlTrafficConfigType config_type, |
| const CsrWifiRouterCtrlTrafficConfig *config) |
| { |
| ta_data_t *tad = &card->ta_sampling; |
| |
| /* Reinitialise our data when we are reset */ |
| if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET) |
| { |
| /* Reset the stats to zero */ |
| tas_reset_data(tad); |
| |
| /* Reset the timer variables */ |
| tad->tx_last_ts = 0; |
| tad->last_indication_time = 0; |
| |
| return CSR_RESULT_SUCCESS; |
| } |
| |
| if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_FILTER) |
| { |
| tad->packet_filter = config->packetFilter; |
| |
| if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM) |
| { |
| tad->custom_filter = config->customFilter; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } /* unifi_ta_configure() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * External API. |
| * unifi_ta_classification |
| * |
| * Configures the current TA classification. |
| * |
| * Arguments: |
| * ta The pointer to the TA module. |
| * traffic_type The classification type |
| * period The traffic period if the type is periodic |
| * |
| * Returns: |
| * None |
| * --------------------------------------------------------------------------- |
| */ |
| void unifi_ta_classification(card_t *card, |
| CsrWifiRouterCtrlTrafficType traffic_type, |
| u16 period) |
| { |
| unifi_trace(card->ospriv, UDBG3, |
| "Changed current ta classification to: %d\n", traffic_type); |
| |
| card->ta_sampling.traffic_type = traffic_type; |
| } |
| |
| |