/* Headroom is not adjusted. Caller should ensure that skb has sufficient
* headroom in case the frame is encrypted. */ staticvoid prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
/** * mesh_path_error_tx - Sends a PERR mesh management frame * * @sdata: local mesh subif * @ttl: allowed remaining hops * @target: broken destination * @target_sn: SN of the broken destination * @target_rcode: reason code for this PERR * @ra: node this frame is addressed to * * Note: This function may be called with driver locks taken that the driver * also acquires in the TX path. To avoid a deadlock we don't transmit the * frame directly but add it to the pending queue instead. * * Returns: 0 on success
*/ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
u8 ttl, const u8 *target, u32 target_sn,
u16 target_rcode, const u8 *ra)
{ struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_mgmt *mgmt;
u8 *pos, ie_len; int hdr_len = offsetofend(struct ieee80211_mgmt,
u.action.u.mesh_action);
if (time_before(jiffies, ifmsh->next_perr)) return -EAGAIN;
/* moving average, scaled to 100. * feed failure as 100 and success as 0
*/
ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) >
LINK_FAIL_THRESH)
mesh_plink_broken(sta);
/* use rate info set by the driver directly if present */ if (st->n_rates)
rinfo = sta->deflink.tx_stats.last_rate_info; else
sta_set_rate_info_tx(sta, &sta->deflink.tx_stats.last_rate, &rinfo);
u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta)
{ /* This should be adjusted for each device */ int device_constant = 1 << ARITH_SHIFT; int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT; int s_unit = 1 << ARITH_SHIFT; int rate, err;
u32 tx_time, estimated_retx;
u64 result; unsignedlong fail_avg =
ewma_mesh_fail_avg_read(&sta->mesh->fail_avg);
if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) return MAX_METRIC;
/* Try to get rate based on HW/SW RC algorithm. * Rate is returned in units of Kbps, correct this * to comply with airtime calculation units * Round up in case we get rate < 100Kbps
*/
rate = DIV_ROUND_UP(sta_get_expected_throughput(sta), 100);
if (rate) {
err = 0;
} else { if (fail_avg > LINK_FAIL_THRESH) return MAX_METRIC;
rate = ewma_mesh_tx_rate_avg_read(&sta->mesh->tx_rate_avg); if (WARN_ON(!rate)) return MAX_METRIC;
err = (fail_avg << ARITH_SHIFT) / 100;
}
/* bitrate is in units of 100 Kbps, while we need rate in units of * 1Mbps. This will be corrected on tx_time computation.
*/
tx_time = (device_constant + 10 * test_frame_len / rate);
estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
result = ((u64)tx_time * estimated_retx) >> (2 * ARITH_SHIFT); return (u32)result;
}
/* Check that the first metric is at least 10% better than the second one */ staticbool is_metric_better(u32 x, u32 y)
{ return (x < y) && (x < (y - x / 10));
}
/** * 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; unsignedlong orig_lifetime, exp_time;
u32 last_hop_metric, new_metric; bool flush_mpath = false; bool process = true;
u8 hopcount;
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);
hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1; 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);
hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1; 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; elseif ((mpath->flags & MESH_PATH_ACTIVE) &&
(mpath->flags & MESH_PATH_SN_VALID)) { if (SN_GT(mpath->sn, orig_sn) ||
(mpath->sn == orig_sn &&
(rcu_access_pointer(mpath->next_hop) !=
sta ?
!is_metric_better(new_metric, mpath->metric) :
new_metric >= mpath->metric))) {
process = false;
fresh_info = false;
}
} elseif (!(mpath->flags & MESH_PATH_ACTIVE)) { bool have_sn, newer_sn, bounced;
if (!have_sn || newer_sn) { /* if SN is newer than what we had
* then we can take it */
} elseif (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) { if (rcu_access_pointer(mpath->next_hop) != sta) {
mpath->path_change_count++;
flush_mpath = true;
}
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;
mpath->hop_count = hopcount;
mesh_path_activate(mpath);
spin_unlock_bh(&mpath->state_lock); if (flush_mpath)
mesh_fast_tx_flush_mpath(mpath);
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;
/* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) return;
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa); if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
rcu_read_unlock(); return;
}
rcu_read_unlock();
baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt;
elems = ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, false, NULL); if (!elems) return;
if (elems->preq) { if (elems->preq_len != 37) /* Right now we support just 1 destination and no AE */ goto free;
path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
MPATH_PREQ); if (path_metric)
hwmp_preq_frame_process(sdata, mgmt, elems->preq,
path_metric);
} if (elems->prep) { if (elems->prep_len != 31) /* Right now we support no AE */ goto free;
path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
MPATH_PREP); if (path_metric)
hwmp_prep_frame_process(sdata, mgmt, elems->prep,
path_metric);
} if (elems->perr) { if (elems->perr_len != 15) /* Right now we support only one destination per PERR */ goto free;
hwmp_perr_frame_process(sdata, mgmt, elems->perr);
} if (elems->rann)
hwmp_rann_frame_process(sdata, mgmt, elems->rann);
free:
kfree(elems);
}
/** * mesh_queue_preq - queue a PREQ to a given destination * * @mpath: mesh path to discover * @flags: special attributes of the PREQ to be sent * * Locking: the function must be called from within a rcu read lock block. *
*/ staticvoid mesh_queue_preq(struct mesh_path *mpath, u8 flags)
{ struct ieee80211_sub_if_data *sdata = mpath->sdata; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_preq_queue *preq_node;
preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); if (!preq_node) {
mhwmp_dbg(sdata, "could not allocate PREQ node\n"); return;
}
spin_lock_bh(&ifmsh->mesh_preq_queue_lock); if (ifmsh->preq_queue_len == MAX_PREQ_QUEUE_LEN) {
spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
kfree(preq_node); if (printk_ratelimit())
mhwmp_dbg(sdata, "PREQ node queue full\n"); return;
}
if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata)))
wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
elseif (time_before(jiffies, ifmsh->last_preq)) { /* avoid long wait if did not send preqs for a long time * and jiffies wrapped around
*/
ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
} else
mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq +
min_preq_int_jiff(sdata));
}
/** * mesh_nexthop_resolve - lookup next hop; conditionally start path discovery * * @sdata: network subif the frame will be sent through * @skb: 802.11 frame to be sent * * Lookup next hop for given skb and start path discovery if no * forwarding information is found. * * Returns: 0 if the next hop was found and -ENOENT if the frame was queued. * skb is freed here if no mpath could be allocated.
*/ int mesh_nexthop_resolve(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct mesh_path *mpath; struct sk_buff *skb_to_free = NULL;
u8 *target_addr = hdr->addr3;
/* Nulls are only sent to peers for PS and should be pre-addressed */ if (ieee80211_is_qos_nullfunc(hdr->frame_control)) return 0;
/* Allow injected packets to bypass mesh routing */ if (info->control.flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP) return 0;
if (!mesh_nexthop_lookup(sdata, skb)) return 0;
/* no nexthop found, start resolving */
mpath = mesh_path_lookup(sdata, target_addr); if (!mpath) {
mpath = mesh_path_add(sdata, target_addr); if (IS_ERR(mpath)) {
mesh_path_discard_frame(sdata, skb); return PTR_ERR(mpath);
}
}
if (!(mpath->flags & MESH_PATH_RESOLVING) &&
mesh_path_sel_is_hwmp(sdata))
mesh_queue_preq(mpath, PREQ_Q_F_START);
if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
skb_to_free = skb_dequeue(&mpath->frame_queue);
/** * mesh_nexthop_lookup_nolearn - try to set next hop without path discovery * @skb: 802.11 frame to be sent * @sdata: network subif the frame will be sent through * * Check if the meshDA (addr3) of a unicast frame is a direct neighbor. * And if so, set the RA (addr1) to it to transmit to this node directly, * avoiding PREQ/PREP path discovery. * * Returns: 0 if the next hop was found and -ENOENT otherwise.
*/ staticint mesh_nexthop_lookup_nolearn(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct sta_info *sta;
if (is_multicast_ether_addr(hdr->addr1)) return -ENOENT;
rcu_read_lock();
sta = sta_info_get(sdata, hdr->addr3);
/** * mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling * this function is considered "using" the associated mpath, so preempt a path * refresh if this mpath expires soon. * * @sdata: network subif the frame will be sent through * @skb: 802.11 frame to be sent * * Returns: 0 if the next hop was found. Nonzero otherwise.
*/ int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath; struct sta_info *next_hop; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u8 *target_addr = hdr->addr3;
if (ifmsh->mshcfg.dot11MeshNolearn &&
!mesh_nexthop_lookup_nolearn(sdata, skb)) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.