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

Change subject: gprs_ns2: rework IP-SNS binds
......................................................................

gprs_ns2: rework IP-SNS binds

Introduce a `ip-sns-bind BINDID` vty command within a `nse` vty object.
The ip-sns-bind defines the binds which will be used by the dynamic
configuration with IP-SNS.
This is only the first part which only uses the binds when doing a
new SNS configuration.
The outgoing add procedure will be supported in a later patch
when the SNS fsm supports outgoing procedures.

This is a behaviour change of the API and must be synchronized with
the osmo-pcu. Otherwise SNS won't work with osmo-pcu.

Related: SYS#5354
Change-Id: I9ab8092bf286e7d90e92f5702a5404425e959c84
---
M include/osmocom/gprs/gprs_ns2.h
M src/gb/gprs_ns2.c
M src/gb/gprs_ns2_sns.c
M src/gb/gprs_ns2_vty.c
M src/gb/libosmogb.map
M tests/gb/osmo-ns-dummy.cfg
6 files changed, 469 insertions(+), 37 deletions(-)

Approvals:
  daniel: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index b406bb0..56a9b4f 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -230,6 +230,8 @@
                                   const struct osmo_sockaddr *saddr);
 int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
                                   const struct osmo_sockaddr *saddr);
+int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse, struct gprs_ns2_vc_bind 
*bind);
+int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse, struct gprs_ns2_vc_bind 
*bind);
 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 9302a16..bc4db53 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -1269,6 +1269,7 @@
 void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
 {
        struct gprs_ns2_vc *nsvc, *tmp;
+       struct gprs_ns2_nse *nse;
        if (!bind)
                return;

@@ -1276,6 +1277,12 @@
                gprs_ns2_free_nsvc(nsvc);
        }

+       if (gprs_ns2_is_ip_bind(bind)) {
+               llist_for_each_entry(nse, &bind->nsi->nse, list) {
+                       gprs_ns2_sns_del_bind(nse, bind);
+               }
+       }
+
        if (bind->driver->free_bind)
                bind->driver->free_bind(bind);

diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
index ab8415d..dfa9afe 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -81,6 +81,8 @@
        GPRS_SNS_EV_CHANGE_WEIGHT,
        GPRS_SNS_EV_NO_NSVC,
        GPRS_SNS_EV_NSVC_ALIVE,         /*!< a NS-VC became alive */
+       GPRS_SNS_EV_REQ_ADD_BIND,
+       GPRS_SNS_EV_REQ_DELETE_BIND,
 };

 static const struct value_string gprs_sns_event_names[] = {
@@ -95,6 +97,8 @@
        { GPRS_SNS_EV_CHANGE_WEIGHT,    "CHANGE_WEIGHT" },
        { GPRS_SNS_EV_NO_NSVC,          "NO_NSVC" },
        { GPRS_SNS_EV_NSVC_ALIVE,       "NSVC_ALIVE"},
+       { GPRS_SNS_EV_REQ_ADD_BIND,     "ADD_BIND"},
+       { GPRS_SNS_EV_REQ_DELETE_BIND,  "DELETE_BIND"},
        { 0, NULL }
 };

@@ -103,6 +107,11 @@
        struct osmo_sockaddr saddr;
 };

+struct ns2_sns_bind {
+       struct llist_head list;
+       struct gprs_ns2_vc_bind *bind;
+};
+
 struct ns2_sns_state {
        struct gprs_ns2_nse *nse;

@@ -110,6 +119,10 @@

        /* holds the list of initial SNS endpoints */
        struct llist_head sns_endpoints;
+       /* list of used struct ns2_sns_bind  */
+       struct llist_head binds;
+       /* pointer to the bind which was used to initiate the SNS connection */
+       struct ns2_sns_bind *initial_bind;
        /* prevent recursive reselection */
        bool reselection_running;

@@ -120,8 +133,6 @@
        struct sns_endpoint *initial;
        /* all SNS PDU will be sent over this nsvc */
        struct gprs_ns2_vc *sns_nsvc;
-       /* iterate over the binds after all remote has been tested */
-       int bind_offset;
        /* timer N */
        int N;
        /* true if at least one nsvc is alive */
@@ -718,7 +729,7 @@
        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 ns2_sns_bind *sbind;
        struct osmo_sockaddr *remote;
        const struct osmo_sockaddr *sa;
        struct osmo_sockaddr local;
@@ -741,23 +752,25 @@
        remote = &gss->initial->saddr;

        /* count how many bindings are available (only UDP binds) */
-       count = ns2_ip_count_bind(nsi, remote);
+       count = llist_count(&gss->binds);
        if (count == 0) {
                /* TODO: logging */
                return;
        }

-       bind = ns2_ip_get_bind_by_index(nsi, remote, gss->bind_offset);
-       if (!bind) {
-               if (gss->bind_offset) {
-                       gss->bind_offset = 0;
-                       bind = ns2_ip_get_bind_by_index(nsi, remote, 
gss->bind_offset);
+       /* take the first bind or take the next bind */
+       if (!gss->initial_bind) {
+               gss->initial_bind = llist_first_entry(&gss->binds, struct 
ns2_sns_bind, list);
+       } else {
+               if (gss->initial_bind->list.next != &gss->binds) {
+                       gss->initial_bind = 
llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
+               } else {
+                       gss->initial_bind = llist_first_entry(&gss->binds, 
struct ns2_sns_bind, list);
                }
-
-               if (!bind)
-                       return;
        }

+       bind = gss->initial_bind->bind;
+
        /* setup the NSVC */
        if (!gss->sns_nsvc) {
                gss->sns_nsvc = ns2_ip_bind_connect(bind, gss->nse, remote);
@@ -773,11 +786,8 @@
                        return;

                gss->ip4_local = ip4_elems;
-
-               llist_for_each_entry(bind, &nsi->binding, list) {
-                       if (!gprs_ns2_is_ip_bind(bind))
-                               continue;
-
+               llist_for_each_entry(sbind, &gss->binds, list) {
+                       bind = sbind->bind;
                        sa = gprs_ns2_ip_bind_sockaddr(bind);
                        if (!sa)
                                continue;
@@ -813,10 +823,8 @@

                gss->ip6_local = ip6_elems;

-               llist_for_each_entry(bind, &nsi->binding, list) {
-                       if (!gprs_ns2_is_ip_bind(bind))
-                               continue;
-
+               llist_for_each_entry(sbind, &gss->binds, list) {
+                       bind = sbind->bind;
                        sa = gprs_ns2_ip_bind_sockaddr(bind);
                        if (!sa)
                                continue;
@@ -1409,6 +1417,8 @@
 {
        struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
        struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+       struct ns2_sns_bind *sbind;
+       struct gprs_ns2_vc *nsvc, *nsvc2;

        /* reset when receiving GPRS_SNS_EV_NO_NSVC */
        switch (event) {
@@ -1428,19 +1438,16 @@
                ns2_clear_ipv46_entries(gss);

                /* Choose the next sns endpoint. */
-               if (llist_empty(&gss->sns_endpoints)) {
+               if (llist_empty(&gss->sns_endpoints) || 
llist_empty(&gss->binds)) {
                        gss->initial = NULL;
                        ns2_prim_status_ind(gss->nse, NULL, 0, 
GPRS_NS2_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);
-                       gss->bind_offset = 0;
                } 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);
-                       gss->bind_offset++;
-                       gss->bind_offset %= ns2_ip_count_bind(nse->nsi, 
&gss->initial->saddr);
                } else {
                        /* next element is an entry */
                        gss->initial = llist_entry(gss->initial->list.next, 
struct sns_endpoint, list);
@@ -1449,6 +1456,50 @@
                gss->reselection_running = false;
                osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SIZE, 
nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
                break;
+       case GPRS_SNS_EV_REQ_ADD_BIND:
+               sbind = data;
+               switch (fi->state) {
+               case GPRS_SNS_ST_UNCONFIGURED:
+                       osmo_fsm_inst_dispatch(nse->bss_sns_fi, 
GPRS_SNS_EV_SELECT_ENDPOINT, NULL);
+                       break;
+               case GPRS_SNS_ST_SIZE:
+                       /* TODO: add the ip4 element to the list */
+                       break;
+               case GPRS_SNS_ST_CONFIG_BSS:
+               case GPRS_SNS_ST_CONFIG_SGSN:
+               case GPRS_SNS_ST_CONFIGURED:
+                       /* TODO: add to SNS-IP procedure queue & add nsvc() */
+                       break;
+               }
+               break;
+       case GPRS_SNS_EV_REQ_DELETE_BIND:
+               sbind = data;
+               switch (fi->state) {
+               case GPRS_SNS_ST_UNCONFIGURED:
+                       break;
+               case GPRS_SNS_ST_SIZE:
+                       /* TODO: remove the ip4 element from the list */
+                       llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, 
list) {
+                               if (nsvc->bind == sbind->bind) {
+                                       gprs_ns2_free_nsvc(nsvc);
+                               }
+                       }
+                       break;
+               case GPRS_SNS_ST_CONFIG_BSS:
+               case GPRS_SNS_ST_CONFIG_SGSN:
+               case GPRS_SNS_ST_CONFIGURED:
+                       /* TODO: do an delete SNS-IP procedure */
+                       /* TODO: remove the ip4 element to the list */
+                       llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, 
list) {
+                               if (nsvc->bind == sbind->bind) {
+                                       gprs_ns2_free_nsvc(nsvc);
+                               }
+                       }
+                       break;
+               }
+               /* if this is the last bind, the free_nsvc() will trigger a 
reselection */
+               talloc_free(sbind);
+               break;
        }
 }

@@ -1457,7 +1508,9 @@
        .states = ns2_sns_bss_states,
        .num_states = ARRAY_SIZE(ns2_sns_bss_states),
        .allstate_event_mask = S(GPRS_SNS_EV_NO_NSVC) |
-                              S(GPRS_SNS_EV_SELECT_ENDPOINT),
+                              S(GPRS_SNS_EV_SELECT_ENDPOINT) |
+                              S(GPRS_SNS_EV_REQ_ADD_BIND) |
+                              S(GPRS_SNS_EV_REQ_DELETE_BIND),
        .allstate_action = ns2_sns_st_all_action,
        .cleanup = NULL,
        .timer_cb = ns2_sns_fsm_bss_timer_cb,
@@ -1488,6 +1541,7 @@
        fi->priv = gss;
        gss->nse = nse;
        INIT_LLIST_HEAD(&gss->sns_endpoints);
+       INIT_LLIST_HEAD(&gss->binds);

        return fi;
 err:
@@ -1810,6 +1864,69 @@
        }
 }

+int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse,
+                         struct gprs_ns2_vc_bind *bind)
+{
+       struct ns2_sns_state *gss;
+       struct ns2_sns_bind *tmp;
+
+       OSMO_ASSERT(nse->bss_sns_fi);
+       gss = nse->bss_sns_fi->priv;
+
+       if (!gprs_ns2_is_ip_bind(bind)) {
+               return -EINVAL;
+       }
+
+       if (!llist_empty(&gss->binds)) {
+               llist_for_each_entry(tmp, &gss->binds, list) {
+                       if (tmp->bind == bind)
+                               return -EALREADY;
+               }
+       }
+
+       tmp = talloc_zero(gss, struct ns2_sns_bind);
+       if (!tmp)
+               return -ENOMEM;
+       tmp->bind = bind;
+       llist_add_tail(&tmp->list, &gss->binds);
+
+       osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_ADD_BIND, tmp);
+       return 0;
+}
+
+/* Remove a bind from the SNS. All assosiated NSVC must be removed. */
+int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse,
+                            struct gprs_ns2_vc_bind *bind)
+{
+       struct ns2_sns_state *gss;
+       struct ns2_sns_bind *tmp, *tmp2;
+       bool found = false;
+
+       if (!nse->bss_sns_fi)
+               return -EINVAL;
+
+       gss = nse->bss_sns_fi->priv;
+       if (gss->initial_bind && gss->initial_bind->bind == bind) {
+               if (gss->initial_bind->list.prev == &gss->binds)
+                       gss->initial_bind = NULL;
+               else
+                       gss->initial_bind = 
llist_entry(gss->initial_bind->list.prev, struct ns2_sns_bind, list);
+       }
+
+       llist_for_each_entry_safe(tmp, tmp2, &gss->binds, list) {
+               if (tmp->bind == bind) {
+                       llist_del(&tmp->list);
+                       found = true;
+               }
+       }
+
+       if (!found)
+               return -ENOENT;
+
+       osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_DELETE_BIND, 
tmp);
+       return 0;
+}
+
 /* Update SNS weights
  * \param[in] nsvc the NSVC which should be updated
  */
diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c
index 7a6b3ba..01409bb 100644
--- a/src/gb/gprs_ns2_vty.c
+++ b/src/gb/gprs_ns2_vty.c
@@ -58,6 +58,7 @@
 static struct gprs_ns2_inst *vty_nsi = NULL;
 static struct osmo_fr_network *vty_fr_network = NULL;
 static struct llist_head binds;
+static struct llist_head nses;

 struct vty_bind {
        struct llist_head list;
@@ -70,6 +71,21 @@
        uint8_t ip_sns_data_weight;
 };

+struct vty_nse {
+       struct llist_head list;
+       uint16_t nsei;
+       /* list of binds which are valid for this nse. Only IP-SNS uses this
+        * to allow `no listen ..` in the bind context. So "half" created binds 
are valid for
+        * IP-SNS. This allows changing the bind ip without modifying all NSEs 
afterwards */
+       struct llist_head binds;
+};
+
+/* used by IP-SNS to connect multiple vty_nse_bind to a vty_nse */
+struct vty_nse_bind {
+       struct llist_head list;
+       struct vty_bind *vbind;
+};
+
 /* TODO: this should into osmo timer */
 static const struct value_string gprs_ns_timer_strs[] = {
        { 0, "tns-block" },
@@ -135,6 +151,94 @@
        talloc_free(vbind);
 }

+static struct vty_nse *vty_nse_by_nsei(uint16_t nsei)
+{
+       struct vty_nse *vnse;
+       llist_for_each_entry(vnse, &nses, list) {
+               if (vnse->nsei == nsei)
+                       return vnse;
+       }
+       return NULL;
+}
+
+static struct vty_nse *vty_nse_alloc(uint16_t nsei)
+{
+       struct vty_nse *vnse = talloc_zero(vty_nsi, struct vty_nse);
+       if (!vnse)
+               return NULL;
+
+       vnse->nsei = nsei;
+       INIT_LLIST_HEAD(&vnse->binds);
+       llist_add(&vnse->list, &nses);
+       return vnse;
+}
+
+static void vty_nse_free(struct vty_nse *vnse)
+{
+       if (!vnse)
+               return;
+
+       llist_del(&vnse->list);
+       /* all vbind of the nse will be freed by talloc */
+       talloc_free(vnse);
+}
+
+static int vty_nse_add_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
+{
+       struct vty_nse_bind *vnse_bind;
+
+       if (vbind->ll != GPRS_NS2_LL_UDP)
+               return -EINVAL;
+
+       llist_for_each_entry(vnse_bind, &vnse->binds, list) {
+               if (vnse_bind->vbind == vbind)
+                       return -EALREADY;
+       }
+
+       vnse_bind = talloc(vnse, struct vty_nse_bind);
+       if (!vnse_bind)
+               return -ENOMEM;
+       vnse_bind->vbind = vbind;
+
+       llist_add_tail(&vnse_bind->list, &vnse->binds);
+       return 0;
+}
+
+static int vty_nse_remove_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
+{
+       struct vty_nse_bind *vnse_bind, *tmp;
+       if (vbind->ll != GPRS_NS2_LL_UDP)
+               return -EINVAL;
+
+       llist_for_each_entry_safe(vnse_bind, tmp, &vnse->binds, list) {
+               if (vnse_bind->vbind == vbind) {
+                       llist_del(&vnse_bind->list);
+                       talloc_free(vnse_bind);
+               }
+       }
+
+       return -ENOENT;
+}
+
+/* check if the NSE still has SNS configuration */
+static bool vty_nse_check_sns(struct gprs_ns2_nse *nse) {
+       struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
+
+       int count = gprs_ns2_sns_count(nse);
+       if (count > 0) {
+                /* there are other sns endpoints */
+               return true;
+       }
+
+       if (!vnse)
+               return false;
+
+       if (llist_empty(&vnse->binds))
+               return false;
+
+       return true;
+}
+
 static struct cmd_node ns_node = {
        L_NS_NODE,
        "%s(config-ns)# ",
@@ -172,14 +276,26 @@
       )
 {
        struct gprs_ns2_nse *nse;
+       struct vty_nse *vnse;
        uint16_t nsei = atoi(argv[0]);
+       bool free_vnse = false;
+
+       vnse = vty_nse_by_nsei(nsei);
+       if (!vnse) {
+               vnse = vty_nse_alloc(nsei);
+               if (!vnse) {
+                       vty_out(vty, "Failed to create vty NSE!%s", 
VTY_NEWLINE);
+                       return CMD_ERR_INCOMPLETE;
+               }
+               free_vnse = true;
+       }

        nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
        if (!nse) {
                nse = gprs_ns2_create_nse(vty_nsi, nsei, GPRS_NS2_LL_UNDEF, 
GPRS_NS2_DIALECT_UNDEF);
                if (!nse) {
                        vty_out(vty, "Failed to create NSE!%s", VTY_NEWLINE);
-                       return CMD_ERR_INCOMPLETE;
+                       goto err;
                }
                nse->persistent = true;
        }
@@ -187,13 +303,19 @@
        if (!nse->persistent) {
                /* TODO: should the dynamic NSE removed? */
                vty_out(vty, "A dynamic NSE with the specified NSEI already 
exists%s", VTY_NEWLINE);
-               return CMD_ERR_INCOMPLETE;
+               goto err;
        }

        vty->node = L_NS_NSE_NODE;
        vty->index = nse;

        return CMD_SUCCESS;
+
+err:
+       if (free_vnse)
+               talloc_free(vnse);
+
+       return CMD_ERR_INCOMPLETE;
 }

 DEFUN(cfg_no_ns_nsei, cfg_no_ns_nsei_cmd,
@@ -204,6 +326,7 @@
       )
 {
        struct gprs_ns2_nse *nse;
+       struct vty_nse *vnse;
        uint16_t nsei = atoi(argv[0]);

        nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
@@ -219,6 +342,10 @@

        vty_out(vty, "Deleting NS Entity %u%s", nse->nsei, VTY_NEWLINE);
        gprs_ns2_free_nse(nse);
+
+       vnse = vty_nse_by_nsei(nsei);
+       vty_nse_free(vnse);
+
        return CMD_SUCCESS;
 }

@@ -394,11 +521,18 @@
 static void _config_write_ns_nse(struct vty *vty, struct gprs_ns2_nse *nse)
 {
        struct gprs_ns2_vc *nsvc;
+       struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
+       struct vty_nse_bind *vbind;
+
+       OSMO_ASSERT(vnse);

        vty_out(vty, " nse %u%s", nse->nsei, VTY_NEWLINE);
        switch (nse->dialect) {
        case GPRS_NS2_DIALECT_SNS:
                ns2_sns_write_vty(vty, nse);
+               llist_for_each_entry(vbind, &vnse->binds, list) {
+                       vty_out(vty, "  ip-sns-bind %s%s", vbind->vbind->name, 
VTY_NEWLINE);
+               }
                break;
        default:
                llist_for_each_entry(nsvc, &nse->nsvc, list) {
@@ -472,7 +606,7 @@
 {
        struct vty_bind *vbind = vty->index;
        struct gprs_ns2_vc_bind *bind;
-
+       int rc;
        const char *addr_str = argv[0];
        unsigned int port = atoi(argv[1]);
        struct osmo_sockaddr_str sockaddr_str;
@@ -494,8 +628,9 @@
                return CMD_WARNING;
        }

-       if (gprs_ns2_ip_bind(vty_nsi, vbind->name, &sockaddr, vbind->dscp, 
&bind) != 0) {
-               vty_out(vty, "Failed to create the bind!%s", VTY_NEWLINE);
+       rc = gprs_ns2_ip_bind(vty_nsi, vbind->name, &sockaddr, vbind->dscp, 
&bind);
+       if (rc != 0) {
+               vty_out(vty, "Failed to create the bind (rc %d)!%s", rc, 
VTY_NEWLINE);
                return CMD_WARNING;
        }

@@ -1320,7 +1455,6 @@
        struct osmo_sockaddr_str remote_str; /* argv[0] */
        struct osmo_sockaddr remote;
        uint16_t port = atoi(argv[1]);
-       int count;

        if (nse->ll != GPRS_NS2_LL_UDP) {
                vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
@@ -1347,12 +1481,9 @@
                return CMD_WARNING;
        }

-       count = gprs_ns2_sns_count(nse);
-       if (count > 0) {
-                /* there are other sns endpoints */
+       if (vty_nse_check_sns(nse)) {
+                /* there is still sns configuration valid */
                return CMD_SUCCESS;
-       } else if (count < 0) {
-               OSMO_ASSERT(0);
        } else {
                /* clean up nse to allow other nsvc commands */
                osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, 
NULL);
@@ -1364,6 +1495,175 @@
        return CMD_SUCCESS;
 }

+DEFUN(cfg_ns_nse_ip_sns_bind, cfg_ns_nse_ip_sns_bind_cmd,
+      "ip-sns-bind BINDID",
+      "IP SNS binds\n"
+      "A udp bind which this SNS will be used. The bind must be already 
exists. Can be given multiple times.\n")
+{
+       struct gprs_ns2_nse *nse = vty->index;
+       struct gprs_ns2_vc_bind *bind;
+       struct vty_bind *vbind;
+       struct vty_nse *vnse;
+       const char *name = argv[0];
+       bool ll_modified = false;
+       bool dialect_modified = false;
+       int rc;
+
+       if (nse->ll == GPRS_NS2_LL_UNDEF) {
+               nse->ll = GPRS_NS2_LL_UDP;
+               ll_modified = true;
+       }
+
+       if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+               char sns[16];
+               snprintf(sns, sizeof(sns), "NSE%05u-SNS", nse->nsei);
+               nse->bss_sns_fi = ns2_sns_bss_fsm_alloc(nse, sns);
+               if (!nse->bss_sns_fi)
+                       goto err;
+               nse->dialect = GPRS_NS2_DIALECT_SNS;
+               dialect_modified = true;
+       }
+
+       if (nse->ll != GPRS_NS2_LL_UDP) {
+               vty_out(vty, "Can not mix NS-VC with different link layer%s", 
VTY_NEWLINE);
+               goto err;
+       }
+
+       if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+               vty_out(vty, "Can not mix NS-VC with different dialects%s", 
VTY_NEWLINE);
+               goto err;
+       }
+
+       vbind = vty_bind_by_name(name);
+       if (!vbind) {
+               vty_out(vty, "Can not find the given bind '%s'%s", name, 
VTY_NEWLINE);
+               goto err;
+       }
+
+       if (vbind->ll != GPRS_NS2_LL_UDP) {
+               vty_out(vty, "ip-sns-bind can only be used with UDP bind%s",
+                       VTY_NEWLINE);
+               goto err;
+       }
+
+       /* the vnse has been created together when creating the nse node. The 
parent node should check this already! */
+       vnse = vty_nse_by_nsei(nse->nsei);
+       OSMO_ASSERT(vnse);
+
+       rc = vty_nse_add_vbind(vnse, vbind);
+       switch (rc) {
+       case 0:
+               break;
+       case -EALREADY:
+               vty_out(vty, "Failed to add ip-sns-bind %s already present%s", 
name, VTY_NEWLINE);
+               goto err;
+       case -ENOMEM:
+               vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", 
name, VTY_NEWLINE);
+               goto err;
+       default:
+               vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, 
VTY_NEWLINE);
+               goto err;
+       }
+
+       /* the bind might not yet created because "listen" is missing. */
+       bind = gprs_ns2_bind_by_name(vty_nsi, name);
+       if (!bind)
+               return CMD_SUCCESS;
+
+       rc = gprs_ns2_sns_add_bind(nse, bind);
+       switch (rc) {
+       case 0:
+               break;
+       case -EALREADY:
+               vty_out(vty, "Failed to add ip-sns-bind %s already present%s", 
name, VTY_NEWLINE);
+               goto err;
+       case -ENOMEM:
+               vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", 
name, VTY_NEWLINE);
+               goto err;
+       default:
+               vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, 
VTY_NEWLINE);
+               goto err;
+       }
+
+       return CMD_SUCCESS;
+err:
+       if (ll_modified)
+               nse->ll = GPRS_NS2_LL_UNDEF;
+       if (dialect_modified)
+               nse->dialect = GPRS_NS2_DIALECT_UNDEF;
+
+       return CMD_WARNING;
+}
+
+DEFUN(cfg_no_ns_nse_ip_sns_bind, cfg_no_ns_nse_ip_sns_bind_cmd,
+      "no ip-sns-bind BINDID",
+      NO_STR
+      "IP SNS binds\n"
+      "A udp bind which this SNS will be used.\n")
+{
+       struct gprs_ns2_nse *nse = vty->index;
+       struct gprs_ns2_vc_bind *bind;
+       struct vty_bind *vbind;
+       struct vty_nse *vnse;
+       const char *name = argv[0];
+       int rc;
+
+       if (nse->ll != GPRS_NS2_LL_UDP) {
+               vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+               vty_out(vty, "This NSE doesn't support UDP with dialect 
ip-sns.%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       vbind = vty_bind_by_name(name);
+       if (!vbind) {
+               vty_out(vty, "Can not find the given bind '%s'%s", name, 
VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       if (vbind->ll != GPRS_NS2_LL_UDP) {
+               vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       /* the vnse has been created together when creating the nse node. The 
parent node should check this already! */
+       vnse = vty_nse_by_nsei(nse->nsei);
+       OSMO_ASSERT(vnse);
+
+       rc = vty_nse_remove_vbind(vnse, vbind);
+       switch(rc) {
+       case 0:
+               break;
+       case -ENOENT:
+               vty_out(vty, "Bind %s is not part of this NSE%s", name, 
VTY_NEWLINE);
+               return CMD_WARNING;
+       case -EINVAL:
+               vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       default:
+               return CMD_WARNING;
+       }
+
+       /* the bind might not exists yet */
+       bind = gprs_ns2_bind_by_name(vty_nsi, name);
+       if (bind)
+               gprs_ns2_sns_del_bind(nse, bind);
+
+       if (!vty_nse_check_sns(nse)) {
+               /* clean up nse to allow other nsvc commands */
+               osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, 
NULL);
+               nse->bss_sns_fi = NULL;
+               nse->ll = GPRS_NS2_LL_UNDEF;
+               nse->dialect = GPRS_NS2_DIALECT_UNDEF;
+       }
+
+       return CMD_SUCCESS;
+}

 /* non-config commands */
 static void dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
@@ -1676,6 +1976,7 @@
 {
        vty_nsi = nsi;
        INIT_LLIST_HEAD(&binds);
+       INIT_LLIST_HEAD(&nses);

        vty_fr_network = osmo_fr_network_alloc(nsi);
        if (!vty_fr_network)
@@ -1737,6 +2038,8 @@
        install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_ipa_cmd);
        install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_remote_cmd);
        install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_remote_cmd);
+       install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_bind_cmd);
+       install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_bind_cmd);

        return 0;
 }
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index 699ed1b..588fabf 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -185,7 +185,9 @@
 gprs_ns2_recv_prim;
 gprs_ns2_reset_persistent_nsvcs;
 gprs_ns2_start_alive_all_nsvcs;
+gprs_ns2_sns_add_bind;
 gprs_ns2_sns_add_endpoint;
+gprs_ns2_sns_del_bind;
 gprs_ns2_sns_del_endpoint;
 gprs_ns2_vty_init;
 gprs_ns2_vty_init_reduced;
diff --git a/tests/gb/osmo-ns-dummy.cfg b/tests/gb/osmo-ns-dummy.cfg
index 1e6dc76..7269618 100644
--- a/tests/gb/osmo-ns-dummy.cfg
+++ b/tests/gb/osmo-ns-dummy.cfg
@@ -21,4 +21,5 @@
  nse 1235
   nsvc udp local 127.0.0.3 23000
  nse 1234
+  ip-sns-bind local
   ip-sns-remote 127.0.0.2 2158

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

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I9ab8092bf286e7d90e92f5702a5404425e959c84
Gerrit-Change-Number: 22872
Gerrit-PatchSet: 13
Gerrit-Owner: lynxis lazus <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: lynxis lazus <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-MessageType: merged

Reply via email to