162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Original code based Host AP (software wireless LAN access point) driver 462306a36Sopenharmony_ci * for Intersil Prism2/2.5/3 - hostap.o module, common routines 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen 762306a36Sopenharmony_ci * <j@w1.fi> 862306a36Sopenharmony_ci * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> 962306a36Sopenharmony_ci * Copyright (c) 2004-2005, Intel Corporation 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/if_arp.h> 1562306a36Sopenharmony_ci#include <linux/in6.h> 1662306a36Sopenharmony_ci#include <linux/gfp.h> 1762306a36Sopenharmony_ci#include <linux/in.h> 1862306a36Sopenharmony_ci#include <linux/ip.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/netdevice.h> 2262306a36Sopenharmony_ci#include <linux/proc_fs.h> 2362306a36Sopenharmony_ci#include <linux/skbuff.h> 2462306a36Sopenharmony_ci#include <linux/tcp.h> 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/wireless.h> 2762306a36Sopenharmony_ci#include <linux/etherdevice.h> 2862306a36Sopenharmony_ci#include <linux/uaccess.h> 2962306a36Sopenharmony_ci#include <linux/ctype.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <net/lib80211.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "libipw.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void libipw_monitor_rx(struct libipw_device *ieee, 3662306a36Sopenharmony_ci struct sk_buff *skb, 3762306a36Sopenharmony_ci struct libipw_rx_stats *rx_stats) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 4062306a36Sopenharmony_ci u16 fc = le16_to_cpu(hdr->frame_control); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci skb->dev = ieee->dev; 4362306a36Sopenharmony_ci skb_reset_mac_header(skb); 4462306a36Sopenharmony_ci skb_pull(skb, libipw_get_hdrlen(fc)); 4562306a36Sopenharmony_ci skb->pkt_type = PACKET_OTHERHOST; 4662306a36Sopenharmony_ci skb->protocol = htons(ETH_P_80211_RAW); 4762306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(skb->cb)); 4862306a36Sopenharmony_ci netif_rx(skb); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */ 5262306a36Sopenharmony_cistatic struct libipw_frag_entry *libipw_frag_cache_find(struct 5362306a36Sopenharmony_ci libipw_device 5462306a36Sopenharmony_ci *ieee, 5562306a36Sopenharmony_ci unsigned int seq, 5662306a36Sopenharmony_ci unsigned int frag, 5762306a36Sopenharmony_ci u8 * src, 5862306a36Sopenharmony_ci u8 * dst) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct libipw_frag_entry *entry; 6162306a36Sopenharmony_ci int i; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { 6462306a36Sopenharmony_ci entry = &ieee->frag_cache[i]; 6562306a36Sopenharmony_ci if (entry->skb != NULL && 6662306a36Sopenharmony_ci time_after(jiffies, entry->first_frag_time + 2 * HZ)) { 6762306a36Sopenharmony_ci LIBIPW_DEBUG_FRAG("expiring fragment cache entry " 6862306a36Sopenharmony_ci "seq=%u last_frag=%u\n", 6962306a36Sopenharmony_ci entry->seq, entry->last_frag); 7062306a36Sopenharmony_ci dev_kfree_skb_any(entry->skb); 7162306a36Sopenharmony_ci entry->skb = NULL; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (entry->skb != NULL && entry->seq == seq && 7562306a36Sopenharmony_ci (entry->last_frag + 1 == frag || frag == -1) && 7662306a36Sopenharmony_ci ether_addr_equal(entry->src_addr, src) && 7762306a36Sopenharmony_ci ether_addr_equal(entry->dst_addr, dst)) 7862306a36Sopenharmony_ci return entry; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */ 8562306a36Sopenharmony_cistatic struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, 8662306a36Sopenharmony_ci struct libipw_hdr_4addr *hdr) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct sk_buff *skb = NULL; 8962306a36Sopenharmony_ci u16 sc; 9062306a36Sopenharmony_ci unsigned int frag, seq; 9162306a36Sopenharmony_ci struct libipw_frag_entry *entry; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci sc = le16_to_cpu(hdr->seq_ctl); 9462306a36Sopenharmony_ci frag = WLAN_GET_SEQ_FRAG(sc); 9562306a36Sopenharmony_ci seq = WLAN_GET_SEQ_SEQ(sc); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (frag == 0) { 9862306a36Sopenharmony_ci /* Reserve enough space to fit maximum frame length */ 9962306a36Sopenharmony_ci skb = dev_alloc_skb(ieee->dev->mtu + 10062306a36Sopenharmony_ci sizeof(struct libipw_hdr_4addr) + 10162306a36Sopenharmony_ci 8 /* LLC */ + 10262306a36Sopenharmony_ci 2 /* alignment */ + 10362306a36Sopenharmony_ci 8 /* WEP */ + ETH_ALEN /* WDS */ ); 10462306a36Sopenharmony_ci if (skb == NULL) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci entry = &ieee->frag_cache[ieee->frag_next_idx]; 10862306a36Sopenharmony_ci ieee->frag_next_idx++; 10962306a36Sopenharmony_ci if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) 11062306a36Sopenharmony_ci ieee->frag_next_idx = 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (entry->skb != NULL) 11362306a36Sopenharmony_ci dev_kfree_skb_any(entry->skb); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci entry->first_frag_time = jiffies; 11662306a36Sopenharmony_ci entry->seq = seq; 11762306a36Sopenharmony_ci entry->last_frag = frag; 11862306a36Sopenharmony_ci entry->skb = skb; 11962306a36Sopenharmony_ci memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); 12062306a36Sopenharmony_ci memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); 12162306a36Sopenharmony_ci } else { 12262306a36Sopenharmony_ci /* received a fragment of a frame for which the head fragment 12362306a36Sopenharmony_ci * should have already been received */ 12462306a36Sopenharmony_ci entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2, 12562306a36Sopenharmony_ci hdr->addr1); 12662306a36Sopenharmony_ci if (entry != NULL) { 12762306a36Sopenharmony_ci entry->last_frag = frag; 12862306a36Sopenharmony_ci skb = entry->skb; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return skb; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ) */ 13662306a36Sopenharmony_cistatic int libipw_frag_cache_invalidate(struct libipw_device *ieee, 13762306a36Sopenharmony_ci struct libipw_hdr_4addr *hdr) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u16 sc; 14062306a36Sopenharmony_ci unsigned int seq; 14162306a36Sopenharmony_ci struct libipw_frag_entry *entry; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci sc = le16_to_cpu(hdr->seq_ctl); 14462306a36Sopenharmony_ci seq = WLAN_GET_SEQ_SEQ(sc); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2, 14762306a36Sopenharmony_ci hdr->addr1); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (entry == NULL) { 15062306a36Sopenharmony_ci LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " 15162306a36Sopenharmony_ci "entry (seq=%u)\n", seq); 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci entry->skb = NULL; 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#ifdef NOT_YET 16062306a36Sopenharmony_ci/* libipw_rx_frame_mgtmt 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Responsible for handling management control frames 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * Called by libipw_rx */ 16562306a36Sopenharmony_cistatic int 16662306a36Sopenharmony_cilibipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, 16762306a36Sopenharmony_ci struct libipw_rx_stats *rx_stats, u16 type, 16862306a36Sopenharmony_ci u16 stype) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MASTER) { 17162306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Master mode not yet supported.\n", 17262306a36Sopenharmony_ci ieee->dev->name); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) 17662306a36Sopenharmony_ci skb->data);*/ 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { 18062306a36Sopenharmony_ci if (stype == WLAN_FC_STYPE_BEACON && 18162306a36Sopenharmony_ci ieee->iw_mode == IW_MODE_MASTER) { 18262306a36Sopenharmony_ci struct sk_buff *skb2; 18362306a36Sopenharmony_ci /* Process beacon frames also in kernel driver to 18462306a36Sopenharmony_ci * update STA(AP) table statistics */ 18562306a36Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 18662306a36Sopenharmony_ci if (skb2) 18762306a36Sopenharmony_ci hostap_rx(skb2->dev, skb2, rx_stats); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* send management frames to the user space daemon for 19162306a36Sopenharmony_ci * processing */ 19262306a36Sopenharmony_ci ieee->apdevstats.rx_packets++; 19362306a36Sopenharmony_ci ieee->apdevstats.rx_bytes += skb->len; 19462306a36Sopenharmony_ci prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MASTER) { 19962306a36Sopenharmony_ci if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { 20062306a36Sopenharmony_ci printk(KERN_DEBUG "%s: unknown management frame " 20162306a36Sopenharmony_ci "(type=0x%02x, stype=0x%02x) dropped\n", 20262306a36Sopenharmony_ci skb->dev->name, type, stype); 20362306a36Sopenharmony_ci return -1; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci hostap_rx(skb->dev, skb, rx_stats); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " 21162306a36Sopenharmony_ci "received in non-Host AP mode\n", skb->dev->name); 21262306a36Sopenharmony_ci return -1; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ 21762306a36Sopenharmony_ci/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ 21862306a36Sopenharmony_cistatic unsigned char libipw_rfc1042_header[] = 21962306a36Sopenharmony_ci { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ 22262306a36Sopenharmony_cistatic unsigned char libipw_bridge_tunnel_header[] = 22362306a36Sopenharmony_ci { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; 22462306a36Sopenharmony_ci/* No encapsulation header if EtherType < 0x600 (=length) */ 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* Called by libipw_rx_frame_decrypt */ 22762306a36Sopenharmony_cistatic int libipw_is_eapol_frame(struct libipw_device *ieee, 22862306a36Sopenharmony_ci struct sk_buff *skb) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct net_device *dev = ieee->dev; 23162306a36Sopenharmony_ci u16 fc, ethertype; 23262306a36Sopenharmony_ci struct libipw_hdr_3addr *hdr; 23362306a36Sopenharmony_ci u8 *pos; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (skb->len < 24) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci hdr = (struct libipw_hdr_3addr *)skb->data; 23962306a36Sopenharmony_ci fc = le16_to_cpu(hdr->frame_ctl); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* check that the frame is unicast frame to us */ 24262306a36Sopenharmony_ci if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == 24362306a36Sopenharmony_ci IEEE80211_FCTL_TODS && 24462306a36Sopenharmony_ci ether_addr_equal(hdr->addr1, dev->dev_addr) && 24562306a36Sopenharmony_ci ether_addr_equal(hdr->addr3, dev->dev_addr)) { 24662306a36Sopenharmony_ci /* ToDS frame with own addr BSSID and DA */ 24762306a36Sopenharmony_ci } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == 24862306a36Sopenharmony_ci IEEE80211_FCTL_FROMDS && 24962306a36Sopenharmony_ci ether_addr_equal(hdr->addr1, dev->dev_addr)) { 25062306a36Sopenharmony_ci /* FromDS frame with own addr as DA */ 25162306a36Sopenharmony_ci } else 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (skb->len < 24 + 8) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* check for port access entity Ethernet type */ 25862306a36Sopenharmony_ci pos = skb->data + 24; 25962306a36Sopenharmony_ci ethertype = (pos[6] << 8) | pos[7]; 26062306a36Sopenharmony_ci if (ethertype == ETH_P_PAE) 26162306a36Sopenharmony_ci return 1; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ), by libipw_rx */ 26762306a36Sopenharmony_cistatic int 26862306a36Sopenharmony_cilibipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, 26962306a36Sopenharmony_ci struct lib80211_crypt_data *crypt) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct libipw_hdr_3addr *hdr; 27262306a36Sopenharmony_ci int res, hdrlen; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci hdr = (struct libipw_hdr_3addr *)skb->data; 27862306a36Sopenharmony_ci hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci atomic_inc(&crypt->refcnt); 28162306a36Sopenharmony_ci res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); 28262306a36Sopenharmony_ci atomic_dec(&crypt->refcnt); 28362306a36Sopenharmony_ci if (res < 0) { 28462306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", 28562306a36Sopenharmony_ci hdr->addr2, res); 28662306a36Sopenharmony_ci if (res == -2) 28762306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("Decryption failed ICV " 28862306a36Sopenharmony_ci "mismatch (key %d)\n", 28962306a36Sopenharmony_ci skb->data[hdrlen + 3] >> 6); 29062306a36Sopenharmony_ci ieee->ieee_stats.rx_discards_undecryptable++; 29162306a36Sopenharmony_ci return -1; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return res; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Called only as a tasklet (software IRQ), by libipw_rx */ 29862306a36Sopenharmony_cistatic int 29962306a36Sopenharmony_cilibipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, 30062306a36Sopenharmony_ci struct sk_buff *skb, int keyidx, 30162306a36Sopenharmony_ci struct lib80211_crypt_data *crypt) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct libipw_hdr_3addr *hdr; 30462306a36Sopenharmony_ci int res, hdrlen; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci hdr = (struct libipw_hdr_3addr *)skb->data; 31062306a36Sopenharmony_ci hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci atomic_inc(&crypt->refcnt); 31362306a36Sopenharmony_ci res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); 31462306a36Sopenharmony_ci atomic_dec(&crypt->refcnt); 31562306a36Sopenharmony_ci if (res < 0) { 31662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" 31762306a36Sopenharmony_ci " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2, 31862306a36Sopenharmony_ci keyidx); 31962306a36Sopenharmony_ci return -1; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* All received frames are sent to this function. @skb contains the frame in 32662306a36Sopenharmony_ci * IEEE 802.11 format, i.e., in the format it was sent over air. 32762306a36Sopenharmony_ci * This function is called only as a tasklet (software IRQ). */ 32862306a36Sopenharmony_ciint libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, 32962306a36Sopenharmony_ci struct libipw_rx_stats *rx_stats) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct net_device *dev = ieee->dev; 33262306a36Sopenharmony_ci struct libipw_hdr_4addr *hdr; 33362306a36Sopenharmony_ci size_t hdrlen; 33462306a36Sopenharmony_ci u16 fc, type, stype, sc; 33562306a36Sopenharmony_ci unsigned int frag; 33662306a36Sopenharmony_ci u8 *payload; 33762306a36Sopenharmony_ci u16 ethertype; 33862306a36Sopenharmony_ci#ifdef NOT_YET 33962306a36Sopenharmony_ci struct net_device *wds = NULL; 34062306a36Sopenharmony_ci struct sk_buff *skb2 = NULL; 34162306a36Sopenharmony_ci struct net_device *wds = NULL; 34262306a36Sopenharmony_ci int frame_authorized = 0; 34362306a36Sopenharmony_ci int from_assoc_ap = 0; 34462306a36Sopenharmony_ci void *sta = NULL; 34562306a36Sopenharmony_ci#endif 34662306a36Sopenharmony_ci u8 dst[ETH_ALEN]; 34762306a36Sopenharmony_ci u8 src[ETH_ALEN]; 34862306a36Sopenharmony_ci struct lib80211_crypt_data *crypt = NULL; 34962306a36Sopenharmony_ci int keyidx = 0; 35062306a36Sopenharmony_ci int can_be_decrypted = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci hdr = (struct libipw_hdr_4addr *)skb->data; 35362306a36Sopenharmony_ci if (skb->len < 10) { 35462306a36Sopenharmony_ci printk(KERN_INFO "%s: SKB length < 10\n", dev->name); 35562306a36Sopenharmony_ci goto rx_dropped; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci fc = le16_to_cpu(hdr->frame_ctl); 35962306a36Sopenharmony_ci type = WLAN_FC_GET_TYPE(fc); 36062306a36Sopenharmony_ci stype = WLAN_FC_GET_STYPE(fc); 36162306a36Sopenharmony_ci sc = le16_to_cpu(hdr->seq_ctl); 36262306a36Sopenharmony_ci frag = WLAN_GET_SEQ_FRAG(sc); 36362306a36Sopenharmony_ci hdrlen = libipw_get_hdrlen(fc); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (skb->len < hdrlen) { 36662306a36Sopenharmony_ci printk(KERN_INFO "%s: invalid SKB length %d\n", 36762306a36Sopenharmony_ci dev->name, skb->len); 36862306a36Sopenharmony_ci goto rx_dropped; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Put this code here so that we avoid duplicating it in all 37262306a36Sopenharmony_ci * Rx paths. - Jean II */ 37362306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT 37462306a36Sopenharmony_ci#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ 37562306a36Sopenharmony_ci /* If spy monitoring on */ 37662306a36Sopenharmony_ci if (ieee->spy_data.spy_number > 0) { 37762306a36Sopenharmony_ci struct iw_quality wstats; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci wstats.updated = 0; 38062306a36Sopenharmony_ci if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { 38162306a36Sopenharmony_ci wstats.level = rx_stats->signal; 38262306a36Sopenharmony_ci wstats.updated |= IW_QUAL_LEVEL_UPDATED; 38362306a36Sopenharmony_ci } else 38462306a36Sopenharmony_ci wstats.updated |= IW_QUAL_LEVEL_INVALID; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { 38762306a36Sopenharmony_ci wstats.noise = rx_stats->noise; 38862306a36Sopenharmony_ci wstats.updated |= IW_QUAL_NOISE_UPDATED; 38962306a36Sopenharmony_ci } else 39062306a36Sopenharmony_ci wstats.updated |= IW_QUAL_NOISE_INVALID; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { 39362306a36Sopenharmony_ci wstats.qual = rx_stats->signal; 39462306a36Sopenharmony_ci wstats.updated |= IW_QUAL_QUAL_UPDATED; 39562306a36Sopenharmony_ci } else 39662306a36Sopenharmony_ci wstats.updated |= IW_QUAL_QUAL_INVALID; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Update spy records */ 39962306a36Sopenharmony_ci wireless_spy_update(ieee->dev, hdr->addr2, &wstats); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci#endif /* IW_WIRELESS_SPY */ 40262306a36Sopenharmony_ci#endif /* CONFIG_WIRELESS_EXT */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci#ifdef NOT_YET 40562306a36Sopenharmony_ci hostap_update_rx_stats(local->ap, hdr, rx_stats); 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MONITOR) { 40962306a36Sopenharmony_ci dev->stats.rx_packets++; 41062306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 41162306a36Sopenharmony_ci libipw_monitor_rx(ieee, skb, rx_stats); 41262306a36Sopenharmony_ci return 1; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) || 41662306a36Sopenharmony_ci is_broadcast_ether_addr(hdr->addr2)) ? 41762306a36Sopenharmony_ci ieee->host_mc_decrypt : ieee->host_decrypt; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (can_be_decrypted) { 42062306a36Sopenharmony_ci if (skb->len >= hdrlen + 3) { 42162306a36Sopenharmony_ci /* Top two-bits of byte 3 are the key index */ 42262306a36Sopenharmony_ci keyidx = skb->data[hdrlen + 3] >> 6; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx 42662306a36Sopenharmony_ci * is only allowed 2-bits of storage, no value of keyidx can 42762306a36Sopenharmony_ci * be provided via above code that would result in keyidx 42862306a36Sopenharmony_ci * being out of range */ 42962306a36Sopenharmony_ci crypt = ieee->crypt_info.crypt[keyidx]; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci#ifdef NOT_YET 43262306a36Sopenharmony_ci sta = NULL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Use station specific key to override default keys if the 43562306a36Sopenharmony_ci * receiver address is a unicast address ("individual RA"). If 43662306a36Sopenharmony_ci * bcrx_sta_key parameter is set, station specific key is used 43762306a36Sopenharmony_ci * even with broad/multicast targets (this is against IEEE 43862306a36Sopenharmony_ci * 802.11, but makes it easier to use different keys with 43962306a36Sopenharmony_ci * stations that do not support WEP key mapping). */ 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key) 44262306a36Sopenharmony_ci (void)hostap_handle_sta_crypto(local, hdr, &crypt, 44362306a36Sopenharmony_ci &sta); 44462306a36Sopenharmony_ci#endif 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* allow NULL decrypt to indicate an station specific override 44762306a36Sopenharmony_ci * for default encryption */ 44862306a36Sopenharmony_ci if (crypt && (crypt->ops == NULL || 44962306a36Sopenharmony_ci crypt->ops->decrypt_mpdu == NULL)) 45062306a36Sopenharmony_ci crypt = NULL; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { 45362306a36Sopenharmony_ci /* This seems to be triggered by some (multicast?) 45462306a36Sopenharmony_ci * frames from other than current BSS, so just drop the 45562306a36Sopenharmony_ci * frames silently instead of filling system log with 45662306a36Sopenharmony_ci * these reports. */ 45762306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("Decryption failed (not set)" 45862306a36Sopenharmony_ci " (SA=%pM)\n", hdr->addr2); 45962306a36Sopenharmony_ci ieee->ieee_stats.rx_discards_undecryptable++; 46062306a36Sopenharmony_ci goto rx_dropped; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci#ifdef NOT_YET 46462306a36Sopenharmony_ci if (type != WLAN_FC_TYPE_DATA) { 46562306a36Sopenharmony_ci if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && 46662306a36Sopenharmony_ci fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt && 46762306a36Sopenharmony_ci (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) { 46862306a36Sopenharmony_ci printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " 46962306a36Sopenharmony_ci "from %pM\n", dev->name, hdr->addr2); 47062306a36Sopenharmony_ci /* TODO: could inform hostapd about this so that it 47162306a36Sopenharmony_ci * could send auth failure report */ 47262306a36Sopenharmony_ci goto rx_dropped; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) 47662306a36Sopenharmony_ci goto rx_dropped; 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci goto rx_exit; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci#endif 48162306a36Sopenharmony_ci /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */ 48262306a36Sopenharmony_ci if (sc == ieee->prev_seq_ctl) 48362306a36Sopenharmony_ci goto rx_dropped; 48462306a36Sopenharmony_ci else 48562306a36Sopenharmony_ci ieee->prev_seq_ctl = sc; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Data frame - extract src/dst addresses */ 48862306a36Sopenharmony_ci if (skb->len < LIBIPW_3ADDR_LEN) 48962306a36Sopenharmony_ci goto rx_dropped; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { 49262306a36Sopenharmony_ci case IEEE80211_FCTL_FROMDS: 49362306a36Sopenharmony_ci memcpy(dst, hdr->addr1, ETH_ALEN); 49462306a36Sopenharmony_ci memcpy(src, hdr->addr3, ETH_ALEN); 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case IEEE80211_FCTL_TODS: 49762306a36Sopenharmony_ci memcpy(dst, hdr->addr3, ETH_ALEN); 49862306a36Sopenharmony_ci memcpy(src, hdr->addr2, ETH_ALEN); 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: 50162306a36Sopenharmony_ci if (skb->len < LIBIPW_4ADDR_LEN) 50262306a36Sopenharmony_ci goto rx_dropped; 50362306a36Sopenharmony_ci memcpy(dst, hdr->addr3, ETH_ALEN); 50462306a36Sopenharmony_ci memcpy(src, hdr->addr4, ETH_ALEN); 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci default: 50762306a36Sopenharmony_ci memcpy(dst, hdr->addr1, ETH_ALEN); 50862306a36Sopenharmony_ci memcpy(src, hdr->addr2, ETH_ALEN); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#ifdef NOT_YET 51362306a36Sopenharmony_ci if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) 51462306a36Sopenharmony_ci goto rx_dropped; 51562306a36Sopenharmony_ci if (wds) { 51662306a36Sopenharmony_ci skb->dev = dev = wds; 51762306a36Sopenharmony_ci stats = hostap_get_stats(dev); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MASTER && !wds && 52162306a36Sopenharmony_ci (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == 52262306a36Sopenharmony_ci IEEE80211_FCTL_FROMDS && ieee->stadev && 52362306a36Sopenharmony_ci ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) { 52462306a36Sopenharmony_ci /* Frame from BSSID of the AP for which we are a client */ 52562306a36Sopenharmony_ci skb->dev = dev = ieee->stadev; 52662306a36Sopenharmony_ci stats = hostap_get_stats(dev); 52762306a36Sopenharmony_ci from_assoc_ap = 1; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci#endif 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci#ifdef NOT_YET 53262306a36Sopenharmony_ci if ((ieee->iw_mode == IW_MODE_MASTER || 53362306a36Sopenharmony_ci ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) { 53462306a36Sopenharmony_ci switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, 53562306a36Sopenharmony_ci wds != NULL)) { 53662306a36Sopenharmony_ci case AP_RX_CONTINUE_NOT_AUTHORIZED: 53762306a36Sopenharmony_ci frame_authorized = 0; 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci case AP_RX_CONTINUE: 54062306a36Sopenharmony_ci frame_authorized = 1; 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci case AP_RX_DROP: 54362306a36Sopenharmony_ci goto rx_dropped; 54462306a36Sopenharmony_ci case AP_RX_EXIT: 54562306a36Sopenharmony_ci goto rx_exit; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci#endif 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Nullfunc frames may have PS-bit set, so they must be passed to 55162306a36Sopenharmony_ci * hostap_handle_sta_rx() before being dropped here. */ 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci stype &= ~IEEE80211_STYPE_QOS_DATA; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (stype != IEEE80211_STYPE_DATA && 55662306a36Sopenharmony_ci stype != IEEE80211_STYPE_DATA_CFACK && 55762306a36Sopenharmony_ci stype != IEEE80211_STYPE_DATA_CFPOLL && 55862306a36Sopenharmony_ci stype != IEEE80211_STYPE_DATA_CFACKPOLL) { 55962306a36Sopenharmony_ci if (stype != IEEE80211_STYPE_NULLFUNC) 56062306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("RX: dropped data frame " 56162306a36Sopenharmony_ci "with no data (type=0x%02x, " 56262306a36Sopenharmony_ci "subtype=0x%02x, len=%d)\n", 56362306a36Sopenharmony_ci type, stype, skb->len); 56462306a36Sopenharmony_ci goto rx_dropped; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && 57062306a36Sopenharmony_ci (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) 57162306a36Sopenharmony_ci goto rx_dropped; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci hdr = (struct libipw_hdr_4addr *)skb->data; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* skb: hdr + (possibly fragmented) plaintext payload */ 57662306a36Sopenharmony_ci // PR: FIXME: hostap has additional conditions in the "if" below: 57762306a36Sopenharmony_ci // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && 57862306a36Sopenharmony_ci if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { 57962306a36Sopenharmony_ci int flen; 58062306a36Sopenharmony_ci struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); 58162306a36Sopenharmony_ci LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (!frag_skb) { 58462306a36Sopenharmony_ci LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, 58562306a36Sopenharmony_ci "Rx cannot get skb from fragment " 58662306a36Sopenharmony_ci "cache (morefrag=%d seq=%u frag=%u)\n", 58762306a36Sopenharmony_ci (fc & IEEE80211_FCTL_MOREFRAGS) != 0, 58862306a36Sopenharmony_ci WLAN_GET_SEQ_SEQ(sc), frag); 58962306a36Sopenharmony_ci goto rx_dropped; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci flen = skb->len; 59362306a36Sopenharmony_ci if (frag != 0) 59462306a36Sopenharmony_ci flen -= hdrlen; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (frag_skb->tail + flen > frag_skb->end) { 59762306a36Sopenharmony_ci printk(KERN_WARNING "%s: host decrypted and " 59862306a36Sopenharmony_ci "reassembled frame did not fit skb\n", 59962306a36Sopenharmony_ci dev->name); 60062306a36Sopenharmony_ci libipw_frag_cache_invalidate(ieee, hdr); 60162306a36Sopenharmony_ci goto rx_dropped; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (frag == 0) { 60562306a36Sopenharmony_ci /* copy first fragment (including full headers) into 60662306a36Sopenharmony_ci * beginning of the fragment cache skb */ 60762306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen); 60862306a36Sopenharmony_ci } else { 60962306a36Sopenharmony_ci /* append frame payload to the end of the fragment 61062306a36Sopenharmony_ci * cache skb */ 61162306a36Sopenharmony_ci skb_copy_from_linear_data_offset(skb, hdrlen, 61262306a36Sopenharmony_ci skb_put(frag_skb, flen), flen); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 61562306a36Sopenharmony_ci skb = NULL; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (fc & IEEE80211_FCTL_MOREFRAGS) { 61862306a36Sopenharmony_ci /* more fragments expected - leave the skb in fragment 61962306a36Sopenharmony_ci * cache for now; it will be delivered to upper layers 62062306a36Sopenharmony_ci * after all fragments have been received */ 62162306a36Sopenharmony_ci goto rx_exit; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* this was the last fragment and the frame will be 62562306a36Sopenharmony_ci * delivered, so remove skb from fragment cache */ 62662306a36Sopenharmony_ci skb = frag_skb; 62762306a36Sopenharmony_ci hdr = (struct libipw_hdr_4addr *)skb->data; 62862306a36Sopenharmony_ci libipw_frag_cache_invalidate(ieee, hdr); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* skb: hdr + (possible reassembled) full MSDU payload; possibly still 63262306a36Sopenharmony_ci * encrypted/authenticated */ 63362306a36Sopenharmony_ci if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && 63462306a36Sopenharmony_ci libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) 63562306a36Sopenharmony_ci goto rx_dropped; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci hdr = (struct libipw_hdr_4addr *)skb->data; 63862306a36Sopenharmony_ci if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { 63962306a36Sopenharmony_ci if ( /*ieee->ieee802_1x && */ 64062306a36Sopenharmony_ci libipw_is_eapol_frame(ieee, skb)) { 64162306a36Sopenharmony_ci /* pass unencrypted EAPOL frames even if encryption is 64262306a36Sopenharmony_ci * configured */ 64362306a36Sopenharmony_ci } else { 64462306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("encryption configured, but RX " 64562306a36Sopenharmony_ci "frame not encrypted (SA=%pM)\n", 64662306a36Sopenharmony_ci hdr->addr2); 64762306a36Sopenharmony_ci goto rx_dropped; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && 65262306a36Sopenharmony_ci !libipw_is_eapol_frame(ieee, skb)) { 65362306a36Sopenharmony_ci LIBIPW_DEBUG_DROP("dropped unencrypted RX data " 65462306a36Sopenharmony_ci "frame from %pM (drop_unencrypted=1)\n", 65562306a36Sopenharmony_ci hdr->addr2); 65662306a36Sopenharmony_ci goto rx_dropped; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* If the frame was decrypted in hardware, we may need to strip off 66062306a36Sopenharmony_ci * any security data (IV, ICV, etc) that was left behind */ 66162306a36Sopenharmony_ci if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) && 66262306a36Sopenharmony_ci ieee->host_strip_iv_icv) { 66362306a36Sopenharmony_ci int trimlen = 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* Top two-bits of byte 3 are the key index */ 66662306a36Sopenharmony_ci if (skb->len >= hdrlen + 3) 66762306a36Sopenharmony_ci keyidx = skb->data[hdrlen + 3] >> 6; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* To strip off any security data which appears before the 67062306a36Sopenharmony_ci * payload, we simply increase hdrlen (as the header gets 67162306a36Sopenharmony_ci * chopped off immediately below). For the security data which 67262306a36Sopenharmony_ci * appears after the payload, we use skb_trim. */ 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci switch (ieee->sec.encode_alg[keyidx]) { 67562306a36Sopenharmony_ci case SEC_ALG_WEP: 67662306a36Sopenharmony_ci /* 4 byte IV */ 67762306a36Sopenharmony_ci hdrlen += 4; 67862306a36Sopenharmony_ci /* 4 byte ICV */ 67962306a36Sopenharmony_ci trimlen = 4; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case SEC_ALG_TKIP: 68262306a36Sopenharmony_ci /* 4 byte IV, 4 byte ExtIV */ 68362306a36Sopenharmony_ci hdrlen += 8; 68462306a36Sopenharmony_ci /* 8 byte MIC, 4 byte ICV */ 68562306a36Sopenharmony_ci trimlen = 12; 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci case SEC_ALG_CCMP: 68862306a36Sopenharmony_ci /* 8 byte CCMP header */ 68962306a36Sopenharmony_ci hdrlen += 8; 69062306a36Sopenharmony_ci /* 8 byte MIC */ 69162306a36Sopenharmony_ci trimlen = 8; 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (skb->len < trimlen) 69662306a36Sopenharmony_ci goto rx_dropped; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci __skb_trim(skb, skb->len - trimlen); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (skb->len < hdrlen) 70162306a36Sopenharmony_ci goto rx_dropped; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* skb: hdr + (possible reassembled) full plaintext payload */ 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci payload = skb->data + hdrlen; 70762306a36Sopenharmony_ci ethertype = (payload[6] << 8) | payload[7]; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci#ifdef NOT_YET 71062306a36Sopenharmony_ci /* If IEEE 802.1X is used, check whether the port is authorized to send 71162306a36Sopenharmony_ci * the received frame. */ 71262306a36Sopenharmony_ci if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { 71362306a36Sopenharmony_ci if (ethertype == ETH_P_PAE) { 71462306a36Sopenharmony_ci printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", 71562306a36Sopenharmony_ci dev->name); 71662306a36Sopenharmony_ci if (ieee->hostapd && ieee->apdev) { 71762306a36Sopenharmony_ci /* Send IEEE 802.1X frames to the user 71862306a36Sopenharmony_ci * space daemon for processing */ 71962306a36Sopenharmony_ci prism2_rx_80211(ieee->apdev, skb, rx_stats, 72062306a36Sopenharmony_ci PRISM2_RX_MGMT); 72162306a36Sopenharmony_ci ieee->apdevstats.rx_packets++; 72262306a36Sopenharmony_ci ieee->apdevstats.rx_bytes += skb->len; 72362306a36Sopenharmony_ci goto rx_exit; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci } else if (!frame_authorized) { 72662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: dropped frame from " 72762306a36Sopenharmony_ci "unauthorized port (IEEE 802.1X): " 72862306a36Sopenharmony_ci "ethertype=0x%04x\n", dev->name, ethertype); 72962306a36Sopenharmony_ci goto rx_dropped; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci#endif 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* convert hdr + possible LLC headers into Ethernet header */ 73562306a36Sopenharmony_ci if (skb->len - hdrlen >= 8 && 73662306a36Sopenharmony_ci ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 && 73762306a36Sopenharmony_ci ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || 73862306a36Sopenharmony_ci memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { 73962306a36Sopenharmony_ci /* remove RFC1042 or Bridge-Tunnel encapsulation and 74062306a36Sopenharmony_ci * replace EtherType */ 74162306a36Sopenharmony_ci skb_pull(skb, hdrlen + SNAP_SIZE); 74262306a36Sopenharmony_ci memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); 74362306a36Sopenharmony_ci memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci __be16 len; 74662306a36Sopenharmony_ci /* Leave Ethernet header part of hdr and full payload */ 74762306a36Sopenharmony_ci skb_pull(skb, hdrlen); 74862306a36Sopenharmony_ci len = htons(skb->len); 74962306a36Sopenharmony_ci memcpy(skb_push(skb, 2), &len, 2); 75062306a36Sopenharmony_ci memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); 75162306a36Sopenharmony_ci memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci#ifdef NOT_YET 75562306a36Sopenharmony_ci if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == 75662306a36Sopenharmony_ci IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { 75762306a36Sopenharmony_ci /* Non-standard frame: get addr4 from its bogus location after 75862306a36Sopenharmony_ci * the payload */ 75962306a36Sopenharmony_ci skb_copy_to_linear_data_offset(skb, ETH_ALEN, 76062306a36Sopenharmony_ci skb->data + skb->len - ETH_ALEN, 76162306a36Sopenharmony_ci ETH_ALEN); 76262306a36Sopenharmony_ci skb_trim(skb, skb->len - ETH_ALEN); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci#endif 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci dev->stats.rx_packets++; 76762306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci#ifdef NOT_YET 77062306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) { 77162306a36Sopenharmony_ci if (is_multicast_ether_addr(dst)) { 77262306a36Sopenharmony_ci /* copy multicast frame both to the higher layers and 77362306a36Sopenharmony_ci * to the wireless media */ 77462306a36Sopenharmony_ci ieee->ap->bridged_multicast++; 77562306a36Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 77662306a36Sopenharmony_ci if (skb2 == NULL) 77762306a36Sopenharmony_ci printk(KERN_DEBUG "%s: skb_clone failed for " 77862306a36Sopenharmony_ci "multicast frame\n", dev->name); 77962306a36Sopenharmony_ci } else if (hostap_is_sta_assoc(ieee->ap, dst)) { 78062306a36Sopenharmony_ci /* send frame directly to the associated STA using 78162306a36Sopenharmony_ci * wireless media and not passing to higher layers */ 78262306a36Sopenharmony_ci ieee->ap->bridged_unicast++; 78362306a36Sopenharmony_ci skb2 = skb; 78462306a36Sopenharmony_ci skb = NULL; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (skb2 != NULL) { 78962306a36Sopenharmony_ci /* send to wireless media */ 79062306a36Sopenharmony_ci skb2->dev = dev; 79162306a36Sopenharmony_ci skb2->protocol = htons(ETH_P_802_3); 79262306a36Sopenharmony_ci skb_reset_mac_header(skb2); 79362306a36Sopenharmony_ci skb_reset_network_header(skb2); 79462306a36Sopenharmony_ci /* skb2->network_header += ETH_HLEN; */ 79562306a36Sopenharmony_ci dev_queue_xmit(skb2); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci#endif 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (skb) { 80062306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 80162306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(skb->cb)); 80262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ 80362306a36Sopenharmony_ci if (netif_rx(skb) == NET_RX_DROP) { 80462306a36Sopenharmony_ci /* netif_rx always succeeds, but it might drop 80562306a36Sopenharmony_ci * the packet. If it drops the packet, we log that 80662306a36Sopenharmony_ci * in our stats. */ 80762306a36Sopenharmony_ci LIBIPW_DEBUG_DROP 80862306a36Sopenharmony_ci ("RX: netif_rx dropped the packet\n"); 80962306a36Sopenharmony_ci dev->stats.rx_dropped++; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci rx_exit: 81462306a36Sopenharmony_ci#ifdef NOT_YET 81562306a36Sopenharmony_ci if (sta) 81662306a36Sopenharmony_ci hostap_handle_sta_release(sta); 81762306a36Sopenharmony_ci#endif 81862306a36Sopenharmony_ci return 1; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci rx_dropped: 82162306a36Sopenharmony_ci dev->stats.rx_dropped++; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* Returning 0 indicates to caller that we have not handled the SKB-- 82462306a36Sopenharmony_ci * so it is still allocated and can be used again by underlying 82562306a36Sopenharmony_ci * hardware as a DMA target */ 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/* Filter out unrelated packets, call libipw_rx[_mgt] 83062306a36Sopenharmony_ci * This function takes over the skb, it should not be used again after calling 83162306a36Sopenharmony_ci * this function. */ 83262306a36Sopenharmony_civoid libipw_rx_any(struct libipw_device *ieee, 83362306a36Sopenharmony_ci struct sk_buff *skb, struct libipw_rx_stats *stats) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct libipw_hdr_4addr *hdr; 83662306a36Sopenharmony_ci int is_packet_for_us; 83762306a36Sopenharmony_ci u16 fc; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_MONITOR) { 84062306a36Sopenharmony_ci if (!libipw_rx(ieee, skb, stats)) 84162306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 84262306a36Sopenharmony_ci return; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (skb->len < sizeof(struct ieee80211_hdr)) 84662306a36Sopenharmony_ci goto drop_free; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci hdr = (struct libipw_hdr_4addr *)skb->data; 84962306a36Sopenharmony_ci fc = le16_to_cpu(hdr->frame_ctl); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if ((fc & IEEE80211_FCTL_VERS) != 0) 85262306a36Sopenharmony_ci goto drop_free; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (fc & IEEE80211_FCTL_FTYPE) { 85562306a36Sopenharmony_ci case IEEE80211_FTYPE_MGMT: 85662306a36Sopenharmony_ci if (skb->len < sizeof(struct libipw_hdr_3addr)) 85762306a36Sopenharmony_ci goto drop_free; 85862306a36Sopenharmony_ci libipw_rx_mgt(ieee, hdr, stats); 85962306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 86062306a36Sopenharmony_ci return; 86162306a36Sopenharmony_ci case IEEE80211_FTYPE_DATA: 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case IEEE80211_FTYPE_CTL: 86462306a36Sopenharmony_ci return; 86562306a36Sopenharmony_ci default: 86662306a36Sopenharmony_ci return; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci is_packet_for_us = 0; 87062306a36Sopenharmony_ci switch (ieee->iw_mode) { 87162306a36Sopenharmony_ci case IW_MODE_ADHOC: 87262306a36Sopenharmony_ci /* our BSS and not from/to DS */ 87362306a36Sopenharmony_ci if (ether_addr_equal(hdr->addr3, ieee->bssid)) 87462306a36Sopenharmony_ci if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { 87562306a36Sopenharmony_ci /* promisc: get all */ 87662306a36Sopenharmony_ci if (ieee->dev->flags & IFF_PROMISC) 87762306a36Sopenharmony_ci is_packet_for_us = 1; 87862306a36Sopenharmony_ci /* to us */ 87962306a36Sopenharmony_ci else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) 88062306a36Sopenharmony_ci is_packet_for_us = 1; 88162306a36Sopenharmony_ci /* mcast */ 88262306a36Sopenharmony_ci else if (is_multicast_ether_addr(hdr->addr1)) 88362306a36Sopenharmony_ci is_packet_for_us = 1; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case IW_MODE_INFRA: 88762306a36Sopenharmony_ci /* our BSS (== from our AP) and from DS */ 88862306a36Sopenharmony_ci if (ether_addr_equal(hdr->addr2, ieee->bssid)) 88962306a36Sopenharmony_ci if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { 89062306a36Sopenharmony_ci /* promisc: get all */ 89162306a36Sopenharmony_ci if (ieee->dev->flags & IFF_PROMISC) 89262306a36Sopenharmony_ci is_packet_for_us = 1; 89362306a36Sopenharmony_ci /* to us */ 89462306a36Sopenharmony_ci else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) 89562306a36Sopenharmony_ci is_packet_for_us = 1; 89662306a36Sopenharmony_ci /* mcast */ 89762306a36Sopenharmony_ci else if (is_multicast_ether_addr(hdr->addr1)) { 89862306a36Sopenharmony_ci /* not our own packet bcasted from AP */ 89962306a36Sopenharmony_ci if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr)) 90062306a36Sopenharmony_ci is_packet_for_us = 1; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci default: 90562306a36Sopenharmony_ci /* ? */ 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (is_packet_for_us) 91062306a36Sopenharmony_ci if (!libipw_rx(ieee, skb, stats)) 91162306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 91262306a36Sopenharmony_ci return; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cidrop_free: 91562306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 91662306a36Sopenharmony_ci ieee->dev->stats.rx_dropped++; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci/* 92462306a36Sopenharmony_ci* Make the structure we read from the beacon packet to have 92562306a36Sopenharmony_ci* the right values 92662306a36Sopenharmony_ci*/ 92762306a36Sopenharmony_cistatic int libipw_verify_qos_info(struct libipw_qos_information_element 92862306a36Sopenharmony_ci *info_element, int sub_type) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci if (info_element->elementID != QOS_ELEMENT_ID) 93162306a36Sopenharmony_ci return -1; 93262306a36Sopenharmony_ci if (info_element->qui_subtype != sub_type) 93362306a36Sopenharmony_ci return -1; 93462306a36Sopenharmony_ci if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) 93562306a36Sopenharmony_ci return -1; 93662306a36Sopenharmony_ci if (info_element->qui_type != QOS_OUI_TYPE) 93762306a36Sopenharmony_ci return -1; 93862306a36Sopenharmony_ci if (info_element->version != QOS_VERSION_1) 93962306a36Sopenharmony_ci return -1; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci return 0; 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci/* 94562306a36Sopenharmony_ci * Parse a QoS parameter element 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic int libipw_read_qos_param_element( 94862306a36Sopenharmony_ci struct libipw_qos_parameter_info *element_param, 94962306a36Sopenharmony_ci struct libipw_info_element *info_element) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci size_t size = sizeof(*element_param); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (!element_param || !info_element || info_element->len != size - 2) 95462306a36Sopenharmony_ci return -1; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci memcpy(element_param, info_element, size); 95762306a36Sopenharmony_ci return libipw_verify_qos_info(&element_param->info_element, 95862306a36Sopenharmony_ci QOS_OUI_PARAM_SUB_TYPE); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* 96262306a36Sopenharmony_ci * Parse a QoS information element 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_cistatic int libipw_read_qos_info_element( 96562306a36Sopenharmony_ci struct libipw_qos_information_element *element_info, 96662306a36Sopenharmony_ci struct libipw_info_element *info_element) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci size_t size = sizeof(struct libipw_qos_information_element) - 2; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (!element_info || !info_element || info_element->len != size - 2) 97162306a36Sopenharmony_ci return -1; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci memcpy(element_info, info_element, size); 97462306a36Sopenharmony_ci return libipw_verify_qos_info(element_info, QOS_OUI_INFO_SUB_TYPE); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* 97862306a36Sopenharmony_ci * Write QoS parameters from the ac parameters. 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_cistatic void libipw_qos_convert_ac_to_parameters(struct 98162306a36Sopenharmony_ci libipw_qos_parameter_info 98262306a36Sopenharmony_ci *param_elm, struct 98362306a36Sopenharmony_ci libipw_qos_parameters 98462306a36Sopenharmony_ci *qos_param) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci int i; 98762306a36Sopenharmony_ci struct libipw_qos_ac_parameter *ac_params; 98862306a36Sopenharmony_ci u32 txop; 98962306a36Sopenharmony_ci u8 cw_min; 99062306a36Sopenharmony_ci u8 cw_max; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci for (i = 0; i < QOS_QUEUE_NUM; i++) { 99362306a36Sopenharmony_ci ac_params = &(param_elm->ac_params_record[i]); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; 99662306a36Sopenharmony_ci qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci cw_min = ac_params->ecw_min_max & 0x0F; 99962306a36Sopenharmony_ci qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; 100262306a36Sopenharmony_ci qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci qos_param->flag[i] = 100562306a36Sopenharmony_ci (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci txop = le16_to_cpu(ac_params->tx_op_limit) * 32; 100862306a36Sopenharmony_ci qos_param->tx_op_limit[i] = cpu_to_le16(txop); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/* 101362306a36Sopenharmony_ci * we have a generic data element which it may contain QoS information or 101462306a36Sopenharmony_ci * parameters element. check the information element length to decide 101562306a36Sopenharmony_ci * which type to read 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_cistatic int libipw_parse_qos_info_param_IE(struct libipw_info_element 101862306a36Sopenharmony_ci *info_element, 101962306a36Sopenharmony_ci struct libipw_network *network) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci int rc = 0; 102262306a36Sopenharmony_ci struct libipw_qos_parameters *qos_param = NULL; 102362306a36Sopenharmony_ci struct libipw_qos_information_element qos_info_element; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci rc = libipw_read_qos_info_element(&qos_info_element, info_element); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (rc == 0) { 102862306a36Sopenharmony_ci network->qos_data.param_count = qos_info_element.ac_info & 0x0F; 102962306a36Sopenharmony_ci network->flags |= NETWORK_HAS_QOS_INFORMATION; 103062306a36Sopenharmony_ci } else { 103162306a36Sopenharmony_ci struct libipw_qos_parameter_info param_element; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci rc = libipw_read_qos_param_element(¶m_element, 103462306a36Sopenharmony_ci info_element); 103562306a36Sopenharmony_ci if (rc == 0) { 103662306a36Sopenharmony_ci qos_param = &(network->qos_data.parameters); 103762306a36Sopenharmony_ci libipw_qos_convert_ac_to_parameters(¶m_element, 103862306a36Sopenharmony_ci qos_param); 103962306a36Sopenharmony_ci network->flags |= NETWORK_HAS_QOS_PARAMETERS; 104062306a36Sopenharmony_ci network->qos_data.param_count = 104162306a36Sopenharmony_ci param_element.info_element.ac_info & 0x0F; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (rc == 0) { 104662306a36Sopenharmony_ci LIBIPW_DEBUG_QOS("QoS is supported\n"); 104762306a36Sopenharmony_ci network->qos_data.supported = 1; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci return rc; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 105362306a36Sopenharmony_ci#define MFIE_STRING(x) case WLAN_EID_ ##x: return #x 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic const char *get_info_element_string(u16 id) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci switch (id) { 105862306a36Sopenharmony_ci MFIE_STRING(SSID); 105962306a36Sopenharmony_ci MFIE_STRING(SUPP_RATES); 106062306a36Sopenharmony_ci MFIE_STRING(FH_PARAMS); 106162306a36Sopenharmony_ci MFIE_STRING(DS_PARAMS); 106262306a36Sopenharmony_ci MFIE_STRING(CF_PARAMS); 106362306a36Sopenharmony_ci MFIE_STRING(TIM); 106462306a36Sopenharmony_ci MFIE_STRING(IBSS_PARAMS); 106562306a36Sopenharmony_ci MFIE_STRING(COUNTRY); 106662306a36Sopenharmony_ci MFIE_STRING(REQUEST); 106762306a36Sopenharmony_ci MFIE_STRING(CHALLENGE); 106862306a36Sopenharmony_ci MFIE_STRING(PWR_CONSTRAINT); 106962306a36Sopenharmony_ci MFIE_STRING(PWR_CAPABILITY); 107062306a36Sopenharmony_ci MFIE_STRING(TPC_REQUEST); 107162306a36Sopenharmony_ci MFIE_STRING(TPC_REPORT); 107262306a36Sopenharmony_ci MFIE_STRING(SUPPORTED_CHANNELS); 107362306a36Sopenharmony_ci MFIE_STRING(CHANNEL_SWITCH); 107462306a36Sopenharmony_ci MFIE_STRING(MEASURE_REQUEST); 107562306a36Sopenharmony_ci MFIE_STRING(MEASURE_REPORT); 107662306a36Sopenharmony_ci MFIE_STRING(QUIET); 107762306a36Sopenharmony_ci MFIE_STRING(IBSS_DFS); 107862306a36Sopenharmony_ci MFIE_STRING(ERP_INFO); 107962306a36Sopenharmony_ci MFIE_STRING(RSN); 108062306a36Sopenharmony_ci MFIE_STRING(EXT_SUPP_RATES); 108162306a36Sopenharmony_ci MFIE_STRING(VENDOR_SPECIFIC); 108262306a36Sopenharmony_ci MFIE_STRING(QOS_PARAMETER); 108362306a36Sopenharmony_ci default: 108462306a36Sopenharmony_ci return "UNKNOWN"; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci#endif 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic int libipw_parse_info_param(struct libipw_info_element 109062306a36Sopenharmony_ci *info_element, u16 length, 109162306a36Sopenharmony_ci struct libipw_network *network) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci u8 i; 109462306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 109562306a36Sopenharmony_ci char rates_str[64]; 109662306a36Sopenharmony_ci char *p; 109762306a36Sopenharmony_ci#endif 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci while (length >= sizeof(*info_element)) { 110062306a36Sopenharmony_ci if (sizeof(*info_element) + info_element->len > length) { 110162306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("Info elem: parse failed: " 110262306a36Sopenharmony_ci "info_element->len + 2 > left : " 110362306a36Sopenharmony_ci "info_element->len+2=%zd left=%d, id=%d.\n", 110462306a36Sopenharmony_ci info_element->len + 110562306a36Sopenharmony_ci sizeof(*info_element), 110662306a36Sopenharmony_ci length, info_element->id); 110762306a36Sopenharmony_ci /* We stop processing but don't return an error here 110862306a36Sopenharmony_ci * because some misbehaviour APs break this rule. ie. 110962306a36Sopenharmony_ci * Orinoco AP1000. */ 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci switch (info_element->id) { 111462306a36Sopenharmony_ci case WLAN_EID_SSID: 111562306a36Sopenharmony_ci network->ssid_len = min(info_element->len, 111662306a36Sopenharmony_ci (u8) IW_ESSID_MAX_SIZE); 111762306a36Sopenharmony_ci memcpy(network->ssid, info_element->data, 111862306a36Sopenharmony_ci network->ssid_len); 111962306a36Sopenharmony_ci if (network->ssid_len < IW_ESSID_MAX_SIZE) 112062306a36Sopenharmony_ci memset(network->ssid + network->ssid_len, 0, 112162306a36Sopenharmony_ci IW_ESSID_MAX_SIZE - network->ssid_len); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%*pE' len=%d.\n", 112462306a36Sopenharmony_ci network->ssid_len, network->ssid, 112562306a36Sopenharmony_ci network->ssid_len); 112662306a36Sopenharmony_ci break; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci case WLAN_EID_SUPP_RATES: 112962306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 113062306a36Sopenharmony_ci p = rates_str; 113162306a36Sopenharmony_ci#endif 113262306a36Sopenharmony_ci network->rates_len = min(info_element->len, 113362306a36Sopenharmony_ci MAX_RATES_LENGTH); 113462306a36Sopenharmony_ci for (i = 0; i < network->rates_len; i++) { 113562306a36Sopenharmony_ci network->rates[i] = info_element->data[i]; 113662306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 113762306a36Sopenharmony_ci p += scnprintf(p, sizeof(rates_str) - 113862306a36Sopenharmony_ci (p - rates_str), "%02X ", 113962306a36Sopenharmony_ci network->rates[i]); 114062306a36Sopenharmony_ci#endif 114162306a36Sopenharmony_ci if (libipw_is_ofdm_rate 114262306a36Sopenharmony_ci (info_element->data[i])) { 114362306a36Sopenharmony_ci network->flags |= NETWORK_HAS_OFDM; 114462306a36Sopenharmony_ci if (info_element->data[i] & 114562306a36Sopenharmony_ci LIBIPW_BASIC_RATE_MASK) 114662306a36Sopenharmony_ci network->flags &= 114762306a36Sopenharmony_ci ~NETWORK_HAS_CCK; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", 115262306a36Sopenharmony_ci rates_str, network->rates_len); 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 115662306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 115762306a36Sopenharmony_ci p = rates_str; 115862306a36Sopenharmony_ci#endif 115962306a36Sopenharmony_ci network->rates_ex_len = min(info_element->len, 116062306a36Sopenharmony_ci MAX_RATES_EX_LENGTH); 116162306a36Sopenharmony_ci for (i = 0; i < network->rates_ex_len; i++) { 116262306a36Sopenharmony_ci network->rates_ex[i] = info_element->data[i]; 116362306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 116462306a36Sopenharmony_ci p += scnprintf(p, sizeof(rates_str) - 116562306a36Sopenharmony_ci (p - rates_str), "%02X ", 116662306a36Sopenharmony_ci network->rates_ex[i]); 116762306a36Sopenharmony_ci#endif 116862306a36Sopenharmony_ci if (libipw_is_ofdm_rate 116962306a36Sopenharmony_ci (info_element->data[i])) { 117062306a36Sopenharmony_ci network->flags |= NETWORK_HAS_OFDM; 117162306a36Sopenharmony_ci if (info_element->data[i] & 117262306a36Sopenharmony_ci LIBIPW_BASIC_RATE_MASK) 117362306a36Sopenharmony_ci network->flags &= 117462306a36Sopenharmony_ci ~NETWORK_HAS_CCK; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", 117962306a36Sopenharmony_ci rates_str, network->rates_ex_len); 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci case WLAN_EID_DS_PARAMS: 118362306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", 118462306a36Sopenharmony_ci info_element->data[0]); 118562306a36Sopenharmony_ci network->channel = info_element->data[0]; 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci case WLAN_EID_FH_PARAMS: 118962306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); 119062306a36Sopenharmony_ci break; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci case WLAN_EID_CF_PARAMS: 119362306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci case WLAN_EID_TIM: 119762306a36Sopenharmony_ci network->tim.tim_count = info_element->data[0]; 119862306a36Sopenharmony_ci network->tim.tim_period = info_element->data[1]; 119962306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); 120062306a36Sopenharmony_ci break; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci case WLAN_EID_ERP_INFO: 120362306a36Sopenharmony_ci network->erp_value = info_element->data[0]; 120462306a36Sopenharmony_ci network->flags |= NETWORK_HAS_ERP_VALUE; 120562306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", 120662306a36Sopenharmony_ci network->erp_value); 120762306a36Sopenharmony_ci break; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci case WLAN_EID_IBSS_PARAMS: 121062306a36Sopenharmony_ci network->atim_window = info_element->data[0]; 121162306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", 121262306a36Sopenharmony_ci network->atim_window); 121362306a36Sopenharmony_ci break; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci case WLAN_EID_CHALLENGE: 121662306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); 121762306a36Sopenharmony_ci break; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 122062306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n", 122162306a36Sopenharmony_ci info_element->len); 122262306a36Sopenharmony_ci if (!libipw_parse_qos_info_param_IE(info_element, 122362306a36Sopenharmony_ci network)) 122462306a36Sopenharmony_ci break; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (info_element->len >= 4 && 122762306a36Sopenharmony_ci info_element->data[0] == 0x00 && 122862306a36Sopenharmony_ci info_element->data[1] == 0x50 && 122962306a36Sopenharmony_ci info_element->data[2] == 0xf2 && 123062306a36Sopenharmony_ci info_element->data[3] == 0x01) { 123162306a36Sopenharmony_ci network->wpa_ie_len = min(info_element->len + 2, 123262306a36Sopenharmony_ci MAX_WPA_IE_LEN); 123362306a36Sopenharmony_ci memcpy(network->wpa_ie, info_element, 123462306a36Sopenharmony_ci network->wpa_ie_len); 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci case WLAN_EID_RSN: 123962306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", 124062306a36Sopenharmony_ci info_element->len); 124162306a36Sopenharmony_ci network->rsn_ie_len = min(info_element->len + 2, 124262306a36Sopenharmony_ci MAX_WPA_IE_LEN); 124362306a36Sopenharmony_ci memcpy(network->rsn_ie, info_element, 124462306a36Sopenharmony_ci network->rsn_ie_len); 124562306a36Sopenharmony_ci break; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci case WLAN_EID_QOS_PARAMETER: 124862306a36Sopenharmony_ci printk(KERN_ERR 124962306a36Sopenharmony_ci "QoS Error need to parse QOS_PARAMETER IE\n"); 125062306a36Sopenharmony_ci break; 125162306a36Sopenharmony_ci /* 802.11h */ 125262306a36Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 125362306a36Sopenharmony_ci network->power_constraint = info_element->data[0]; 125462306a36Sopenharmony_ci network->flags |= NETWORK_HAS_POWER_CONSTRAINT; 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 125862306a36Sopenharmony_ci network->power_constraint = info_element->data[0]; 125962306a36Sopenharmony_ci network->flags |= NETWORK_HAS_CSA; 126062306a36Sopenharmony_ci break; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci case WLAN_EID_QUIET: 126362306a36Sopenharmony_ci network->quiet.count = info_element->data[0]; 126462306a36Sopenharmony_ci network->quiet.period = info_element->data[1]; 126562306a36Sopenharmony_ci network->quiet.duration = info_element->data[2]; 126662306a36Sopenharmony_ci network->quiet.offset = info_element->data[3]; 126762306a36Sopenharmony_ci network->flags |= NETWORK_HAS_QUIET; 126862306a36Sopenharmony_ci break; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci case WLAN_EID_IBSS_DFS: 127162306a36Sopenharmony_ci network->flags |= NETWORK_HAS_IBSS_DFS; 127262306a36Sopenharmony_ci break; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci case WLAN_EID_TPC_REPORT: 127562306a36Sopenharmony_ci network->tpc_report.transmit_power = 127662306a36Sopenharmony_ci info_element->data[0]; 127762306a36Sopenharmony_ci network->tpc_report.link_margin = info_element->data[1]; 127862306a36Sopenharmony_ci network->flags |= NETWORK_HAS_TPC_REPORT; 127962306a36Sopenharmony_ci break; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci default: 128262306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT 128362306a36Sopenharmony_ci ("Unsupported info element: %s (%d)\n", 128462306a36Sopenharmony_ci get_info_element_string(info_element->id), 128562306a36Sopenharmony_ci info_element->id); 128662306a36Sopenharmony_ci break; 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci length -= sizeof(*info_element) + info_element->len; 129062306a36Sopenharmony_ci info_element = 129162306a36Sopenharmony_ci (struct libipw_info_element *)&info_element-> 129262306a36Sopenharmony_ci data[info_element->len]; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return 0; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response 129962306a36Sopenharmony_ci *frame, struct libipw_rx_stats *stats) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct libipw_network network_resp = { }; 130262306a36Sopenharmony_ci struct libipw_network *network = &network_resp; 130362306a36Sopenharmony_ci struct net_device *dev = ieee->dev; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci network->flags = 0; 130662306a36Sopenharmony_ci network->qos_data.active = 0; 130762306a36Sopenharmony_ci network->qos_data.supported = 0; 130862306a36Sopenharmony_ci network->qos_data.param_count = 0; 130962306a36Sopenharmony_ci network->qos_data.old_param_count = 0; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); 131262306a36Sopenharmony_ci network->atim_window = le16_to_cpu(frame->aid); 131362306a36Sopenharmony_ci network->listen_interval = le16_to_cpu(frame->status); 131462306a36Sopenharmony_ci memcpy(network->bssid, frame->header.addr3, ETH_ALEN); 131562306a36Sopenharmony_ci network->capability = le16_to_cpu(frame->capability); 131662306a36Sopenharmony_ci network->last_scanned = jiffies; 131762306a36Sopenharmony_ci network->rates_len = network->rates_ex_len = 0; 131862306a36Sopenharmony_ci network->last_associate = 0; 131962306a36Sopenharmony_ci network->ssid_len = 0; 132062306a36Sopenharmony_ci network->erp_value = 132162306a36Sopenharmony_ci (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (stats->freq == LIBIPW_52GHZ_BAND) { 132462306a36Sopenharmony_ci /* for A band (No DS info) */ 132562306a36Sopenharmony_ci network->channel = stats->received_channel; 132662306a36Sopenharmony_ci } else 132762306a36Sopenharmony_ci network->flags |= NETWORK_HAS_CCK; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci network->wpa_ie_len = 0; 133062306a36Sopenharmony_ci network->rsn_ie_len = 0; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (libipw_parse_info_param((void *)frame->variable, 133362306a36Sopenharmony_ci stats->len - sizeof(*frame), network)) 133462306a36Sopenharmony_ci return 1; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci network->mode = 0; 133762306a36Sopenharmony_ci if (stats->freq == LIBIPW_52GHZ_BAND) 133862306a36Sopenharmony_ci network->mode = IEEE_A; 133962306a36Sopenharmony_ci else { 134062306a36Sopenharmony_ci if (network->flags & NETWORK_HAS_OFDM) 134162306a36Sopenharmony_ci network->mode |= IEEE_G; 134262306a36Sopenharmony_ci if (network->flags & NETWORK_HAS_CCK) 134362306a36Sopenharmony_ci network->mode |= IEEE_B; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci memcpy(&network->stats, stats, sizeof(network->stats)); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (ieee->handle_assoc_response != NULL) 134962306a36Sopenharmony_ci ieee->handle_assoc_response(dev, frame, network); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci return 0; 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci/***************************************************/ 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response 135762306a36Sopenharmony_ci *beacon, 135862306a36Sopenharmony_ci struct libipw_network *network, 135962306a36Sopenharmony_ci struct libipw_rx_stats *stats) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci network->qos_data.active = 0; 136262306a36Sopenharmony_ci network->qos_data.supported = 0; 136362306a36Sopenharmony_ci network->qos_data.param_count = 0; 136462306a36Sopenharmony_ci network->qos_data.old_param_count = 0; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* Pull out fixed field data */ 136762306a36Sopenharmony_ci memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); 136862306a36Sopenharmony_ci network->capability = le16_to_cpu(beacon->capability); 136962306a36Sopenharmony_ci network->last_scanned = jiffies; 137062306a36Sopenharmony_ci network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]); 137162306a36Sopenharmony_ci network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]); 137262306a36Sopenharmony_ci network->beacon_interval = le16_to_cpu(beacon->beacon_interval); 137362306a36Sopenharmony_ci /* Where to pull this? beacon->listen_interval; */ 137462306a36Sopenharmony_ci network->listen_interval = 0x0A; 137562306a36Sopenharmony_ci network->rates_len = network->rates_ex_len = 0; 137662306a36Sopenharmony_ci network->last_associate = 0; 137762306a36Sopenharmony_ci network->ssid_len = 0; 137862306a36Sopenharmony_ci network->flags = 0; 137962306a36Sopenharmony_ci network->atim_window = 0; 138062306a36Sopenharmony_ci network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? 138162306a36Sopenharmony_ci 0x3 : 0x0; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if (stats->freq == LIBIPW_52GHZ_BAND) { 138462306a36Sopenharmony_ci /* for A band (No DS info) */ 138562306a36Sopenharmony_ci network->channel = stats->received_channel; 138662306a36Sopenharmony_ci } else 138762306a36Sopenharmony_ci network->flags |= NETWORK_HAS_CCK; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci network->wpa_ie_len = 0; 139062306a36Sopenharmony_ci network->rsn_ie_len = 0; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (libipw_parse_info_param((void *)beacon->variable, 139362306a36Sopenharmony_ci stats->len - sizeof(*beacon), network)) 139462306a36Sopenharmony_ci return 1; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci network->mode = 0; 139762306a36Sopenharmony_ci if (stats->freq == LIBIPW_52GHZ_BAND) 139862306a36Sopenharmony_ci network->mode = IEEE_A; 139962306a36Sopenharmony_ci else { 140062306a36Sopenharmony_ci if (network->flags & NETWORK_HAS_OFDM) 140162306a36Sopenharmony_ci network->mode |= IEEE_G; 140262306a36Sopenharmony_ci if (network->flags & NETWORK_HAS_CCK) 140362306a36Sopenharmony_ci network->mode |= IEEE_B; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (network->mode == 0) { 140762306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Filtered out '%*pE (%pM)' network.\n", 140862306a36Sopenharmony_ci network->ssid_len, network->ssid, 140962306a36Sopenharmony_ci network->bssid); 141062306a36Sopenharmony_ci return 1; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci memcpy(&network->stats, stats, sizeof(network->stats)); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci return 0; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic inline int is_same_network(struct libipw_network *src, 141962306a36Sopenharmony_ci struct libipw_network *dst) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci /* A network is only a duplicate if the channel, BSSID, and ESSID 142262306a36Sopenharmony_ci * all match. We treat all <hidden> with the same BSSID and channel 142362306a36Sopenharmony_ci * as one network */ 142462306a36Sopenharmony_ci return ((src->ssid_len == dst->ssid_len) && 142562306a36Sopenharmony_ci (src->channel == dst->channel) && 142662306a36Sopenharmony_ci ether_addr_equal_64bits(src->bssid, dst->bssid) && 142762306a36Sopenharmony_ci !memcmp(src->ssid, dst->ssid, src->ssid_len)); 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic void update_network(struct libipw_network *dst, 143162306a36Sopenharmony_ci struct libipw_network *src) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci int qos_active; 143462306a36Sopenharmony_ci u8 old_param; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* We only update the statistics if they were created by receiving 143762306a36Sopenharmony_ci * the network information on the actual channel the network is on. 143862306a36Sopenharmony_ci * 143962306a36Sopenharmony_ci * This keeps beacons received on neighbor channels from bringing 144062306a36Sopenharmony_ci * down the signal level of an AP. */ 144162306a36Sopenharmony_ci if (dst->channel == src->stats.received_channel) 144262306a36Sopenharmony_ci memcpy(&dst->stats, &src->stats, 144362306a36Sopenharmony_ci sizeof(struct libipw_rx_stats)); 144462306a36Sopenharmony_ci else 144562306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Network %pM info received " 144662306a36Sopenharmony_ci "off channel (%d vs. %d)\n", src->bssid, 144762306a36Sopenharmony_ci dst->channel, src->stats.received_channel); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci dst->capability = src->capability; 145062306a36Sopenharmony_ci memcpy(dst->rates, src->rates, src->rates_len); 145162306a36Sopenharmony_ci dst->rates_len = src->rates_len; 145262306a36Sopenharmony_ci memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); 145362306a36Sopenharmony_ci dst->rates_ex_len = src->rates_ex_len; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci dst->mode = src->mode; 145662306a36Sopenharmony_ci dst->flags = src->flags; 145762306a36Sopenharmony_ci dst->time_stamp[0] = src->time_stamp[0]; 145862306a36Sopenharmony_ci dst->time_stamp[1] = src->time_stamp[1]; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci dst->beacon_interval = src->beacon_interval; 146162306a36Sopenharmony_ci dst->listen_interval = src->listen_interval; 146262306a36Sopenharmony_ci dst->atim_window = src->atim_window; 146362306a36Sopenharmony_ci dst->erp_value = src->erp_value; 146462306a36Sopenharmony_ci dst->tim = src->tim; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); 146762306a36Sopenharmony_ci dst->wpa_ie_len = src->wpa_ie_len; 146862306a36Sopenharmony_ci memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); 146962306a36Sopenharmony_ci dst->rsn_ie_len = src->rsn_ie_len; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci dst->last_scanned = jiffies; 147262306a36Sopenharmony_ci qos_active = src->qos_data.active; 147362306a36Sopenharmony_ci old_param = dst->qos_data.old_param_count; 147462306a36Sopenharmony_ci if (dst->flags & NETWORK_HAS_QOS_MASK) 147562306a36Sopenharmony_ci memcpy(&dst->qos_data, &src->qos_data, 147662306a36Sopenharmony_ci sizeof(struct libipw_qos_data)); 147762306a36Sopenharmony_ci else { 147862306a36Sopenharmony_ci dst->qos_data.supported = src->qos_data.supported; 147962306a36Sopenharmony_ci dst->qos_data.param_count = src->qos_data.param_count; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (dst->qos_data.supported == 1) { 148362306a36Sopenharmony_ci if (dst->ssid_len) 148462306a36Sopenharmony_ci LIBIPW_DEBUG_QOS 148562306a36Sopenharmony_ci ("QoS the network %s is QoS supported\n", 148662306a36Sopenharmony_ci dst->ssid); 148762306a36Sopenharmony_ci else 148862306a36Sopenharmony_ci LIBIPW_DEBUG_QOS 148962306a36Sopenharmony_ci ("QoS the network is QoS supported\n"); 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci dst->qos_data.active = qos_active; 149262306a36Sopenharmony_ci dst->qos_data.old_param_count = old_param; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* dst->last_associate is not overwritten */ 149562306a36Sopenharmony_ci} 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic inline int is_beacon(__le16 fc) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic void libipw_process_probe_response(struct libipw_device 150362306a36Sopenharmony_ci *ieee, struct 150462306a36Sopenharmony_ci libipw_probe_response 150562306a36Sopenharmony_ci *beacon, struct libipw_rx_stats 150662306a36Sopenharmony_ci *stats) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci struct net_device *dev = ieee->dev; 150962306a36Sopenharmony_ci struct libipw_network network = { }; 151062306a36Sopenharmony_ci struct libipw_network *target; 151162306a36Sopenharmony_ci struct libipw_network *oldest = NULL; 151262306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 151362306a36Sopenharmony_ci struct libipw_info_element *info_element = (void *)beacon->variable; 151462306a36Sopenharmony_ci#endif 151562306a36Sopenharmony_ci unsigned long flags; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", 151862306a36Sopenharmony_ci info_element->len, info_element->data, 151962306a36Sopenharmony_ci beacon->header.addr3, 152062306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0', 152162306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0', 152262306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0', 152362306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0', 152462306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0', 152562306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0', 152662306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0', 152762306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0', 152862306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0', 152962306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0', 153062306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0', 153162306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0', 153262306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0', 153362306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0', 153462306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', 153562306a36Sopenharmony_ci (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (libipw_network_init(ieee, beacon, &network, stats)) { 153862306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Dropped '%*pE' (%pM) via %s.\n", 153962306a36Sopenharmony_ci info_element->len, info_element->data, 154062306a36Sopenharmony_ci beacon->header.addr3, 154162306a36Sopenharmony_ci is_beacon(beacon->header.frame_ctl) ? 154262306a36Sopenharmony_ci "BEACON" : "PROBE RESPONSE"); 154362306a36Sopenharmony_ci return; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci /* The network parsed correctly -- so now we scan our known networks 154762306a36Sopenharmony_ci * to see if we can find it in our list. 154862306a36Sopenharmony_ci * 154962306a36Sopenharmony_ci * NOTE: This search is definitely not optimized. Once its doing 155062306a36Sopenharmony_ci * the "right thing" we'll optimize it for efficiency if 155162306a36Sopenharmony_ci * necessary */ 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci /* Search for this entry in the list and update it if it is 155462306a36Sopenharmony_ci * already there. */ 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci spin_lock_irqsave(&ieee->lock, flags); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci list_for_each_entry(target, &ieee->network_list, list) { 155962306a36Sopenharmony_ci if (is_same_network(target, &network)) 156062306a36Sopenharmony_ci break; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if ((oldest == NULL) || 156362306a36Sopenharmony_ci time_before(target->last_scanned, oldest->last_scanned)) 156462306a36Sopenharmony_ci oldest = target; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* If we didn't find a match, then get a new network slot to initialize 156862306a36Sopenharmony_ci * with this beacon's information */ 156962306a36Sopenharmony_ci if (&target->list == &ieee->network_list) { 157062306a36Sopenharmony_ci if (list_empty(&ieee->network_free_list)) { 157162306a36Sopenharmony_ci /* If there are no more slots, expire the oldest */ 157262306a36Sopenharmony_ci list_del(&oldest->list); 157362306a36Sopenharmony_ci target = oldest; 157462306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n", 157562306a36Sopenharmony_ci target->ssid_len, target->ssid, 157662306a36Sopenharmony_ci target->bssid); 157762306a36Sopenharmony_ci } else { 157862306a36Sopenharmony_ci /* Otherwise just pull from the free list */ 157962306a36Sopenharmony_ci target = list_entry(ieee->network_free_list.next, 158062306a36Sopenharmony_ci struct libipw_network, list); 158162306a36Sopenharmony_ci list_del(ieee->network_free_list.next); 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci#ifdef CONFIG_LIBIPW_DEBUG 158562306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Adding '%*pE' (%pM) via %s.\n", 158662306a36Sopenharmony_ci network.ssid_len, network.ssid, 158762306a36Sopenharmony_ci network.bssid, 158862306a36Sopenharmony_ci is_beacon(beacon->header.frame_ctl) ? 158962306a36Sopenharmony_ci "BEACON" : "PROBE RESPONSE"); 159062306a36Sopenharmony_ci#endif 159162306a36Sopenharmony_ci memcpy(target, &network, sizeof(*target)); 159262306a36Sopenharmony_ci list_add_tail(&target->list, &ieee->network_list); 159362306a36Sopenharmony_ci } else { 159462306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n", 159562306a36Sopenharmony_ci target->ssid_len, target->ssid, 159662306a36Sopenharmony_ci target->bssid, 159762306a36Sopenharmony_ci is_beacon(beacon->header.frame_ctl) ? 159862306a36Sopenharmony_ci "BEACON" : "PROBE RESPONSE"); 159962306a36Sopenharmony_ci update_network(target, &network); 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci spin_unlock_irqrestore(&ieee->lock, flags); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (is_beacon(beacon->header.frame_ctl)) { 160562306a36Sopenharmony_ci if (ieee->handle_beacon != NULL) 160662306a36Sopenharmony_ci ieee->handle_beacon(dev, beacon, target); 160762306a36Sopenharmony_ci } else { 160862306a36Sopenharmony_ci if (ieee->handle_probe_response != NULL) 160962306a36Sopenharmony_ci ieee->handle_probe_response(dev, beacon, target); 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_civoid libipw_rx_mgt(struct libipw_device *ieee, 161462306a36Sopenharmony_ci struct libipw_hdr_4addr *header, 161562306a36Sopenharmony_ci struct libipw_rx_stats *stats) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { 161862306a36Sopenharmony_ci case IEEE80211_STYPE_ASSOC_RESP: 161962306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", 162062306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 162162306a36Sopenharmony_ci (header->frame_ctl))); 162262306a36Sopenharmony_ci libipw_handle_assoc_resp(ieee, 162362306a36Sopenharmony_ci (struct libipw_assoc_response *) 162462306a36Sopenharmony_ci header, stats); 162562306a36Sopenharmony_ci break; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci case IEEE80211_STYPE_REASSOC_RESP: 162862306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", 162962306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 163062306a36Sopenharmony_ci (header->frame_ctl))); 163162306a36Sopenharmony_ci break; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci case IEEE80211_STYPE_PROBE_REQ: 163462306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received auth (%d)\n", 163562306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 163662306a36Sopenharmony_ci (header->frame_ctl))); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (ieee->handle_probe_request != NULL) 163962306a36Sopenharmony_ci ieee->handle_probe_request(ieee->dev, 164062306a36Sopenharmony_ci (struct 164162306a36Sopenharmony_ci libipw_probe_request *) 164262306a36Sopenharmony_ci header, stats); 164362306a36Sopenharmony_ci break; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci case IEEE80211_STYPE_PROBE_RESP: 164662306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", 164762306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 164862306a36Sopenharmony_ci (header->frame_ctl))); 164962306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Probe response\n"); 165062306a36Sopenharmony_ci libipw_process_probe_response(ieee, 165162306a36Sopenharmony_ci (struct 165262306a36Sopenharmony_ci libipw_probe_response *) 165362306a36Sopenharmony_ci header, stats); 165462306a36Sopenharmony_ci break; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci case IEEE80211_STYPE_BEACON: 165762306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received BEACON (%d)\n", 165862306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 165962306a36Sopenharmony_ci (header->frame_ctl))); 166062306a36Sopenharmony_ci LIBIPW_DEBUG_SCAN("Beacon\n"); 166162306a36Sopenharmony_ci libipw_process_probe_response(ieee, 166262306a36Sopenharmony_ci (struct 166362306a36Sopenharmony_ci libipw_probe_response *) 166462306a36Sopenharmony_ci header, stats); 166562306a36Sopenharmony_ci break; 166662306a36Sopenharmony_ci case IEEE80211_STYPE_AUTH: 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received auth (%d)\n", 166962306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 167062306a36Sopenharmony_ci (header->frame_ctl))); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (ieee->handle_auth != NULL) 167362306a36Sopenharmony_ci ieee->handle_auth(ieee->dev, 167462306a36Sopenharmony_ci (struct libipw_auth *)header); 167562306a36Sopenharmony_ci break; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci case IEEE80211_STYPE_DISASSOC: 167862306a36Sopenharmony_ci if (ieee->handle_disassoc != NULL) 167962306a36Sopenharmony_ci ieee->handle_disassoc(ieee->dev, 168062306a36Sopenharmony_ci (struct libipw_disassoc *) 168162306a36Sopenharmony_ci header); 168262306a36Sopenharmony_ci break; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci case IEEE80211_STYPE_ACTION: 168562306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("ACTION\n"); 168662306a36Sopenharmony_ci if (ieee->handle_action) 168762306a36Sopenharmony_ci ieee->handle_action(ieee->dev, 168862306a36Sopenharmony_ci (struct libipw_action *) 168962306a36Sopenharmony_ci header, stats); 169062306a36Sopenharmony_ci break; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci case IEEE80211_STYPE_REASSOC_REQ: 169362306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received reassoc (%d)\n", 169462306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 169562306a36Sopenharmony_ci (header->frame_ctl))); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n", 169862306a36Sopenharmony_ci ieee->dev->name); 169962306a36Sopenharmony_ci if (ieee->handle_reassoc_request != NULL) 170062306a36Sopenharmony_ci ieee->handle_reassoc_request(ieee->dev, 170162306a36Sopenharmony_ci (struct libipw_reassoc_request *) 170262306a36Sopenharmony_ci header); 170362306a36Sopenharmony_ci break; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci case IEEE80211_STYPE_ASSOC_REQ: 170662306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received assoc (%d)\n", 170762306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 170862306a36Sopenharmony_ci (header->frame_ctl))); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n", 171162306a36Sopenharmony_ci ieee->dev->name); 171262306a36Sopenharmony_ci if (ieee->handle_assoc_request != NULL) 171362306a36Sopenharmony_ci ieee->handle_assoc_request(ieee->dev); 171462306a36Sopenharmony_ci break; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci case IEEE80211_STYPE_DEAUTH: 171762306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("DEAUTH\n"); 171862306a36Sopenharmony_ci if (ieee->handle_deauth != NULL) 171962306a36Sopenharmony_ci ieee->handle_deauth(ieee->dev, 172062306a36Sopenharmony_ci (struct libipw_deauth *) 172162306a36Sopenharmony_ci header); 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci default: 172462306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n", 172562306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 172662306a36Sopenharmony_ci (header->frame_ctl))); 172762306a36Sopenharmony_ci LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n", 172862306a36Sopenharmony_ci ieee->dev->name, 172962306a36Sopenharmony_ci WLAN_FC_GET_STYPE(le16_to_cpu 173062306a36Sopenharmony_ci (header->frame_ctl))); 173162306a36Sopenharmony_ci break; 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(libipw_rx_any); 173662306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_rx_mgt); 173762306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_rx); 1738