mac80211: implement the proactive PREP generation
[profile/ivi/kernel-x86-ivi.git] / net / mac80211 / mesh_hwmp.c
index 1c6f3d0..35e3acb 100644 (file)
@@ -15,7 +15,7 @@
 
 #ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG
 #define mhwmp_dbg(fmt, args...) \
-       printk(KERN_DEBUG "Mesh HWMP (%s): " fmt "\n", sdata->name, ##args)
+       pr_debug("Mesh HWMP (%s): " fmt "\n", sdata->name, ##args)
 #else
 #define mhwmp_dbg(fmt, args...)   do { (void)(0); } while (0)
 #endif
@@ -86,8 +86,8 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)
 #define PERR_IE_TARGET_RCODE(x)        u16_field_get(x, 13, 0)
 
 #define MSEC_TO_TU(x) (x*1000/1024)
-#define SN_GT(x, y) ((long) (y) - (long) (x) < 0)
-#define SN_LT(x, y) ((long) (x) - (long) (y) < 0)
+#define SN_GT(x, y) ((s32)(y - x) < 0)
+#define SN_LT(x, y) ((s32)(x - y) < 0)
 
 #define net_traversal_jiffies(s) \
        msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@@ -303,7 +303,7 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
 }
 
 void ieee80211s_update_metric(struct ieee80211_local *local,
-               struct sta_info *stainfo, struct sk_buff *skb)
+               struct sta_info *sta, struct sk_buff *skb)
 {
        struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -315,15 +315,14 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
        failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
 
        /* moving average, scaled to 100 */
-       stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed);
-       if (stainfo->fail_avg > 95)
-               mesh_plink_broken(stainfo);
+       sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed);
+       if (sta->fail_avg > 95)
+               mesh_plink_broken(sta);
 }
 
 static u32 airtime_link_metric_get(struct ieee80211_local *local,
                                   struct sta_info *sta)
 {
-       struct ieee80211_supported_band *sband;
        struct rate_info rinfo;
        /* This should be adjusted for each device */
        int device_constant = 1 << ARITH_SHIFT;
@@ -333,8 +332,6 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
        u32 tx_time, estimated_retx;
        u64 result;
 
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
        if (sta->fail_avg >= 100)
                return MAX_METRIC;
 
@@ -422,7 +419,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                new_metric = MAX_METRIC;
        exp_time = TU_TO_EXP_TIME(orig_lifetime);
 
-       if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0) {
+       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.
                 */
@@ -472,7 +469,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 
        /* Update and check transmitter routing info */
        ta = mgmt->sa;
-       if (compare_ether_addr(orig_addr, ta) == 0)
+       if (ether_addr_equal(orig_addr, ta))
                fresh_info = false;
        else {
                fresh_info = true;
@@ -519,10 +516,11 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
        struct mesh_path *mpath = NULL;
        u8 *target_addr, *orig_addr;
        const u8 *da;
-       u8 target_flags, ttl;
-       u32 orig_sn, target_sn, lifetime;
+       u8 target_flags, ttl, flags;
+       u32 orig_sn, target_sn, lifetime, orig_metric;
        bool reply = false;
        bool forward = true;
+       bool root_is_gate;
 
        /* Update target SN, if present */
        target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
@@ -530,10 +528,14 @@ 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);
 
        mhwmp_dbg("received PREQ from %pM", orig_addr);
 
-       if (compare_ether_addr(target_addr, sdata->vif.addr) == 0) {
+       if (ether_addr_equal(target_addr, sdata->vif.addr)) {
                mhwmp_dbg("PREQ is for us");
                forward = false;
                reply = true;
@@ -544,6 +546,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                        target_sn = ++ifmsh->sn;
                        ifmsh->last_sn_update = jiffies;
                }
+       } else if (is_broadcast_ether_addr(target_addr) &&
+                  (target_flags & IEEE80211_PREQ_TO_FLAG)) {
+               rcu_read_lock();
+               mpath = mesh_path_lookup(orig_addr, sdata);
+               if (mpath) {
+                       if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) {
+                               reply = true;
+                               target_addr = sdata->vif.addr;
+                               target_sn = ++ifmsh->sn;
+                               metric = 0;
+                               ifmsh->last_sn_update = jiffies;
+                       }
+                       if (root_is_gate)
+                               mesh_path_add_gate(mpath);
+               }
+               rcu_read_unlock();
        } else {
                rcu_read_lock();
                mpath = mesh_path_lookup(target_addr, sdata);
@@ -576,13 +594,14 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                                cpu_to_le32(target_sn), mgmt->sa, 0, ttl,
                                cpu_to_le32(lifetime), cpu_to_le32(metric),
                                0, sdata);
-               } else
+               } else {
                        ifmsh->mshstats.dropped_frames_ttl++;
+               }
        }
 
        if (forward && ifmsh->mshcfg.dot11MeshForwarding) {
                u32 preq_id;
-               u8 hopcount, flags;
+               u8 hopcount;
 
                ttl = PREQ_IE_TTL(preq_elem);
                lifetime = PREQ_IE_LIFETIME(preq_elem);
@@ -592,18 +611,27 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                }
                mhwmp_dbg("forwarding the PREQ from %pM", orig_addr);
                --ttl;
-               flags = PREQ_IE_FLAGS(preq_elem);
                preq_id = PREQ_IE_PREQ_ID(preq_elem);
                hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
                da = (mpath && mpath->is_root) ?
                        mpath->rann_snd_addr : broadcast_addr;
+
+               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,
                                cpu_to_le32(orig_sn), target_flags, target_addr,
                                cpu_to_le32(target_sn), da,
                                hopcount, ttl, cpu_to_le32(lifetime),
                                cpu_to_le32(metric), cpu_to_le32(preq_id),
                                sdata);
-               ifmsh->mshstats.fwded_mcast++;
+               if (!is_multicast_ether_addr(da))
+                       ifmsh->mshstats.fwded_unicast++;
+               else
+                       ifmsh->mshstats.fwded_mcast++;
                ifmsh->mshstats.fwded_frames++;
        }
 }
@@ -631,7 +659,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
        mhwmp_dbg("received PREP from %pM", PREP_IE_ORIG_ADDR(prep_elem));
 
        orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
-       if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0)
+       if (ether_addr_equal(orig_addr, sdata->vif.addr))
                /* destination, no forwarding required */
                return;
 
@@ -709,7 +737,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
                spin_lock_bh(&mpath->state_lock);
                sta = next_hop_deref_protected(mpath);
                if (mpath->flags & MESH_PATH_ACTIVE &&
-                   compare_ether_addr(ta, sta->sta.addr) == 0 &&
+                   ether_addr_equal(ta, sta->sta.addr) &&
                    (!(mpath->flags & MESH_PATH_SN_VALID) ||
                    SN_GT(target_sn, mpath->sn))) {
                        mpath->flags &= ~MESH_PATH_ACTIVE;
@@ -732,11 +760,12 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
                                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;
-       u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
+       u32 orig_sn, metric, metric_txsta, interval;
        bool root_is_gate;
 
        ttl = rann->rann_ttl;
@@ -748,19 +777,28 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
        flags = rann->rann_flags;
        root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
        orig_addr = rann->rann_addr;
-       orig_sn = rann->rann_seq;
+       orig_sn = le32_to_cpu(rann->rann_seq);
+       interval = le32_to_cpu(rann->rann_interval);
        hopcount = rann->rann_hopcount;
        hopcount++;
-       metric = rann->rann_metric;
+       metric = le32_to_cpu(rann->rann_metric);
 
        /*  Ignore our own RANNs */
-       if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0)
+       if (ether_addr_equal(orig_addr, sdata->vif.addr))
                return;
 
        mhwmp_dbg("received RANN from %pM via neighbour %pM (is_gate=%d)",
                        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);
@@ -780,18 +818,21 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
                mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH);
        }
 
-       if (mpath->sn < orig_sn && ifmsh->mshcfg.dot11MeshForwarding) {
+       if ((SN_LT(mpath->sn, orig_sn) || (mpath->sn == orig_sn &&
+          metric < mpath->rann_metric)) && 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 + mpath->metric),
+                                      cpu_to_le32(metric + metric_txsta),
                                       0, sdata);
                mpath->sn = orig_sn;
+               mpath->rann_metric = metric + metric_txsta;
+               /* Recording RANNs sender address to send individually
+                * addressed PREQs destined for root mesh STA */
+               memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN);
        }
 
-       /* Using individually addressed PREQ for root node */
-       memcpy(mpath->rann_snd_addr, mgmt->sa, ETH_ALEN);
        mpath->is_root = true;
 
        if (root_is_gate)
@@ -1086,7 +1127,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
        if (time_after(jiffies,
                       mpath->exp_time -
                       msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) &&
-           !compare_ether_addr(sdata->vif.addr, hdr->addr4) &&
+           ether_addr_equal(sdata->vif.addr, hdr->addr4) &&
            !(mpath->flags & MESH_PATH_RESOLVING) &&
            !(mpath->flags & MESH_PATH_FIXED))
                mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH);
@@ -1141,13 +1182,34 @@ mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval;
-       u8 flags;
+       u8 flags, target_flags = 0;
 
        flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol)
                        ? RANN_FLAG_IS_GATE : 0;
-       mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr,
+
+       switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) {
+       case IEEE80211_PROACTIVE_RANN:
+               mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr,
                               cpu_to_le32(++ifmsh->sn),
                               0, NULL, 0, broadcast_addr,
-                              0, sdata->u.mesh.mshcfg.element_ttl,
+                              0, ifmsh->mshcfg.element_ttl,
                               cpu_to_le32(interval), 0, 0, sdata);
+               break;
+       case IEEE80211_PROACTIVE_PREQ_WITH_PREP:
+               flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG;
+       case IEEE80211_PROACTIVE_PREQ_NO_PREP:
+               interval = ifmsh->mshcfg.dot11MeshHWMPactivePathToRootTimeout;
+               target_flags |= IEEE80211_PREQ_TO_FLAG |
+                               IEEE80211_PREQ_USN_FLAG;
+               mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr,
+                               cpu_to_le32(++ifmsh->sn), target_flags,
+                               (u8 *) broadcast_addr, 0, broadcast_addr,
+                               0, ifmsh->mshcfg.element_ttl,
+                               cpu_to_le32(interval),
+                               0, cpu_to_le32(ifmsh->preq_id++), sdata);
+               break;
+       default:
+               mhwmp_dbg("Proactive mechanism not supported");
+               return;
+       }
 }