| /* |
| * --------------------------------------------------------------------------- |
| * FILE: monitor.c |
| * |
| * Copyright (C) 2006-2008 by Cambridge Silicon Radio Ltd. |
| * |
| * Refer to LICENSE.txt included with this source code for details on |
| * the license terms. |
| * |
| * --------------------------------------------------------------------------- |
| */ |
| |
| #include "unifi_priv.h" |
| |
| #ifdef UNIFI_SNIFF_ARPHRD |
| |
| |
| #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP) |
| #include <net/ieee80211_radiotap.h> |
| #endif |
| |
| #ifndef ETH_P_80211_RAW |
| #define ETH_P_80211_RAW ETH_P_ALL |
| #endif |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * uf_start_sniff |
| * |
| * Start UniFi capture in SNIFF mode, i.e capture everything it hears. |
| * |
| * Arguments: |
| * priv Pointer to device private context struct |
| * |
| * Returns: |
| * 0 on success or kernel error code |
| * --------------------------------------------------------------------------- |
| */ |
| int |
| uf_start_sniff(unifi_priv_t *priv) |
| { |
| ul_client_t *pcli = priv->wext_client; |
| CSR_SIGNAL signal; |
| CSR_MLME_SNIFFJOIN_REQUEST *req = &signal.u.MlmeSniffjoinRequest; |
| int timeout = 1000; |
| int r; |
| |
| req->Ifindex = priv->if_index; |
| req->Channel = priv->wext_conf.channel; |
| req->ChannelStartingFactor = 0; |
| |
| signal.SignalPrimitiveHeader.SignalId = CSR_MLME_SNIFFJOIN_REQUEST_ID; |
| |
| r = unifi_mlme_blocking_request(priv, pcli, &signal, NULL, timeout); |
| if (r < 0) { |
| unifi_error(priv, "failed to send SNIFFJOIN request, error %d\n", r); |
| return r; |
| } |
| |
| r = pcli->reply_signal->u.MlmeSniffjoinConfirm.Resultcode; |
| if (r) { |
| unifi_notice(priv, "SNIFFJOIN request was rejected with result 0x%X (%s)\n", |
| r, lookup_result_code(r)); |
| return -EIO; |
| } |
| |
| return 0; |
| } /* uf_start_sniff() */ |
| |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * netrx_radiotap |
| * |
| * Reformat a UniFi SNIFFDATA signal into a radiotap packet. |
| * |
| * Arguments: |
| * priv OS private context pointer. |
| * ind Pointer to a MA_UNITDATA_INDICATION or |
| * DS_UNITDATA_INDICATION indication structure. |
| * |
| * Notes: |
| * Radiotap header values are all little-endian, UniFi signals will have |
| * been converted to host-endian. |
| * --------------------------------------------------------------------------- |
| */ |
| #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP) |
| static void |
| netrx_radiotap(unifi_priv_t *priv, |
| const CSR_MA_SNIFFDATA_INDICATION *ind, |
| struct sk_buff *skb_orig) |
| { |
| struct net_device *dev = priv->netdev; |
| struct sk_buff *skb = NULL; |
| unsigned char *ptr; |
| unsigned char *base; |
| int ind_data_len = skb_orig->len - 2 - ETH_HLEN; |
| struct unifi_rx_radiotap_header { |
| struct ieee80211_radiotap_header rt_hdr; |
| /* IEEE80211_RADIOTAP_TSFT */ |
| u64 rt_tsft; |
| /* IEEE80211_RADIOTAP_FLAGS */ |
| u8 rt_flags; |
| /* IEEE80211_RADIOTAP_RATE */ |
| u8 rt_rate; |
| /* IEEE80211_RADIOTAP_CHANNEL */ |
| u16 rt_chan; |
| u16 rt_chan_flags; |
| /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ |
| u8 rt_dbm_antsignal; |
| /* IEEE80211_RADIOTAP_DBM_ANTNOISE */ |
| u8 rt_dbm_antnoise; |
| /* IEEE80211_RADIOTAP_ANTENNA */ |
| u8 rt_antenna; |
| |
| /* pad to 4-byte boundary */ |
| u8 pad[3]; |
| } __attribute__((__packed__)); |
| |
| struct unifi_rx_radiotap_header *unifi_rt; |
| int signal, noise, snr; |
| |
| if (ind_data_len <= 0) { |
| unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n"); |
| return; |
| } |
| |
| /* |
| * Allocate a SKB for the received data packet, including radiotap |
| * header. |
| */ |
| skb = dev_alloc_skb(ind_data_len + sizeof(struct unifi_rx_radiotap_header) + 4); |
| if (! skb) { |
| unifi_error(priv, "alloc_skb failed.\n"); |
| priv->stats.rx_errors++; |
| return; |
| } |
| |
| base = skb->data; |
| |
| /* Reserve the radiotap header at the front of skb */ |
| unifi_rt = (struct unifi_rx_radiotap_header *) |
| skb_put(skb, sizeof(struct unifi_rx_radiotap_header)); |
| |
| /* Copy in the 802.11 frame */ |
| ptr = skb_put(skb, ind_data_len); |
| memcpy(ptr, skb_orig->data, ind_data_len); |
| |
| unifi_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; |
| unifi_rt->rt_hdr.it_pad = 0; /* always good to zero */ |
| unifi_rt->rt_hdr.it_len = sizeof(struct unifi_rx_radiotap_header); |
| |
| /* Big bitfield of all the fields we provide in radiotap */ |
| unifi_rt->rt_hdr.it_present = 0 |
| | (1 << IEEE80211_RADIOTAP_TSFT) |
| | (1 << IEEE80211_RADIOTAP_FLAGS) |
| | (1 << IEEE80211_RADIOTAP_RATE) |
| | (1 << IEEE80211_RADIOTAP_CHANNEL) |
| | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
| | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
| | (1 << IEEE80211_RADIOTAP_ANTENNA) |
| ; |
| |
| |
| /* No flags to set */ |
| unifi_rt->rt_tsft = (((u64)ind->Timestamp.x[7]) | (((u64)ind->Timestamp.x[6]) << 8) | |
| (((u64)ind->Timestamp.x[5]) << 16) | (((u64)ind->Timestamp.x[4]) << 24) | |
| (((u64)ind->Timestamp.x[3]) << 32) | (((u64)ind->Timestamp.x[2]) << 40) | |
| (((u64)ind->Timestamp.x[1]) << 48) | (((u64)ind->Timestamp.x[0]) << 56)); |
| |
| unifi_rt->rt_flags = 0; |
| |
| unifi_rt->rt_rate = ind->Rate; |
| |
| unifi_rt->rt_chan = cpu_to_le16(ieee80211chan2mhz(priv->wext_conf.channel)); |
| unifi_rt->rt_chan_flags = 0; |
| |
| /* Convert signal to dBm */ |
| signal = (s16)unifi2host_16(ind->Rssi); /* in dBm */ |
| snr = (s16)unifi2host_16(ind->Snr); /* in dB */ |
| noise = signal - snr; |
| |
| unifi_rt->rt_dbm_antsignal = signal; |
| unifi_rt->rt_dbm_antnoise = noise; |
| |
| unifi_rt->rt_antenna = ind->AntennaId; |
| |
| |
| skb->dev = dev; |
| skb->mac_header = skb->data; |
| skb->pkt_type = PACKET_OTHERHOST; |
| skb->protocol = __constant_htons(ETH_P_80211_RAW); |
| memset(skb->cb, 0, sizeof(skb->cb)); |
| |
| /* Pass up to Linux network stack */ |
| netif_rx_ni(skb); |
| |
| dev->last_rx = jiffies; |
| |
| /* Bump the rx stats */ |
| priv->stats.rx_packets++; |
| priv->stats.rx_bytes += ind_data_len; |
| |
| } /* netrx_radiotap() */ |
| #endif /* RADIOTAP */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * netrx_prism |
| * |
| * Reformat a UniFi SNIFFDATA signal into a Prism format sniff packet. |
| * |
| * Arguments: |
| * priv OS private context pointer. |
| * ind Pointer to a MA_UNITDATA_INDICATION or |
| * DS_UNITDATA_INDICATION indication structure. |
| * |
| * Notes: |
| * Radiotap header values are all little-endian, UniFi signals will have |
| * been converted to host-endian. |
| * --------------------------------------------------------------------------- |
| */ |
| #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM) |
| static void |
| netrx_prism(unifi_priv_t *priv, |
| const CSR_MA_SNIFFDATA_INDICATION *ind, |
| struct sk_buff *skb_orig) |
| { |
| struct net_device *dev = priv->netdev; |
| struct sk_buff *skb = NULL; |
| unsigned char *ptr; |
| unsigned char *base; |
| int ind_data_len = skb_orig->len - 2 - ETH_HLEN; |
| #define WLANCAP_MAGIC_COOKIE_V1 0x80211001 |
| struct avs_header_v1 { |
| uint32 version; |
| uint32 length; |
| uint64 mactime; |
| uint64 hosttime; |
| uint32 phytype; |
| uint32 channel; |
| uint32 datarate; |
| uint32 antenna; |
| uint32 priority; |
| uint32 ssi_type; |
| int32 ssi_signal; |
| int32 ssi_noise; |
| uint32 preamble; |
| uint32 encoding; |
| } *avs; |
| int signal, noise, snr; |
| |
| if (ind_data_len <= 0) { |
| unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n"); |
| return; |
| } |
| |
| /* |
| * Allocate a SKB for the received data packet, including radiotap |
| * header. |
| */ |
| skb = dev_alloc_skb(ind_data_len + sizeof(struct avs_header_v1) + 4); |
| if (! skb) { |
| unifi_error(priv, "alloc_skb failed.\n"); |
| priv->stats.rx_errors++; |
| return; |
| } |
| |
| base = skb->data; |
| |
| /* Reserve the radiotap header at the front of skb */ |
| avs = (struct avs_header_v1 *)skb_put(skb, sizeof(struct avs_header_v1)); |
| |
| /* Copy in the 802.11 frame */ |
| ptr = skb_put(skb, ind_data_len); |
| memcpy(ptr, skb_orig->data, ind_data_len); |
| |
| /* Convert signal to dBm */ |
| signal = 0x10000 - ((s16)unifi2host_16(ind->Rssi)); /* in dBm */ |
| snr = (s16)unifi2host_16(ind->Snr); /* in dB */ |
| noise = signal - snr; |
| |
| avs->version = htonl(WLANCAP_MAGIC_COOKIE_V1); |
| avs->length = htonl(sizeof(struct avs_header_v1)); |
| avs->mactime = __cpu_to_be64(ind->Timestamp); |
| avs->hosttime = __cpu_to_be64(jiffies); |
| avs->phytype = htonl(9); /* dss_ofdm_dot11_g */ |
| avs->channel = htonl(priv->wext_conf.channel); |
| avs->datarate = htonl(ind->Rate * 5); |
| avs->antenna = htonl(ind->Antenna); |
| avs->priority = htonl(0); /* unknown */ |
| avs->ssi_type = htonl(2); /* dBm */ |
| avs->ssi_signal = htonl(signal); |
| avs->ssi_noise = htonl(noise); |
| avs->preamble = htonl(0); /* unknown */ |
| avs->encoding = htonl(0); /* unknown */ |
| |
| |
| skb->dev = dev; |
| skb->mac.raw = skb->data; |
| skb->pkt_type = PACKET_OTHERHOST; |
| skb->protocol = __constant_htons(ETH_P_80211_RAW); |
| memset(skb->cb, 0, sizeof(skb->cb)); |
| |
| /* Pass up to Linux network stack */ |
| netif_rx_ni(skb); |
| |
| dev->last_rx = jiffies; |
| |
| /* Bump the rx stats */ |
| priv->stats.rx_packets++; |
| priv->stats.rx_bytes += ind_data_len; |
| |
| } /* netrx_prism() */ |
| #endif /* PRISM */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * ma_sniffdata_ind |
| * |
| * Reformat a UniFi SNIFFDATA signal into a network |
| * |
| * Arguments: |
| * ospriv OS private context pointer. |
| * ind Pointer to a MA_UNITDATA_INDICATION or |
| * DS_UNITDATA_INDICATION indication structure. |
| * bulkdata Pointer to a bulk data structure, describing |
| * the data received. |
| * |
| * Notes: |
| * Radiotap header values are all little-endian, UniFi signals will have |
| * been converted to host-endian. |
| * --------------------------------------------------------------------------- |
| */ |
| void |
| ma_sniffdata_ind(void *ospriv, |
| const CSR_MA_SNIFFDATA_INDICATION *ind, |
| const bulk_data_param_t *bulkdata) |
| { |
| unifi_priv_t *priv = ospriv; |
| struct net_device *dev = priv->netdev; |
| struct sk_buff *skb = (struct sk_buff*)bulkdata->d[0].os_net_buf_ptr; |
| |
| if (bulkdata->d[0].data_length == 0) { |
| unifi_warning(priv, "rx: MA-SNIFFDATA indication with zero bulk data\n"); |
| return; |
| } |
| |
| skb->len = bulkdata->d[0].data_length; |
| |
| /* We only process data packets if the interface is open */ |
| if (unlikely(!netif_running(dev))) { |
| priv->stats.rx_dropped++; |
| priv->wext_conf.wireless_stats.discard.misc++; |
| dev_kfree_skb(skb); |
| return; |
| } |
| |
| if (ind->ReceptionStatus) { |
| priv->stats.rx_dropped++; |
| priv->wext_conf.wireless_stats.discard.misc++; |
| printk(KERN_INFO "unifi: Dropping corrupt sniff packet\n"); |
| dev_kfree_skb(skb); |
| return; |
| } |
| |
| #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM) |
| netrx_prism(priv, ind, skb); |
| #endif /* PRISM */ |
| |
| #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP) |
| netrx_radiotap(priv, ind, skb); |
| #endif /* RADIOTAP */ |
| |
| dev_kfree_skb(skb); |
| |
| } /* ma_sniffdata_ind() */ |
| |
| |
| #endif /* UNIFI_SNIFF_ARPHRD */ |
| |