[PATCH] libosmo-sccp[master]: Add IPA/SCCPlite support as SIGTRAN alternative

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Sat Apr 15 21:14:17 UTC 2017


Hello Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/2282

to look at the new patch set (#3).

Add IPA/SCCPlite support as SIGTRAN alternative

This tries as good as possible to fit the IPA/SCCPlite stacking into the
existing SIGTRAN/SS7 code architecture/model.  To the user, the IPA
protocol looks like yet another protocol on the same level as the choice
between SUA and M3AU.  On the inside, things are obviously quite
different.

We need to handle TCP with IPA framing instead of SCTP for both server
and client.  We also implement an alternative "ASP FSM" for IPA, which
takes care of the CCM handshake (ID_REQ/ID_RESP/ID_ACK/ID_ACK2) for both
client and server mode.

In server mode, we use the 'unit name' as identifier to look up the AS,
similar to how we use a routing context to look up the AS in the xUA
case.

We also have to bypass activating the default layer manager in the
simple client to make sure we don't run into even more complexity.

What's missing right now is some way to manually override/set the point
codes.  As IPA/SCCPlite is missing any routing label, we currently
simply generate one with SPC=0/DPC=0, which will obviously not work in
most configurations.

Change-Id: I9098574cddeba10fcf8f1b6c196a7069a6805c56
---
M include/osmocom/sigtran/osmo_ss7.h
M src/Makefile.am
A src/ipa.c
M src/osmo_ss7.c
M src/osmo_ss7_hmrt.c
M src/osmo_ss7_vty.c
M src/sccp_scrc.c
M src/sccp_user.c
M src/xua_as_fsm.c
M src/xua_asp_fsm.c
M src/xua_asp_fsm.h
M src/xua_internal.h
12 files changed, 683 insertions(+), 17 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/82/2282/3

diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h
index 76403e2..2ae4c1e 100644
--- a/include/osmocom/sigtran/osmo_ss7.h
+++ b/include/osmocom/sigtran/osmo_ss7.h
@@ -258,6 +258,7 @@
 	OSMO_SS7_ASP_PROT_NONE,
 	OSMO_SS7_ASP_PROT_SUA,
 	OSMO_SS7_ASP_PROT_M3UA,
+	OSMO_SS7_ASP_PROT_IPA,
 	_NUM_OSMO_SS7_ASP_PROT
 };
 
@@ -359,6 +360,9 @@
 	/*! Were we dynamically allocated */
 	bool dyn_allocated;
 
+	/*! Pending message for non-blocking IPA read */
+	struct msgb *pending_msg;
+
 	struct {
 		char *name;
 		char *description;
diff --git a/src/Makefile.am b/src/Makefile.am
index 217f2f7..15f17fe 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,6 @@
 			     sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \
 			     sccp_user.c xua_rkm.c xua_default_lm_fsm.c \
 			     osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c \
-			     osmo_ss7_vty.c sccp_vty.c
+			     osmo_ss7_vty.c sccp_vty.c ipa.c
 libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_'
 libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS)
diff --git a/src/ipa.c b/src/ipa.c
new file mode 100644
index 0000000..83dde9b
--- /dev/null
+++ b/src/ipa.c
@@ -0,0 +1,184 @@
+/* implementation of IPA/SCCPlite transport */
+
+/* (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+
+//#include <osmocom/netif/stream.h>
+#include <osmocom/netif/ipa.h>
+#include <osmocom/sigtran/xua_msg.h>
+
+#include <osmocom/sigtran/mtp_sap.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/sigtran/protocol/mtp.h>
+
+#include "xua_internal.h"
+#include "xua_asp_fsm.h"
+
+
+/*! \brief Send a given xUA message via a given IPA "Application Server"
+ *  \param[in] as Application Server through which to send \a xua
+ *  \param[in] xua xUA message to be sent
+ *  \return 0 on success; negative on error */
+int ipa_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua)
+{
+	struct xua_msg_part *data_ie;
+	struct msgb *msg;
+	unsigned int src_len;
+	const uint8_t *src;
+	uint8_t *dst;
+
+	OSMO_ASSERT(as->cfg.proto == OSMO_SS7_ASP_PROT_IPA);
+
+	/* we're actually only interested in the data part */
+	data_ie = xua_msg_find_tag(xua, M3UA_IEI_PROT_DATA);
+	if (!data_ie || data_ie->len < sizeof(struct m3ua_data_hdr))
+		return -1;
+
+	/* and even the data part still has the header prepended */
+	src = data_ie->dat + sizeof(struct m3ua_data_hdr);
+	src_len = data_ie->len - sizeof(struct m3ua_data_hdr);
+
+	/* sufficient headroom for osmo_ipa_msg_push_header() */
+	msg = ipa_msg_alloc(16);
+	if (!msg)
+		return -1;
+
+	dst = msgb_put(msg, src_len);
+	memcpy(dst, src, src_len);
+
+	/* TODO: if we ever need something beyond SCCP, we can use the
+	 * M3UA SIO to determine the protocol */
+	osmo_ipa_msg_push_header(msg, IPAC_PROTO_SCCP);
+
+	return xua_as_transmit_msg(as, msg);
+}
+
+static int ipa_rx_msg_ccm(struct osmo_ss7_asp *asp, struct msgb *msg)
+{
+	uint8_t msg_type = msg->l2h[0];
+
+	LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s:%s\n", __func__, msgb_hexdump(msg));
+
+	/* Convert CCM into events to the IPA_ASP_FSM */
+	switch (msg_type) {
+	case IPAC_MSGT_ID_ACK:
+		osmo_fsm_inst_dispatch(asp->fi, IPA_ASP_E_ID_ACK, msg);
+		break;
+	case IPAC_MSGT_ID_RESP:
+		osmo_fsm_inst_dispatch(asp->fi, IPA_ASP_E_ID_RESP, msg);
+		break;
+	case IPAC_MSGT_ID_GET:
+		osmo_fsm_inst_dispatch(asp->fi, IPA_ASP_E_ID_GET, msg);
+		break;
+	case IPAC_MSGT_PING:
+		osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_ASPSM_BEAT, msg);
+		break;
+	case IPAC_MSGT_PONG:
+		osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_ASPSM_BEAT_ACK, msg);
+		break;
+	default:
+		LOGPASP(asp, DLSS7, LOGL_NOTICE, "Unknown CCM Message 0x%02x: %s\n",
+			msg_type, msgb_hexdump(msg));
+		return -1;
+	}
+
+	msgb_free(msg);
+
+	return 0;
+}
+
+static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
+{
+	struct osmo_mtp_prim *omp;
+	struct m3ua_data_hdr data_hdr;
+	struct xua_msg *xua = xua_msg_alloc();
+
+	/* pull the IPA header */
+	msgb_pull_to_l2(msg);
+
+	/* We have received an IPA-encapsulated SCCP message, without
+	 * any MTP routing label.  This means we have no real idea where
+	 * it came from, nor where it goes to.  We could simply treat it
+	 * as being for the local point code, but then this means that
+	 * we would have to implement SCCP connection coupling in order
+	 * to route the connections to any other point code.  The reason
+	 * for this is the lack of addressing information inside the
+	 * non-CR/CC connection oriented messages.
+	 *
+	 * The only other alternative we have is to simply have a
+	 * STP (server) side configuration that specifies which point
+	 * code those messages are to be routed to, and then use this
+	 * 'override DPC' in the routing decision.  We could do the same
+	 * for the source point code to ensure responses are routed back
+	 * to us.  This is all quite ugly, but then what can we do :/
+	 */
+
+	memset(&data_hdr, 0, sizeof(data_hdr));
+	data_hdr.opc = 0;//FIXME;
+	data_hdr.dpc = 0;//FIXME;
+	data_hdr.si = MTP_SI_SCCP;
+	xua = m3ua_xfer_from_data(&data_hdr, msgb_l2(msg), msgb_l2len(msg));
+
+	return m3ua_hmdc_rx_from_l2(asp->inst, xua);
+}
+
+/*! \brief process M3UA message received from socket
+ *  \param[in] asp Application Server Process receiving \a msg
+ *  \param[in] msg received message buffer. Callee takes ownership!
+ *  \returns 0 on success; negative on error */
+int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg)
+{
+	struct ipaccess_head *hh;
+	uint16_t len;
+	int rc;
+
+	OSMO_ASSERT(asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA);
+
+	/* osmo_ipa_process_msg() will already have verified length
+	 * consistency and set up l2h poiter */
+	hh = (struct ipaccess_head *) msg->l1h;
+
+	switch (hh->proto) {
+	case IPAC_PROTO_IPACCESS:
+		rc = ipa_rx_msg_ccm(asp, msg);
+		break;
+	case IPAC_PROTO_SCCP:
+		rc = ipa_rx_msg_sccp(asp, msg);
+		break;
+	default:
+		LOGPASP(asp, DLSS7, LOGL_DEBUG, "Unknown Stream ID 0x%02x: %s\n",
+			hh->proto, msgb_hexdump(msg));
+		rc = -1;
+	}
+
+	return rc;
+}
diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c
index 4aa7be5..ffe587c 100644
--- a/src/osmo_ss7.c
+++ b/src/osmo_ss7.c
@@ -41,6 +41,7 @@
 #include <osmocom/core/socket.h>
 
 #include <osmocom/netif/stream.h>
+#include <osmocom/netif/ipa.h>
 
 #include "sccp_internal.h"
 #include "xua_internal.h"
@@ -68,11 +69,24 @@
 	{ OSMO_SS7_ASP_PROT_NONE,	"none" },
 	{ OSMO_SS7_ASP_PROT_SUA,	"sua" },
 	{ OSMO_SS7_ASP_PROT_M3UA,	"m3ua" },
+	{ OSMO_SS7_ASP_PROT_IPA,	"ipa" },
 	{ 0, NULL }
 };
 
 #define LOGSS7(inst, level, fmt, args ...)	\
 	LOGP(DLSS7, level, "%u: " fmt, inst ? (inst)->cfg.id : 0, ## args)
+
+static int asp_proto_to_ip_proto(enum osmo_ss7_asp_protocol proto)
+{
+	switch (proto) {
+	case OSMO_SS7_ASP_PROT_IPA:
+		return IPPROTO_TCP;
+	case OSMO_SS7_ASP_PROT_SUA:
+	case OSMO_SS7_ASP_PROT_M3UA:
+	default:
+		return IPPROTO_SCTP;
+	}
+}
 
 int osmo_ss7_find_free_rctx(struct osmo_ss7_instance *inst)
 {
@@ -1070,6 +1084,7 @@
 }
 
 static int xua_cli_read_cb(struct osmo_stream_cli *conn);
+static int ipa_cli_read_cb(struct osmo_stream_cli *conn);
 static int xua_cli_connect_cb(struct osmo_stream_cli *cli);
 
 int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp)
@@ -1100,10 +1115,13 @@
 		osmo_stream_cli_set_port(asp->client, asp->cfg.remote.port);
 		osmo_stream_cli_set_local_addr(asp->client, asp->cfg.local.host);
 		osmo_stream_cli_set_local_port(asp->client, asp->cfg.local.port);
-		osmo_stream_cli_set_proto(asp->client, IPPROTO_SCTP);
+		osmo_stream_cli_set_proto(asp->client, asp_proto_to_ip_proto(asp->cfg.proto));
 		osmo_stream_cli_set_reconnect_timeout(asp->client, 5);
 		osmo_stream_cli_set_connect_cb(asp->client, xua_cli_connect_cb);
-		osmo_stream_cli_set_read_cb(asp->client, xua_cli_read_cb);
+		if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA)
+			osmo_stream_cli_set_read_cb(asp->client, ipa_cli_read_cb);
+		else
+			osmo_stream_cli_set_read_cb(asp->client, xua_cli_read_cb);
 		osmo_stream_cli_set_data(asp->client, asp);
 		rc = osmo_stream_cli_open2(asp->client, 1);
 		if (rc < 0) {
@@ -1213,6 +1231,37 @@
 				notif->sn_header.sn_type));
 		break;
 	}
+}
+
+/* netif code tells us we can read something from the socket */
+static int ipa_srv_conn_cb(struct osmo_stream_srv *conn)
+{
+	struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
+	struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn);
+	struct msgb *msg = NULL;
+	int rc;
+
+	/* read IPA message from socket and process it */
+	rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg);
+	LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n",
+		__func__, rc);
+	if (rc <= 0) {
+		if (rc == -EAGAIN) {
+			/* more data needed */
+			return 0;
+		}
+		osmo_stream_srv_destroy(conn);
+		return rc;
+	}
+	if (osmo_ipa_process_msg(msg) < 0) {
+		LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n");
+		osmo_stream_srv_destroy(conn);
+		msgb_free(msg);
+		return -1;
+	}
+	msg->dst = asp;
+
+	return ipa_rx_msg(asp, msg);
 }
 
 /* netif code tells us we can read something from the socket */
@@ -1327,6 +1376,36 @@
 	osmo_stream_cli_reconnect(cli);
 }
 
+/* read call-back for IPA/SCCPlite socket */
+static int ipa_cli_read_cb(struct osmo_stream_cli *conn)
+{
+	struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
+	struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn);
+	struct msgb *msg = NULL;
+	int rc;
+
+	/* read IPA message from socket and process it */
+	rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg);
+	LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n",
+		__func__, rc);
+	if (rc <= 0) {
+		if (rc == -EAGAIN) {
+			/* more data needed */
+			return 0;
+		}
+		osmo_stream_cli_reconnect(conn);
+		return rc;
+	}
+	if (osmo_ipa_process_msg(msg) < 0) {
+		LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n");
+		osmo_stream_cli_reconnect(conn);
+		msgb_free(msg);
+		return -1;
+	}
+	msg->dst = asp;
+	return ipa_rx_msg(asp, msg);
+}
+
 static int xua_cli_read_cb(struct osmo_stream_cli *conn)
 {
 	struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
@@ -1439,15 +1518,21 @@
 	struct osmo_ss7_asp *asp;
 	char *sock_name = osmo_sock_get_name(link, fd);
 
-	LOGP(DLSS7, LOGL_INFO, "%s: New SCTP connection accepted\n",
-		sock_name);
+	LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n",
+		sock_name, get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto));
 
-	srv = osmo_stream_srv_create(oxs, link, fd,
-				     xua_srv_conn_cb,
-				     xua_srv_conn_closed_cb, NULL);
+	if (oxs->cfg.proto == OSMO_SS7_ASP_PROT_IPA) {
+		srv = osmo_stream_srv_create(oxs, link, fd,
+					     ipa_srv_conn_cb,
+					     xua_srv_conn_closed_cb, NULL);
+	} else {
+		srv = osmo_stream_srv_create(oxs, link, fd,
+					     xua_srv_conn_cb,
+					     xua_srv_conn_closed_cb, NULL);
+	}
 	if (!srv) {
 		LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server "
-		     "for SCTP connection\n", sock_name);
+		     "for connection\n", sock_name);
 		close(fd);
 		talloc_free(sock_name);
 		return -1;
@@ -1473,6 +1558,7 @@
 					sock_name, asp->cfg.name);
 				asp->cfg.is_server = true;
 				asp->dyn_allocated = true;
+				asp->server = srv;
 				osmo_ss7_asp_restart(asp);
 			}
 		}
@@ -1517,6 +1603,8 @@
 		break;
 	case OSMO_SS7_ASP_PROT_M3UA:
 		msgb_sctp_ppid(msg) = M3UA_PPID;
+		break;
+	case OSMO_SS7_ASP_PROT_IPA:
 		break;
 	default:
 		OSMO_ASSERT(0);
@@ -1589,8 +1677,8 @@
 	if (!oxs)
 		return NULL;
 
-	LOGP(DLSS7, LOGL_INFO, "Creating XUA Server %s:%u\n",
-		local_host, local_port);
+	LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n",
+		get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port);
 
 	INIT_LLIST_HEAD(&oxs->asp_list);
 
@@ -1605,7 +1693,7 @@
 	osmo_stream_srv_link_set_nodelay(oxs->server, true);
 	osmo_stream_srv_link_set_addr(oxs->server, oxs->cfg.local.host);
 	osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port);
-	osmo_stream_srv_link_set_proto(oxs->server, IPPROTO_SCTP);
+	osmo_stream_srv_link_set_proto(oxs->server, asp_proto_to_ip_proto(proto));
 
 	rc = osmo_stream_srv_link_open(oxs->server);
 	if (rc < 0) {
@@ -1664,6 +1752,7 @@
 	osmo_fsm_register(&sccp_scoc_fsm);
 	osmo_fsm_register(&xua_as_fsm);
 	osmo_fsm_register(&xua_asp_fsm);
+	osmo_fsm_register(&ipa_asp_fsm);
 	osmo_fsm_register(&xua_default_lm_fsm);
 	ss7_initialized = true;
 	return 0;
diff --git a/src/osmo_ss7_hmrt.c b/src/osmo_ss7_hmrt.c
index ce0728b..bbbb3a9 100644
--- a/src/osmo_ss7_hmrt.c
+++ b/src/osmo_ss7_hmrt.c
@@ -141,6 +141,8 @@
 			switch (as->cfg.proto) {
 			case OSMO_SS7_ASP_PROT_M3UA:
 				return m3ua_tx_xua_as(as,xua);
+			case OSMO_SS7_ASP_PROT_IPA:
+				return ipa_tx_xua_as(as, xua);
 			default:
 				LOGP(DLSS7, LOGL_ERROR, "MTP message "
 					"for ASP of unknown protocol%u\n",
diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c
index 282a5a0..a6a0f5b 100644
--- a/src/osmo_ss7_vty.c
+++ b/src/osmo_ss7_vty.c
@@ -37,11 +37,12 @@
 
 #include "xua_internal.h"
 
-#define XUA_VAR_STR	"(sua|m3ua)"
+#define XUA_VAR_STR	"(sua|m3ua|ipa)"
 
 #define XUA_VAR_HELP_STR		\
 	"SCCP User Adaptation\n"	 \
-	"MTP3 User Adaptation\n"
+	"MTP3 User Adaptation\n"	\
+	"IPA Multiplex (SCCP Lite)\n"
 
 
 /***********************************************************************
diff --git a/src/sccp_scrc.c b/src/sccp_scrc.c
index 4491ce6..9a6a865 100644
--- a/src/sccp_scrc.c
+++ b/src/sccp_scrc.c
@@ -142,6 +142,7 @@
 		case OSMO_SS7_ASP_PROT_SUA:
 			return sua_tx_xua_as(as, xua);
 		case OSMO_SS7_ASP_PROT_M3UA:
+		case OSMO_SS7_ASP_PROT_IPA:
 			return sua2sccp_tx_m3ua(inst, xua);
 		default:
 			LOGP(DLSCCP, LOGL_ERROR, "MTP-TRANSFER.req for "
@@ -296,7 +297,8 @@
 		       const struct osmo_sccp_addr *called)
 {
 	struct osmo_sccp_user *scu;
-
+	/* it is not really clear that called->pc will be set to
+	 * anything here, in the case of a SSN-only CalledAddr */
 	scu = sccp_user_find(inst, called->ssn, called->pc);
 
 	/* Is subsystem equipped? */
diff --git a/src/sccp_user.c b/src/sccp_user.c
index 51cc6b1..d9e40b6 100644
--- a/src/sccp_user.c
+++ b/src/sccp_user.c
@@ -274,7 +274,8 @@
 	asp->cfg.local.host = talloc_strdup(asp, local_ip);
 	asp->cfg.remote.host = talloc_strdup(asp, remote_ip);
 	osmo_ss7_as_add_asp(as, asp_name);
-	osmo_ss7_asp_use_default_lm(asp, LOGL_DEBUG);
+	if (prot != OSMO_SS7_ASP_PROT_IPA)
+		osmo_ss7_asp_use_default_lm(asp, LOGL_DEBUG);
 	talloc_free(asp_name);
 	osmo_ss7_asp_restart(asp);
 
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
index 282a6bf..0dd6ba1 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -38,6 +38,10 @@
 	struct msgb *msg;
 	unsigned int i, sent = 0;
 
+	/* we don't send notify to IPA peers! */
+	if (as->cfg.proto == OSMO_SS7_ASP_PROT_IPA)
+		return 0;
+
 	/* iterate over all non-DOWN ASPs and send them the message */
 	for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
 		struct osmo_ss7_asp *asp = as->cfg.asps[i];
@@ -66,7 +70,7 @@
 }
 
 /* actually transmit a message through this AS */
-static int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg)
+int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg)
 {
 	struct osmo_ss7_asp *asp;
 	unsigned int i;
diff --git a/src/xua_asp_fsm.c b/src/xua_asp_fsm.c
index ce15038..147aa45 100644
--- a/src/xua_asp_fsm.c
+++ b/src/xua_asp_fsm.c
@@ -14,6 +14,11 @@
 #include <osmocom/core/timer.h>
 #include <osmocom/core/prim.h>
 #include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/gsm/ipa.h>
+
+#include <osmocom/netif/stream.h>
+#include <osmocom/netif/ipa.h>
 
 #include <osmocom/sigtran/osmo_ss7.h>
 #include <osmocom/sigtran/sigtran_sap.h>
@@ -65,6 +70,14 @@
 	{ XUA_ASP_E_ASPSM_ASPDN_ACK,	"ASPSM-ASP_DN_ACK" },
 	{ XUA_ASP_E_ASPTM_ASPIA,	"ASPTM-ASP_IA" },
 	{ XUA_ASP_E_ASPTM_ASPIA_ACK,	"ASPTM_ASP_IA_ACK" },
+
+	{ XUA_ASP_E_ASPSM_BEAT,		"ASPSM_BEAT" },
+	{ XUA_ASP_E_ASPSM_BEAT_ACK,	"ASPSM_BEAT_ACK" },
+
+	{ IPA_ASP_E_ID_RESP,		"IPA_CCM_ID_RESP" },
+	{ IPA_ASP_E_ID_GET,		"IPA_CCM_ID_GET" },
+	{ IPA_ASP_E_ID_ACK,		"IPA_CCM_ID_ACK" },
+
 	{ 0, NULL }
 };
 
@@ -654,6 +667,8 @@
 	.allstate_action = xua_asp_allstate,
 };
 
+static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
+					enum xua_asp_role role, int log_level);
 
 /*! \brief Start a new ASP finite stae machine for given ASP
  *  \param[in] asp Application Server Process for which to start FSM
@@ -665,6 +680,9 @@
 {
 	struct osmo_fsm_inst *fi;
 	struct xua_asp_fsm_priv *xafp;
+
+	if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA)
+		return ipa_asp_fsm_start(asp, role, log_level);
 
 	/* allocate as child of AS? */
 	fi = osmo_fsm_inst_alloc(&xua_asp_fsm, asp, NULL, log_level, asp->cfg.name);
@@ -681,3 +699,352 @@
 
 	return fi;
 }
+
+
+
+
+
+/***********************************************************************
+ * IPA Compatibility FSM
+ ***********************************************************************/
+
+/* The idea here is to have a FSM that handles an IPA / SCCPlite link in
+ * a way that the higher-layer code considers it the same like an M3UA
+ * or SUA link.  We have a couple of different states and some
+ * additional events. */
+
+enum ipa_asp_state {
+	IPA_ASP_S_DOWN = XUA_ASP_S_DOWN,
+	IPA_ASP_S_ACTIVE = XUA_ASP_S_ACTIVE,
+	IPA_ASP_S_WAIT_ID_RESP,		/* Waiting for ID_RESP from peer */
+	IPA_ASP_S_WAIT_ID_GET,		/* Waiting for ID_GET from peer */
+	IPA_ASP_S_WAIT_ID_ACK,		/* Waiting for ID_ACK from peer */
+	IPA_ASP_S_WAIT_ID_ACK2,		/* Waiting for ID_ACK (of ACK) from peer */
+};
+
+/* private data structure for each FSM instance */
+struct ipa_asp_fsm_priv {
+	/* pointer back to ASP to which we belong */
+	struct osmo_ss7_asp *asp;
+	/* Role (ASP/SG/IPSP) */
+	enum xua_asp_role role;
+
+	/* Structure holding parsed data of the IPA CCM ID exchange */
+	struct ipaccess_unit *ipa_unit;
+	/* Timer for tracking if no PONG is received in response to PING */
+	struct osmo_timer_list pong_timer;
+};
+
+enum ipa_asp_fsm_t {
+	T_WAIT_ID_RESP	= 1,
+	T_WAIT_ID_ACK,
+	T_WAIT_ID_GET,
+};
+
+/* get the file descriptor related to a given ASP */
+static int get_fd_from_iafp(struct ipa_asp_fsm_priv *iafp)
+{
+	struct osmo_ss7_asp *asp = iafp->asp;
+	struct osmo_fd *ofd;
+
+	if (asp->server)
+		ofd = osmo_stream_srv_get_ofd(asp->server);
+	else if (asp->client)
+		ofd = osmo_stream_cli_get_ofd(asp->client);
+	else
+		return -1;
+
+	return ofd->fd;
+}
+
+/* Server + Client: Initial State, wait for M-ASP-UP.req */
+static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+	int fd = get_fd_from_iafp(iafp);
+
+	switch (event) {
+	case XUA_ASP_E_M_ASP_UP_REQ:
+	case XUA_ASP_E_SCTP_EST_IND:
+		if (iafp->role == XUA_ASPFSM_ROLE_SG) {
+			/* Server: Transmit IPA ID GET + Wait for Response */
+			ipa_ccm_send_id_req(fd);
+			osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_RESP, 10, T_WAIT_ID_RESP);
+		} else {
+			/* Client: We simply wait for an ID GET */
+			osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_GET, 10, T_WAIT_ID_GET);
+		}
+		break;
+	}
+}
+
+/* Server: We're waiting for an ID RESP */
+static void ipa_asp_fsm_wait_id_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+	struct osmo_ss7_asp *asp = iafp->asp;
+	int fd = get_fd_from_iafp(iafp);
+	struct osmo_ss7_as *as;
+	struct tlv_parsed tp;
+	struct msgb *msg;
+	int rc;
+
+	switch (event) {
+	case IPA_ASP_E_ID_RESP:
+		/* resolve the AS based on the identity provided by peer. */
+		msg = data;
+			rc = ipa_ccm_idtag_parse(&tp, msgb_l2(msg)+2, msgb_l2len(msg)-2);
+		if (rc < 0) {
+			LOGPFSML(fi, LOGL_ERROR, "Error %d parsing ID_RESP TLV: %s\n", rc,
+				 msgb_hexdump(msg));
+			goto out_err;
+		}
+		rc = ipa_ccm_tlv_to_unitdata(iafp->ipa_unit, &tp);
+		if (rc < 0) {
+			LOGPFSML(fi, LOGL_ERROR, "Error %d parsing ID_RESP: %s\n", rc, msgb_hexdump(msg));
+			goto out_err;
+		}
+		if (!iafp->ipa_unit->unit_name) {
+			LOGPFSML(fi, LOGL_NOTICE, "No Unit Name specified by client\n");
+			goto out_err;
+		}
+		as = osmo_ss7_as_find_by_name(asp->inst, iafp->ipa_unit->unit_name);
+		if (!as) {
+			LOGPFSML(fi, LOGL_NOTICE, "Cannot find any definition for IPA Unit Name '%s'\n",
+				iafp->ipa_unit->unit_name);
+			goto out_err;
+		}
+		osmo_ss7_as_add_asp(as, asp->cfg.name);
+		/* TODO: OAP Authentication? */
+		/* Send ID_ACK */
+		ipaccess_send_id_ack(fd);
+		osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK2, 10, T_WAIT_ID_ACK);
+		break;
+	}
+	return;
+out_err:
+	osmo_ss7_asp_disconnect(asp);
+	return;
+}
+
+/* Server: We're waiting for an ID ACK */
+static void ipa_asp_fsm_wait_id_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	switch (event) {
+	case IPA_ASP_E_ID_ACK:
+		/* ACK received, we can go to active state now.  The
+		 * ACTIVE onenter function will inform the AS */
+		osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
+		break;
+	}
+}
+
+/* Client: We're waiting for an ID GET */
+static void ipa_asp_fsm_wait_id_get(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+	struct osmo_ss7_asp *asp = iafp->asp;
+	struct msgb *msg_get, *msg_resp;
+	const uint8_t *req_data;
+	int data_len;
+
+	switch (event) {
+	case IPA_ASP_E_ID_GET:
+		msg_get = data;
+		req_data = msgb_l2(msg_get)+1;
+		data_len = msgb_l2len(msg_get)-1;
+		LOGPFSM(fi, "Received IPA CCM IDENTITY REQUEST for IEs %s\n",
+			osmo_hexdump(req_data, data_len));
+		/* Send ID_RESP to server */
+		msg_resp = ipa_ccm_make_id_resp_from_req(iafp->ipa_unit, req_data, data_len);
+		if (!msg_resp) {
+			LOGPFSML(fi, LOGL_ERROR, "Error building IPA CCM IDENTITY RESPONSE\n");
+			break;
+		}
+		osmo_ss7_asp_send(asp, msg_resp);
+		osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
+		break;
+	}
+}
+
+/* Client: We're waiting for an ID ACK */
+static void ipa_asp_fsm_wait_id_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+	int fd;
+
+	switch (event) {
+	case IPA_ASP_E_ID_ACK:
+		/* Send ACK2 to server */
+		fd = get_fd_from_iafp(iafp);
+		ipaccess_send_id_ack(fd);
+		osmo_fsm_inst_state_chg(fi, IPA_ASP_S_ACTIVE, 0, 0);
+		break;
+	}
+}
+
+
+/* Server + Client: We're actively transmitting user data */
+static void ipa_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	switch (event) {
+	case XUA_ASP_E_M_ASP_DOWN_REQ:
+	case XUA_ASP_E_M_ASP_INACTIVE_REQ:
+		/* FIXME: kill ASP and (wait for) re-connect */
+		break;
+	}
+}
+
+static void ipa_asp_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+	int fd;
+
+	switch (event) {
+	case XUA_ASP_E_SCTP_COMM_DOWN_IND:
+	case XUA_ASP_E_SCTP_RESTART_IND:
+		osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
+		send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_DOWN,
+				     PRIM_OP_INDICATION);
+		break;
+	case XUA_ASP_E_ASPSM_BEAT:
+		/* PING -> PONG */
+		fd = get_fd_from_iafp(iafp);
+		ipaccess_send_pong(fd);
+		break;
+	case XUA_ASP_E_ASPSM_BEAT_ACK:
+		/* stop timer, if any */
+		osmo_timer_del(&iafp->pong_timer);
+		break;
+	default:
+		break;
+	}
+}
+
+static void ipa_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+	dispatch_to_all_as(fi, XUA_ASPAS_ASP_INACTIVE_IND);
+	dispatch_to_all_as(fi, XUA_ASPAS_ASP_ACTIVE_IND);
+}
+
+static void ipa_pong_timer_cb(void *_fi)
+{
+	struct osmo_fsm_inst *fi = _fi;
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+
+	LOGPFSML(fi, LOGL_NOTICE, "Peer didn't respond to PING? with PONG!\n");
+	/* kill ASP and (wait for) re-connect */
+	osmo_ss7_asp_disconnect(iafp->asp);
+}
+
+static int ipa_asp_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+	struct ipa_asp_fsm_priv *iafp = fi->priv;
+
+	LOGPFSML(fi, LOGL_ERROR, "Timeout waiting for peer response\n");
+	/* kill ASP and (wait for) re-connect */
+	osmo_ss7_asp_disconnect(iafp->asp);
+	return -1;
+}
+
+static const struct osmo_fsm_state ipa_asp_states[] = {
+	[IPA_ASP_S_DOWN] = {
+		.in_event_mask = S(XUA_ASP_E_M_ASP_UP_REQ) |
+				 S(XUA_ASP_E_SCTP_EST_IND),
+		.out_state_mask = S(IPA_ASP_S_WAIT_ID_GET) |
+				  S(IPA_ASP_S_WAIT_ID_RESP),
+		.name = "ASP_DOWN",
+		.action = ipa_asp_fsm_down,
+		.onenter = xua_asp_fsm_down_onenter,
+	},
+	/* Server Side */
+	[IPA_ASP_S_WAIT_ID_RESP] = {
+		.in_event_mask = S(IPA_ASP_E_ID_RESP),
+		.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK2) |
+				  S(IPA_ASP_S_DOWN),
+		.name = "WAIT_ID_RESP",
+		.action = ipa_asp_fsm_wait_id_resp,
+	},
+	/* Server Side */
+	[IPA_ASP_S_WAIT_ID_ACK2] = {
+		.in_event_mask = S(IPA_ASP_E_ID_ACK),
+		.out_state_mask = S(IPA_ASP_S_ACTIVE) |
+				  S(IPA_ASP_S_DOWN),
+		.name = "WAIT_ID_ACK2",
+		.action = ipa_asp_fsm_wait_id_ack2,
+	},
+	/* Client Side */
+	[IPA_ASP_S_WAIT_ID_GET] = {
+		.in_event_mask = S(IPA_ASP_E_ID_GET),
+		.out_state_mask = S(IPA_ASP_S_WAIT_ID_ACK),
+		.name = "WAIT_ID_GET",
+		.action = ipa_asp_fsm_wait_id_get,
+	},
+	/* Client Side */
+	[IPA_ASP_S_WAIT_ID_ACK] = {
+		.in_event_mask = S(IPA_ASP_E_ID_ACK),
+		.out_state_mask = S(IPA_ASP_S_ACTIVE) |
+				  S(IPA_ASP_S_DOWN),
+		.name = "WAIT_ID_ACK",
+		.action = ipa_asp_fsm_wait_id_ack,
+	},
+	[IPA_ASP_S_ACTIVE] = {
+		.in_event_mask = S(XUA_ASP_E_M_ASP_DOWN_REQ) |
+				 S(XUA_ASP_E_M_ASP_INACTIVE_REQ),
+		.out_state_mask = S(XUA_ASP_S_INACTIVE) |
+				  S(XUA_ASP_S_DOWN),
+		.name = "ASP_ACTIVE",
+		.action = ipa_asp_fsm_active,
+		.onenter = ipa_asp_fsm_active_onenter,
+	},
+};
+
+
+struct osmo_fsm ipa_asp_fsm = {
+	.name = "IPA_ASP",
+	.states = ipa_asp_states,
+	.num_states = ARRAY_SIZE(ipa_asp_states),
+	.timer_cb = ipa_asp_fsm_timer_cb,
+	.log_subsys = DLSS7,
+	.event_names = xua_asp_event_names,
+	.allstate_event_mask = S(XUA_ASP_E_SCTP_COMM_DOWN_IND) |
+			       S(XUA_ASP_E_SCTP_RESTART_IND) |
+			       S(XUA_ASP_E_ASPSM_BEAT) |
+			       S(XUA_ASP_E_ASPSM_BEAT_ACK),
+	.allstate_action = ipa_asp_allstate,
+};
+
+
+/*! \brief Start a new ASP finite stae machine for given ASP
+ *  \param[in] asp Application Server Process for which to start FSM
+ *  \param[in] role Role (ASP, SG, IPSP) of this FSM
+ *  \param[in] log_level Logging Level for ASP FSM logging
+ *  \returns FSM instance on success; NULL on error */
+static struct osmo_fsm_inst *ipa_asp_fsm_start(struct osmo_ss7_asp *asp,
+					enum xua_asp_role role, int log_level)
+{
+	struct osmo_fsm_inst *fi;
+	struct ipa_asp_fsm_priv *iafp;
+
+	/* allocate as child of AS? */
+	fi = osmo_fsm_inst_alloc(&ipa_asp_fsm, asp, NULL, log_level, asp->cfg.name);
+
+	iafp = talloc_zero(fi, struct ipa_asp_fsm_priv);
+	if (!iafp) {
+		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+		return NULL;
+	}
+	iafp->role = role;
+	iafp->asp = asp;
+	iafp->ipa_unit = talloc_zero(iafp, struct ipaccess_unit);
+	iafp->ipa_unit->unit_name = talloc_strdup(iafp->ipa_unit, asp->cfg.name);
+	iafp->pong_timer.cb = ipa_pong_timer_cb;
+	iafp->pong_timer.data = fi;
+
+	fi->priv = iafp;
+
+	if (role == XUA_ASPFSM_ROLE_ASP)
+		osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_UP_REQ, NULL);
+
+	return fi;
+}
diff --git a/src/xua_asp_fsm.h b/src/xua_asp_fsm.h
index 60e09da..32749ec 100644
--- a/src/xua_asp_fsm.h
+++ b/src/xua_asp_fsm.h
@@ -28,6 +28,11 @@
 	XUA_ASP_E_ASPSM_BEAT,
 	XUA_ASP_E_ASPSM_BEAT_ACK,
 
+	/* IPA specific */
+	IPA_ASP_E_ID_RESP,
+	IPA_ASP_E_ID_ACK,
+	IPA_ASP_E_ID_GET,
+
 	_NUM_XUA_ASP_E
 };
 
@@ -38,6 +43,7 @@
 };
 
 extern struct osmo_fsm xua_asp_fsm;
+extern struct osmo_fsm ipa_asp_fsm;
 
 struct osmo_fsm_inst *xua_asp_fsm_start(struct osmo_ss7_asp *asp,
 					enum xua_asp_role role, int log_level);
diff --git a/src/xua_internal.h b/src/xua_internal.h
index 3831f56..bb54205 100644
--- a/src/xua_internal.h
+++ b/src/xua_internal.h
@@ -66,3 +66,9 @@
 #define CS7_STR	"ITU-T Signaling System 7\n"
 #define PC_STR	"Point Code\n"
 #define INST_STR "An instance of the SS7 stack\n"
+
+int xua_as_transmit_msg(struct osmo_ss7_as *as, struct msgb *msg);
+
+
+int ipa_tx_xua_as(struct osmo_ss7_as *as, struct xua_msg *xua);
+int ipa_rx_msg(struct osmo_ss7_asp *asp, struct msgb *msg);

-- 
To view, visit https://gerrit.osmocom.org/2282
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: I9098574cddeba10fcf8f1b6c196a7069a6805c56
Gerrit-PatchSet: 3
Gerrit-Project: libosmo-sccp
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list