summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-04-02 11:58:17 +0200
committerHarald Welte <laforge@gnumonks.org>2017-04-18 10:08:25 +0000
commit5f0a8df34cae20455a520c3a85cf94124f486bf2 (patch)
treea3be446812b69c3725fb5f6b655af053d7c474e7
parentafc3b856135237c8ce85ca39a42da892e5bdd9de (diff)
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
-rw-r--r--include/osmocom/sigtran/osmo_ss7.h4
-rw-r--r--src/Makefile.am2
-rw-r--r--src/ipa.c182
-rw-r--r--src/osmo_ss7.c111
-rw-r--r--src/osmo_ss7_hmrt.c2
-rw-r--r--src/osmo_ss7_vty.c5
-rw-r--r--src/sccp_scrc.c4
-rw-r--r--src/sccp_user.c3
-rw-r--r--src/xua_as_fsm.c6
-rw-r--r--src/xua_asp_fsm.c367
-rw-r--r--src/xua_asp_fsm.h6
-rw-r--r--src/xua_internal.h6
12 files changed, 681 insertions, 17 deletions
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 @@ enum osmo_ss7_asp_protocol {
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 @@ struct osmo_ss7_asp {
/*! 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 @@ libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \
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..1668f0f
--- /dev/null
+++ b/src/ipa.c
@@ -0,0 +1,182 @@
+/* implementation of IPA/SCCPlite transport */
+
+/* (C) 2015-2017 by Harald Welte <laforge@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 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;
+ 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 6b77dc4..6495947 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,12 +69,25 @@ struct value_string osmo_ss7_asp_protocol_vals[] = {
{ 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)
{
int32_t rctx;
@@ -1075,6 +1089,7 @@ void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp)
}
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)
@@ -1105,10 +1120,13 @@ int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp)
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) {
@@ -1221,6 +1239,37 @@ static void log_sctp_notification(struct osmo_ss7_asp *asp, const char *pfx,
}
/* 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 */
static int xua_srv_conn_cb(struct osmo_stream_srv *conn)
{
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
@@ -1332,6 +1381,36 @@ static void xua_cli_close_and_reconnect(struct osmo_stream_cli *cli)
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);
@@ -1444,15 +1523,21 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
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;
@@ -1478,6 +1563,7 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
sock_name, asp->cfg.name);
asp->cfg.is_server = true;
asp->dyn_allocated = true;
+ asp->server = srv;
osmo_ss7_asp_restart(asp);
}
}
@@ -1523,6 +1609,8 @@ int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg)
case OSMO_SS7_ASP_PROT_M3UA:
msgb_sctp_ppid(msg) = M3UA_PPID;
break;
+ case OSMO_SS7_ASP_PROT_IPA:
+ break;
default:
OSMO_ASSERT(0);
}
@@ -1594,8 +1682,8 @@ osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_pro
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);
@@ -1610,7 +1698,7 @@ osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_pro
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) {
@@ -1673,6 +1761,7 @@ int osmo_ss7_init(void)
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 @@ static int hmrt_message_for_routing(struct osmo_ss7_instance *inst,
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 @@ static int gen_mtp_transfer_req_xua(struct osmo_sccp_instance *inst,
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 @@ static int scrc_node_6(struct osmo_sccp_instance *inst,
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 @@ osmo_sccp_simple_client(void *ctx, const char *name, uint32_t pc,
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 36aed64..8f764f1 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -38,6 +38,10 @@ static int asp_notify_all_as(struct osmo_ss7_as *as, struct osmo_xlm_prim_notify
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 @@ static int asp_notify_all_as(struct osmo_ss7_as *as, struct osmo_xlm_prim_notify
}
/* 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 a0f93bd..2b5bfdd 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 @@ static const struct value_string xua_asp_event_names[] = {
{ 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 }
};
@@ -655,6 +668,8 @@ struct osmo_fsm xua_asp_fsm = {
.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
@@ -667,6 +682,9 @@ struct osmo_fsm_inst *xua_asp_fsm_start(struct osmo_ss7_asp *asp,
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);
@@ -682,3 +700,352 @@ struct osmo_fsm_inst *xua_asp_fsm_start(struct osmo_ss7_asp *asp,
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 @@ enum xua_asp_event {
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 @@ enum xua_asp_role {
};
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 @@ extern const struct value_string m3ua_rkm_dereg_status_vals[];
#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);