aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md63
-rw-r--r--include/osmocom/sigtran/osmo_ss7.h9
-rw-r--r--src/ipa.c1
-rw-r--r--src/m3ua.c24
-rw-r--r--src/osmo_ss7.c44
-rw-r--r--src/osmo_ss7_vty.c75
-rw-r--r--src/sccp_scoc.c5
-rw-r--r--src/sua.c23
-rw-r--r--src/xua_as_fsm.c74
-rw-r--r--src/xua_asp_fsm.c50
-rw-r--r--src/xua_default_lm_fsm.c9
-rw-r--r--tests/vty/ss7_asp_test.vty2
12 files changed, 316 insertions, 63 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..98b3591
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+libosmo-sccp - Osmocom SCCP, SIGTRAN and STP
+============================================
+
+This repository contains a C-language library implementation of a
+variety of telecom signaling protocols, such as M3UA, SUA, SCCP
+(connection oriented and connectionless), as well as the OsmoSTP, a SS7
+Transfer Point.
+
+Homepage
+--------
+
+The official homepage of libosmo-sccp is
+ https://osmocom.org/projects/libosmo-sccp/wiki
+
+The official homepage of osmo-stp is
+ https://osmocom.org/projects/osmo-stp/wiki
+
+GIT Repository
+--------------
+
+You can clone from the official git repository using
+
+ git clone git://git.osmocom.org/libosmo-sccp.git
+ git clone https://git.osmocom.org/libosmo-sccp.git
+
+There is a cgit interface at https://git.osmocom.org/libosmo-sccp/
+
+Documentation
+-------------
+
+User Manuals and VTY reference manuals are [optionally] built in PDF form
+as part of the build process.
+
+Pre-rendered PDF version of the current "master" can be found at
+[User Manual](https://ftp.osmocom.org/docs/latest/osmostp-usermanual.pdf)
+as well as the VTY reference manuals
+* [VTY Reference Manual for osmo-stp](https://ftp.osmocom.org/docs/latest/osmostp-vty-reference.pdf)
+
+Mailing List
+------------
+
+Discussions related to osmo-stp are happening on the
+openbsc@lists.osmocom.org mailing list, please see
+https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
+options and the list archive.
+
+Please observe the [Osmocom Mailing List
+Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
+when posting.
+
+Contributing
+------------
+
+Our coding standards are described at
+https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
+
+We us a gerrit based patch submission/review process for managing
+contributions. Please see
+https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
+more details
+
+The current patch queue can be seen at
+https://gerrit.osmocom.org/#/q/project:libosmo-sccp+status:open
diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h
index b717d53..3d13b6a 100644
--- a/include/osmocom/sigtran/osmo_ss7.h
+++ b/include/osmocom/sigtran/osmo_ss7.h
@@ -114,6 +114,7 @@ int osmo_ss7_instance_set_pc_fmt(struct osmo_ss7_instance *inst,
struct osmo_sccp_instance *osmo_ss7_ensure_sccp(struct osmo_ss7_instance *inst);
uint8_t osmo_ss7_pc_width(const struct osmo_ss7_pc_fmt *pc_fmt);
+uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc);
/***********************************************************************
* MTP Users (Users of MTP, such as SCCP or ISUP)
@@ -426,9 +427,17 @@ struct osmo_ss7_asp {
struct osmo_ss7_asp_peer local;
struct osmo_ss7_asp_peer remote;
uint8_t qos_class;
+ uint32_t quirks;
} cfg;
};
+/*! Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP procedure */
+#define OSMO_SS7_ASP_QUIRK_NO_NOTIFY 0x00000001
+/*! Accept DAUD in ASP role (RFC states only permitted in ASP->SG role) */
+#define OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP 0x00000002
+/*! Accept SSNM even if ASP is in AS-INACTIVE state */
+#define OSMO_SS7_ASP_QUIRK_SNM_INACTIVE 0x00000004
+
int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer);
int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx,
const char* const* hosts, size_t host_cnt);
diff --git a/src/ipa.c b/src/ipa.c
index d7a929d..93edef8 100644
--- a/src/ipa.c
+++ b/src/ipa.c
@@ -273,6 +273,7 @@ static int ipa_rx_msg_sccp(struct osmo_ss7_asp *asp, struct msgb *msg)
data_hdr.si = MTP_SI_SCCP;
data_hdr.opc = osmo_htonl(opc);
data_hdr.dpc = osmo_htonl(dpc);
+ data_hdr.ni = as->inst->cfg.network_indicator;
/* Create M3UA message in XUA structure */
xua = m3ua_xfer_from_data(&data_hdr, msgb_l2(msg), msgb_l2len(msg));
msgb_free(msg);
diff --git a/src/m3ua.c b/src/m3ua.c
index 77326e0..e2db6c1 100644
--- a/src/m3ua.c
+++ b/src/m3ua.c
@@ -901,6 +901,18 @@ static int m3ua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
xua->hdr.msg_type);
/* silently ignore those to not confuse the sender */
break;
+ case M3UA_SNM_DAUD:
+ /* RFC states only permitted in ASP->SG direction, not reverse. But some
+ * equipment still sends it to us as ASP ?!? */
+ if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
+ LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
+ "despite being in ASP role\n");
+ xua_snm_rx_daud(asp, xua);
+ } else {
+ LOGPASP(asp, DLM3UA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
+ return M3UA_ERR_UNSUPP_MSG_TYPE;
+ }
+ break;
default:
return M3UA_ERR_UNSUPP_MSG_TYPE;
}
@@ -926,9 +938,15 @@ static int m3ua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
/* SNM only permitted in ACTIVE state */
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
- LOGPASP(asp, DLM3UA, LOGL_NOTICE, "Received M3UA SNM while ASP in state %s\n",
- osmo_fsm_inst_state_name(asp->fi));
- return M3UA_ERR_UNEXPECTED_MSG;
+ if (asp->fi->state == XUA_ASP_S_INACTIVE &&
+ asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
+ LOGPASP(asp, DLM3UA, LOGL_NOTICE, "quirk snm_inactive active: "
+ "Accepting SNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
+ } else {
+ LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx M3UA SNM not permitted "
+ "while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
+ return M3UA_ERR_UNEXPECTED_MSG;
+ }
}
switch (asp->cfg.role) {
diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c
index 5ff4ca4..6d68290 100644
--- a/src/osmo_ss7.c
+++ b/src/osmo_ss7.c
@@ -196,6 +196,15 @@ uint8_t osmo_ss7_pc_width(const struct osmo_ss7_pc_fmt *pc_fmt)
return pc_fmt->component_len[0] + pc_fmt->component_len[1] + pc_fmt->component_len[2];
}
+/* truncate pc or mask to maximum permitted length. This solves
+ * callers specifying arbitrary large masks which then evade duplicate
+ * detection with longer mask lengths */
+uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc)
+{
+ uint32_t mask = (1 << osmo_ss7_pc_width(pc_fmt))-1;
+ return pc & mask;
+}
+
/* get the number of bits we must shift the given component of a point
* code in this ss7_instance */
static unsigned int get_pc_comp_shift(const struct osmo_ss7_pc_fmt *pc_fmt,
@@ -702,6 +711,9 @@ osmo_ss7_route_find_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc)
struct osmo_ss7_route *rt;
OSMO_ASSERT(ss7_initialized);
+
+ dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
+
/* we assume the routes are sorted by mask length, i.e. more
* specific routes first, and less specific routes with shorter
* mask later */
@@ -720,6 +732,9 @@ osmo_ss7_route_find_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc,
struct osmo_ss7_route *rt;
OSMO_ASSERT(ss7_initialized);
+ mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
+ dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
+
/* we assume the routes are sorted by mask length, i.e. more
* specific routes first, and less specific routes with shorter
* mask later */
@@ -775,6 +790,11 @@ osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc,
struct osmo_ss7_linkset *lset;
struct osmo_ss7_as *as = NULL;
+ /* truncate mask to maximum. Let's avoid callers specifying arbitrary large
+ * masks to ensure we don't fail duplicate detection with longer mask lengths */
+ mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
+ pc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, pc);
+
OSMO_ASSERT(ss7_initialized);
lset = osmo_ss7_linkset_find_by_name(rtbl->inst, linkset_name);
if (!lset) {
@@ -783,6 +803,15 @@ osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc,
return NULL;
}
+ /* check for duplicates */
+ rt = osmo_ss7_route_find_dpc_mask(rtbl, pc, mask);
+ if (rt && !strcmp(rt->cfg.linkset_name, linkset_name)) {
+ LOGSS7(rtbl->inst, LOGL_ERROR, "Refusing to create duplicate route: "
+ "pc=%u=%s mask=0x%x via linkset/AS '%s'\n",
+ pc, osmo_ss7_pointcode_print(rtbl->inst, pc), mask, linkset_name);
+ return rt;
+ }
+
rt = talloc_zero(rtbl, struct osmo_ss7_route);
if (!rt)
return NULL;
@@ -809,7 +838,13 @@ osmo_ss7_route_create(struct osmo_ss7_route_table *rtbl, uint32_t pc,
/*! \brief Destroy a given SS7 route */
void osmo_ss7_route_destroy(struct osmo_ss7_route *rt)
{
+ struct osmo_ss7_route_table *rtbl = rt->rtable;
+
OSMO_ASSERT(ss7_initialized);
+
+ LOGSS7(rtbl->inst, LOGL_INFO, "Destroying route: pc=%u=%s mask=0x%x via linkset/ASP '%s'\n",
+ rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc), rt->cfg.mask, rt->cfg.linkset_name);
+
llist_del(&rt->list);
talloc_free(rt);
}
@@ -1699,8 +1734,7 @@ static int xua_cli_connect_cb(struct osmo_stream_cli *cli)
struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli);
/* update the socket name */
- if (asp->sock_name)
- talloc_free(asp->sock_name);
+ talloc_free(asp->sock_name);
asp->sock_name = osmo_sock_get_name(asp, ofd->fd);
LOGPASP(asp, DLSS7, LOGL_INFO, "Client connected %s\n", asp->sock_name);
@@ -1834,8 +1868,7 @@ static int xua_srv_conn_closed_cb(struct osmo_stream_srv *srv)
{
struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(srv);
- LOGP(DLSS7, LOGL_INFO, "%s: SCTP connection closed\n",
- asp ? asp->cfg.name : "?");
+ LOGP(DLSS7, LOGL_INFO, "%s: connection closed\n", asp ? asp->cfg.name : "?");
if (!asp)
return 0;
@@ -1953,8 +1986,7 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd)
asp->server = srv;
asp->xua_server = oxs;
/* update the ASP socket name */
- if (asp->sock_name)
- talloc_free(asp->sock_name);
+ talloc_free(asp->sock_name);
asp->sock_name = talloc_reparent(link, asp, sock_name);
/* make sure the conn_cb() is called with the asp as private
* data */
diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c
index b3aa9ef..dcbe9b2 100644
--- a/src/osmo_ss7_vty.c
+++ b/src/osmo_ss7_vty.c
@@ -1,6 +1,6 @@
/* Core SS7 Instance/Linkset/Link/AS/ASP VTY Interface */
-/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2015-2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@@ -37,6 +37,8 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
+#include <osmocom/netif/stream.h>
+
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/mtp.h>
@@ -53,6 +55,19 @@
"MTP3 User Adaptation\n" \
"IPA Multiplex (SCCP Lite)\n"
+static const struct value_string asp_quirk_names[] = {
+ { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "no_notify" },
+ { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "daud_in_asp" },
+ { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "snm_inactive" },
+ { 0, NULL }
+};
+
+static const struct value_string asp_quirk_descs[] = {
+ { OSMO_SS7_ASP_QUIRK_NO_NOTIFY, "Peer SG doesn't send NTFY(AS-INACTIVE) after ASP-UP" },
+ { OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP, "Allow Rx of DAUD in ASP role" },
+ { OSMO_SS7_ASP_QUIRK_SNM_INACTIVE, "Allow Rx of [S]SNM in AS-INACTIVE state" },
+ { 0, NULL }
+};
/***********************************************************************
* Core CS7 Configuration
@@ -745,6 +760,36 @@ DEFUN_ATTR(asp_shutdown, asp_shutdown_cmd,
return CMD_WARNING;
}
+DEFUN_ATTR(asp_quirk, asp_quirk_cmd,
+ "OVERWRITTEN",
+ "OVERWRITTEN\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct osmo_ss7_asp *asp = vty->index;
+ int quirk = get_string_value(asp_quirk_names, argv[0]);
+
+ if (quirk < 0)
+ return CMD_WARNING;
+
+ asp->cfg.quirks |= quirk;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(asp_no_quirk, asp_no_quirk_cmd,
+ "OVERWRITTEN",
+ "OVERWRITTEN\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct osmo_ss7_asp *asp = vty->index;
+ int quirk = get_string_value(asp_quirk_names, argv[0]);
+
+ if (quirk < 0)
+ return CMD_WARNING;
+
+ asp->cfg.quirks &= ~quirk;
+ return CMD_SUCCESS;
+}
+
DEFUN(show_cs7_asp, show_cs7_asp_cmd,
"show cs7 instance <0-15> asp",
SHOW_STR CS7_STR INST_STR INST_STR "Application Server Process (ASP)\n")
@@ -765,7 +810,15 @@ DEFUN(show_cs7_asp, show_cs7_asp_cmd,
vty_out(vty, "------------ ------------ ------------- ---- ----------------------- ----------%s", VTY_NEWLINE);
llist_for_each_entry(asp, &inst->asp_list, list) {
- osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &asp->cfg.remote);
+ if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA && asp->cfg.remote.port == 0 && asp->server) {
+ struct osmo_fd *ofd = osmo_stream_srv_get_ofd(asp->server);
+ char hostbuf[64];
+ char portbuf[16];
+ osmo_sock_get_ip_and_port(ofd->fd, hostbuf, sizeof(hostbuf),
+ portbuf, sizeof(portbuf), false);
+ snprintf(buf, sizeof(buf), "%s:%s", hostbuf, portbuf);
+ } else
+ osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &asp->cfg.remote);
vty_out(vty, "%-12s %-12s %-13s %-4s %-14s %-10s%s",
asp->cfg.name, "?",
asp->fi? osmo_fsm_inst_state_name(asp->fi) : "uninitialized",
@@ -804,6 +857,11 @@ static void write_one_asp(struct vty *vty, struct osmo_ss7_asp *asp, bool show_d
}
if (!asp->cfg.is_server)
vty_out(vty, " sctp-role client%s", VTY_NEWLINE);
+ for (i = 0; i < 32; i++) {
+ if (!(asp->cfg.quirks & (1 << i)))
+ continue;
+ vty_out(vty, " quirk %s%s", get_value_string(asp_quirk_names, (1 << i)), VTY_NEWLINE);
+ }
}
@@ -2019,6 +2077,17 @@ static void vty_init_shared(void *ctx)
{
g_ctx = ctx;
+ asp_quirk_cmd.string = vty_cmd_string_from_valstr(ctx, asp_quirk_names,
+ "quirk (", "|", ")", VTY_DO_LOWER);
+ asp_quirk_cmd.doc = vty_cmd_string_from_valstr(ctx, asp_quirk_descs,
+ "Enable quirk to work around interop issues\n",
+ "\n", "\n", 0);
+ asp_no_quirk_cmd.string = vty_cmd_string_from_valstr(ctx, asp_quirk_names,
+ "no quirk (", "|", ")", VTY_DO_LOWER);
+ asp_no_quirk_cmd.doc = vty_cmd_string_from_valstr(ctx, asp_quirk_descs,
+ NO_STR "Disable quirk to work around interop issues\n",
+ "\n", "\n", 0);
+
install_lib_element_ve(&show_cs7_user_cmd);
install_lib_element_ve(&show_cs7_xua_cmd);
install_lib_element_ve(&show_cs7_config_cmd);
@@ -2047,6 +2116,8 @@ static void vty_init_shared(void *ctx)
install_lib_element(L_CS7_ASP_NODE, &asp_sctp_role_cmd);
install_lib_element(L_CS7_ASP_NODE, &asp_block_cmd);
install_lib_element(L_CS7_ASP_NODE, &asp_shutdown_cmd);
+ install_lib_element(L_CS7_ASP_NODE, &asp_quirk_cmd);
+ install_lib_element(L_CS7_ASP_NODE, &asp_no_quirk_cmd);
install_node(&as_node, NULL);
install_lib_element_ve(&show_cs7_as_cmd);
diff --git a/src/sccp_scoc.c b/src/sccp_scoc.c
index 1ce08f8..244e6d3 100644
--- a/src/sccp_scoc.c
+++ b/src/sccp_scoc.c
@@ -499,7 +499,10 @@ static struct sccp_connection *conn_create(struct osmo_sccp_user *user)
uint32_t conn_id;
do {
- conn_id = user->inst->next_id++;
+ /* modulo 2^24 as we currently use the connection ID also as local
+ * reference, and that is limited to 24 bits */
+ user->inst->next_id = (user->inst->next_id + 1) % (1 << 24);
+ conn_id = user->inst->next_id;
} while (conn_find_by_id(user->inst, conn_id));
return conn_create_id(user, conn_id);
diff --git a/src/sua.c b/src/sua.c
index 2ff5a9a..8415fa3 100644
--- a/src/sua.c
+++ b/src/sua.c
@@ -912,6 +912,18 @@ static int sua_rx_snm_asp(struct osmo_ss7_asp *asp, struct xua_msg *xua)
xua->hdr.msg_type);
/* silently ignore those to not confuse the sender */
break;
+ case SUA_SNM_DAUD:
+ /* RFC states only permitted in ASP->SG direction, not reverse. But some
+ * equipment still sends it to us as ASP ?!? */
+ if (asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_DAUD_IN_ASP) {
+ LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk daud_in_asp active: Accepting DAUD "
+ "despite being in ASP role\n");
+ xua_snm_rx_daud(asp, xua);
+ } else {
+ LOGPASP(asp, DLSUA, LOGL_ERROR, "DAUD not permitted in ASP role\n");
+ return SUA_ERR_UNSUPP_MSG_TYPE;
+ }
+ break;
default:
return SUA_ERR_UNSUPP_MSG_TYPE;
}
@@ -937,9 +949,14 @@ static int sua_rx_snm(struct osmo_ss7_asp *asp, struct xua_msg *xua)
{
/* SNM only permitted in ACTIVE state */
if (asp->fi->state != XUA_ASP_S_ACTIVE) {
- LOGPASP(asp, DLSUA, LOGL_NOTICE, "Received M3UA SNM while ASP in state %s\n",
- osmo_fsm_inst_state_name(asp->fi));
- return SUA_ERR_UNEXPECTED_MSG;
+ if (asp->fi->state == XUA_ASP_S_INACTIVE && asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_SNM_INACTIVE) {
+ LOGPASP(asp, DLSUA, LOGL_NOTICE, "quirk snm_inactive active: "
+ "Accepting SSNM in state %s\n", osmo_fsm_inst_state_name(asp->fi));
+ } else {
+ LOGPASP(asp, DLM3UA, LOGL_ERROR, "Rx SUA SSNM not permitted "
+ "while ASP in state %s\n", osmo_fsm_inst_state_name(asp->fi));
+ return SUA_ERR_UNEXPECTED_MSG;
+ }
}
switch (asp->cfg.role) {
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
index 7c791cf..2f52762 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -200,8 +200,78 @@ struct xua_as_fsm_priv {
struct osmo_timer_list t_r;
struct llist_head queued_msgs;
} recovery;
+ bool ipa_route_created;
};
+/* is the given AS one with a single ASP of IPA type? */
+static bool is_single_ipa_asp(struct osmo_ss7_as *as)
+{
+ unsigned int asp_count = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
+ struct osmo_ss7_asp *asp = as->cfg.asps[i];
+ if (!asp)
+ continue;
+ asp_count++;
+ if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA)
+ return false;
+ }
+ if (asp_count == 1)
+ return true;
+ return false;
+}
+
+static void ipa_add_route(struct osmo_fsm_inst *fi)
+{
+ struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
+ struct osmo_ss7_as *as = xafp->as;
+ struct osmo_ss7_instance *inst = as->inst;
+
+ if (osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff))
+ return;
+
+ /* As opposed to M3UA, there is no RKM and we have to implicitly
+ * automatically add a route once an IPA connection has come up */
+ if (osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff, as->cfg.name))
+ xafp->ipa_route_created = true;
+}
+
+static void ipa_del_route(struct osmo_fsm_inst *fi)
+{
+ struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
+ struct osmo_ss7_as *as = xafp->as;
+ struct osmo_ss7_instance *inst = as->inst;
+ struct osmo_ss7_route *rt;
+
+ /* don't delete a route if we added none */
+ if (!xafp->ipa_route_created)
+ return;
+
+ /* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
+ rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
+ /* no route found, bail out */
+ if (!rt) {
+ LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA AS, but cannot "
+ "find route for DPC %s. Did you manually delete it?\n",
+ osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc));
+ return;
+ }
+
+ /* route points to different AS, bail out */
+ if (rt->dest.as != as) {
+ LOGPFSML(fi, LOGL_NOTICE, "Attempting to delete route for this IPA ASP, but found "
+ "route for DPC %s points to different AS (%s)\n",
+ osmo_ss7_pointcode_print(inst, as->cfg.routing_key.pc), rt->dest.as->cfg.name);
+ return;
+ }
+
+ osmo_ss7_route_destroy(rt);
+ xafp->ipa_route_created = false;
+}
+
+
+
/* is any other ASP in this AS in state != DOWN? */
static bool check_any_other_asp_not_down(struct osmo_ss7_as *as, struct osmo_ss7_asp *asp_cmp)
{
@@ -309,12 +379,16 @@ static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
npar.status_info = M3UA_NOTIFY_I_AS_INACT;
break;
case XUA_AS_S_ACTIVE:
+ if (is_single_ipa_asp(as))
+ ipa_add_route(fi);
npar.status_info = M3UA_NOTIFY_I_AS_ACT;
break;
case XUA_AS_S_PENDING:
npar.status_info = M3UA_NOTIFY_I_AS_PEND;
break;
case XUA_AS_S_DOWN:
+ if (is_single_ipa_asp(as))
+ ipa_del_route(fi);
/* RFC4666 sec 4.3.2 AS States:
If we end up here, it means no ASP is ACTIVE or INACTIVE,
meaning no ASP can have already configured the traffic mode
diff --git a/src/xua_asp_fsm.c b/src/xua_asp_fsm.c
index c8dcac6..dd1dddb 100644
--- a/src/xua_asp_fsm.c
+++ b/src/xua_asp_fsm.c
@@ -29,6 +29,7 @@
#include "xua_asp_fsm.h"
#include "xua_as_fsm.h"
+#include "xua_internal.h"
#define S(x) (1 << (x))
@@ -866,7 +867,7 @@ static void ipa_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *dat
}
} else {
/* Client: We simply wait for an ID GET */
- osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_ACK, 10, T_WAIT_ID_ACK);
+ osmo_fsm_inst_state_chg(fi, IPA_ASP_S_WAIT_ID_GET, 10, T_WAIT_ID_GET);
}
break;
}
@@ -938,23 +939,11 @@ out_err:
/* 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)
{
- struct ipa_asp_fsm_priv *iafp = fi->priv;
- struct osmo_ss7_asp *asp = iafp->asp;
- struct osmo_ss7_instance *inst = asp->inst;
- /* We use routing-context '0' here, as that's the only one we support in IPA */
- struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
-
- OSMO_ASSERT(as);
-
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);
- /* As opposed to M3UA, there is no RKM and we have to implicitly automatically add
- * a route once an IPA connection has come up */
- osmo_ss7_route_create(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff,
- as->cfg.name);
break;
}
}
@@ -1010,38 +999,12 @@ static void ipa_asp_fsm_wait_id_ack(struct osmo_fsm_inst *fi, uint32_t event, vo
}
}
-static void ipa_asp_fsm_del_route(struct ipa_asp_fsm_priv *iafp)
-{
- struct osmo_ss7_asp *asp = iafp->asp;
- struct osmo_ss7_instance *inst = asp->inst;
- /* We use routing-context '0' here, as that's the only one we support in IPA */
- struct osmo_ss7_as *as = osmo_ss7_as_find_by_rctx(inst, 0);
- struct osmo_ss7_route *rt;
-
- OSMO_ASSERT(as);
-
- /* find the route which we have created if we ever reached ipa_asp_fsm_wait_id_ack2 */
- rt = osmo_ss7_route_find_dpc_mask(inst->rtable_system, as->cfg.routing_key.pc, 0xffffff);
- /* no route found, bail out */
- if (!rt)
- return;
- /* route points to different AS, bail out */
- if (rt->dest.as != as)
- return;
-
- osmo_ss7_route_destroy(rt);
- /* FIXME: Why don't we also delete this timer if we return early above?
- * FIXME: Where is this timer even scheduled? */
- osmo_timer_del(&iafp->pong_timer);
-}
-
/* 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:
- ipa_asp_fsm_del_route(fi->priv);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
break;
}
@@ -1051,7 +1014,6 @@ static void ipa_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void
{
switch (event) {
case XUA_ASP_E_M_ASP_DOWN_REQ:
- ipa_asp_fsm_del_route(fi->priv);
osmo_fsm_inst_state_chg(fi, IPA_ASP_S_DOWN, 0, 0);
break;
}
@@ -1119,7 +1081,7 @@ 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_ACK) |
+ .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,
@@ -1176,11 +1138,6 @@ static const struct osmo_fsm_state ipa_asp_states[] = {
},
};
-static void ipa_asp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
-{
- ipa_asp_fsm_del_route(fi->priv);
-}
-
struct osmo_fsm ipa_asp_fsm = {
.name = "IPA_ASP",
.states = ipa_asp_states,
@@ -1193,7 +1150,6 @@ struct osmo_fsm ipa_asp_fsm = {
S(XUA_ASP_E_ASPSM_BEAT) |
S(XUA_ASP_E_ASPSM_BEAT_ACK),
.allstate_action = ipa_asp_allstate,
- .cleanup = ipa_asp_fsm_cleanup,
};
diff --git a/src/xua_default_lm_fsm.c b/src/xua_default_lm_fsm.c
index 338f4ae..ca488a7 100644
--- a/src/xua_default_lm_fsm.c
+++ b/src/xua_default_lm_fsm.c
@@ -1,5 +1,5 @@
/* Default XUA Layer Manager */
-/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2017-2021 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@@ -170,6 +170,13 @@ static int lm_timer_cb(struct osmo_fsm_inst *fi)
restart_asp(fi);
break;
case T_WAIT_NOTIFY:
+ if (lmp->asp->cfg.quirks & OSMO_SS7_ASP_QUIRK_NO_NOTIFY) {
+ /* some implementations don't send the NOTIFY which they SHOULD
+ * according to RFC4666 (see OS#5145) */
+ LOGPFSM(fi, "quirk no_notify active; locally emulate AS-INACTIVE.ind\n");
+ osmo_fsm_inst_dispatch(fi, LM_E_AS_INACTIVE_IND, NULL);
+ break;
+ }
/* No AS has reported via NOTIFY that is was
* (statically) configured at the SG for this ASP, so
* let's dynamically register */
diff --git a/tests/vty/ss7_asp_test.vty b/tests/vty/ss7_asp_test.vty
index ee1b3e5..fd54642 100644
--- a/tests/vty/ss7_asp_test.vty
+++ b/tests/vty/ss7_asp_test.vty
@@ -222,6 +222,7 @@ ss7_asp_vty_test(config-cs7-asp)# list
sctp-role (client|server)
block
shutdown
+...
ss7_asp_vty_test(config-cs7-asp)# ?
...
@@ -233,6 +234,7 @@ ss7_asp_vty_test(config-cs7-asp)# ?
sctp-role Specify the SCTP role for this ASP
block Allows a SCTP Association with ASP, but doesn't let it become active
shutdown Terminates SCTP association; New associations will be rejected
+...
ss7_asp_vty_test(config-cs7-asp)# remote-ip 127.0.0.200
ss7_asp_vty_test(config-cs7-asp)# local-ip 127.0.0.100