pespin submitted this change.

View Change

Approvals: Jenkins Builder: Verified fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved
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
---
M doc/examples/osmo-cbc/osmo-cbc.cfg
M include/osmocom/cbc/cbc_peer.h
M include/osmocom/cbc/cbsp_link.h
M include/osmocom/cbc/sbcap_link.h
M src/cbc_main.c
M src/cbc_peer.c
M src/cbc_vty.c
M src/cbsp_link.c
M src/sbcap_link.c
9 files changed, 477 insertions(+), 32 deletions(-)

diff --git a/doc/examples/osmo-cbc/osmo-cbc.cfg b/doc/examples/osmo-cbc/osmo-cbc.cfg
index 994da8c..847b901 100644
--- a/doc/examples/osmo-cbc/osmo-cbc.cfg
+++ b/doc/examples/osmo-cbc/osmo-cbc.cfg
@@ -16,9 +16,11 @@
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
diff --git a/include/osmocom/cbc/cbc_peer.h b/include/osmocom/cbc/cbc_peer.h
index 785ea09..cf2a578 100644
--- a/include/osmocom/cbc/cbc_peer.h
+++ b/include/osmocom/cbc/cbc_peer.h
@@ -19,6 +19,16 @@
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_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 @@
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);
diff --git a/include/osmocom/cbc/cbsp_link.h b/include/osmocom/cbc/cbsp_link.h
index 9f964e8..a96162d 100644
--- a/include/osmocom/cbc/cbsp_link.h
+++ b/include/osmocom/cbc/cbsp_link.h
@@ -31,19 +31,22 @@
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);
diff --git a/include/osmocom/cbc/sbcap_link.h b/include/osmocom/cbc/sbcap_link.h
index da804a3..0c23710 100644
--- a/include/osmocom/cbc/sbcap_link.h
+++ b/include/osmocom/cbc/sbcap_link.h
@@ -33,15 +33,20 @@
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);
diff --git a/src/cbc_main.c b/src/cbc_main.c
index 4ad59b6..9373c41 100644
--- a/src/cbc_main.c
+++ b/src/cbc_main.c
@@ -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 @@
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;
diff --git a/src/cbc_peer.c b/src/cbc_peer.c
index e8b79f9..a277204 100644
--- a/src/cbc_peer.c
+++ b/src/cbc_peer.c
@@ -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 @@
{ 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 @@
}
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;
+}
diff --git a/src/cbc_vty.c b/src/cbc_vty.c
index 7e5a9db..5f02e46 100644
--- a/src/cbc_vty.c
+++ b/src/cbc_vty.c
@@ -593,6 +593,19 @@
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 @@
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 @@
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);
diff --git a/src/cbsp_link.c b/src/cbsp_link.c
index baf2def..343ac26 100644
--- a/src/cbsp_link.c
+++ b/src/cbsp_link.c
@@ -46,6 +46,7 @@
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 @@

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

/* 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 @@
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 @@
}
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 @@
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);
}

/*
diff --git a/src/sbcap_link.c b/src/sbcap_link.c
index b4f54c1..6459c58 100644
--- a/src/sbcap_link.c
+++ b/src/sbcap_link.c
@@ -50,6 +50,7 @@
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 @@
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 @@
}

/* 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 @@
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 @@
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 @@
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);
}

/*

To view, visit change 28732. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-cbc
Gerrit-Branch: master
Gerrit-Change-Id: I3ec54b615b41b56f7a9c64298e3fcaac37f4b60e
Gerrit-Change-Number: 28732
Gerrit-PatchSet: 5
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-MessageType: merged