lynxis lazus has submitted this change. ( 
https://gerrit.osmocom.org/c/libosmocore/+/21573 )

Change subject: gprs_ns2_sns: rework IP-SNS initial remote
......................................................................

gprs_ns2_sns: rework IP-SNS initial remote

The IP-SNS requires at least one initial remote address of the SGSN.
However it should be multiple initial remote address instead of a single
in case the interface might fail.
Rework the SNS to support multiple initial remote addresses.

Change-Id: I71cdbfb53e361e6112fed5e2712236d797ef3ab2
---
M include/osmocom/gprs/gprs_ns2.h
M src/gb/gprs_ns2.c
M src/gb/gprs_ns2_internal.h
M src/gb/gprs_ns2_sns.c
M src/gb/gprs_ns2_udp.c
M src/gb/libosmogb.map
6 files changed, 364 insertions(+), 190 deletions(-)

Approvals:
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified



diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index 00879d7..8dd5699 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -82,6 +82,7 @@
        /* osmocom own causes */
        NS_AFF_CAUSE_SNS_CONFIGURED,
        NS_AFF_CAUSE_SNS_FAILURE,
+       NS_AFF_CAUSE_SNS_NO_ENDPOINTS,
 };

 extern const struct value_string gprs_ns2_aff_cause_prim_strs[];
@@ -218,9 +219,11 @@
 void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi);

 /* create a VC SNS connection */
-int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,
-                           const struct osmo_sockaddr *remote,
-                           uint16_t nsei);
+int gprs_ns2_sns_count(struct gprs_ns2_nse *nse);
+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,
+                                  const struct osmo_sockaddr *saddr);
+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
+                                  const struct osmo_sockaddr *saddr);
 const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse);

 const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc 
*nsvc);
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
index 21c69cb..5e5dd83 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -953,42 +953,6 @@
        return gprs_ns2_ip_connect(bind, remote, nse, nsvci);
 }

-/*! Create, connect and activate a new IP-SNS NSE.
- *  \param[in] bind bind in which the new NS-VC is to be created
- *  \param[in] remote remote address to which to connect
- *  \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
- *  \return 0 on success; negative on error */
-int gprs_ns2_ip_connect_sns(struct gprs_ns2_vc_bind *bind,
-                           const struct osmo_sockaddr *remote,
-                           uint16_t nsei)
-{
-       struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
-       struct gprs_ns2_vc *nsvc;
-
-       if (!nse) {
-               nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_UDP, 
NS2_DIALECT_SNS);
-               if (!nse)
-                       return -1;
-       }
-
-       if (nse->ll != GPRS_NS2_LL_UDP) {
-               return -2;
-       }
-
-       if (nse->dialect != NS2_DIALECT_SNS) {
-               return -2;
-       }
-
-       if (!nse->bss_sns_fi)
-               return -1;
-
-       nsvc = gprs_ns2_ip_bind_connect(bind, nse, remote);
-       if (!nsvc)
-               return -1;
-
-       return ns2_sns_bss_fsm_start(nse, nsvc, remote);
-}
-
 /*! Find NS-VC for given socket address.
  *  \param[in] nse NS Entity in which to search
  *  \param[in] sockaddr socket address to search for
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
index 9bfe0b0..d12c663 100644
--- a/src/gb/gprs_ns2_internal.h
+++ b/src/gb/gprs_ns2_internal.h
@@ -286,13 +286,15 @@
 struct gprs_ns2_vc *gprs_ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
                                             struct gprs_ns2_nse *nse,
                                             const struct osmo_sockaddr 
*remote);
+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote);
+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
+                                                 struct osmo_sockaddr *remote,
+                                                 int index);

 /* sns */
 int gprs_ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct 
tlv_parsed *tp);
 struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
                                             const char *id);
-int ns2_sns_bss_fsm_start(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc,
-                         const struct osmo_sockaddr *remote);
 void ns2_sns_free_nsvc(struct gprs_ns2_vc *nsvc);

 /* vc */
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index 5d18d04..b7fbbf8 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -69,7 +69,7 @@
 };

 enum gprs_sns_event {
-       GPRS_SNS_EV_START,
+       GPRS_SNS_EV_SELECT_ENDPOINT,    /*!< Select a SNS endpoint from the 
list */
        GPRS_SNS_EV_SIZE,
        GPRS_SNS_EV_SIZE_ACK,
        GPRS_SNS_EV_CONFIG,
@@ -82,7 +82,7 @@
 };

 static const struct value_string gprs_sns_event_names[] = {
-       { GPRS_SNS_EV_START,            "START" },
+       { GPRS_SNS_EV_SELECT_ENDPOINT,  "SELECT_ENDPOINT" },
        { GPRS_SNS_EV_SIZE,             "SIZE" },
        { GPRS_SNS_EV_SIZE_ACK,         "SIZE_ACK" },
        { GPRS_SNS_EV_CONFIG,           "CONFIG" },
@@ -95,14 +95,26 @@
        { 0, NULL }
 };

+struct sns_endpoint {
+       struct llist_head list;
+       struct osmo_sockaddr saddr;
+};
+
 struct ns2_sns_state {
        struct gprs_ns2_nse *nse;

        enum ns2_sns_type ip;

-       /* initial connection. the initial connection will be terminated
-        * in configured state or moved into NSE if valid */
-       struct osmo_sockaddr initial;
+       /* holds the list of initial SNS endpoints */
+       struct llist_head sns_endpoints;
+       /* prevent recursive reselection */
+       bool reselection_running;
+
+       /* The current initial SNS endpoints.
+        * The initial connection will be moved into the NSE
+        * if configured via SNS. Otherwise it will be removed
+        * in configured state. */
+       struct sns_endpoint *initial;
        /* all SNS PDU will be sent over this nsvc */
        struct gprs_ns2_vc *sns_nsvc;

@@ -207,7 +219,7 @@
                return NULL;

        gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
-       return &gss->initial;
+       return &gss->initial->saddr;
 }

 /*! called when a nsvc is beeing freed */
@@ -646,16 +658,7 @@

 static void ns2_sns_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, 
void *data)
 {
-       struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
-       struct gprs_ns2_inst *nsi = nse->nsi;
-
-       switch (event) {
-       case GPRS_SNS_EV_START:
-               osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, 
nsi->timeout[NS_TOUT_TSNS_PROV], 1);
-               break;
-       default:
-               OSMO_ASSERT(0);
-       }
+       /* empty state - SNS Select will start by ns2_sns_st_all_action() */
 }

 static void ns2_sns_st_size(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
@@ -681,18 +684,133 @@
        }
 }

+/* setup all dynamic SNS settings, create a new nsvc and send the SIZE */
 static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t 
old_state)
 {
        struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+       struct gprs_ns_ie_ip4_elem *ip4_elems;
+       struct gprs_ns_ie_ip6_elem *ip6_elems;
+       struct gprs_ns2_vc_bind *bind;
+       struct gprs_ns2_inst *nsi = gss->nse->nsi;
+       struct osmo_sockaddr *remote;
+       const struct osmo_sockaddr *sa;
+       struct osmo_sockaddr local;
+       int count;

+       /* on a generic failure, the timer callback will recover */
        if (old_state != GPRS_SNS_ST_UNCONFIGURED)
                ns2_prim_status_ind(gss->nse, NULL, 0, 
NS_AFF_CAUSE_SNS_FAILURE);

+       /* no initial available */
+       if (!gss->initial)
+               return;
+
+       remote = &gss->initial->saddr;
+
+       /* count how many bindings are available (only UDP binds) */
+       count = ns2_ip_count_bind(nsi, remote);
+       if (count == 0) {
+               /* TODO: logging */
+               return;
+       }
+
+       bind = ns2_ip_get_bind_by_index(nsi, remote, 0);
+       if (!bind) {
+               return;
+       }
+
+       /* setup the NSVC */
+       if (!gss->sns_nsvc) {
+               gss->sns_nsvc = gprs_ns2_ip_bind_connect(bind, gss->nse, 
remote);
+               if (!gss->sns_nsvc)
+                       return;
+               gss->sns_nsvc->sns_only = true;
+       }
+
+       switch (gss->ip) {
+       case IPv4:
+               ip4_elems = talloc_zero_size(fi, sizeof(struct 
gprs_ns_ie_ip4_elem) * count);
+               if (!ip4_elems)
+                       return;
+
+               gss->ip4_local = ip4_elems;
+
+               llist_for_each_entry(bind, &nsi->binding, list) {
+                       if (!gprs_ns2_is_ip_bind(bind))
+                               continue;
+
+                       sa = gprs_ns2_ip_bind_sockaddr(bind);
+                       if (!sa)
+                               continue;
+
+                       if (sa->u.sas.ss_family != AF_INET)
+                               continue;
+
+                       /* check if this is an specific bind */
+                       if (sa->u.sin.sin_addr.s_addr == 0) {
+                               if (osmo_sockaddr_local_ip(&local, remote))
+                                       continue;
+
+                               ip4_elems->ip_addr = 
local.u.sin.sin_addr.s_addr;
+                       } else {
+                               ip4_elems->ip_addr = sa->u.sin.sin_addr.s_addr;
+                       }
+
+                       ip4_elems->udp_port = sa->u.sin.sin_port;
+                       ip4_elems->sig_weight = 2;
+                       ip4_elems->data_weight = 1;
+                       ip4_elems++;
+               }
+
+               gss->num_ip4_local = count;
+               gss->num_max_ip4_remote = 4;
+               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * 
gss->num_ip4_local, 8);
+               break;
+       case IPv6:
+               /* IPv6 */
+               ip6_elems = talloc_zero_size(fi, sizeof(struct 
gprs_ns_ie_ip6_elem) * count);
+               if (!ip6_elems)
+                       return;
+
+               gss->ip6_local = ip6_elems;
+
+               llist_for_each_entry(bind, &nsi->binding, list) {
+                       if (!gprs_ns2_is_ip_bind(bind))
+                               continue;
+
+                       sa = gprs_ns2_ip_bind_sockaddr(bind);
+                       if (!sa)
+                               continue;
+
+                       if (sa->u.sas.ss_family != AF_INET6)
+                               continue;
+
+                       /* check if this is an specific bind */
+                       if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {
+                               if (osmo_sockaddr_local_ip(&local, remote))
+                                       continue;
+
+                               ip6_elems->ip_addr = local.u.sin6.sin6_addr;
+                       } else {
+                               ip6_elems->ip_addr = sa->u.sin6.sin6_addr;
+                       }
+
+                       ip6_elems->udp_port = sa->u.sin.sin_port;
+                       ip6_elems->sig_weight = 2;
+                       ip6_elems->data_weight = 1;
+
+                       ip6_elems++;
+               }
+               gss->num_ip6_local = count;
+               gss->num_max_ip6_remote = 4;
+               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * 
gss->num_ip6_local, 8);
+               break;
+       }
+
        if (gss->num_max_ip4_remote > 0)
                ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, 
gss->num_max_ip4_remote, -1);
        else
                ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, 
gss->num_max_ip6_remote);
-
 }

 static void ns2_sns_st_config_bss(struct osmo_fsm_inst *fi, uint32_t event, 
void *data)
@@ -1130,7 +1248,7 @@

 static const struct osmo_fsm_state ns2_sns_bss_states[] = {
        [GPRS_SNS_ST_UNCONFIGURED] = {
-               .in_event_mask = S(GPRS_SNS_EV_START),
+               .in_event_mask = 0, /* handled by all_state_action */
                .out_state_mask = S(GPRS_SNS_ST_SIZE),
                .name = "UNCONFIGURED",
                .action = ns2_sns_st_unconfigured,
@@ -1194,17 +1312,49 @@

 static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, 
void *data)
 {
+       struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
        struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);

        /* reset when receiving GPRS_SNS_EV_NO_NSVC */
-       osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, 
nse->nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+       switch (event) {
+       case GPRS_SNS_EV_NO_NSVC:
+               if (!gss->reselection_running)
+                       osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_SELECT_ENDPOINT, 
NULL);
+               break;
+       case GPRS_SNS_EV_SELECT_ENDPOINT:
+               /* tear down previous state
+                * gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this 
from triggering a reselection */
+               gss->reselection_running = true;
+               gprs_ns2_free_nsvcs(nse);
+
+               /* Choose the next sns endpoint. */
+               if (llist_empty(&gss->sns_endpoints)) {
+                       gss->initial = NULL;
+                       ns2_prim_status_ind(gss->nse, NULL, 0, 
NS_AFF_CAUSE_SNS_NO_ENDPOINTS);
+                       osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 
0, 3);
+                       return;
+               } else if (!gss->initial) {
+                       gss->initial = llist_first_entry(&gss->sns_endpoints, 
struct sns_endpoint, list);
+               } else if (gss->initial->list.next == &gss->sns_endpoints) {
+                       /* last entry, continue with first */
+                       gss->initial = llist_first_entry(&gss->sns_endpoints, 
struct sns_endpoint, list);
+               } else {
+                       /* next element is an entry */
+                       gss->initial = llist_entry(gss->initial->list.next, 
struct sns_endpoint, list);
+               }
+
+               gss->reselection_running = false;
+               osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, 
nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
+               break;
+       }
 }

 static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
        .name = "GPRS-NS2-SNS-BSS",
        .states = ns2_sns_bss_states,
        .num_states = ARRAY_SIZE(ns2_sns_bss_states),
-       .allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC),
+       .allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC) |
+                              S(GPRS_SNS_EV_SELECT_ENDPOINT),
        .allstate_action = ns2_sns_st_all_action,
        .cleanup = NULL,
        .timer_cb = ns2_sns_fsm_bss_timer_cb,
@@ -1234,6 +1384,7 @@

        fi->priv = gss;
        gss->nse = nse;
+       INIT_LLIST_HEAD(&gss->sns_endpoints);

        return fi;
 err:
@@ -1241,134 +1392,6 @@
        return NULL;
 }

-/*! Start an IP-SNS FSM.
- *  \param[in] nse NS Entity whose IP-SNS FSM shall be started
- *  \param[in] nsvc Initial NS-VC
- *  \param[in] remote remote (SGSN) address
- *  \returns 0 on success; negative on error */
-int ns2_sns_bss_fsm_start(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc,
-                         const struct osmo_sockaddr *remote)
-{
-       struct osmo_fsm_inst *fi = nse->bss_sns_fi;
-       struct ns2_sns_state *gss = (struct ns2_sns_state *) 
nse->bss_sns_fi->priv;
-       struct gprs_ns_ie_ip4_elem *ip4_elems;
-       struct gprs_ns_ie_ip6_elem *ip6_elems;
-       struct gprs_ns2_vc_bind *bind;
-       struct gprs_ns2_inst *nsi = nse->nsi;
-       const struct osmo_sockaddr *sa;
-       struct osmo_sockaddr local;
-
-       gss->ip = remote->u.sa.sa_family == AF_INET ? IPv4 : IPv6;
-
-       gss->initial = *remote;
-       gss->sns_nsvc = nsvc;
-       nsvc->sns_only = true;
-
-       /* count how many bindings are available (only UDP binds) */
-       int count = 0;
-       llist_for_each_entry(bind, &nsi->binding, list) {
-               if (!gprs_ns2_is_ip_bind(bind))
-                       continue;
-
-               sa = gprs_ns2_ip_bind_sockaddr(bind);
-               if (!sa)
-                       continue;
-
-               if (sa->u.sa.sa_family == remote->u.sa.sa_family)
-                       count++;
-       }
-
-       if (count == 0) {
-               /* TODO: logging */
-               goto err;
-       }
-
-       switch (gss->ip) {
-       case IPv4:
-               ip4_elems = talloc_zero_size(fi, sizeof(struct 
gprs_ns_ie_ip4_elem) * count);
-               if (!ip4_elems)
-                       goto err;
-
-               gss->ip4_local = ip4_elems;
-
-               llist_for_each_entry(bind, &nsi->binding, list) {
-                       if (!gprs_ns2_is_ip_bind(bind))
-                               continue;
-
-                       sa = gprs_ns2_ip_bind_sockaddr(bind);
-                       if (!sa)
-                               continue;
-
-                       if (sa->u.sas.ss_family != AF_INET)
-                               continue;
-
-                       /* check if this is an specific bind */
-                       if (sa->u.sin.sin_addr.s_addr == 0) {
-                               if (osmo_sockaddr_local_ip(&local, remote))
-                                       continue;
-
-                               ip4_elems->ip_addr = 
local.u.sin.sin_addr.s_addr;
-                       } else {
-                               ip4_elems->ip_addr = sa->u.sin.sin_addr.s_addr;
-                       }
-
-                       ip4_elems->udp_port = sa->u.sin.sin_port;
-                       ip4_elems->sig_weight = 2;
-                       ip4_elems->data_weight = 1;
-                       ip4_elems++;
-               }
-
-               gss->num_ip4_local = count;
-               gss->num_max_ip4_remote = 4;
-               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * 
gss->num_ip4_local, 8);
-               break;
-       case IPv6:
-               /* IPv6 */
-               ip6_elems = talloc_zero_size(fi, sizeof(struct 
gprs_ns_ie_ip6_elem) * count);
-               if (!ip6_elems)
-                       goto err;
-
-               gss->ip6_local = ip6_elems;
-
-               llist_for_each_entry(bind, &nsi->binding, list) {
-                       if (!gprs_ns2_is_ip_bind(bind))
-                               continue;
-
-                       sa = gprs_ns2_ip_bind_sockaddr(bind);
-                       if (!sa)
-                               continue;
-
-                       if (sa->u.sas.ss_family != AF_INET6)
-                               continue;
-
-                       /* check if this is an specific bind */
-                       if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {
-                               if (osmo_sockaddr_local_ip(&local, remote))
-                                       continue;
-
-                               ip6_elems->ip_addr = local.u.sin6.sin6_addr;
-                       } else {
-                               ip6_elems->ip_addr = sa->u.sin6.sin6_addr;
-                       }
-
-                       ip6_elems->udp_port = sa->u.sin.sin_port;
-                       ip6_elems->sig_weight = 2;
-                       ip6_elems->data_weight = 1;
-
-                       ip6_elems++;
-               }
-               gss->num_ip6_local = count;
-               gss->num_max_ip6_remote = 4;
-               gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * 
gss->num_ip6_local, 8);
-               break;
-       }
-
-       return osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_START, NULL);
-
-err:
-       return -1;
-}
-
 /*! main entry point for receiving SNS messages from the network.
  *  \param[in] nsvc NS-VC on which the message was received
  *  \param[in] msg message buffer of the IP-SNS message
@@ -1490,6 +1513,138 @@
        }
 }

+static struct sns_endpoint *ns2_get_sns_endpoint(struct ns2_sns_state *state,
+                                                const struct osmo_sockaddr 
*saddr)
+{
+       struct sns_endpoint *endpoint;
+
+       llist_for_each_entry(endpoint, &state->sns_endpoints, list) {
+               if (!osmo_sockaddr_cmp(saddr, &endpoint->saddr))
+                       return endpoint;
+       }
+
+       return NULL;
+}
+
+/*! gprs_ns2_sns_add_endpoint
+ *  \param[in] nse
+ *  \param[in] sockaddr
+ *  \return
+ */
+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,
+                             const struct osmo_sockaddr *saddr)
+{
+       struct ns2_sns_state *gss;
+       struct sns_endpoint *endpoint;
+       bool do_selection = false;
+
+       if (nse->ll != GPRS_NS2_LL_UDP) {
+               return -EINVAL;
+       }
+
+       if (nse->dialect != NS2_DIALECT_SNS) {
+               return -EINVAL;
+       }
+
+       gss = nse->bss_sns_fi->priv;
+
+       if (ns2_get_sns_endpoint(gss, saddr))
+               return -EADDRINUSE;
+
+       endpoint = talloc_zero(nse->bss_sns_fi->priv, struct sns_endpoint);
+       if (!endpoint)
+               return -ENOMEM;
+
+       endpoint->saddr = *saddr;
+       if (llist_empty(&gss->sns_endpoints))
+               do_selection = true;
+
+       llist_add_tail(&endpoint->list, &gss->sns_endpoints);
+       if (do_selection)
+               osmo_fsm_inst_dispatch(nse->bss_sns_fi, 
GPRS_SNS_EV_SELECT_ENDPOINT, NULL);
+
+       return 0;
+}
+
+/*! gprs_ns2_sns_del_endpoint
+ *  \param[in] nse
+ *  \param[in] sockaddr
+ *  \return 0 on success, otherwise < 0
+ */
+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
+                             const struct osmo_sockaddr *saddr)
+{
+       struct ns2_sns_state *gss;
+       struct sns_endpoint *endpoint;
+
+       if (nse->ll != GPRS_NS2_LL_UDP) {
+               return -EINVAL;
+       }
+
+       if (nse->dialect != NS2_DIALECT_SNS) {
+               return -EINVAL;
+       }
+
+       gss = nse->bss_sns_fi->priv;
+       endpoint = ns2_get_sns_endpoint(gss, saddr);
+       if (!endpoint)
+               return -ENOENT;
+
+       /* if this is an unused SNS endpoint it's done */
+       if (gss->initial != endpoint) {
+               llist_del(&endpoint->list);
+               talloc_free(endpoint);
+               return 0;
+       }
+
+       /* gprs_ns2_free_nsvcs() will trigger GPRS_SNS_EV_NO_NSVC on the last 
NS-VC
+        * and restart SNS SIZE procedure which selects a new initial */
+       LOGP(DLNS, LOGL_INFO, "Current in-use SNS endpoint is being removed."
+                             "Closing all NS-VC and restart SNS-SIZE procedure"
+                             "with a remaining SNS endpoint.\n");
+
+       /* Continue with the next endpoint in the list.
+        * Special case if the endpoint is at the start or end of the list */
+       if (endpoint->list.prev == &gss->sns_endpoints ||
+                       endpoint->list.next == &gss->sns_endpoints)
+               gss->initial = NULL;
+       else
+               gss->initial = llist_entry(endpoint->list.next->prev,
+                                           struct sns_endpoint,
+                                           list);
+
+       llist_del(&endpoint->list);
+       gprs_ns2_free_nsvcs(nse);
+       talloc_free(endpoint);
+
+       return 0;
+}
+
+/*! gprs_ns2_sns_count
+ *  \param[in] nse NS Entity whose IP-SNS endpoints shall be printed
+ *  \return the count of endpoints or < 0 if NSE doesn't contain sns.
+ */
+int gprs_ns2_sns_count(struct gprs_ns2_nse *nse)
+{
+       struct ns2_sns_state *gss;
+       struct sns_endpoint *endpoint;
+       int count = 0;
+
+       if (nse->ll != GPRS_NS2_LL_UDP) {
+               return -EINVAL;
+       }
+
+       if (nse->dialect != NS2_DIALECT_SNS) {
+               return -EINVAL;
+       }
+
+       gss = nse->bss_sns_fi->priv;
+       llist_for_each_entry(endpoint, &gss->sns_endpoints, list)
+               count++;
+
+       return count;
+}
+
 /* initialize osmo_ctx on main tread */
 static __attribute__((constructor)) void on_dso_load_ctx(void)
 {
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
index 3eb8116..9058279 100644
--- a/src/gb/gprs_ns2_udp.c
+++ b/src/gb/gprs_ns2_udp.c
@@ -514,3 +514,52 @@

        return rc;
 }
+
+/*! Count UDP binds compatible with remote */
+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote)
+{
+       struct gprs_ns2_vc_bind *bind;
+       const struct osmo_sockaddr *sa;
+       int count = 0;
+
+       llist_for_each_entry(bind, &nsi->binding, list) {
+               if (!gprs_ns2_is_ip_bind(bind))
+                       continue;
+
+               sa = gprs_ns2_ip_bind_sockaddr(bind);
+               if (!sa)
+                       continue;
+
+               if (sa->u.sa.sa_family == remote->u.sa.sa_family)
+                       count++;
+       }
+
+       return count;
+}
+
+/* return the matching bind by index */
+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
+                                                 struct osmo_sockaddr *remote,
+                                                 int index)
+{
+       struct gprs_ns2_vc_bind *bind;
+       const struct osmo_sockaddr *sa;
+       int i = 0;
+
+       llist_for_each_entry(bind, &nsi->binding, list) {
+               if (!gprs_ns2_is_ip_bind(bind))
+                       continue;
+
+               sa = gprs_ns2_ip_bind_sockaddr(bind);
+               if (!sa)
+                       continue;
+
+               if (sa->u.sa.sa_family == remote->u.sa.sa_family) {
+                       if (index == i)
+                               return bind;
+                       i++;
+               }
+       }
+
+       return NULL;
+}
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index d4114df..59405b0 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -138,7 +138,6 @@
 gprs_ns2_ip_connect;
 gprs_ns2_ip_connect2;
 gprs_ns2_ip_connect_inactive;
-gprs_ns2_ip_connect_sns;
 gprs_ns2_ip_vc_local;
 gprs_ns2_ip_vc_remote;
 gprs_ns2_ip_vc_equal;
@@ -159,6 +158,8 @@
 gprs_ns2_recv_prim;
 gprs_ns2_reset_persistent_nsvcs;
 gprs_ns2_start_alive_all_nsvcs;
+gprs_ns2_sns_add_endpoint;
+gprs_ns2_sns_del_endpoint;
 gprs_ns2_vty_create;
 gprs_ns2_vty_init;


--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/21573
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I71cdbfb53e361e6112fed5e2712236d797ef3ab2
Gerrit-Change-Number: 21573
Gerrit-PatchSet: 12
Gerrit-Owner: lynxis lazus <lyn...@fe80.eu>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: lynxis lazus <lyn...@fe80.eu>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>
Gerrit-MessageType: merged

Reply via email to