[PATCH v2] mac80211: mesh: update target SN when new origin SN RX'ed This patch is to address a HWMP mesh path selection bug found by Bob Copeland. If a STA sends a PREQ, it may reach its target as multiple PREQs via PREQ propagation. Previously, if the STA is the target, it would increment its own SN if it was within net_traversal_jiffies of the last update *and* use this as the target SN in the PREP. The PREQs arriving just after (less than net_traversal_jiffies later) would not meet the criteria for SN update and would use the older SN. Later, these paths would be ignored during path selection since an older SN indicates a staler path.
In the IEEE 802.11-2012 standard, section 13.10.8.3 indicates that the STA shall "shall update its own HWMP SN to maximum (current HWMP SN, target HWMP SN in the PREQ) + 1 immediately before it generates a PREP in response to a PREQ". However, since this would result in all of the PREPs in reponse to the single PREQ having different target SN's, here we increment the STA's SN if we detect a new origin SN. As a result, now when the PREQ's are RX'ed at the target, they will each receive the same target SN. Note that net_traversal_jiffies should only applies to updating a STA's origin SN as per section 13.10.8.6. It only applies to when the station is an originating station generating a PREQ. Signed-off-by: Colleen Twitty <[email protected]> --- changes: renaming old_sn to old_orig_sn, use SN_GT instead of LT (Bob Copeland) add macro for SN max which respects their arithmetic (Bob Copeland) whitespace (Bob Copeland) commit message which describes bug (Thomas Pederson) net/mac80211/mesh_hwmp.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index f951468..27f2c677 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -79,6 +79,7 @@ static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae) #define MSEC_TO_TU(x) (x*1000/1024) #define SN_GT(x, y) ((s32)(y - x) < 0) #define SN_LT(x, y) ((s32)(x - y) < 0) +#define SN_MAX(x, y) (SN_GT((x), (y)) ? (x) : (y)) #define net_traversal_jiffies(s) \ msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime) @@ -511,7 +512,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, - const u8 *preq_elem, u32 metric) + const u8 *preq_elem, u32 metric, + bool chk_old_orig_sn, u32 old_sn) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct mesh_path *mpath = NULL; @@ -541,12 +543,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, forward = false; reply = true; metric = 0; - if (time_after(jiffies, ifmsh->last_sn_update + - net_traversal_jiffies(sdata)) || - time_before(jiffies, ifmsh->last_sn_update)) { - target_sn = ++ifmsh->sn; - ifmsh->last_sn_update = jiffies; - } + + /* 802.11-2012 Sec. 13.10.8.3 HWMP sequence numbering calls for + * the target HWMP seq number to be incremented after each + * processed PREQ, but that would result in all but the last + * PREP metric info to be ignored. Instead, we only increment + * the target SN when we detect that the originator SN has + * changed. This results in all the PREPs generated in response + * to a single PREQ to have the same seq num, and, therefore, + * for the metrics for the different paths to be taken into + * account. + */ + + if (chk_old_orig_sn && SN_GT(orig_sn, old_sn)) + ifmsh->sn = SN_MAX(ifmsh->sn, target_sn) + 1; + + target_sn = ifmsh->sn; } else if (is_broadcast_ether_addr(target_addr) && (target_flags & IEEE80211_PREQ_TO_FLAG)) { rcu_read_lock(); @@ -856,6 +868,10 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, size_t baselen; u32 last_hop_metric; struct sta_info *sta; + struct mesh_path *mpath = NULL; + u32 old_orig_sn = 0; + const u8 *orig_addr; + bool chk_old_orig_sn = false; /* need action_code */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) @@ -877,11 +893,23 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, if (elems.preq_len != 37) /* Right now we support just 1 destination and no AE */ return; + + orig_addr = PREQ_IE_ORIG_ADDR(elems.preq); + + rcu_read_lock(); + mpath = mesh_path_lookup(sdata, orig_addr); + if (mpath) { + chk_old_orig_sn = true; + old_orig_sn = mpath->sn; + } + rcu_read_unlock(); + 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); + last_hop_metric, + chk_old_orig_sn, old_orig_sn); } if (elems.prep) { if (elems.prep_len != 31) -- 1.8.4.3 _______________________________________________ Devel mailing list [email protected] http://lists.open80211s.org/cgi-bin/mailman/listinfo/devel
