<p>lynxis lazus <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/23187">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">gprs_ns2_sns: implement local change weight procedure<br><br>When changing the bind ip-sns weight, initiate a<br>SNS CHANGE WEIGHT procedure to inform the other side.<br><br>Related: OS#5036<br>Change-Id: Icec4dabb46bc198f68f91bfe09ba279fbe68d454<br>---<br>M src/gb/gprs_ns2.c<br>M src/gb/gprs_ns2_internal.h<br>M src/gb/gprs_ns2_sns.c<br>M src/gb/gprs_ns2_vty.c<br>M tests/gb/gprs_ns2_vty.vty<br>5 files changed, 460 insertions(+), 9 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c</span><br><span>index a895e3d..adf3b32 100644</span><br><span>--- a/src/gb/gprs_ns2.c</span><br><span>+++ b/src/gb/gprs_ns2.c</span><br><span>@@ -1440,6 +1440,7 @@</span><br><span>      nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */</span><br><span>  nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES] = 3;</span><br><span>      nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES] = 3;</span><br><span style="color: hsl(120, 100%, 40%);">+     nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES] = 3;</span><br><span> </span><br><span>    return nsi;</span><br><span> }</span><br><span>diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h</span><br><span>index ca6bfb7..afe6b69 100644</span><br><span>--- a/src/gb/gprs_ns2_internal.h</span><br><span>+++ b/src/gb/gprs_ns2_internal.h</span><br><span>@@ -60,8 +60,8 @@</span><br><span> struct gprs_ns2_vc_driver;</span><br><span> struct gprs_ns2_vc_bind;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#define NS_TIMERS_COUNT 10</span><br><span style="color: hsl(0, 100%, 40%);">-#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries)"</span><br><span style="color: hsl(120, 100%, 40%);">+#define NS_TIMERS_COUNT 11</span><br><span style="color: hsl(120, 100%, 40%);">+#define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries)"</span><br><span> #define NS_TIMERS_HELP       \</span><br><span>    "(un)blocking Timer (Tns-block) timeout\n"            \</span><br><span>    "(un)blocking Timer (Tns-block) number of retries\n"  \</span><br><span>@@ -73,6 +73,7 @@</span><br><span>        "SNS Provision Timer (Tsns-prov) timeout\n"           \</span><br><span>    "SNS Size number of retries\n"                                \</span><br><span>    "SNS Config number of retries\n"                      \</span><br><span style="color: hsl(120, 100%, 40%);">+     "SNS Procedures number of retries\n"                  \</span><br><span> </span><br><span> /* Educated guess - LLC user payload is 1500 bytes plus possible headers */</span><br><span> #define NS_ALLOC_SIZE   3072</span><br><span>@@ -89,6 +90,7 @@</span><br><span>     NS_TOUT_TSNS_PROV,</span><br><span>   NS_TOUT_TSNS_SIZE_RETRIES,</span><br><span>   NS_TOUT_TSNS_CONFIG_RETRIES,</span><br><span style="color: hsl(120, 100%, 40%);">+  NS_TOUT_TSNS_PROCEDURES_RETRIES,</span><br><span> };</span><br><span> </span><br><span> enum nsvc_timer_mode {</span><br><span>@@ -336,6 +338,7 @@</span><br><span>   NS2_SNS_EV_REQ_NSVC_ALIVE,              /*!< a NS-VC became alive */</span><br><span>      NS2_SNS_EV_REQ_ADD_BIND,                /*!< add a new local bind to this NSE */</span><br><span>  NS2_SNS_EV_REQ_DELETE_BIND,             /*!< remove a local bind from this NSE */</span><br><span style="color: hsl(120, 100%, 40%);">+  NS2_SNS_EV_REQ_CHANGE_WEIGHT,           /*!< a bind changed its weight */</span><br><span> };</span><br><span> </span><br><span> enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,</span><br><span>diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c</span><br><span>index 43e4920..9e30f62 100644</span><br><span>--- a/src/gb/gprs_ns2_sns.c</span><br><span>+++ b/src/gb/gprs_ns2_sns.c</span><br><span>@@ -70,6 +70,7 @@</span><br><span>      GPRS_SNS_ST_CONFIGURED,</span><br><span>      GPRS_SNS_ST_SGSN_WAIT_CONFIG,           /* !< SGSN role: Wait for CONFIG from BSS */</span><br><span>      GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK,       /* !< SGSN role: Wait for CONFIG-ACK from BSS */</span><br><span style="color: hsl(120, 100%, 40%);">+   GPRS_SNS_ST_LOCAL_PROCEDURE,            /*!< in process of a ADD/DEL/CHANGE procedure towards SGSN (BSS->SGSN) */</span><br><span> };</span><br><span> </span><br><span> static const struct value_string gprs_sns_event_names[] = {</span><br><span>@@ -88,9 +89,17 @@</span><br><span>        { NS2_SNS_EV_REQ_NSVC_ALIVE,            "REQ_NSVC_ALIVE"},</span><br><span>         { NS2_SNS_EV_REQ_ADD_BIND,              "REQ_ADD_BIND"},</span><br><span>   { NS2_SNS_EV_REQ_DELETE_BIND,           "REQ_DELETE_BIND"},</span><br><span style="color: hsl(120, 100%, 40%);">+ { NS2_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_CHANGE_WEIGHT"},</span><br><span>      { 0, NULL }</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+enum sns_procedure {</span><br><span style="color: hsl(120, 100%, 40%);">+   SNS_PROC_NONE,          /*!< used as invalid/idle value */</span><br><span style="color: hsl(120, 100%, 40%);">+ SNS_PROC_ADD,</span><br><span style="color: hsl(120, 100%, 40%);">+ SNS_PROC_DEL,</span><br><span style="color: hsl(120, 100%, 40%);">+ SNS_PROC_CHANGE_WEIGHT,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct sns_endpoint {</span><br><span>       struct llist_head list;</span><br><span>      struct osmo_sockaddr saddr;</span><br><span>@@ -99,6 +108,21 @@</span><br><span> struct ns2_sns_bind {</span><br><span>   struct llist_head list;</span><br><span>      struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t change_weight_state;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ns2_sns_procedure {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ns2_sns_bind *sbind;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* copy entry to protect against changes of gss->local */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_ns_ie_ip4_elem ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_ns_ie_ip6_elem ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+       enum sns_procedure procedure;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t trans_id;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* is the procedure in process */</span><br><span style="color: hsl(120, 100%, 40%);">+       bool running;</span><br><span> };</span><br><span> </span><br><span> struct ns2_sns_elems {</span><br><span>@@ -139,6 +163,9 @@</span><br><span>         /* local configuration to send to the remote end */</span><br><span>  struct ns2_sns_elems local;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       /* local configuration after all local procedures applied */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ns2_sns_elems local_procedure;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      /* remote configuration as received */</span><br><span>       struct ns2_sns_elems remote;</span><br><span> </span><br><span>@@ -147,6 +174,10 @@</span><br><span>      size_t num_max_nsvcs;</span><br><span>        size_t num_max_ip4_remote;</span><br><span>   size_t num_max_ip6_remote;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head procedures;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ns2_sns_procedure *current_procedure;</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t trans_id;</span><br><span> };</span><br><span> </span><br><span> static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)</span><br><span>@@ -302,6 +333,16 @@</span><br><span>  elems->num_ip6 = 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void ns2_clear_procedures(struct ns2_sns_state *gss)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ns2_sns_procedure *procedure, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+    gss->current_procedure = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(procedure, tmp, &gss->procedures, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_del(&procedure->list);</span><br><span style="color: hsl(120, 100%, 40%);">+           talloc_free(procedure);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void ns2_vc_create_ip(struct osmo_fsm_inst *fi, struct gprs_ns2_nse *nse, const struct osmo_sockaddr *remote,</span><br><span>                            uint8_t sig_weight, uint8_t data_weight)</span><br><span> {</span><br><span>@@ -777,6 +818,36 @@</span><br><span>    return count;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int ns2_sns_copy_local_endpoints(struct ns2_sns_state *gss)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (gss->family) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+         gss->local_procedure.ip4 = talloc_realloc(gss, gss->local_procedure.ip4, struct gprs_ns_ie_ip4_elem,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      gss->local.num_ip4);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!gss->local_procedure.ip4)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             gss->local_procedure.num_ip4 = gss->local.num_ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+              memcpy(gss->local_procedure.ip4, gss->local.ip4,</span><br><span style="color: hsl(120, 100%, 40%);">+                       sizeof(struct gprs_ns_ie_ip4_elem) * gss->local.num_ip4);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+                gss->local_procedure.ip6 = talloc_realloc(gss, gss->local_procedure.ip6, struct gprs_ns_ie_ip6_elem,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      gss->local.num_ip6);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!gss->local_procedure.ip6)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             gss->local_procedure.num_ip6 = gss->local.num_ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+              memcpy(gss->local_procedure.ip6, gss->local.ip6,</span><br><span style="color: hsl(120, 100%, 40%);">+                       sizeof(struct gprs_ns_ie_ip6_elem) * gss->local.num_ip6);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)</span><br><span> {</span><br><span>     struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span>@@ -878,6 +949,8 @@</span><br><span>              gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->local.num_ip6, 8);</span><br><span>             break;</span><br><span>       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ns2_sns_copy_local_endpoints(gss);</span><br><span> }</span><br><span> </span><br><span> static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss)</span><br><span>@@ -902,6 +975,7 @@</span><br><span>         if (old_state != GPRS_SNS_ST_BSS_SIZE)</span><br><span>               gss->N = 0;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    ns2_clear_procedures(gss);</span><br><span>   gss->alive = false;</span><br><span> </span><br><span>   ns2_sns_compute_local_ep_from_binds(fi);</span><br><span>@@ -1367,7 +1441,135 @@</span><br><span>   if (gss->sns_nsvc->sns_only)</span><br><span>           gprs_ns2_free_nsvc(gss->sns_nsvc);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE)</span><br><span style="color: hsl(120, 100%, 40%);">+         ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!llist_empty(&gss->procedures)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* check if resend or not */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!gss->current_procedure) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* take next procedure */</span><br><span style="color: hsl(120, 100%, 40%);">+             gss->current_procedure = llist_first_entry_or_null(&gss->procedures,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                   struct ns2_sns_procedure, list);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!gss->current_procedure) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             gss->N = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                gss->current_procedure->running = true;</span><br><span style="color: hsl(120, 100%, 40%);">+         gss->current_procedure->trans_id = ++gss->trans_id;</span><br><span style="color: hsl(120, 100%, 40%);">+          if (gss->trans_id == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                    gss->trans_id = gss->current_procedure->trans_id = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* also takes care of retransmitting */</span><br><span style="color: hsl(120, 100%, 40%);">+       switch (gss->current_procedure->procedure) {</span><br><span style="color: hsl(120, 100%, 40%);">+    case SNS_PROC_CHANGE_WEIGHT:</span><br><span style="color: hsl(120, 100%, 40%);">+          if (gss->family == AF_INET)</span><br><span style="color: hsl(120, 100%, 40%);">+                        ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+               else</span><br><span style="color: hsl(120, 100%, 40%);">+                  ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gprs_ns_ie_ip4_elem *ip4, *proc4;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gprs_ns_ie_ip6_elem *ip6, *proc6;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct tlv_parsed *tp = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t trans_id;</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t cause;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NS2_SNS_EV_RX_ADD:</span><br><span style="color: hsl(120, 100%, 40%);">+               ns2_sns_st_configured_add(fi, gss, tp);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case NS2_SNS_EV_RX_DELETE:</span><br><span style="color: hsl(120, 100%, 40%);">+            ns2_sns_st_configured_delete(fi, gss, tp);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case NS2_SNS_EV_RX_CHANGE_WEIGHT:</span><br><span style="color: hsl(120, 100%, 40%);">+             ns2_sns_st_configured_change(fi, gss, tp);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case NS2_SNS_EV_RX_ACK:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* presence of trans_id is already checked here */</span><br><span style="color: hsl(120, 100%, 40%);">+            trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (trans_id != gss->current_procedure->trans_id) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     LOGPFSML(fi, LOGL_INFO, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                             nse->nsei, trans_id, gss->current_procedure->trans_id);</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (TLVP_PRESENT(tp, NS_IE_CAUSE)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* what happend on error cause? return to size? */</span><br><span style="color: hsl(120, 100%, 40%);">+                    cause = tlvp_val8(tp, NS_IE_CAUSE, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK trans %d with cause code %d.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                nse->nsei, trans_id, cause);</span><br><span style="color: hsl(120, 100%, 40%);">+                      sns_failed(fi, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (gss->current_procedure->procedure) {</span><br><span style="color: hsl(120, 100%, 40%);">+            case SNS_PROC_CHANGE_WEIGHT:</span><br><span style="color: hsl(120, 100%, 40%);">+                  switch (gss->family) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+                         proc4 = &gss->current_procedure->ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+                               for (unsigned int i=0; i<gss->local.num_ip4; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ip4 = &gss->local.ip4[i];</span><br><span style="color: hsl(120, 100%, 40%);">+                                      if (ip4->ip_addr != proc4->ip_addr ||</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   ip4->udp_port != proc4->udp_port)</span><br><span style="color: hsl(120, 100%, 40%);">+                                               continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ip4->sig_weight = proc4->sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ip4->data_weight = proc4->data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                             break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+                                proc6 = &gss->current_procedure->ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+                               for (unsigned int i=0; i<gss->local.num_ip6; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ip6 = &gss->local.ip6[i];</span><br><span style="color: hsl(120, 100%, 40%);">+                                      if (memcmp(&ip6->ip_addr, &proc6->ip_addr, sizeof(proc6->ip_addr)) ||</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        ip6->udp_port != proc6->udp_port) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                             continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     ip6->sig_weight = proc6->sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                    ip6->data_weight = proc6->data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                             break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        default:</span><br><span style="color: hsl(120, 100%, 40%);">+                              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_del(&gss->current_procedure->list);</span><br><span style="color: hsl(120, 100%, 40%);">+           talloc_free(gss->current_procedure);</span><br><span style="color: hsl(120, 100%, 40%);">+               gss->current_procedure = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (llist_empty(&gss->procedures))</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                else</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span> }</span><br><span> </span><br><span> static const struct osmo_fsm_state ns2_sns_bss_states[] = {</span><br><span>@@ -1414,11 +1616,27 @@</span><br><span>                           S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |</span><br><span>                             S(NS2_SNS_EV_REQ_NSVC_ALIVE),</span><br><span>               .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |</span><br><span style="color: hsl(0, 100%, 40%);">-                           S(GPRS_SNS_ST_BSS_SIZE),</span><br><span style="color: hsl(120, 100%, 40%);">+                              S(GPRS_SNS_ST_BSS_SIZE) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(GPRS_SNS_ST_LOCAL_PROCEDURE),</span><br><span>            .name = "CONFIGURED",</span><br><span>              .action = ns2_sns_st_configured,</span><br><span>             .onenter = ns2_sns_st_configured_onenter,</span><br><span>    },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GPRS_SNS_ST_LOCAL_PROCEDURE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask = S(NS2_SNS_EV_RX_ADD) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                S(NS2_SNS_EV_RX_DELETE) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |</span><br><span style="color: hsl(120, 100%, 40%);">+                              S(NS2_SNS_EV_RX_ACK) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                S(NS2_SNS_EV_REQ_NSVC_ALIVE),</span><br><span style="color: hsl(120, 100%, 40%);">+                .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                 S(GPRS_SNS_ST_BSS_SIZE) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(GPRS_SNS_ST_CONFIGURED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                           S(GPRS_SNS_ST_LOCAL_PROCEDURE),</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "LOCAL_PROCEDURE",</span><br><span style="color: hsl(120, 100%, 40%);">+          .action = ns2_sns_st_local_procedure,</span><br><span style="color: hsl(120, 100%, 40%);">+         .onenter = ns2_sns_st_local_procedure_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> };</span><br><span> </span><br><span> static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)</span><br><span>@@ -1453,14 +1671,173 @@</span><br><span>   case 4:</span><br><span>              sns_failed(fi, "Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.");</span><br><span>              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 5:</span><br><span style="color: hsl(120, 100%, 40%);">+               if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   sns_failed(fi, "SNS Procedure retries failed.");</span><br><span style="color: hsl(120, 100%, 40%);">+            } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], 5);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span>       }</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct gprs_ns_ie_ip4_elem *ns2_get_sbind_ip4_entry(struct ns2_sns_state *gss,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        struct ns2_sns_bind *sbind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           struct ns2_sns_elems *endpoints)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_sockaddr *addr;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns_ie_ip4_elem *ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (gss->family != AF_INET)</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (addr->u.sa.sa_family != AF_INET)</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for (unsigned int i=0; i<endpoints->num_ip4; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ip4 = &endpoints->ip4[i];</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ip4->ip_addr == addr->u.sin.sin_addr.s_addr &&</span><br><span style="color: hsl(120, 100%, 40%);">+                              ip4->udp_port == addr->u.sin.sin_port)</span><br><span style="color: hsl(120, 100%, 40%);">+                  return ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct gprs_ns_ie_ip6_elem *ns2_get_sbind_ip6_entry(struct ns2_sns_state *gss,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                         struct ns2_sns_bind *sbind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                           struct ns2_sns_elems *endpoints)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_sockaddr *addr;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns_ie_ip6_elem *ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (gss->family != AF_INET6)</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (addr->u.sa.sa_family != AF_INET6)</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        for (unsigned int i=0; i<endpoints->num_ip6; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ip6 = &endpoints->ip6[i];</span><br><span style="color: hsl(120, 100%, 40%);">+              if (memcmp(&ip6->ip_addr, &addr->u.sin6.sin6_addr, sizeof(ip6->ip_addr)) ||</span><br><span style="color: hsl(120, 100%, 40%);">+              ip6->udp_port != addr->u.sin6.sin6_port)</span><br><span style="color: hsl(120, 100%, 40%);">+                    return ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* return != 0 if the resulting weight is invalid. return 1 if sbind doesn't have an entry */</span><br><span style="color: hsl(120, 100%, 40%);">+static int ns2_update_weight_entry(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  struct ns2_sns_elems *endpoints)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns_ie_ip4_elem *ip4;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gprs_ns_ie_ip6_elem *ip6;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (gss->family) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+         ip4 = ns2_get_sbind_ip4_entry(gss, sbind, endpoints);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ip4)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             ip4->sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+               ip4->data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+             return (ip4_weight_sum_sig(endpoints) != 0 && ip4_weight_sum_data(endpoints) != 0);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+                ip6 = ns2_get_sbind_ip6_entry(gss, sbind, endpoints);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ip6)</span><br><span style="color: hsl(120, 100%, 40%);">+                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             ip6->sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+               ip6->data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+             return (ip6_weight_sum_sig(endpoints) != 0 && ip6_weight_sum_data(endpoints) != 0);</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,</span><br><span style="color: hsl(120, 100%, 40%);">+                        enum sns_procedure procedure_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ns2_sns_procedure *procedure = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct osmo_sockaddr *saddr;</span><br><span style="color: hsl(120, 100%, 40%);">+    saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (saddr->u.sa.sa_family != gss->family)</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (procedure_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+     case SNS_PROC_CHANGE_WEIGHT:</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_for_each_entry(procedure, &gss->procedures, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (procedure->sbind == sbind && procedure->procedure == procedure_type &&</span><br><span style="color: hsl(120, 100%, 40%);">+                                      !procedure->running) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             switch(gss->family) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+                                 /* merge it with a previous procedure */</span><br><span style="color: hsl(120, 100%, 40%);">+                                      procedure->ip4.ip_addr = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                        procedure->ip4.data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+                                        /* merge it with a previous procedure */</span><br><span style="color: hsl(120, 100%, 40%);">+                                      procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                     procedure->ip6.data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                                      OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+                               }</span><br><span style="color: hsl(120, 100%, 40%);">+                             return;</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           procedure = talloc_zero(gss, struct ns2_sns_procedure);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!procedure)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             procedure->sbind = sbind;</span><br><span style="color: hsl(120, 100%, 40%);">+          procedure->procedure = procedure_type;</span><br><span style="color: hsl(120, 100%, 40%);">+             procedure->sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+         procedure->data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             switch(gss->family) {</span><br><span style="color: hsl(120, 100%, 40%);">+              case AF_INET:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                       procedure->ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+                  procedure->ip4.udp_port = saddr->u.sin.sin_port;</span><br><span style="color: hsl(120, 100%, 40%);">+                        procedure->ip4.sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                     procedure->ip4.data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case AF_INET6:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                      memcpy(&procedure->ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));</span><br><span style="color: hsl(120, 100%, 40%);">+                     procedure->ip6.udp_port = saddr->u.sin.sin_port;</span><br><span style="color: hsl(120, 100%, 40%);">+                        procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                     procedure->ip6.data_weight = sbind->bind->sns_data_weight;</span><br><span style="color: hsl(120, 100%, 40%);">+                   break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_add_tail(&procedure->list, &gss->procedures);</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (gss->nse->bss_sns_fi->state == GPRS_SNS_ST_CONFIGURED) {</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* common allstate-action for both roles */</span><br><span> static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span>      struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span>    struct ns2_sns_bind *sbind;</span><br><span>  struct gprs_ns2_vc *nsvc, *nsvc2;</span><br><span> </span><br><span>@@ -1509,6 +1886,27 @@</span><br><span>               /* if this is the last bind, the free_nsvc() will trigger a reselection */</span><br><span>           talloc_free(sbind);</span><br><span>          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case NS2_SNS_EV_REQ_CHANGE_WEIGHT:</span><br><span style="color: hsl(120, 100%, 40%);">+            sbind = data;</span><br><span style="color: hsl(120, 100%, 40%);">+         switch (fi->state) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GPRS_SNS_ST_UNCONFIGURED:</span><br><span style="color: hsl(120, 100%, 40%);">+                        /* select_endpoint will check if this is a valid configuration */</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (gss->role == GPRS_SNS_ROLE_BSS)</span><br><span style="color: hsl(120, 100%, 40%);">+                                osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case GPRS_SNS_ST_BSS_SIZE:</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* invalid weight? */</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!ns2_update_weight_entry(gss, sbind, &gss->local))</span><br><span style="color: hsl(120, 100%, 40%);">+                         sns_failed(fi, "updating weights results in an invalid configuration.");</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (!ns2_update_weight_entry(gss, sbind, &gss->local_procedure)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             sns_failed(fi, "updating weights results in an invalid configuration.");</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ns2_add_procedure(gss, sbind, SNS_PROC_CHANGE_WEIGHT);</span><br><span style="color: hsl(120, 100%, 40%);">+                        break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span>    }</span><br><span> }</span><br><span> </span><br><span>@@ -1618,6 +2016,7 @@</span><br><span>                          S(NS2_SNS_EV_REQ_FREE_NSVCS) |</span><br><span>                               S(NS2_SNS_EV_REQ_SELECT_ENDPOINT) |</span><br><span>                          S(NS2_SNS_EV_REQ_ADD_BIND) |</span><br><span style="color: hsl(120, 100%, 40%);">+                          S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |</span><br><span>                            S(NS2_SNS_EV_REQ_DELETE_BIND),</span><br><span>        .allstate_action = ns2_sns_st_all_action_bss,</span><br><span>        .cleanup = NULL,</span><br><span>@@ -1653,6 +2052,7 @@</span><br><span>     gss->num_max_ip6_remote = 8192;</span><br><span>   INIT_LLIST_HEAD(&gss->sns_endpoints);</span><br><span>         INIT_LLIST_HEAD(&gss->binds);</span><br><span style="color: hsl(120, 100%, 40%);">+  INIT_LLIST_HEAD(&gss->procedures);</span><br><span> </span><br><span>        return fi;</span><br><span> err:</span><br><span>@@ -1952,7 +2352,7 @@</span><br><span>           return;</span><br><span> </span><br><span>  gss = nse->bss_sns_fi->priv;</span><br><span style="color: hsl(0, 100%, 40%);">-      if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+    if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE)</span><br><span>           return;</span><br><span> </span><br><span>  if (alive == gss->alive)</span><br><span>@@ -2052,7 +2452,26 @@</span><br><span>  */</span><br><span> void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  /* TODO: implement weights after binds per sns implemented */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ns2_sns_bind *sbind;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gprs_ns2_nse *nse;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ns2_sns_state *gss;</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct osmo_sockaddr *addr = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(nse, &bind->nsi->nse, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!nse->bss_sns_fi)</span><br><span style="color: hsl(120, 100%, 40%);">+                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           gss = nse->bss_sns_fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (addr->u.sa.sa_family != gss->family)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_for_each_entry(sbind, &gss->binds, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (sbind->bind == bind) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, NS2_SNS_EV_REQ_CHANGE_WEIGHT, sbind);</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span> }</span><br><span> </span><br><span> </span><br><span>@@ -2067,6 +2486,7 @@</span><br><span> {</span><br><span>  struct gprs_ns2_vc *nsvc, *nsvc2;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ns2_clear_procedures(gss);</span><br><span>   ns2_clear_elems(&gss->local);</span><br><span>         ns2_clear_elems(&gss->remote);</span><br><span>        llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {</span><br><span>@@ -2207,12 +2627,28 @@</span><br><span>                           S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |</span><br><span>                             S(NS2_SNS_EV_REQ_NSVC_ALIVE),</span><br><span>               .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |</span><br><span style="color: hsl(0, 100%, 40%);">-                           S(GPRS_SNS_ST_SGSN_WAIT_CONFIG),</span><br><span style="color: hsl(120, 100%, 40%);">+                              S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(GPRS_SNS_ST_LOCAL_PROCEDURE),</span><br><span>            .name = "CONFIGURED",</span><br><span>              /* shared with BSS side; once configured there's no difference */</span><br><span>                .action = ns2_sns_st_configured,</span><br><span>             .onenter = ns2_sns_st_configured_onenter,</span><br><span>    },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GPRS_SNS_ST_LOCAL_PROCEDURE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask = S(NS2_SNS_EV_RX_ADD) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                S(NS2_SNS_EV_RX_DELETE) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |</span><br><span style="color: hsl(120, 100%, 40%);">+                              S(NS2_SNS_EV_RX_ACK) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |</span><br><span style="color: hsl(120, 100%, 40%);">+                             S(NS2_SNS_EV_REQ_NSVC_ALIVE),</span><br><span style="color: hsl(120, 100%, 40%);">+                .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                                 S(GPRS_SNS_ST_CONFIGURED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                           S(GPRS_SNS_ST_LOCAL_PROCEDURE),</span><br><span style="color: hsl(120, 100%, 40%);">+             .name = "LOCAL_PROCEDURE",</span><br><span style="color: hsl(120, 100%, 40%);">+          /* shared with BSS side; once configured there's no difference */</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = ns2_sns_st_local_procedure,</span><br><span style="color: hsl(120, 100%, 40%);">+         .onenter = ns2_sns_st_local_procedure_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span> };</span><br><span> </span><br><span> static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)</span><br><span>@@ -2234,11 +2670,19 @@</span><br><span>     case 4:</span><br><span>              LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei);</span><br><span>            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 5:</span><br><span style="color: hsl(120, 100%, 40%);">+               if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       sns_failed(fi, "SNS Procedure retries failed.");</span><br><span style="color: hsl(120, 100%, 40%);">+            } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],</span><br><span style="color: hsl(120, 100%, 40%);">+                                          fi->T);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span>       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /* allstate-action for SGSN role */</span><br><span> static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span>@@ -2349,6 +2793,7 @@</span><br><span>                              S(NS2_SNS_EV_REQ_NO_NSVC) |</span><br><span>                          S(NS2_SNS_EV_REQ_FREE_NSVCS) |</span><br><span>                               S(NS2_SNS_EV_REQ_ADD_BIND) |</span><br><span style="color: hsl(120, 100%, 40%);">+                          S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |</span><br><span>                            S(NS2_SNS_EV_REQ_DELETE_BIND),</span><br><span>        .allstate_action = ns2_sns_st_all_action_sgsn,</span><br><span>       .cleanup = NULL,</span><br><span>@@ -2380,6 +2825,7 @@</span><br><span>     gss->role = GPRS_SNS_ROLE_SGSN;</span><br><span>   INIT_LLIST_HEAD(&gss->sns_endpoints);</span><br><span>         INIT_LLIST_HEAD(&gss->binds);</span><br><span style="color: hsl(120, 100%, 40%);">+  INIT_LLIST_HEAD(&gss->procedures);</span><br><span> </span><br><span>        return fi;</span><br><span> err:</span><br><span>diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c</span><br><span>index 0aa7902..52ce207 100644</span><br><span>--- a/src/gb/gprs_ns2_vty.c</span><br><span>+++ b/src/gb/gprs_ns2_vty.c</span><br><span>@@ -101,6 +101,7 @@</span><br><span>    { 7, "tsns-prov" },</span><br><span>        { 8, "tsns-size-retries" },</span><br><span>        { 9, "tsns-config-retries" },</span><br><span style="color: hsl(120, 100%, 40%);">+       {10, "tsns-procedures-retries" },</span><br><span>  { 0, NULL }</span><br><span> };</span><br><span> </span><br><span>diff --git a/tests/gb/gprs_ns2_vty.vty b/tests/gb/gprs_ns2_vty.vty</span><br><span>index 45c8a16..62fff66 100644</span><br><span>--- a/tests/gb/gprs_ns2_vty.vty</span><br><span>+++ b/tests/gb/gprs_ns2_vty.vty</span><br><span>@@ -17,7 +17,7 @@</span><br><span> OsmoNSdummy(config)# ns</span><br><span> OsmoNSdummy(config-ns)# list</span><br><span> ...</span><br><span style="color: hsl(0, 100%, 40%);">-  timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries) <0-65535></span><br><span style="color: hsl(120, 100%, 40%);">+  timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries) <0-65535></span><br><span>   nse <0-65535> [ip-sns-role-sgsn]</span><br><span>   no nse <0-65535></span><br><span>   bind (fr|udp) ID</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/23187">change 23187</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/23187"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Icec4dabb46bc198f68f91bfe09ba279fbe68d454 </div>
<div style="display:none"> Gerrit-Change-Number: 23187 </div>
<div style="display:none"> Gerrit-PatchSet: 17 </div>
<div style="display:none"> Gerrit-Owner: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>