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))
|
|
|
|
|
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 */
|
2021-06-05 20:08:11 +00:00
|
|
|
GPRS_SNS_ST_LOCAL_PROCEDURE, /*!< in process of a ADD/DEL/CHANGE procedure towards SGSN (BSS->SGSN) */
|
2020-07-12 11:45:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct value_string gprs_sns_event_names[] = {
|
2021-07-20 16:41:14 +00:00
|
|
|
{ NS2_SNS_EV_REQ_SELECT_ENDPOINT, "REQ_SELECT_ENDPOINT" },
|
|
|
|
{ NS2_SNS_EV_RX_SIZE, "RX_SIZE" },
|
|
|
|
{ NS2_SNS_EV_RX_SIZE_ACK, "RX_SIZE_ACK" },
|
|
|
|
{ NS2_SNS_EV_RX_CONFIG, "RX_CONFIG" },
|
|
|
|
{ NS2_SNS_EV_RX_CONFIG_END, "RX_CONFIG_END" },
|
|
|
|
{ NS2_SNS_EV_RX_CONFIG_ACK, "RX_CONFIG_ACK" },
|
|
|
|
{ NS2_SNS_EV_RX_ADD, "RX_ADD" },
|
|
|
|
{ NS2_SNS_EV_RX_DELETE, "RX_DELETE" },
|
|
|
|
{ NS2_SNS_EV_RX_ACK, "RX_ACK" },
|
|
|
|
{ NS2_SNS_EV_RX_CHANGE_WEIGHT, "RX_CHANGE_WEIGHT" },
|
|
|
|
{ NS2_SNS_EV_REQ_NO_NSVC, "REQ_NO_NSVC" },
|
2021-08-06 17:50:09 +00:00
|
|
|
{ NS2_SNS_EV_REQ_FREE_NSVCS, "REQ_FREE_NSVCS" },
|
2021-07-20 16:41:14 +00:00
|
|
|
{ NS2_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
|
|
|
|
{ NS2_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
|
|
|
|
{ NS2_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
|
2021-06-05 20:08:11 +00:00
|
|
|
{ NS2_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_CHANGE_WEIGHT"},
|
2020-07-12 11:45:50 +00:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
#define GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER (void *) 1
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
enum sns_procedure {
|
|
|
|
SNS_PROC_NONE, /*!< used as invalid/idle value */
|
|
|
|
SNS_PROC_ADD,
|
|
|
|
SNS_PROC_DEL,
|
|
|
|
SNS_PROC_CHANGE_WEIGHT,
|
|
|
|
};
|
|
|
|
|
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 20:08:11 +00:00
|
|
|
uint8_t change_weight_state;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ns2_sns_procedure {
|
|
|
|
struct llist_head list;
|
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
uint16_t sig_weight;
|
|
|
|
uint16_t data_weight;
|
|
|
|
/* copy entry to protect against changes of gss->local */
|
|
|
|
struct gprs_ns_ie_ip4_elem ip4;
|
|
|
|
struct gprs_ns_ie_ip6_elem ip6;
|
|
|
|
enum sns_procedure procedure;
|
|
|
|
uint8_t trans_id;
|
|
|
|
/* is the procedure in process */
|
|
|
|
bool running;
|
2021-02-12 02:17:59 +00:00
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
2021-06-05 23:32:45 +00:00
|
|
|
/* containing the address family AF_* */
|
|
|
|
int family;
|
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;
|
|
|
|
|
2021-06-06 16:58:01 +00:00
|
|
|
/* protection against recursive free() */
|
|
|
|
bool block_no_nsvc_events;
|
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
/* 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 20:08:11 +00:00
|
|
|
/* local configuration after all local procedures applied */
|
|
|
|
struct ns2_sns_elems local_procedure;
|
|
|
|
|
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;
|
2021-06-05 20:08:11 +00:00
|
|
|
|
|
|
|
struct llist_head procedures;
|
|
|
|
struct ns2_sns_procedure *current_procedure;
|
|
|
|
uint8_t trans_id;
|
2020-07-12 11:45:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-06-12 21:09:46 +00:00
|
|
|
/* The SNS has failed. Etither restart the SNS (BSS) or remove the SNS (SGSN) */
|
|
|
|
#define sns_failed(fi, reason) \
|
|
|
|
_sns_failed(fi, reason, __FILE__, __LINE__)
|
|
|
|
static void _sns_failed(struct osmo_fsm_inst *fi, const char *reason, const char *file, int line)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = fi->priv;
|
|
|
|
|
|
|
|
if (reason)
|
2021-07-20 20:15:00 +00:00
|
|
|
LOGPFSMLSRC(fi, LOGL_ERROR, file, line, "NSE %d: SNS failed: %s\n", gss->nse->nsei, reason);
|
2021-06-12 21:09:46 +00:00
|
|
|
|
2021-07-20 20:16:09 +00:00
|
|
|
gss->alive = false;
|
2021-06-12 21:09:46 +00:00
|
|
|
if (gss->role == GPRS_SNS_ROLE_SGSN) {
|
|
|
|
if (!gss->nse->persistent)
|
|
|
|
gprs_ns2_free_nse(gss->nse);
|
|
|
|
else
|
|
|
|
_osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0, file, line);
|
|
|
|
} else {
|
2021-07-20 16:41:14 +00:00
|
|
|
_osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL, file, line);
|
2021-06-12 21:09:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 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-06-06 00:48:18 +00:00
|
|
|
static int ip46_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
|
2021-03-02 19:47:29 +00:00
|
|
|
{
|
2021-06-06 00:48:18 +00:00
|
|
|
return ip4_weight_sum(elems, data_weight) +
|
|
|
|
ip6_weight_sum(elems, data_weight);
|
2021-03-02 19:47:29 +00:00
|
|
|
}
|
2021-06-06 00:48:18 +00:00
|
|
|
#define ip46_weight_sum_data(elems) ip46_weight_sum(elems, true)
|
|
|
|
#define ip46_weight_sum_sig(elems) ip46_weight_sum(elems, false)
|
2021-03-02 19:47:29 +00:00
|
|
|
|
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-06-06 16:58:01 +00:00
|
|
|
if (gss->block_no_nsvc_events)
|
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_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-06-05 20:08:11 +00:00
|
|
|
static void ns2_clear_procedures(struct ns2_sns_state *gss)
|
|
|
|
{
|
|
|
|
struct ns2_sns_procedure *procedure, *tmp;
|
|
|
|
gss->current_procedure = NULL;
|
|
|
|
llist_for_each_entry_safe(procedure, tmp, &gss->procedures, list) {
|
|
|
|
llist_del(&procedure->list);
|
|
|
|
talloc_free(procedure);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-06-06 16:58:01 +00:00
|
|
|
static int remove_bind_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems, struct ns2_sns_bind *sbind)
|
|
|
|
{
|
|
|
|
struct gprs_ns_ie_ip4_elem ip4;
|
|
|
|
struct gprs_ns_ie_ip6_elem ip6;
|
|
|
|
const struct osmo_sockaddr *saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
|
|
|
|
switch (saddr->u.sa.sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
|
|
|
|
ip4.udp_port = saddr->u.sin.sin_port;
|
|
|
|
ip4.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip4.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
return remove_ip4_elem(gss, elems, &ip4);
|
|
|
|
case AF_INET6:
|
|
|
|
memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
|
|
|
|
ip6.udp_port = saddr->u.sin.sin_port;
|
|
|
|
ip6.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip6.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
return remove_ip6_elem(gss, elems, &ip6);
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
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". */
|
2021-06-05 23:32:45 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
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;
|
2021-06-05 23:32:45 +00:00
|
|
|
case AF_INET6:
|
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" */
|
2021-06-05 23:32:45 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
2020-07-12 11:45:50 +00:00
|
|
|
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;
|
2021-06-05 23:32:45 +00:00
|
|
|
case AF_INET6:
|
2020-07-12 11:45:50 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
case NS2_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-06-05 23:32:45 +00:00
|
|
|
static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, int ip_proto)
|
2021-03-04 18:49:38 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2021-06-05 23:32:45 +00:00
|
|
|
switch (ip_proto) {
|
|
|
|
case AF_INET:
|
2021-03-04 18:49:38 +00:00
|
|
|
if (sa->u.sas.ss_family == AF_INET)
|
|
|
|
count++;
|
|
|
|
break;
|
2021-06-05 23:32:45 +00:00
|
|
|
case AF_INET6:
|
2021-03-04 18:49:38 +00:00
|
|
|
if (sa->u.sas.ss_family == AF_INET6)
|
|
|
|
count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
static int ns2_sns_copy_local_endpoints(struct ns2_sns_state *gss)
|
|
|
|
{
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
gss->local_procedure.ip4 = talloc_realloc(gss, gss->local_procedure.ip4, struct gprs_ns_ie_ip4_elem,
|
|
|
|
gss->local.num_ip4);
|
|
|
|
if (!gss->local_procedure.ip4)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
gss->local_procedure.num_ip4 = gss->local.num_ip4;
|
|
|
|
memcpy(gss->local_procedure.ip4, gss->local.ip4,
|
|
|
|
sizeof(struct gprs_ns_ie_ip4_elem) * gss->local.num_ip4);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
gss->local_procedure.ip6 = talloc_realloc(gss, gss->local_procedure.ip6, struct gprs_ns_ie_ip6_elem,
|
|
|
|
gss->local.num_ip6);
|
|
|
|
if (!gss->local_procedure.ip6)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
gss->local_procedure.num_ip6 = gss->local.num_ip6;
|
|
|
|
memcpy(gss->local_procedure.ip6, gss->local.ip6,
|
|
|
|
sizeof(struct gprs_ns_ie_ip6_elem) * gss->local.num_ip6);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-06-05 23:32:45 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
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;
|
2021-06-05 23:32:45 +00:00
|
|
|
case AF_INET6:
|
2020-12-07 06:37:07 +00:00
|
|
|
/* 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-06-05 20:08:11 +00:00
|
|
|
|
|
|
|
ns2_sns_copy_local_endpoints(gss);
|
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;
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
ns2_clear_procedures(gss);
|
2021-03-04 12:03:27 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
case NS2_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 */
|
2021-06-05 23:32:45 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
2020-07-12 11:45:50 +00:00
|
|
|
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;
|
2021-06-05 23:32:45 +00:00
|
|
|
case AF_INET6:
|
2020-07-12 11:45:50 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_CONFIG_END:
|
|
|
|
case NS2_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-07-20 16:41:14 +00:00
|
|
|
if (event == NS2_SNS_EV_RX_CONFIG_END) {
|
2021-03-02 19:47:29 +00:00
|
|
|
/* check if sum of data / sig weights == 0 */
|
2021-06-06 00:48:18 +00:00
|
|
|
if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
|
2021-03-02 19:47:29 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
/* called when receiving NS2_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);
|
2021-06-05 23:32:45 +00:00
|
|
|
if (gss->family == AF_INET) {
|
2020-07-12 11:45:50 +00:00
|
|
|
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);
|
2021-06-05 23:32:45 +00:00
|
|
|
if (gss->family == AF_INET) {
|
2020-07-12 11:45:50 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_ADD:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_add(fi, gss, tp);
|
|
|
|
break;
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_DELETE:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_delete(fi, gss, tp);
|
|
|
|
break;
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_CHANGE_WEIGHT:
|
2020-07-12 11:45:50 +00:00
|
|
|
ns2_sns_st_configured_change(fi, gss, tp);
|
|
|
|
break;
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_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-06-05 20:08:11 +00:00
|
|
|
if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE)
|
|
|
|
ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
|
|
|
|
|
|
|
|
if (!llist_empty(&gss->procedures)) {
|
|
|
|
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
|
|
|
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
|
|
|
|
/* check if resend or not */
|
|
|
|
if (!gss->current_procedure) {
|
|
|
|
/* take next procedure */
|
|
|
|
gss->current_procedure = llist_first_entry_or_null(&gss->procedures,
|
|
|
|
struct ns2_sns_procedure, list);
|
|
|
|
if (!gss->current_procedure) {
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gss->N = 0;
|
|
|
|
gss->current_procedure->running = true;
|
|
|
|
gss->current_procedure->trans_id = ++gss->trans_id;
|
|
|
|
if (gss->trans_id == 0)
|
|
|
|
gss->trans_id = gss->current_procedure->trans_id = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* also takes care of retransmitting */
|
|
|
|
switch (gss->current_procedure->procedure) {
|
2021-06-06 16:57:56 +00:00
|
|
|
case SNS_PROC_ADD:
|
|
|
|
if (gss->family == AF_INET)
|
|
|
|
ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
|
|
|
|
else
|
|
|
|
ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
|
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
case SNS_PROC_CHANGE_WEIGHT:
|
|
|
|
if (gss->family == AF_INET)
|
|
|
|
ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
|
|
|
|
else
|
|
|
|
ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
|
|
|
|
break;
|
2021-06-06 16:58:01 +00:00
|
|
|
case SNS_PROC_DEL:
|
|
|
|
if (gss->family == AF_INET)
|
|
|
|
ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
|
|
|
|
else
|
|
|
|
ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
|
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
static void create_nsvc_for_new_sbind(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind)
|
|
|
|
{
|
|
|
|
struct gprs_ns2_nse *nse = gss->nse;
|
|
|
|
struct gprs_ns2_vc_bind *bind = sbind->bind;
|
|
|
|
struct gprs_ns2_vc *nsvc;
|
|
|
|
struct osmo_sockaddr remote = { };
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* iterate over all remote IPv4 endpoints */
|
|
|
|
for (i = 0; i < gss->remote.num_ip4; i++) {
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
|
|
|
|
|
|
|
|
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;
|
|
|
|
/* we only care about UDP binds */
|
|
|
|
if (bind->ll != GPRS_NS2_LL_UDP)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
|
|
|
|
if (!nsvc) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* iterate over all remote IPv4 endpoints */
|
|
|
|
for (i = 0; i < gss->remote.num_ip6; i++) {
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
|
|
|
|
|
|
|
|
remote.u.sin6.sin6_family = AF_INET6;
|
|
|
|
remote.u.sin6.sin6_addr = ip6->ip_addr;
|
|
|
|
remote.u.sin6.sin6_port = ip6->udp_port;
|
|
|
|
|
|
|
|
/* we only care about UDP binds */
|
|
|
|
nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
|
|
|
|
if (!nsvc) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
static void ns2_sns_st_local_procedure(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_ns_ie_ip4_elem *ip4, *proc4;
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6, *proc6;
|
|
|
|
struct tlv_parsed *tp = data;
|
|
|
|
uint8_t trans_id;
|
|
|
|
uint8_t cause;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NS2_SNS_EV_RX_ADD:
|
|
|
|
ns2_sns_st_configured_add(fi, gss, tp);
|
|
|
|
break;
|
|
|
|
case NS2_SNS_EV_RX_DELETE:
|
|
|
|
ns2_sns_st_configured_delete(fi, gss, tp);
|
|
|
|
break;
|
|
|
|
case NS2_SNS_EV_RX_CHANGE_WEIGHT:
|
|
|
|
ns2_sns_st_configured_change(fi, gss, tp);
|
|
|
|
break;
|
|
|
|
case NS2_SNS_EV_RX_ACK:
|
|
|
|
/* presence of trans_id is already checked here */
|
|
|
|
trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);
|
|
|
|
if (trans_id != gss->current_procedure->trans_id) {
|
|
|
|
LOGPFSML(fi, LOGL_INFO, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",
|
|
|
|
nse->nsei, trans_id, gss->current_procedure->trans_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TLVP_PRESENT(tp, NS_IE_CAUSE)) {
|
|
|
|
/* what happend on error cause? return to size? */
|
|
|
|
cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK trans %d with cause code %d.\n",
|
|
|
|
nse->nsei, trans_id, cause);
|
|
|
|
sns_failed(fi, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (gss->current_procedure->procedure) {
|
2021-06-06 16:57:56 +00:00
|
|
|
case SNS_PROC_ADD:
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
add_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
add_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
|
|
|
|
break;
|
|
|
|
}
|
2021-06-06 16:58:01 +00:00
|
|
|
/* the sbind can be NULL if the bind has been released by del_bind */
|
|
|
|
if (gss->current_procedure->sbind) {
|
|
|
|
create_nsvc_for_new_sbind(gss, gss->current_procedure->sbind);
|
|
|
|
gprs_ns2_start_alive_all_nsvcs(nse);
|
|
|
|
}
|
2021-06-06 16:57:56 +00:00
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
case SNS_PROC_CHANGE_WEIGHT:
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
proc4 = &gss->current_procedure->ip4;
|
|
|
|
for (unsigned int i=0; i<gss->local.num_ip4; i++) {
|
|
|
|
ip4 = &gss->local.ip4[i];
|
|
|
|
if (ip4->ip_addr != proc4->ip_addr ||
|
|
|
|
ip4->udp_port != proc4->udp_port)
|
|
|
|
continue;
|
|
|
|
ip4->sig_weight = proc4->sig_weight;
|
|
|
|
ip4->data_weight = proc4->data_weight;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
proc6 = &gss->current_procedure->ip6;
|
|
|
|
for (unsigned int i=0; i<gss->local.num_ip6; i++) {
|
|
|
|
ip6 = &gss->local.ip6[i];
|
|
|
|
if (memcmp(&ip6->ip_addr, &proc6->ip_addr, sizeof(proc6->ip_addr)) ||
|
|
|
|
ip6->udp_port != proc6->udp_port) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ip6->sig_weight = proc6->sig_weight;
|
|
|
|
ip6->data_weight = proc6->data_weight;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
break;
|
2021-06-06 16:58:01 +00:00
|
|
|
case SNS_PROC_DEL:
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
remove_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
remove_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_del(&gss->current_procedure->list);
|
|
|
|
talloc_free(gss->current_procedure);
|
|
|
|
gss->current_procedure = NULL;
|
|
|
|
|
|
|
|
if (llist_empty(&gss->procedures))
|
|
|
|
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_CONFIGURED,
|
|
|
|
0, 0);
|
|
|
|
else
|
|
|
|
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
|
|
|
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
|
|
|
break;
|
|
|
|
}
|
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-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_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-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_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-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
|
|
|
|
S(NS2_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-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
|
|
|
S(NS2_SNS_EV_RX_DELETE) |
|
|
|
|
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
2020-12-06 02:31:44 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-06-05 20:08:11 +00:00
|
|
|
S(GPRS_SNS_ST_BSS_SIZE) |
|
|
|
|
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
2020-07-12 11:45:50 +00:00
|
|
|
.name = "CONFIGURED",
|
|
|
|
.action = ns2_sns_st_configured,
|
|
|
|
.onenter = ns2_sns_st_configured_onenter,
|
|
|
|
},
|
2021-06-05 20:08:11 +00:00
|
|
|
[GPRS_SNS_ST_LOCAL_PROCEDURE] = {
|
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
|
|
|
S(NS2_SNS_EV_RX_DELETE) |
|
|
|
|
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(NS2_SNS_EV_RX_ACK) |
|
|
|
|
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_BSS_SIZE) |
|
|
|
|
S(GPRS_SNS_ST_CONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
|
|
|
.name = "LOCAL_PROCEDURE",
|
|
|
|
.action = ns2_sns_st_local_procedure,
|
|
|
|
.onenter = ns2_sns_st_local_procedure_onenter,
|
|
|
|
},
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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]) {
|
2021-06-12 21:09:46 +00:00
|
|
|
sns_failed(fi, "Size retries failed. Selecting next IP-SNS endpoint.");
|
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-06-12 21:09:46 +00:00
|
|
|
sns_failed(fi, "BSS Config retries failed. Selecting next IP-SNS endpoint");
|
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]) {
|
2021-06-12 21:09:46 +00:00
|
|
|
sns_failed(fi, "SGSN Config retries failed. Selecting next IP-SNS endpoint.");
|
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-06-12 21:09:46 +00:00
|
|
|
sns_failed(fi, "Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.");
|
2021-02-03 17:25:27 +00:00
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
case 5:
|
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
|
|
|
|
sns_failed(fi, "SNS Procedure retries failed.");
|
|
|
|
} else {
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
|
|
|
}
|
|
|
|
break;
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
static struct gprs_ns_ie_ip4_elem *ns2_get_sbind_ip4_entry(struct ns2_sns_state *gss,
|
|
|
|
struct ns2_sns_bind *sbind,
|
|
|
|
struct ns2_sns_elems *endpoints)
|
|
|
|
{
|
|
|
|
const struct osmo_sockaddr *addr;
|
|
|
|
struct gprs_ns_ie_ip4_elem *ip4;
|
|
|
|
|
|
|
|
if (gss->family != AF_INET)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
if (addr->u.sa.sa_family != AF_INET)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (unsigned int i=0; i<endpoints->num_ip4; i++) {
|
|
|
|
ip4 = &endpoints->ip4[i];
|
|
|
|
if (ip4->ip_addr == addr->u.sin.sin_addr.s_addr &&
|
|
|
|
ip4->udp_port == addr->u.sin.sin_port)
|
|
|
|
return ip4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct gprs_ns_ie_ip6_elem *ns2_get_sbind_ip6_entry(struct ns2_sns_state *gss,
|
|
|
|
struct ns2_sns_bind *sbind,
|
|
|
|
struct ns2_sns_elems *endpoints)
|
|
|
|
{
|
|
|
|
const struct osmo_sockaddr *addr;
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6;
|
|
|
|
|
|
|
|
if (gss->family != AF_INET6)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
if (addr->u.sa.sa_family != AF_INET6)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (unsigned int i=0; i<endpoints->num_ip6; i++) {
|
|
|
|
ip6 = &endpoints->ip6[i];
|
|
|
|
if (memcmp(&ip6->ip_addr, &addr->u.sin6.sin6_addr, sizeof(ip6->ip_addr)) ||
|
|
|
|
ip6->udp_port != addr->u.sin6.sin6_port)
|
|
|
|
return ip6;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return != 0 if the resulting weight is invalid. return 1 if sbind doesn't have an entry */
|
|
|
|
static int ns2_update_weight_entry(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
|
|
|
|
struct ns2_sns_elems *endpoints)
|
|
|
|
{
|
|
|
|
struct gprs_ns_ie_ip4_elem *ip4;
|
|
|
|
struct gprs_ns_ie_ip6_elem *ip6;
|
|
|
|
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
ip4 = ns2_get_sbind_ip4_entry(gss, sbind, endpoints);
|
|
|
|
if (!ip4)
|
|
|
|
return 1;
|
|
|
|
ip4->sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip4->data_weight = sbind->bind->sns_data_weight;
|
|
|
|
return (ip4_weight_sum_sig(endpoints) != 0 && ip4_weight_sum_data(endpoints) != 0);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
ip6 = ns2_get_sbind_ip6_entry(gss, sbind, endpoints);
|
|
|
|
if (!ip6)
|
|
|
|
return 1;
|
|
|
|
ip6->sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip6->data_weight = sbind->bind->sns_data_weight;
|
|
|
|
return (ip6_weight_sum_sig(endpoints) != 0 && ip6_weight_sum_data(endpoints) != 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
|
|
|
|
enum sns_procedure procedure_type)
|
|
|
|
{
|
|
|
|
struct ns2_sns_procedure *procedure = NULL;
|
|
|
|
const struct osmo_sockaddr *saddr;
|
|
|
|
saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
|
2021-06-05 20:08:11 +00:00
|
|
|
|
|
|
|
switch (procedure_type) {
|
2021-06-06 16:57:56 +00:00
|
|
|
case SNS_PROC_ADD:
|
|
|
|
break;
|
2021-06-06 16:58:01 +00:00
|
|
|
case SNS_PROC_DEL:
|
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
case SNS_PROC_CHANGE_WEIGHT:
|
|
|
|
llist_for_each_entry(procedure, &gss->procedures, list) {
|
|
|
|
if (procedure->sbind == sbind && procedure->procedure == procedure_type &&
|
|
|
|
!procedure->running) {
|
|
|
|
switch(gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
/* merge it with a previous procedure */
|
|
|
|
procedure->ip4.ip_addr = sbind->bind->sns_sig_weight;
|
|
|
|
procedure->ip4.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
/* merge it with a previous procedure */
|
|
|
|
procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
procedure->ip6.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OSMO_ASSERT(0);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-06-06 16:57:56 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2021-06-05 20:08:11 +00:00
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
procedure = talloc_zero(gss, struct ns2_sns_procedure);
|
|
|
|
if (!procedure)
|
|
|
|
return;
|
2021-06-05 20:08:11 +00:00
|
|
|
|
2021-06-06 16:58:01 +00:00
|
|
|
switch (procedure_type) {
|
|
|
|
case SNS_PROC_ADD:
|
|
|
|
case SNS_PROC_CHANGE_WEIGHT:
|
|
|
|
procedure->sbind = sbind;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
llist_add_tail(&procedure->list, &gss->procedures);
|
|
|
|
procedure->procedure = procedure_type;
|
|
|
|
procedure->sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
procedure->data_weight = sbind->bind->sns_data_weight;
|
2021-06-05 20:08:11 +00:00
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
switch(gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
procedure->ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
|
|
|
|
procedure->ip4.udp_port = saddr->u.sin.sin_port;
|
|
|
|
procedure->ip4.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
procedure->ip4.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
memcpy(&procedure->ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
|
|
|
|
procedure->ip6.udp_port = saddr->u.sin.sin_port;
|
|
|
|
procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
procedure->ip6.data_weight = sbind->bind->sns_data_weight;
|
2021-06-05 20:08:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
2021-06-06 16:57:56 +00:00
|
|
|
OSMO_ASSERT(0);
|
2021-06-05 20:08:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gss->nse->bss_sns_fi->state == GPRS_SNS_ST_CONFIGURED) {
|
|
|
|
osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
|
|
|
|
gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 16:57:56 +00:00
|
|
|
/* add an entrypoint to sns_endpoints */
|
|
|
|
static int ns2_sns_add_elements(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
|
|
|
|
struct ns2_sns_elems *elems)
|
|
|
|
{
|
|
|
|
const struct osmo_sockaddr *saddr;
|
|
|
|
struct gprs_ns_ie_ip4_elem ip4;
|
|
|
|
struct gprs_ns_ie_ip6_elem ip6;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
|
|
|
|
OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
|
|
|
|
|
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
|
|
|
|
ip4.udp_port= saddr->u.sin.sin_port;
|
|
|
|
ip4.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip4.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
rc = add_ip4_elem(gss, elems, &ip4);
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
|
|
|
|
ip6.udp_port= saddr->u.sin.sin_port;
|
|
|
|
ip6.sig_weight = sbind->bind->sns_sig_weight;
|
|
|
|
ip6.data_weight = sbind->bind->sns_data_weight;
|
|
|
|
rc = add_ip6_elem(gss, elems, &ip6);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2021-06-05 20:08:11 +00:00
|
|
|
|
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-06-05 20:08:11 +00:00
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
2021-02-12 02:17:59 +00:00
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
struct gprs_ns2_vc *nsvc, *nsvc2;
|
2021-06-06 16:58:01 +00:00
|
|
|
struct ns2_sns_procedure *procedure;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-07 06:37:07 +00:00
|
|
|
switch (event) {
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_REQ_ADD_BIND:
|
2021-02-12 02:17:59 +00:00
|
|
|
sbind = data;
|
|
|
|
switch (fi->state) {
|
|
|
|
case GPRS_SNS_ST_UNCONFIGURED:
|
2021-06-06 16:57:56 +00:00
|
|
|
if (gss->role == GPRS_SNS_ROLE_BSS)
|
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_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-06-06 16:57:56 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
if (gss->num_max_ip4_remote <= gss->local.num_ip4 ||
|
|
|
|
gss->num_max_ip4_remote * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
|
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
if (gss->num_max_ip6_remote <= gss->local.num_ip6 ||
|
|
|
|
gss->num_max_ip6_remote * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
|
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ns2_sns_add_elements(gss, sbind, &gss->local);
|
2021-02-12 02:17:59 +00:00
|
|
|
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:
|
2021-06-06 16:57:56 +00:00
|
|
|
switch (gss->family) {
|
|
|
|
case AF_INET:
|
|
|
|
if (gss->num_max_ip4_remote <= gss->local.num_ip4) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR,
|
|
|
|
"NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
|
|
|
|
nse->nsei, sbind->bind->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (gss->remote.num_ip4 * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR,
|
|
|
|
"NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
|
|
|
|
nse->nsei, sbind->bind->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
|
|
if (gss->num_max_ip6_remote <= gss->local.num_ip6) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR,
|
|
|
|
"NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
|
|
|
|
nse->nsei, sbind->bind->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (gss->remote.num_ip6 * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR,
|
|
|
|
"NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
|
|
|
|
nse->nsei, sbind->bind->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ns2_sns_add_elements(gss, sbind, &gss->local_procedure);
|
|
|
|
ns2_add_procedure(gss, sbind, SNS_PROC_ADD);
|
2021-02-12 02:17:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_REQ_DELETE_BIND:
|
2021-02-12 02:17:59 +00:00
|
|
|
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
|
|
|
llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
|
|
|
|
if (nsvc->bind == sbind->bind) {
|
|
|
|
gprs_ns2_free_nsvc(nsvc);
|
|
|
|
}
|
|
|
|
}
|
2021-06-06 16:58:01 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_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_CONFIG_BSS:
|
|
|
|
case GPRS_SNS_ST_BSS_CONFIG_SGSN:
|
2021-02-12 02:17:59 +00:00
|
|
|
case GPRS_SNS_ST_CONFIGURED:
|
2021-06-06 16:58:01 +00:00
|
|
|
case GPRS_SNS_ST_LOCAL_PROCEDURE:
|
|
|
|
remove_bind_elem(gss, &gss->local_procedure, sbind);
|
|
|
|
if (ip46_weight_sum(&gss->local_procedure, true) == 0 ||
|
|
|
|
ip46_weight_sum(&gss->local_procedure, false) == 0) {
|
|
|
|
LOGPFSML(fi, LOGL_ERROR, "NSE %d: weight has become invalid because of removing bind %s. Resetting the configuration\n",
|
|
|
|
nse->nsei, sbind->bind->name);
|
|
|
|
sns_failed(fi, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gss->block_no_nsvc_events = true;
|
2021-02-12 02:17:59 +00:00
|
|
|
llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
|
|
|
|
if (nsvc->bind == sbind->bind) {
|
|
|
|
gprs_ns2_free_nsvc(nsvc);
|
|
|
|
}
|
|
|
|
}
|
2021-06-06 16:58:01 +00:00
|
|
|
gss->block_no_nsvc_events = false;
|
|
|
|
if (nse->sum_sig_weight == 0 || !nse->alive || !gss->alive) {
|
|
|
|
sns_failed(fi, "While deleting a bind the current state became invalid (no signalling weight)");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ensure other procedures doesn't use the sbind */
|
|
|
|
llist_for_each_entry(procedure, &gss->procedures, list) {
|
|
|
|
if (procedure->sbind == sbind)
|
|
|
|
procedure->sbind = NULL;
|
|
|
|
}
|
|
|
|
ns2_add_procedure(gss, sbind, SNS_PROC_DEL);
|
2021-02-12 02:17:59 +00:00
|
|
|
break;
|
|
|
|
}
|
2021-06-06 16:58:01 +00:00
|
|
|
|
2021-02-12 02:17:59 +00:00
|
|
|
/* if this is the last bind, the free_nsvc() will trigger a reselection */
|
|
|
|
talloc_free(sbind);
|
|
|
|
break;
|
2021-06-05 20:08:11 +00:00
|
|
|
case NS2_SNS_EV_REQ_CHANGE_WEIGHT:
|
|
|
|
sbind = data;
|
|
|
|
switch (fi->state) {
|
|
|
|
case GPRS_SNS_ST_UNCONFIGURED:
|
|
|
|
/* select_endpoint will check if this is a valid configuration */
|
|
|
|
if (gss->role == GPRS_SNS_ROLE_BSS)
|
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
|
|
|
|
break;
|
|
|
|
case GPRS_SNS_ST_BSS_SIZE:
|
|
|
|
/* invalid weight? */
|
|
|
|
if (!ns2_update_weight_entry(gss, sbind, &gss->local))
|
|
|
|
sns_failed(fi, "updating weights results in an invalid configuration.");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!ns2_update_weight_entry(gss, sbind, &gss->local_procedure)) {
|
|
|
|
sns_failed(fi, "updating weights results in an invalid configuration.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ns2_add_procedure(gss, sbind, SNS_PROC_CHANGE_WEIGHT);
|
|
|
|
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);
|
|
|
|
|
2021-07-20 16:41:14 +00:00
|
|
|
/* reset when receiving NS2_SNS_EV_REQ_NO_NSVC */
|
2021-03-02 19:48:31 +00:00
|
|
|
switch (event) {
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_REQ_NO_NSVC:
|
2021-03-02 19:48:31 +00:00
|
|
|
/* ignore reselection running */
|
2021-06-06 16:58:01 +00:00
|
|
|
if (gss->reselection_running || gss->block_no_nsvc_events)
|
2021-03-02 19:48:31 +00:00
|
|
|
break;
|
|
|
|
|
2021-06-12 21:09:46 +00:00
|
|
|
sns_failed(fi, "no remaining NSVC, resetting SNS FSM");
|
2021-03-02 19:48:31 +00:00
|
|
|
break;
|
2021-08-06 17:50:09 +00:00
|
|
|
case NS2_SNS_EV_REQ_FREE_NSVCS:
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_REQ_SELECT_ENDPOINT:
|
2021-06-06 16:57:56 +00:00
|
|
|
/* TODO: keep the order of binds when data == GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER */
|
2021-03-02 19:48:31 +00:00
|
|
|
/* tear down previous state
|
|
|
|
* gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this from triggering a reselection */
|
2021-06-06 16:58:01 +00:00
|
|
|
if (gss->reselection_running || gss->block_no_nsvc_events)
|
|
|
|
break;
|
|
|
|
|
2021-03-02 19:48:31 +00:00
|
|
|
gss->reselection_running = true;
|
2021-07-20 17:05:45 +00:00
|
|
|
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);
|
2021-07-20 20:15:40 +00:00
|
|
|
gss->reselection_running = false;
|
2021-03-02 19:48:31 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-06-06 01:03:40 +00:00
|
|
|
gss->family = gss->initial->saddr.u.sa.sa_family;
|
2021-03-02 19:48:31 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
.allstate_event_mask = S(NS2_SNS_EV_REQ_NO_NSVC) |
|
2021-08-06 17:50:09 +00:00
|
|
|
S(NS2_SNS_EV_REQ_FREE_NSVCS) |
|
2021-07-20 16:41:14 +00:00
|
|
|
S(NS2_SNS_EV_REQ_SELECT_ENDPOINT) |
|
|
|
|
S(NS2_SNS_EV_REQ_ADD_BIND) |
|
2021-06-05 20:08:11 +00:00
|
|
|
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
2021-07-20 16:41:14 +00:00
|
|
|
S(NS2_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);
|
2021-06-05 20:08:11 +00:00
|
|
|
INIT_LLIST_HEAD(&gss->procedures);
|
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;
|
2021-09-03 22:44:18 +00:00
|
|
|
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-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_SIZE, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_SIZE_ACK:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_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-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_END, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
else
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_CONFIG_ACK:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_ACK, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_ADD:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_ADD, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_DELETE:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_DELETE, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_CHANGE_WEIGHT:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CHANGE_WEIGHT, tp);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case SNS_PDUT_ACK:
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(fi, NS2_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-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_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-07-20 16:41:14 +00:00
|
|
|
/* gprs_ns2_free_nsvcs() will trigger NS2_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;
|
2021-06-05 20:08:11 +00:00
|
|
|
if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE)
|
2021-02-03 17:25:27 +00:00
|
|
|
return;
|
|
|
|
|
2021-06-06 16:58:01 +00:00
|
|
|
if (gss->block_no_nsvc_events)
|
|
|
|
return;
|
|
|
|
|
2021-07-20 20:16:09 +00:00
|
|
|
if (gss->alive && nse->sum_sig_weight == 0) {
|
|
|
|
sns_failed(nse->bss_sns_fi, "No signalling NSVC available");
|
2021-02-03 17:25:27 +00:00
|
|
|
return;
|
2021-07-20 20:16:09 +00:00
|
|
|
}
|
2021-02-03 17:25:27 +00:00
|
|
|
|
|
|
|
/* check if this is the current SNS NS-VC */
|
2021-07-20 20:16:09 +00:00
|
|
|
if (nsvc == gss->sns_nsvc && !alive) {
|
2021-02-03 17:25:27 +00:00
|
|
|
/* 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 */
|
2021-07-20 20:16:09 +00:00
|
|
|
llist_for_each_entry(tmp, &nse->nsvc, list) {
|
|
|
|
if (nsvc == tmp)
|
|
|
|
continue;
|
|
|
|
if (ns2_vc_is_unblocked(nsvc)) {
|
|
|
|
ns2_sns_replace_nsvc(nsvc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-02-03 17:25:27 +00:00
|
|
|
}
|
|
|
|
|
2021-07-20 20:16:09 +00:00
|
|
|
if (alive == gss->alive)
|
|
|
|
return;
|
|
|
|
|
2021-02-03 17:25:27 +00:00
|
|
|
if (alive) {
|
2021-07-20 20:16:09 +00:00
|
|
|
/* we need at least a signalling NSVC before become alive */
|
|
|
|
if (nse->sum_sig_weight == 0)
|
|
|
|
return;
|
2021-02-03 17:25:27 +00:00
|
|
|
gss->alive = true;
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_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-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_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);
|
|
|
|
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_ADD_BIND, tmp);
|
2021-02-12 02:17:59 +00:00
|
|
|
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;
|
|
|
|
|
2021-07-20 16:41:14 +00:00
|
|
|
osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_DELETE_BIND, tmp);
|
2021-02-12 02:17:59 +00:00
|
|
|
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)
|
|
|
|
{
|
2021-06-05 20:08:11 +00:00
|
|
|
struct ns2_sns_bind *sbind;
|
|
|
|
struct gprs_ns2_nse *nse;
|
|
|
|
struct ns2_sns_state *gss;
|
|
|
|
const struct osmo_sockaddr *addr = gprs_ns2_ip_bind_sockaddr(bind);
|
|
|
|
|
|
|
|
llist_for_each_entry(nse, &bind->nsi->nse, list) {
|
|
|
|
if (!nse->bss_sns_fi)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gss = nse->bss_sns_fi->priv;
|
|
|
|
if (addr->u.sa.sa_family != gss->family)
|
|
|
|
return;
|
|
|
|
|
|
|
|
llist_for_each_entry(sbind, &gss->binds, list) {
|
|
|
|
if (sbind->bind == bind) {
|
|
|
|
osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, NS2_SNS_EV_REQ_CHANGE_WEIGHT, sbind);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-08 22:13:12 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 19:49:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SGSN role
|
|
|
|
***********************************************************************/
|
|
|
|
|
2021-07-20 16:59:40 +00:00
|
|
|
/* cleanup all state. If nsvc is given, don't remove this nsvc. (nsvc is given when a SIZE PDU received) */
|
|
|
|
static void ns2_clear_sgsn(struct ns2_sns_state *gss, struct gprs_ns2_vc *size_nsvc)
|
|
|
|
{
|
|
|
|
struct gprs_ns2_vc *nsvc, *nsvc2;
|
|
|
|
|
2021-06-05 20:08:11 +00:00
|
|
|
ns2_clear_procedures(gss);
|
2021-07-20 16:59:40 +00:00
|
|
|
ns2_clear_elems(&gss->local);
|
|
|
|
ns2_clear_elems(&gss->remote);
|
2021-09-03 23:10:46 +00:00
|
|
|
gss->block_no_nsvc_events = true;
|
2021-07-20 16:59:40 +00:00
|
|
|
llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
|
|
|
|
/* Ignore the NSVC over which the SIZE PDU got received */
|
|
|
|
if (size_nsvc && size_nsvc == nsvc)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gprs_ns2_free_nsvc(nsvc);
|
|
|
|
}
|
2021-09-03 23:10:46 +00:00
|
|
|
gss->block_no_nsvc_events = false;
|
2021-07-20 16:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ns2_sns_st_sgsn_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
|
|
|
|
{
|
|
|
|
struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
|
|
|
|
|
|
|
|
ns2_clear_sgsn(gss, NULL);
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:49:10 +00:00
|
|
|
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) {
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_CONFIG:
|
|
|
|
case NS2_SNS_EV_RX_CONFIG_END:
|
2021-03-02 19:49:10 +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;
|
|
|
|
}
|
|
|
|
/* only change state if last CONFIG was received */
|
2021-07-20 16:41:14 +00:00
|
|
|
if (event == NS2_SNS_EV_RX_CONFIG_END) {
|
2021-03-02 19:49:10 +00:00
|
|
|
/* ensure sum of data weight / sig weights is > 0 */
|
2021-06-06 00:48:18 +00:00
|
|
|
if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
|
2021-03-02 19:49:10 +00:00
|
|
|
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) {
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_CONFIG_ACK:
|
2021-03-02 19:49:10 +00:00
|
|
|
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,
|
2021-07-20 16:59:40 +00:00
|
|
|
.onenter = ns2_sns_st_sgsn_unconfigured_onenter,
|
2021-03-02 19:49:10 +00:00
|
|
|
},
|
|
|
|
[GPRS_SNS_ST_SGSN_WAIT_CONFIG] = {
|
2021-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
|
|
|
|
S(NS2_SNS_EV_RX_CONFIG_END),
|
2021-03-02 19:49:10 +00:00
|
|
|
.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] = {
|
2021-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_CONFIG_ACK),
|
2021-03-02 19:49:10 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-07-20 16:59:40 +00:00
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
|
2021-03-02 19:49:10 +00:00
|
|
|
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] = {
|
2021-07-20 16:41:14 +00:00
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
|
|
|
S(NS2_SNS_EV_RX_DELETE) |
|
|
|
|
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
2021-07-20 16:59:40 +00:00
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
2021-06-05 20:08:11 +00:00
|
|
|
S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
|
|
|
|
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
2021-03-02 19:49:10 +00:00
|
|
|
.name = "CONFIGURED",
|
|
|
|
/* shared with BSS side; once configured there's no difference */
|
|
|
|
.action = ns2_sns_st_configured,
|
|
|
|
.onenter = ns2_sns_st_configured_onenter,
|
|
|
|
},
|
2021-06-05 20:08:11 +00:00
|
|
|
[GPRS_SNS_ST_LOCAL_PROCEDURE] = {
|
|
|
|
.in_event_mask = S(NS2_SNS_EV_RX_ADD) |
|
|
|
|
S(NS2_SNS_EV_RX_DELETE) |
|
|
|
|
S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
|
|
|
|
S(NS2_SNS_EV_RX_ACK) |
|
|
|
|
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
|
|
|
S(NS2_SNS_EV_REQ_NSVC_ALIVE),
|
|
|
|
.out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_CONFIGURED) |
|
|
|
|
S(GPRS_SNS_ST_LOCAL_PROCEDURE),
|
|
|
|
.name = "LOCAL_PROCEDURE",
|
|
|
|
/* shared with BSS side; once configured there's no difference */
|
|
|
|
.action = ns2_sns_st_local_procedure,
|
|
|
|
.onenter = ns2_sns_st_local_procedure_onenter,
|
|
|
|
},
|
2021-03-02 19:49:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2021-06-05 20:08:11 +00:00
|
|
|
case 5:
|
|
|
|
if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {
|
|
|
|
sns_failed(fi, "SNS Procedure retries failed.");
|
|
|
|
} else {
|
|
|
|
osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],
|
|
|
|
fi->T);
|
|
|
|
}
|
|
|
|
break;
|
2021-03-02 19:49:10 +00:00
|
|
|
}
|
2021-06-05 20:08:11 +00:00
|
|
|
|
2021-03-02 19:49:10 +00:00
|
|
|
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) {
|
2021-07-20 16:41:14 +00:00
|
|
|
case NS2_SNS_EV_RX_SIZE:
|
2021-03-02 19:49:10 +00:00
|
|
|
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);
|
2021-06-12 22:14:48 +00:00
|
|
|
if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
|
|
|
|
sns_failed(fi, "Rx Size: Missing essential IE");
|
2021-03-04 16:59:35 +00:00
|
|
|
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);
|
2021-06-12 22:14:48 +00:00
|
|
|
if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
|
|
|
|
sns_failed(fi, "Rx Size: Missing essential IE");
|
2021-03-02 19:49:10 +00:00
|
|
|
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 */
|
2021-06-05 23:32:45 +00:00
|
|
|
if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, AF_INET6)) {
|
|
|
|
gss->family = AF_INET6;
|
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;
|
2021-06-05 23:32:45 +00:00
|
|
|
} else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, AF_INET)) {
|
|
|
|
gss->family = AF_INET;
|
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);
|
2021-06-12 22:14:48 +00:00
|
|
|
if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
|
|
|
|
sns_failed(fi, "Rx Size: Invalid Nr of IPv4/IPv6 EPs");
|
2021-03-04 18:49:38 +00:00
|
|
|
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);
|
2021-06-12 22:14:48 +00:00
|
|
|
if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
|
|
|
|
sns_failed(fi, NULL);
|
2021-03-04 18:49:38 +00:00
|
|
|
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) {
|
|
|
|
/* clear all state */
|
2021-07-20 16:59:40 +00:00
|
|
|
/* TODO: ensure gss->sns_nsvc is always the NSVC on which we received the SIZE PDU */
|
2021-03-02 19:49:10 +00:00
|
|
|
gss->N = 0;
|
2021-07-20 16:59:40 +00:00
|
|
|
ns2_clear_sgsn(gss, gss->sns_nsvc);
|
|
|
|
/* keep the NSVC we need for SNS, but unconfigure it */
|
|
|
|
gss->sns_nsvc->sig_weight = 0;
|
|
|
|
gss->sns_nsvc->data_weight = 0;
|
2021-09-06 15:47:16 +00:00
|
|
|
gss->block_no_nsvc_events = true;
|
2021-07-20 16:59:40 +00:00
|
|
|
ns2_vc_force_unconfigured(gss->sns_nsvc);
|
2021-09-06 15:47:16 +00:00
|
|
|
gss->block_no_nsvc_events = false;
|
2021-03-24 00:57:30 +00:00
|
|
|
ns2_sns_compute_local_ep_from_binds(fi);
|
2021-03-02 19:49:10 +00:00
|
|
|
}
|
2021-06-12 22:14:48 +00:00
|
|
|
|
|
|
|
if (fi->state == GPRS_SNS_ST_UNCONFIGURED && !(flag & 1)) {
|
|
|
|
sns_failed(fi, "Rx Size without Reset flag, but NSE is unknown");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
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;
|
2021-08-06 17:50:09 +00:00
|
|
|
case NS2_SNS_EV_REQ_FREE_NSVCS:
|
|
|
|
sns_failed(fi, "On user request to free all NSVCs");
|
|
|
|
break;
|
2021-03-02 19:49:10 +00:00
|
|
|
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),
|
2021-07-20 16:41:14 +00:00
|
|
|
.allstate_event_mask = S(NS2_SNS_EV_RX_SIZE) |
|
|
|
|
S(NS2_SNS_EV_REQ_NO_NSVC) |
|
2021-08-06 17:50:09 +00:00
|
|
|
S(NS2_SNS_EV_REQ_FREE_NSVCS) |
|
2021-07-20 16:41:14 +00:00
|
|
|
S(NS2_SNS_EV_REQ_ADD_BIND) |
|
2021-06-05 20:08:11 +00:00
|
|
|
S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
|
2021-07-20 16:41:14 +00:00
|
|
|
S(NS2_SNS_EV_REQ_DELETE_BIND),
|
2021-03-02 19:49:10 +00:00
|
|
|
.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);
|
2021-06-05 20:08:11 +00:00
|
|
|
INIT_LLIST_HEAD(&gss->procedures);
|
2021-03-02 19:49:10 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|