aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/gprs/gprs_ns2.h2
-rw-r--r--src/gb/gprs_ns2.c7
-rw-r--r--src/gb/gprs_ns2_sns.c169
-rw-r--r--src/gb/gprs_ns2_vty.c325
-rw-r--r--src/gb/libosmogb.map2
-rw-r--r--tests/gb/osmo-ns-dummy.cfg1
6 files changed, 469 insertions, 37 deletions
diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
index b406bb0d..56a9b4f4 100644
--- a/include/osmocom/gprs/gprs_ns2.h
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -230,6 +230,8 @@ 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);
+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 9302a16f..bc4db53e 100644
--- a/src/gb/gprs_ns2.c
+++ b/src/gb/gprs_ns2.c
@@ -1269,6 +1269,7 @@ void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse)
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 @@ void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
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 ab8415df..dfa9afe2 100644
--- a/src/gb/gprs_ns2_sns.c
+++ b/src/gb/gprs_ns2_sns.c
@@ -81,6 +81,8 @@ enum gprs_sns_event {
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 @@ static const struct value_string gprs_sns_event_names[] = {
{ 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 sns_endpoint {
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 @@ struct ns2_sns_state {
/* 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 ns2_sns_state {
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 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
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 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
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 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
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 @@ static void ns2_sns_st_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state
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 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
{
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 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
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 @@ static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void
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 @@ static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
.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 @@ struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
fi->priv = gss;
gss->nse = nse;
INIT_LLIST_HEAD(&gss->sns_endpoints);
+ INIT_LLIST_HEAD(&gss->binds);
return fi;
err:
@@ -1810,6 +1864,69 @@ void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bo
}
}
+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 7a6b3bae..01409bbf 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 @@ struct vty_bind {
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 @@ static void vty_bind_free(struct vty_bind *vbind)
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 @@ DEFUN(cfg_ns_nsei, cfg_ns_nsei_cmd,
)
{
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 @@ DEFUN(cfg_ns_nsei, cfg_ns_nsei_cmd,
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 @@ DEFUN(cfg_no_ns_nsei, cfg_no_ns_nsei_cmd,
)
{
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 @@ DEFUN(cfg_no_ns_nsei, cfg_no_ns_nsei_cmd,
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_nsvc(struct vty *vty, const struct gprs_ns2_vc *nsvc)
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 @@ DEFUN(cfg_ns_bind_listen, cfg_ns_bind_listen_cmd,
{
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 @@ DEFUN(cfg_ns_bind_listen, cfg_ns_bind_listen_cmd,
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 @@ DEFUN(cfg_no_ns_nse_ip_sns_remote, cfg_no_ns_nse_ip_sns_remote_cmd,
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 @@ DEFUN(cfg_no_ns_nse_ip_sns_remote, cfg_no_ns_nse_ip_sns_remote_cmd,
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 @@ DEFUN(cfg_no_ns_nse_ip_sns_remote, cfg_no_ns_nse_ip_sns_remote_cmd,
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 @@ int gprs_ns2_vty_init_reduced(struct gprs_ns2_inst *nsi)
{
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 @@ int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi)
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 699ed1be..588fabfb 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -185,7 +185,9 @@ gprs_ns2_prim_strs;
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 1e6dc76e..72696189 100644
--- a/tests/gb/osmo-ns-dummy.cfg
+++ b/tests/gb/osmo-ns-dummy.cfg
@@ -21,4 +21,5 @@ ns
nse 1235
nsvc udp local 127.0.0.3 23000
nse 1234
+ ip-sns-bind local
ip-sns-remote 127.0.0.2 2158