diff options
author | Alexander Couzens <lynxis@fe80.eu> | 2021-02-12 03:17:59 +0100 |
---|---|---|
committer | lynxis lazus <lynxis@fe80.eu> | 2021-02-19 10:41:50 +0000 |
commit | 6b9d2324219d50f38f27cfaa5d546379f0aec74e (patch) | |
tree | be6b86d6383e0f879f4819026057a6bcddc0f0b2 /src | |
parent | d7a209bce9b4d3707ef86b1b65aeaea76c729c80 (diff) |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/gb/gprs_ns2.c | 7 | ||||
-rw-r--r-- | src/gb/gprs_ns2_sns.c | 169 | ||||
-rw-r--r-- | src/gb/gprs_ns2_vty.c | 325 | ||||
-rw-r--r-- | src/gb/libosmogb.map | 2 |
4 files changed, 466 insertions, 37 deletions
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; |