pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-remsim/+/39033?usp=email )
Change subject: server: Implement IPA server with osmo_stream ......................................................................
server: Implement IPA server with osmo_stream
libosmo-abis is still needed since we are still using ipa_keepalive_fsm from there. This needs to be imported here or moved to libosmo-netif.
Change-Id: I6341612e41a0005de85f45fd6454bb954becb69c --- M src/server/rspro_server.c M src/server/rspro_server.h 2 files changed, 136 insertions(+), 54 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-remsim refs/changes/33/39033/1
diff --git a/src/server/rspro_server.c b/src/server/rspro_server.c index 5803c8a..730e519 100644 --- a/src/server/rspro_server.c +++ b/src/server/rspro_server.c @@ -2,6 +2,7 @@ #include <string.h> #include <unistd.h> #include <pthread.h> +#include <errno.h>
#include <osmocom/core/linuxlist.h> #include <osmocom/core/select.h> @@ -9,6 +10,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/socket.h> #include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/netif/ipa.h> #include <osmocom/abis/ipa.h>
#include <osmocom/rspro/RsproPDU.h> @@ -60,7 +62,7 @@ } ipa_prepend_header_ext(msg_tx, IPAC_PROTO_EXT_RSPRO); ipa_prepend_header(msg_tx, IPAC_PROTO_OSMO); - ipa_server_conn_send(conn->peer, msg_tx); + osmo_stream_srv_send(conn->peer, msg_tx); }
@@ -127,9 +129,10 @@ RsproPDU_t *resp = NULL; char ip_str[INET6_ADDRSTRLEN]; char port_str[6]; + int fd = osmo_stream_srv_get_fd(conn->peer);
/* remote IP and port */ - osmo_sock_get_ip_and_port(conn->peer->ofd.fd, ip_str, sizeof(ip_str), + osmo_sock_get_ip_and_port(fd, ip_str, sizeof(ip_str), port_str, sizeof(port_str), false);
switch (event) { @@ -174,9 +177,14 @@ * timeout, or to continue to operate. If we were to drop the old * connection, this could interrupt a perfectly working connection and opens * some kind of DoS. */ + char prev_ip_str[INET6_ADDRSTRLEN]; + char prev_port_str[6]; + int prev_fd = osmo_stream_srv_get_fd(previous_conn->peer); + osmo_sock_get_ip_and_port(prev_fd, prev_ip_str, sizeof(prev_ip_str), + prev_port_str, sizeof(prev_port_str), false); LOGPFSML(fi, LOGL_ERROR, "New client connection from %s:%s, but we already " - "have a connection from %s:%u. Dropping new connection.\n", - ip_str, port_str, previous_conn->peer->addr, previous_conn->peer->port); + "have a connection from %s:%s. Dropping new connection.\n", + ip_str, port_str, prev_ip_str, prev_port_str); resp = rspro_gen_ConnectClientRes(&conn->srv->comp_id, ResultCode_identityInUse); client_conn_send(conn, resp); osmo_fsm_inst_state_chg(fi, CLNTC_ST_REJECTED, 1, 2); @@ -218,14 +226,19 @@ /* check for unique-ness */ previous_conn = bankd_conn_by_id(conn->srv, conn->bank.bank_id); if (previous_conn && previous_conn != conn) { + char prev_ip_str[INET6_ADDRSTRLEN]; + char prev_port_str[6]; + int prev_fd = osmo_stream_srv_get_fd(previous_conn->peer); + osmo_sock_get_ip_and_port(prev_fd, prev_ip_str, sizeof(prev_ip_str), + prev_port_str, sizeof(prev_port_str), false); /* we're dropping the current (new) connection as we don't really know which * is the "right" one. Dropping the new gives the old connection time to * timeout, or to continue to operate. If we were to drop the old * connection, this could interrupt a perfectly working connection and opens * some kind of DoS. */ LOGPFSML(fi, LOGL_ERROR, "New bankd connection from %s:%s, but we already " - "have a connection from %s:%u. Dropping new connection.\n", - ip_str, port_str, previous_conn->peer->addr, previous_conn->peer->port); + "have a connection from %s:%s. Dropping new connection.\n", + ip_str, port_str, prev_ip_str, prev_port_str); resp = rspro_gen_ConnectBankRes(&conn->srv->comp_id, ResultCode_identityInUse); client_conn_send(conn, resp); osmo_fsm_inst_state_chg(fi, CLNTC_ST_REJECTED, 1, 2); @@ -307,8 +320,9 @@ bankd_port = 0; } else { /* obtain IP and port of bankd */ - rc = osmo_sock_get_ip_and_port(bankd_conn->peer->ofd.fd, ip_str, sizeof(ip_str), - port_str, sizeof(port_str), false); + rc = osmo_sock_get_ip_and_port(osmo_stream_srv_get_fd(bankd_conn->peer), + ip_str, sizeof(ip_str), + port_str, sizeof(port_str), false); if (rc < 0) { LOGPFSML(bankd_conn->fi, LOGL_ERROR, "Error during getpeername\n"); return; @@ -636,64 +650,121 @@ return 0; }
-/* data was received from one of the client connections to the RSPRO socket */ -static int sock_read_cb(struct ipa_server_conn *peer, struct msgb *msg) +static int _ipa_srv_conn_ccm(struct rspro_client_conn *conn, struct msgb *msg) { - struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; - struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); - struct rspro_client_conn *conn = peer->data; + struct tlv_parsed tlvp; + uint8_t msg_type; + struct ipaccess_unit unit_data = {}; + char *unitid; + int len, ret; + + OSMO_ASSERT(msgb_l2len(msg) > 0); + msg_type = msg->l2h[0]; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = ipa_ccm_send_pong(osmo_stream_srv_get_fd(conn->peer)); + if (ret < 0) { + LOGPFSML(conn->fi, LOGL_ERROR, "Cannot send PONG message. Reason: %s\n", strerror(errno)); + goto err; + } + return 0; + case IPAC_MSGT_PONG: + LOGPFSML(conn->fi, LOGL_DEBUG, "PONG!\n"); + ipa_keepalive_fsm_pong_received(conn->keepalive_fi); + return 0; + case IPAC_MSGT_ID_ACK: + LOGPFSML(conn->fi, LOGL_DEBUG, "ID_ACK? -> ACK!\n"); + ret = ipa_ccm_send_id_ack(osmo_stream_srv_get_fd(conn->peer)); + if (ret < 0) { + LOGPFSML(conn->fi, LOGL_ERROR, "Cannot send ID_ACK message. Reason: %s\n", strerror(errno)); + goto err; + } + return 0; + case IPAC_MSGT_ID_RESP: + ret = ipa_ccm_id_resp_parse(&tlvp, (const uint8_t *)msg->l2h+1, msgb_l2len(msg)-1); + if (ret < 0) { + LOGPFSML(conn->fi, LOGL_ERROR, "IPA CCM RESPonse with malformed TLVs\n"); + goto err; + } + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { + LOGPFSML(conn->fi, LOGL_ERROR, "IPA CCM RESP without unit ID\n"); + goto err; + } + len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT); + if (len < 1) { + LOGPFSML(conn->fi, LOGL_ERROR, "IPA CCM RESP with short unit ID\n"); + goto err; + } + unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT); + unitid[len-1] = '\0'; + ipa_parse_unitid(unitid, &unit_data); + break; + default: + LOGPFSML(conn->fi, LOGL_ERROR, "Unknown IPA message type\n"); + goto err; + } + + return 0; +err: + /* Connection and msgb destroyed by parent. */ + return -1; +} + +/* data was received from one of the client connections to the RSPRO socket */ +static int sock_read_cb(struct osmo_stream_srv *peer, int res, struct msgb *msg) +{ + enum ipaccess_proto ipa_proto = osmo_ipa_msgb_cb_proto(msg); + struct rspro_client_conn *conn = osmo_stream_srv_get_data(peer); RsproPDU_t *pdu; int rc;
- if (msgb_length(msg) < sizeof(*hh)) - goto invalid; - msg->l2h = &hh->data[0]; - switch (hh->proto) { + if (res <= 0) { + LOGPFSML(conn->fi, LOGL_NOTICE, "failed reading from socket: %d\n", res); + goto err; + } + + switch (ipa_proto) { case IPAC_PROTO_IPACCESS: - rc = ipa_server_conn_ccm(peer, msg); + rc = _ipa_srv_conn_ccm(conn, msg); if (rc < 0) - break; - switch (hh->data[0]) { - case IPAC_MSGT_PONG: - ipa_keepalive_fsm_pong_received(conn->keepalive_fi); - rc = 0; - break; - default: - break; - } + goto err; break; case IPAC_PROTO_OSMO: - if (!he || msgb_l2len(msg)< sizeof(*he)) - goto invalid; - msg->l2h = &he->data[0]; - - switch (he->proto) { + switch (osmo_ipa_msgb_cb_proto_ext(msg)) { case IPAC_PROTO_EXT_RSPRO: pdu = rspro_dec_msg(msg); - if (!pdu) - goto invalid; - + if (!pdu) { + rc = -EIO; + break; + } rc = handle_rx_rspro(conn, pdu); ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu); break; default: - goto invalid; + LOGPFSML(conn->fi, LOGL_ERROR, "Rx unexpected ipa proto ext: %d\n", + osmo_ipa_msgb_cb_proto_ext(msg)); + goto err; } break; default: - goto invalid; + LOGPFSML(conn->fi, LOGL_ERROR, "Rx unexpected ipa proto: %d\n", ipa_proto); + goto err; } + msgb_free(msg); return rc;
-invalid: +err: msgb_free(msg); - return -1; + osmo_stream_srv_destroy(peer); + return -EBADF; }
-static int sock_closed_cb(struct ipa_server_conn *peer) +static int sock_closed_cb(struct osmo_stream_srv *peer) { - struct rspro_client_conn *conn = peer->data; + struct rspro_client_conn *conn = osmo_stream_srv_get_data(peer); + osmo_stream_srv_set_data(peer, NULL); if (conn->fi) osmo_fsm_inst_dispatch(conn->fi, CLNTC_E_TCP_DOWN, NULL); /* FIXME: who cleans up conn? */ @@ -707,9 +778,9 @@ };
/* a new TCP connection was accepted on the RSPRO server socket */ -static int accept_cb(struct ipa_server_link *link, int fd) +static int accept_cb(struct osmo_stream_srv_link *link, int fd) { - struct rspro_server *srv = link->data; + struct rspro_server *srv = osmo_stream_srv_link_get_data(link); struct rspro_client_conn *conn;
conn = talloc_zero(srv, struct rspro_client_conn); @@ -717,9 +788,12 @@
conn->srv = srv; /* don't allocate peer under 'conn', as it must survive 'conn' during teardown */ - conn->peer = ipa_server_conn_create(link, link, fd, sock_read_cb, sock_closed_cb, conn); + conn->peer = osmo_stream_srv_create2(link, link, fd, conn); if (!conn->peer) goto out_err; + osmo_stream_srv_set_read_cb(conn->peer, sock_read_cb); + osmo_stream_srv_set_closed_cb(conn->peer, sock_closed_cb); + osmo_stream_srv_set_segmentation_cb(conn->peer, osmo_ipa_segmentation_cb);
/* don't allocate 'fi' as slave from 'conn', as 'fi' needs to survive 'conn' during * teardown */ @@ -728,7 +802,7 @@ goto out_err_conn;
/* use ipa_keepalive_fsm to periodically send an IPA_PING and expect a PONG in response */ - conn->keepalive_fi = ipa_server_conn_alloc_keepalive_fsm(conn->peer, &ka_params, NULL); + conn->keepalive_fi = ipa_generic_conn_alloc_keepalive_fsm(conn->peer, conn->peer, &ka_params, NULL); if (!conn->keepalive_fi) goto out_err_fi; /* ensure parent is notified once keepalive FSM instance is dying */ @@ -751,7 +825,7 @@ out_err_fi: osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_ERROR, NULL); out_err_conn: - ipa_server_conn_destroy(conn->peer); + osmo_stream_srv_destroy(conn->peer); /* the above will free 'conn' down the chain */ return -1; out_err: @@ -824,7 +898,7 @@ static void rspro_client_conn_destroy(struct rspro_client_conn *conn) { /* this will internally call closed_cb() which will dispatch a TCP_DOWN event */ - ipa_server_conn_destroy(conn->peer); + osmo_stream_srv_destroy(conn->peer); conn->peer = NULL;
/* ensure all slotmaps are unlinked + returned to NEW or deleted */ @@ -854,18 +928,26 @@ INIT_LLIST_HEAD(&srv->banks); pthread_rwlock_unlock(&srv->rwlock);
- srv->link = ipa_server_link_create(ctx, NULL, host, port, accept_cb, srv); + srv->link = osmo_stream_srv_link_create(ctx); if (!srv->link) goto out_free;
- rc = ipa_server_link_open(srv->link); + osmo_stream_srv_link_set_proto(srv->link, IPPROTO_TCP); + osmo_stream_srv_link_set_addr(srv->link, host); + osmo_stream_srv_link_set_port(srv->link, port); + osmo_stream_srv_link_set_data(srv->link, srv); + osmo_stream_srv_link_set_nodelay(srv->link, true); + osmo_stream_srv_link_set_accept_cb(srv->link, accept_cb); + + + rc = osmo_stream_srv_link_open(srv->link); if (rc < 0) goto out_destroy;
return srv;
out_destroy: - ipa_server_link_destroy(srv->link); + osmo_stream_srv_link_destroy(srv->link); out_free: pthread_rwlock_destroy(&srv->rwlock); talloc_free(srv); @@ -877,7 +959,7 @@ { /* FIXME: clear all lists */
- ipa_server_link_destroy(srv->link); + osmo_stream_srv_link_destroy(srv->link); srv->link = NULL; pthread_rwlock_destroy(&srv->rwlock); talloc_free(srv); diff --git a/src/server/rspro_server.h b/src/server/rspro_server.h index 6dd498e..4843821 100644 --- a/src/server/rspro_server.h +++ b/src/server/rspro_server.h @@ -3,13 +3,13 @@ #include <osmocom/core/linuxlist.h> #include <osmocom/core/select.h> #include <osmocom/core/fsm.h> -#include <osmocom/abis/ipa.h> +#include <osmocom/netif/stream.h>
#include "rspro_util.h" #include "slotmap.h"
struct rspro_server { - struct ipa_server_link *link; + struct osmo_stream_srv_link *link; /* list of rspro_client_conn */ struct llist_head connections; struct llist_head clients; @@ -30,7 +30,7 @@ /* back-pointer to rspro_server */ struct rspro_server *srv; /* reference to the underlying IPA server connection */ - struct ipa_server_conn *peer; + struct osmo_stream_srv *peer; /* FSM instance for this connection */ struct osmo_fsm_inst *fi; /* remote component identity (after it has been received) */