From e769f5226be6b0f8d59366c7b9a156e9cf8fc22d Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 7 Dec 2020 07:37:07 +0100 Subject: [PATCH] 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 --- include/osmocom/gprs/gprs_ns2.h | 9 +- src/gb/gprs_ns2.c | 36 --- src/gb/gprs_ns2_internal.h | 6 +- src/gb/gprs_ns2_sns.c | 451 +++++++++++++++++++++----------- src/gb/gprs_ns2_udp.c | 49 ++++ src/gb/libosmogb.map | 3 +- 6 files changed, 364 insertions(+), 190 deletions(-) diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h index 00879d7fc..8dd56998f 100644 --- a/include/osmocom/gprs/gprs_ns2.h +++ b/include/osmocom/gprs/gprs_ns2.h @@ -82,6 +82,7 @@ enum gprs_ns2_affecting_cause { /* 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_bind(struct gprs_ns2_vc_bind *bind); 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 21c69cbd9..5e5dd83bb 100644 --- a/src/gb/gprs_ns2.c +++ b/src/gb/gprs_ns2.c @@ -953,42 +953,6 @@ struct gprs_ns2_vc *gprs_ns2_ip_connect2(struct gprs_ns2_vc_bind *bind, 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 9bfe0b055..d12c66369 100644 --- a/src/gb/gprs_ns2_internal.h +++ b/src/gb/gprs_ns2_internal.h @@ -286,13 +286,15 @@ int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause, 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 5d18d04f5..b7fbbf807 100644 --- a/src/gb/gprs_ns2_sns.c +++ b/src/gb/gprs_ns2_sns.c @@ -69,7 +69,7 @@ enum gprs_sns_bss_state { }; 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 @@ enum gprs_sns_event { }; 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 @@ static const struct value_string gprs_sns_event_names[] = { { 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 @@ const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse) 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 int do_sns_add(struct osmo_fsm_inst *fi, 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 @@ static void ns2_sns_st_size(struct osmo_fsm_inst *fi, uint32_t event, void *data } } +/* 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 void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old 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 int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi) 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 @@ 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); return fi; err: @@ -1241,134 +1392,6 @@ err: 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 @@ void gprs_ns2_sns_dump_vty(struct vty *vty, const struct gprs_ns2_nse *nse, bool } } +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 3eb8116bb..905827945 100644 --- a/src/gb/gprs_ns2_udp.c +++ b/src/gb/gprs_ns2_udp.c @@ -514,3 +514,52 @@ int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp) 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 d4114dfa9..59405b0a9 100644 --- a/src/gb/libosmogb.map +++ b/src/gb/libosmogb.map @@ -138,7 +138,6 @@ gprs_ns2_ip_bind_sockaddr; 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_prim_strs; 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;