<p>lynxis lazus has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/21573">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">gprs_ns2_sns: rework IP-SNS initial remote<br><br>The IP-SNS requires at least one initial remote address of the SGSN.<br>However it should be multiple initial remote address instead of a single<br>in case the interface might fail.<br>Rework the SNS to support multiple initial remote addresses.<br><br>Change-Id: I71cdbfb53e361e6112fed5e2712236d797ef3ab2<br>---<br>M include/osmocom/gprs/gprs_ns2.h<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_udp.c<br>M src/gb/libosmogb.map<br>6 files changed, 334 insertions(+), 190 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/73/21573/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h</span><br><span>index 3c3c63a..bc89b6a 100644</span><br><span>--- a/include/osmocom/gprs/gprs_ns2.h</span><br><span>+++ b/include/osmocom/gprs/gprs_ns2.h</span><br><span>@@ -83,6 +83,7 @@</span><br><span>  /* osmocom own causes */</span><br><span>     NS_AFF_CAUSE_SNS_CONFIGURED,</span><br><span>         NS_AFF_CAUSE_SNS_FAILURE,</span><br><span style="color: hsl(120, 100%, 40%);">+     NS_AFF_CAUSE_SNS_NO_ENDPOINTS,</span><br><span> };</span><br><span> </span><br><span> extern const struct value_string gprs_ns2_aff_cause_prim_strs[];</span><br><span>@@ -219,9 +220,10 @@</span><br><span> void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi);</span><br><span> </span><br><span> /* create a VC SNS connection */</span><br><span style="color: hsl(0, 100%, 40%);">-int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(0, 100%, 40%);">-                       const struct osmo_sockaddr *remote,</span><br><span style="color: hsl(0, 100%, 40%);">-                     uint16_t nsei);</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const struct osmo_sockaddr *saddr);</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  const struct osmo_sockaddr *saddr);</span><br><span> const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse);</span><br><span> </span><br><span> const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc);</span><br><span>diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c</span><br><span>index 6d4b079..9947e59 100644</span><br><span>--- a/src/gb/gprs_ns2.c</span><br><span>+++ b/src/gb/gprs_ns2.c</span><br><span>@@ -953,42 +953,6 @@</span><br><span>   return gprs_ns2_ip_connect(bind, remote, nse, nsvci);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Create, connect and activate a new IP-SNS NSE.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] bind bind in which the new NS-VC is to be created</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] remote remote address to which to connect</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created</span><br><span style="color: hsl(0, 100%, 40%);">- *  \return 0 on success; negative on error */</span><br><span style="color: hsl(0, 100%, 40%);">-int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,</span><br><span style="color: hsl(0, 100%, 40%);">-                       const struct osmo_sockaddr *remote,</span><br><span style="color: hsl(0, 100%, 40%);">-                     uint16_t nsei)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);</span><br><span style="color: hsl(0, 100%, 40%);">-    struct gprs_ns2_vc *nsvc;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!nse) {</span><br><span style="color: hsl(0, 100%, 40%);">-             nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_UDP, NS2_DIALECT_SNS);</span><br><span style="color: hsl(0, 100%, 40%);">-                if (!nse)</span><br><span style="color: hsl(0, 100%, 40%);">-                       return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (nse->ll != GPRS_NS2_LL_UDP) {</span><br><span style="color: hsl(0, 100%, 40%);">-            return -2;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (nse->dialect != NS2_DIALECT_SNS) {</span><br><span style="color: hsl(0, 100%, 40%);">-               return -2;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!nse->bss_sns_fi)</span><br><span style="color: hsl(0, 100%, 40%);">-                return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      nsvc = gprs_ns2_ip_bind_connect(bind, nse, remote);</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!nsvc)</span><br><span style="color: hsl(0, 100%, 40%);">-              return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      return ns2_sns_bss_fsm_start(nse, nsvc, remote);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! Find NS-VC for given socket address.</span><br><span>  *  \param[in] nse NS Entity in which to search</span><br><span>  *  \param[in] sockaddr socket address to search for</span><br><span>diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h</span><br><span>index 3ef0906..5a090c2 100644</span><br><span>--- a/src/gb/gprs_ns2_internal.h</span><br><span>+++ b/src/gb/gprs_ns2_internal.h</span><br><span>@@ -290,13 +290,15 @@</span><br><span> struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,</span><br><span>                                              struct gprs_ns2_nse *nse,</span><br><span>                                            const struct osmo_sockaddr *remote);</span><br><span style="color: hsl(120, 100%, 40%);">+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote);</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_offset(struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                struct osmo_sockaddr *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 int offset);</span><br><span> </span><br><span> /* sns */</span><br><span> int gprs_ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);</span><br><span> struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,</span><br><span>                                              const char *id);</span><br><span style="color: hsl(0, 100%, 40%);">-int ns2_sns_bss_fsm_start(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc,</span><br><span style="color: hsl(0, 100%, 40%);">-                        const struct osmo_sockaddr *remote);</span><br><span> void ns2_sns_free_nsvc(struct gprs_ns2_vc *nsvc);</span><br><span> </span><br><span> /* vc */</span><br><span>diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c</span><br><span>index d13d920..718d075 100644</span><br><span>--- a/src/gb/gprs_ns2_sns.c</span><br><span>+++ b/src/gb/gprs_ns2_sns.c</span><br><span>@@ -69,7 +69,7 @@</span><br><span> };</span><br><span> </span><br><span> enum gprs_sns_event {</span><br><span style="color: hsl(0, 100%, 40%);">-     GPRS_SNS_EV_START,</span><br><span style="color: hsl(120, 100%, 40%);">+    GPRS_SNS_EV_SELECT_ENDPOINT,    /*!< Select a SNS endpoint from the list */</span><br><span>       GPRS_SNS_EV_SIZE,</span><br><span>    GPRS_SNS_EV_SIZE_ACK,</span><br><span>        GPRS_SNS_EV_CONFIG,</span><br><span>@@ -82,7 +82,7 @@</span><br><span> };</span><br><span> </span><br><span> static const struct value_string gprs_sns_event_names[] = {</span><br><span style="color: hsl(0, 100%, 40%);">-        { GPRS_SNS_EV_START,            "START" },</span><br><span style="color: hsl(120, 100%, 40%);">+  { GPRS_SNS_EV_SELECT_ENDPOINT,  "SELECT_ENDPOINT" },</span><br><span>       { GPRS_SNS_EV_SIZE,             "SIZE" },</span><br><span>  { GPRS_SNS_EV_SIZE_ACK,         "SIZE_ACK" },</span><br><span>      { GPRS_SNS_EV_CONFIG,           "CONFIG" },</span><br><span>@@ -95,14 +95,26 @@</span><br><span>  { 0, NULL }</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct sns_endpoint {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_sockaddr saddr;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ns2_sns_state {</span><br><span>  struct gprs_ns2_nse *nse;</span><br><span> </span><br><span>        enum ns2_sns_type ip;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       /* initial connection. the initial connection will be terminated</span><br><span style="color: hsl(0, 100%, 40%);">-         * in configured state or moved into NSE if valid */</span><br><span style="color: hsl(0, 100%, 40%);">-    struct osmo_sockaddr initial;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* holds the list of initial SNS endpoints */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head sns_endpoints;</span><br><span style="color: hsl(120, 100%, 40%);">+      /* prevent recursive reselection */</span><br><span style="color: hsl(120, 100%, 40%);">+   bool reselection_running;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* The current initial SNS endpoints.</span><br><span style="color: hsl(120, 100%, 40%);">+  * The initial connection will be moved into the NSE</span><br><span style="color: hsl(120, 100%, 40%);">+   * if configured via SNS. Otherwise it will be removed</span><br><span style="color: hsl(120, 100%, 40%);">+         * in configured state. */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct sns_endpoint *initial;</span><br><span>        /* all SNS PDU will be sent over this nsvc */</span><br><span>        struct gprs_ns2_vc *sns_nsvc;</span><br><span> </span><br><span>@@ -207,7 +219,7 @@</span><br><span>              return NULL;</span><br><span> </span><br><span>     gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;</span><br><span style="color: hsl(0, 100%, 40%);">-     return &gss->initial;</span><br><span style="color: hsl(120, 100%, 40%);">+  return &gss->initial->saddr;</span><br><span> }</span><br><span> </span><br><span> /*! called when a nsvc is beeing freed */</span><br><span>@@ -646,16 +658,7 @@</span><br><span> </span><br><span> static void ns2_sns_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-        struct gprs_ns2_inst *nsi = nse->nsi;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        switch (event) {</span><br><span style="color: hsl(0, 100%, 40%);">-        case GPRS_SNS_EV_START:</span><br><span style="color: hsl(0, 100%, 40%);">-         osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, nsi->timeout[NS_TOUT_TSNS_PROV], 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           break;</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                OSMO_ASSERT(0);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* empty state - SNS Select will start by all action */</span><br><span> }</span><br><span> </span><br><span> static void ns2_sns_st_size(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span>@@ -681,18 +684,133 @@</span><br><span>       }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* setup all dynamic SNS settings, create a new nsvc and send the SIZE */</span><br><span> static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)</span><br><span> {</span><br><span>      struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_ns_ie_ip4_elem *ip4_elems;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns_ie_ip6_elem *ip6_elems;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_ns2_inst *nsi = gss->nse->nsi;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_sockaddr *remote;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct osmo_sockaddr *sa;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_sockaddr local;</span><br><span style="color: hsl(120, 100%, 40%);">+   int count;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        /* on a generic failure, the timer callback will recover */</span><br><span>  if (old_state != GPRS_SNS_ST_UNCONFIGURED)</span><br><span>           ns2_prim_status_ind(gss->nse, NULL, 0, NS_AFF_CAUSE_SNS_FAILURE);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      /* no initial available */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!gss->initial)</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%);">+     remote = &gss->initial->saddr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* count how many bindings are available (only UDP binds) */</span><br><span style="color: hsl(120, 100%, 40%);">+  count = ns2_ip_count_bind(nsi, remote);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* TODO: logging */</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%);">+   bind = ns2_ip_get_bind_by_offset(nsi, remote, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!bind) {</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%);">+   /* setup the NSVC */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!gss->sns_nsvc) {</span><br><span style="color: hsl(120, 100%, 40%);">+              gss->sns_nsvc = gprs_ns2_ip_bind_connect(bind, gss->nse, remote);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!gss->sns_nsvc)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return;</span><br><span style="color: hsl(120, 100%, 40%);">+               gss->sns_nsvc->sns_only = true;</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->ip) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case IPv4:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip4_elems = talloc_zero_size(fi, sizeof(struct gprs_ns_ie_ip4_elem) * count);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ip4_elems)</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->ip4_local = ip4_elems;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (!gprs_ns2_is_ip_bind(bind))</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%);">+                   sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!sa)</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%);">+                   if (sa->u.sas.ss_family != AF_INET)</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%);">+                   /* check if this is an specific bind */</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (sa->u.sin.sin_addr.s_addr == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (osmo_sockaddr_local_ip(&local, remote))</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%);">+                           ip4_elems->ip_addr = local.u.sin.sin_addr.s_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+                  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ip4_elems->ip_addr = sa->u.sin.sin_addr.s_addr;</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%);">+                   ip4_elems->udp_port = sa->u.sin.sin_port;</span><br><span style="color: hsl(120, 100%, 40%);">+                       ip4_elems->sig_weight = 2;</span><br><span style="color: hsl(120, 100%, 40%);">+                 ip4_elems->data_weight = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                        ip4_elems++;</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%);">+           gss->num_ip4_local = count;</span><br><span style="color: hsl(120, 100%, 40%);">+                gss->num_max_ip4_remote = 4;</span><br><span style="color: hsl(120, 100%, 40%);">+               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * 4, 8);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case IPv6:</span><br><span style="color: hsl(120, 100%, 40%);">+            /* IPv6 */</span><br><span style="color: hsl(120, 100%, 40%);">+            ip6_elems = talloc_zero_size(fi, sizeof(struct gprs_ns_ie_ip6_elem) * count);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ip6_elems)</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->ip6_local = ip6_elems;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (!gprs_ns2_is_ip_bind(bind))</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%);">+                   sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!sa)</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%);">+                   if (sa->u.sas.ss_family != AF_INET6)</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%);">+                   /* check if this is an specific bind */</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          if (osmo_sockaddr_local_ip(&local, remote))</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_elems->ip_addr = local.u.sin6.sin6_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+                       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ip6_elems->ip_addr = sa->u.sin6.sin6_addr;</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%);">+                   ip6_elems->udp_port = sa->u.sin.sin_port;</span><br><span style="color: hsl(120, 100%, 40%);">+                       ip6_elems->sig_weight = 2;</span><br><span style="color: hsl(120, 100%, 40%);">+                 ip6_elems->data_weight = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                      ip6_elems++;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+             gss->num_ip6_local = count;</span><br><span style="color: hsl(120, 100%, 40%);">+                gss->num_max_ip6_remote = 4;</span><br><span style="color: hsl(120, 100%, 40%);">+               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * 4, 8);</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>  if (gss->num_max_ip4_remote > 0)</span><br><span>               ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->num_max_ip4_remote, -1);</span><br><span>      else</span><br><span>                 ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, gss->num_max_ip6_remote);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> }</span><br><span> </span><br><span> static void ns2_sns_st_config_bss(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span>@@ -1130,7 +1248,7 @@</span><br><span> </span><br><span> static const struct osmo_fsm_state ns2_sns_bss_states[] = {</span><br><span>       [GPRS_SNS_ST_UNCONFIGURED] = {</span><br><span style="color: hsl(0, 100%, 40%);">-          .in_event_mask = S(GPRS_SNS_EV_START),</span><br><span style="color: hsl(120, 100%, 40%);">+                .in_event_mask = 0,</span><br><span>          .out_state_mask = S(GPRS_SNS_ST_SIZE),</span><br><span>               .name = "UNCONFIGURED",</span><br><span>            .action = ns2_sns_st_unconfigured,</span><br><span>@@ -1194,17 +1312,46 @@</span><br><span> </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 style="color: hsl(120, 100%, 40%);">+      struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;</span><br><span>    struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);</span><br><span> </span><br><span>         /* reset when receiving GPRS_SNS_EV_NO_NSVC */</span><br><span style="color: hsl(0, 100%, 40%);">-  osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 3);</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case GPRS_SNS_EV_NO_NSVC:</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!gss->reselection_running)</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_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_EV_SELECT_ENDPOINT:</span><br><span style="color: hsl(120, 100%, 40%);">+             /* tear down previous state</span><br><span style="color: hsl(120, 100%, 40%);">+            * gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this from triggering a reselection */</span><br><span style="color: hsl(120, 100%, 40%);">+          gss->reselection_running = true;</span><br><span style="color: hsl(120, 100%, 40%);">+           gprs_ns2_free_nsvcs(nse);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Choose the next sns endpoint. */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (llist_empty(&gss->sns_endpoints)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        gss->initial = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                       ns2_prim_status_ind(gss->nse, NULL, 0, NS_AFF_CAUSE_SNS_NO_ENDPOINTS);</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (!gss->initial)</span><br><span style="color: hsl(120, 100%, 40%);">+                  gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);</span><br><span style="color: hsl(120, 100%, 40%);">+           else if (gss->initial->list.next == &gss->sns_endpoints) /* last entry, continue with first */</span><br><span style="color: hsl(120, 100%, 40%);">+                   gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);</span><br><span style="color: hsl(120, 100%, 40%);">+           else /* next element is an entry */</span><br><span style="color: hsl(120, 100%, 40%);">+                   gss->initial = llist_entry(gss->initial->list.next, struct sns_endpoint, list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            gss->reselection_running = false;</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);</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 struct osmo_fsm gprs_ns2_sns_bss_fsm = {</span><br><span>        .name = "GPRS-NS2-SNS-BSS",</span><br><span>        .states = ns2_sns_bss_states,</span><br><span>        .num_states = ARRAY_SIZE(ns2_sns_bss_states),</span><br><span style="color: hsl(0, 100%, 40%);">-   .allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC),</span><br><span style="color: hsl(120, 100%, 40%);">+        .allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC) |</span><br><span style="color: hsl(120, 100%, 40%);">+                              S(GPRS_SNS_EV_SELECT_ENDPOINT),</span><br><span>       .allstate_action = ns2_sns_st_all_action,</span><br><span>    .cleanup = NULL,</span><br><span>     .timer_cb = ns2_sns_fsm_bss_timer_cb,</span><br><span>@@ -1241,134 +1388,6 @@</span><br><span>      return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Start an IP-SNS FSM.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] nse NS Entity whose IP-SNS FSM shall be started</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] nsvc Initial NS-VC</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] remote remote (SGSN) address</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns 0 on success; negative on error */</span><br><span style="color: hsl(0, 100%, 40%);">-int ns2_sns_bss_fsm_start(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc,</span><br><span style="color: hsl(0, 100%, 40%);">-                       const struct osmo_sockaddr *remote)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  struct osmo_fsm_inst *fi = nse->bss_sns_fi;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct ns2_sns_state *gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;</span><br><span style="color: hsl(0, 100%, 40%);">-       struct gprs_ns_ie_ip4_elem *ip4_elems;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct gprs_ns_ie_ip6_elem *ip6_elems;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct gprs_ns2_inst *nsi = nse->nsi;</span><br><span style="color: hsl(0, 100%, 40%);">-        const struct osmo_sockaddr *sa;</span><br><span style="color: hsl(0, 100%, 40%);">- struct osmo_sockaddr local;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     gss->ip = remote->u.sa.sa_family == AF_INET ? IPv4 : IPv6;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        gss->initial = *remote;</span><br><span style="color: hsl(0, 100%, 40%);">-      gss->sns_nsvc = nsvc;</span><br><span style="color: hsl(0, 100%, 40%);">-        nsvc->sns_only = true;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* count how many bindings are available (only UDP binds) */</span><br><span style="color: hsl(0, 100%, 40%);">-    int count = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-  llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-                if (!gprs_ns2_is_ip_bind(bind))</span><br><span style="color: hsl(0, 100%, 40%);">-                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(0, 100%, 40%);">-           if (!sa)</span><br><span style="color: hsl(0, 100%, 40%);">-                        continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (sa->u.sa.sa_family == remote->u.sa.sa_family)</span><br><span style="color: hsl(0, 100%, 40%);">-                 count++;</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (count == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-               /* TODO: logging */</span><br><span style="color: hsl(0, 100%, 40%);">-             goto err;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       switch (gss->ip) {</span><br><span style="color: hsl(0, 100%, 40%);">-   case IPv4:</span><br><span style="color: hsl(0, 100%, 40%);">-              ip4_elems = talloc_zero_size(fi, sizeof(struct gprs_ns_ie_ip4_elem) * count);</span><br><span style="color: hsl(0, 100%, 40%);">-           if (!ip4_elems)</span><br><span style="color: hsl(0, 100%, 40%);">-                 goto err;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               gss->ip4_local = ip4_elems;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-          llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (!gprs_ns2_is_ip_bind(bind))</span><br><span style="color: hsl(0, 100%, 40%);">-                         continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (!sa)</span><br><span style="color: hsl(0, 100%, 40%);">-                                continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (sa->u.sas.ss_family != AF_INET)</span><br><span style="color: hsl(0, 100%, 40%);">-                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       /* check if this is an specific bind */</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (sa->u.sin.sin_addr.s_addr == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                if (osmo_sockaddr_local_ip(&local, remote))</span><br><span style="color: hsl(0, 100%, 40%);">-                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                               ip4_elems->ip_addr = local.u.sin.sin_addr.s_addr;</span><br><span style="color: hsl(0, 100%, 40%);">-                    } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                ip4_elems->ip_addr = sa->u.sin.sin_addr.s_addr;</span><br><span style="color: hsl(0, 100%, 40%);">-                   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       ip4_elems->udp_port = sa->u.sin.sin_port;</span><br><span style="color: hsl(0, 100%, 40%);">-                 ip4_elems->sig_weight = 2;</span><br><span style="color: hsl(0, 100%, 40%);">-                   ip4_elems->data_weight = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                  ip4_elems++;</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               gss->num_ip4_local = count;</span><br><span style="color: hsl(0, 100%, 40%);">-          gss->num_max_ip4_remote = 4;</span><br><span style="color: hsl(0, 100%, 40%);">-         gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * 4, 8);</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(0, 100%, 40%);">-  case IPv6:</span><br><span style="color: hsl(0, 100%, 40%);">-              /* IPv6 */</span><br><span style="color: hsl(0, 100%, 40%);">-              ip6_elems = talloc_zero_size(fi, sizeof(struct gprs_ns_ie_ip6_elem) * count);</span><br><span style="color: hsl(0, 100%, 40%);">-           if (!ip6_elems)</span><br><span style="color: hsl(0, 100%, 40%);">-                 goto err;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               gss->ip6_local = ip6_elems;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-          llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (!gprs_ns2_is_ip_bind(bind))</span><br><span style="color: hsl(0, 100%, 40%);">-                         continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (!sa)</span><br><span style="color: hsl(0, 100%, 40%);">-                                continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (sa->u.sas.ss_family != AF_INET6)</span><br><span style="color: hsl(0, 100%, 40%);">-                         continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       /* check if this is an specific bind */</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            if (osmo_sockaddr_local_ip(&local, remote))</span><br><span style="color: hsl(0, 100%, 40%);">-                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                               ip6_elems->ip_addr = local.u.sin6.sin6_addr;</span><br><span style="color: hsl(0, 100%, 40%);">-                 } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                ip6_elems->ip_addr = sa->u.sin6.sin6_addr;</span><br><span style="color: hsl(0, 100%, 40%);">-                        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       ip6_elems->udp_port = sa->u.sin.sin_port;</span><br><span style="color: hsl(0, 100%, 40%);">-                 ip6_elems->sig_weight = 2;</span><br><span style="color: hsl(0, 100%, 40%);">-                   ip6_elems->data_weight = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                  ip6_elems++;</span><br><span style="color: hsl(0, 100%, 40%);">-            }</span><br><span style="color: hsl(0, 100%, 40%);">-               gss->num_ip6_local = count;</span><br><span style="color: hsl(0, 100%, 40%);">-          gss->num_max_ip6_remote = 4;</span><br><span style="color: hsl(0, 100%, 40%);">-         gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * 4, 8);</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_START, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-err:</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! main entry point for receiving SNS messages from the network.</span><br><span>  *  \param[in] nsvc NS-VC on which the message was received</span><br><span>  *  \param[in] msg message buffer of the IP-SNS message</span><br><span>@@ -1490,6 +1509,113 @@</span><br><span>         }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct sns_endpoint *ns2_get_sns_endpoint(struct ns2_sns_state *state,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              const struct osmo_sockaddr *saddr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct sns_endpoint *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(endpoint, &state->sns_endpoints, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!osmo_sockaddr_cmp(saddr, &endpoint->saddr))</span><br><span style="color: hsl(120, 100%, 40%);">+                       return endpoint;</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%);">+/*! gprs_ns2_sns_add_endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] nse</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] sockaddr</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%);">+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,</span><br><span style="color: hsl(120, 100%, 40%);">+                            const struct osmo_sockaddr *saddr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ns2_sns_state *gss;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct sns_endpoint *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+        bool do_selection = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (nse->ll != GPRS_NS2_LL_UDP) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -EINVAL;</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 (nse->dialect != NS2_DIALECT_SNS) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</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%);">+   gss = nse->bss_sns_fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ns2_get_sns_endpoint(gss, saddr))</span><br><span style="color: hsl(120, 100%, 40%);">+         return -EADDRINUSE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint = talloc_zero(nse->bss_sns_fi->priv, struct sns_endpoint);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!endpoint)</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%);">+     endpoint->saddr = *saddr;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (llist_empty(&gss->sns_endpoints))</span><br><span style="color: hsl(120, 100%, 40%);">+          do_selection = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_add_tail(&endpoint->list, &gss->sns_endpoints);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (do_selection)</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_SELECT_ENDPOINT, NULL);</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 style="color: hsl(120, 100%, 40%);">+/*! gprs_ns2_sns_del_endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] nse</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] sockaddr</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return 0 on success, otherwise < 0</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,</span><br><span style="color: hsl(120, 100%, 40%);">+                        const struct osmo_sockaddr *saddr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ns2_sns_state *gss;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct sns_endpoint *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (nse->ll != GPRS_NS2_LL_UDP) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -EINVAL;</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 (nse->dialect != NS2_DIALECT_SNS) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</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%);">+   gss = nse->bss_sns_fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+    endpoint = ns2_get_sns_endpoint(gss, saddr);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!endpoint)</span><br><span style="color: hsl(120, 100%, 40%);">+                return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* if this is an unused SNS endpoint it's done */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (gss->initial != endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_del(&endpoint->list);</span><br><span style="color: hsl(120, 100%, 40%);">+            talloc_free(endpoint);</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 style="color: hsl(120, 100%, 40%);">+   /* gprs_ns2_free_nsvcs() will trigger GPRS_SNS_EV_NO_NSVC on the last NS-VC</span><br><span style="color: hsl(120, 100%, 40%);">+    * and restart SNS SIZE procedure which selects a new initial */</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DLNS, LOGL_INFO, "Current in-use SNS endpoint is being removed."</span><br><span style="color: hsl(120, 100%, 40%);">+                             "Closing all NS-VC and restart SNS-SIZE procedure"</span><br><span style="color: hsl(120, 100%, 40%);">+                          "with a remaining SNS endpoint.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Continue with the next endpoint in the list.</span><br><span style="color: hsl(120, 100%, 40%);">+        * Special case if the endpoint is at the start or end of the list */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (endpoint->list.prev == &gss->sns_endpoints ||</span><br><span style="color: hsl(120, 100%, 40%);">+                   endpoint->list.next == &gss->sns_endpoints)</span><br><span style="color: hsl(120, 100%, 40%);">+         gss->initial = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       else</span><br><span style="color: hsl(120, 100%, 40%);">+          gss->initial = llist_entry(endpoint->list.next->prev,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            struct sns_endpoint,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_del(&endpoint->list);</span><br><span style="color: hsl(120, 100%, 40%);">+    gprs_ns2_free_nsvcs(nse);</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_free(endpoint);</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> /* initialize osmo_ctx on main tread */</span><br><span> static __attribute__((constructor)) void on_dso_load_ctx(void)</span><br><span> {</span><br><span>diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c</span><br><span>index c0ab5f5..69ea83f 100644</span><br><span>--- a/src/gb/gprs_ns2_udp.c</span><br><span>+++ b/src/gb/gprs_ns2_udp.c</span><br><span>@@ -513,3 +513,52 @@</span><br><span> </span><br><span>         return rc;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Count UDP binds compatible with remote */</span><br><span style="color: hsl(120, 100%, 40%);">+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_sockaddr *sa;</span><br><span style="color: hsl(120, 100%, 40%);">+       int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!gprs_ns2_is_ip_bind(bind))</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%);">+           sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!sa)</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%);">+           if (sa->u.sa.sa_family == remote->u.sa.sa_family)</span><br><span style="color: hsl(120, 100%, 40%);">+                       count++;</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 count;</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 the matching bind by offset */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_offset(struct gprs_ns2_inst *nsi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                               struct osmo_sockaddr *remote,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 int offset)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gprs_ns2_vc_bind *bind;</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_sockaddr *sa;</span><br><span style="color: hsl(120, 100%, 40%);">+       int i = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(bind, &nsi->binding, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!gprs_ns2_is_ip_bind(bind))</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%);">+           sa = gprs_ns2_ip_bind_sockaddr(bind);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!sa)</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%);">+           if (sa->u.sa.sa_family == remote->u.sa.sa_family) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (offset == i)</span><br><span style="color: hsl(120, 100%, 40%);">+                              return bind;</span><br><span style="color: hsl(120, 100%, 40%);">+                  i++;</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%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map</span><br><span>index 7e7971a..7fcac6a 100644</span><br><span>--- a/src/gb/libosmogb.map</span><br><span>+++ b/src/gb/libosmogb.map</span><br><span>@@ -111,7 +111,6 @@</span><br><span> gprs_ns2_ip_connect;</span><br><span> gprs_ns2_ip_connect2;</span><br><span> gprs_ns2_ip_connect_inactive;</span><br><span style="color: hsl(0, 100%, 40%);">-gprs_ns2_ip_connect_sns;</span><br><span> gprs_ns2_ip_vc_local;</span><br><span> gprs_ns2_ip_vc_remote;</span><br><span> gprs_ns2_ip_vc_equal;</span><br><span>@@ -132,6 +131,8 @@</span><br><span> gprs_ns2_recv_prim;</span><br><span> gprs_ns2_reset_persistent_nsvcs;</span><br><span> gprs_ns2_start_alive_all_nsvcs;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_sns_add_endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+gprs_ns2_sns_del_endpoint;</span><br><span> gprs_ns2_vty_create;</span><br><span> gprs_ns2_vty_init;</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/21573">change 21573</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/+/21573"/><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: I71cdbfb53e361e6112fed5e2712236d797ef3ab2 </div>
<div style="display:none"> Gerrit-Change-Number: 21573 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: lynxis lazus <lynxis@fe80.eu> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>