542 lines
20 KiB
C
542 lines
20 KiB
C
/*****************************************************************************
|
|
|
|
(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;
|
|
}
|
|
|
|
|