policy: don't select better candidate with bad signal
[project/usteer.git] / policy.c
index 1b4fa57abda385e30691e1a9c2091b20de0496aa..445abdefe8d9d57d3420bf01b387b557c67756b3 100644 (file)
--- a/policy.c
+++ b/policy.c
@@ -72,6 +72,18 @@ below_max_assoc(struct sta_info *si)
        return !node->max_assoc || node->n_assoc < node->max_assoc;
 }
 
+static bool
+over_min_signal(struct sta_info *si)
+{
+       if (config.min_snr && si->signal < usteer_snr_to_signal(si->node, config.min_snr))
+               return false;
+
+       if (config.roam_trigger_snr && si->signal < usteer_snr_to_signal(si->node, config.roam_trigger_snr))
+               return false;
+       
+       return true;
+}
+
 static uint32_t
 is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
 {
@@ -80,6 +92,9 @@ is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
        if (!below_max_assoc(si_new))
                return 0;
 
+       if (!over_min_signal(si_new))
+               return 0;
+
        if (below_assoc_threshold(si_cur, si_new) &&
            !below_assoc_threshold(si_new, si_cur))
                reasons |= (1 << UEV_SELECT_REASON_NUM_ASSOC);
@@ -95,7 +110,7 @@ is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
 }
 
 static struct sta_info *
-find_better_candidate(struct sta_info *si_ref, struct uevent *ev)
+find_better_candidate(struct sta_info *si_ref, struct uevent *ev, uint32_t required_criteria)
 {
        struct sta_info *si;
        struct sta *sta = si_ref->sta;
@@ -115,7 +130,7 @@ find_better_candidate(struct sta_info *si_ref, struct uevent *ev)
                if (!reasons)
                        continue;
 
-               if (is_better_candidate(si, si_ref))
+               if (!(reasons & required_criteria))
                        continue;
 
                if (ev) {
@@ -129,8 +144,8 @@ find_better_candidate(struct sta_info *si_ref, struct uevent *ev)
        return NULL;
 }
 
-static int
-snr_to_signal(struct usteer_node *node, int snr)
+int
+usteer_snr_to_signal(struct usteer_node *node, int snr)
 {
        int noise = -95;
 
@@ -155,10 +170,24 @@ usteer_check_request(struct sta_info *si, enum usteer_event_type type)
        if (type == EVENT_TYPE_AUTH)
                goto out;
 
-       if (type == EVENT_TYPE_ASSOC && !config.assoc_steering)
-               goto out;
+       if (type == EVENT_TYPE_ASSOC) {
+               /* Check if assoc request has lower signal than min_signal.
+                * If this is the case, block assoc even when assoc steering is enabled.
+                *
+                * Otherwise, the client potentially ends up in a assoc - kick loop.
+                */
+               if (config.min_snr && si->signal < usteer_snr_to_signal(si->node, config.min_snr)) {
+                       ev.reason = UEV_REASON_LOW_SIGNAL;
+                       ev.threshold.cur = si->signal;
+                       ev.threshold.ref = usteer_snr_to_signal(si->node, config.min_snr);
+                       ret = false;
+                       goto out;
+               } else if (!config.assoc_steering) {
+                       goto out;
+               }
+       }
 
-       min_signal = snr_to_signal(si->node, config.min_connect_snr);
+       min_signal = usteer_snr_to_signal(si->node, config.min_connect_snr);
        if (si->signal < min_signal) {
                ev.reason = UEV_REASON_LOW_SIGNAL;
                ev.threshold.cur = si->signal;
@@ -175,7 +204,7 @@ usteer_check_request(struct sta_info *si, enum usteer_event_type type)
                goto out;
        }
 
-       if (!find_better_candidate(si, &ev))
+       if (!find_better_candidate(si, &ev, UEV_SELECT_REASON_ALL))
                goto out;
 
        ev.reason = UEV_REASON_BETTER_CANDIDATE;
@@ -251,14 +280,14 @@ usteer_roam_trigger_sm(struct sta_info *si)
        };
        int min_signal;
 
-       min_signal = snr_to_signal(si->node, config.roam_trigger_snr);
+       min_signal = usteer_snr_to_signal(si->node, config.roam_trigger_snr);
 
        switch (si->roam_state) {
        case ROAM_TRIGGER_SCAN:
                if (current_time - si->roam_event < config.roam_scan_interval)
                        break;
 
-               if (find_better_candidate(si, &ev) ||
+               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)) ||
                    si->roam_scan_done > si->roam_event) {
                        usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
                        break;
@@ -275,7 +304,7 @@ usteer_roam_trigger_sm(struct sta_info *si)
                break;
 
        case ROAM_TRIGGER_IDLE:
-               if (find_better_candidate(si, &ev)) {
+               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL))) {
                        usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev);
                        break;
                }
@@ -290,7 +319,7 @@ usteer_roam_trigger_sm(struct sta_info *si)
                        break;
                }
 
-               if (find_better_candidate(si, &ev))
+               if (find_better_candidate(si, &ev, (1 << UEV_SELECT_REASON_SIGNAL)))
                        usteer_roam_set_state(si, ROAM_TRIGGER_WAIT_KICK, &ev);
 
                break;
@@ -331,7 +360,7 @@ usteer_local_node_roam_check(struct usteer_local_node *ln, struct uevent *ev)
                return;
 
        usteer_update_time();
-       min_signal = snr_to_signal(&ln->node, min_signal);
+       min_signal = usteer_snr_to_signal(&ln->node, min_signal);
 
        list_for_each_entry(si, &ln->node.sta_info, node_list) {
                if (si->connected != STA_CONNECTED || si->signal >= min_signal ||
@@ -361,7 +390,7 @@ usteer_local_node_snr_kick(struct usteer_local_node *ln)
        if (!config.min_snr)
                return;
 
-       min_signal = snr_to_signal(&ln->node, config.min_snr);
+       min_signal = usteer_snr_to_signal(&ln->node, config.min_snr);
        ev.threshold.ref = min_signal;
 
        list_for_each_entry(si, &ln->node.sta_info, node_list) {
@@ -440,7 +469,7 @@ usteer_local_node_kick(struct usteer_local_node *ln)
                if (is_more_kickable(kick1, si))
                        kick1 = si;
 
-               tmp = find_better_candidate(si, NULL);
+               tmp = find_better_candidate(si, NULL, (1 << UEV_SELECT_REASON_LOAD));
                if (!tmp)
                        continue;