These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / mac80211 / mesh_hwmp.c
index 214e63b..c6be0b4 100644 (file)
 
 #define MAX_PREQ_QUEUE_LEN     64
 
-/* Destination only */
-#define MP_F_DO        0x1
-/* Reply and forward */
-#define MP_F_RF        0x2
-/* Unknown Sequence Number */
-#define MP_F_USN    0x01
-/* Reason code Present */
-#define MP_F_RCODE  0x02
-
 static void mesh_queue_preq(struct mesh_path *, u8);
 
 static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
@@ -79,6 +70,12 @@ static inline u16 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 MAX_SANE_SN_DELTA 32
+
+static inline u32 SN_DELTA(u32 x, u32 y)
+{
+       return x >= y ? x - y : y - x;
+}
 
 #define net_traversal_jiffies(s) \
        msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@@ -279,15 +276,10 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
        *pos++ = ttl;
        /* number of destinations */
        *pos++ = 1;
-       /*
-        * flags bit, bit 1 is unset if we know the sequence number and
-        * bit 2 is set if we have a reason code
+       /* Flags field has AE bit only as defined in
+        * sec 8.4.2.117 IEEE802.11-2012
         */
        *pos = 0;
-       if (!target_sn)
-               *pos |= MP_F_USN;
-       if (target_rcode)
-               *pos |= MP_F_RCODE;
        pos++;
        memcpy(pos, target, ETH_ALEN);
        pos += ETH_ALEN;
@@ -316,8 +308,9 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
        failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
 
        /* moving average, scaled to 100 */
-       sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed);
-       if (sta->fail_avg > 95)
+       sta->mesh->fail_avg =
+               ((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
+       if (sta->mesh->fail_avg > 95)
                mesh_plink_broken(sta);
 }
 
@@ -333,15 +326,15 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
        u32 tx_time, estimated_retx;
        u64 result;
 
-       if (sta->fail_avg >= 100)
+       if (sta->mesh->fail_avg >= 100)
                return MAX_METRIC;
 
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
+       sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo);
        rate = cfg80211_calculate_bitrate(&rinfo);
        if (WARN_ON(!rate))
                return MAX_METRIC;
 
-       err = (sta->fail_avg << ARITH_SHIFT) / 100;
+       err = (sta->mesh->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.
@@ -441,6 +434,26 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                                        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);
@@ -510,14 +523,14 @@ 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 orig_metric)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct mesh_path *mpath = NULL;
        const u8 *target_addr, *orig_addr;
        const u8 *da;
        u8 target_flags, ttl, flags;
-       u32 orig_sn, target_sn, lifetime, orig_metric;
+       u32 orig_sn, target_sn, lifetime, target_metric;
        bool reply = false;
        bool forward = true;
        bool root_is_gate;
@@ -528,7 +541,6 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
        target_sn = PREQ_IE_TARGET_SN(preq_elem);
        orig_sn = PREQ_IE_ORIG_SN(preq_elem);
        target_flags = PREQ_IE_TARGET_F(preq_elem);
-       orig_metric = metric;
        /* Proactive PREQ gate announcements */
        flags = PREQ_IE_FLAGS(preq_elem);
        root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
@@ -539,7 +551,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                mhwmp_dbg(sdata, "PREQ is for us\n");
                forward = false;
                reply = true;
-               metric = 0;
+               target_metric = 0;
                if (time_after(jiffies, ifmsh->last_sn_update +
                                        net_traversal_jiffies(sdata)) ||
                    time_before(jiffies, ifmsh->last_sn_update)) {
@@ -556,7 +568,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                                reply = true;
                                target_addr = sdata->vif.addr;
                                target_sn = ++ifmsh->sn;
-                               metric = 0;
+                               target_metric = 0;
                                ifmsh->last_sn_update = jiffies;
                        }
                        if (root_is_gate)
@@ -571,15 +583,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                                        SN_LT(mpath->sn, target_sn)) {
                                mpath->sn = target_sn;
                                mpath->flags |= MESH_PATH_SN_VALID;
-                       } else if ((!(target_flags & MP_F_DO)) &&
+                       } else if ((!(target_flags & IEEE80211_PREQ_TO_FLAG)) &&
                                        (mpath->flags & MESH_PATH_ACTIVE)) {
                                reply = true;
-                               metric = mpath->metric;
+                               target_metric = mpath->metric;
                                target_sn = mpath->sn;
-                               if (target_flags & MP_F_RF)
-                                       target_flags |= MP_F_DO;
-                               else
-                                       forward = false;
+                               /* Case E2 of sec 13.10.9.3 IEEE 802.11-2012*/
+                               target_flags |= IEEE80211_PREQ_TO_FLAG;
                        }
                }
                rcu_read_unlock();
@@ -593,7 +603,8 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                        mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr,
                                               orig_sn, 0, target_addr,
                                               target_sn, mgmt->sa, 0, ttl,
-                                              lifetime, metric, 0, sdata);
+                                              lifetime, target_metric, 0,
+                                              sdata);
                } else {
                        ifmsh->mshstats.dropped_frames_ttl++;
                }
@@ -619,13 +630,12 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) {
                        target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
                        target_sn = PREQ_IE_TARGET_SN(preq_elem);
-                       metric = orig_metric;
                }
 
                mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
                                       orig_sn, target_flags, target_addr,
                                       target_sn, da, hopcount, ttl, lifetime,
-                                      metric, preq_id, sdata);
+                                      orig_metric, preq_id, sdata);
                if (!is_multicast_ether_addr(da))
                        ifmsh->mshstats.fwded_unicast++;
                else
@@ -737,9 +747,12 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
                if (mpath->flags & MESH_PATH_ACTIVE &&
                    ether_addr_equal(ta, sta->sta.addr) &&
                    (!(mpath->flags & MESH_PATH_SN_VALID) ||
-                   SN_GT(target_sn, mpath->sn))) {
+                   SN_GT(target_sn, mpath->sn)  || target_sn == 0)) {
                        mpath->flags &= ~MESH_PATH_ACTIVE;
-                       mpath->sn = target_sn;
+                       if (target_sn != 0)
+                               mpath->sn = target_sn;
+                       else
+                               mpath->sn += 1;
                        spin_unlock_bh(&mpath->state_lock);
                        if (!ifmsh->mshcfg.dot11MeshForwarding)
                                goto endperr;
@@ -854,7 +867,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee802_11_elems elems;
        size_t baselen;
-       u32 last_hop_metric;
+       u32 path_metric;
        struct sta_info *sta;
 
        /* need action_code */
@@ -863,7 +876,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
        sta = sta_info_get(sdata, mgmt->sa);
-       if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) {
+       if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
                rcu_read_unlock();
                return;
        }
@@ -877,21 +890,21 @@ 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;
-               last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
-                                                     MPATH_PREQ);
-               if (last_hop_metric)
+               path_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
+                                                 MPATH_PREQ);
+               if (path_metric)
                        hwmp_preq_frame_process(sdata, mgmt, elems.preq,
-                                               last_hop_metric);
+                                               path_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)
+               path_metric = hwmp_route_info_get(sdata, mgmt, elems.prep,
+                                                 MPATH_PREP);
+               if (path_metric)
                        hwmp_prep_frame_process(sdata, mgmt, elems.prep,
-                                               last_hop_metric);
+                                               path_metric);
        }
        if (elems.perr) {
                if (elems.perr_len != 15)
@@ -975,7 +988,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct mesh_preq_queue *preq_node;
        struct mesh_path *mpath;
-       u8 ttl, target_flags;
+       u8 ttl, target_flags = 0;
        const u8 *da;
        u32 lifetime;
 
@@ -1034,9 +1047,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
        }
 
        if (preq_node->flags & PREQ_Q_F_REFRESH)
-               target_flags = MP_F_DO;
+               target_flags |= IEEE80211_PREQ_TO_FLAG;
        else
-               target_flags = MP_F_RF;
+               target_flags &= ~IEEE80211_PREQ_TO_FLAG;
 
        spin_unlock_bh(&mpath->state_lock);
        da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
@@ -1177,7 +1190,9 @@ void mesh_path_timer(unsigned long data)
                spin_unlock_bh(&mpath->state_lock);
                mesh_queue_preq(mpath, 0);
        } else {
-               mpath->flags = 0;
+               mpath->flags &= ~(MESH_PATH_RESOLVING |
+                                 MESH_PATH_RESOLVED |
+                                 MESH_PATH_REQ_QUEUED);
                mpath->exp_time = jiffies;
                spin_unlock_bh(&mpath->state_lock);
                if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {