Add initial SBc-AP support to osmo-cbc
This patch makes use of the newly introduced sbcap library, and introduces new code (and extends existing one) to handle all the aspects of MME peers talking SBc-AP and its underlaying SCTP connection. This commit doesn't aim to implement all the SBc-AP features, but to implement a minimal subset of features already available for CBSP in osmo-cbc, in order to have similar support level for both 2G and 4G networks. Related: OS#4945 Change-Id: Ib278bc1d1a74459814016fef7a8fe21cc29d46c9
This commit is contained in:
parent
ac2f4037bc
commit
6e59534d6b
12
configure.ac
12
configure.ac
|
@ -48,6 +48,18 @@ AC_SUBST([ASN1C_SKELETON_PATH])
|
|||
AC_SUBST([ASN1C_BIN_PATH])
|
||||
AC_SUBST([ASN_MODULE_CFLAGS])
|
||||
|
||||
AC_CHECK_HEADERS(netinet/sctp.h,,AC_MSG_ERROR(netinet/sctp.h not found))
|
||||
old_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
|
||||
AC_SUBST(HAVE_LIBSCTP, [1])
|
||||
if test -n "$ac_lib"; then
|
||||
AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
|
||||
fi
|
||||
], [
|
||||
AC_MSG_ERROR([sctp_recvmsg not found in searched libs])])
|
||||
LIBS=$old_LIBS
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
|
|
|
@ -30,6 +30,7 @@ BuildRequires: pkgconfig >= 0.20
|
|||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libsctp)
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.7.0
|
||||
|
|
|
@ -15,6 +15,7 @@ Build-Depends: debhelper (>=9),
|
|||
libosmo-netif-dev (>= 1.2.0),
|
||||
libulfius-dev,
|
||||
libjansson-dev,
|
||||
libsctp-dev,
|
||||
osmo-gsm-manuals-dev
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-cbc
|
||||
|
|
|
@ -9,3 +9,7 @@ cbc
|
|||
ecbe
|
||||
local-ip 127.0.0.1
|
||||
local-port 12345
|
||||
sbcap
|
||||
local-ip 127.0.0.1
|
||||
local-ip ::1
|
||||
local-port 6789
|
||||
|
|
|
@ -3,5 +3,7 @@ noinst_HEADERS = \
|
|||
cbsp_server.h \
|
||||
charset.h \
|
||||
internal.h \
|
||||
sbcap_msg.h \
|
||||
sbcap_server.h \
|
||||
rest_it_op.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -11,6 +11,8 @@ struct osmo_cbsp_cbc_client;
|
|||
struct osmo_sabp_cbc_client;
|
||||
struct rest_it_op;
|
||||
|
||||
#define CBC_MAX_ADDRS 8
|
||||
|
||||
/*********************************************************************************
|
||||
* CBC Peer
|
||||
*********************************************************************************/
|
||||
|
@ -18,13 +20,15 @@ struct rest_it_op;
|
|||
enum cbc_peer_protocol {
|
||||
CBC_PEER_PROTO_CBSP,
|
||||
CBC_PEER_PROTO_SABP,
|
||||
CBC_PEER_PROTO_SBcAP
|
||||
};
|
||||
|
||||
struct cbc_peer {
|
||||
struct llist_head list; /* linked to cbc.peers */
|
||||
const char *name;
|
||||
|
||||
char *remote_host; /* remote IP address in string format */
|
||||
char *remote_host[CBC_MAX_ADDRS]; /* remote IP address in string format */
|
||||
unsigned int num_remote_host; /* number of addresses present in remote_host */
|
||||
int remote_port; /* remote port number or -1 for random */
|
||||
bool unknown_dynamic_peer; /* dynamic/unknown peer; not saved in VTY */
|
||||
|
||||
|
@ -32,6 +36,7 @@ struct cbc_peer {
|
|||
union {
|
||||
struct osmo_cbsp_cbc_client *cbsp;
|
||||
struct osmo_sabp_cbc_client *sabp;
|
||||
struct osmo_sbcap_cbc_client *sbcap;
|
||||
} client;
|
||||
};
|
||||
|
||||
|
@ -170,6 +175,11 @@ struct cbc {
|
|||
char *local_host;
|
||||
int local_port;
|
||||
} cbsp;
|
||||
struct {
|
||||
char *local_host[CBC_MAX_ADDRS];
|
||||
unsigned int num_local_host;
|
||||
int local_port;
|
||||
} sbcap;
|
||||
struct {
|
||||
char *local_host;
|
||||
int local_port;
|
||||
|
@ -197,4 +207,3 @@ struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote
|
|||
enum cbc_peer_protocol proto);
|
||||
struct cbc_peer *cbc_peer_create(const char *name, enum cbc_peer_protocol proto);
|
||||
void cbc_peer_remove(struct cbc_peer *peer);
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include <osmocom/cbc/cbc_data.h>
|
||||
|
||||
enum {
|
||||
DCBSP,
|
||||
DSBcAP,
|
||||
DREST,
|
||||
};
|
||||
|
||||
|
@ -23,12 +25,30 @@ enum cbsp_server_event {
|
|||
CBSP_SRV_E_CMD_CLOSE, /* CLOSE command from CBC */
|
||||
};
|
||||
|
||||
extern struct osmo_fsm sbcap_server_fsm;
|
||||
|
||||
enum sbcap_server_event {
|
||||
SBcAP_SRV_E_RX_RST_COMPL, /* reset complete received */
|
||||
SBcAP_SRV_E_RX_RST_FAIL, /* reset failure received */
|
||||
SBcAP_SRV_E_RX_KA_COMPL, /* keep-alive complete received */
|
||||
SBcAP_SRV_E_RX_RESTART, /* restart received */
|
||||
SBcAP_SRV_E_CMD_RESET, /* RESET command from CBC */
|
||||
SBcAP_SRV_E_CMD_CLOSE, /* CLOSE command from CBC */
|
||||
};
|
||||
|
||||
|
||||
/* rest_api.c */
|
||||
int rest_api_init(void *ctx, const char *bind_addr, uint16_t port);
|
||||
void rest_api_fin(void);
|
||||
|
||||
/* cbc_vty.c */
|
||||
enum cbc_vty_node {
|
||||
CBC_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
PEER_NODE,
|
||||
CBSP_NODE,
|
||||
SBcAP_NODE,
|
||||
ECBE_NODE,
|
||||
};
|
||||
void cbc_vty_init(void);
|
||||
|
||||
/* message_handling.c */
|
||||
|
@ -65,6 +85,12 @@ enum smscb_fsm_event {
|
|||
/* CBSP peer confirms status query */
|
||||
SMSCB_E_CBSP_STATUS_ACK,
|
||||
SMSCB_E_CBSP_STATUS_NACK,
|
||||
/* SBc-AP peer confirms write */
|
||||
SMSCB_E_SBCAP_WRITE_ACK,
|
||||
SMSCB_E_SBCAP_WRITE_NACK,
|
||||
/* SBc-AP peer confirms delete */
|
||||
SMSCB_E_SBCAP_DELETE_ACK,
|
||||
SMSCB_E_SBCAP_DELETE_NACK,
|
||||
};
|
||||
|
||||
enum smscb_fsm_state {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include "cbc_data.h"
|
||||
|
||||
struct cbc_message;
|
||||
typedef struct SBcAP_SBC_AP_PDU SBcAP_SBC_AP_PDU_t;
|
||||
|
||||
SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg);
|
||||
SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_message *cbcmsg);
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/sbcap/sbcap_common.h>
|
||||
|
||||
#include "cbc_data.h"
|
||||
|
||||
#define SBcAP_SCTP_PORT 29168
|
||||
typedef struct SBcAP_SBC_AP_PDU SBcAP_SBC_AP_PDU_t;
|
||||
#define LOGPSBCAPC(client, level, fmt, args...) \
|
||||
LOGP(DSBcAP, level, "%s: " fmt, sbcap_cbc_client_name(client), ## args)
|
||||
|
||||
struct osmo_sbcap_cbc_client;
|
||||
struct osmo_fsm_inst;
|
||||
|
||||
/* a CBC server */
|
||||
struct osmo_sbcap_cbc {
|
||||
/* libosmo-netif stream server */
|
||||
struct osmo_stream_srv_link *link;
|
||||
|
||||
/* MMEs / clients connected to this CBC */
|
||||
struct llist_head clients;
|
||||
|
||||
/* receive call-back; called for every received message */
|
||||
int (*rx_cb)(struct osmo_sbcap_cbc_client *client, SBcAP_SBC_AP_PDU_t *pdu);
|
||||
};
|
||||
struct osmo_sbcap_cbc *sbcap_cbc_create(void *ctx);
|
||||
|
||||
/* a single (remote) client connected to the (local) CBC server */
|
||||
struct osmo_sbcap_cbc_client {
|
||||
/* entry in osmo_sbcap_cbc.clients */
|
||||
struct llist_head list;
|
||||
/* stream server connection for this client */
|
||||
struct osmo_stream_srv *conn;
|
||||
/* partially received sbcap message (rx completion pending) */
|
||||
struct msgb *rx_msg;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
struct cbc_peer *peer;
|
||||
};
|
||||
|
||||
const char *sbcap_cbc_client_name(const struct osmo_sbcap_cbc_client *client);
|
||||
void sbcap_cbc_client_tx(struct osmo_sbcap_cbc_client *client, SBcAP_SBC_AP_PDU_t *pdu);
|
||||
void sbcap_cbc_client_close(struct osmo_sbcap_cbc_client *client);
|
||||
int sbcap_cbc_client_rx_cb(struct osmo_sbcap_cbc_client *client, SBcAP_SBC_AP_PDU_t *pdu);
|
|
@ -4,7 +4,7 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/src/sbcap/
|
|||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(ULFIUS_CFLAGS) $(JANSSON_CFLAGS) $(ORCANIA_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS)
|
||||
$(COVERAGE_CFLAGS) $(ASN_MODULE_CFLAGS)
|
||||
AM_LDFLAGS=$(COVERAGE_LDFLAGS)
|
||||
|
||||
bin_PROGRAMS = osmo-cbc
|
||||
|
@ -19,13 +19,16 @@ osmo_cbc_SOURCES = \
|
|||
charset.c \
|
||||
message_handling.c \
|
||||
rest_it_op.c \
|
||||
sbcap_msg.c \
|
||||
sbcap_server.c \
|
||||
sbcap_server_fsm.c \
|
||||
smscb_peer_fsm.c \
|
||||
smscb_message_fsm.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_cbc_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(ULFIUS_LIBS) $(JANSSON_LIBS) $(ORCANIA_LIBS) \
|
||||
$(ULFIUS_LIBS) $(JANSSON_LIBS) $(ORCANIA_LIBS) $(LIBSCTP_LIBS) \
|
||||
sbcap/libosmo-sbcap.la
|
||||
|
||||
regen:
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
const struct value_string cbc_peer_proto_name[] = {
|
||||
{ CBC_PEER_PROTO_CBSP, "CBSP" },
|
||||
{ CBC_PEER_PROTO_SABP, "SABP" },
|
||||
{ CBC_PEER_PROTO_SBcAP, "SBc-AP" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
@ -99,13 +100,16 @@ struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote
|
|||
struct cbc_peer *peer;
|
||||
|
||||
llist_for_each_entry(peer, &g_cbc->peers, list) {
|
||||
if (!peer->remote_host)
|
||||
continue;
|
||||
if (!strcasecmp(remote_host, peer->remote_host)) {
|
||||
if (peer->remote_port == -1)
|
||||
return peer;
|
||||
else if (remote_port == peer->remote_port)
|
||||
return peer;
|
||||
unsigned int i;
|
||||
for (i = 0; i < peer->num_remote_host; i++) {
|
||||
if (peer->proto != proto)
|
||||
continue;
|
||||
if (!strcasecmp(remote_host, peer->remote_host[i])) {
|
||||
if (peer->remote_port == -1)
|
||||
return peer;
|
||||
else if (remote_port == peer->remote_port)
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
#include <osmocom/cbc/internal.h>
|
||||
#include <osmocom/cbc/cbsp_server.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
#include <osmocom/cbc/cbc_data.h>
|
||||
|
||||
static void *tall_cbc_ctx;
|
||||
|
@ -59,10 +60,17 @@ static const struct log_info_cat log_info_cat[] = {
|
|||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DSBcAP] = {
|
||||
.name = "DSBcAP",
|
||||
.description = "SBc Application Part (CBC-MME)",
|
||||
.color = "\033[1;32m",
|
||||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DREST] = {
|
||||
.name = "DREST",
|
||||
.description = "REST interface",
|
||||
.color = "\033[1;32m",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1,
|
||||
.loglevel = LOGL_NOTICE,
|
||||
},
|
||||
|
@ -73,6 +81,27 @@ static const struct log_info log_info = {
|
|||
.num_cat = ARRAY_SIZE(log_info_cat),
|
||||
};
|
||||
|
||||
static int cbc_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case SBcAP_NODE:
|
||||
/* If no local addr set, add a default one: */
|
||||
if (g_cbc->config.sbcap.num_local_host) {
|
||||
g_cbc->config.sbcap.local_host[0] = talloc_strdup(g_cbc, "127.0.0.1");
|
||||
g_cbc->config.sbcap.num_local_host = 1;
|
||||
}
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
default:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static const char cbc_copyright[] =
|
||||
"Copyright (C) 2019-2021 by Harald Welte <laforge@gnumonks.org>\r\n"
|
||||
"License AGPLv3+: GNU Affero GPL Version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
|
@ -83,6 +112,7 @@ static const char cbc_copyright[] =
|
|||
static struct vty_app_info vty_info = {
|
||||
.name = "OsmoCBC",
|
||||
.copyright = cbc_copyright,
|
||||
.go_parent_cb = cbc_vty_go_parent,
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = NULL,
|
||||
.is_config_node = NULL,
|
||||
|
@ -201,8 +231,6 @@ static void signal_handler(int signal)
|
|||
}
|
||||
}
|
||||
|
||||
extern int cbc_client_rx_cb(struct osmo_cbsp_cbc_client *client, struct osmo_cbsp_decoded *dec);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_rest_ctx;
|
||||
|
@ -222,6 +250,8 @@ int main(int argc, char **argv)
|
|||
INIT_LLIST_HEAD(&g_cbc->expired_messages);
|
||||
g_cbc->config.cbsp.local_host = talloc_strdup(g_cbc, "127.0.0.1");
|
||||
g_cbc->config.cbsp.local_port = CBSP_TCP_PORT;
|
||||
/* g_cbc->config.sbcap local_host set up during VTY (and vty_go_parent) */
|
||||
g_cbc->config.sbcap.local_port = SBcAP_SCTP_PORT;
|
||||
g_cbc->config.ecbe.local_host = talloc_strdup(g_cbc, "127.0.0.1");
|
||||
g_cbc->config.ecbe.local_port = 12345;
|
||||
|
||||
|
@ -250,6 +280,11 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (sbcap_cbc_create(tall_cbc_ctx) == NULL) {
|
||||
perror("Error binding SBc-AP port\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = rest_api_init(tall_rest_ctx, g_cbc->config.ecbe.local_host, g_cbc->config.ecbe.local_port);
|
||||
if (rc < 0) {
|
||||
perror("Error binding ECBE port");
|
||||
|
|
179
src/cbc_vty.c
179
src/cbc_vty.c
|
@ -33,10 +33,14 @@
|
|||
#include <osmocom/cbc/cbc_data.h>
|
||||
#include <osmocom/cbc/internal.h>
|
||||
#include <osmocom/cbc/cbsp_server.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
|
||||
static void dump_one_cbc_peer(struct vty *vty, const struct cbc_peer *peer)
|
||||
{
|
||||
const char *state = "<disconnected>";
|
||||
char rem_addrs[1024] = "<unset>";
|
||||
struct osmo_strbuf sb = { .buf = rem_addrs, .len = sizeof(rem_addrs) };
|
||||
unsigned int i;
|
||||
|
||||
switch (peer->proto) {
|
||||
case CBC_PEER_PROTO_CBSP:
|
||||
|
@ -45,11 +49,21 @@ static void dump_one_cbc_peer(struct vty *vty, const struct cbc_peer *peer)
|
|||
break;
|
||||
case CBC_PEER_PROTO_SABP:
|
||||
break;
|
||||
case CBC_PEER_PROTO_SBcAP:
|
||||
if (peer->client.sbcap)
|
||||
state = osmo_fsm_inst_state_name(peer->client.sbcap->fi);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < peer->num_remote_host; i++) {
|
||||
if (i > 0)
|
||||
OSMO_STRBUF_PRINTF(sb, ",");
|
||||
OSMO_STRBUF_PRINTF(sb, peer->remote_host[i]);
|
||||
}
|
||||
|
||||
vty_out(vty, "|%-20s| %-15s| %-5d| %-6s| %-20s|%s",
|
||||
peer->name ? peer->name : "<unnamed>",
|
||||
peer->remote_host ? peer->remote_host : "<unset>", peer->remote_port,
|
||||
rem_addrs, peer->remote_port,
|
||||
get_value_string(cbc_peer_proto_name, peer->proto), state, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
|
@ -265,13 +279,6 @@ DEFUN(show_messages_etws, show_messages_etws_cmd,
|
|||
/* TODO: Re-send all messages to one peer / all peers? */
|
||||
/* TODO: Completed / Load status */
|
||||
|
||||
enum cbc_vty_node {
|
||||
CBC_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
PEER_NODE,
|
||||
CBSP_NODE,
|
||||
ECBE_NODE,
|
||||
};
|
||||
|
||||
static struct cmd_node cbc_node = {
|
||||
CBC_NODE,
|
||||
"%s(config-cbc)# ",
|
||||
|
@ -321,6 +328,14 @@ DEFUN(cfg_cbsp, cfg_cbsp_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_sbcap, cfg_sbcap_cmd,
|
||||
"sbcap",
|
||||
"SBc Application Part\n")
|
||||
{
|
||||
vty->node = SBcAP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_ecbe, cfg_ecbe_cmd,
|
||||
"ecbe",
|
||||
"External CBS Entity (REST Interface)\n")
|
||||
|
@ -402,6 +417,87 @@ DEFUN(cfg_ecbe_local_port, cfg_ecbe_local_port_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* SBc-AP */
|
||||
|
||||
static struct cmd_node sbcap_node = {
|
||||
SBcAP_NODE,
|
||||
"%s(config-sbcap)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static int config_write_sbcap(struct vty *vty)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
vty_out(vty, " sbcap%s", VTY_NEWLINE);
|
||||
for (i = 0; i < g_cbc->config.sbcap.num_local_host; i++)
|
||||
vty_out(vty, " local-ip %s%s", g_cbc->config.sbcap.local_host[i], VTY_NEWLINE);
|
||||
vty_out(vty, " local-port %u%s", g_cbc->config.sbcap.local_port, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_sbcap_local_ip, cfg_sbcap_local_ip_cmd,
|
||||
"local-ip " VTY_IPV46_CMD,
|
||||
"Local IP address for SBc-AP\n"
|
||||
"Local IPv4 address for SBc-AP Interface\n"
|
||||
"Local IPv6 address for SBc-AP Interface\n")
|
||||
{
|
||||
unsigned int i;
|
||||
const char *newaddr = argv[0];
|
||||
|
||||
if (g_cbc->config.sbcap.num_local_host >= ARRAY_SIZE(g_cbc->config.sbcap.local_host)) {
|
||||
vty_out(vty, "%% Only up to %zu addresses allowed%s",
|
||||
ARRAY_SIZE(g_cbc->config.sbcap.local_host), VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Check for repeated entries: */
|
||||
for (i = 0; i < g_cbc->config.sbcap.num_local_host; i++) {
|
||||
if (strcmp(g_cbc->config.sbcap.local_host[i], newaddr) == 0) {
|
||||
vty_out(vty, "%% IP address %s already in list%s", newaddr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
}
|
||||
osmo_talloc_replace_string(g_cbc,
|
||||
&g_cbc->config.sbcap.local_host[g_cbc->config.sbcap.num_local_host], newaddr);
|
||||
g_cbc->config.sbcap.num_local_host++;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_sbcap_no_local_ip, cfg_sbcap_no_local_ip_cmd,
|
||||
"no local-ip " VTY_IPV46_CMD,
|
||||
NO_STR "Local IP address for SBc-AP\n"
|
||||
"Local IPv4 address for SBc-AP Interface\n"
|
||||
"Local IPv6 address for SBc-AP Interface\n")
|
||||
{
|
||||
unsigned int i, j;
|
||||
const char *rmaddr = argv[0];
|
||||
|
||||
for (i = 0; i < g_cbc->config.sbcap.num_local_host; i++) {
|
||||
if (strcmp(g_cbc->config.sbcap.local_host[i], rmaddr) == 0) {
|
||||
talloc_free(g_cbc->config.sbcap.local_host[i]);
|
||||
g_cbc->config.sbcap.num_local_host--;
|
||||
for (j = i; j < g_cbc->config.sbcap.num_local_host; j++) {
|
||||
g_cbc->config.sbcap.local_host[j] = g_cbc->config.sbcap.local_host[j + 1];
|
||||
}
|
||||
g_cbc->config.sbcap.local_host[j] = NULL;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
}
|
||||
vty_out(vty, "%% IP address %s not in list%s", rmaddr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
DEFUN(cfg_sbcap_local_port, cfg_sbcap_local_port_cmd,
|
||||
"local-port <0-65535>",
|
||||
"Local TCP port for SBc-AP Interface\n"
|
||||
"Local TCP port for SBc-AP Interface\n")
|
||||
{
|
||||
g_cbc->config.sbcap.local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* PEER */
|
||||
|
||||
|
@ -441,12 +537,15 @@ DEFUN(cfg_cbc_no_peer, cfg_cbc_no_peer_cmd,
|
|||
|
||||
|
||||
DEFUN(cfg_peer_proto, cfg_peer_proto_cmd,
|
||||
"protocol (cbsp)",
|
||||
"protocol (cbsp|sbcap)",
|
||||
"Configure Protocol of Peer\n"
|
||||
"Cell Broadcast Service Protocol (GSM)\n")
|
||||
{
|
||||
struct cbc_peer *peer = (struct cbc_peer *) vty->index;
|
||||
peer->proto = CBC_PEER_PROTO_CBSP;
|
||||
if (strcmp(argv[0], "cbsp") == 0)
|
||||
peer->proto = CBC_PEER_PROTO_CBSP;
|
||||
else
|
||||
peer->proto = CBC_PEER_PROTO_SBcAP;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -477,22 +576,69 @@ DEFUN(cfg_peer_remote_ip, cfg_peer_remote_ip_cmd,
|
|||
"IPv4 address of peer\n" "IPv6 address of peer\n")
|
||||
{
|
||||
struct cbc_peer *peer = (struct cbc_peer *) vty->index;
|
||||
osmo_talloc_replace_string(peer, &peer->remote_host, argv[0]);
|
||||
unsigned int allowed_address;
|
||||
unsigned int i;
|
||||
const char *newaddr = argv[0];
|
||||
|
||||
if (peer->proto == CBC_PEER_PROTO_SBcAP)
|
||||
allowed_address = ARRAY_SIZE(peer->remote_host);
|
||||
else
|
||||
allowed_address = 1;
|
||||
|
||||
if (peer->num_remote_host >= allowed_address) {
|
||||
vty_out(vty, "%% Only up to %u addresses allowed%s",
|
||||
allowed_address, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* Check for repeated entries: */
|
||||
for (i = 0; i < peer->num_remote_host; i++) {
|
||||
if (strcmp(peer->remote_host[i], newaddr) == 0) {
|
||||
vty_out(vty, "%% IP address %s already in list%s", newaddr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
}
|
||||
osmo_talloc_replace_string(peer, &peer->remote_host[peer->num_remote_host], newaddr);
|
||||
peer->num_remote_host++;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_peer_no_remote_ip, cfg_peer_no_remote_ip_cmd,
|
||||
"no remote-ip " VTY_IPV46_CMD,
|
||||
NO_STR "Keep remote IP of peer\n"
|
||||
"IPv4 address of peer\n" "IPv6 address of peer\n")
|
||||
{
|
||||
struct cbc_peer *peer = (struct cbc_peer *) vty->index;
|
||||
unsigned int i, j;
|
||||
const char *rmaddr = argv[0];
|
||||
|
||||
for (i = 0; i < peer->num_remote_host; i++) {
|
||||
if (strcmp(peer->remote_host[i], rmaddr) == 0) {
|
||||
talloc_free(peer->remote_host[i]);
|
||||
peer->num_remote_host--;
|
||||
for (j = i; j < peer->num_remote_host; j++) {
|
||||
peer->remote_host[j] = peer->remote_host[j + 1];
|
||||
}
|
||||
peer->remote_host[j] = NULL;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
}
|
||||
vty_out(vty, "%% IP address %s not in list%s", rmaddr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
static void write_one_peer(struct vty *vty, struct cbc_peer *peer)
|
||||
{
|
||||
unsigned int i;
|
||||
vty_out(vty, " peer %s%s", peer->name, VTY_NEWLINE);
|
||||
vty_out(vty, " protocol cbsp%s", VTY_NEWLINE);
|
||||
if (peer->remote_port == -1)
|
||||
vty_out(vty, " no remote-port%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " remote-port %d%s", peer->remote_port, VTY_NEWLINE);
|
||||
if (peer->remote_host)
|
||||
vty_out(vty, " remote-ip %s%s", peer->remote_host, VTY_NEWLINE);
|
||||
for (i = 0; i < peer->num_remote_host; i++)
|
||||
vty_out(vty, " remote-ip %s%s", peer->remote_host[i], VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_peer(struct vty *vty)
|
||||
|
@ -528,6 +674,12 @@ void cbc_vty_init(void)
|
|||
install_lib_element(ECBE_NODE, &cfg_ecbe_local_ip_cmd);
|
||||
install_lib_element(ECBE_NODE, &cfg_ecbe_local_port_cmd);
|
||||
|
||||
install_lib_element(CBC_NODE, &cfg_sbcap_cmd);
|
||||
install_node(&sbcap_node, config_write_sbcap);
|
||||
install_lib_element(SBcAP_NODE, &cfg_sbcap_local_ip_cmd);
|
||||
install_lib_element(SBcAP_NODE, &cfg_sbcap_no_local_ip_cmd);
|
||||
install_lib_element(SBcAP_NODE, &cfg_sbcap_local_port_cmd);
|
||||
|
||||
install_lib_element(CBC_NODE, &cfg_cbc_peer_cmd);
|
||||
install_lib_element(CBC_NODE, &cfg_cbc_no_peer_cmd);
|
||||
install_node(&peer_node, config_write_peer);
|
||||
|
@ -535,5 +687,6 @@ void cbc_vty_init(void)
|
|||
install_lib_element(PEER_NODE, &cfg_peer_remote_port_cmd);
|
||||
install_lib_element(PEER_NODE, &cfg_peer_no_remote_port_cmd);
|
||||
install_lib_element(PEER_NODE, &cfg_peer_remote_ip_cmd);
|
||||
install_lib_element(PEER_NODE, &cfg_peer_no_remote_ip_cmd);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#include <osmocom/cbc/cbc_data.h>
|
||||
#include <osmocom/cbc/cbsp_server.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
#include <osmocom/cbc/sbcap_msg.h>
|
||||
#include <osmocom/cbc/rest_it_op.h>
|
||||
#include <osmocom/cbc/internal.h>
|
||||
|
||||
|
@ -128,6 +130,7 @@ static bool is_peer_in_scope(const struct cbc_peer *peer, const struct cbc_messa
|
|||
int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg)
|
||||
{
|
||||
struct osmo_cbsp_decoded *cbsp;
|
||||
SBcAP_SBC_AP_PDU_t *sbcap;
|
||||
|
||||
switch (peer->proto) {
|
||||
case CBC_PEER_PROTO_CBSP:
|
||||
|
@ -144,6 +147,24 @@ int peer_new_cbc_message(struct cbc_peer *peer, struct cbc_message *cbcmsg)
|
|||
}
|
||||
cbsp_cbc_client_tx(peer->client.cbsp, cbsp);
|
||||
break;
|
||||
case CBC_PEER_PROTO_SBcAP:
|
||||
/* skip peers without any current SBc-AP connection */
|
||||
if (!peer->client.sbcap) {
|
||||
LOGP(DSBcAP, LOGL_NOTICE, "[%s] Tx SBc-AP: not connected\n",
|
||||
peer->name);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
if (!(sbcap = cbcmsg_to_sbcap(peer, cbcmsg))) {
|
||||
LOGP(DSBcAP, LOGL_ERROR, "[%s] Tx SBc-AP: msg gen failed\n",
|
||||
peer->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
sbcap_cbc_client_tx(peer->client.sbcap, sbcap);
|
||||
break;
|
||||
case CBC_PEER_PROTO_SABP:
|
||||
LOGP(DLGLOBAL, LOGL_ERROR, "Sending message to peer proto %s not implemented!\n",
|
||||
get_value_string(cbc_peer_proto_name, peer->proto));
|
||||
return -1;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
/* Osmocom CBC (Cell Broacast Centre) */
|
||||
|
||||
/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/sbcap/sbcap_common.h>
|
||||
|
||||
#include <osmocom/cbc/cbc_data.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
#include <osmocom/cbc/internal.h>
|
||||
|
||||
/* 3GPP TS 36.413 9.2.1.53 */
|
||||
#define SBCAP_WARN_MSG_CONTENTS_IE_MAX_LEN 9600
|
||||
|
||||
#if 0
|
||||
static void msgb_put_sbcap_cell_list(const struct cbc_message *cbcmsg, void *void_li)
|
||||
{
|
||||
static uint8_t ie_plm_id0[] = {0x05, 0xf5, 0x32};
|
||||
static uint8_t ie_cell_id0[] = {0xa0, 0x00, 0x00, 0x10};
|
||||
static uint8_t ie_cell_id1[] = {0xa0, 0x00, 0x00, 0x20};
|
||||
SBcAP_EUTRAN_CGI_t *ecgi;
|
||||
A_SEQUENCE_OF(void) *li = void_li;
|
||||
|
||||
ecgi = CALLOC(1, sizeof(*ecgi));
|
||||
*ecgi = (SBcAP_EUTRAN_CGI_t) {
|
||||
.pLMNidentity = {
|
||||
.buf = ie_plm_id0,
|
||||
.size = sizeof(ie_plm_id0),
|
||||
},
|
||||
.cell_ID = { /* SBcAP_CellIdentity_t*/
|
||||
.buf = ie_cell_id0,
|
||||
.size = sizeof(ie_cell_id0),
|
||||
.bits_unused = 4,
|
||||
}
|
||||
};
|
||||
ASN_SEQUENCE_ADD(li, ecgi);
|
||||
|
||||
ecgi = CALLOC(1, sizeof(*ecgi));
|
||||
*ecgi = (SBcAP_EUTRAN_CGI_t) {
|
||||
.pLMNidentity = {
|
||||
.buf = ie_plm_id0,
|
||||
.size = sizeof(ie_plm_id0),
|
||||
},
|
||||
.cell_ID = { /* SBcAP_CellIdentity_t*/
|
||||
.buf = ie_cell_id1,
|
||||
.size = sizeof(ie_cell_id1),
|
||||
.bits_unused = 4,
|
||||
}
|
||||
};
|
||||
ASN_SEQUENCE_ADD(li, ecgi);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation */
|
||||
SBcAP_SBC_AP_PDU_t *cbcmsg_to_sbcap(void *ctx, const struct cbc_message *cbcmsg)
|
||||
{
|
||||
const struct smscb_message *smscb = &cbcmsg->msg;
|
||||
SBcAP_SBC_AP_PDU_t *pdu;
|
||||
SBcAP_Write_Replace_Warning_Request_IEs_t *ie;
|
||||
uint16_t ie_warning_type;
|
||||
unsigned int i;
|
||||
uint8_t *ptr;
|
||||
#if 0
|
||||
A_SEQUENCE_OF(void) *as_warn_area_ecgi = NULL;
|
||||
#endif
|
||||
|
||||
pdu = sbcap_pdu_alloc();
|
||||
if (!pdu)
|
||||
return NULL;
|
||||
pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage;
|
||||
pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Write_Replace_Warning;
|
||||
pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_reject;
|
||||
pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Write_Replace_Warning_Request;
|
||||
|
||||
A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Write_Replace_Warning_Request.protocolIEs.list;
|
||||
|
||||
/* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(5, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier);
|
||||
ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t));
|
||||
ie->value.choice.Message_Identifier.size = sizeof(uint16_t);
|
||||
ie->value.choice.Message_Identifier.bits_unused = 0;
|
||||
osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
/* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(11, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number);
|
||||
ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t));
|
||||
ie->value.choice.Serial_Number.size = sizeof(uint16_t);
|
||||
ie->value.choice.Serial_Number.bits_unused = 0;
|
||||
osmo_store16be(smscb->serial_nr, ie->value.choice.Serial_Number.buf);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
switch (cbcmsg->scope) {
|
||||
case CBC_MSG_SCOPE_PLMN:
|
||||
break; /* Nothing to be done :*/
|
||||
#if 0
|
||||
case CBC_MSG_SCOPE_EUTRAN_CGI:
|
||||
/* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(15, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
as_warn_area_ecgi = (void *)ie->value.choice.Warning_Area_List.choice.cell_ID_List.list;
|
||||
msgb_put_sbcap_cell_list(cbcmsg, as_warn_area_ecgi);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
/* static const long asn_VAL_5_SBcAP_id_Repetition_Period = 10; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(10, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Repetition_Period);
|
||||
ie->value.choice.Repetition_Period = cbcmsg->rep_period; /*seconds */
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
/* static const long asn_VAL_7_SBcAP_id_Number_of_Broadcasts_Requested = 7; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(7, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Number_of_Broadcasts_Requested);
|
||||
ie->value.choice.Number_of_Broadcasts_Requested = cbcmsg->num_bcast;
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
if (smscb->is_etws) {
|
||||
/* Warning Type, 3GPP TS 36.413 sec 9.2.1.50: */
|
||||
ie_warning_type = smscb->etws.warning_type;
|
||||
if (smscb->etws.user_alert)
|
||||
ie_warning_type |= 0x0100;
|
||||
if (smscb->etws.popup_on_display)
|
||||
ie_warning_type |= 0x0080;
|
||||
/* static const long asn_VAL_8_SBcAP_id_Warning_Type = 18; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(18, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Type);
|
||||
ie->value.choice.Warning_Type.buf = MALLOC(sizeof(ie_warning_type));
|
||||
ie->value.choice.Warning_Type.size = sizeof(ie_warning_type);
|
||||
memcpy(ie->value.choice.Warning_Type.buf, &ie_warning_type, sizeof(ie_warning_type));
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
/* Warning Security Information, 3GPP TS 36.413 sec 9.2.1.51: */
|
||||
/*static const long asn_VAL_9_SBcAP_id_Warning_Security_Information = 17 */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(17, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Security_Information);
|
||||
ie->value.choice.Warning_Security_Information.buf = MALLOC(sizeof(smscb->etws.warning_sec_info));
|
||||
ie->value.choice.Warning_Security_Information.size = sizeof(smscb->etws.warning_sec_info);
|
||||
memcpy(ie->value.choice.Warning_Security_Information.buf,
|
||||
smscb->etws.warning_sec_info, sizeof(smscb->etws.warning_sec_info));
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
} else {
|
||||
/* static const long asn_VAL_10_SBcAP_id_Data_Coding_Scheme = 3; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(3, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Data_Coding_Scheme);
|
||||
ie->value.choice.Data_Coding_Scheme.buf = MALLOC(1);
|
||||
ie->value.choice.Data_Coding_Scheme.size = 1;
|
||||
ie->value.choice.Data_Coding_Scheme.bits_unused = 0;
|
||||
*ie->value.choice.Data_Coding_Scheme.buf = smscb->cbs.dcs;
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
/* 3GPP TS 36.413 9.2.1.53 Warning Message Contents. Encoded as in S1AP. */
|
||||
/* static const long asn_VAL_11_SBcAP_id_Warning_Message_Content = 16; */
|
||||
ie = sbcap_alloc_Write_Replace_Warning_Request_IE(16, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Message_Content);
|
||||
ie->value.choice.Warning_Message_Content.buf = MALLOC(1 + smscb->cbs.num_pages * (SMSCB_RAW_PAGE_LEN+1));
|
||||
ie->value.choice.Warning_Message_Content.size = 1 + smscb->cbs.num_pages * (SMSCB_RAW_PAGE_LEN+1);
|
||||
ptr = &ie->value.choice.Warning_Message_Content.buf[0];
|
||||
*ptr = (uint8_t)smscb->cbs.num_pages;
|
||||
ptr++;
|
||||
for (i = 0; i < smscb->cbs.num_pages; i++) {
|
||||
unsigned len = 0;
|
||||
if (i == smscb->cbs.num_pages - 1)
|
||||
len = smscb->cbs.data_user_len - (i * SMSCB_RAW_PAGE_LEN);
|
||||
else
|
||||
len = SMSCB_RAW_PAGE_LEN;
|
||||
if (len > 0) {
|
||||
memcpy(ptr, smscb->cbs.data[i], SMSCB_RAW_PAGE_LEN);
|
||||
ptr += SMSCB_RAW_PAGE_LEN;
|
||||
}
|
||||
*ptr = (uint8_t)len;
|
||||
ptr++;
|
||||
}
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
/* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation */
|
||||
SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_message *cbcmsg)
|
||||
{
|
||||
const struct smscb_message *smscb = &cbcmsg->msg;
|
||||
SBcAP_SBC_AP_PDU_t *pdu;
|
||||
SBcAP_Stop_Warning_Request_IEs_t *ie;
|
||||
#if 0
|
||||
A_SEQUENCE_OF(void) *as_warn_area_ecgi = NULL;
|
||||
#endif
|
||||
|
||||
pdu = sbcap_pdu_alloc();
|
||||
if (!pdu)
|
||||
return NULL;
|
||||
pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage;
|
||||
pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Stop_Warning;
|
||||
pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_reject;
|
||||
pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Stop_Warning_Request;
|
||||
|
||||
A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Stop_Warning_Request.protocolIEs.list;
|
||||
|
||||
/* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
|
||||
ie = sbcap_alloc_Stop_Warning_Request_IE(5, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier);
|
||||
ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t));
|
||||
ie->value.choice.Message_Identifier.size = sizeof(uint16_t);
|
||||
ie->value.choice.Message_Identifier.bits_unused = 0;
|
||||
osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
/* static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */
|
||||
ie = sbcap_alloc_Stop_Warning_Request_IE(11, SBcAP_Criticality_reject,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number);
|
||||
ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t));
|
||||
ie->value.choice.Serial_Number.size = sizeof(uint16_t);
|
||||
ie->value.choice.Serial_Number.bits_unused = 0;
|
||||
osmo_store16be(smscb->serial_nr, ie->value.choice.Serial_Number.buf);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
|
||||
switch (cbcmsg->scope) {
|
||||
case CBC_MSG_SCOPE_PLMN:
|
||||
break; /* Nothing to be done :*/
|
||||
#if 0
|
||||
case CBC_MSG_SCOPE_EUTRAN_CGI:
|
||||
/* static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */
|
||||
ie = sbcap_alloc_Stop_Warning_Request_IE(15, SBcAP_Criticality_ignore,
|
||||
SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List);
|
||||
ASN_SEQUENCE_ADD(as_pdu, ie);
|
||||
as_warn_area_ecgi = (void *)ie->value.choice.Warning_Area_List.choice.cell_ID_List.list;
|
||||
msgb_put_sbcap_cell_list(cbcmsg, as_warn_area_ecgi);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/netif/sctp.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/sbcap/sbcap_common.h>
|
||||
|
||||
#include <osmocom/cbc/internal.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
|
||||
const char *sbcap_cbc_client_name(const struct osmo_sbcap_cbc_client *client)
|
||||
{
|
||||
struct osmo_fd *ofd;
|
||||
OSMO_ASSERT(client);
|
||||
|
||||
if (client->peer && client->peer->name) {
|
||||
return client->peer->name;
|
||||
}
|
||||
|
||||
ofd = osmo_stream_srv_get_ofd(client->conn);
|
||||
return osmo_sock_get_name2(ofd->fd);
|
||||
}
|
||||
|
||||
/* data from MME has arrived at CBC */
|
||||
static int sbcap_cbc_read_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct osmo_stream_srv_link *link = osmo_stream_srv_get_master(conn);
|
||||
struct osmo_sbcap_cbc_client *client = osmo_stream_srv_get_data(conn);
|
||||
struct osmo_sbcap_cbc *cbc = osmo_stream_srv_link_get_data(link);
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
||||
SBcAP_SBC_AP_PDU_t *pdu;
|
||||
struct msgb *msg = msgb_alloc_c(client, 1500, "SBcAP-rx");
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
int flags = 0;
|
||||
int rc;
|
||||
|
||||
/* read SBc-AP message from socket and process it */
|
||||
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
|
||||
NULL, NULL, &sinfo, &flags);
|
||||
LOGPSBCAPC(client, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n",
|
||||
__func__, rc, flags);
|
||||
if (rc < 0) {
|
||||
osmo_stream_srv_destroy(conn);
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
osmo_stream_srv_destroy(conn);
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
if (flags & MSG_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
||||
LOGPSBCAPC(client, LOGL_DEBUG, "Rx sctp notif %s\n",
|
||||
osmo_sctp_sn_type_str(notif->sn_header.sn_type));
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
osmo_stream_srv_destroy(conn);
|
||||
break;
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
LOGPSBCAPC(client, LOGL_DEBUG, "Rx sctp notif SCTP_ASSOC_CHANGE: %s\n",
|
||||
osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
goto out;
|
||||
|
||||
LOGPSBCAPC(client, LOGL_DEBUG, "Received SBc-AP %s\n", msgb_hexdump(msg));
|
||||
|
||||
/* decode + dispatch message */
|
||||
pdu = sbcap_decode(msg);
|
||||
if (pdu) {
|
||||
LOGPSBCAPC(client, LOGL_INFO, "Received SBc-AP %d\n",
|
||||
pdu->present);
|
||||
cbc->rx_cb(client, pdu);
|
||||
} else {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* connection from MME to CBC has been closed */
|
||||
static int sbcap_cbc_closed_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct osmo_sbcap_cbc_client *client = osmo_stream_srv_get_data(conn);
|
||||
LOGPSBCAPC(client, LOGL_NOTICE, "connection closed\n");
|
||||
|
||||
if (client->peer)
|
||||
client->peer->client.sbcap = NULL;
|
||||
client->conn = NULL;
|
||||
if (client->fi)
|
||||
osmo_fsm_inst_dispatch(client->fi, SBcAP_SRV_E_CMD_CLOSE, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new connection from MME has arrived at CBC */
|
||||
static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *link, int fd)
|
||||
{
|
||||
struct osmo_sbcap_cbc *cbc = osmo_stream_srv_link_get_data(link);
|
||||
struct osmo_sbcap_cbc_client *client = talloc_zero(cbc, struct osmo_sbcap_cbc_client);
|
||||
char remote_ip[INET6_ADDRSTRLEN], portbuf[6];
|
||||
int remote_port;
|
||||
OSMO_ASSERT(client);
|
||||
|
||||
remote_ip[0] = '\0';
|
||||
portbuf[0] = '\0';
|
||||
osmo_sock_get_ip_and_port(fd, remote_ip, sizeof(remote_ip), portbuf, sizeof(portbuf), false);
|
||||
remote_port = atoi(portbuf);
|
||||
|
||||
LOGP(DSBcAP, LOGL_NOTICE, "New SBc-AP client connection from %s:%u\n", remote_ip, remote_port);
|
||||
|
||||
client->conn = osmo_stream_srv_create(link, link, fd, sbcap_cbc_read_cb, sbcap_cbc_closed_cb, client);
|
||||
if (!client->conn) {
|
||||
LOGP(DSBcAP, LOGL_ERROR, "Unable to create stream server for %s:%d\n",
|
||||
remote_ip, remote_port);
|
||||
talloc_free(client);
|
||||
return -1;
|
||||
}
|
||||
client->fi = osmo_fsm_inst_alloc(&sbcap_server_fsm, client, client, LOGL_DEBUG, NULL);
|
||||
if (!client->fi) {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "Unable to allocate FSM\n");
|
||||
osmo_stream_srv_destroy(client->conn);
|
||||
talloc_free(client);
|
||||
return -1;
|
||||
}
|
||||
llist_add_tail(&client->list, &cbc->clients);
|
||||
|
||||
/* Match client to peer */
|
||||
client->peer = cbc_peer_by_addr_proto(remote_ip, remote_port, CBC_PEER_PROTO_SBcAP);
|
||||
if (!client->peer) {
|
||||
if (g_cbc->config.permit_unknown_peers) {
|
||||
LOGPSBCAPC(client, LOGL_NOTICE, "Accepting unknown SBc-AP peer %s:%d\n",
|
||||
remote_ip, remote_port);
|
||||
client->peer = cbc_peer_create(NULL, CBC_PEER_PROTO_SBcAP);
|
||||
OSMO_ASSERT(client->peer);
|
||||
client->peer->unknown_dynamic_peer = true;
|
||||
} else {
|
||||
LOGPSBCAPC(client, LOGL_NOTICE, "Rejecting unknown SBc-AP peer %s:%d (not permitted)\n",
|
||||
remote_ip, remote_port);
|
||||
osmo_stream_srv_destroy(client->conn);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (client->peer->client.sbcap) {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "We already have a connection for peer %s\n",
|
||||
client->peer->name);
|
||||
/* FIXME */
|
||||
}
|
||||
client->peer->client.sbcap = client;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(client->fi, SBcAP_SRV_E_CMD_RESET, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbcap_cbc_client_tx(struct osmo_sbcap_cbc_client *client, SBcAP_SBC_AP_PDU_t *pdu)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (!pdu) {
|
||||
LOGP(DSBcAP, LOGL_NOTICE, "Cannot transmit msg: no pdu\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
LOGP(DSBcAP, LOGL_NOTICE, "Cannot transmit msg: no connection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGPSBCAPC(client, LOGL_INFO, "Transmitting msg\n");
|
||||
OSMO_ASSERT(client->conn);
|
||||
msg = sbcap_encode(pdu);
|
||||
if (!msg)
|
||||
goto ret_free;
|
||||
LOGPSBCAPC(client, LOGL_DEBUG, "Encoded message: %s\n", msgb_hexdump(msg));
|
||||
osmo_stream_srv_send(client->conn, msg);
|
||||
ret_free:
|
||||
sbcap_pdu_free(pdu);
|
||||
}
|
||||
|
||||
void sbcap_cbc_client_close(struct osmo_sbcap_cbc_client *client)
|
||||
{
|
||||
osmo_stream_srv_destroy(client->conn);
|
||||
}
|
||||
|
||||
/* initialize the CBC-side SBc-AP server */
|
||||
struct osmo_sbcap_cbc *sbcap_cbc_create(void *ctx)
|
||||
{
|
||||
struct osmo_sbcap_cbc *cbc = talloc_zero(ctx, struct osmo_sbcap_cbc);
|
||||
int rc;
|
||||
int bind_port = g_cbc->config.sbcap.local_port;
|
||||
|
||||
if (bind_port == -1)
|
||||
bind_port = SBcAP_SCTP_PORT;
|
||||
|
||||
OSMO_ASSERT(cbc);
|
||||
cbc->rx_cb = sbcap_cbc_client_rx_cb;
|
||||
INIT_LLIST_HEAD(&cbc->clients);
|
||||
cbc->link = osmo_stream_srv_link_create(cbc);
|
||||
osmo_stream_srv_link_set_proto(cbc->link, IPPROTO_SCTP);
|
||||
osmo_stream_srv_link_set_data(cbc->link, cbc);
|
||||
osmo_stream_srv_link_set_nodelay(cbc->link, true);
|
||||
osmo_stream_srv_link_set_port(cbc->link, bind_port);
|
||||
osmo_stream_srv_link_set_addrs(cbc->link, (const char **)g_cbc->config.sbcap.local_host,
|
||||
g_cbc->config.sbcap.num_local_host);
|
||||
osmo_stream_srv_link_set_accept_cb(cbc->link, sbcap_cbc_accept_cb);
|
||||
rc = osmo_stream_srv_link_open(cbc->link);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
LOGP(DSBcAP, LOGL_NOTICE, "Listening for SBc-AP at %s\n",
|
||||
osmo_stream_srv_link_get_sockname(cbc->link));
|
||||
|
||||
return cbc;
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/sbcap/sbcap_common.h>
|
||||
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
#include <osmocom/cbc/internal.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
enum sbcap_server_state {
|
||||
/* initial state after client SCTP connection established */
|
||||
SBcAP_SRV_S_INIT,
|
||||
/* normal operation (idle) */
|
||||
SBcAP_SRV_S_IDLE,
|
||||
};
|
||||
|
||||
static const struct value_string sbcap_server_event_names[] = {
|
||||
{ SBcAP_SRV_E_RX_RST_COMPL, "Rx Reset Complete" },
|
||||
{ SBcAP_SRV_E_RX_RST_FAIL, "Rx Reset Failure" },
|
||||
{ SBcAP_SRV_E_RX_KA_COMPL, "Rx Keep-Alive Complete" },
|
||||
{ SBcAP_SRV_E_RX_RESTART, "Rx Restart" },
|
||||
{ SBcAP_SRV_E_CMD_RESET, "RESET.cmd" },
|
||||
{ SBcAP_SRV_E_CMD_CLOSE, "CLOSE.cmd" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static void sbcap_server_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case SBcAP_SRV_E_CMD_RESET:
|
||||
osmo_fsm_inst_state_chg(fi, SBcAP_SRV_S_IDLE, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void sbcap_server_s_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void sbcap_server_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct osmo_sbcap_cbc_client *client = (struct osmo_sbcap_cbc_client *) fi->priv;
|
||||
//SBcAP_SBC_AP_PDU_t *pdu;
|
||||
|
||||
switch (event) {
|
||||
case SBcAP_SRV_E_CMD_CLOSE:
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
break;
|
||||
case SBcAP_SRV_E_RX_RESTART:
|
||||
//pdu = data;
|
||||
/* TODO: delete any CBS state we have for this peer */
|
||||
/* TODO: re-send messages we have matching the scope of the peer */
|
||||
LOGPSBCAPC(client, LOGL_NOTICE, "RESTART but re-sending not implemented yet\n");
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void sbcap_server_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct osmo_sbcap_cbc_client *client = (struct osmo_sbcap_cbc_client *) fi->priv;
|
||||
|
||||
if (client->conn)
|
||||
osmo_stream_srv_destroy(client->conn);
|
||||
llist_del(&client->list);
|
||||
client->fi = NULL;
|
||||
|
||||
/* reparent the fsm_inst to the cbc as we're about to free() it's talloc
|
||||
* parent 'client' */
|
||||
talloc_steal(g_cbc, fi);
|
||||
talloc_free(client);
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state sbcap_server_fsm_states[] = {
|
||||
[SBcAP_SRV_S_INIT] = {
|
||||
.name = "INIT",
|
||||
.in_event_mask = S(SBcAP_SRV_E_CMD_RESET),
|
||||
.out_state_mask = S(SBcAP_SRV_S_IDLE),
|
||||
.action = sbcap_server_s_init,
|
||||
},
|
||||
[SBcAP_SRV_S_IDLE] = {
|
||||
.name = "IDLE",
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask = 0,
|
||||
.action = sbcap_server_s_idle,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm sbcap_server_fsm = {
|
||||
.name = "SBcAP-SERVER",
|
||||
.states = sbcap_server_fsm_states,
|
||||
.num_states = ARRAY_SIZE(sbcap_server_fsm_states),
|
||||
.allstate_event_mask = S(SBcAP_SRV_E_CMD_CLOSE) |
|
||||
S(SBcAP_SRV_E_RX_RESTART),
|
||||
.allstate_action = sbcap_server_fsm_allstate,
|
||||
.log_subsys = DSBcAP,
|
||||
.event_names = sbcap_server_event_names,
|
||||
.cleanup = sbcap_server_fsm_cleanup,
|
||||
};
|
||||
|
||||
static void *sbcap_as_find_ie(void *void_list, SBcAP_ProtocolIE_ID_t ie_id)
|
||||
{
|
||||
A_SEQUENCE_OF(SBcAP_ProtocolIE_ID_t) *li = (void *)void_list;
|
||||
int i;
|
||||
for (i = 0; i < li->count; i++) {
|
||||
/* "SBcAP_ProtocolIE_ID_t id" is first element in all *_IEs struct */
|
||||
SBcAP_ProtocolIE_ID_t *cur_ie_id = li->array[i];
|
||||
if (*cur_ie_id == ie_id) {
|
||||
return cur_ie_id;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SBcAP_Message_Identifier_t *get_msg_id_ie(struct osmo_sbcap_cbc_client *client,
|
||||
const SBcAP_SBC_AP_PDU_t *pdu)
|
||||
{
|
||||
A_SEQUENCE_OF(void) *as_pdu = NULL;
|
||||
/* static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */
|
||||
const SBcAP_ProtocolIE_ID_t msg_id_ie = 5;
|
||||
void *ie;
|
||||
|
||||
switch (pdu->present) {
|
||||
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
|
||||
switch (pdu->choice.initiatingMessage.procedureCode) {
|
||||
case SBcAP_ProcedureId_Write_Replace_Warning_Indication:
|
||||
as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Write_Replace_Warning_Indication.protocolIEs.list;
|
||||
if (!(ie = sbcap_as_find_ie(as_pdu, msg_id_ie)))
|
||||
return NULL;
|
||||
return &((SBcAP_Write_Replace_Warning_Indication_IEs_t *)ie)->value.choice.Message_Identifier;
|
||||
case SBcAP_ProcedureId_Stop_Warning_Indication:
|
||||
as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Stop_Warning_Indication.protocolIEs.list;
|
||||
if (!(ie = sbcap_as_find_ie(as_pdu, msg_id_ie)))
|
||||
return NULL;
|
||||
return &((SBcAP_Stop_Warning_Indication_IEs_t *)ie)->value.choice.Message_Identifier;
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "get_msg_id initiatingMessage procedure=%ld not implemented\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_successfulOutcome:
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
case SBcAP_ProcedureId_Write_Replace_Warning:
|
||||
as_pdu = (void *)&pdu->choice.successfulOutcome.value.choice.Write_Replace_Warning_Response.protocolIEs.list;
|
||||
if (!(ie = sbcap_as_find_ie(as_pdu, msg_id_ie)))
|
||||
return NULL;
|
||||
return &((SBcAP_Write_Replace_Warning_Response_IEs_t *)ie)->value.choice.Message_Identifier;
|
||||
case SBcAP_ProcedureId_Stop_Warning:
|
||||
as_pdu = (void *)&pdu->choice.successfulOutcome.value.choice.Stop_Warning_Response.protocolIEs.list;
|
||||
if (!(ie = sbcap_as_find_ie(as_pdu, msg_id_ie)))
|
||||
return NULL;
|
||||
return &((SBcAP_Stop_Warning_Response_IEs_t *)ie)->value.choice.Message_Identifier;
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "get_msg_id successfulOutcome procedure=%ld not implemented\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
|
||||
switch (pdu->choice.unsuccessfulOutcome.procedureCode) {
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "get_msg_id unsuccessfulOutcome procedure=%ld not implemented\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_msg_id(struct osmo_sbcap_cbc_client *client, const SBcAP_SBC_AP_PDU_t *pdu)
|
||||
{
|
||||
SBcAP_Message_Identifier_t *ie = get_msg_id_ie(client, pdu);
|
||||
if (!ie)
|
||||
return -1;
|
||||
if (ie->size != 2) {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "get_msg_id wrong size %ld\n", ie->size);
|
||||
return -1;
|
||||
}
|
||||
return osmo_load16be(ie->buf);
|
||||
}
|
||||
|
||||
/* message was received from remote SBcAP peer (BSC) */
|
||||
int sbcap_cbc_client_rx_cb(struct osmo_sbcap_cbc_client *client, SBcAP_SBC_AP_PDU_t *pdu)
|
||||
{
|
||||
struct cbc_message *smscb;
|
||||
struct cbc_message_peer *mp;
|
||||
int msg_id;
|
||||
|
||||
/* messages without reference to a specific SMSCB message */
|
||||
switch (pdu->present) {
|
||||
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
|
||||
switch (pdu->choice.initiatingMessage.procedureCode) {
|
||||
case SBcAP_ProcedureId_Write_Replace_Warning:
|
||||
case SBcAP_ProcedureId_Stop_Warning:
|
||||
LOGPSBCAPC(client, LOGL_ERROR,
|
||||
"SBcAP initiatingMessage procedure=%ld MME->CBC not expected\n",
|
||||
pdu->choice.initiatingMessage.procedureCode);
|
||||
return -EINVAL;
|
||||
case SBcAP_ProcedureId_PWS_Restart_Indication:
|
||||
return osmo_fsm_inst_dispatch(client->fi, SBcAP_SRV_E_RX_RESTART, pdu);
|
||||
case SBcAP_ProcedureId_Stop_Warning_Indication:
|
||||
case SBcAP_ProcedureId_Write_Replace_Warning_Indication:
|
||||
break; /* Handle msg id below */
|
||||
case SBcAP_ProcedureId_Error_Indication:
|
||||
case SBcAP_ProcedureId_PWS_Failure_Indication:
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "SBcAP initiatingMessage procedure=%ld not implemented?\n",
|
||||
pdu->choice.initiatingMessage.procedureCode);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_successfulOutcome:
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_INFO, "SBcAP SuccessfulOutcome procedure=%ld\n",
|
||||
pdu->choice.successfulOutcome.procedureCode);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
|
||||
switch (pdu->choice.unsuccessfulOutcome.procedureCode) {
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "SBcAP UnsuccessfulOutcome procedure=%ld\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_NOTHING:
|
||||
default:
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "Rx SBc-AP unexpected message type %d\n",
|
||||
pdu->present);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* messages with reference to a specific SMSCB message handled below*/
|
||||
msg_id = get_msg_id(client, pdu);
|
||||
OSMO_ASSERT(msg_id >= 0);
|
||||
|
||||
/* look-up smscb_message */
|
||||
smscb = cbc_message_by_id(msg_id);
|
||||
if (!smscb) {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "Rx SBc-AP msg for unknown message-id 0x%04x\n",
|
||||
msg_id);
|
||||
/* TODO: inform peer? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* look-up smscb_message_peer */
|
||||
mp = cbc_message_peer_get(smscb, client->peer);
|
||||
if (!mp) {
|
||||
LOGPSBCAPC(client, LOGL_ERROR, "Rx SBc-AP msg for message-id 0x%04x without peer %s\n",
|
||||
msg_id, client->peer->name);
|
||||
/* TODO: inform peer? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dispatch event to smscp_p_fms instance */
|
||||
switch (pdu->present) {
|
||||
case SBcAP_SBC_AP_PDU_PR_initiatingMessage:
|
||||
switch (pdu->choice.initiatingMessage.procedureCode) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_successfulOutcome:
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
case SBcAP_ProcedureId_Write_Replace_Warning:
|
||||
//if (dec->u.write_replace_compl.old_serial_nr)
|
||||
// return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBcAP_REPLACE_ACK, dec);
|
||||
//else
|
||||
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_WRITE_ACK, pdu);
|
||||
case SBcAP_ProcedureId_Stop_Warning:
|
||||
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_DELETE_ACK, pdu);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_unsuccessfulOutcome:
|
||||
switch (pdu->choice.unsuccessfulOutcome.procedureCode) {
|
||||
case SBcAP_ProcedureId_Stop_Warning:
|
||||
return osmo_fsm_inst_dispatch(mp->fi, SMSCB_E_SBCAP_DELETE_NACK, pdu);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SBcAP_SBC_AP_PDU_PR_NOTHING:
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_sbcap_srv_fsm(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&sbcap_server_fsm) == 0);
|
||||
}
|
|
@ -62,6 +62,8 @@ static void smscb_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event, v
|
|||
switch (event) {
|
||||
case SMSCB_E_CBSP_WRITE_ACK:
|
||||
case SMSCB_E_CBSP_WRITE_NACK:
|
||||
case SMSCB_E_SBCAP_WRITE_ACK:
|
||||
case SMSCB_E_SBCAP_WRITE_NACK:
|
||||
/* check if any per-peer children have not yet received the ACK or
|
||||
* timed out */
|
||||
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
|
||||
|
@ -179,6 +181,8 @@ static void smscb_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event,
|
|||
switch (event) {
|
||||
case SMSCB_E_CBSP_DELETE_ACK:
|
||||
case SMSCB_E_CBSP_DELETE_NACK:
|
||||
case SMSCB_E_SBCAP_DELETE_ACK:
|
||||
case SMSCB_E_SBCAP_DELETE_NACK:
|
||||
llist_for_each_entry(peer_fi, &fi->proc.children, proc.child) {
|
||||
if (peer_fi->state != SMSCB_S_DELETED)
|
||||
return;
|
||||
|
@ -225,7 +229,9 @@ static struct osmo_fsm_state smscb_fsm_states[] = {
|
|||
[SMSCB_S_WAIT_WRITE_ACK] = {
|
||||
.name = "WAIT_WRITE_ACK",
|
||||
.in_event_mask = S(SMSCB_E_CBSP_WRITE_ACK) |
|
||||
S(SMSCB_E_CBSP_WRITE_NACK),
|
||||
S(SMSCB_E_CBSP_WRITE_NACK) |
|
||||
S(SMSCB_E_SBCAP_WRITE_ACK) |
|
||||
S(SMSCB_E_SBCAP_WRITE_NACK),
|
||||
.out_state_mask = S(SMSCB_S_ACTIVE),
|
||||
.action = smscb_fsm_wait_write_ack,
|
||||
.onleave = smscb_fsm_wait_write_ack_onleave,
|
||||
|
@ -260,7 +266,9 @@ static struct osmo_fsm_state smscb_fsm_states[] = {
|
|||
[SMSCB_S_WAIT_DELETE_ACK] = {
|
||||
.name = "WAIT_DELETE_ACK",
|
||||
.in_event_mask = S(SMSCB_E_CBSP_DELETE_ACK) |
|
||||
S(SMSCB_E_CBSP_DELETE_NACK),
|
||||
S(SMSCB_E_CBSP_DELETE_NACK) |
|
||||
S(SMSCB_E_SBCAP_DELETE_ACK) |
|
||||
S(SMSCB_E_SBCAP_DELETE_NACK),
|
||||
.out_state_mask = S(SMSCB_S_DELETED),
|
||||
.action = smscb_fsm_wait_delete_ack,
|
||||
.onleave = smscb_fsm_wait_delete_ack_onleave,
|
||||
|
|
|
@ -32,8 +32,12 @@
|
|||
#include <osmocom/gsm/gsm0808_utils.h>
|
||||
#include <osmocom/gsm/cbsp.h>
|
||||
|
||||
#include <osmocom/sbcap/sbcap_common.h>
|
||||
|
||||
#include <osmocom/cbc/cbc_data.h>
|
||||
#include <osmocom/cbc/cbsp_server.h>
|
||||
#include <osmocom/cbc/sbcap_server.h>
|
||||
#include <osmocom/cbc/sbcap_msg.h>
|
||||
#include <osmocom/cbc/internal.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
@ -44,14 +48,18 @@ const struct value_string smscb_fsm_event_names[] = {
|
|||
{ SMSCB_E_REPLACE, "REPLACE" },
|
||||
{ SMSCB_E_STATUS, "STATUS" },
|
||||
{ SMSCB_E_DELETE, "DELETE" },
|
||||
{ SMSCB_E_CBSP_WRITE_ACK, "WRITE_ACK" },
|
||||
{ SMSCB_E_CBSP_WRITE_NACK, "WRITE_NACK" },
|
||||
{ SMSCB_E_CBSP_REPLACE_ACK, "REPLACE_ACK" },
|
||||
{ SMSCB_E_CBSP_REPLACE_NACK, "REPLACE_NACK" },
|
||||
{ SMSCB_E_CBSP_DELETE_ACK, "DELETE_ACK" },
|
||||
{ SMSCB_E_CBSP_DELETE_NACK, "DELETE_NACK" },
|
||||
{ SMSCB_E_CBSP_STATUS_ACK, "STATUS_ACK" },
|
||||
{ SMSCB_E_CBSP_STATUS_NACK, "STATUS_NACK" },
|
||||
{ SMSCB_E_CBSP_WRITE_ACK, "CBSP_WRITE_ACK" },
|
||||
{ SMSCB_E_CBSP_WRITE_NACK, "CBSP_WRITE_NACK" },
|
||||
{ SMSCB_E_CBSP_REPLACE_ACK, "CBSP_REPLACE_ACK" },
|
||||
{ SMSCB_E_CBSP_REPLACE_NACK, "CBSP_REPLACE_NACK" },
|
||||
{ SMSCB_E_CBSP_DELETE_ACK, "CBSP_DELETE_ACK" },
|
||||
{ SMSCB_E_CBSP_DELETE_NACK, "CBSP_DELETE_NACK" },
|
||||
{ SMSCB_E_CBSP_STATUS_ACK, "CBSP_STATUS_ACK" },
|
||||
{ SMSCB_E_CBSP_STATUS_NACK, "CBSP_STATUS_NACK" },
|
||||
{ SMSCB_E_SBCAP_WRITE_ACK, "SBcAP_WRITE_ACK" },
|
||||
{ SMSCB_E_SBCAP_WRITE_NACK, "SBcAP_WRITE_NACK" },
|
||||
{ SMSCB_E_SBCAP_DELETE_ACK, "SBcAP_DELETE_ACK" },
|
||||
{ SMSCB_E_SBCAP_DELETE_NACK, "SBcAP_DELETE_NACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
@ -300,6 +308,7 @@ static void smscb_p_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event,
|
|||
{
|
||||
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
|
||||
struct osmo_cbsp_decoded *dec = NULL;
|
||||
//SBcAP_SBC_AP_PDU_t *pdu = NULL;
|
||||
|
||||
switch (event) {
|
||||
case SMSCB_E_CBSP_WRITE_ACK:
|
||||
|
@ -319,6 +328,18 @@ static void smscb_p_fsm_wait_write_ack(struct osmo_fsm_inst *fi, uint32_t event,
|
|||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_WRITE_NACK, mp);
|
||||
break;
|
||||
case SMSCB_E_SBCAP_WRITE_ACK:
|
||||
//pdu = data;
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
|
||||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_WRITE_ACK, mp);
|
||||
break;
|
||||
case SMSCB_E_SBCAP_WRITE_NACK:
|
||||
//pdu = data;
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
|
||||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_WRITE_NACK, mp);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
@ -416,6 +437,7 @@ static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event
|
|||
{
|
||||
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
|
||||
struct osmo_cbsp_decoded *dec = NULL;
|
||||
//SBcAP_SBC_AP_PDU_t *pdu = NULL;
|
||||
|
||||
switch (event) {
|
||||
case SMSCB_E_CBSP_DELETE_ACK:
|
||||
|
@ -437,6 +459,18 @@ static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event
|
|||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_DELETE_NACK, mp);
|
||||
break;
|
||||
case SMSCB_E_SBCAP_DELETE_ACK:
|
||||
//pdu = data;
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
|
||||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_DELETE_ACK, mp);
|
||||
break;
|
||||
case SMSCB_E_SBCAP_DELETE_NACK:
|
||||
//pdu = data;
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
|
||||
/* Signal parent fsm about completion */
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_SBCAP_DELETE_NACK, mp);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
@ -446,10 +480,22 @@ static void smscb_p_fsm_wait_delete_ack(struct osmo_fsm_inst *fi, uint32_t event
|
|||
|
||||
static int smscb_p_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct cbc_message_peer *mp = (struct cbc_message_peer *)fi->priv;
|
||||
int ev;
|
||||
switch (fi->T) {
|
||||
case T_WAIT_WRITE_ACK:
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_WRITE_NACK, NULL);
|
||||
switch (mp->peer->proto) {
|
||||
case CBC_PEER_PROTO_CBSP:
|
||||
ev = SMSCB_E_CBSP_WRITE_NACK;
|
||||
break;
|
||||
case CBC_PEER_PROTO_SBcAP:
|
||||
ev = SMSCB_E_SBCAP_WRITE_NACK;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, ev, NULL);
|
||||
break;
|
||||
case T_WAIT_REPLACE_ACK:
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_ACTIVE, 0, 0);
|
||||
|
@ -461,7 +507,17 @@ static int smscb_p_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
|||
break;
|
||||
case T_WAIT_DELETE_ACK:
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_DELETED, 0, 0);
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, SMSCB_E_CBSP_DELETE_NACK, NULL);
|
||||
switch (mp->peer->proto) {
|
||||
case CBC_PEER_PROTO_CBSP:
|
||||
ev = SMSCB_E_CBSP_DELETE_NACK;
|
||||
break;
|
||||
case CBC_PEER_PROTO_SBcAP:
|
||||
ev = SMSCB_E_SBCAP_DELETE_NACK;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
osmo_fsm_inst_dispatch(fi->proc.parent, ev, NULL);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
|
@ -473,6 +529,7 @@ static void smscb_p_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
{
|
||||
struct cbc_message_peer *mp = (struct cbc_message_peer *) fi->priv;
|
||||
struct osmo_cbsp_decoded *cbsp;
|
||||
SBcAP_SBC_AP_PDU_t *sbcap;
|
||||
|
||||
switch (event) {
|
||||
case SMSCB_E_DELETE: /* send KILL to BSC */
|
||||
|
@ -485,19 +542,35 @@ static void smscb_p_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void
|
|||
default:
|
||||
break;
|
||||
}
|
||||
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_KILL);
|
||||
OSMO_ASSERT(cbsp);
|
||||
cbsp->u.kill.msg_id = mp->cbcmsg->msg.message_id;
|
||||
cbsp->u.kill.old_serial_nr = mp->cbcmsg->msg.serial_nr;
|
||||
/* TODO: we assume that the delete will always affect all original cells */
|
||||
cbsp_append_cell_list(&cbsp->u.kill.cell_list, cbsp, mp);
|
||||
if (!mp->cbcmsg->msg.is_etws) {
|
||||
/* Channel Indication IE is only present in CBS, not in ETWS! */
|
||||
cbsp->u.kill.channel_ind = talloc_zero(cbsp, enum cbsp_channel_ind);
|
||||
OSMO_ASSERT(cbsp->u.kill.channel_ind);
|
||||
*(cbsp->u.kill.channel_ind) = CBSP_CHAN_IND_BASIC;
|
||||
switch (mp->peer->proto) {
|
||||
case CBC_PEER_PROTO_CBSP:
|
||||
cbsp = osmo_cbsp_decoded_alloc(mp->peer, CBSP_MSGT_KILL);
|
||||
OSMO_ASSERT(cbsp);
|
||||
cbsp->u.kill.msg_id = mp->cbcmsg->msg.message_id;
|
||||
cbsp->u.kill.old_serial_nr = mp->cbcmsg->msg.serial_nr;
|
||||
/* TODO: we assume that the delete will always affect all original cells */
|
||||
cbsp_append_cell_list(&cbsp->u.kill.cell_list, cbsp, mp);
|
||||
if (!mp->cbcmsg->msg.is_etws) {
|
||||
/* Channel Indication IE is only present in CBS, not in ETWS! */
|
||||
cbsp->u.kill.channel_ind = talloc_zero(cbsp, enum cbsp_channel_ind);
|
||||
OSMO_ASSERT(cbsp->u.kill.channel_ind);
|
||||
*(cbsp->u.kill.channel_ind) = CBSP_CHAN_IND_BASIC;
|
||||
}
|
||||
cbsp_cbc_client_tx(mp->peer->client.cbsp, cbsp);
|
||||
break;
|
||||
case CBC_PEER_PROTO_SBcAP:
|
||||
if ((sbcap = sbcap_gen_stop_warning_req(mp->peer, mp->cbcmsg))) {
|
||||
sbcap_cbc_client_tx(mp->peer->client.sbcap, sbcap);
|
||||
} else {
|
||||
LOGP(DSBcAP, LOGL_ERROR,
|
||||
"[%s] Tx SBc-AP Stop-Warning-Request: msg gen failed\n",
|
||||
mp->peer->name);
|
||||
}
|
||||
break;
|
||||
case CBC_PEER_PROTO_SABP:
|
||||
default:
|
||||
osmo_panic("SMSCB_E_DELETE not implemented for proto %u", mp->peer->proto);
|
||||
}
|
||||
cbsp_cbc_client_tx(mp->peer->client.cbsp, cbsp);
|
||||
osmo_fsm_inst_state_chg(fi, SMSCB_S_WAIT_DELETE_ACK, 10, T_WAIT_DELETE_ACK);
|
||||
break;
|
||||
default:
|
||||
|
@ -522,7 +595,9 @@ static const struct osmo_fsm_state smscb_p_fsm_states[] = {
|
|||
[SMSCB_S_WAIT_WRITE_ACK] = {
|
||||
.name = "WAIT_WRITE_ACK",
|
||||
.in_event_mask = S(SMSCB_E_CBSP_WRITE_ACK) |
|
||||
S(SMSCB_E_CBSP_WRITE_NACK),
|
||||
S(SMSCB_E_CBSP_WRITE_NACK) |
|
||||
S(SMSCB_E_SBCAP_WRITE_ACK) |
|
||||
S(SMSCB_E_SBCAP_WRITE_NACK),
|
||||
.out_state_mask = S(SMSCB_S_ACTIVE) |
|
||||
S(SMSCB_S_WAIT_DELETE_ACK),
|
||||
.action = smscb_p_fsm_wait_write_ack,
|
||||
|
@ -555,7 +630,9 @@ static const struct osmo_fsm_state smscb_p_fsm_states[] = {
|
|||
[SMSCB_S_WAIT_DELETE_ACK] = {
|
||||
.name = "WAIT_DELETE_ACK",
|
||||
.in_event_mask = S(SMSCB_E_CBSP_DELETE_ACK) |
|
||||
S(SMSCB_E_CBSP_DELETE_NACK),
|
||||
S(SMSCB_E_CBSP_DELETE_NACK) |
|
||||
S(SMSCB_E_SBCAP_DELETE_ACK) |
|
||||
S(SMSCB_E_SBCAP_DELETE_NACK),
|
||||
.out_state_mask = S(SMSCB_S_DELETED),
|
||||
.action = smscb_p_fsm_wait_delete_ack,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue