Support CBSP/TCP and SBc-AP/SCTP client mode

This patch adds support to set up a CBSP link so that osmo-cbc connects
as a TCP client to the BSC, which runs as a TCP server.
Similary, support to set up a SBc-AP link so that osmo-cbc connects
as a SCTP client to the MME, which runs as SCTP server.

A new "mode (client|server|disabled)" VTY command is added to use one
mother or the other, and also to disable the link and hence the peer
until it is set again. This is useful if the peer is under manintenance
for instance.

client sockets are created automatically when the "peer" vty node is
exited if the link is not yet created, hence creating it at startup or
if moved back from "disabled" mode. If client socket dies, it will
keep attempting reconnect every 5 seconds.

Related: OS#4945
Change-Id: I3ec54b615b41b56f7a9c64298e3fcaac37f4b60e
This commit is contained in:
Pau Espin 2022-07-22 16:12:22 +02:00
parent 5044524f01
commit 3ef5020007
9 changed files with 477 additions and 32 deletions

View File

@ -16,9 +16,11 @@ cbc
local-ip ::1
local-port 29168
peer cbsp example-bsc
mode server
remote-ip 127.0.0.2
remote-port 48049
peer sbcap example-mme
mode client
remote-ip 127.0.0.2
remote-ip ::2
remote-port 29168

View File

@ -19,6 +19,16 @@ enum cbc_peer_protocol {
CBC_PEER_PROTO_SBcAP
};
enum cbc_peer_link_mode {
CBC_PEER_LINK_MODE_DISABLED = 0,
CBC_PEER_LINK_MODE_SERVER,
CBC_PEER_LINK_MODE_CLIENT,
};
extern const struct value_string cbc_peer_link_mode_names[];
static inline const char *cbc_peer_link_mode_name(enum cbc_peer_link_mode val)
{ return get_value_string(cbc_peer_link_mode_names, val); }
struct cbc_peer {
struct llist_head list; /* linked to cbc.peers */
const char *name;
@ -34,6 +44,7 @@ struct cbc_peer {
struct cbc_sabp_link *sabp;
struct cbc_sbcap_link *sbcap;
} link;
enum cbc_peer_link_mode link_mode;
};
extern const struct value_string cbc_peer_proto_name[];
@ -44,3 +55,4 @@ void cbc_peer_remove(struct cbc_peer *peer);
struct cbc_peer *cbc_peer_by_name(const char *name);
struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote_port,
enum cbc_peer_protocol proto);
int cbc_peer_apply_cfg_chg(struct cbc_peer *peer);

View File

@ -31,19 +31,22 @@ int cbc_cbsp_mgr_open_srv(struct cbc_cbsp_mgr *mgr);
struct cbc_cbsp_link {
/* entry in osmo_cbsp_cbc.links */
struct llist_head list;
/* stream server connection for this link */
struct osmo_stream_srv *conn;
/* partially received CBSP message (rx completion pending) */
struct msgb *rx_msg;
struct osmo_fsm_inst *fi;
struct cbc_peer *peer;
bool is_client;
union {
struct osmo_stream_srv *srv_conn;
struct osmo_stream_cli *cli_conn;
void *conn; /* used when we just care about the pointer */
};
};
struct cbc_cbsp_link *cbc_cbsp_link_alloc(struct cbc_cbsp_mgr *cbc, struct cbc_peer *peer);
void cbc_cbsp_link_free(struct cbc_cbsp_link *link);
const char *cbc_cbsp_link_name(const struct cbc_cbsp_link *link);
int cbc_cbsp_link_open_cli(struct cbc_cbsp_link *link);
void cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp);
void cbc_cbsp_link_close(struct cbc_cbsp_link *link);
int cbc_cbsp_link_rx_cb(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *dec);

View File

@ -33,15 +33,20 @@ int cbc_sbcap_mgr_open_srv(struct cbc_sbcap_mgr *mgr);
struct cbc_sbcap_link {
/* entry in osmo_sbcap_cbc.links */
struct llist_head list;
/* stream server connection for this link */
struct osmo_stream_srv *conn;
struct osmo_fsm_inst *fi;
struct cbc_peer *peer;
bool is_client;
union {
struct osmo_stream_srv *srv_conn;
struct osmo_stream_cli *cli_conn;
void *conn; /* used when we just care about the pointer */
};
};
struct cbc_sbcap_link *cbc_sbcap_link_alloc(struct cbc_sbcap_mgr *cbc, struct cbc_peer *peer);
void cbc_sbcap_link_free(struct cbc_sbcap_link *link);
const char *cbc_sbcap_link_name(const struct cbc_sbcap_link *link);
int cbc_sbcap_link_open_cli(struct cbc_sbcap_link *link);
void cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu);
void cbc_sbcap_link_close(struct cbc_sbcap_link *link);
int cbc_sbcap_link_rx_cb(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu);

View File

@ -47,6 +47,7 @@
#include <osmocom/cbc/debug.h>
#include <osmocom/cbc/cbc_data.h>
#include <osmocom/cbc/cbc_vty.h>
#include <osmocom/cbc/cbc_peer.h>
static void *tall_cbc_ctx;
struct cbc *g_cbc;
@ -99,6 +100,11 @@ static int cbc_vty_go_parent(struct vty *vty)
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
case PEER_NODE:
cbc_peer_apply_cfg_chg((struct cbc_peer *)vty->index);
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
default:
vty->node = CONFIG_NODE;
vty->index = NULL;

View File

@ -32,6 +32,7 @@
#include <osmocom/cbc/cbc_peer.h>
#include <osmocom/cbc/cbsp_link.h>
#include <osmocom/cbc/sbcap_link.h>
#include <osmocom/cbc/debug.h>
const struct value_string cbc_peer_proto_name[] = {
{ CBC_PEER_PROTO_CBSP, "CBSP" },
@ -40,6 +41,14 @@ const struct value_string cbc_peer_proto_name[] = {
{ 0, NULL }
};
const struct value_string cbc_peer_link_mode_names[] = {
{ CBC_PEER_LINK_MODE_DISABLED, "disabled" },
{ CBC_PEER_LINK_MODE_SERVER, "server" },
{ CBC_PEER_LINK_MODE_CLIENT, "client" },
{}
};
/* create a new cbc_peer */
struct cbc_peer *cbc_peer_create(const char *name, enum cbc_peer_protocol proto)
{
@ -120,3 +129,110 @@ struct cbc_peer *cbc_peer_by_addr_proto(const char *remote_host, uint16_t remote
}
return NULL;
}
static int cbc_peer_apply_cfg_chg_cbsp(struct cbc_peer *peer)
{
struct cbc_cbsp_link *link = peer->link.cbsp;
int rc = 0;
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
if (link) {
LOGPCC(link, LOGL_NOTICE,
"link mode changed to 'disabled', closing active link\n");
cbc_cbsp_link_close(link);
}
/* Nothing to be done, cbc_cbsp_mgr->srv_link will refuse
* accepting() disabled peers. */
OSMO_ASSERT(!peer->link.cbsp);
break;
case CBC_PEER_LINK_MODE_SERVER:
if (link && link->is_client) {
LOGPCC(link, LOGL_NOTICE,
"link mode changed 'client' -> 'server', closing active link\n");
cbc_cbsp_link_close(link);
}
/* Nothing to be done, cbc_cbsp_mgr->srv_link will accept() and
* recreate the link */
OSMO_ASSERT(!peer->link.cbsp);
break;
case CBC_PEER_LINK_MODE_CLIENT:
if (link) {
if (link->is_client) {
/* nothing to be done, cli link already created */
break;
}
LOGPCC(link, LOGL_NOTICE,
"link mode changed 'server' -> 'client', closing active link\n");
cbc_cbsp_link_close(link);
}
OSMO_ASSERT(!peer->link.cbsp);
link = cbc_cbsp_link_alloc(g_cbc->cbsp.mgr, peer);
peer->link.cbsp = link;
rc = cbc_cbsp_link_open_cli(link);
break;
}
return rc;
}
static int cbc_peer_apply_cfg_chg_sbcap(struct cbc_peer *peer)
{
struct cbc_sbcap_link *link = peer->link.sbcap;
int rc = 0;
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
if (link) {
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed to 'disabled', closing active link\n");
cbc_sbcap_link_close(link);
}
/* Nothing to be done, cbc_sbcap_mgr->srv_link will refuse
* accepting() disabled peers. */
OSMO_ASSERT(!peer->link.sbcap);
break;
case CBC_PEER_LINK_MODE_SERVER:
if (link && link->is_client) {
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed 'client' -> 'server', closing active link\n");
cbc_sbcap_link_close(link);
}
/* Nothing to be done, cbc_sbcap_mgr->srv_link will accept() and
* recreate the link */
OSMO_ASSERT(!peer->link.sbcap);
break;
case CBC_PEER_LINK_MODE_CLIENT:
if (link) {
if (link->is_client) {
/* nothing to be done, cli link already created */
break;
}
LOGPSBCAPC(link, LOGL_NOTICE,
"link mode changed 'server' -> 'client', closing active link\n");
cbc_sbcap_link_close(link);
}
OSMO_ASSERT(!peer->link.sbcap);
link = cbc_sbcap_link_alloc(g_cbc->sbcap.mgr, peer);
peer->link.sbcap = link;
rc = cbc_sbcap_link_open_cli(link);
break;
}
return rc;
}
int cbc_peer_apply_cfg_chg(struct cbc_peer *peer)
{
int rc = -ENOTSUP;
switch (peer->proto) {
case CBC_PEER_PROTO_CBSP:
rc = cbc_peer_apply_cfg_chg_cbsp(peer);
break;
case CBC_PEER_PROTO_SBcAP:
rc = cbc_peer_apply_cfg_chg_sbcap(peer);
break;
case CBC_PEER_PROTO_SABP:
break;
}
return rc;
}

View File

@ -593,6 +593,19 @@ DEFUN_DEPRECATED(cfg_peer_proto, cfg_peer_proto_cmd,
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_peer_mode, cfg_peer_mode_cmd,
"mode (server|client|disabled)",
"Connect to peer as TCP(CBSP)/SCTP(SBc-AP) server or client\n"
"server: listen for inbound TCP (CBSP) / SCTP (SBc-AP) connections from a remote peer\n"
"client: establish outbound TCP (CBSP) / SCTP (SBc-AP) connection to a remote peer\n"
"Disable CBSP link\n",
CMD_ATTR_NODE_EXIT)
{
struct cbc_peer *peer = (struct cbc_peer *) vty->index;
peer->link_mode = get_string_value(cbc_peer_link_mode_names, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_peer_remote_port, cfg_peer_remote_port_cmd,
"remote-port <0-65535>",
"Configure remote (TCP) port of peer\n"
@ -677,6 +690,7 @@ static void write_one_peer(struct vty *vty, struct cbc_peer *peer)
unsigned int i;
vty_out(vty, " peer %s %s%s", get_value_string(cbc_peer_proto_name_vty, peer->proto),
peer->name, VTY_NEWLINE);
vty_out(vty, " mode %s%s", cbc_peer_link_mode_name(peer->link_mode), VTY_NEWLINE);
if (peer->remote_port == -1)
vty_out(vty, " no remote-port%s", VTY_NEWLINE);
else
@ -729,6 +743,7 @@ void cbc_vty_init(void)
install_element(CBC_NODE, &cfg_cbc_no_peer_cmd);
install_node(&peer_node, NULL);
install_element(PEER_NODE, &cfg_peer_proto_cmd);
install_element(PEER_NODE, &cfg_peer_mode_cmd);
install_element(PEER_NODE, &cfg_peer_remote_port_cmd);
install_element(PEER_NODE, &cfg_peer_no_remote_port_cmd);
install_element(PEER_NODE, &cfg_peer_remote_ip_cmd);

View File

@ -46,6 +46,7 @@ struct cbc_cbsp_link *cbc_cbsp_link_alloc(struct cbc_cbsp_mgr *cbc, struct cbc_p
OSMO_ASSERT(link);
link->peer = peer;
link->is_client = (peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
link->fi = osmo_fsm_inst_alloc(&cbsp_link_fsm, link, link, LOGL_DEBUG, NULL);
if (!link->fi) {
@ -70,18 +71,122 @@ void cbc_cbsp_link_free(struct cbc_cbsp_link *link)
const char *cbc_cbsp_link_name(const struct cbc_cbsp_link *link)
{
struct osmo_fd *ofd;
OSMO_ASSERT(link);
if (link->peer && link->peer->name) {
if (link->peer && link->peer->name)
return link->peer->name;
} else {
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(link->conn);
return osmo_sock_get_name2(ofd->fd);
}
if (link->is_client)
ofd = osmo_stream_cli_get_ofd(link->cli_conn);
else
ofd = osmo_stream_srv_get_ofd(link->srv_conn);
return osmo_sock_get_name2(ofd->fd);
}
/*
* TCP client
*/
static int cbc_cbsp_link_cli_connect_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "Connected\n");
osmo_fsm_inst_dispatch(link->fi, CBSP_LINK_E_CMD_RESET, NULL);
return 0;
}
static int cbc_cbsp_link_cli_disconnect_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "Disconnected.\n");
LOGPCC(link, LOGL_NOTICE, "Reconnecting...\n");
osmo_stream_cli_reconnect(conn);
return 0;
}
static int cbc_cbsp_link_cli_read_cb(struct osmo_stream_cli *conn)
{
struct cbc_cbsp_link *link = osmo_stream_cli_get_data(conn);
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
struct osmo_cbsp_decoded *decoded;
struct msgb *msg = NULL;
int rc;
LOGPCC(link, LOGL_DEBUG, "read_cb rx_msg=%p\n", link->rx_msg);
/* message de-segmentation */
rc = osmo_cbsp_recv_buffered(conn, ofd->fd, &msg, &link->rx_msg);
if (rc <= 0) {
if (rc == -EAGAIN || rc == -EINTR) {
/* more data needs to be read */
return 0;
} else if (rc == -EPIPE || rc == -ECONNRESET) {
/* lost connection with server */
} else if (rc == 0) {
/* connection closed with server */
}
/* destroy connection */
cbc_cbsp_link_close(link);
return -EBADF;
}
OSMO_ASSERT(msg);
LOGPCC(link, LOGL_DEBUG, "Received CBSP %s\n", msgb_hexdump(msg));
/* decode + dispatch message */
decoded = osmo_cbsp_decode(link, msg);
if (decoded) {
LOGPCC(link, LOGL_INFO, "Received CBSP %s\n",
get_value_string(cbsp_msg_type_names, decoded->msg_type));
g_cbc->cbsp.mgr->rx_cb(link, decoded);
} else {
LOGPCC(link, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
}
msgb_free(msg);
return 0;
}
int cbc_cbsp_link_open_cli(struct cbc_cbsp_link *link)
{
struct osmo_stream_cli *conn;
struct cbc_peer *peer = link->peer;
int rc;
OSMO_ASSERT(link->is_client);
OSMO_ASSERT(peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
conn = osmo_stream_cli_create(link);
osmo_stream_cli_set_data(conn, link);
osmo_stream_cli_set_nodelay(conn, true);
osmo_stream_cli_set_reconnect_timeout(conn, 5);
osmo_stream_cli_set_proto(conn, IPPROTO_TCP);
osmo_stream_cli_set_connect_cb(conn, cbc_cbsp_link_cli_connect_cb);
osmo_stream_cli_set_disconnect_cb(conn, cbc_cbsp_link_cli_disconnect_cb);
osmo_stream_cli_set_read_cb(conn, cbc_cbsp_link_cli_read_cb);
rc = osmo_stream_cli_set_local_addrs(conn, (const char **)&g_cbc->config.cbsp.local_host, 1);
if (rc < 0)
goto free_ret;
/* We assign free local port for client links:
* osmo_stream_cli_set_local_port(conn, g_cbc->cbsp.local_port);
*/
OSMO_ASSERT(peer->num_remote_host > 0);
rc = osmo_stream_cli_set_addrs(conn, (const char **)peer->remote_host, peer->num_remote_host);
if (rc < 0)
goto free_ret;
osmo_stream_cli_set_port(conn, peer->remote_port);
rc = osmo_stream_cli_open(conn);
if (rc < 0)
goto free_ret;
link->cli_conn = conn;
return 0;
free_ret:
osmo_stream_cli_destroy(conn);
return rc;
}
/*
* TCP server
*/
/* data from BSC has arrived at CBC */
static int cbsp_cbc_read_cb(struct osmo_stream_srv *conn)
static int cbsp_cbc_srv_read_cb(struct osmo_stream_srv *conn)
{
struct osmo_stream_srv_link *srv_link = osmo_stream_srv_get_master(conn);
struct cbc_cbsp_link *link = osmo_stream_srv_get_data(conn);
@ -125,7 +230,7 @@ static int cbsp_cbc_read_cb(struct osmo_stream_srv *conn)
}
/* connection from BSC to CBC has been closed */
static int cbsp_cbc_closed_cb(struct osmo_stream_srv *conn)
static int cbsp_cbc_srv_closed_cb(struct osmo_stream_srv *conn)
{
struct cbc_cbsp_link *link = osmo_stream_srv_get_data(conn);
LOGPCC(link, LOGL_NOTICE, "connection closed\n");
@ -170,6 +275,23 @@ static int cbsp_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
peer = cbc_peer_create(NULL, CBC_PEER_PROTO_CBSP);
OSMO_ASSERT(peer);
peer->unknown_dynamic_peer = true;
} else { /* peer is known */
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
LOGP(DCBSP, LOGL_NOTICE,
"Rejecting conn for disabled CBSP peer %s:%d\n",
remote_ip, remote_port);
close(fd);
return -1;
case CBC_PEER_LINK_MODE_CLIENT:
LOGP(DCBSP, LOGL_NOTICE,
"Rejecting conn for CBSP peer %s:%d configured as 'client'\n",
remote_ip, remote_port);
close(fd);
return -1;
default: /* MODE_SERVER */
break;
}
}
if (peer->link.cbsp) {
LOGPCC(peer->link.cbsp, LOGL_ERROR,
@ -179,10 +301,11 @@ static int cbsp_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
}
link = cbc_cbsp_link_alloc(cbc, peer);
OSMO_ASSERT(link);
link->conn = osmo_stream_srv_create(srv_link, srv_link, fd,
cbsp_cbc_read_cb, cbsp_cbc_closed_cb,
link);
if (!link->conn) {
link->srv_conn = osmo_stream_srv_create(srv_link, srv_link, fd,
cbsp_cbc_srv_read_cb, cbsp_cbc_srv_closed_cb,
link);
if (!link->srv_conn) {
LOGPCC(link, LOGL_ERROR,
"Unable to create stream server for %s:%u\n",
remote_ip, remote_port);
@ -216,13 +339,21 @@ void cbc_cbsp_link_tx(struct cbc_cbsp_link *link, struct osmo_cbsp_decoded *cbsp
return;
}
talloc_free(cbsp);
osmo_stream_srv_send(link->conn, msg);
if (link->is_client)
osmo_stream_cli_send(link->cli_conn, msg);
else
osmo_stream_srv_send(link->srv_conn, msg);
}
void cbc_cbsp_link_close(struct cbc_cbsp_link *link)
{
if (link->conn)
osmo_stream_srv_destroy(link->conn);
if (!link->conn)
return;
if (link->is_client)
osmo_stream_cli_destroy(link->cli_conn);
else
osmo_stream_srv_destroy(link->srv_conn);
}
/*

View File

@ -50,6 +50,7 @@ struct cbc_sbcap_link *cbc_sbcap_link_alloc(struct cbc_sbcap_mgr *cbc, struct cb
OSMO_ASSERT(link);
link->peer = peer;
link->is_client = (peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
link->fi = osmo_fsm_inst_alloc(&sbcap_link_fsm, link, link, LOGL_DEBUG, NULL);
if (!link->fi) {
@ -77,16 +78,145 @@ const char *cbc_sbcap_link_name(const struct cbc_sbcap_link *link)
struct osmo_fd *ofd;
OSMO_ASSERT(link);
if (link->peer && link->peer->name) {
if (link->peer && link->peer->name)
return link->peer->name;
}
ofd = osmo_stream_srv_get_ofd(link->conn);
if (link->is_client)
ofd = osmo_stream_cli_get_ofd(link->cli_conn);
else
ofd = osmo_stream_srv_get_ofd(link->srv_conn);
return osmo_sock_get_name2(ofd->fd);
}
/*
* SCTP client
*/
static int cbc_sbcap_link_cli_connect_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "Connected\n");
osmo_fsm_inst_dispatch(link->fi, SBcAP_LINK_E_CMD_RESET, NULL);
return 0;
}
static int cbc_sbcap_link_cli_disconnect_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "Disconnected.\n");
LOGPSBCAPC(link, LOGL_NOTICE, "Reconnecting...\n");
osmo_stream_cli_reconnect(conn);
return 0;
}
static int cbc_sbcap_link_cli_read_cb(struct osmo_stream_cli *conn)
{
struct cbc_sbcap_link *link = osmo_stream_cli_get_data(conn);
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
SBcAP_SBC_AP_PDU_t *pdu;
struct msgb *msg = msgb_alloc_c(g_cbc, 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(link, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n",
__func__, rc, flags);
if (rc < 0) {
osmo_stream_cli_reconnect(conn);
goto out;
} else if (rc == 0) {
osmo_stream_cli_reconnect(conn);
} else {
msgb_put(msg, rc);
}
if (flags & MSG_NOTIFICATION) {
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
LOGPSBCAPC(link, 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_cli_reconnect(conn);
break;
case SCTP_ASSOC_CHANGE:
LOGPSBCAPC(link, LOGL_DEBUG, "Rx sctp notif SCTP_ASSOC_CHANGE: %s\n",
osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state));
break;
default:
LOGPSBCAPC(link, LOGL_DEBUG, "Rx sctp notif %s (%u)\n",
osmo_sctp_sn_type_str(notif->sn_header.sn_type),
notif->sn_header.sn_type);
break;
}
rc = 0;
}
if (rc == 0)
goto out;
LOGPSBCAPC(link, LOGL_DEBUG, "Received SBc-AP %s\n", msgb_hexdump(msg));
/* decode + dispatch message */
pdu = sbcap_decode(msg);
if (pdu) {
LOGPSBCAPC(link, LOGL_INFO, "Received SBc-AP %d\n",
pdu->present);
g_cbc->sbcap.mgr->rx_cb(link, pdu);
} else {
LOGPSBCAPC(link, LOGL_ERROR, "Unable to decode %s\n", msgb_hexdump(msg));
}
out:
msgb_free(msg);
return rc;
}
int cbc_sbcap_link_open_cli(struct cbc_sbcap_link *link)
{
struct osmo_stream_cli *conn;
struct cbc_peer *peer = link->peer;
int rc;
OSMO_ASSERT(link->is_client);
OSMO_ASSERT(peer->link_mode == CBC_PEER_LINK_MODE_CLIENT);
conn = osmo_stream_cli_create(link);
osmo_stream_cli_set_data(conn, link);
osmo_stream_cli_set_nodelay(conn, true);
osmo_stream_cli_set_reconnect_timeout(conn, 5);
osmo_stream_cli_set_proto(conn, IPPROTO_SCTP);
osmo_stream_cli_set_connect_cb(conn, cbc_sbcap_link_cli_connect_cb);
osmo_stream_cli_set_disconnect_cb(conn, cbc_sbcap_link_cli_disconnect_cb);
osmo_stream_cli_set_read_cb(conn, cbc_sbcap_link_cli_read_cb);
OSMO_ASSERT(g_cbc->config.sbcap.num_local_host > 0);
rc = osmo_stream_cli_set_local_addrs(conn, (const char **)&g_cbc->config.sbcap.local_host,
g_cbc->config.sbcap.num_local_host);
if (rc < 0)
goto free_ret;
/* We assign free local port for client links:
* osmo_stream_cli_set_local_port(conn, g_cbc->sbcap.local_port);
*/
OSMO_ASSERT(peer->num_remote_host > 0);
rc = osmo_stream_cli_set_addrs(conn, (const char **)peer->remote_host, peer->num_remote_host);
if (rc < 0)
goto free_ret;
osmo_stream_cli_set_port(conn, peer->remote_port);
rc = osmo_stream_cli_open(conn);
if (rc < 0)
goto free_ret;
link->cli_conn = conn;
return 0;
free_ret:
osmo_stream_cli_destroy(conn);
return rc;
}
/*
* SCTP server
*/
/* data from MME has arrived at CBC */
static int sbcap_cbc_read_cb(struct osmo_stream_srv *conn)
static int sbcap_cbc_srv_read_cb(struct osmo_stream_srv *conn)
{
struct osmo_stream_srv_link *srv_link = osmo_stream_srv_get_master(conn);
struct cbc_sbcap_link *link = osmo_stream_srv_get_data(conn);
@ -153,7 +283,7 @@ out:
}
/* connection from MME to CBC has been closed */
static int sbcap_cbc_closed_cb(struct osmo_stream_srv *conn)
static int sbcap_cbc_srv_closed_cb(struct osmo_stream_srv *conn)
{
struct cbc_sbcap_link *link = osmo_stream_srv_get_data(conn);
LOGPSBCAPC(link, LOGL_NOTICE, "connection closed\n");
@ -198,6 +328,23 @@ static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
peer = cbc_peer_create(NULL, CBC_PEER_PROTO_SBcAP);
OSMO_ASSERT(peer);
peer->unknown_dynamic_peer = true;
} else { /* peer is known */
switch (peer->link_mode) {
case CBC_PEER_LINK_MODE_DISABLED:
LOGP(DSBcAP, LOGL_NOTICE,
"Rejecting conn for disabled SBc-AP peer %s:%d\n",
remote_ip, remote_port);
close(fd);
return -1;
case CBC_PEER_LINK_MODE_CLIENT:
LOGP(DSBcAP, LOGL_NOTICE,
"Rejecting conn for SBc-AP peer %s:%d configured as 'client'\n",
remote_ip, remote_port);
close(fd);
return -1;
default: /* MODE_SERVER */
break;
}
}
if (peer->link.sbcap) {
LOGPSBCAPC(peer->link.sbcap, LOGL_ERROR,
@ -208,10 +355,10 @@ static int sbcap_cbc_accept_cb(struct osmo_stream_srv_link *srv_link, int fd)
link = cbc_sbcap_link_alloc(cbc, peer);
OSMO_ASSERT(link);
link->conn = osmo_stream_srv_create(srv_link, srv_link, fd,
sbcap_cbc_read_cb, sbcap_cbc_closed_cb,
link);
if (!link->conn) {
link->srv_conn = osmo_stream_srv_create(srv_link, srv_link, fd,
sbcap_cbc_srv_read_cb, sbcap_cbc_srv_closed_cb,
link);
if (!link->srv_conn) {
LOGPSBCAPC(link, LOGL_ERROR,
"Unable to create stream server for %s:%u\n",
remote_ip, remote_port);
@ -244,15 +391,23 @@ void cbc_sbcap_link_tx(struct cbc_sbcap_link *link, SBcAP_SBC_AP_PDU_t *pdu)
if (!msg)
goto ret_free;
LOGPSBCAPC(link, LOGL_DEBUG, "Encoded message: %s\n", msgb_hexdump(msg));
osmo_stream_srv_send(link->conn, msg);
if (link->is_client)
osmo_stream_cli_send(link->cli_conn, msg);
else
osmo_stream_srv_send(link->srv_conn, msg);
ret_free:
sbcap_pdu_free(pdu);
}
void cbc_sbcap_link_close(struct cbc_sbcap_link *link)
{
if (link->conn)
osmo_stream_srv_destroy(link->conn);
if (!link->conn)
return;
if (link->is_client)
osmo_stream_cli_destroy(link->cli_conn);
else
osmo_stream_srv_destroy(link->srv_conn);
}
/*