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
changes/02/28602/12
Pau Espin 6 months ago committed by pespin
parent ac2f4037bc
commit 6e59534d6b
  1. 12
      configure.ac
  2. 1
      contrib/osmo-cbc.spec.in
  3. 1
      debian/control
  4. 4
      doc/examples/osmo-cbc/osmo-cbc.cfg
  5. 2
      include/osmocom/cbc/Makefile.am
  6. 13
      include/osmocom/cbc/cbc_data.h
  7. 26
      include/osmocom/cbc/internal.h
  8. 11
      include/osmocom/cbc/sbcap_msg.h
  9. 47
      include/osmocom/cbc/sbcap_server.h
  10. 7
      src/Makefile.am
  11. 18
      src/cbc_data.c
  12. 41
      src/cbc_main.c
  13. 181
      src/cbc_vty.c
  14. 21
      src/message_handling.c
  15. 270
      src/sbcap_msg.c
  16. 250
      src/sbcap_server.c
  17. 330
      src/sbcap_server_fsm.c
  18. 12
      src/smscb_message_fsm.c
  19. 125
      src/smscb_peer_fsm.c

@ -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

1
debian/control vendored

@ -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");

@ -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 <