pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-cbc/+/28732 )
Change subject: WIP: support TCP/SCTP clients ......................................................................
WIP: support TCP/SCTP clients
Change-Id: I3ec54b615b41b56f7a9c64298e3fcaac37f4b60e --- 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 8 files changed, 473 insertions(+), 32 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-cbc refs/changes/32/28732/1
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 7fcacdf..ad5fa71 100644 --- a/include/osmocom/cbc/cbsp_link.h +++ b/include/osmocom/cbc/cbsp_link.h @@ -30,19 +30,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 5946544..6185e3d 100644 --- a/include/osmocom/cbc/sbcap_link.h +++ b/include/osmocom/cbc/sbcap_link.h @@ -32,15 +32,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 cd80e31..4198383 100644 --- a/src/cbc_main.c +++ b/src/cbc_main.c @@ -50,6 +50,7 @@ #include <osmocom/cbc/sbcap_link.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; @@ -95,6 +96,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; @@ -276,6 +282,7 @@ exit(1); }
+ /* TODO: split create into create and open_server */ if (!(g_cbc->cbsp.mgr = cbc_cbsp_mgr_create(tall_cbc_ctx))) { perror("Error binding CBSP port"); exit(1); diff --git a/src/cbc_peer.c b/src/cbc_peer.c index e8b79f9..e8e730d 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..b8e8fc3 100644 --- a/src/cbc_vty.c +++ b/src/cbc_vty.c @@ -593,6 +593,18 @@ return CMD_SUCCESS; }
+DEFUN(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") +{ + 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 +689,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 +742,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 cd1c308..94cf4cc 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,121 @@
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"); + 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 +229,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 +274,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 +300,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 +338,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); }
/* initialize the CBC-side CBSP server */ diff --git a/src/sbcap_link.c b/src/sbcap_link.c index 9194509..b6f5415 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,144 @@ 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"); + 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 +282,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 +327,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 +354,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 +390,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); }
/* initialize the CBC-side SBc-AP server */