pespin has uploaded this change for review.

View Change

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) */

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

Gerrit-MessageType: newchange
Gerrit-Project: osmo-remsim
Gerrit-Branch: master
Gerrit-Change-Id: I6341612e41a0005de85f45fd6454bb954becb69c
Gerrit-Change-Number: 39033
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>