📄 ieee80211.c
字号:
dev->stop = ieee80211_stop; dev->destructor = ieee80211_if_free;}/* WDS specialties */int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; DECLARE_MAC_BUF(mac); if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0) return 0; /* Create STA entry for the new peer */ sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL); if (!sta) return -ENOMEM; sta_info_put(sta); /* Remove STA entry for the old peer */ sta = sta_info_get(local, sdata->u.wds.remote_addr); if (sta) { sta_info_free(sta); sta_info_put(sta); } else { printk(KERN_DEBUG "%s: could not find STA entry for WDS link " "peer %s\n", dev->name, print_mac(mac, sdata->u.wds.remote_addr)); } /* Update WDS link data */ memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN); return 0;}/* everything else */static int __ieee80211_if_config(struct net_device *dev, struct sk_buff *beacon, struct ieee80211_tx_control *control){ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_if_conf conf; if (!local->ops->config_interface || !netif_running(dev)) return 0; memset(&conf, 0, sizeof(conf)); conf.type = sdata->type; if (sdata->type == IEEE80211_IF_TYPE_STA || sdata->type == IEEE80211_IF_TYPE_IBSS) { conf.bssid = sdata->u.sta.bssid; conf.ssid = sdata->u.sta.ssid; conf.ssid_len = sdata->u.sta.ssid_len; } else if (sdata->type == IEEE80211_IF_TYPE_AP) { conf.ssid = sdata->u.ap.ssid; conf.ssid_len = sdata->u.ap.ssid_len; conf.beacon = beacon; conf.beacon_control = control; } return local->ops->config_interface(local_to_hw(local), dev->ifindex, &conf);}int ieee80211_if_config(struct net_device *dev){ return __ieee80211_if_config(dev, NULL, NULL);}int ieee80211_if_config_beacon(struct net_device *dev){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_tx_control control; struct sk_buff *skb; if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE)) return 0; skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control); if (!skb) return -ENOMEM; return __ieee80211_if_config(dev, skb, &control);}int ieee80211_hw_config(struct ieee80211_local *local){ struct ieee80211_hw_mode *mode; struct ieee80211_channel *chan; int ret = 0; if (local->sta_scanning) { chan = local->scan_channel; mode = local->scan_hw_mode; } else { chan = local->oper_channel; mode = local->oper_hw_mode; } local->hw.conf.channel = chan->chan; local->hw.conf.channel_val = chan->val; if (!local->hw.conf.power_level) { local->hw.conf.power_level = chan->power_level; } else { local->hw.conf.power_level = min(chan->power_level, local->hw.conf.power_level); } local->hw.conf.freq = chan->freq; local->hw.conf.phymode = mode->mode; local->hw.conf.antenna_max = chan->antenna_max; local->hw.conf.chan = chan; local->hw.conf.mode = mode;#ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d " "phymode=%d\n", local->hw.conf.channel, local->hw.conf.freq, local->hw.conf.phymode);#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ if (local->open_count) ret = local->ops->config(local_to_hw(local), &local->hw.conf); return ret;}void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (local->ops->erp_ie_changed) local->ops->erp_ie_changed(local_to_hw(local), changes, !!(sdata->flags & IEEE80211_SDATA_USE_PROTECTION), !(sdata->flags & IEEE80211_SDATA_SHORT_PREAMBLE));}void ieee80211_reset_erp_info(struct net_device *dev){ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); sdata->flags &= ~(IEEE80211_SDATA_USE_PROTECTION | IEEE80211_SDATA_SHORT_PREAMBLE); ieee80211_erp_info_change_notify(dev, IEEE80211_ERP_CHANGE_PROTECTION | IEEE80211_ERP_CHANGE_PREAMBLE);}void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_status *status){ struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_status *saved; int tmp; skb->dev = local->mdev; saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC); if (unlikely(!saved)) { if (net_ratelimit()) printk(KERN_WARNING "%s: Not enough memory, " "dropping tx status", skb->dev->name); /* should be dev_kfree_skb_irq, but due to this function being * named _irqsafe instead of just _irq we can't be sure that * people won't call it from non-irq contexts */ dev_kfree_skb_any(skb); return; } memcpy(saved, status, sizeof(struct ieee80211_tx_status)); /* copy pointer to saved status into skb->cb for use by tasklet */ memcpy(skb->cb, &saved, sizeof(saved)); skb->pkt_type = IEEE80211_TX_STATUS_MSG; skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ? &local->skb_queue : &local->skb_queue_unreliable, skb); tmp = skb_queue_len(&local->skb_queue) + skb_queue_len(&local->skb_queue_unreliable); while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && (skb = skb_dequeue(&local->skb_queue_unreliable))) { memcpy(&saved, skb->cb, sizeof(saved)); kfree(saved); dev_kfree_skb_irq(skb); tmp--; I802_DEBUG_INC(local->tx_status_drop); } tasklet_schedule(&local->tasklet);}EXPORT_SYMBOL(ieee80211_tx_status_irqsafe);static void ieee80211_tasklet_handler(unsigned long data){ struct ieee80211_local *local = (struct ieee80211_local *) data; struct sk_buff *skb; struct ieee80211_rx_status rx_status; struct ieee80211_tx_status *tx_status; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { switch (skb->pkt_type) { case IEEE80211_RX_MSG: /* status is in skb->cb */ memcpy(&rx_status, skb->cb, sizeof(rx_status)); /* Clear skb->type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; __ieee80211_rx(local_to_hw(local), skb, &rx_status); break; case IEEE80211_TX_STATUS_MSG: /* get pointer to saved status out of skb->cb */ memcpy(&tx_status, skb->cb, sizeof(tx_status)); skb->pkt_type = 0; ieee80211_tx_status(local_to_hw(local), skb, tx_status); kfree(tx_status); break; default: /* should never get here! */ printk(KERN_ERR "%s: Unknown message type (%d)\n", wiphy_name(local->hw.wiphy), skb->pkt_type); dev_kfree_skb(skb); break; } }}/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to * make a prepared TX frame (one that has been given to hw) to look like brand * new IEEE 802.11 frame that is ready to go through TX processing again. * Also, tx_packet_data in cb is restored from tx_control. */static void ieee80211_remove_tx_extra(struct ieee80211_local *local, struct ieee80211_key *key, struct sk_buff *skb, struct ieee80211_tx_control *control){ int hdrlen, iv_len, mic_len; struct ieee80211_tx_packet_data *pkt_data; pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; pkt_data->ifindex = control->ifindex; pkt_data->flags = 0; if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS) pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS; if (control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT) pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT; if (control->flags & IEEE80211_TXCTL_REQUEUE) pkt_data->flags |= IEEE80211_TXPD_REQUEUE; pkt_data->queue = control->queue; hdrlen = ieee80211_get_hdrlen_from_skb(skb); if (!key) goto no_key; switch (key->conf.alg) { case ALG_WEP: iv_len = WEP_IV_LEN; mic_len = WEP_ICV_LEN; break; case ALG_TKIP: iv_len = TKIP_IV_LEN; mic_len = TKIP_ICV_LEN; break; case ALG_CCMP: iv_len = CCMP_HDR_LEN; mic_len = CCMP_MIC_LEN; break; default: goto no_key; } if (skb->len >= mic_len && !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) skb_trim(skb, skb->len - mic_len); if (skb->len >= iv_len && skb->len > hdrlen) { memmove(skb->data + iv_len, skb->data, hdrlen); skb_pull(skb, iv_len); }no_key: { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; u16 fc = le16_to_cpu(hdr->frame_control); if ((fc & 0x8C) == 0x88) /* QoS Control Field */ { fc &= ~IEEE80211_STYPE_QOS_DATA; hdr->frame_control = cpu_to_le16(fc); memmove(skb->data + 2, skb->data, hdrlen - 2); skb_pull(skb, 2); } }}void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_status *status){ struct sk_buff *skb2; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = hw_to_local(hw); u16 frag, type; struct ieee80211_tx_status_rtap_hdr *rthdr; struct ieee80211_sub_if_data *sdata; int monitors; if (!status) { printk(KERN_ERR "%s: ieee80211_tx_status called with NULL status\n", wiphy_name(local->hw.wiphy)); dev_kfree_skb(skb); return; } if (status->excessive_retries) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { if (sta->flags & WLAN_STA_PS) { /* The STA is in power save mode, so assume * that this TX packet failed because of that. */ status->excessive_retries = 0; status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; } sta_info_put(sta); } } if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { sta->tx_filtered_count++; /* Clear the TX filter mask for this STA when sending * the next packet. If the STA went to power save mode, * this will happen when it is waking up for the next * time. */ sta->clear_dst_mask = 1; /* TODO: Is the WLAN_STA_PS flag always set here or is * the race between RX and TX status causing some * packets to be filtered out before 80211.o gets an * update for PS status? This seems to be the case, so * no changes are likely to be needed. */ if (sta->flags & WLAN_STA_PS && skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { ieee80211_remove_tx_extra(local, sta->key, skb, &status->control); skb_queue_tail(&sta->tx_filtered, skb); } else if (!(sta->flags & WLAN_STA_PS) && !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { /* Software retry the packet once */ status->control.flags |= IEEE80211_TXCTL_REQUEUE; ieee80211_remove_tx_extra(local, sta->key, skb, &status->control); dev_queue_xmit(skb); } else { if (net_ratelimit()) { printk(KERN_DEBUG "%s: dropped TX " "filtered frame queue_len=%d " "PS=%d @%lu\n", wiphy_name(local->hw.wiphy), skb_queue_len( &sta->tx_filtered), !!(sta->flags & WLAN_STA_PS), jiffies); } dev_kfree_skb(skb); } sta_info_put(sta); return; } } else { /* FIXME: STUPID to call this with both local and local->mdev */ rate_control_tx_status(local, local->mdev, skb, status); } ieee80211_led_tx(local, 0); /* SNMP counters * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; if (status->flags & IEEE80211_TX_STATUS_ACK) { if (frag == 0) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) local->dot11MulticastTransmittedFrameCount++; if (status->retry_count > 0) local->dot11RetryCount++; if (status->retry_count > 1) local->dot11MultipleRetryCount++; } /* This counter shall be incremented for an acknowledged MPDU * with an individual address in the address 1 field or an MPDU * with a multicast address in the address 1 field of type Data * or Management. */ if (!is_multicast_ether_addr(hdr->addr1) || type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT) local->dot11TransmittedFragmentCount++; } else { if (frag == 0) local->dot11FailedCount++; } /* this was a transmitted frame, but now we want to reuse it */ skb_orphan(skb); if (!local->monitors) { dev_kfree_skb(skb); return; } /* send frame to monitor interfaces now */ if (skb_headroom(skb) < sizeof(*rthdr)) { printk(KERN_ERR "ieee80211_tx_status: headroom too small\n"); dev_kfree_skb(skb); return; } rthdr = (struct ieee80211_tx_status_rtap_hdr*) skb_push(skb, sizeof(*rthdr)); memset(rthdr, 0, sizeof(*rthdr)); rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); rthdr->hdr.it_present =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -