void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, bool peer_accepting_plinks) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; rcu_read_lock(); sta = sta_info_get(sdata, hw_addr); if (!sta) { rcu_read_unlock(); sta = mesh_plink_alloc(sdata, hw_addr, rates); if (!sta) return; if (sta_info_insert_rcu(sta)) { rcu_read_unlock(); return; } } sta->last_rx = jiffies; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks) mesh_plink_open(sta); rcu_read_unlock(); }
static int ieee80211_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; struct ieee80211_sub_if_data *sdata; struct ieee80211_supported_band *sband; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; rcu_read_lock(); sta = sta_info_get(local, sdata->u.sta.bssid); if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate; else rate->value = 0; rcu_read_unlock(); if (!sta) return -ENODEV; rate->value *= 100000; return 0; }
int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid, enum ieee80211_back_parties initiator) { struct ieee80211_local *local = hw_to_local(hw); struct sta_info *sta; int ret = 0; if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; if (tid >= STA_TID_NUM) return -EINVAL; rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { rcu_read_unlock(); return -ENOENT; } ret = __ieee80211_stop_tx_ba_session(sta, tid, initiator); rcu_read_unlock(); return ret; }
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct iw_statistics *wstats = &local->wstats; struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta = NULL; rcu_read_lock(); if (sdata->vif.type == NL80211_IFTYPE_STATION || sdata->vif.type == NL80211_IFTYPE_ADHOC) sta = sta_info_get(local, sdata->u.sta.bssid); if (!sta) { wstats->discard.fragment = 0; wstats->discard.misc = 0; wstats->qual.qual = 0; wstats->qual.level = 0; wstats->qual.noise = 0; wstats->qual.updated = IW_QUAL_ALL_INVALID; } else { wstats->qual.level = sta->last_signal; wstats->qual.qual = sta->last_qual; wstats->qual.noise = sta->last_noise; wstats->qual.updated = local->wstats_flags; } rcu_read_unlock(); return wstats; }
static int ieee80211_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; struct ieee80211_sub_if_data *sdata; struct ieee80211_supported_band *sband; sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->vif.type == IEEE80211_IF_TYPE_STA) sta = sta_info_get(local, sdata->u.sta.bssid); else return -EOPNOTSUPP; if (!sta) return -ENODEV; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; if (sta->txrate_idx < sband->n_bitrates) rate->value = sband->bitrates[sta->txrate_idx].bitrate; else rate->value = 0; rate->value *= 100000; return 0; }
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; size_t baselen; u32 last_hop_metric; struct sta_info *sta; /* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) return; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { rcu_read_unlock(); return; } rcu_read_unlock(); baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, len - baselen, &elems); if (elems.preq) { if (elems.preq_len != 37) /* Right now we support just 1 destination and no AE */ return; last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq, MPATH_PREQ); if (last_hop_metric) hwmp_preq_frame_process(sdata, mgmt, elems.preq, last_hop_metric); } if (elems.prep) { if (elems.prep_len != 31) /* Right now we support no AE */ return; last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.prep, MPATH_PREP); if (last_hop_metric) hwmp_prep_frame_process(sdata, mgmt, elems.prep, last_hop_metric); } if (elems.perr) { if (elems.perr_len != 15) /* Right now we support only one destination per PERR */ return; hwmp_perr_frame_process(sdata, mgmt, elems.perr); } if (elems.rann) hwmp_rann_frame_process(sdata, mgmt, elems.rann); }
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sta_info *sta; u8 *state; trace_api_start_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); #endif return; } rcu_read_lock(); sta = sta_info_get(sdata, ra); if (!sta) { rcu_read_unlock(); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); #endif return; } state = &sta->ampdu_mlme.tid_state_tx[tid]; spin_lock_bh(&sta->lock); if (WARN_ON(!(*state & HT_ADDBA_REQUESTED_MSK))) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", *state); #endif spin_unlock_bh(&sta->lock); rcu_read_unlock(); return; } if (WARN_ON(*state & HT_ADDBA_DRV_READY_MSK)) goto out; *state |= HT_ADDBA_DRV_READY_MSK; if (*state == HT_AGG_STATE_OPERATIONAL) ieee80211_agg_tx_operational(local, sta, tid); out: spin_unlock_bh(&sta->lock); rcu_read_unlock(); }
static struct ieee80211_rate * rate_control_simple_get_rate(void *priv, struct net_device *dev, struct sk_buff *skb, struct rate_control_extra *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_hw_mode *mode = extra->mode; struct sta_info *sta; int rateidx, nonerp_idx; u16 fc; memset(extra, 0, sizeof(*extra)); fc = le16_to_cpu(hdr->frame_control); if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || (hdr->addr1[0] & 0x01)) { /* Send management frames and broadcast/multicast data using * lowest rate. */ /* TODO: this could probably be improved.. */ return rate_control_lowest_rate(local, mode); } sta = sta_info_get(local, hdr->addr1); if (!sta) return rate_control_lowest_rate(local, mode); sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) sta->txrate = sdata->bss->force_unicast_rateidx; rateidx = sta->txrate; if (rateidx >= mode->num_rates) rateidx = mode->num_rates - 1; sta->last_txrate = rateidx; nonerp_idx = rateidx; while (nonerp_idx > 0 && ((mode->rates[nonerp_idx].flags & IEEE80211_RATE_ERP) || !(mode->rates[nonerp_idx].flags & IEEE80211_RATE_SUPPORTED) || !(sta->supp_rates & BIT(nonerp_idx)))) nonerp_idx--; extra->nonerp = &mode->rates[nonerp_idx]; sta_info_put(sta); return &mode->rates[rateidx]; }
static void rate_control_pid_get_rate(void *priv, struct net_device *dev, struct ieee80211_supported_band *sband, struct sk_buff *skb, struct rate_selection *sel) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int rateidx; u16 fc; rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); /* Send management frames and broadcast/multicast data using lowest * rate. */ fc = le16_to_cpu(hdr->frame_control); if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || is_multicast_ether_addr(hdr->addr1) || !sta) { sel->rate = rate_lowest(local, sband, sta); rcu_read_unlock(); return; } /* If a forced rate is in effect, select it. */ sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) sta->txrate_idx = sdata->bss->force_unicast_rateidx; rateidx = sta->txrate_idx; if (rateidx >= sband->n_bitrates) rateidx = sband->n_bitrates - 1; sta->last_txrate_idx = rateidx; rcu_read_unlock(); sel->rate = &sband->bitrates[rateidx]; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_tx_rate( &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events, rateidx, sband->bitrates[rateidx].bitrate); #endif }
void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct tid_ampdu_tx *tid_tx; trace_api_start_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); #endif return; } mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); #endif return; } mutex_lock(&sta->ampdu_mlme.mtx); tid_tx = sta->ampdu_mlme.tid_tx[tid]; if (WARN_ON(!tid_tx)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "addBA was not requested!\n"); #endif goto unlock; } if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))) goto unlock; if (test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) ieee80211_agg_tx_operational(local, sta, tid); unlock: mutex_unlock(&sta->ampdu_mlme.mtx); mutex_unlock(&local->sta_mtx); }
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) { struct ieee802_11_elems elems; size_t baselen; u8 *orig_addr;//ymj u32 last_hop_metric; struct sta_info *sta; /* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) return; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { rcu_read_unlock(); return; } rcu_read_unlock(); baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, len - baselen, &elems); if (elems.preq) { if (elems.preq_len != 37)//ymj /* Right now we support just 1 destination and no AE */ return; orig_addr=PREQ_IE_ORIG_ADDR(elems.preq); if(/*blank*/)//调用mesh_flood_detect函数,判断是否是SUPPRESSED的节点 { last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq, MPATH_PREQ); if (last_hop_metric) hwmp_preq_frame_process(sdata, mgmt, elems.preq, last_hop_metric); }else{ //to be modified //是SUPPRESSED节点,在debug信息中提示该节点MAC } }
void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; rcu_read_lock(); sta = sta_info_get(sdata, hw_addr); if (!sta) { rcu_read_unlock(); /* */ if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, elems->ie_start, elems->total_len, GFP_KERNEL); else sta = mesh_plink_alloc(sdata, hw_addr, rates, elems); if (!sta) return; if (sta_info_insert_rcu(sta)) { rcu_read_unlock(); return; } } sta->last_rx = jiffies; sta->sta.supp_rates[local->hw.conf.channel->band] = rates; if (mesh_peer_accepts_plinks(elems) && sta->plink_state == NL80211_PLINK_LISTEN && sdata->u.mesh.accepting_plinks && sdata->u.mesh.mshcfg.auto_open_plinks && rssi_threshold_check(sta, sdata)) mesh_plink_open(sta); rcu_read_unlock(); }
/** * hwmp_route_info_get - Update routing info to originator and transmitter * * @sdata: local mesh subif * @mgmt: mesh management frame * @hwmp_ie: hwmp information element (PREP or PREQ) * * This function updates the path routing information to the originator and the * transmitter of a HWMP PREQ or PREP fram. * * Returns: metric to frame originator or 0 if the frame should not be further * processed * * Notes: this function is the only place (besides user-provided info) where * path routing information is updated. */ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, u8 *hwmp_ie) { struct ieee80211_local *local = sdata->local; struct mesh_path *mpath; struct sta_info *sta; bool fresh_info; u8 *orig_addr, *ta; u32 orig_dsn, orig_metric; unsigned long orig_lifetime, exp_time; u32 last_hop_metric, new_metric; bool process = true; u8 action = mgmt->u.action.u.mesh_action.action_code; rcu_read_lock(); sta = sta_info_get(local, mgmt->sa); if (!sta) { rcu_read_unlock(); return 0; } last_hop_metric = airtime_link_metric_get(local, sta); /* Update and check originator routing info */ fresh_info = true; switch (action) { case MPATH_PREQ: orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie); orig_dsn = PREQ_IE_ORIG_DSN(hwmp_ie); orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); orig_metric = PREQ_IE_METRIC(hwmp_ie); break; case MPATH_PREP: /* Originator here refers to the MP that was the destination in * the Path Request. The draft refers to that MP as the * destination address, even though usually it is the origin of * the PREP frame. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie); orig_dsn = PREP_IE_ORIG_DSN(hwmp_ie); orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); orig_metric = PREP_IE_METRIC(hwmp_ie); break; default: rcu_read_unlock(); return 0; } new_metric = orig_metric + last_hop_metric; if (new_metric < orig_metric) new_metric = MAX_METRIC; exp_time = TU_TO_EXP_TIME(orig_lifetime); if (memcmp(orig_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) { /* This MP is the originator, we are not interested in this * frame, except for updating transmitter's path info. */ process = false; fresh_info = false; } else { mpath = mesh_path_lookup(orig_addr, sdata); if (mpath) { spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_FIXED) fresh_info = false; else if ((mpath->flags & MESH_PATH_ACTIVE) && (mpath->flags & MESH_PATH_DSN_VALID)) { if (DSN_GT(mpath->dsn, orig_dsn) || (mpath->dsn == orig_dsn && action == MPATH_PREQ && new_metric > mpath->metric)) { process = false; fresh_info = false; } } } else { mesh_path_add(orig_addr, sdata); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->flags |= MESH_PATH_DSN_VALID; mpath->metric = new_metric; mpath->dsn = orig_dsn; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); mesh_path_tx_pending(mpath); /* draft says preq_id should be saved to, but there does * not seem to be any use for it, skipping by now */ } else spin_unlock_bh(&mpath->state_lock); } /* Update and check transmitter routing info */ ta = mgmt->sa; if (memcmp(orig_addr, ta, ETH_ALEN) == 0) fresh_info = false; else { fresh_info = true; mpath = mesh_path_lookup(ta, sdata); if (mpath) { spin_lock_bh(&mpath->state_lock); if ((mpath->flags & MESH_PATH_FIXED) || ((mpath->flags & MESH_PATH_ACTIVE) && (last_hop_metric > mpath->metric))) fresh_info = false; } else { mesh_path_add(ta, sdata); mpath = mesh_path_lookup(ta, sdata); if (!mpath) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->flags &= ~MESH_PATH_DSN_VALID; mpath->metric = last_hop_metric; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); mesh_path_tx_pending(mpath); } else spin_unlock_bh(&mpath->state_lock); } rcu_read_unlock(); return process ? new_metric : 0; }
static void rate_control_pid_tx_status(void *priv, struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_sub_if_data *sdata; struct rc_pid_info *pinfo = priv; struct sta_info *sta; struct rc_pid_sta_info *spinfo; unsigned long period; sta = sta_info_get(local, hdr->addr1); if (!sta) return; /* Don't update the state if we're not controlling the rate. */ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { sta->txrate = sdata->bss->max_ratectrl_rateidx; return; } /* Ignore all frames that were sent with a different rate than the rate * we currently advise mac80211 to use. */ if (status->control.rate != &local->oper_hw_mode->rates[sta->txrate]) goto ignore; spinfo = sta->rate_ctrl_priv; spinfo->tx_num_xmit++; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_tx_status(&spinfo->events, status); #endif /* We count frames that totally failed to be transmitted as two bad * frames, those that made it out but had some retries as one good and * one bad frame. */ if (status->excessive_retries) { spinfo->tx_num_failed += 2; spinfo->tx_num_xmit++; } else if (status->retry_count) { spinfo->tx_num_failed++; spinfo->tx_num_xmit++; } if (status->excessive_retries) { sta->tx_retry_failed++; sta->tx_num_consecutive_failures++; sta->tx_num_mpdu_fail++; } else { sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; sta->last_ack_rssi[2] = status->ack_signal; sta->tx_num_consecutive_failures = 0; sta->tx_num_mpdu_ok++; } sta->tx_retry_count += status->retry_count; sta->tx_num_mpdu_fail += status->retry_count; /* Update PID controller state. */ period = (HZ * pinfo->sampling_period + 500) / 1000; if (!period) period = 1; if (time_after(jiffies, spinfo->last_sample + period)) rate_control_pid_sample(pinfo, local, sta); ignore: sta_info_put(sta); }
/* Indicate which queue to use. */ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct sta_info *sta = NULL; u32 sta_flags = 0; const u8 *ra = NULL; bool qos = false; if (local->hw.queues < 4 || skb->len < 6) { skb->priority = 0; /* required for correct WPA/11i MIC */ return min_t(u16, local->hw.queues - 1, ieee802_1d_to_ac[skb->priority]); } rcu_read_lock(); switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: rcu_read_lock(); sta = rcu_dereference(sdata->u.vlan.sta); if (sta) sta_flags = get_sta_flags(sta); rcu_read_unlock(); if (sta) break; case NL80211_IFTYPE_AP: ra = skb->data; break; case NL80211_IFTYPE_WDS: ra = sdata->u.wds.remote_addr; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: /* * XXX: This is clearly broken ... but already was before, * because ieee80211_fill_mesh_addresses() would clear A1 * except for multicast addresses. */ break; #endif case NL80211_IFTYPE_STATION: ra = sdata->u.mgd.bssid; break; case NL80211_IFTYPE_ADHOC: ra = skb->data; break; default: break; } if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) sta_flags = get_sta_flags(sta); } if (sta_flags & WLAN_STA_WME) qos = true; rcu_read_unlock(); if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ return ieee802_1d_to_ac[skb->priority]; } /* use the data classifier to determine what 802.1d tag the * data frame has */ skb->priority = cfg80211_classify8021d(skb); return ieee80211_downgrade_queue(local, skb); }
void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) { struct ieee80211_local *local = hw_to_local(hw); struct sta_info *sta; u8 *state; if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); #endif return; } #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n", ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); #endif rcu_read_unlock(); return; } state = &sta->ampdu_mlme.tid_state_tx[tid]; /* NOTE: no need to use sta->lock in this state check, as * ieee80211_stop_tx_ba_session will let only one stop call to * pass through per sta/tid */ if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); #endif rcu_read_unlock(); return; } if (*state & HT_AGG_STATE_INITIATOR_MSK) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); spin_lock_bh(&sta->lock); spin_lock(&local->ampdu_lock); ieee80211_agg_splice_packets(local, sta, tid); *state = HT_AGG_STATE_IDLE; /* from now on packets are no longer put onto sta->pending */ kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; ieee80211_agg_splice_finish(local, sta, tid); spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); rcu_read_unlock(); }
static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, struct ieee80211_rann_ie *rann) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct mesh_path *mpath; u8 ttl, flags, hopcount; u8 *orig_addr; u32 orig_sn, metric, metric_txsta, interval; bool root_is_gate; ttl = rann->rann_ttl; flags = rann->rann_flags; root_is_gate = !!(flags & RANN_FLAG_IS_GATE); orig_addr = rann->rann_addr; orig_sn = le32_to_cpu(rann->rann_seq); interval = le32_to_cpu(rann->rann_interval); hopcount = rann->rann_hopcount; hopcount++; metric = le32_to_cpu(rann->rann_metric); /* Ignore our own RANNs */ if (ether_addr_equal(orig_addr, sdata->vif.addr)) return; mhwmp_dbg(sdata, "received RANN from %pM via neighbour %pM (is_gate=%d)\n", orig_addr, mgmt->sa, root_is_gate); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) { rcu_read_unlock(); return; } metric_txsta = airtime_link_metric_get(local, sta); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { mesh_path_add(orig_addr, sdata); mpath = mesh_path_lookup(orig_addr, sdata); if (!mpath) { rcu_read_unlock(); sdata->u.mesh.mshstats.dropped_frames_no_route++; return; } } if (!(SN_LT(mpath->sn, orig_sn)) && !(mpath->sn == orig_sn && metric < mpath->rann_metric)) { rcu_read_unlock(); return; } if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || (time_after(jiffies, mpath->last_preq_to_root + root_path_confirmation_jiffies(sdata)) || time_before(jiffies, mpath->last_preq_to_root))) && !(mpath->flags & MESH_PATH_FIXED) && (ttl != 0)) { mhwmp_dbg(sdata, "time to refresh root mpath %pM\n", orig_addr); mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); mpath->last_preq_to_root = jiffies; } mpath->sn = orig_sn; mpath->rann_metric = metric + metric_txsta; mpath->is_root = true; /* Recording RANNs sender address to send individually * addressed PREQs destined for root mesh STA */ memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN); if (root_is_gate) mesh_path_add_gate(mpath); if (ttl <= 1) { ifmsh->mshstats.dropped_frames_ttl++; rcu_read_unlock(); return; } ttl--; if (ifmsh->mshcfg.dot11MeshForwarding) { mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr, cpu_to_le32(orig_sn), 0, NULL, 0, broadcast_addr, hopcount, ttl, cpu_to_le32(interval), cpu_to_le32(metric + metric_txsta), 0, sdata); } rcu_read_unlock(); }
int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) { struct ieee80211_local *local = hw_to_local(hw); struct sta_info *sta; struct ieee80211_sub_if_data *sdata; u8 *state; int ret = 0; u16 start_seq_num; if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) return -EINVAL; #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n", ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find the station\n"); #endif ret = -ENOENT; goto unlock; } /* * The aggregation code is not prepared to handle * anything but STA/AP due to the BSSID handling. * IBSS could work in the code but isn't supported * by drivers or the standard. */ if (sta->sdata->vif.type != NL80211_IFTYPE_STATION && sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sta->sdata->vif.type != NL80211_IFTYPE_AP) { ret = -EINVAL; goto unlock; } if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Suspend in progress. " "Denying BA session request\n"); #endif ret = -EINVAL; goto unlock; } spin_lock_bh(&sta->lock); spin_lock(&local->ampdu_lock); sdata = sta->sdata; /* we have tried too many times, receiver does not want A-MPDU */ if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { ret = -EBUSY; goto err_unlock_sta; } state = &sta->ampdu_mlme.tid_state_tx[tid]; /* check if the TID is not in aggregation flow already */ if (*state != HT_AGG_STATE_IDLE) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA request denied - session is not " "idle on tid %u\n", tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ ret = -EAGAIN; goto err_unlock_sta; } /* * While we're asking the driver about the aggregation, * stop the AC queue so that we don't have to worry * about frames that came in while we were doing that, * which would require us to put them to the AC pending * afterwards which just makes the code more complex. */ ieee80211_stop_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); /* prepare A-MPDU MLME for Tx aggregation */ sta->ampdu_mlme.tid_tx[tid] = kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); if (!sta->ampdu_mlme.tid_tx[tid]) { #ifdef CONFIG_MAC80211_HT_DEBUG if (net_ratelimit()) printk(KERN_ERR "allocate tx mlme to tid %d failed\n", tid); #endif ret = -ENOMEM; goto err_wake_queue; } skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); /* Tx timer */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = sta_addba_resp_timer_expired; sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the * call back right away, it must see that the flow has begun */ *state |= HT_ADDBA_REQUESTED_MSK; start_seq_num = sta->tid_seq[tid]; ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_START, &sta->sta, tid, &start_seq_num); if (ret) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "BA request denied - HW unavailable for" " tid %d\n", tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ *state = HT_AGG_STATE_IDLE; goto err_free; } /* Driver vetoed or OKed, but we can take packets again now */ ieee80211_wake_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ sta->ampdu_mlme.dialog_token_allocator++; sta->ampdu_mlme.tid_tx[tid]->dialog_token = sta->ampdu_mlme.dialog_token_allocator; sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; ieee80211_send_addba_request(sta->sdata, ra, tid, sta->ampdu_mlme.tid_tx[tid]->dialog_token, sta->ampdu_mlme.tid_tx[tid]->ssn, 0x40, 5000); sta->ampdu_mlme.addba_req_num[tid]++; /* activate the timer for the recipient's addBA response */ sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL; add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); #endif goto unlock; err_free: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; err_wake_queue: ieee80211_wake_queue_by_reason( &local->hw, ieee80211_ac_from_tid(tid), IEEE80211_QUEUE_STOP_REASON_AGGREGATION); err_unlock_sta: spin_unlock(&local->ampdu_lock); spin_unlock_bh(&sta->lock); unlock: rcu_read_unlock(); return ret; }
u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) { struct ieee80211_master_priv *mpriv = netdev_priv(dev); struct ieee80211_local *local = mpriv->local; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct sta_info *sta; u16 queue; u8 tid; queue = classify80211(local, skb); if (unlikely(queue >= local->hw.queues)) queue = local->hw.queues - 1; if (info->flags & IEEE80211_TX_CTL_REQUEUE) { rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (sta) { struct ieee80211_hw *hw = &local->hw; int ampdu_queue = sta->tid_to_tx_q[tid]; if ((ampdu_queue < ieee80211_num_queues(hw)) && test_bit(ampdu_queue, local->queue_pool)) { queue = ampdu_queue; info->flags |= IEEE80211_TX_CTL_AMPDU; } else { info->flags &= ~IEEE80211_TX_CTL_AMPDU; } } rcu_read_unlock(); return queue; } /* Now we know the 1d priority, fill in the QoS header if * there is one. */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy = 0; tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (local->wifi_wme_noack_test) ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; /* qos header is 2 bytes, second reserved */ *p++ = ack_policy | tid; *p = 0; rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); if (sta) { int ampdu_queue = sta->tid_to_tx_q[tid]; struct ieee80211_hw *hw = &local->hw; if ((ampdu_queue < ieee80211_num_queues(hw)) && test_bit(ampdu_queue, local->queue_pool)) { queue = ampdu_queue; info->flags |= IEEE80211_TX_CTL_AMPDU; } else { info->flags &= ~IEEE80211_TX_CTL_AMPDU; } } rcu_read_unlock(); } return queue; }
struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, enum ieee80211_key_alg alg, int idx, size_t key_len, const u8 *key_data) { struct ieee80211_key *key; BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); if (!key) return NULL; /* * Default to software encryption; we'll later upload the * key to the hardware if possible. */ key->conf.flags = 0; key->flags = 0; key->conf.alg = alg; key->conf.keyidx = idx; key->conf.keylen = key_len; memcpy(key->conf.key, key_data, key_len); key->local = sdata->local; key->sdata = sdata; key->sta = sta; if (alg == ALG_CCMP) { /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. */ key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); if (!key->u.ccmp.tfm) { ieee80211_key_free(key); return NULL; } } ieee80211_debugfs_key_add(key->local, key); /* remove key first */ if (sta) ieee80211_key_free(sta->key); else ieee80211_key_free(sdata->keys[idx]); if (sta) { ieee80211_debugfs_key_sta_link(key, sta); /* * some hardware cannot handle TKIP with QoS, so * we indicate whether QoS could be in use. */ if (sta->flags & WLAN_STA_WME) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; } else { if (sdata->type == IEEE80211_IF_TYPE_STA) { struct sta_info *ap; /* same here, the AP could be using QoS */ ap = sta_info_get(key->local, key->sdata->u.sta.bssid); if (ap) { if (ap->flags & WLAN_STA_WME) key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; sta_info_put(ap); } } } /* enable hwaccel if appropriate */ if (netif_running(key->sdata->dev)) ieee80211_key_enable_hw_accel(key); if (sta) rcu_assign_pointer(sta->key, key); else rcu_assign_pointer(sdata->keys[idx], key); list_add(&key->list, &sdata->key_list); return key; }
/* * join the information into the hdr with the correct ABPS_info int ABPS_info_response(struct sock *sk, struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_status *status) */ int ABPS_info_response(struct sock *sk, struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, struct ieee80211_sub_if_data *sdata) { int success = 0; u8 acked = -1; u8 retry_count = -1; unsigned long filtered_count = -1; struct ieee80211_local *local = hw_to_local(hw); struct ABPS_info *packet_info; int i; /* se era richiesto l'ack */ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { /* e l'ack e' arrivato */ if (info->flags & IEEE80211_TX_STAT_ACK) success=1; } /* VEDERE SE RIMETTERE A POSTO else { if (!(info->excessive_retries)) success=2; } */ if (info->flags & IEEE80211_TX_CTL_NO_ACK) { /* ack not required */ acked= ACK_NOT_REQ; } else if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { /* filtered frame */ acked= ACK_FILTERED; } else if (info->flags & IEEE80211_TX_STAT_ACK) { /* frame acked */ struct sta_info *sta; acked = ACK; retry_count = 0; /* modifiche per kernel da 2.6.27 in poi */ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { /* the HW cannot have attempted that rate */ if (i >= hw->max_rates) { ; } else retry_count += info->status.rates[i].count; } if (retry_count > 0) retry_count--; sta = sta_info_get(sdata, hdr->addr1); if (sta) filtered_count = sta->tx_filtered_count; else filtered_count = ACK_ERROR ; } else { /* frame not acked, ack not recieved */ struct sta_info *sta; acked = ACK_NOT; retry_count = 0; /* modifiche per kernel da 2.6.27 in poi */ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { /* the HW cannot have attempted that rate */ if (i >= hw->max_rates) { ; } else retry_count += info->status.rates[i].count; } if (retry_count > 0) retry_count--; sta = sta_info_get(sdata, hdr->addr1); if (sta) filtered_count = sta->tx_filtered_count; else filtered_count = ACK_ERROR; } packet_info = ABPS_info_search(hdr->seq_ctrl); if (packet_info != 0) { packet_info->datagram_info.acked = acked; packet_info->datagram_info.retry_count = retry_count; packet_info->rx_time = CURRENT_TIME; /* questa chiamata a funzione required ... potrebbe essere eliminata * perche' viene fatta gia' fuori prima */ /* mando la notifica al socket */ /*NOTA ABPS DIE KURO: adesso estrae solo data_len, offset e more_frags, comunque non potevo estrearre dati da udp in caso di frammentazione, l'indirizzo ip invece non e' invece mai propagato fino all'utente */ if(!packet_info->is_ipv6) { ip_local_error_notify(sk, success, packet_info->datagram_info.ip_id, packet_info->datagram_info.fragment_data_len, packet_info->datagram_info.fragment_offset, packet_info->datagram_info.more_fragment, packet_info->datagram_info.retry_count ); } else { ipv6_local_error_notify(sk,success,packet_info->datagram_info.ip_id,packet_info->datagram_info.retry_count); } printk(KERN_NOTICE "ip_local_error notify performed!. \n"); #ifdef ABPS_DEBUG ABPS_info_take_response(packet_info); #endif ABPS_info_remove(packet_info); return(1); } return(0); }
static void rate_control_pid_tx_status(void *priv, struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_sub_if_data *sdata; struct rc_pid_info *pinfo = priv; struct sta_info *sta; struct rc_pid_sta_info *spinfo; unsigned long period; struct ieee80211_supported_band *sband; rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; if (!sta) goto unlock; /* Don't update the state if we're not controlling the rate. */ sdata = sta->sdata; if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { sta->txrate_idx = sdata->bss->max_ratectrl_rateidx; goto unlock; } /* Ignore all frames that were sent with a different rate than the rate * we currently advise mac80211 to use. */ if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx]) goto unlock; spinfo = sta->rate_ctrl_priv; spinfo->tx_num_xmit++; #ifdef CONFIG_MAC80211_DEBUGFS rate_control_pid_event_tx_status(&spinfo->events, status); #endif /* We count frames that totally failed to be transmitted as two bad * frames, those that made it out but had some retries as one good and * one bad frame. */ if (status->excessive_retries) { spinfo->tx_num_failed += 2; spinfo->tx_num_xmit++; } else if (status->retry_count) { spinfo->tx_num_failed++; spinfo->tx_num_xmit++; } if (status->excessive_retries) { sta->tx_retry_failed++; sta->tx_num_consecutive_failures++; sta->tx_num_mpdu_fail++; } else { sta->tx_num_consecutive_failures = 0; sta->tx_num_mpdu_ok++; } sta->tx_retry_count += status->retry_count; sta->tx_num_mpdu_fail += status->retry_count; /* Update PID controller state. */ period = (HZ * pinfo->sampling_period + 500) / 1000; if (!period) period = 1; if (time_after(jiffies, spinfo->last_sample + period)) rate_control_pid_sample(pinfo, local, sta); unlock: rcu_read_unlock(); }
/* Indicate which queue to use. */ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct sta_info *sta = NULL; const u8 *ra = NULL; bool qos = false; struct mac80211_qos_map *qos_map; if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) { skb->priority = 0; /* required for correct WPA/11i MIC */ return 0; } rcu_read_lock(); switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); if (sta) { qos = test_sta_flag(sta, WLAN_STA_WME); break; } case NL80211_IFTYPE_AP: ra = skb->data; break; case NL80211_IFTYPE_WDS: ra = sdata->u.wds.remote_addr; break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: qos = true; break; #endif case NL80211_IFTYPE_STATION: ra = sdata->u.mgd.bssid; break; case NL80211_IFTYPE_ADHOC: ra = skb->data; break; default: break; } if (!sta && ra && !is_multicast_ether_addr(ra)) { sta = sta_info_get(sdata, ra); if (sta) qos = test_sta_flag(sta, WLAN_STA_WME); } rcu_read_unlock(); if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ return IEEE80211_AC_BE; } if (skb->protocol == sdata->control_port_protocol) { skb->priority = 7; return ieee80211_downgrade_queue(sdata, skb); } /* use the data classifier to determine what 802.1d tag the * data frame has */ rcu_read_lock(); qos_map = rcu_dereference(sdata->qos_map); skb->priority = cfg80211_classify8021d(skb, qos_map ? &qos_map->qos_map : NULL); rcu_read_unlock(); return ieee80211_downgrade_queue(sdata, skb); }
static void rate_control_simple_tx_status(void *priv, struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct sta_info *sta; struct sta_rate_control *srctrl; sta = sta_info_get(local, hdr->addr1); if (!sta) return; srctrl = sta->rate_ctrl_priv; srctrl->tx_num_xmit++; if (status->excessive_retries) { srctrl->tx_num_failures++; sta->tx_retry_failed++; sta->tx_num_consecutive_failures++; sta->tx_num_mpdu_fail++; } else { sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; sta->last_ack_rssi[2] = status->ack_signal; sta->tx_num_consecutive_failures = 0; sta->tx_num_mpdu_ok++; } sta->tx_retry_count += status->retry_count; sta->tx_num_mpdu_fail += status->retry_count; if (time_after(jiffies, srctrl->last_rate_change + RATE_CONTROL_INTERVAL) && srctrl->tx_num_xmit > RATE_CONTROL_MIN_TX) { u32 per_failed; srctrl->last_rate_change = jiffies; per_failed = (100 * sta->tx_num_mpdu_fail) / (sta->tx_num_mpdu_fail + sta->tx_num_mpdu_ok); /* TODO: calculate average per_failed to make adjusting * parameters easier */ #if 0 if (net_ratelimit()) { printk(KERN_DEBUG "MPDU fail=%d ok=%d per_failed=%d\n", sta->tx_num_mpdu_fail, sta->tx_num_mpdu_ok, per_failed); } #endif /* * XXX: Make these configurable once we have an * interface to the rate control algorithms */ if (per_failed > RATE_CONTROL_NUM_DOWN) { rate_control_rate_dec(local, sta); } else if (per_failed < RATE_CONTROL_NUM_UP) { rate_control_rate_inc(local, sta); } srctrl->tx_avg_rate_sum += status->control.rate->rate; srctrl->tx_avg_rate_num++; srctrl->tx_num_failures = 0; srctrl->tx_num_xmit = 0; } else if (sta->tx_num_consecutive_failures >= RATE_CONTROL_EMERG_DEC) { rate_control_rate_dec(local, sta); } if (srctrl->avg_rate_update + 60 * HZ < jiffies) { srctrl->avg_rate_update = jiffies; if (srctrl->tx_avg_rate_num > 0) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "%s: STA %s Average rate: " "%d (%d/%d)\n", dev->name, print_mac(mac, sta->addr), srctrl->tx_avg_rate_sum / srctrl->tx_avg_rate_num, srctrl->tx_avg_rate_sum, srctrl->tx_avg_rate_num); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ srctrl->tx_avg_rate_sum = 0; srctrl->tx_avg_rate_num = 0; } } sta_info_put(sta); }
static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta_addr, int idx, int alg, int remove, int set_tx_key, const u8 *_key, size_t key_len) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct ieee80211_key *key; int err; if (idx < 0 || idx >= NUM_DEFAULT_KEYS) { printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n", sdata->dev->name, idx); return -EINVAL; } if (remove) { rcu_read_lock(); err = 0; if (is_broadcast_ether_addr(sta_addr)) { key = sdata->keys[idx]; } else { sta = sta_info_get(local, sta_addr); if (!sta) { err = -ENOENT; goto out_unlock; } key = sta->key; } ieee80211_key_free(key); } else { key = ieee80211_key_alloc(alg, idx, key_len, _key); if (!key) return -ENOMEM; sta = NULL; err = 0; rcu_read_lock(); if (!is_broadcast_ether_addr(sta_addr)) { set_tx_key = 0; /* * According to the standard, the key index of a * pairwise key must be zero. However, some AP are * broken when it comes to WEP key indices, so we * work around this. */ if (idx != 0 && alg != ALG_WEP) { ieee80211_key_free(key); err = -EINVAL; goto out_unlock; } sta = sta_info_get(local, sta_addr); if (!sta) { ieee80211_key_free(key); err = -ENOENT; goto out_unlock; } } if (alg == ALG_WEP && key_len != LEN_WEP40 && key_len != LEN_WEP104) { ieee80211_key_free(key); err = -EINVAL; goto out_unlock; } ieee80211_key_link(key, sdata, sta); if (set_tx_key || (!sta && !sdata->default_key && key)) ieee80211_set_default_key(sdata, idx); } out_unlock: rcu_read_unlock(); return err; }
static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; struct sta_info *sta; u64 t_t, t_r; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); /* standard mentions only beacons */ if (stype != IEEE80211_STYPE_BEACON) return; /* * Get time when timestamp field was received. If we don't * have rx timestamps, then use current tsf as an approximation. * drv_get_tsf() must be called before entering the rcu-read * section. */ if (ieee80211_have_rx_timestamp(rx_status)) t_r = ieee80211_calculate_rx_timestamp(local, rx_status, 24 + 12 + elems->total_len + FCS_LEN, 24); else t_r = drv_get_tsf(local, sdata); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) goto no_sync; /* check offset sync conditions (13.13.2.2.1) * * TODO also sync to * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors */ if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", sta->sta.addr); goto no_sync; } /* Timing offset calculation (see 13.13.2.2.2) */ t_t = le64_to_cpu(mgmt->u.beacon.timestamp); sta->mesh->t_offset = t_t - t_r; if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset; msync_dbg(sdata, "STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n", sta->sta.addr, (long long) sta->mesh->t_offset, (long long) sta->mesh->t_offset_setpoint, (long long) t_clockdrift); if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { msync_dbg(sdata, "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", sta->sta.addr, (long long) t_clockdrift); clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); goto no_sync; } spin_lock_bh(&ifmsh->sync_offset_lock); if (t_clockdrift > ifmsh->sync_offset_clockdrift_max) ifmsh->sync_offset_clockdrift_max = t_clockdrift; spin_unlock_bh(&ifmsh->sync_offset_lock); } else { sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN; set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); msync_dbg(sdata, "STA %pM : offset was invalid, t_offset=%lld\n", sta->sta.addr, (long long) sta->mesh->t_offset); } no_sync: rcu_read_unlock(); }
void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct tid_ampdu_tx *tid_tx; trace_api_stop_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); #endif return; } #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n", ra, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, ra); if (!sta) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "Could not find station: %pM\n", ra); #endif goto unlock; } mutex_lock(&sta->ampdu_mlme.mtx); spin_lock_bh(&sta->lock); tid_tx = sta->ampdu_mlme.tid_tx[tid]; if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { #ifdef CONFIG_MAC80211_HT_DEBUG printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); #endif goto unlock_sta; } if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR) ieee80211_send_delba(sta->sdata, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); /* * When we get here, the TX path will not be lockless any more wrt. * aggregation, since the OPERATIONAL bit has long been cleared. * Thus it will block on getting the lock, if it occurs. So if we * stop the queue now, we will not get any more packets, and any * that might be being processed will wait for us here, thereby * guaranteeing that no packets go to the tid_tx pending queue any * more. */ ieee80211_agg_splice_packets(local, tid_tx, tid); /* future packets must not find the tid_tx struct any more */ rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); ieee80211_agg_splice_finish(local, tid); call_rcu(&tid_tx->rcu_head, kfree_tid_tx); unlock_sta: spin_unlock_bh(&sta->lock); mutex_unlock(&sta->ampdu_mlme.mtx); unlock: mutex_unlock(&local->sta_mtx); }
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_local *local = sdata->local; struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; enum ieee80211_self_protected_actioncode ftype; size_t baselen; bool deactivated, matches_local = true; u8 ie_len; u8 *baseaddr; __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { [NL80211_PLINK_LISTEN] = "LISTEN", [NL80211_PLINK_OPN_SNT] = "OPN-SNT", [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", [NL80211_PLINK_ESTAB] = "ESTAB", [NL80211_PLINK_HOLDING] = "HOLDING", [NL80211_PLINK_BLOCKED] = "BLOCKED" }; #endif /* */ if (len < IEEE80211_MIN_ACTION_SIZE + 3) return; if (is_multicast_ether_addr(mgmt->da)) { mpl_dbg("Mesh plink: ignore frame from multicast address"); return; } baseaddr = mgmt->u.action.u.self_prot.variable; baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; if (mgmt->u.action.u.self_prot.action_code == WLAN_SP_MESH_PEERING_CONFIRM) { baseaddr += 4; baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peering) { mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { mpl_dbg("Mesh plink: can't establish link with secure peer\n"); return; } ftype = mgmt->u.action.u.self_prot.action_code; ie_len = elems.peering_len; if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 && ie_len != 8)) { mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", ftype, ie_len); return; } if (ftype != WLAN_SP_MESH_PEERING_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { mpl_dbg("Mesh plink: missing necessary ie\n"); return; } /* */ memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); rcu_read_unlock(); return; } if (ftype == WLAN_SP_MESH_PEERING_OPEN && !rssi_threshold_check(sta, sdata)) { mpl_dbg("Mesh plink: %pM does not meet rssi threshold\n", mgmt->sa); rcu_read_unlock(); return; } if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); return; } if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { rcu_read_unlock(); return; } /* */ event = PLINK_UNDEFINED; if (ftype != WLAN_SP_MESH_PEERING_CLOSE && (!mesh_matches_local(&elems, sdata))) { matches_local = false; switch (ftype) { case WLAN_SP_MESH_PEERING_OPEN: event = OPN_RJCT; break; case WLAN_SP_MESH_PEERING_CONFIRM: event = CNF_RJCT; break; default: break; } } if (!sta && !matches_local) { rcu_read_unlock(); reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); llid = 0; mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, mgmt->sa, llid, plid, reason); return; } else if (!sta) { /* */ u32 rates; rcu_read_unlock(); if (!mesh_plink_free_count(sdata)) { mpl_dbg("Mesh plink error: no more free plinks\n"); return; } rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems); if (!sta) { mpl_dbg("Mesh plink error: plink table full\n"); return; } if (sta_info_insert_rcu(sta)) { rcu_read_unlock(); return; } event = OPN_ACPT; spin_lock_bh(&sta->lock); } else if (matches_local) { spin_lock_bh(&sta->lock); switch (ftype) { case WLAN_SP_MESH_PEERING_OPEN: if (!mesh_plink_free_count(sdata) || (sta->plid && sta->plid != plid)) event = OPN_IGNR; else event = OPN_ACPT; break; case WLAN_SP_MESH_PEERING_CONFIRM: if (!mesh_plink_free_count(sdata) || (sta->llid != llid || sta->plid != plid)) event = CNF_IGNR; else event = CNF_ACPT; break; case WLAN_SP_MESH_PEERING_CLOSE: if (sta->plink_state == NL80211_PLINK_ESTAB) /* */ event = CLS_ACPT; else if (sta->plid != plid) event = CLS_IGNR; else if (ie_len == 7 && sta->llid != llid) event = CLS_IGNR; else event = CLS_ACPT; break; default: mpl_dbg("Mesh plink: unknown frame subtype\n"); spin_unlock_bh(&sta->lock); rcu_read_unlock(); return; } } else { spin_lock_bh(&sta->lock); } mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", mgmt->sa, mplstates[sta->plink_state], le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), event); reason = 0; switch (sta->plink_state) { /* */ case NL80211_PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, sta->sta.addr, llid, 0, 0); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: /* */ sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: sta->plink_state = NL80211_PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; spin_unlock_bh(&sta->lock); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: del_timer(&sta->plink_timer); sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); case CLS_ACPT: if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: del_timer(&sta->plink_timer); sta->plink_state = NL80211_PLINK_ESTAB; spin_unlock_bh(&sta->lock); mesh_plink_inc_estab_count(sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); sta->reason = reason; deactivated = __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); if (deactivated) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case NL80211_PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) sta->ignore_plink_timer = 1; mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: case CNF_ACPT: case OPN_RJCT: case CNF_RJCT: llid = sta->llid; reason = sta->reason; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); break; default: spin_unlock_bh(&sta->lock); } break; default: /* */ spin_unlock_bh(&sta->lock); break; } rcu_read_unlock(); }
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { struct ieee80211_local *local = sdata->local; struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; enum plink_frame_type ftype; size_t baselen; u8 ie_len; u8 *baseaddr; __le16 plid, llid, reason; #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { [PLINK_LISTEN] = "LISTEN", [PLINK_OPN_SNT] = "OPN-SNT", [PLINK_OPN_RCVD] = "OPN-RCVD", [PLINK_CNF_RCVD] = "CNF_RCVD", [PLINK_ESTAB] = "ESTAB", [PLINK_HOLDING] = "HOLDING", [PLINK_BLOCKED] = "BLOCKED" }; #endif /* need action_code, aux */ if (len < IEEE80211_MIN_ACTION_SIZE + 3) return; if (is_multicast_ether_addr(mgmt->da)) { mpl_dbg("Mesh plink: ignore frame from multicast address"); return; } baseaddr = mgmt->u.action.u.plink_action.variable; baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { baseaddr += 4; baselen += 4; } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peer_link) { mpl_dbg("Mesh plink: missing necessary peer link ie\n"); return; } ftype = mgmt->u.action.u.plink_action.action_code; ie_len = elems.peer_link_len; if ((ftype == PLINK_OPEN && ie_len != 6) || (ftype == PLINK_CONFIRM && ie_len != 8) || (ftype == PLINK_CLOSE && ie_len != 8 && ie_len != 10)) { mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", ftype, ie_len); return; } if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { mpl_dbg("Mesh plink: missing necessary ie\n"); return; } /* Note the lines below are correct, the llid in the frame is the plid * from the point of view of this host. */ memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 10)) memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta && ftype != PLINK_OPEN) { mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); rcu_read_unlock(); return; } if (sta && sta->plink_state == PLINK_BLOCKED) { rcu_read_unlock(); return; } /* Now we will figure out the appropriate event... */ event = PLINK_UNDEFINED; if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) { switch (ftype) { case PLINK_OPEN: event = OPN_RJCT; break; case PLINK_CONFIRM: event = CNF_RJCT; break; case PLINK_CLOSE: /* avoid warning */ break; } spin_lock_bh(&sta->lock); } else if (!sta) { /* ftype == PLINK_OPEN */ u32 rates; rcu_read_unlock(); if (!mesh_plink_free_count(sdata)) { mpl_dbg("Mesh plink error: no more free plinks\n"); return; } rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); sta = mesh_plink_alloc(sdata, mgmt->sa, rates); if (!sta) { mpl_dbg("Mesh plink error: plink table full\n"); return; } if (sta_info_insert_rcu(sta)) { rcu_read_unlock(); return; } event = OPN_ACPT; spin_lock_bh(&sta->lock); } else { spin_lock_bh(&sta->lock); switch (ftype) { case PLINK_OPEN: if (!mesh_plink_free_count(sdata) || (sta->plid && sta->plid != plid)) event = OPN_IGNR; else event = OPN_ACPT; break; case PLINK_CONFIRM: if (!mesh_plink_free_count(sdata) || (sta->llid != llid || sta->plid != plid)) event = CNF_IGNR; else event = CNF_ACPT; break; case PLINK_CLOSE: if (sta->plink_state == PLINK_ESTAB) /* Do not check for llid or plid. This does not * follow the standard but since multiple plinks * per sta are not supported, it is necessary in * order to avoid a livelock when MP A sees an * establish peer link to MP B but MP B does not * see it. This can be caused by a timeout in * B's peer link establishment or B beign * restarted. */ event = CLS_ACPT; else if (sta->plid != plid) event = CLS_IGNR; else if (ie_len == 7 && sta->llid != llid) event = CLS_IGNR; else event = CLS_ACPT; break; default: mpl_dbg("Mesh plink: unknown frame subtype\n"); spin_unlock_bh(&sta->lock); rcu_read_unlock(); return; } } mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", mgmt->sa, mplstates[sta->plink_state], le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), event); reason = 0; switch (sta->plink_state) { /* spin_unlock as soon as state is updated at each case */ case PLINK_LISTEN: switch (event) { case CLS_ACPT: mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: sta->plink_state = PLINK_OPN_RCVD; sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, 0, 0); mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: /* retry timer is left untouched */ sta->plink_state = PLINK_OPN_RCVD; sta->plid = plid; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: sta->plink_state = PLINK_CNF_RCVD; if (!mod_plink_timer(sta, dot11MeshConfirmTimeout(sdata))) sta->ignore_plink_timer = true; spin_unlock_bh(&sta->lock); break; default: spin_unlock_bh(&sta->lock); break; } break; case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, plid, 0); break; case CNF_ACPT: del_timer(&sta->plink_timer); sta->plink_state = PLINK_ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->lock); mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; default: spin_unlock_bh(&sta->lock); break; } break; case PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; sta->plink_state = PLINK_HOLDING; if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) sta->ignore_plink_timer = true; llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: del_timer(&sta->plink_timer); sta->plink_state = PLINK_ESTAB; mesh_plink_inc_estab_count(sdata); spin_unlock_bh(&sta->lock); mpl_dbg("Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = cpu_to_le16(MESH_CLOSE_RCVD); sta->reason = reason; __mesh_plink_deactivate(sta); sta->plink_state = PLINK_HOLDING; llid = sta->llid; mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; case OPN_ACPT: llid = sta->llid; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, plid, 0); break; default: spin_unlock_bh(&sta->lock); break; } break; case PLINK_HOLDING: switch (event) { case CLS_ACPT: if (del_timer(&sta->plink_timer)) sta->ignore_plink_timer = 1; mesh_plink_fsm_restart(sta); spin_unlock_bh(&sta->lock); break; case OPN_ACPT: case CNF_ACPT: case OPN_RJCT: case CNF_RJCT: llid = sta->llid; reason = sta->reason; spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, reason); break; default: spin_unlock_bh(&sta->lock); } break; default: /* should not get here, PLINK_BLOCKED is dealt with at the * beggining of the function */ spin_unlock_bh(&sta->lock); break; } rcu_read_unlock(); }
/** * hwmp_route_info_get - Update routing info to originator and transmitter * * @sdata: local mesh subif * @mgmt: mesh management frame * @hwmp_ie: hwmp information element (PREP or PREQ) * @action: type of hwmp ie * * This function updates the path routing information to the originator and the * transmitter of a HWMP PREQ or PREP frame. * * Returns: metric to frame originator or 0 if the frame should not be further * processed * * Notes: this function is the only place (besides user-provided info) where * path routing information is updated. */ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, const u8 *hwmp_ie, enum mpath_frame_type action) { struct ieee80211_local *local = sdata->local; struct mesh_path *mpath; struct sta_info *sta; bool fresh_info; const u8 *orig_addr, *ta; u32 orig_sn, orig_metric; unsigned long orig_lifetime, exp_time; u32 last_hop_metric, new_metric; bool process = true; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); if (!sta) { rcu_read_unlock(); return 0; } last_hop_metric = airtime_link_metric_get(local, sta); /* Update and check originator routing info */ fresh_info = true; switch (action) { case MPATH_PREQ: orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie); orig_sn = PREQ_IE_ORIG_SN(hwmp_ie); orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); orig_metric = PREQ_IE_METRIC(hwmp_ie); break; case MPATH_PREP: /* Originator here refers to the MP that was the target in the * Path Request. We divert from the nomenclature in the draft * so that we can easily use a single function to gather path * information from both PREQ and PREP frames. */ orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie); orig_sn = PREP_IE_TARGET_SN(hwmp_ie); orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); orig_metric = PREP_IE_METRIC(hwmp_ie); break; default: rcu_read_unlock(); return 0; } new_metric = orig_metric + last_hop_metric; if (new_metric < orig_metric) new_metric = MAX_METRIC; exp_time = TU_TO_EXP_TIME(orig_lifetime); if (ether_addr_equal(orig_addr, sdata->vif.addr)) { /* This MP is the originator, we are not interested in this * frame, except for updating transmitter's path info. */ process = false; fresh_info = false; } else { mpath = mesh_path_lookup(sdata, orig_addr); if (mpath) { spin_lock_bh(&mpath->state_lock); if (mpath->flags & MESH_PATH_FIXED) fresh_info = false; else if ((mpath->flags & MESH_PATH_ACTIVE) && (mpath->flags & MESH_PATH_SN_VALID)) { if (SN_GT(mpath->sn, orig_sn) || (mpath->sn == orig_sn && new_metric >= mpath->metric)) { process = false; fresh_info = false; } } else if (!(mpath->flags & MESH_PATH_ACTIVE)) { bool have_sn, newer_sn, bounced; have_sn = mpath->flags & MESH_PATH_SN_VALID; newer_sn = have_sn && SN_GT(orig_sn, mpath->sn); bounced = have_sn && (SN_DELTA(orig_sn, mpath->sn) > MAX_SANE_SN_DELTA); if (!have_sn || newer_sn) { /* if SN is newer than what we had * then we can take it */; } else if (bounced) { /* if SN is way different than what * we had then assume the other side * rebooted or restarted */; } else { process = false; fresh_info = false; } } } else { mpath = mesh_path_add(sdata, orig_addr); if (IS_ERR(mpath)) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->flags |= MESH_PATH_SN_VALID; mpath->metric = new_metric; mpath->sn = orig_sn; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); /* init it at a low value - 0 start is tricky */ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); /* draft says preq_id should be saved to, but there does * not seem to be any use for it, skipping by now */ } else spin_unlock_bh(&mpath->state_lock); } /* Update and check transmitter routing info */ ta = mgmt->sa; if (ether_addr_equal(orig_addr, ta)) fresh_info = false; else { fresh_info = true; mpath = mesh_path_lookup(sdata, ta); if (mpath) { spin_lock_bh(&mpath->state_lock); if ((mpath->flags & MESH_PATH_FIXED) || ((mpath->flags & MESH_PATH_ACTIVE) && (last_hop_metric > mpath->metric))) fresh_info = false; } else { mpath = mesh_path_add(sdata, ta); if (IS_ERR(mpath)) { rcu_read_unlock(); return 0; } spin_lock_bh(&mpath->state_lock); } if (fresh_info) { mesh_path_assign_nexthop(mpath, sta); mpath->metric = last_hop_metric; mpath->exp_time = time_after(mpath->exp_time, exp_time) ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); /* init it at a low value - 0 start is tricky */ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); } else spin_unlock_bh(&mpath->state_lock); } rcu_read_unlock(); return process ? new_metric : 0; }