2020-07-12 11:45:50 +00:00
|
|
|
/*! \file gprs_ns2_sns.c
|
|
|
|
* NS Sub-Network Service Protocol implementation
|
|
|
|
* 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
|
|
|
|
* as well as its successor 3GPP TS 48.016 */
|
|
|
|
|
2021-03-02 19:47:29 +00:00
|
|
|
/* (C) 2018-2021 by Harald Welte <laforge@gnumonks.org>
|
2020-07-12 11:45:50 +00:00
|
|
|
* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
|
|
* Author: Alexander Couzens <lynxis@fe80.eu>
|
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* The BSS NSE only has one SGSN IP address configured, and it will use the SNS procedures
|
|
|
|
* to communicated its local IPs/ports as well as all the SGSN side IPs/ports and
|
|
|
|
* associated weights. The BSS then uses this to establish a full mesh
|
|
|
|
* of NSVCs between all BSS-side IPs/ports and SGSN-side IPs/ports.
|
|
|
|
*
|
|
|
|
* Known limitation/expectation/bugs:
|
|
|
|
* - No concurrent dual stack. It supports either IPv4 or IPv6, but not both at the same time.
|
|
|
|
* - SNS Add/Change/Delete: Doesn't answer on the same NSVC as received SNS ADD/CHANGE/DELETE PDUs.
|
|
|
|
* - SNS Add/Change/Delete: Doesn't communicated the failed IPv4/IPv6 entries on the SNS_ACK.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/fsm.h>
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/core/socket.h>
|
2020-11-19 04:24:37 +00:00
|
|
|
#include <osmocom/core/sockaddr_str.h>
|
2020-07-12 11:45:50 +00:00
|
|
|
#include <osmocom/gsm/tlv.h>
|
|
|
|
#include <osmocom/gprs/gprs_msgb.h>
|
|
|
|
#include <osmocom/gprs/gprs_ns2.h>
|
|
|
|
#include <osmocom/gprs/protocol/gsm_08_16.h>
|
|
|
|
|
|
|
|
#include "gprs_ns2_internal.h"
|
|
|
|
|
|
|
|
#define S(x) (1 << (x))
|
|
|
|
|
|
|
|
enum ns2_sns_type {
|
|
|
|
IPv4,
|
|
|
|
IPv6,
|
|
|
|
};
|
|
|
|
|
2021-03-02 19:49:10 +00:00
|
|
|
enum ns2_sns_role {
|
|
|
|
GPRS_SNS_ROLE_BSS,
|
|
|
|
GPRS_SNS_ROLE_SGSN,
|
|
|
|
};
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
/* BSS-side-only states _ST_BSS_; SGSN-side only states _ST_SGSN_; others shared */
|
2020-07-12 11:45:50 +00:00
|
|
|
enum gprs_sns_bss_state {
|
|
|
|
GPRS_SNS_ST_UNCONFIGURED,
|
2021-03-23 14:22:16 +00:00
|
|
|
GPRS_SNS_ST_BSS_SIZE, /*!< SNS-SIZE procedure ongoing */
|
|
|
|
GPRS_SNS_ST_BSS_CONFIG_BSS, /*!< SNS-CONFIG procedure (BSS->SGSN) ongoing */
|
|
|
|
GPRS_SNS_ST_BSS_CONFIG_SGSN, /*!< SNS-CONFIG procedure (SGSN->BSS) ongoing */
|
2020-07-12 11:45:50 +00:00
|
|
|
GPRS_SNS_ST_CONFIGURED,
|
2021-03-02 19:49:10 +00:00
|
|
|
GPRS_SNS_ST_SGSN_WAIT_CONFIG, /* !< SGSN role: Wait for CONFIG from BSS */
|
|
|
|
GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, /* !< SGSN role: Wait for CONFIG-ACK from BSS */
|
2020-07-12 11:45:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum gprs_sns_event {
|
2021-02-15 01:37:03 +00:00
|
|
|
GPRS_SNS_EV_REQ_SELECT_ENDPOINT, /*!< Select a SNS endpoint from the list */
|
|
|
|
GPRS_SNS_EV_RX_SIZE,
|
|
|
|
GPRS_SNS_EV_RX_SIZE_ACK,
|
|
|
|
GPRS_SNS_EV_RX_CONFIG,
|
|
|
|
GPRS_SNS_EV_RX_CONFIG_END, /*!< SNS-CONFIG with end flag received */
|
|
|
|
GPRS_SNS_EV_RX_CONFIG_ACK,
|
|
|
|
GPRS_SNS_EV_RX_ADD,
|
|
|
|
GPRS_SNS_EV_RX_DELETE,
|
|
|
|
GPRS_SNS_EV_RX_CHANGE_WEIGHT,
|
2021-03-02 19:48:54 +00:00
|
|
|
GPRS_SNS_EV_RX_ACK, /*!< Rx of SNS-ACK (response to ADD/DELETE/CHG_WEIGHT */
|
2021-03-02 17:50:40 +00:00
|
|
|
GPRS_SNS_EV_REQ_NO_NSVC, /*!< no more NS-VC remaining (all dead) */
|
2021-02-15 01:37:03 +00:00
|
|
|
GPRS_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */
|
2021-03-02 17:50:40 +00:00
|
|
|
GPRS_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */
|
|
|
|
GPRS_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */
|
2020-07-12 11:45:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct value_string gprs_sns_event_names[] = {
|
2021-02-15 01:37:03 +00:00
|
|
|
{ GPRS_SNS_EV_REQ_SELECT_ENDPOINT, "REQ_SELECT_ENDPOINT" },
|
|
|
|
{ GPRS_SNS_EV_RX_SIZE, "RX_SIZE" },
|
|
|
|
{ GPRS_SNS_EV_RX_SIZE_ACK, "RX_SIZE_ACK" },
|
|
|
|
{ GPRS_SNS_EV_RX_CONFIG, "RX_CONFIG" },
|
|
|
|
{ GPRS_SNS_EV_RX_CONFIG_END, "RX_CONFIG_END" },
|
|
|
|
{ GPRS_SNS_EV_RX_CONFIG_ACK, "RX_CONFIG_ACK" },
|
|
|
|
{ GPRS_SNS_EV_RX_ADD, "RX_ADD" },
|
|
|
|
{ GPRS_SNS_EV_RX_DELETE, "RX_DELETE" },
|
2021-03-02 19:48:54 +00:00
|
|
|
{ GPRS_SNS_EV_RX_ACK, "RX_ACK" },
|
2021-02-15 01:37:03 +00:00
|
|
|
{ GPRS_SNS_EV_RX_CHANGE_WEIGHT, "RX_CHANGE_WEIGHT" },
|
|
|
|
{ GPRS_SNS_EV_REQ_NO_NSVC, "REQ_NO_NSVC" },
|
|
|
|
{ GPRS_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
|
|
|
|
{ GPRS_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
|
|
|
|
{ GPRS_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
|
2020-07-12 11:45:50 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
struct sns_endpoint {
|
|
|
|
struct llist_head list;
|
|
|
|
struct osmo_sockaddr saddr;
|
|
|
|
};
|
|
|
|
|
2021-02-12 02:17:59 +00:00
|
|
|
struct ns2_sns_bind {
|
|
|
|
struct llist_head list;
|
|
|
|
struct gprs_ns2_vc_bind *bind;
|
|
|
|
};
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
struct ns2_sns_elems {
|
|
|
|
struct gprs_ns_ie_ip4_elem *ip4;
|
|
|
|
unsigned int num_ip4;
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6;
|
|
|
|
unsigned int num_ip6;
|
|
|
|
};
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
struct ns2_sns_state {
|
|
|
|
struct gprs_ns2_nse *nse;
|
|
|
|
|
|
|
|
enum ns2_sns_type ip;
|
2021-03-02 19:49:10 +00:00
|
|
|
enum ns2_sns_role role; /* local role: BSS or SGSN */
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
/* holds the list of initial SNS endpoints */
|
|
|
|
struct llist_head sns_endpoints;
|
2021-02-12 02:17:59 +00:00
|
|
|
/* 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;
|
2020-12-07 06:37:07 +00:00
|
|
|
/* 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;
|
2020-07-12 11:45:50 +00:00
|
|
|
/* all SNS PDU will be sent over this nsvc */
|
|
|
|
struct gprs_ns2_vc *sns_nsvc;
|
2020-12-07 05:18:32 +00:00
|
|
|
/* timer N */
|
|
|
|
int N;
|
2021-02-03 17:25:27 +00:00
|
|
|
/* true if at least one nsvc is alive */
|
|
|
|
bool alive;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/* local configuration to send to the remote end */
|
2021-06-05 16:44:01 +00:00
|
|
|
struct ns2_sns_elems local;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
/* remote configuration as received */
|
|
|
|
struct ns2_sns_elems remote;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/* local configuration about our capabilities in terms of connections to
|
|
|
|
* remote (SGSN) side */
|
|
|
|
size_t num_max_nsvcs;
|
|
|
|
size_t num_max_ip4_remote;
|
|
|
|
size_t num_max_ip6_remote;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
return gss->nse;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* helper function to compute the sum of all (data or signaling) weights */
|
2021-06-06 00:43:14 +00:00
|
|
|
static int ip4_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
int weight_sum = 0;
|
|
|
|
|
2021-06-06 00:43:14 +00:00
|
|
|
for (i = 0; i < elems->num_ip4; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
if (data_weight)
|
2021-06-06 00:43:14 +00:00
|
|
|
weight_sum += elems->ip4[i].data_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
else
|
2021-06-06 00:43:14 +00:00
|
|
|
weight_sum += elems->ip4[i].sig_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
return weight_sum;
|
|
|
|
}
|
2021-06-06 00:43:14 +00:00
|
|
|
#define ip4_weight_sum_data(elems) ip4_weight_sum(elems, true)
|
|
|
|
#define ip4_weight_sum_sig(elems) ip4_weight_sum(elems, false)
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/* helper function to compute the sum of all (data or signaling) weights */
|
2021-06-06 00:43:14 +00:00
|
|
|
static int ip6_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
int weight_sum = 0;
|
|
|
|
|
2021-06-06 00:43:14 +00:00
|
|
|
for (i = 0; i < elems->num_ip6; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
if (data_weight)
|
2021-06-06 00:43:14 +00:00
|
|
|
weight_sum += elems->ip6[i].data_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
else
|
2021-06-06 00:43:14 +00:00
|
|
|
weight_sum += elems->ip6[i].sig_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
return weight_sum;
|
|
|
|
}
|
2021-06-06 00:43:14 +00:00
|
|
|
#define ip6_weight_sum_data(elems) ip6_weight_sum(elems, true)
|
|
|
|
#define ip6_weight_sum_sig(elems) ip6_weight_sum(elems, false)
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-03-02 19:47:29 +00:00
|
|
|
static int nss_weight_sum(const struct ns2_sns_state *nss, bool data_weight)
|
|
|
|
{
|
2021-06-06 00:43:14 +00:00
|
|
|
return ip4_weight_sum(&nss->remote, data_weight) +
|
|
|
|
ip6_weight_sum(&nss->remote, data_weight);
|
2021-03-02 19:47:29 +00:00
|
|
|
}
|
|
|
|
#define nss_weight_sum_data(nss) nss_weight_sum(nss, true)
|
|
|
|
#define nss_weight_sum_sig(nss) nss_weight_sum(nss, false)
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
static struct gprs_ns2_vc *nsvc_by_ip4_elem(struct gprs_ns2_nse *nse,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4)
|
|
|
|
{
|
|
|
|
struct osmo_sockaddr sa;
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
|
|
|
|
sa.u.sin.sin_port = ip4->udp_port;
|
|
|
|
sa.u.sin.sin_family = AF_INET;
|
|
|
|
|
2020-09-23 21:56:37 +00:00
|
|
|
return gprs_ns2_nsvc_by_sockaddr_nse(nse, &sa);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct gprs_ns2_vc *nsvc_by_ip6_elem(struct gprs_ns2_nse *nse,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
|
|
|
{
|
|
|
|
struct osmo_sockaddr sa;
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
sa.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
sa.u.sin6.sin6_family = AF_INET;
|
|
|
|
|
2020-09-23 21:56:37 +00:00
|
|
|
return gprs_ns2_nsvc_by_sockaddr_nse(nse, &sa);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 19:22:42 +00:00
|
|
|
/*! Return the initial SNS remote socket address
|
|
|
|
* \param nse NS Entity
|
|
|
|
* \return address of the initial SNS connection; NULL in case of error
|
|
|
|
*/
|
|
|
|
const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
|
|
|
|
if (!nse->bss_sns_fi)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
|
2020-12-07 06:37:07 +00:00
|
|
|
return &gss->initial->saddr;
|
2020-10-11 19:22:42 +00:00
|
|
|
}
|
|
|
|
|
2021-02-03 17:25:27 +00:00
|
|
|
/*! called when a nsvc is beeing freed or the nsvc became dead */
|
|
|
|
void ns2_sns_replace_nsvc(struct gprs_ns2_vc *nsvc)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-02-03 17:25:27 +00:00
|
|
|
struct gprs_ns2_nse *nse = nsvc->nse;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns2_vc *tmp;
|
2021-02-03 17:25:27 +00:00
|
|
|
struct osmo_fsm_inst *fi = nse->bss_sns_fi;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
|
|
|
|
if (!fi)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
if (nsvc != gss->sns_nsvc)
|
|
|
|
return;
|
|
|
|
|
2021-02-03 17:25:27 +00:00
|
|
|
gss->sns_nsvc = NULL;
|
|
|
|
if (gss->alive) {
|
2020-07-12 11:45:50 +00:00
|
|
|
llist_for_each_entry(tmp, &nse->nsvc, list) {
|
2021-02-03 17:25:27 +00:00
|
|
|
if (ns2_vc_is_unblocked(tmp)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
gss->sns_nsvc = tmp;
|
2021-02-03 17:25:27 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-02-03 17:25:27 +00:00
|
|
|
/* the SNS is waiting for its first NS-VC to come up
|
|
|
|
* choose any other nsvc */
|
|
|
|
llist_for_each_entry(tmp, &nse->nsvc, list) {
|
|
|
|
if (nsvc != tmp) {
|
|
|
|
gss->sns_nsvc = tmp;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
2021-02-03 17:25:27 +00:00
|
|
|
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_NO_NSVC, NULL);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-06-05 23:57:52 +00:00
|
|
|
static void ns2_clear_elems(struct ns2_sns_elems *elems)
|
2021-01-18 09:47:33 +00:00
|
|
|
{
|
2021-06-05 23:57:52 +00:00
|
|
|
TALLOC_FREE(elems->ip4);
|
|
|
|
TALLOC_FREE(elems->ip6);
|
2021-01-18 09:47:33 +00:00
|
|
|
|
2021-06-05 23:57:52 +00:00
|
|
|
elems->num_ip4 = 0;
|
|
|
|
elems->num_ip6 = 0;
|
2021-01-18 09:47:33 +00:00
|
|
|
}
|
|
|
|
|
2021-01-16 14:13:51 +00:00
|
|
|
static void ns2_vc_create_ip(struct osmo_fsm_inst *fi, struct gprs_ns2_nse *nse, const struct osmo_sockaddr *remote,
|
|
|
|
uint8_t sig_weight, uint8_t data_weight)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct gprs_ns2_inst *nsi = nse->nsi;
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
struct gprs_ns2_vc_bind *bind;
|
|
|
|
|
|
|
|
/* for every bind, create a connection if bind type == IP */
|
|
|
|
llist_for_each_entry(bind, &nsi->binding, list) {
|
2021-01-14 15:58:17 +00:00
|
|
|
if (bind->ll != GPRS_NS2_LL_UDP)
|
|
|
|
continue;
|
2020-07-12 11:45:50 +00:00
|
|
|
/* ignore failed connection */
|
|
|
|
nsvc = gprs_ns2_ip_connect_inactive(bind,
|
2021-01-16 14:13:51 +00:00
|
|
|
remote,
|
2020-07-12 11:45:50 +00:00
|
|
|
nse, 0);
|
|
|
|
if (!nsvc) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "SNS-CONFIG: Failed to create NSVC\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-01-16 14:13:51 +00:00
|
|
|
nsvc->sig_weight = sig_weight;
|
|
|
|
nsvc->data_weight = data_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-16 14:13:51 +00:00
|
|
|
static void ns2_nsvc_create_ip4(struct osmo_fsm_inst *fi,
|
|
|
|
struct gprs_ns2_nse *nse,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4)
|
|
|
|
{
|
|
|
|
struct osmo_sockaddr remote = { };
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
remote.u.sin.sin_family = AF_INET;
|
|
|
|
remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
|
|
|
|
remote.u.sin.sin_port = ip4->udp_port;
|
|
|
|
|
|
|
|
ns2_vc_create_ip(fi, nse, &remote, ip4->sig_weight, ip4->data_weight);
|
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
static void ns2_nsvc_create_ip6(struct osmo_fsm_inst *fi,
|
|
|
|
struct gprs_ns2_nse *nse,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
|
|
|
{
|
|
|
|
struct osmo_sockaddr remote = {};
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
remote.u.sin6.sin6_family = AF_INET6;
|
|
|
|
remote.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
remote.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
|
2021-01-16 14:13:51 +00:00
|
|
|
ns2_vc_create_ip(fi, nse, &remote, ip6->sig_weight, ip6->data_weight);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
static struct gprs_ns2_vc *nsvc_for_bind_and_remote(struct gprs_ns2_nse *nse,
|
|
|
|
struct gprs_ns2_vc_bind *bind,
|
|
|
|
const struct osmo_sockaddr *remote)
|
|
|
|
{
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
|
|
|
|
llist_for_each_entry(nsvc, &nse->nsvc, list) {
|
|
|
|
if (nsvc->bind != bind)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!osmo_sockaddr_cmp(remote, gprs_ns2_ip_vc_remote(nsvc)))
|
|
|
|
return nsvc;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
static int create_missing_nsvcs(struct osmo_fsm_inst *fi)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
2021-03-24 12:22:18 +00:00
|
|
|
struct ns2_sns_bind *sbind;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct osmo_sockaddr remote = { };
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
/* iterate over all remote IPv4 endpoints */
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->remote.num_ip4; i++) {
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
remote.u.sin.sin_family = AF_INET;
|
|
|
|
remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
|
|
|
|
remote.u.sin.sin_port = ip4->udp_port;
|
|
|
|
|
2021-03-24 12:22:18 +00:00
|
|
|
/* iterate over all local binds within this SNS */
|
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
struct gprs_ns2_vc_bind *bind = sbind->bind;
|
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
/* we only care about UDP binds */
|
2021-01-14 15:58:17 +00:00
|
|
|
if (bind->ll != GPRS_NS2_LL_UDP)
|
|
|
|
continue;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
|
|
|
|
if (!nsvc) {
|
2020-07-12 11:45:50 +00:00
|
|
|
nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
|
|
|
|
if (!nsvc) {
|
|
|
|
/* TODO: add to a list to send back a NS-STATUS */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update data / signalling weight */
|
|
|
|
nsvc->data_weight = ip4->data_weight;
|
|
|
|
nsvc->sig_weight = ip4->sig_weight;
|
|
|
|
nsvc->sns_only = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
/* iterate over all remote IPv4 endpoints */
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->remote.num_ip6; i++) {
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
remote.u.sin6.sin6_family = AF_INET6;
|
|
|
|
remote.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
remote.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
|
2021-03-24 12:22:18 +00:00
|
|
|
/* iterate over all local binds within this SNS */
|
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
struct gprs_ns2_vc_bind *bind = sbind->bind;
|
|
|
|
|
2021-01-14 15:58:17 +00:00
|
|
|
if (bind->ll != GPRS_NS2_LL_UDP)
|
|
|
|
continue;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-03-24 12:16:27 +00:00
|
|
|
/* we only care about UDP binds */
|
|
|
|
nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
|
|
|
|
if (!nsvc) {
|
2020-07-12 11:45:50 +00:00
|
|
|
nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
|
|
|
|
if (!nsvc) {
|
|
|
|
/* TODO: add to a list to send back a NS-STATUS */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update data / signalling weight */
|
|
|
|
nsvc->data_weight = ip6->data_weight;
|
|
|
|
nsvc->sig_weight = ip6->sig_weight;
|
|
|
|
nsvc->sns_only = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a given remote IPv4 element to gprs_sns_state */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int add_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
/* check for duplicates */
|
2021-06-06 01:33:35 +00:00
|
|
|
for (unsigned int i = 0; i < elems->num_ip4; i++) {
|
2021-06-06 00:26:40 +00:00
|
|
|
if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
|
2020-07-12 11:45:50 +00:00
|
|
|
continue;
|
2021-06-06 01:32:32 +00:00
|
|
|
return -1;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
elems->ip4 = talloc_realloc(gss, elems->ip4, struct gprs_ns_ie_ip4_elem,
|
|
|
|
elems->num_ip4+1);
|
|
|
|
elems->ip4[elems->num_ip4] = *ip4;
|
|
|
|
elems->num_ip4 += 1;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove a given remote IPv4 element from gprs_sns_state */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int remove_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
for (i = 0; i < elems->num_ip4; i++) {
|
|
|
|
if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
|
2020-07-12 11:45:50 +00:00
|
|
|
continue;
|
|
|
|
/* all array elements < i remain as they are; all > i are shifted left by one */
|
2021-06-06 00:26:40 +00:00
|
|
|
memmove(&elems->ip4[i], &elems->ip4[i+1], elems->num_ip4-i-1);
|
|
|
|
elems->num_ip4 -= 1;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the weights for specified remote IPv4 */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int update_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
for (i = 0; i < elems->num_ip4; i++) {
|
|
|
|
if (elems->ip4[i].ip_addr != ip4->ip_addr ||
|
|
|
|
elems->ip4[i].udp_port != ip4->udp_port)
|
2020-07-12 11:45:50 +00:00
|
|
|
continue;
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
elems->ip4[i].sig_weight = ip4->sig_weight;
|
|
|
|
elems->ip4[i].data_weight = ip4->data_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a given remote IPv6 element to gprs_sns_state */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int add_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-06-06 01:33:35 +00:00
|
|
|
/* check for duplicates */
|
|
|
|
for (unsigned int i = 0; i < elems->num_ip6; i++) {
|
|
|
|
if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
|
|
|
|
elems->ip6[i].udp_port != ip6->udp_port)
|
|
|
|
continue;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
elems->ip6 = talloc_realloc(gss, elems->ip6, struct gprs_ns_ie_ip6_elem,
|
|
|
|
elems->num_ip6+1);
|
|
|
|
elems->ip6[elems->num_ip6] = *ip6;
|
|
|
|
elems->num_ip6 += 1;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove a given remote IPv6 element from gprs_sns_state */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int remove_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
for (i = 0; i < elems->num_ip6; i++) {
|
|
|
|
if (memcmp(&elems->ip6[i], ip6, sizeof(*ip6)))
|
2020-07-12 11:45:50 +00:00
|
|
|
continue;
|
|
|
|
/* all array elements < i remain as they are; all > i are shifted left by one */
|
2021-06-06 00:26:40 +00:00
|
|
|
memmove(&elems->ip6[i], &elems->ip6[i+1], elems->num_ip6-i-1);
|
|
|
|
elems->num_ip6 -= 1;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the weights for specified remote IPv6 */
|
2021-06-06 00:26:40 +00:00
|
|
|
static int update_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2021-06-06 00:26:40 +00:00
|
|
|
for (i = 0; i < elems->num_ip6; i++) {
|
|
|
|
if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
|
|
|
|
elems->ip6[i].udp_port != ip6->udp_port)
|
2020-07-12 11:45:50 +00:00
|
|
|
continue;
|
2021-06-06 00:26:40 +00:00
|
|
|
elems->ip6[i].sig_weight = ip6->sig_weight;
|
|
|
|
elems->ip6[i].data_weight = ip6->data_weight;
|
2020-07-12 11:45:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_ie_ip4_elem *ip4, const struct gprs_ns_ie_ip6_elem *ip6)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
struct osmo_sockaddr sa = {};
|
2020-10-11 18:48:04 +00:00
|
|
|
const struct osmo_sockaddr *remote;
|
2020-07-12 11:45:50 +00:00
|
|
|
uint8_t new_signal;
|
|
|
|
uint8_t new_data;
|
|
|
|
|
|
|
|
/* TODO: Upon receiving an SNS-CHANGEWEIGHT PDU, if the resulting sum of the
|
|
|
|
* signalling weights of all the peer IP endpoints configured for this NSE is
|
|
|
|
* equal to zero or if the resulting sum of the data weights of all the peer IP
|
|
|
|
* endpoints configured for this NSE is equal to zero, the BSS/SGSN shall send an
|
|
|
|
* SNS-ACK PDU with a cause code of "Invalid weights". */
|
|
|
|
|
|
|
|
if (ip4) {
|
2021-06-06 00:26:40 +00:00
|
|
|
if (update_ip4_elem(gss, &gss->remote, ip4))
|
2020-07-12 11:45:50 +00:00
|
|
|
return -NS_CAUSE_UNKN_IP_EP;
|
|
|
|
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
|
|
|
|
sa.u.sin.sin_port = ip4->udp_port;
|
|
|
|
sa.u.sin.sin_family = AF_INET;
|
|
|
|
new_signal = ip4->sig_weight;
|
|
|
|
new_data = ip4->data_weight;
|
|
|
|
} else if (ip6) {
|
2021-06-06 00:26:40 +00:00
|
|
|
if (update_ip6_elem(gss, &gss->remote, ip6))
|
2020-07-12 11:45:50 +00:00
|
|
|
return -NS_CAUSE_UNKN_IP_EP;
|
|
|
|
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
sa.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
sa.u.sin6.sin6_family = AF_INET6;
|
|
|
|
new_signal = ip6->sig_weight;
|
|
|
|
new_data = ip6->data_weight;
|
|
|
|
} else {
|
|
|
|
OSMO_ASSERT(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_for_each_entry(nsvc, &nse->nsvc, list) {
|
2020-10-11 18:58:04 +00:00
|
|
|
remote = gprs_ns2_ip_vc_remote(nsvc);
|
2020-07-12 11:45:50 +00:00
|
|
|
/* all nsvc in NSE should be IP/UDP nsvc */
|
|
|
|
OSMO_ASSERT(remote);
|
|
|
|
|
|
|
|
if (osmo_sockaddr_cmp(&sa, remote))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_INFO, "CHANGE-WEIGHT NS-VC %s data_weight %u->%u, sig_weight %u->%u\n",
|
|
|
|
gprs_ns2_ll_str(nsvc), nsvc->data_weight, new_data,
|
|
|
|
nsvc->sig_weight, new_signal);
|
|
|
|
|
|
|
|
nsvc->data_weight = new_data;
|
|
|
|
nsvc->sig_weight = new_signal;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_sns_delete(struct osmo_fsm_inst *fi,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_vc *nsvc, *tmp;
|
2020-10-11 18:48:04 +00:00
|
|
|
const struct osmo_sockaddr *remote;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct osmo_sockaddr sa = {};
|
|
|
|
|
|
|
|
if (ip4) {
|
2021-06-06 00:26:40 +00:00
|
|
|
if (remove_ip4_elem(gss, &gss->remote, ip4) < 0)
|
2020-07-12 11:45:50 +00:00
|
|
|
return -NS_CAUSE_UNKN_IP_EP;
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
|
|
|
|
sa.u.sin.sin_port = ip4->udp_port;
|
|
|
|
sa.u.sin.sin_family = AF_INET;
|
|
|
|
} else if (ip6) {
|
2021-06-06 00:26:40 +00:00
|
|
|
if (remove_ip6_elem(gss, &gss->remote, ip6))
|
2020-07-12 11:45:50 +00:00
|
|
|
return -NS_CAUSE_UNKN_IP_EP;
|
|
|
|
|
|
|
|
/* copy over. Both data structures use network byte order */
|
|
|
|
sa.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
sa.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
sa.u.sin6.sin6_family = AF_INET6;
|
|
|
|
} else {
|
|
|
|
OSMO_ASSERT(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
|
2020-10-11 18:58:04 +00:00
|
|
|
remote = gprs_ns2_ip_vc_remote(nsvc);
|
2020-07-12 11:45:50 +00:00
|
|
|
/* all nsvc in NSE should be IP/UDP nsvc */
|
|
|
|
OSMO_ASSERT(remote);
|
|
|
|
if (osmo_sockaddr_cmp(&sa, remote))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_INFO, "DELETE NS-VC %s\n", gprs_ns2_ll_str(nsvc));
|
|
|
|
gprs_ns2_free_nsvc(nsvc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_sns_add(struct osmo_fsm_inst *fi,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
/* Upon receiving an SNS-ADD PDU, if the consequent number of IPv4 endpoints
|
|
|
|
* exceeds the number of IPv4 endpoints supported by the NSE, the NSE shall send
|
|
|
|
* an SNS-ACK PDU with a cause code set to "Invalid number of IP4 Endpoints". */
|
|
|
|
switch (gss->ip) {
|
|
|
|
case IPv4:
|
2021-06-06 01:32:32 +00:00
|
|
|
if (gss->remote.num_ip4 >= gss->num_max_ip4_remote)
|
|
|
|
return -NS_CAUSE_INVAL_NR_NS_VC;
|
|
|
|
/* TODO: log message duplicate */
|
2021-06-06 00:26:40 +00:00
|
|
|
rc = add_ip4_elem(gss, &gss->remote, ip4);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case IPv6:
|
2021-06-06 01:32:32 +00:00
|
|
|
if (gss->remote.num_ip6 >= gss->num_max_ip6_remote)
|
|
|
|
return -NS_CAUSE_INVAL_NR_NS_VC;
|
|
|
|
/* TODO: log message duplicate */
|
2021-06-06 00:26:40 +00:00
|
|
|
rc = add_ip6_elem(gss, &gss->remote, ip6);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* the gss->ip is initialized with the bss */
|
|
|
|
OSMO_ASSERT(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc)
|
2021-06-06 01:32:32 +00:00
|
|
|
return -NS_CAUSE_PROTO_ERR_UNSPEC;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/* Upon receiving an SNS-ADD PDU containing an already configured IP endpoint the
|
|
|
|
* NSE shall send an SNS-ACK PDU with the cause code "Protocol error -
|
|
|
|
* unspecified" */
|
|
|
|
switch (gss->ip) {
|
|
|
|
case IPv4:
|
|
|
|
nsvc = nsvc_by_ip4_elem(nse, ip4);
|
|
|
|
if (nsvc) {
|
|
|
|
/* the nsvc should be already in sync with the ip4 / ip6 elements */
|
|
|
|
return -NS_CAUSE_PROTO_ERR_UNSPEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: failure case */
|
|
|
|
ns2_nsvc_create_ip4(fi, nse, ip4);
|
|
|
|
break;
|
|
|
|
case IPv6:
|
|
|
|
nsvc = nsvc_by_ip6_elem(nse, ip6);
|
|
|
|
if (nsvc) {
|
|
|
|
/* the nsvc should be already in sync with the ip4 / ip6 elements */
|
|
|
|
return -NS_CAUSE_PROTO_ERR_UNSPEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: failure case */
|
|
|
|
ns2_nsvc_create_ip6(fi, nse, ip6);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
gprs_ns2_start_alive_all_nsvcs(nse);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-03-02 21:20:17 +00:00
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
2020-12-07 06:37:07 +00:00
|
|
|
/* empty state - SNS Select will start by ns2_sns_st_all_action() */
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_size(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-03-02 21:20:17 +00:00
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_inst *nsi = nse->nsi;
|
|
|
|
struct tlv_parsed *tp = NULL;
|
|
|
|
|
2021-03-02 21:20:17 +00:00
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
switch (event) {
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_SIZE_ACK:
|
2020-07-12 11:45:50 +00:00
|
|
|
tp = data;
|
|
|
|
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "SNS-SIZE-ACK with cause %s\n",
|
|
|
|
gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
|
|
|
|
/* TODO: What to do? */
|
|
|
|
} else {
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_BSS,
|
2020-07-12 11:45:50 +00:00
|
|
|
nsi->timeout[NS_TOUT_TSNS_PROV], 2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-04 18:49:38 +00:00
|
|
|
static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, enum ns2_sns_type stype)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
const struct osmo_sockaddr *sa = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
if (!sa)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (stype) {
|
|
|
|
case IPv4:
|
|
|
|
if (sa->u.sas.ss_family == AF_INET)
|
|
|
|
count++;
|
|
|
|
break;
|
|
|
|
case IPv6:
|
|
|
|
if (sa->u.sas.ss_family == AF_INET6)
|
|
|
|
count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:03:27 +00:00
|
|
|
static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2020-12-07 06:37:07 +00:00
|
|
|
struct gprs_ns_ie_ip4_elem *ip4_elems;
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6_elems;
|
|
|
|
struct gprs_ns2_vc_bind *bind;
|
2021-02-12 02:17:59 +00:00
|
|
|
struct ns2_sns_bind *sbind;
|
2021-03-02 19:49:10 +00:00
|
|
|
const struct osmo_sockaddr *remote;
|
2020-12-07 06:37:07 +00:00
|
|
|
const struct osmo_sockaddr *sa;
|
|
|
|
struct osmo_sockaddr local;
|
|
|
|
int count;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-06-05 23:57:52 +00:00
|
|
|
ns2_clear_elems(&gss->local);
|
2021-01-18 09:47:33 +00:00
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
/* no initial available */
|
2021-03-02 19:49:10 +00:00
|
|
|
if (gss->role == GPRS_SNS_ROLE_BSS) {
|
|
|
|
if (!gss->initial)
|
|
|
|
return;
|
|
|
|
remote = &gss->initial->saddr;
|
|
|
|
} else
|
|
|
|
remote = gprs_ns2_ip_vc_remote(gss->sns_nsvc);
|
2020-12-07 06:37:07 +00:00
|
|
|
|
|
|
|
/* count how many bindings are available (only UDP binds) */
|
2021-02-12 02:17:59 +00:00
|
|
|
count = llist_count(&gss->binds);
|
2020-12-07 06:37:07 +00:00
|
|
|
if (count == 0) {
|
2021-03-04 14:49:21 +00:00
|
|
|
LOGPFSML(fi, LOGL_ERROR, "No local binds for this NSE -> cannot determine IP endpoints\n");
|
2020-12-07 06:37:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (gss->ip) {
|
|
|
|
case IPv4:
|
2021-06-05 16:44:01 +00:00
|
|
|
ip4_elems = talloc_realloc(fi, gss->local.ip4, struct gprs_ns_ie_ip4_elem, count);
|
2020-12-07 06:37:07 +00:00
|
|
|
if (!ip4_elems)
|
|
|
|
return;
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.ip4 = ip4_elems;
|
2021-02-12 02:17:59 +00:00
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
bind = sbind->bind;
|
2020-12-07 06:37:07 +00:00
|
|
|
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;
|
2021-02-08 22:13:12 +00:00
|
|
|
ip4_elems->sig_weight = bind->sns_sig_weight;
|
|
|
|
ip4_elems->data_weight = bind->sns_data_weight;
|
2020-12-07 06:37:07 +00:00
|
|
|
ip4_elems++;
|
|
|
|
}
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.num_ip4 = count;
|
|
|
|
gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * gss->local.num_ip4, 8);
|
2020-12-07 06:37:07 +00:00
|
|
|
break;
|
|
|
|
case IPv6:
|
|
|
|
/* IPv6 */
|
2021-06-05 16:44:01 +00:00
|
|
|
ip6_elems = talloc_realloc(fi, gss->local.ip6, struct gprs_ns_ie_ip6_elem, count);
|
2020-12-07 06:37:07 +00:00
|
|
|
if (!ip6_elems)
|
|
|
|
return;
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.ip6 = ip6_elems;
|
2020-12-07 06:37:07 +00:00
|
|
|
|
2021-02-12 02:17:59 +00:00
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
bind = sbind->bind;
|
2020-12-07 06:37:07 +00:00
|
|
|
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;
|
2021-02-08 22:13:12 +00:00
|
|
|
ip6_elems->sig_weight = bind->sns_sig_weight;
|
|
|
|
ip6_elems->data_weight = bind->sns_data_weight;
|
2020-12-07 06:37:07 +00:00
|
|
|
|
|
|
|
ip6_elems++;
|
|
|
|
}
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.num_ip6 = count;
|
|
|
|
gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->local.num_ip6, 8);
|
2020-12-07 06:37:07 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-03-04 12:03:27 +00:00
|
|
|
}
|
|
|
|
|
2021-04-26 18:39:46 +00:00
|
|
|
static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss)
|
|
|
|
{
|
|
|
|
/* take the first bind or take the next bind */
|
|
|
|
if (!gss->initial_bind || gss->initial_bind->list.next == &gss->binds)
|
|
|
|
gss->initial_bind = llist_first_entry_or_null(&gss->binds, struct ns2_sns_bind, list);
|
|
|
|
else
|
|
|
|
gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
|
|
|
|
}
|
|
|
|
|
2021-03-04 12:03:27 +00:00
|
|
|
/* setup all dynamic SNS settings, create a new nsvc and send the SIZE */
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
2021-03-04 12:03:27 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
|
2021-03-02 21:20:17 +00:00
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
|
|
|
|
2021-03-04 12:03:27 +00:00
|
|
|
/* on a generic failure, the timer callback will recover */
|
|
|
|
if (old_state != GPRS_SNS_ST_UNCONFIGURED)
|
|
|
|
ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_FAILURE);
|
2021-03-23 14:22:16 +00:00
|
|
|
if (old_state != GPRS_SNS_ST_BSS_SIZE)
|
2021-03-04 12:03:27 +00:00
|
|
|
gss->N = 0;
|
|
|
|
|
|
|
|
gss->alive = false;
|
|
|
|
|
|
|
|
ns2_sns_compute_local_ep_from_binds(fi);
|
2021-04-26 18:39:46 +00:00
|
|
|
ns2_sns_choose_next_bind(gss);
|
2021-03-04 12:03:27 +00:00
|
|
|
|
|
|
|
/* setup the NSVC */
|
|
|
|
if (!gss->sns_nsvc) {
|
|
|
|
struct gprs_ns2_vc_bind *bind = gss->initial_bind->bind;
|
|
|
|
struct osmo_sockaddr *remote = &gss->initial->saddr;
|
|
|
|
gss->sns_nsvc = ns2_ip_bind_connect(bind, gss->nse, remote);
|
|
|
|
if (!gss->sns_nsvc)
|
|
|
|
return;
|
2021-03-05 07:09:08 +00:00
|
|
|
/* A pre-configured endpoint shall not be used for NSE data or signalling traffic
|
|
|
|
* (with the exception of Size and Configuration procedures) unless it is configured
|
|
|
|
* by the SGSN using the auto-configuration procedures */
|
2021-03-04 12:03:27 +00:00
|
|
|
gss->sns_nsvc->sns_only = true;
|
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
if (gss->num_max_ip4_remote > 0)
|
2021-06-05 16:44:01 +00:00
|
|
|
ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->local.num_ip4, -1);
|
2020-07-12 11:45:50 +00:00
|
|
|
else
|
2021-06-05 16:44:01 +00:00
|
|
|
ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, gss->local.num_ip6);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_config_bss(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-03-02 21:20:17 +00:00
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2021-02-05 16:18:08 +00:00
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
2021-03-02 21:20:17 +00:00
|
|
|
struct tlv_parsed *tp = NULL;
|
|
|
|
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
switch (event) {
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_CONFIG_ACK:
|
2020-07-12 11:45:50 +00:00
|
|
|
tp = (struct tlv_parsed *) data;
|
|
|
|
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "SNS-CONFIG-ACK with cause %s\n",
|
|
|
|
gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
|
|
|
|
/* TODO: What to do? */
|
|
|
|
} else {
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_SGSN, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 3);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_config_bss_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2021-02-05 16:18:39 +00:00
|
|
|
|
2021-03-02 21:20:17 +00:00
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
if (old_state != GPRS_SNS_ST_BSS_CONFIG_BSS)
|
2021-02-05 16:18:39 +00:00
|
|
|
gss->N = 0;
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
/* Transmit SNS-CONFIG */
|
|
|
|
switch (gss->ip) {
|
|
|
|
case IPv4:
|
|
|
|
ns2_tx_sns_config(gss->sns_nsvc, true,
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.ip4, gss->local.num_ip4,
|
2020-12-07 05:19:29 +00:00
|
|
|
NULL, 0);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case IPv6:
|
|
|
|
ns2_tx_sns_config(gss->sns_nsvc, true,
|
2020-12-07 05:19:29 +00:00
|
|
|
NULL, 0,
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->local.ip6, gss->local.num_ip6);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-03 17:25:27 +00:00
|
|
|
/* calculate the timeout of the configured state. the configured
|
|
|
|
* state will fail if not at least one NS-VC is alive within X second.
|
|
|
|
*/
|
|
|
|
static inline int ns_sns_configured_timeout(struct osmo_fsm_inst *fi)
|
|
|
|
{
|
|
|
|
int secs;
|
|
|
|
struct gprs_ns2_inst *nsi = nse_inst_from_fi(fi)->nsi;
|
|
|
|
secs = nsi->timeout[NS_TOUT_TNS_ALIVE] * nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES];
|
|
|
|
secs += nsi->timeout[NS_TOUT_TNS_TEST];
|
|
|
|
|
|
|
|
return secs;
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-03-02 19:47:29 +00:00
|
|
|
/* append the remote endpoints from the parsed TLV array to the ns2_sns_state */
|
|
|
|
static int ns_sns_append_remote_eps(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
|
2021-03-02 19:47:29 +00:00
|
|
|
if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
|
|
|
|
const struct gprs_ns_ie_ip4_elem *v4_list;
|
|
|
|
unsigned int num_v4;
|
|
|
|
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
|
|
|
|
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
if (num_v4 && gss->remote.ip6)
|
2021-03-02 19:47:29 +00:00
|
|
|
return -NS_CAUSE_INVAL_NR_IPv4_EP;
|
|
|
|
|
|
|
|
/* realloc to the new size */
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->remote.ip4 = talloc_realloc(gss, gss->remote.ip4,
|
2021-03-02 19:47:29 +00:00
|
|
|
struct gprs_ns_ie_ip4_elem,
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->remote.num_ip4 + num_v4);
|
2021-03-02 19:47:29 +00:00
|
|
|
/* append the new entries to the end of the list */
|
2021-06-05 16:44:01 +00:00
|
|
|
memcpy(&gss->remote.ip4[gss->remote.num_ip4], v4_list, num_v4*sizeof(*v4_list));
|
|
|
|
gss->remote.num_ip4 += num_v4;
|
2021-03-02 19:47:29 +00:00
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv4 list now %u entries\n",
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->remote.num_ip4);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 19:47:29 +00:00
|
|
|
if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
|
|
|
|
const struct gprs_ns_ie_ip6_elem *v6_list;
|
|
|
|
unsigned int num_v6;
|
|
|
|
v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
|
|
|
|
num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
if (num_v6 && gss->remote.ip4)
|
2021-03-02 19:47:29 +00:00
|
|
|
return -NS_CAUSE_INVAL_NR_IPv6_EP;
|
|
|
|
|
|
|
|
/* realloc to the new size */
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->remote.ip6 = talloc_realloc(gss, gss->remote.ip6,
|
2021-03-02 19:47:29 +00:00
|
|
|
struct gprs_ns_ie_ip6_elem,
|
2021-06-05 16:44:01 +00:00
|
|
|
gss->remote.num_ip6 + num_v6);
|
2021-03-02 19:47:29 +00:00
|
|
|
/* append the new entries to the end of the list */
|
2021-06-05 16:44:01 +00:00
|
|
|
memcpy(&gss->remote.ip6[gss->remote.num_ip6], v6_list, num_v6*sizeof(*v6_list));
|
|
|
|
gss->remote.num_ip6 += num_v6;
|
2021-03-02 19:47:29 +00:00
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv6 list now %d entries\n",
|
|
|
|
gss->remote.num_ip6);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
2021-03-02 19:47:29 +00:00
|
|
|
|
|
|
|
return 0;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_config_sgsn_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
2021-02-05 16:18:39 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
|
2021-03-02 21:20:17 +00:00
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
if (old_state != GPRS_SNS_ST_BSS_CONFIG_SGSN)
|
2021-02-05 16:18:39 +00:00
|
|
|
gss->N = 0;
|
|
|
|
}
|
|
|
|
|
2021-03-23 14:22:16 +00:00
|
|
|
static void ns2_sns_st_bss_config_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2021-03-02 19:47:29 +00:00
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
uint8_t cause;
|
|
|
|
int rc;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-03-02 21:20:17 +00:00
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
switch (event) {
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_CONFIG_END:
|
|
|
|
case GPRS_SNS_EV_RX_CONFIG:
|
2021-03-02 19:47:29 +00:00
|
|
|
rc = ns_sns_append_remote_eps(fi, data);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
|
|
|
return;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
2021-03-02 19:47:29 +00:00
|
|
|
if (event == GPRS_SNS_EV_RX_CONFIG_END) {
|
|
|
|
/* check if sum of data / sig weights == 0 */
|
|
|
|
if (nss_weight_sum_data(gss) == 0 || nss_weight_sum_sig(gss) == 0) {
|
|
|
|
cause = NS_CAUSE_INVAL_WEIGH;
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
create_missing_nsvcs(fi);
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
|
|
|
|
/* start the test procedure on ALL NSVCs! */
|
|
|
|
gprs_ns2_start_alive_all_nsvcs(nse);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
|
|
|
|
} else {
|
|
|
|
/* just send CONFIG-ACK */
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
|
|
|
|
osmo_timer_schedule(&fi->timer, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 0);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 01:37:03 +00:00
|
|
|
/* called when receiving GPRS_SNS_EV_RX_ADD in state configure */
|
2020-07-12 11:45:50 +00:00
|
|
|
static void ns2_sns_st_configured_add(struct osmo_fsm_inst *fi,
|
|
|
|
struct ns2_sns_state *gss,
|
|
|
|
struct tlv_parsed *tp)
|
|
|
|
{
|
|
|
|
const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
|
|
|
|
const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
|
|
|
|
int num_v4 = 0, num_v6 = 0;
|
|
|
|
uint8_t trans_id, cause = 0xff;
|
2020-09-18 07:48:05 +00:00
|
|
|
unsigned int i;
|
2020-07-12 11:45:50 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
/* TODO: refactor EV_ADD/CHANGE/REMOVE by
|
|
|
|
* check uniqueness within the lists (no doublicate entries)
|
|
|
|
* check not-known-by-us and sent back a list of unknown/known values
|
|
|
|
* (abnormal behaviour according to 48.016)
|
|
|
|
*/
|
|
|
|
|
|
|
|
trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
|
|
|
|
if (gss->ip == IPv4) {
|
|
|
|
if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
|
|
|
|
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for (i = 0; i < num_v4; i++) {
|
|
|
|
unsigned int j;
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_add(fi, &v4_list[i], NULL);
|
|
|
|
if (rc < 0) {
|
|
|
|
/* rollback/undo to restore previous state */
|
2020-09-18 07:48:05 +00:00
|
|
|
for (j = 0; j < i; j++)
|
2020-07-12 11:45:50 +00:00
|
|
|
do_sns_delete(fi, &v4_list[j], NULL);
|
|
|
|
cause = -rc;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { /* IPv6 */
|
|
|
|
if (!TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv6_EP;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
|
|
|
|
num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for (i = 0; i < num_v6; i++) {
|
|
|
|
unsigned int j;
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_add(fi, NULL, &v6_list[i]);
|
|
|
|
if (rc < 0) {
|
|
|
|
/* rollback/undo to restore previous state */
|
2020-09-18 07:48:05 +00:00
|
|
|
for (j = 0; j < i; j++)
|
2020-07-12 11:45:50 +00:00
|
|
|
do_sns_delete(fi, NULL, &v6_list[j]);
|
|
|
|
cause = -rc;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: correct behaviour is to answer to the *same* NSVC from which the SNS_ADD was received */
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_configured_delete(struct osmo_fsm_inst *fi,
|
|
|
|
struct ns2_sns_state *gss,
|
|
|
|
struct tlv_parsed *tp)
|
|
|
|
{
|
|
|
|
const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
|
|
|
|
const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
|
|
|
|
int num_v4 = 0, num_v6 = 0;
|
|
|
|
uint8_t trans_id, cause = 0xff;
|
2020-09-18 07:48:05 +00:00
|
|
|
unsigned int i;
|
2020-07-12 11:45:50 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
/* TODO: split up delete into v4 + v6
|
|
|
|
* TODO: check if IPv4_LIST or IP_ADDR(v4) is present on IPv6 and vice versa
|
|
|
|
* TODO: check if IPv4_LIST/IPv6_LIST and IP_ADDR is present at the same time
|
|
|
|
*/
|
|
|
|
trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
|
|
|
|
if (gss->ip == IPv4) {
|
|
|
|
if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
|
|
|
|
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
|
|
|
|
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for ( i = 0; i < num_v4; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_delete(fi, &v4_list[i], NULL);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to delete others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cause != 0xff) {
|
|
|
|
/* TODO: create list of not-deleted and return it */
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (TLVP_PRESENT(tp, NS_IE_IP_ADDR) && TLVP_LEN(tp, NS_IE_IP_ADDR) == 5) {
|
|
|
|
/* delete all NS-VCs for given IPv4 address */
|
|
|
|
const uint8_t *ie = TLVP_VAL(tp, NS_IE_IP_ADDR);
|
|
|
|
struct gprs_ns_ie_ip4_elem *ip4_remote;
|
|
|
|
uint32_t ip_addr = *(uint32_t *)(ie+1);
|
|
|
|
if (ie[0] != 0x01) { /* Address Type != IPv4 */
|
|
|
|
cause = NS_CAUSE_UNKN_IP_ADDR;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* make a copy as do_sns_delete() will change the array underneath us */
|
2021-06-05 16:44:01 +00:00
|
|
|
ip4_remote = talloc_memdup(fi, gss->remote.ip4,
|
|
|
|
gss->remote.num_ip4 * sizeof(*v4_list));
|
|
|
|
for (i = 0; i < gss->remote.num_ip4; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
if (ip4_remote[i].ip_addr == ip_addr) {
|
|
|
|
rc = do_sns_delete(fi, &ip4_remote[i], NULL);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to delete others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
talloc_free(ip4_remote);
|
|
|
|
if (cause != 0xff) {
|
|
|
|
/* TODO: create list of not-deleted and return it */
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else { /* IPv6 */
|
|
|
|
if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
|
|
|
|
v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
|
|
|
|
num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for (i = 0; i < num_v6; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_delete(fi, NULL, &v6_list[i]);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to delete others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cause != 0xff) {
|
|
|
|
/* TODO: create list of not-deleted and return it */
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (TLVP_PRES_LEN(tp, NS_IE_IP_ADDR, 17)) {
|
|
|
|
/* delete all NS-VCs for given IPv4 address */
|
|
|
|
const uint8_t *ie = TLVP_VAL(tp, NS_IE_IP_ADDR);
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6_remote;
|
|
|
|
struct in6_addr ip6_addr;
|
2020-09-18 07:48:05 +00:00
|
|
|
unsigned int i;
|
2020-07-12 11:45:50 +00:00
|
|
|
if (ie[0] != 0x02) { /* Address Type != IPv6 */
|
|
|
|
cause = NS_CAUSE_UNKN_IP_ADDR;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(&ip6_addr, (ie+1), sizeof(struct in6_addr));
|
|
|
|
/* make a copy as do_sns_delete() will change the array underneath us */
|
2021-06-05 16:44:01 +00:00
|
|
|
ip6_remote = talloc_memdup(fi, gss->remote.ip6,
|
|
|
|
gss->remote.num_ip6 * sizeof(*v4_list));
|
|
|
|
for (i = 0; i < gss->remote.num_ip6; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
if (!memcmp(&ip6_remote[i].ip_addr, &ip6_addr, sizeof(struct in6_addr))) {
|
|
|
|
rc = do_sns_delete(fi, NULL, &ip6_remote[i]);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to delete others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
talloc_free(ip6_remote);
|
|
|
|
if (cause != 0xff) {
|
|
|
|
/* TODO: create list of not-deleted and return it */
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv6_EP;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_configured_change(struct osmo_fsm_inst *fi,
|
|
|
|
struct ns2_sns_state *gss,
|
|
|
|
struct tlv_parsed *tp)
|
|
|
|
{
|
|
|
|
const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
|
|
|
|
const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
|
|
|
|
int num_v4 = 0, num_v6 = 0;
|
|
|
|
uint8_t trans_id, cause = 0xff;
|
|
|
|
int rc = 0;
|
2020-09-18 07:48:05 +00:00
|
|
|
unsigned int i;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
|
|
|
|
if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
|
|
|
|
v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
|
|
|
|
num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for (i = 0; i < num_v4; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_change_weight(fi, &v4_list[i], NULL);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cause != 0xff) {
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
|
|
|
|
v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
|
|
|
|
num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
|
2020-09-18 07:48:05 +00:00
|
|
|
for (i = 0; i < num_v6; i++) {
|
2020-07-12 11:45:50 +00:00
|
|
|
rc = do_sns_change_weight(fi, NULL, &v6_list[i]);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
/* continue to others */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cause != 0xff) {
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct tlv_parsed *tp = data;
|
|
|
|
|
|
|
|
switch (event) {
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_ADD:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_add(fi, gss, tp);
|
|
|
|
break;
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_DELETE:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_delete(fi, gss, tp);
|
|
|
|
break;
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_RX_CHANGE_WEIGHT:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_change(fi, gss, tp);
|
|
|
|
break;
|
2021-02-15 01:37:03 +00:00
|
|
|
case GPRS_SNS_EV_REQ_NSVC_ALIVE:
|
2021-02-03 17:25:27 +00:00
|
|
|
osmo_timer_del(&fi->timer);
|
|
|
|
break;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|
|
|
{
|
2021-04-01 13:29:16 +00:00
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
2021-04-01 13:29:16 +00:00
|
|
|
/* NS-VC status updates are only parsed in ST_CONFIGURED.
|
|
|
|
* Do an initial check if there are any nsvc alive atm */
|
|
|
|
llist_for_each_entry(nsvc, &nse->nsvc, list) {
|
|
|
|
if (ns2_vc_is_unblocked(nsvc)) {
|
|
|
|
gss->alive = true;
|
|
|
|
osmo_timer_del(&fi->timer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
gprs_ns2: sns: remove the initial SNS NSVC if it's not part
A SNS configuration can be done over a NSVC, however this initial NSVC doesn't
need to be part of the configuration.
Those NSVC need to be removed when the configuration is done.
This wrong behaviour can be seen in the vty `show ns` on
NSEI 00001: UDP, ALIVE
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00001-SNS)[0x55c72c09b420]', ID: 'NSE00001-SNS'
Log-Level: 'DEBUG', State: 'CONFIGURED'
Maximum number of remote NS-VCs: 8192, IPv4 Endpoints: 8192, IPv6 Endpoints: 8192
Local IPv4 Endpoints:
10.0.0.1:23000, Signalling Weight: 1, Data Weight: 1
Remote IPv4 Endpoints:
10.0.2.2:23000, Signalling Weight: 1, Data Weight: 0
10.0.2.2:23001, Signalling Weight: 0, Data Weight: 1
3 NS-VC:
NSVCI none: UNBLOCKED DYNAMIC data_weight=1 sig_weight=0 udp)[10.0.0.1]:23000<>[10.0.2.2]:23000
NSVCI none: UNBLOCKED DYNAMIC data_weight=0 sig_weight=1 udp)[10.0.0.1]:23000<>[10.0.2.2]:23001
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[10.0.0.1]:23000<>[10.0.2.2]:8888
The UNCONFIGURED NSVC should not be present in when SNS is in CONFIGURED.
Related: SYS#5416
Change-Id: I4045ac6c033ae084743b17a16eef4fcff76589b9
2021-04-06 13:45:47 +00:00
|
|
|
/* remove the initial NSVC if the NSVC isn't part of the configuration */
|
|
|
|
if (gss->sns_nsvc->sns_only)
|
|
|
|
gprs_ns2_free_nsvc(gss->sns_nsvc);
|
|
|
|
|
2021-01-25 15:23:29 +00:00
|
|
|
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct osmo_fsm_state ns2_sns_bss_states[] = {
|
|
|
|
[GPRS_SNS_ST_UNCONFIGURED] = {
|
2020-12-07 06:37:07 +00:00
|
|
|
.in_event_mask = 0, /* handled by all_state_action */
|
2021-04-10 16:20:21 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_BSS_SIZE),
|
2020-07-12 11:45:50 +00:00
|
|
|
.name = "UNCONFIGURED",
|
2021-03-23 14:22:16 +00:00
|
|
|
.action = ns2_sns_st_bss_unconfigured,
|
2020-07-12 11:45:50 +00:00
|
|
|
},
|
2021-03-23 14:22:16 +00:00
|
|
|
[GPRS_SNS_ST_BSS_SIZE] = {
|
2021-02-15 01:37:03 +00:00
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_SIZE_ACK),
|
2020-07-12 11:45:50 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-03-23 14:22:16 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_SIZE) |
|
|
|
|
S(GPRS_SNS_ST_BSS_CONFIG_BSS),
|
|
|
|
.name = "BSS_SIZE",
|
|
|
|
.action = ns2_sns_st_bss_size,
|
|
|
|
.onenter = ns2_sns_st_bss_size_onenter,
|
2020-07-12 11:45:50 +00:00
|
|
|
},
|
2021-03-23 14:22:16 +00:00
|
|
|
[GPRS_SNS_ST_BSS_CONFIG_BSS] = {
|
2021-02-15 01:37:03 +00:00
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_CONFIG_ACK),
|
2020-07-12 11:45:50 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-03-23 14:22:16 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_CONFIG_BSS) |
|
|
|
|
S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
|
|
|
|
S(GPRS_SNS_ST_BSS_SIZE),
|
|
|
|
.name = "BSS_CONFIG_BSS",
|
|
|
|
.action = ns2_sns_st_bss_config_bss,
|
|
|
|
.onenter = ns2_sns_st_bss_config_bss_onenter,
|
2020-07-12 11:45:50 +00:00
|
|
|
},
|
2021-03-23 14:22:16 +00:00
|
|
|
[GPRS_SNS_ST_BSS_CONFIG_SGSN] = {
|
2021-02-15 01:37:03 +00:00
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_CONFIG) |
|
|
|
|
S(GPRS_SNS_EV_RX_CONFIG_END),
|
2020-07-12 11:45:50 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-03-23 14:22:16 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
|
2020-07-12 11:45:50 +00:00
|
|
|
S(GPRS_SNS_ST_CONFIGURED) |
|
2021-03-23 14:22:16 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_SIZE),
|
|
|
|
.name = "BSS_CONFIG_SGSN",
|
|
|
|
.action = ns2_sns_st_bss_config_sgsn,
|
|
|
|
.onenter = ns2_sns_st_bss_config_sgsn_onenter,
|
2020-07-12 11:45:50 +00:00
|
|
|
},
|
|
|
|
[GPRS_SNS_ST_CONFIGURED] = {
|
2021-02-15 01:37:03 +00:00
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_ADD) |
|
|
|
|
S(GPRS_SNS_EV_RX_DELETE) |
|
|
|
|
S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
|
2020-12-06 02:31:44 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-03-23 14:22:16 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_SIZE),
|
2020-07-12 11:45:50 +00:00
|
|
|
.name = "CONFIGURED",
|
|
|
|
.action = ns2_sns_st_configured,
|
|
|
|
.onenter = ns2_sns_st_configured_onenter,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
|
|
|
|
{
|
2020-12-07 05:18:32 +00:00
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_inst *nsi = nse->nsi;
|
|
|
|
|
2020-12-07 05:18:32 +00:00
|
|
|
gss->N++;
|
2020-07-12 11:45:50 +00:00
|
|
|
switch (fi->T) {
|
|
|
|
case 1:
|
2020-12-21 13:06:24 +00:00
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES]) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: Size retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2020-12-21 13:06:24 +00:00
|
|
|
} else {
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nsi->timeout[NS_TOUT_TSNS_PROV], 1);
|
2020-12-21 13:06:24 +00:00
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2020-12-21 13:06:24 +00:00
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
|
2021-02-05 16:18:08 +00:00
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: BSS Config retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2020-12-21 13:06:24 +00:00
|
|
|
} else {
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_BSS, nsi->timeout[NS_TOUT_TSNS_PROV], 2);
|
2020-12-21 13:06:24 +00:00
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
2021-02-03 17:25:27 +00:00
|
|
|
case 3:
|
2021-02-05 16:18:08 +00:00
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: SGSN Config retries failed. Selecting next IP-SNS endpoint.\n", nse->nsei);
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2021-02-05 16:18:08 +00:00
|
|
|
} else {
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_SGSN, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
|
2021-02-05 16:18:08 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
2021-02-03 17:25:27 +00:00
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.\n", nse->nsei);
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2021-02-03 17:25:27 +00:00
|
|
|
break;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:48:31 +00:00
|
|
|
/* common allstate-action for both roles */
|
2020-07-12 11:45:50 +00:00
|
|
|
static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
|
|
{
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
2021-02-12 02:17:59 +00:00
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
struct gprs_ns2_vc *nsvc, *nsvc2;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
switch (event) {
|
2021-02-12 02:17:59 +00:00
|
|
|
case GPRS_SNS_EV_REQ_ADD_BIND:
|
|
|
|
sbind = data;
|
|
|
|
switch (fi->state) {
|
|
|
|
case GPRS_SNS_ST_UNCONFIGURED:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2021-02-12 02:17:59 +00:00
|
|
|
break;
|
2021-03-23 14:22:16 +00:00
|
|
|
case GPRS_SNS_ST_BSS_SIZE:
|
2021-02-12 02:17:59 +00:00
|
|
|
/* TODO: add the ip4 element to the list */
|
|
|
|
break;
|
2021-03-23 14:22:16 +00:00
|
|
|
case GPRS_SNS_ST_BSS_CONFIG_BSS:
|
|
|
|
case GPRS_SNS_ST_BSS_CONFIG_SGSN:
|
2021-02-12 02:17:59 +00:00
|
|
|
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;
|
2021-03-23 14:22:16 +00:00
|
|
|
case GPRS_SNS_ST_BSS_SIZE:
|
2021-02-12 02:17:59 +00:00
|
|
|
/* 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;
|
2021-03-23 14:22:16 +00:00
|
|
|
case GPRS_SNS_ST_BSS_CONFIG_BSS:
|
|
|
|
case GPRS_SNS_ST_BSS_CONFIG_SGSN:
|
2021-02-12 02:17:59 +00:00
|
|
|
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;
|
2020-12-07 06:37:07 +00:00
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-06-05 18:04:04 +00:00
|
|
|
/* validate the bss configuration (sns endpoint and binds)
|
|
|
|
* - no endpoints -> invalid
|
|
|
|
* - no binds -> invalid
|
|
|
|
* - only v4 sns endpoints, only v6 binds -> invalid
|
|
|
|
* - only v4 sns endpoints, but v4 sig weights == 0 -> invalid ...
|
|
|
|
*/
|
|
|
|
static int ns2_sns_bss_valid_configuration(struct ns2_sns_state *gss)
|
|
|
|
{
|
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
struct sns_endpoint *endpoint;
|
|
|
|
const struct osmo_sockaddr *addr;
|
|
|
|
int v4_sig = 0, v4_data = 0, v6_sig = 0, v6_data = 0;
|
|
|
|
bool v4_endpoints = false;
|
|
|
|
bool v6_endpoints = false;
|
|
|
|
|
|
|
|
if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
if (!addr)
|
|
|
|
continue;
|
|
|
|
switch (addr->u.sa.sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
v4_sig += sbind->bind->sns_sig_weight;
|
|
|
|
v4_data += sbind->bind->sns_data_weight;
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
v6_sig += sbind->bind->sns_sig_weight;
|
|
|
|
v6_data += sbind->bind->sns_data_weight;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_for_each_entry(endpoint, &gss->sns_endpoints, list) {
|
|
|
|
switch (endpoint->saddr.u.sa.sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
v4_endpoints = true;
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
v6_endpoints = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (v4_endpoints && v4_sig && v4_data) || (v6_endpoints && v6_sig && v6_data);
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:48:31 +00:00
|
|
|
/* allstate-action for BSS role */
|
|
|
|
static void ns2_sns_st_all_action_bss(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_REQ_NO_NSVC */
|
|
|
|
switch (event) {
|
|
|
|
case GPRS_SNS_EV_REQ_NO_NSVC:
|
|
|
|
/* ignore reselection running */
|
|
|
|
if (gss->reselection_running)
|
|
|
|
break;
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: no remaining NSVC, resetting SNS FSM\n", nse->nsei);
|
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
|
|
|
break;
|
|
|
|
case GPRS_SNS_EV_REQ_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);
|
2021-06-05 23:57:52 +00:00
|
|
|
ns2_clear_elems(&gss->local);
|
|
|
|
ns2_clear_elems(&gss->remote);
|
2021-03-02 19:48:31 +00:00
|
|
|
|
|
|
|
/* Choose the next sns endpoint. */
|
2021-06-05 18:04:04 +00:00
|
|
|
if (!ns2_sns_bss_valid_configuration(gss)) {
|
2021-03-02 19:48:31 +00:00
|
|
|
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);
|
|
|
|
} 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;
|
2021-03-23 14:22:16 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
|
2021-03-02 19:48:31 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ns2_sns_st_all_action(fi, event, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
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),
|
2021-02-15 01:37:03 +00:00
|
|
|
.allstate_event_mask = S(GPRS_SNS_EV_REQ_NO_NSVC) |
|
|
|
|
S(GPRS_SNS_EV_REQ_SELECT_ENDPOINT) |
|
2021-02-12 02:17:59 +00:00
|
|
|
S(GPRS_SNS_EV_REQ_ADD_BIND) |
|
|
|
|
S(GPRS_SNS_EV_REQ_DELETE_BIND),
|
2021-03-02 19:48:31 +00:00
|
|
|
.allstate_action = ns2_sns_st_all_action_bss,
|
2020-07-12 11:45:50 +00:00
|
|
|
.cleanup = NULL,
|
|
|
|
.timer_cb = ns2_sns_fsm_bss_timer_cb,
|
|
|
|
.event_names = gprs_sns_event_names,
|
|
|
|
.pre_term = NULL,
|
|
|
|
.log_subsys = DLNS,
|
|
|
|
};
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Allocate an IP-SNS FSM for the BSS side.
|
|
|
|
* \param[in] nse NS Entity in which the FSM runs
|
|
|
|
* \param[in] id string identifier
|
2021-02-15 04:05:15 +00:00
|
|
|
* \returns FSM instance on success; NULL on error */
|
2020-07-12 11:45:50 +00:00
|
|
|
struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
|
|
|
|
const char *id)
|
|
|
|
{
|
|
|
|
struct osmo_fsm_inst *fi;
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
|
|
|
|
fi = osmo_fsm_inst_alloc(&gprs_ns2_sns_bss_fsm, nse, NULL, LOGL_DEBUG, id);
|
|
|
|
if (!fi)
|
|
|
|
return fi;
|
|
|
|
|
|
|
|
gss = talloc_zero(fi, struct ns2_sns_state);
|
|
|
|
if (!gss)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
fi->priv = gss;
|
|
|
|
gss->nse = nse;
|
2021-03-02 19:49:10 +00:00
|
|
|
gss->role = GPRS_SNS_ROLE_BSS;
|
2021-03-04 17:02:54 +00:00
|
|
|
/* The SGSN doesn't tell the BSS, so we assume there's always sufficient */
|
|
|
|
gss->num_max_ip4_remote = 8192;
|
|
|
|
gss->num_max_ip6_remote = 8192;
|
2020-12-07 06:37:07 +00:00
|
|
|
INIT_LLIST_HEAD(&gss->sns_endpoints);
|
2021-02-12 02:17:59 +00:00
|
|
|
INIT_LLIST_HEAD(&gss->binds);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
return fi;
|
|
|
|
err:
|
|
|
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! 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
|
|
|
|
* \param[in] tp parsed TLV structure of message
|
2021-02-15 04:05:15 +00:00
|
|
|
* \returns 0 on success; negative on error */
|
2021-01-25 15:09:23 +00:00
|
|
|
int ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct gprs_ns2_nse *nse = nsvc->nse;
|
|
|
|
struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
uint16_t nsei = nsvc->nse->nsei;
|
2021-03-02 19:49:10 +00:00
|
|
|
struct ns2_sns_state *gss;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct osmo_fsm_inst *fi;
|
2021-03-24 16:44:03 +00:00
|
|
|
int rc = 0;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
if (!nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_NOTICE, "Rx %s for NS Instance that has no SNS!\n",
|
|
|
|
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
|
2021-03-24 16:44:03 +00:00
|
|
|
rc = -EINVAL;
|
|
|
|
goto out;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: how to resolve SNS FSM Instance by NSEI (SGSN)? */
|
|
|
|
fi = nse->bss_sns_fi;
|
2021-03-02 19:49:10 +00:00
|
|
|
gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
if (!gss->sns_nsvc)
|
|
|
|
gss->sns_nsvc = nsvc;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGPFSML(fi, LOGL_DEBUG, "NSEI=%u Rx SNS PDU type %s\n", nsei,
|
|
|
|
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
switch (nsh->pdu_type) {
|
|
|
|
case SNS_PDUT_SIZE:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_SIZE, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_SIZE_ACK:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_SIZE_ACK, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_CONFIG:
|
|
|
|
if (nsh->data[0] & 0x01)
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG_END, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
else
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_CONFIG_ACK:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CONFIG_ACK, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_ADD:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_ADD, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_DELETE:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_DELETE, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_CHANGE_WEIGHT:
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_CHANGE_WEIGHT, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_ACK:
|
2021-03-02 19:48:54 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, GPRS_SNS_EV_RX_ACK, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown SNS PDU type %s\n", nsei,
|
|
|
|
get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
|
2021-03-24 16:44:03 +00:00
|
|
|
rc = -EINVAL;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 16:44:03 +00:00
|
|
|
out:
|
|
|
|
msgb_free(msg);
|
|
|
|
|
|
|
|
return rc;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#include <osmocom/vty/vty.h>
|
|
|
|
#include <osmocom/vty/misc.h>
|
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
static void vty_dump_sns_ip4(struct vty *vty, const char *prefix, const struct gprs_ns_ie_ip4_elem *ip4)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct in_addr in = { .s_addr = ip4->ip_addr };
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%s %s:%u, Signalling Weight: %u, Data Weight: %u%s", prefix,
|
2020-07-12 11:45:50 +00:00
|
|
|
inet_ntoa(in), ntohs(ip4->udp_port), ip4->sig_weight, ip4->data_weight, VTY_NEWLINE);
|
|
|
|
}
|
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
static void vty_dump_sns_ip6(struct vty *vty, const char *prefix, const struct gprs_ns_ie_ip6_elem *ip6)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
char ip_addr[INET6_ADDRSTRLEN] = {};
|
|
|
|
if (!inet_ntop(AF_INET6, &ip6->ip_addr, ip_addr, (INET6_ADDRSTRLEN)))
|
|
|
|
strcpy(ip_addr, "Invalid IPv6");
|
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%s %s:%u, Signalling Weight: %u, Data Weight: %u%s", prefix,
|
2020-07-12 11:45:50 +00:00
|
|
|
ip_addr, ntohs(ip6->udp_port), ip6->sig_weight, ip6->data_weight, VTY_NEWLINE);
|
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Dump the IP-SNS state to a vty.
|
|
|
|
* \param[in] vty VTY to which the state shall be printed
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
* \param[in] prefix prefix to print at start of each line (typically indenting)
|
2020-09-18 20:33:24 +00:00
|
|
|
* \param[in] nse NS Entity whose IP-SNS state shall be printed
|
|
|
|
* \param[in] stats Whether or not statistics shall also be printed */
|
2021-01-25 15:09:23 +00:00
|
|
|
void ns2_sns_dump_vty(struct vty *vty, const char *prefix, const struct gprs_ns2_nse *nse, bool stats)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!nse->bss_sns_fi)
|
|
|
|
return;
|
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out_fsm_inst2(vty, prefix, nse->bss_sns_fi);
|
2020-07-12 11:45:50 +00:00
|
|
|
gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
|
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%sMaximum number of remote NS-VCs: %zu, IPv4 Endpoints: %zu, IPv6 Endpoints: %zu%s",
|
|
|
|
prefix, gss->num_max_nsvcs, gss->num_max_ip4_remote, gss->num_max_ip6_remote, VTY_NEWLINE);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
if (gss->local.num_ip4 && gss->remote.num_ip4) {
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%sLocal IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->local.num_ip4; i++)
|
|
|
|
vty_dump_sns_ip4(vty, prefix, &gss->local.ip4[i]);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%sRemote IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->remote.num_ip4; i++)
|
|
|
|
vty_dump_sns_ip4(vty, prefix, &gss->remote.ip4[i]);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
if (gss->local.num_ip6 && gss->remote.num_ip6) {
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%sLocal IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->local.num_ip6; i++)
|
|
|
|
vty_dump_sns_ip6(vty, prefix, &gss->local.ip6[i]);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
ns2: Properly indent VTY output
If multiple objects are printed in the VTY, only the first line of each
object should be on the first character of the line, all others should
be indented. With this patch the "snow ns entities" output becomes
much more readable:
OsmoGbProxy> show ns entities
NSEI 00102: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00102-SNS)[0x6120000018a0]', ID: 'NSE00102-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.11]:8888
NSEI 00101: UDP, DEAD
FSM Instance Name: 'GPRS-NS2-SNS-BSS(NSE00101-SNS)[0x6120000015a0]', ID: 'NSE00101-SNS'
Log-Level: 'DEBUG', State: 'SIZE'
Timer: 1
Maximum number of remote NS-VCs: 8, IPv4 Endpoints: 4, IPv6 Endpoints: 0
NSVCI none: UNCONFIGURED DYNAMIC data_weight=1 sig_weight=1 udp)[127.0.0.1]:23000<>[127.0.0.10]:7777
Change-Id: Id1b4c80a6caef410076a68b4301adaa01ba7e57a
2021-01-19 19:58:33 +00:00
|
|
|
vty_out(vty, "%sRemote IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
|
2021-06-05 16:44:01 +00:00
|
|
|
for (i = 0; i < gss->remote.num_ip6; i++)
|
|
|
|
vty_dump_sns_ip6(vty, prefix, &gss->remote.ip6[i]);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 04:24:37 +00:00
|
|
|
/*! write IP-SNS to a vty
|
|
|
|
* \param[in] vty VTY to which the state shall be printed
|
|
|
|
* \param[in] nse NS Entity whose IP-SNS state shall be printed */
|
2021-01-25 15:09:23 +00:00
|
|
|
void ns2_sns_write_vty(struct vty *vty, const struct gprs_ns2_nse *nse)
|
2020-11-19 04:24:37 +00:00
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
struct osmo_sockaddr_str addr_str;
|
|
|
|
struct sns_endpoint *endpoint;
|
|
|
|
|
|
|
|
if (!nse->bss_sns_fi)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
|
|
|
|
llist_for_each_entry(endpoint, &gss->sns_endpoints, list) {
|
2021-01-05 13:24:09 +00:00
|
|
|
/* It's unlikely that an error happens, but let's better be safe. */
|
|
|
|
if (osmo_sockaddr_str_from_sockaddr(&addr_str, &endpoint->saddr.u.sas) != 0)
|
|
|
|
addr_str = (struct osmo_sockaddr_str) { .ip = "<INVALID>" };
|
2021-02-08 22:21:54 +00:00
|
|
|
vty_out(vty, " ip-sns-remote %s %u%s", addr_str.ip, addr_str.port, VTY_NEWLINE);
|
2020-11-19 04:24:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:23:29 +00:00
|
|
|
if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
|
2020-12-07 06:37:07 +00:00
|
|
|
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)
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
2020-12-07 06:37:07 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:23:29 +00:00
|
|
|
if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
|
2020-12-07 06:37:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-15 01:37:03 +00:00
|
|
|
/* gprs_ns2_free_nsvcs() will trigger GPRS_SNS_EV_REQ_NO_NSVC on the last NS-VC
|
2020-12-07 06:37:07 +00:00
|
|
|
* and restart SNS SIZE procedure which selects a new initial */
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSE(nse, LOGL_INFO, "Current in-use SNS endpoint is being removed."
|
2020-12-07 06:37:07 +00:00
|
|
|
"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;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:23:29 +00:00
|
|
|
if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
|
2020-12-07 06:37:07 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
gss = nse->bss_sns_fi->priv;
|
|
|
|
llist_for_each_entry(endpoint, &gss->sns_endpoints, list)
|
|
|
|
count++;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2021-02-03 17:25:27 +00:00
|
|
|
void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bool alive)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
struct gprs_ns2_vc *tmp;
|
|
|
|
|
|
|
|
if (!nse->bss_sns_fi)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gss = nse->bss_sns_fi->priv;
|
|
|
|
if(nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (alive == gss->alive)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* check if this is the current SNS NS-VC */
|
|
|
|
if (nsvc == gss->sns_nsvc) {
|
|
|
|
/* only replace the SNS NS-VC if there are other alive NS-VC.
|
|
|
|
* There aren't any other alive NS-VC when the SNS fsm just reached CONFIGURED
|
|
|
|
* and couldn't confirm yet if the NS-VC comes up */
|
|
|
|
if (gss->alive && !alive)
|
|
|
|
ns2_sns_replace_nsvc(nsvc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alive) {
|
|
|
|
gss->alive = true;
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_NSVC_ALIVE, NULL);
|
2021-02-03 17:25:27 +00:00
|
|
|
} else {
|
|
|
|
/* is there at least another alive nsvc? */
|
|
|
|
llist_for_each_entry(tmp, &nse->nsvc, list) {
|
|
|
|
if (ns2_vc_is_unblocked(tmp))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* all NS-VC have failed */
|
|
|
|
gss->alive = false;
|
2021-02-15 01:37:03 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_NO_NSVC, NULL);
|
2021-02-03 17:25:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 02:17:59 +00:00
|
|
|
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;
|
2021-04-19 01:30:15 +00:00
|
|
|
break;
|
2021-02-12 02:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, GPRS_SNS_EV_REQ_DELETE_BIND, tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-06-05 16:44:01 +00:00
|
|
|
/* Update SNS weights for a bind (local endpoint).
|
|
|
|
* \param[in] bind the bind which has been updated
|
2021-02-08 22:13:12 +00:00
|
|
|
*/
|
|
|
|
void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
|
|
|
|
{
|
|
|
|
/* TODO: implement weights after binds per sns implemented */
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:49:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SGSN role
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
static void ns2_sns_st_sgsn_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
|
|
|
|
/* do nothing; Rx SNS-SIZE handled in ns2_sns_st_all_action_sgsn() */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We're waiting for inbound SNS-CONFIG from the BSS */
|
|
|
|
static void ns2_sns_st_sgsn_wait_config(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);
|
|
|
|
struct gprs_ns2_inst *nsi = nse->nsi;
|
|
|
|
uint8_t cause;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case GPRS_SNS_EV_RX_CONFIG:
|
|
|
|
case GPRS_SNS_EV_RX_CONFIG_END:
|
|
|
|
rc = ns_sns_append_remote_eps(fi, data);
|
|
|
|
if (rc < 0) {
|
|
|
|
cause = -rc;
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* only change state if last CONFIG was received */
|
|
|
|
if (event == GPRS_SNS_EV_RX_CONFIG_END) {
|
|
|
|
/* ensure sum of data weight / sig weights is > 0 */
|
|
|
|
if (nss_weight_sum_data(gss) == 0 || nss_weight_sum_sig(gss) == 0) {
|
|
|
|
cause = NS_CAUSE_INVAL_WEIGH;
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
|
|
|
|
} else {
|
|
|
|
/* just send CONFIG-ACK */
|
|
|
|
ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
|
|
|
|
osmo_timer_schedule(&fi->timer, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_sgsn_wait_config_ack_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
|
|
|
|
|
|
|
|
/* transmit SGSN-oriented SNS-CONFIG */
|
2021-06-05 16:44:01 +00:00
|
|
|
ns2_tx_sns_config(gss->sns_nsvc, true, gss->local.ip4, gss->local.num_ip4,
|
|
|
|
gss->local.ip6, gss->local.num_ip6);
|
2021-03-02 19:49:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We're waiting for SNS-CONFIG-ACK from the BSS (in response to our outbound SNS-CONFIG) */
|
|
|
|
static void ns2_sns_st_sgsn_wait_config_ack(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);
|
|
|
|
struct tlv_parsed *tp = NULL;
|
|
|
|
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case GPRS_SNS_EV_RX_CONFIG_ACK:
|
|
|
|
tp = data;
|
|
|
|
if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "Rx SNS-CONFIG-ACK with cause %s\n",
|
|
|
|
gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* we currently only send one SNS-CONFIG with END FLAG */
|
|
|
|
if (true) {
|
|
|
|
create_missing_nsvcs(fi);
|
|
|
|
/* start the test procedure on ALL NSVCs! */
|
|
|
|
gprs_ns2_start_alive_all_nsvcs(nse);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, ns_sns_configured_timeout(fi), 4);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* SGSN-side SNS state machine */
|
|
|
|
static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
|
|
|
|
[GPRS_SNS_ST_UNCONFIGURED] = {
|
|
|
|
.in_event_mask = 0, /* handled by all_state_action */
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG),
|
|
|
|
.name = "UNCONFIGURED",
|
|
|
|
.action = ns2_sns_st_sgsn_unconfigured,
|
|
|
|
},
|
|
|
|
[GPRS_SNS_ST_SGSN_WAIT_CONFIG] = {
|
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_CONFIG) |
|
|
|
|
S(GPRS_SNS_EV_RX_CONFIG_END),
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
|
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK),
|
|
|
|
.name = "SGSN_WAIT_CONFIG",
|
|
|
|
.action = ns2_sns_st_sgsn_wait_config,
|
|
|
|
},
|
|
|
|
[GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK] = {
|
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_CONFIG_ACK),
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK) |
|
|
|
|
S(GPRS_SNS_ST_CONFIGURED),
|
|
|
|
.name = "SGSN_WAIT_CONFIG_ACK",
|
|
|
|
.action = ns2_sns_st_sgsn_wait_config_ack,
|
|
|
|
.onenter = ns2_sns_st_sgsn_wait_config_ack_onenter,
|
|
|
|
},
|
|
|
|
[GPRS_SNS_ST_CONFIGURED] = {
|
|
|
|
.in_event_mask = S(GPRS_SNS_EV_RX_ADD) |
|
|
|
|
S(GPRS_SNS_EV_RX_DELETE) |
|
|
|
|
S(GPRS_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(GPRS_SNS_EV_REQ_NSVC_ALIVE),
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED),
|
|
|
|
.name = "CONFIGURED",
|
|
|
|
/* shared with BSS side; once configured there's no difference */
|
|
|
|
.action = ns2_sns_st_configured,
|
|
|
|
.onenter = ns2_sns_st_configured_onenter,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
|
|
|
|
struct gprs_ns2_inst *nsi = nse->nsi;
|
|
|
|
|
|
|
|
gss->N++;
|
|
|
|
switch (fi->T) {
|
|
|
|
case 3:
|
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: SGSN Config retries failed. Giving up.\n", nse->nsei);
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
|
|
|
|
} else {
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* allstate-action for SGSN role */
|
|
|
|
static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
struct tlv_parsed *tp = NULL;
|
2021-03-04 18:49:38 +00:00
|
|
|
size_t num_local_eps, num_remote_eps;
|
2021-03-02 19:49:10 +00:00
|
|
|
uint8_t flag;
|
2021-03-04 16:59:35 +00:00
|
|
|
uint8_t cause;
|
2021-03-02 19:49:10 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case GPRS_SNS_EV_RX_SIZE:
|
|
|
|
tp = (struct tlv_parsed *) data;
|
2021-03-04 16:59:35 +00:00
|
|
|
/* check for mandatory / conditional IEs */
|
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_RESET_FLAG, 1) ||
|
|
|
|
!TLVP_PRES_LEN(tp, NS_IE_MAX_NR_NSVC, 2)) {
|
|
|
|
cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2) &&
|
|
|
|
!TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2)) {
|
|
|
|
cause = NS_CAUSE_MISSING_ESSENT_IE;
|
2021-03-02 19:49:10 +00:00
|
|
|
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
|
|
|
|
break;
|
|
|
|
}
|
2021-03-04 18:49:38 +00:00
|
|
|
if (TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2))
|
|
|
|
gss->num_max_ip4_remote = tlvp_val16be(tp, NS_IE_IPv4_EP_NR);
|
|
|
|
if (TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2))
|
|
|
|
gss->num_max_ip6_remote = tlvp_val16be(tp, NS_IE_IPv6_EP_NR);
|
|
|
|
/* decide if we go for IPv4 or IPv6 */
|
|
|
|
if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, IPv6)) {
|
|
|
|
gss->ip = IPv6;
|
2021-03-24 00:57:30 +00:00
|
|
|
ns2_sns_compute_local_ep_from_binds(fi);
|
2021-06-05 16:44:01 +00:00
|
|
|
num_local_eps = gss->local.num_ip6;
|
2021-03-04 18:49:38 +00:00
|
|
|
num_remote_eps = gss->num_max_ip6_remote;
|
|
|
|
} else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, IPv4)) {
|
|
|
|
gss->ip = IPv4;
|
2021-03-24 00:57:30 +00:00
|
|
|
ns2_sns_compute_local_ep_from_binds(fi);
|
2021-06-05 16:44:01 +00:00
|
|
|
num_local_eps = gss->local.num_ip4;
|
2021-03-04 18:49:38 +00:00
|
|
|
num_remote_eps = gss->num_max_ip4_remote;
|
|
|
|
} else {
|
2021-06-05 16:44:01 +00:00
|
|
|
if (gss->local.num_ip4 && !gss->num_max_ip4_remote)
|
2021-03-04 18:49:38 +00:00
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv4_EP;
|
|
|
|
else
|
|
|
|
cause = NS_CAUSE_INVAL_NR_IPv6_EP;
|
|
|
|
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* ensure number of NS-VCs is sufficient for full mesh */
|
|
|
|
gss->num_max_nsvcs = tlvp_val16be(tp, NS_IE_MAX_NR_NSVC);
|
|
|
|
if (gss->num_max_nsvcs < num_remote_eps * num_local_eps) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "%zu local and %zu remote EPs, requires %zu NS-VC, "
|
|
|
|
"but BSS supports only %zu maximum NS-VCs\n", num_local_eps,
|
|
|
|
num_remote_eps, num_local_eps * num_remote_eps, gss->num_max_nsvcs);
|
|
|
|
cause = NS_CAUSE_INVAL_NR_NS_VC;
|
|
|
|
ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* perform state reset, if requested */
|
2021-03-02 19:49:10 +00:00
|
|
|
flag = *TLVP_VAL(tp, NS_IE_RESET_FLAG);
|
|
|
|
if (flag & 1) {
|
|
|
|
struct gprs_ns2_vc *nsvc, *nsvc2;
|
|
|
|
/* clear all state */
|
2021-03-04 16:49:59 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
|
2021-03-02 19:49:10 +00:00
|
|
|
gss->N = 0;
|
2021-06-05 23:57:52 +00:00
|
|
|
ns2_clear_elems(&gss->local);
|
|
|
|
ns2_clear_elems(&gss->remote);
|
2021-03-02 19:49:10 +00:00
|
|
|
llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
|
|
|
|
if (nsvc == gss->sns_nsvc) {
|
|
|
|
/* keep the NSVC we need for SNS, but unconfigure it */
|
|
|
|
nsvc->sig_weight = 0;
|
|
|
|
nsvc->data_weight = 0;
|
|
|
|
ns2_vc_force_unconfigured(nsvc);
|
|
|
|
} else {
|
|
|
|
/* free all other NS-VCs */
|
|
|
|
gprs_ns2_free_nsvc(nsvc);
|
|
|
|
}
|
|
|
|
}
|
2021-03-24 00:57:30 +00:00
|
|
|
ns2_sns_compute_local_ep_from_binds(fi);
|
2021-03-02 19:49:10 +00:00
|
|
|
}
|
|
|
|
/* send SIZE_ACK */
|
|
|
|
ns2_tx_sns_size_ack(gss->sns_nsvc, NULL);
|
|
|
|
/* only wait for SNS-CONFIG in case of Reset flag */
|
|
|
|
if (flag & 1)
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG, 0, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ns2_sns_st_all_action(fi, event, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct osmo_fsm gprs_ns2_sns_sgsn_fsm = {
|
|
|
|
.name = "GPRS-NS2-SNS-SGSN",
|
|
|
|
.states = ns2_sns_sgsn_states,
|
|
|
|
.num_states = ARRAY_SIZE(ns2_sns_sgsn_states),
|
|
|
|
.allstate_event_mask = S(GPRS_SNS_EV_RX_SIZE) |
|
|
|
|
S(GPRS_SNS_EV_REQ_NO_NSVC) |
|
|
|
|
S(GPRS_SNS_EV_REQ_ADD_BIND) |
|
|
|
|
S(GPRS_SNS_EV_REQ_DELETE_BIND),
|
|
|
|
.allstate_action = ns2_sns_st_all_action_sgsn,
|
|
|
|
.cleanup = NULL,
|
|
|
|
.timer_cb = ns2_sns_fsm_sgsn_timer_cb,
|
|
|
|
.event_names = gprs_sns_event_names,
|
|
|
|
.pre_term = NULL,
|
|
|
|
.log_subsys = DLNS,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*! Allocate an IP-SNS FSM for the SGSN side.
|
|
|
|
* \param[in] nse NS Entity in which the FSM runs
|
|
|
|
* \param[in] id string identifier
|
|
|
|
* \returns FSM instance on success; NULL on error */
|
|
|
|
struct osmo_fsm_inst *ns2_sns_sgsn_fsm_alloc(struct gprs_ns2_nse *nse, const char *id)
|
|
|
|
{
|
|
|
|
struct osmo_fsm_inst *fi;
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
|
|
|
|
fi = osmo_fsm_inst_alloc(&gprs_ns2_sns_sgsn_fsm, nse, NULL, LOGL_DEBUG, id);
|
|
|
|
if (!fi)
|
|
|
|
return fi;
|
|
|
|
|
|
|
|
gss = talloc_zero(fi, struct ns2_sns_state);
|
|
|
|
if (!gss)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
fi->priv = gss;
|
|
|
|
gss->nse = nse;
|
|
|
|
gss->role = GPRS_SNS_ROLE_SGSN;
|
|
|
|
INIT_LLIST_HEAD(&gss->sns_endpoints);
|
|
|
|
INIT_LLIST_HEAD(&gss->binds);
|
|
|
|
|
|
|
|
return fi;
|
|
|
|
err:
|
|
|
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
/* initialize osmo_ctx on main tread */
|
|
|
|
static __attribute__((constructor)) void on_dso_load_ctx(void)
|
|
|
|
{
|
|
|
|
OSMO_ASSERT(osmo_fsm_register(&gprs_ns2_sns_bss_fsm) == 0);
|
2021-03-02 19:49:10 +00:00
|
|
|
OSMO_ASSERT(osmo_fsm_register(&gprs_ns2_sns_sgsn_fsm) == 0);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|