diff options
Diffstat (limited to 'net/mac80211/rx.c')
-rw-r--r-- | net/mac80211/rx.c | 199 |
1 files changed, 141 insertions, 58 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe2c2a7..b867bd5 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -476,7 +476,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; - unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); char *dev_addr = rx->sdata->vif.addr; if (ieee80211_is_data(hdr->frame_control)) { @@ -524,14 +523,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) } -#define msh_h_get(h, l) ((struct ieee80211s_hdr *) ((u8 *)h + l)) - - if (ieee80211_is_data(hdr->frame_control) && - is_multicast_ether_addr(hdr->addr1) && - mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata)) - return RX_DROP_MONITOR; -#undef msh_h_get - return RX_CONTINUE; } @@ -850,8 +841,21 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && - (!rx->sta || !test_sta_flags(rx->sta, WLAN_STA_ASSOC)))) + (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { + if (rx->sta && rx->sta->dummy && + ieee80211_is_data_present(hdr->frame_control)) { + u16 ethertype; + u8 *payload; + + payload = rx->skb->data + + ieee80211_hdrlen(hdr->frame_control); + ethertype = (payload[6] << 8) | payload[7]; + if (cpu_to_be16(ethertype) == + rx->sdata->control_port_protocol) + return RX_CONTINUE; + } return RX_DROP_MONITOR; + } return RX_CONTINUE; } @@ -1106,7 +1110,7 @@ static void ap_sta_ps_start(struct sta_info *sta) struct ieee80211_local *local = sdata->local; atomic_inc(&sdata->bss->num_sta_ps); - set_sta_flags(sta, WLAN_STA_PS_STA); + set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG @@ -1126,7 +1130,7 @@ static void ap_sta_ps_end(struct sta_info *sta) sdata->name, sta->sta.addr, sta->sta.aid); #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ - if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) { + if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n", sdata->name, sta->sta.addr, sta->sta.aid); @@ -1145,7 +1149,7 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) WARN_ON(!(sta_inf->local->hw.flags & IEEE80211_HW_AP_LINK_PS)); /* Don't let the same PS state be set twice */ - in_ps = test_sta_flags(sta_inf, WLAN_STA_PS_STA); + in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); if ((start && in_ps) || (!start && !in_ps)) return -EINVAL; @@ -1159,6 +1163,81 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) EXPORT_SYMBOL(ieee80211_sta_ps_transition); static ieee80211_rx_result debug_noinline +ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) +{ + struct ieee80211_sub_if_data *sdata = rx->sdata; + struct ieee80211_hdr *hdr = (void *)rx->skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + int tid, ac; + + if (!rx->sta || !(status->rx_flags & IEEE80211_RX_RA_MATCH)) + return RX_CONTINUE; + + if (sdata->vif.type != NL80211_IFTYPE_AP && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return RX_CONTINUE; + + /* + * The device handles station powersave, so don't do anything about + * uAPSD and PS-Poll frames (the latter shouldn't even come up from + * it to mac80211 since they're handled.) + */ + if (sdata->local->hw.flags & IEEE80211_HW_AP_LINK_PS) + return RX_CONTINUE; + + /* + * Don't do anything if the station isn't already asleep. In + * the uAPSD case, the station will probably be marked asleep, + * in the PS-Poll case the station must be confused ... + */ + if (!test_sta_flag(rx->sta, WLAN_STA_PS_STA)) + return RX_CONTINUE; + + if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) { + if (!test_sta_flag(rx->sta, WLAN_STA_SP)) { + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_poll_response(rx->sta); + else + set_sta_flag(rx->sta, WLAN_STA_PSPOLL); + } + + /* Free PS Poll skb here instead of returning RX_DROP that would + * count as an dropped frame. */ + dev_kfree_skb(rx->skb); + + return RX_QUEUED; + } else if (!ieee80211_has_morefrags(hdr->frame_control) && + !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && + ieee80211_has_pm(hdr->frame_control) && + (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control))) { + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + ac = ieee802_1d_to_ac[tid & 7]; + + /* + * If this AC is not trigger-enabled do nothing. + * + * NB: This could/should check a separate bitmap of trigger- + * enabled queues, but for now we only implement uAPSD w/o + * TSPEC changes to the ACs, so they're always the same. + */ + if (!(rx->sta->sta.uapsd_queues & BIT(ac))) + return RX_CONTINUE; + + /* if we are in a service period, do nothing */ + if (test_sta_flag(rx->sta, WLAN_STA_SP)) + return RX_CONTINUE; + + if (!test_sta_flag(rx->sta, WLAN_STA_PS_DRIVER)) + ieee80211_sta_ps_deliver_uapsd(rx->sta); + else + set_sta_flag(rx->sta, WLAN_STA_UAPSD); + } + + return RX_CONTINUE; +} + +static ieee80211_rx_result debug_noinline ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) { struct sta_info *sta = rx->sta; @@ -1216,7 +1295,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) { - if (test_sta_flags(sta, WLAN_STA_PS_STA)) { + if (test_sta_flag(sta, WLAN_STA_PS_STA)) { /* * Ignore doze->wake transitions that are * indicated by non-data frames, the standard @@ -1469,33 +1548,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) } static ieee80211_rx_result debug_noinline -ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) -{ - struct ieee80211_sub_if_data *sdata = rx->sdata; - __le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - - if (likely(!rx->sta || !ieee80211_is_pspoll(fc) || - !(status->rx_flags & IEEE80211_RX_RA_MATCH))) - return RX_CONTINUE; - - if ((sdata->vif.type != NL80211_IFTYPE_AP) && - (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)) - return RX_DROP_UNUSABLE; - - if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER)) - ieee80211_sta_ps_deliver_poll_response(rx->sta); - else - set_sta_flags(rx->sta, WLAN_STA_PSPOLL); - - /* Free PS Poll skb here instead of returning RX_DROP that would - * count as an dropped frame. */ - dev_kfree_skb(rx->skb); - - return RX_QUEUED; -} - -static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { u8 *data = rx->skb->data; @@ -1518,7 +1570,7 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) { if (unlikely(!rx->sta || - !test_sta_flags(rx->sta, WLAN_STA_AUTHORIZED))) + !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) return -EACCES; return 0; @@ -1561,7 +1613,7 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DECRYPTED) return 0; - if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) { + if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) && ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && rx->key)) { @@ -1827,6 +1879,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(hdr->frame_control); mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); + /* frame is in RMC, don't forward */ + if (ieee80211_is_data(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr1) && + mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata)) + return RX_DROP_MONITOR; + if (!ieee80211_is_data(hdr->frame_control)) return RX_CONTINUE; @@ -1834,6 +1892,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) /* illegal frame */ return RX_DROP_MONITOR; + if (ieee80211_queue_stopped(&local->hw, skb_get_queue_mapping(skb))) { + IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, + dropped_frames_congestion); + return RX_DROP_MONITOR; + } + if (mesh_hdr->flags & MESH_FLAGS_AE) { struct mesh_path *mppath; char *proxied_addr; @@ -1889,13 +1953,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; - skb_set_queue_mapping(skb, - ieee80211_select_queue(rx->sdata, fwd_skb)); - ieee80211_set_qos_hdr(local, skb); - if (is_multicast_ether_addr(fwd_hdr->addr1)) + if (is_multicast_ether_addr(fwd_hdr->addr1)) { IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_mcast); - else { + skb_set_queue_mapping(fwd_skb, + ieee80211_select_queue(sdata, fwd_skb)); + ieee80211_set_qos_hdr(sdata, fwd_skb); + } else { int err; /* * Save TA to addr1 to send TA a path error if a @@ -2220,12 +2284,29 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto handled; } break; + case WLAN_CATEGORY_SELF_PROTECTED: + switch (mgmt->u.action.u.self_prot.action_code) { + case WLAN_SP_MESH_PEERING_OPEN: + case WLAN_SP_MESH_PEERING_CLOSE: + case WLAN_SP_MESH_PEERING_CONFIRM: + if (!ieee80211_vif_is_mesh(&sdata->vif)) + goto invalid; + if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE) + /* userspace handles this frame */ + break; + goto queue; + case WLAN_SP_MGK_INFORM: + case WLAN_SP_MGK_ACK: + if (!ieee80211_vif_is_mesh(&sdata->vif)) + goto invalid; + break; + } + break; case WLAN_CATEGORY_MESH_ACTION: if (!ieee80211_vif_is_mesh(&sdata->vif)) break; - goto queue; - case WLAN_CATEGORY_MESH_PATH_SEL: - if (!mesh_path_sel_is_hwmp(sdata)) + if (mesh_action_is_path_sel(mgmt) && + (!mesh_path_sel_is_hwmp(sdata))) break; goto queue; } @@ -2534,17 +2615,17 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx) CALL_RXH(ieee80211_rx_h_decrypt) CALL_RXH(ieee80211_rx_h_check_more_data) + CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll) CALL_RXH(ieee80211_rx_h_sta_process) CALL_RXH(ieee80211_rx_h_defragment) - CALL_RXH(ieee80211_rx_h_ps_poll) CALL_RXH(ieee80211_rx_h_michael_mic_verify) /* must be after MMIC verify so header is counted in MPDU mic */ - CALL_RXH(ieee80211_rx_h_remove_qos_control) - CALL_RXH(ieee80211_rx_h_amsdu) #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&rx->sdata->vif)) CALL_RXH(ieee80211_rx_h_mesh_fwding); #endif + CALL_RXH(ieee80211_rx_h_remove_qos_control) + CALL_RXH(ieee80211_rx_h_amsdu) CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_ctrl); CALL_RXH(ieee80211_rx_h_mgmt_check) @@ -2686,7 +2767,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) { if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) && - !ieee80211_is_beacon(hdr->frame_control)) + !ieee80211_is_beacon(hdr->frame_control) && + !(ieee80211_is_action(hdr->frame_control) && + sdata->vif.p2p)) return 0; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } @@ -2791,7 +2874,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, if (ieee80211_is_data(fc)) { prev_sta = NULL; - for_each_sta_info(local, hdr->addr2, sta, tmp) { + for_each_sta_info_rx(local, hdr->addr2, sta, tmp) { if (!prev_sta) { prev_sta = sta; continue; @@ -2835,7 +2918,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, continue; } - rx.sta = sta_info_get_bss(prev, hdr->addr2); + rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); rx.sdata = prev; ieee80211_prepare_and_rx_handle(&rx, skb, false); @@ -2843,7 +2926,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, } if (prev) { - rx.sta = sta_info_get_bss(prev, hdr->addr2); + rx.sta = sta_info_get_bss_rx(prev, hdr->addr2); rx.sdata = prev; if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) |