aboutsummaryrefslogtreecommitdiffstats
path: root/src/gb/gprs_ns.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2018-07-01 21:04:45 +0200
committerHarald Welte <laforge@gnumonks.org>2019-02-26 12:18:30 +0100
commit047f3872f511353e894659719a6c5346249bca40 (patch)
treee719164186b93e96cfca6daa86b95f7679bbe669 /src/gb/gprs_ns.c
parent35042a29197bd086a545976e5fa38d01c434f8ac (diff)
NS: Add support for GPRS NS IP Sub-Network-Service (SNS)
The NS implementation part of the Gb implementation libosmogb so far implemented a rather classic dialect of Gb, with lots of heritage to FR (Frame Relay) transports. At least since Release 6 of the NS specification, there's an IP Sub-Network Service (SNS), which * permits for dynamic configuration of IP endpoints and their NS-VCs * abandons the concept of a NSVCI on IP transport * forbids the use of RESET/BLOCK/UNBLOCK procedures on IP transport This commit introduces BSS-side IP-SNS support to libosmogb in a minimally invasive way. It adds a corresponding SNS FSM to each NS instance, and implements the new SIZE/CONFIG/ADD/DELETE/CHANGE_WEIGHT procedures very closely aligned with the spec. In order to use the SNS flavor (rather than the classic one), a BSS implementation should use gprs_ns_nsip_connect_sns() instead of the existing gprs_ns_nsip_connect(). This implementation comes with a set of TTCN-3 tests in PCU_Tests_RAW_SNS.ttcn, see Change-ID I0fe3d4579960bab0494c294ec7ab8032feed4fb2 of osmo-ttcn3-hacks.git Closes: OS#3372 Closes: OS#3617 Change-Id: I84786c3b43a8ae34ef3b3ba84b33c90042d234ea
Diffstat (limited to 'src/gb/gprs_ns.c')
-rw-r--r--src/gb/gprs_ns.c253
1 files changed, 237 insertions, 16 deletions
diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c
index ad68bc99..33de2c1a 100644
--- a/src/gb/gprs_ns.c
+++ b/src/gb/gprs_ns.c
@@ -72,6 +72,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/gsm/tlv.h>
@@ -93,6 +94,18 @@
#define ns_set_remote_state(ns_, st_) ns_set_state_with_log(ns_, st_, true, __FILE__, __LINE__)
#define ns_mark_blocked(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_BLOCKED)
#define ns_mark_unblocked(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_BLOCKED));
+#define ns_mark_alive(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_ALIVE)
+#define ns_mark_dead(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_ALIVE));
+
+#define ERR_IF_NSVC_USES_SNS(nsvc, reason) \
+ do { \
+ if ((nsvc)->nsi->bss_sns_fi) { \
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Asked to %s. Rejected on IP-SNS\n", \
+ nsvc->nsei, reason); \
+ osmo_log_backtrace(DNS, LOGL_ERROR); \
+ return -EIO; \
+ } \
+ } while (0)
static const struct tlv_definition ns_att_tlvdef = {
.def = {
@@ -107,6 +120,9 @@ static const struct tlv_definition ns_att_tlvdef = {
[NS_IE_IPv4_EP_NR] = { TLV_TYPE_FIXED, 2 },
[NS_IE_IPv6_EP_NR] = { TLV_TYPE_FIXED, 2 },
[NS_IE_RESET_FLAG] = { TLV_TYPE_TV, 0 },
+ /* TODO: IP_ADDR can be 5 or 17 bytes long, depending on first byte. This
+ * cannot be expressed in our TLV parser. However, we don't do IPv6 anyway */
+ [NS_IE_IP_ADDR] = { TLV_TYPE_FIXED, 5 },
},
};
@@ -171,6 +187,7 @@ const struct value_string gprs_ns_signal_ns_names[] = {
{ S_NS_ALIVE_EXP, "NS-ALIVE expired" },
{ S_NS_REPLACED, "NSVC replaced" },
{ S_NS_MISMATCH, "Unexpected IE" },
+ { S_SNS_CONFIGURED, "SNS Configured" },
{ 0, NULL }
};
@@ -237,11 +254,20 @@ struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei)
return NULL;
}
+/*! Determine active NS-VC for given NSEI + BVCI.
+ * Use this function to determine which of the NS-VCs inside the NS Instance
+ * shall be used to transmit data for given NSEI + BVCI */
static struct gprs_nsvc *gprs_active_nsvc_by_nsei(struct gprs_ns_inst *nsi,
- uint16_t nsei)
+ uint16_t nsei, uint16_t bvci)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ /* if signalling BVCI, skip any NSVC with signalling weight == 0 */
+ if (bvci == 0 && nsvc->sig_weight == 0)
+ continue;
+ /* if point-to-point BVCI, skip any NSVC with data weight == 0 */
+ if (bvci != 0 && nsvc->data_weight == 0)
+ continue;
if (nsvc->nsei == nsei) {
if (!(nsvc->state & NSE_S_BLOCKED) &&
nsvc->state & NSE_S_ALIVE)
@@ -251,9 +277,11 @@ static struct gprs_nsvc *gprs_active_nsvc_by_nsei(struct gprs_ns_inst *nsi,
return NULL;
}
-/* Lookup struct gprs_nsvc based on remote peer socket addr */
-static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
- struct sockaddr_in *sin)
+/*! Lookup NS-VC based on specified remote peer socket addr.
+ * \param[in] nsi NS Instance within which we shall look up the NS-VC
+ * \param[in] sin Remote peer Socket Address (IP + UDP Port)
+ * \returns NS-VC matching the given peer; NULL in case of none */
+struct gprs_nsvc *gprs_nsvc_by_rem_addr(struct gprs_ns_inst *nsi, const struct sockaddr_in *sin)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
@@ -267,27 +295,52 @@ static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
static void gprs_ns_timer_cb(void *data);
-struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
+/*! Create a new NS-VC (Virtual Circuit) within given instance
+ * \param[in] nsi NS Instance in which to create the NSVC
+ * \param[in] nsvci] NS Virtual Connection Identifier for this NSVC
+ * \param[in] sig_weight Signalling Weight of this NS-VC. Use "0" for no signalling
+ * \param[in] data_weight Data WEight of this NS-VC. Use "0" for no data
+ * \returns newly-created gprs_nsvc within nsi. NULL on error. */
+struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
+ uint8_t sig_weight, uint8_t data_weight)
{
struct gprs_nsvc *nsvc;
+ if (gprs_nsvc_by_nsvci(nsi, nsvci)) {
+ LOGP(DNS, LOGL_ERROR, "Cannot create NS-VC for already-existing NSVCI=%u\n", nsvci);
+ return NULL;
+ }
+
LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci);
nsvc = talloc_zero(nsi, struct gprs_nsvc);
+ if (!nsvc)
+ return NULL;
nsvc->nsvci = nsvci;
nsvc->nsvci_is_valid = 1;
/* before RESET procedure: BLOCKED and DEAD */
- ns_set_state(nsvc, NSE_S_BLOCKED);
+ if (nsi->bss_sns_fi)
+ ns_set_state(nsvc, 0);
+ else
+ ns_set_state(nsvc, NSE_S_BLOCKED);
nsvc->nsi = nsi;
osmo_timer_setup(&nsvc->timer, gprs_ns_timer_cb, nsvc);
nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci);
nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, nsvci);
+ nsvc->sig_weight = sig_weight;
+ nsvc->data_weight = data_weight;
llist_add(&nsvc->list, &nsi->gprs_nsvcs);
return nsvc;
}
+/*! Old API for creating a NS-VC. Uses gprs_nsvc_create2 with fixed weights. */
+struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
+{
+ return gprs_nsvc_create2(nsi, nsvci, 1, 1);
+}
+
/*! Delete given NS-VC
* \param[in] nsvc gprs_nsvc to be deleted
*/
@@ -450,13 +503,16 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type)
*/
int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause)
{
- struct msgb *msg = gprs_ns_msgb_alloc();
+ struct msgb *msg;
struct gprs_ns_hdr *nsh;
uint16_t nsvci = osmo_htons(nsvc->nsvci);
uint16_t nsei = osmo_htons(nsvc->nsei);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
+
+ msg = gprs_ns_msgb_alloc();
if (!msg)
return -ENOMEM;
@@ -536,12 +592,15 @@ int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause,
*/
int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause)
{
- struct msgb *msg = gprs_ns_msgb_alloc();
+ struct msgb *msg;
struct gprs_ns_hdr *nsh;
uint16_t nsvci = osmo_htons(nsvc->nsvci);
log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
+
+ msg = gprs_ns_msgb_alloc();
if (!msg)
return -ENOMEM;
@@ -574,6 +633,8 @@ static int gprs_ns_tx_block_ack(struct gprs_nsvc *nsvc)
log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
+
msg = gprs_ns_msgb_alloc();
if (!msg)
return -ENOMEM;
@@ -597,6 +658,9 @@ static int gprs_ns_tx_block_ack(struct gprs_nsvc *nsvc)
int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc)
{
log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
+
LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n",
nsvc->nsei, nsvc->nsvci);
@@ -687,16 +751,21 @@ static void gprs_ns_timer_cb(void *data)
nsvc->alive_retries++;
if (nsvc->alive_retries >
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
- /* mark as dead and blocked */
- ns_set_state(nsvc, NSE_S_BLOCKED);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ /* mark as dead (and blocked unless IP-SNS) */
rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
+ if (!nsvc->nsi->bss_sns_fi) {
+ ns_set_state(nsvc, NSE_S_BLOCKED);
+ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ } else
+ ns_set_state(nsvc, 0);
LOGP(DNS, LOGL_NOTICE,
"NSEI=%u Tns-alive expired more then "
"%u times, blocking NS-VC\n", nsvc->nsei,
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]);
ns_osmo_signal_dispatch(nsvc, S_NS_ALIVE_EXP, 0);
- ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED);
+ /* FIXME: should we send this signal in case of SNS? */
+ if (!nsvc->nsi->bss_sns_fi)
+ ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED);
return;
}
/* Tns-test case: send NS-ALIVE PDU */
@@ -733,11 +802,15 @@ static void gprs_ns_timer_cb(void *data)
/* Section 9.2.6 */
static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc)
{
- struct msgb *msg = gprs_ns_msgb_alloc();
+ struct msgb *msg;
struct gprs_ns_hdr *nsh;
uint16_t nsvci, nsei;
log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
+
+ msg = gprs_ns_msgb_alloc();
if (!msg)
return -ENOMEM;
@@ -777,6 +850,13 @@ int gprs_ns_tx_sns_ack(struct gprs_nsvc *nsvc, uint8_t trans_id, uint8_t *cause,
if (!msg)
return -ENOMEM;
+ if (!nsvc->nsi->bss_sns_fi) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
+ nsvc->nsei);
+ msgb_free(msg);
+ return -EIO;
+ }
+
nsei = osmo_htons(nsvc->nsei);
msg->l2h = msgb_put(msg, sizeof(*nsh));
@@ -815,6 +895,13 @@ int gprs_ns_tx_sns_config(struct gprs_nsvc *nsvc, bool end_flag,
if (!msg)
return -ENOMEM;
+ if (!nsvc->nsi->bss_sns_fi) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
+ nsvc->nsei);
+ msgb_free(msg);
+ return -EIO;
+ }
+
nsei = osmo_htons(nsvc->nsei);
msg->l2h = msgb_put(msg, sizeof(*nsh));
@@ -847,6 +934,13 @@ int gprs_ns_tx_sns_config_ack(struct gprs_nsvc *nsvc, uint8_t *cause)
if (!msg)
return -ENOMEM;
+ if (!nsvc->nsi->bss_sns_fi) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
+ nsvc->nsei);
+ msgb_free(msg);
+ return -EIO;
+ }
+
nsei = osmo_htons(nsvc->nsei);
msg->l2h = msgb_put(msg, sizeof(*nsh));
@@ -880,6 +974,13 @@ int gprs_ns_tx_sns_size(struct gprs_nsvc *nsvc, bool reset_flag, uint16_t max_nr
if (!msg)
return -ENOMEM;
+ if (!nsvc->nsi->bss_sns_fi) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
+ nsvc->nsei);
+ msgb_free(msg);
+ return -EIO;
+ }
+
nsei = osmo_htons(nsvc->nsei);
msg->l2h = msgb_put(msg, sizeof(*nsh));
@@ -912,6 +1013,13 @@ int gprs_ns_tx_sns_size_ack(struct gprs_nsvc *nsvc, uint8_t *cause)
if (!msg)
return -ENOMEM;
+ if (!nsvc->nsi->bss_sns_fi) {
+ LOGP(DNS, LOGL_ERROR, "NSEI=%u Cannot transmit SNS on NSVC without SNS active\n",
+ nsvc->nsei);
+ msgb_free(msg);
+ return -EIO;
+ }
+
nsei = osmo_htons(nsvc->nsei);
msg->l2h = msgb_put(msg, sizeof(*nsh));
@@ -942,7 +1050,7 @@ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
struct gprs_ns_hdr *nsh;
uint16_t bvci = msgb_bvci(msg);
- nsvc = gprs_active_nsvc_by_nsei(nsi, msgb_nsei(msg));
+ nsvc = gprs_active_nsvc_by_nsei(nsi, msgb_nsei(msg), msgb_bvci(msg));
if (!nsvc) {
int rc;
if (gprs_nsvc_by_nsei(nsi, msgb_nsei(msg))) {
@@ -1358,7 +1466,7 @@ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
int rc = 0;
/* look up the NSVC based on source address */
- nsvc = nsvc_by_rem_addr(nsi, saddr);
+ nsvc = gprs_nsvc_by_rem_addr(nsi, saddr);
if (!nsvc) {
struct gprs_nsvc *fallback_nsvc;
@@ -1572,6 +1680,7 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
struct gprs_nsvc **nsvc)
{
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp;
int rc = 0;
msgb_nsei(msg) = (*nsvc)->nsei;
@@ -1594,6 +1703,7 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
rc = gprs_ns_tx_alive_ack(*nsvc);
break;
case NS_PDUT_ALIVE_ACK:
+ ns_mark_alive(*nsvc);
if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY],
nsvc_timer_elapsed_ms(*nsvc));
@@ -1648,15 +1758,68 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
/* mark remote NS-VC as blocked + active */
ns_set_remote_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
break;
+ case SNS_PDUT_CONFIG:
+ if (!nsi->bss_sns_fi)
+ goto unexpected_sns;
+ /* one additional byte ('end flag') before the TLV part starts */
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data+1,
+ msgb_l2len(msg) - sizeof(*nsh)-1, 0, 0);
+ if (rc < 0) {
+ LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ return rc;
+ }
+ /* All sub-network service related message types */
+ rc = gprs_ns_rx_sns(nsi, msg, &tp);
+ break;
+ case SNS_PDUT_ACK:
+ case SNS_PDUT_ADD:
+ case SNS_PDUT_CHANGE_WEIGHT:
+ case SNS_PDUT_DELETE:
+ if (!nsi->bss_sns_fi)
+ goto unexpected_sns;
+ /* weird layout: NSEI TLV, then value-only transaction IE, then TLV again */
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data+5,
+ msgb_l2len(msg) - sizeof(*nsh)-5, 0, 0);
+ if (rc < 0) {
+ LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ return rc;
+ }
+ tp.lv[NS_IE_NSEI].val = nsh->data+2;
+ tp.lv[NS_IE_NSEI].len = 2;
+ tp.lv[NS_IE_TRANS_ID].val = nsh->data+4;
+ tp.lv[NS_IE_TRANS_ID].len = 1;
+ rc = gprs_ns_rx_sns(nsi, msg, &tp);
+ break;
+ case SNS_PDUT_CONFIG_ACK:
+ case SNS_PDUT_SIZE:
+ case SNS_PDUT_SIZE_ACK:
+ if (!nsi->bss_sns_fi)
+ goto unexpected_sns;
+ rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ if (rc < 0) {
+ LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ return rc;
+ }
+ /* All sub-network service related message types */
+ rc = gprs_ns_rx_sns(nsi, msg, &tp);
+ break;
default:
LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n",
(*nsvc)->nsei, nsh->pdu_type);
rc = -EINVAL;
break;
+unexpected_sns:
+ LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx %s for NS Instance that has no SNS!\n",
+ (*nsvc)->nsei, get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+ rc = -EINVAL;
+ break;
}
return rc;
}
+static bool gprs_sns_fsm_registered = false;
+
/*! Create a new GPRS NS instance
* \param[in] cb Call-back function for incoming BSSGP data
* \returns dynamically allocated gprs_ns_inst
@@ -1665,6 +1828,11 @@ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx)
{
struct gprs_ns_inst *nsi = talloc_zero(ctx, struct gprs_ns_inst);
+ if (!gprs_sns_fsm_registered) {
+ gprs_sns_init();
+ gprs_sns_fsm_registered = true;
+ }
+
nsi->cb = cb;
INIT_LLIST_HEAD(&nsi->gprs_nsvcs);
nsi->timeout[NS_TOUT_TNS_BLOCK] = 3;
@@ -1674,6 +1842,7 @@ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx)
nsi->timeout[NS_TOUT_TNS_TEST] = 30;
nsi->timeout[NS_TOUT_TNS_ALIVE] = 3;
nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10;
+ nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */
/* Create the dummy NSVC that we use for sending
* messages to non-existant/unknown NS-VC's */
@@ -1875,6 +2044,8 @@ int gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause)
{
int rc;
+ ERR_IF_NSVC_USES_SNS(nsvc, "RESET procedure based on API request");
+
LOGP(DNS, LOGL_INFO, "NSEI=%u RESET procedure based on API request\n",
nsvc->nsei);
@@ -1909,7 +2080,7 @@ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
{
struct gprs_nsvc *nsvc;
- nsvc = nsvc_by_rem_addr(nsi, dest);
+ nsvc = gprs_nsvc_by_rem_addr(nsi, dest);
if (!nsvc)
nsvc = gprs_nsvc_create(nsi, nsvci);
nsvc->ip.bts_addr = *dest;
@@ -1920,6 +2091,47 @@ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
return nsvc;
}
+/*! Establish a NS connection (from the BSS) to the SGSN using SNS auto-configuration
+ * \param nsi NS-instance
+ * \param[in] dest Destination IP/Port
+ * \param[in] nsei NSEI of the to-be-established NS-VC
+ * \param[in] nsvci NSVCI of the to-be-established NS-VC
+ * \returns struct gprs_nsvc representing the new NS-VC
+ *
+ * This function will establish a single NS/UDP/IP connection in uplink
+ * (BSS to SGSN) direction. It will start with the SNS-SIZE procedure,
+ * followed by BSS-originated SNS-CONFIG, then SGSN-originated SNS-CONFIG.
+ *
+ * Once configuration completes, the user will be notified by the S_SNS_CONFIGURED signal,
+ * at which point he typically would want to initiate NS-RESET by means of gprs_nsvc_reset().
+ */
+struct gprs_nsvc *gprs_ns_nsip_connect_sns(struct gprs_ns_inst *nsi,
+ struct sockaddr_in *dest, uint16_t nsei,
+ uint16_t nsvci)
+{
+ struct gprs_nsvc *nsvc;
+
+ /* FIXME: We are getting the order wrong here. Normally, one would want
+ * to start the SNS FSM *before* creating any NS-VC and then create the NS-VC
+ * after the SNS layer has established the IP/port/etc. However, this would
+ * require some massive code and API changes compared to existing libosmogb,
+ * so let's keep the old logic. */
+ nsvc = gprs_nsvc_by_rem_addr(nsi, dest);
+ if (!nsvc)
+ nsvc = gprs_nsvc_create(nsi, nsvci);
+ nsvc->ip.bts_addr = *dest;
+ nsvc->nsei = nsei;
+ nsvc->remote_end_is_sgsn = 1;
+ /* NSVCs are always UNBLOCKED in IP-SNS */
+ ns_set_state(nsvc, 0);
+
+ if (nsi->bss_sns_fi)
+ osmo_fsm_inst_term(nsi->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
+ nsi->bss_sns_fi = gprs_sns_bss_fsm_alloc(nsi, nsvc, "NSIP");
+ gprs_sns_bss_fsm_start(nsi);
+ return nsvc;
+}
+
void gprs_ns_set_log_ss(int ss)
{
DNS = ss;
@@ -1954,4 +2166,13 @@ void gprs_nsvc_start_test(struct gprs_nsvc *nsvc)
nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST);
}
+void gprs_start_alive_all_nsvcs(struct gprs_ns_inst *nsi)
+{
+ struct gprs_nsvc *nsvc;
+ llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
+ /* start the test procedure */
+ gprs_nsvc_start_test(nsvc);
+ }
+}
+
/*! @} */