pespin has submitted this change. (
https://gerrit.osmocom.org/c/osmo-cbc/+/28602 )
Change subject: Add initial SBc-AP support to osmo-cbc
......................................................................
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
---
M configure.ac
M contrib/osmo-cbc.spec.in
M debian/control
M doc/examples/osmo-cbc/osmo-cbc.cfg
M include/osmocom/cbc/Makefile.am
M include/osmocom/cbc/cbc_data.h
M include/osmocom/cbc/internal.h
A include/osmocom/cbc/sbcap_msg.h
A include/osmocom/cbc/sbcap_server.h
M src/Makefile.am
M src/cbc_data.c
M src/cbc_main.c
M src/cbc_vty.c
M src/message_handling.c
A src/sbcap_msg.c
A src/sbcap_server.c
A src/sbcap_server_fsm.c
M src/smscb_message_fsm.c
M src/smscb_peer_fsm.c
19 files changed, 1,317 insertions(+), 53 deletions(-)
Approvals:
fixeria: Looks good to me, but someone else must approve
pespin: Looks good to me, approved; Verified
osmith: Looks good to me, but someone else must approve
diff --git a/configure.ac b/configure.ac
index 2c910e7..4baced9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,6 +48,18 @@
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],
diff --git a/contrib/osmo-cbc.spec.in b/contrib/osmo-cbc.spec.in
index 4feada7..0a4fecf 100644
--- a/contrib/osmo-cbc.spec.in
+++ b/contrib/osmo-cbc.spec.in
@@ -30,6 +30,7 @@
%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
diff --git a/debian/control b/debian/control
index 7dbaa10..aeb021f 100644
--- a/debian/control
+++ b/debian/control
@@ -15,6 +15,7 @@
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
diff --git a/doc/examples/osmo-cbc/osmo-cbc.cfg b/doc/examples/osmo-cbc/osmo-cbc.cfg
index 0b66996..b2bce58 100644
--- a/doc/examples/osmo-cbc/osmo-cbc.cfg
+++ b/doc/examples/osmo-cbc/osmo-cbc.cfg
@@ -9,3 +9,7 @@
ecbe
local-ip 127.0.0.1
local-port 12345
+ sbcap
+ local-ip 127.0.0.1
+ local-ip ::1
+ local-port 6789
diff --git a/include/osmocom/cbc/Makefile.am b/include/osmocom/cbc/Makefile.am
index 1135800..d1cc46e 100644
--- a/include/osmocom/cbc/Makefile.am
+++ b/include/osmocom/cbc/Makefile.am
@@ -3,5 +3,7 @@
cbsp_server.h \
charset.h \
internal.h \
+ sbcap_msg.h \
+ sbcap_server.h \
rest_it_op.h \
$(NULL)
diff --git a/include/osmocom/cbc/cbc_data.h b/include/osmocom/cbc/cbc_data.h
index d6fc3d2..a90ff66 100644
--- a/include/osmocom/cbc/cbc_data.h
+++ b/include/osmocom/cbc/cbc_data.h
@@ -11,6 +11,8 @@
struct osmo_sabp_cbc_client;
struct rest_it_op;
+#define CBC_MAX_ADDRS 8
+
/*********************************************************************************
* CBC Peer
*********************************************************************************/
@@ -18,13 +20,15 @@
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 @@
union {
struct osmo_cbsp_cbc_client *cbsp;
struct osmo_sabp_cbc_client *sabp;
+ struct osmo_sbcap_cbc_client *sbcap;
} client;
};
@@ -171,6 +176,11 @@
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;
} ecbe;
@@ -197,4 +207,3 @@
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);
-
diff --git a/include/osmocom/cbc/internal.h b/include/osmocom/cbc/internal.h
index e3521bd..044387a 100644
--- a/include/osmocom/cbc/internal.h
+++ b/include/osmocom/cbc/internal.h
@@ -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 @@
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 @@
/* 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 {
diff --git a/include/osmocom/cbc/sbcap_msg.h b/include/osmocom/cbc/sbcap_msg.h
new file mode 100644
index 0000000..784938d
--- /dev/null
+++ b/include/osmocom/cbc/sbcap_msg.h
@@ -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);
diff --git a/include/osmocom/cbc/sbcap_server.h b/include/osmocom/cbc/sbcap_server.h
new file mode 100644
index 0000000..7b4b7e1
--- /dev/null
+++ b/include/osmocom/cbc/sbcap_server.h
@@ -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);
diff --git a/src/Makefile.am b/src/Makefile.am
index cc88962..5805fd7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@
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 @@
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:
diff --git a/src/cbc_data.c b/src/cbc_data.c
index 4b2c831..92da369 100644
--- a/src/cbc_data.c
+++ b/src/cbc_data.c
@@ -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 *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;
diff --git a/src/cbc_main.c b/src/cbc_main.c
index c49514b3..dd157ca 100644
--- a/src/cbc_main.c
+++ b/src/cbc_main.c
@@ -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 @@
.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 @@
.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(a)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 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 @@
}
}
-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 @@
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 @@
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");
diff --git a/src/cbc_vty.c b/src/cbc_vty.c
index b07bf70..5244c3e 100644
--- a/src/cbc_vty.c
+++ b/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 @@
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 @@
/* 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 @@
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 @@
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_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 @@
"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 @@
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 @@
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);
}
diff --git a/src/message_handling.c b/src/message_handling.c
index 41b854a..3344a75 100644
--- a/src/message_handling.c
+++ b/src/message_handling.c
@@ -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 @@
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 @@
}
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);
}
diff --git a/src/sbcap_msg.c b/src/sbcap_msg.c
new file mode 100644
index 0000000..6cb3a35
--- /dev/null
+++ b/src/sbcap_msg.c
@@ -0,0 +1,270 @@
+/* Osmocom CBC (Cell Broacast Centre) */
+
+/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin(a)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;
+}
diff --git a/src/sbcap_server.c b/src/sbcap_server.c
new file mode 100644
index 0000000..e111675
--- /dev/null
+++ b/src/sbcap_server.c
@@ -0,0 +1,250 @@
+/* (C) 2019 by Harald Welte <laforge(a)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;
+}
diff --git a/src/sbcap_server_fsm.c b/src/sbcap_server_fsm.c
new file mode 100644
index 0000000..18167bd
--- /dev/null
+++ b/src/sbcap_server_fsm.c
@@ -0,0 +1,330 @@
+/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin(a)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);
+}
diff --git a/src/smscb_message_fsm.c b/src/smscb_message_fsm.c
index 04b0718..27d31c2 100644
--- a/src/smscb_message_fsm.c
+++ b/src/smscb_message_fsm.c
@@ -62,6 +62,8 @@
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 @@
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 @@
[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 @@
[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,
diff --git a/src/smscb_peer_fsm.c b/src/smscb_peer_fsm.c
index cc91e39..c72a8b4 100644
--- a/src/smscb_peer_fsm.c
+++ b/src/smscb_peer_fsm.c
@@ -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 @@
{ 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 @@
{
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 @@
/* 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 @@
{
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 @@
/* 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 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 @@
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 @@
{
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 @@
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 @@
[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 @@
[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,
},
--
To view, visit
https://gerrit.osmocom.org/c/osmo-cbc/+/28602
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-cbc
Gerrit-Branch: master
Gerrit-Change-Id: Ib278bc1d1a74459814016fef7a8fe21cc29d46c9
Gerrit-Change-Number: 28602
Gerrit-PatchSet: 12
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: merged