2015-12-22 21:22:23 +00:00
|
|
|
/* Minimal implementation of RFC 3868 - SCCP User Adaptation Layer */
|
|
|
|
|
2016-01-04 11:35:49 +00:00
|
|
|
/* (C) 2015-2017 by Harald Welte <laforge@gnumonks.org>
|
2015-12-22 21:22:23 +00:00
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2017-04-03 15:04:00 +00:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
2015-12-22 21:22:23 +00:00
|
|
|
* (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
|
2017-04-03 15:04:00 +00:00
|
|
|
* GNU General Public License for more details.
|
2015-12-22 21:22:23 +00:00
|
|
|
*
|
2017-04-03 15:04:00 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2015-12-22 21:22:23 +00:00
|
|
|
* 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>
|
2016-01-04 11:35:49 +00:00
|
|
|
#include <osmocom/core/socket.h>
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
#include <osmocom/netif/stream.h>
|
|
|
|
#include <osmocom/sigtran/xua_msg.h>
|
|
|
|
|
|
|
|
#include <osmocom/sigtran/sccp_sap.h>
|
|
|
|
#include <osmocom/sigtran/protocol/sua.h>
|
|
|
|
#include <osmocom/sigtran/sua.h>
|
|
|
|
|
2017-04-03 17:24:06 +00:00
|
|
|
#include "xua_internal.h"
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
#define SUA_MSGB_SIZE 1500
|
|
|
|
|
|
|
|
/* Appendix C.4 of Q.714 (all in milliseconds) */
|
|
|
|
#define CONNECTION_TIMER ( 1 * 60 * 100)
|
|
|
|
#define TX_INACT_TIMER ( 7 * 60 * 100) /* RFC 3868 Ch. 8. */
|
|
|
|
#define RX_INACT_TIMER (15 * 60 * 100) /* RFC 3868 Ch. 8. */
|
|
|
|
#define RELEASE_TIMER ( 10 * 100)
|
|
|
|
#define RELEASE_REP_TIMER ( 10 * 100)
|
|
|
|
#define INT_TIMER ( 1 * 60 * 100)
|
|
|
|
#define GUARD_TIMER (23 * 60 * 100)
|
|
|
|
#define RESET_TIMER ( 10 * 100)
|
|
|
|
|
2017-04-03 17:46:20 +00:00
|
|
|
#define SCCP_MSG_SIZE 2048
|
|
|
|
#define SCCP_MSG_HEADROOM 512
|
|
|
|
|
|
|
|
struct msgb *sccp_msgb_alloc(const char *name)
|
|
|
|
{
|
|
|
|
if (!name)
|
|
|
|
name = "SCCP";
|
|
|
|
return msgb_alloc_headroom(SCCP_MSG_SIZE+SCCP_MSG_HEADROOM,
|
|
|
|
SCCP_MSG_HEADROOM, name);
|
|
|
|
}
|
|
|
|
|
2017-04-03 17:24:06 +00:00
|
|
|
/***********************************************************************
|
|
|
|
* Protocol Definition (string tables, mandatory IE checking)
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
static const struct value_string sua_iei_names[] = {
|
|
|
|
{ SUA_IEI_ROUTE_CTX, "Routing Context" },
|
|
|
|
{ SUA_IEI_CORR_ID, "Correlation Id" },
|
|
|
|
{ SUA_IEI_REG_RESULT, "Registration Result" },
|
|
|
|
{ SUA_IEI_DEREG_RESULT, "De-Registration Result" },
|
|
|
|
|
|
|
|
{ SUA_IEI_S7_HOP_CTR, "SS7 Hop Counter" },
|
|
|
|
{ SUA_IEI_SRC_ADDR, "Source Address" },
|
|
|
|
{ SUA_IEI_DEST_ADDR, "Destination Address" },
|
|
|
|
{ SUA_IEI_SRC_REF, "Source Reference" },
|
|
|
|
{ SUA_IEI_DEST_REF, "Destination Reference" },
|
|
|
|
{ SUA_IEI_CAUSE, "Cause" },
|
|
|
|
{ SUA_IEI_SEQ_NR, "Sequence Number" },
|
|
|
|
{ SUA_IEI_RX_SEQ_NR, "Receive Sequence Number" },
|
|
|
|
{ SUA_IEI_ASP_CAPA, "ASP Capability" },
|
|
|
|
{ SUA_IEI_CREDIT, "Credit" },
|
|
|
|
{ SUA_IEI_DATA, "Data" },
|
|
|
|
{ SUA_IEI_USER_CAUSE, "User/Cause" },
|
|
|
|
{ SUA_IEI_NET_APPEARANCE, "Network Appearance" },
|
|
|
|
{ SUA_IEI_ROUTING_KEY, "Routing Key" },
|
|
|
|
{ SUA_IEI_DRN, "DRN Label" },
|
|
|
|
{ SUA_IEI_TID, "TID Label" },
|
|
|
|
{ SUA_IEI_SMI, "SMI" },
|
|
|
|
{ SUA_IEI_IMPORTANCE, "Importance" },
|
|
|
|
{ SUA_IEI_MSG_PRIO, "Message Priority" },
|
|
|
|
{ SUA_IEI_PROTO_CLASS, "Protocol Class" },
|
|
|
|
{ SUA_IEI_SEQ_CTRL, "Sequence Control" },
|
|
|
|
{ SUA_IEI_SEGMENTATION, "Segmentation" },
|
|
|
|
{ SUA_IEI_CONG_LEVEL, "Congestion Level" },
|
|
|
|
|
|
|
|
{ SUA_IEI_GT, "Global Title" },
|
|
|
|
{ SUA_IEI_PC, "Point Code" },
|
|
|
|
{ SUA_IEI_SSN, "Sub-System Number" },
|
|
|
|
{ SUA_IEI_IPv4, "IPv4 Address" },
|
|
|
|
{ SUA_IEI_HOST, "Host Name" },
|
|
|
|
{ SUA_IEI_IPv6, "IPv6 Address" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MAND_IES(msgt, ies) [msgt] = (ies)
|
|
|
|
|
|
|
|
static const uint16_t cldt_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_ADDR,
|
|
|
|
SUA_IEI_DEST_ADDR, SUA_IEI_SEQ_CTRL, SUA_IEI_DATA, 0
|
|
|
|
};
|
|
|
|
static const uint16_t cldr_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_CAUSE, SUA_IEI_SRC_ADDR,
|
|
|
|
SUA_IEI_DEST_ADDR, 0
|
|
|
|
};
|
|
|
|
static const struct value_string sua_cl_msgt_names[] = {
|
|
|
|
{ SUA_CL_CLDT, "CLDT" },
|
|
|
|
{ SUA_CL_CLDR, "CLDR" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
static const struct xua_msg_class msg_class_cl = {
|
|
|
|
.name = "CL",
|
|
|
|
.msgt_names = sua_cl_msgt_names,
|
|
|
|
.iei_names = sua_iei_names,
|
|
|
|
.mand_ies = {
|
|
|
|
MAND_IES(SUA_CL_CLDT, cldt_mand_ies),
|
|
|
|
MAND_IES(SUA_CL_CLDR, cldr_mand_ies),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uint16_t codt_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_DATA, 0
|
|
|
|
};
|
|
|
|
static const uint16_t coda_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, 0
|
|
|
|
};
|
|
|
|
static const uint16_t core_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
|
|
|
|
SUA_IEI_DEST_ADDR, SUA_IEI_SEQ_CTRL, 0
|
|
|
|
};
|
|
|
|
static const uint16_t coak_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_DEST_REF,
|
|
|
|
SUA_IEI_SRC_REF, SUA_IEI_SEQ_CTRL, 0
|
|
|
|
};
|
|
|
|
static const uint16_t coref_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0
|
|
|
|
};
|
|
|
|
static const uint16_t relre_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF,
|
|
|
|
SUA_IEI_CAUSE, 0
|
|
|
|
};
|
|
|
|
static const uint16_t relco_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0
|
|
|
|
};
|
|
|
|
static const uint16_t resre_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF,
|
|
|
|
SUA_IEI_CAUSE, 0
|
|
|
|
};
|
|
|
|
static const uint16_t resco_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_SRC_REF, 0
|
|
|
|
};
|
|
|
|
static const uint16_t coerr_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_DEST_REF, SUA_IEI_CAUSE, 0
|
|
|
|
};
|
|
|
|
static const uint16_t coit_mand_ies[] = {
|
|
|
|
SUA_IEI_ROUTE_CTX, SUA_IEI_PROTO_CLASS, SUA_IEI_SRC_REF,
|
|
|
|
SUA_IEI_DEST_REF, 0
|
|
|
|
};
|
|
|
|
static const struct value_string sua_co_msgt_names[] = {
|
|
|
|
{ SUA_CO_CODT, "CODT" },
|
|
|
|
{ SUA_CO_CODA, "CODA" },
|
|
|
|
{ SUA_CO_CORE, "CORE" },
|
|
|
|
{ SUA_CO_COAK, "COAK" },
|
|
|
|
{ SUA_CO_COREF, "COREF" },
|
|
|
|
{ SUA_CO_RELRE, "RELRE" },
|
|
|
|
{ SUA_CO_RELCO, "RELCO" },
|
|
|
|
{ SUA_CO_RESRE, "RESRE" },
|
|
|
|
{ SUA_CO_RESCO, "RESCO" },
|
|
|
|
{ SUA_CO_COERR, "COERR" },
|
|
|
|
{ SUA_CO_COIT, "COIT" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
static const struct xua_msg_class msg_class_co = {
|
|
|
|
.name = "CO",
|
|
|
|
.msgt_names = sua_co_msgt_names,
|
|
|
|
.iei_names = sua_iei_names,
|
|
|
|
.mand_ies = {
|
|
|
|
MAND_IES(SUA_CO_CODT, codt_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_CODA, coda_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_CORE, core_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_COAK, coak_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_COREF, coref_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_RELRE, relre_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_RELCO, relco_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_RESRE, resre_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_RESCO, resco_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_COERR, coerr_mand_ies),
|
|
|
|
MAND_IES(SUA_CO_COIT, coit_mand_ies),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct xua_dialect xua_dialect_sua = {
|
|
|
|
.name = "SUA",
|
|
|
|
.ppid = SUA_PPID,
|
|
|
|
.port = SUA_PORT,
|
|
|
|
.class = {
|
|
|
|
[SUA_MSGC_MGMT] = &m3ua_msg_class_mgmt,
|
|
|
|
[SUA_MSGC_SNM] = &m3ua_msg_class_snm,
|
|
|
|
[SUA_MSGC_ASPSM] = &m3ua_msg_class_aspsm,
|
|
|
|
[SUA_MSGC_ASPTM] = &m3ua_msg_class_asptm,
|
|
|
|
[SUA_MSGC_CL] = &msg_class_cl,
|
|
|
|
[SUA_MSGC_CO] = &msg_class_co,
|
|
|
|
[SUA_MSGC_RKM] = &m3ua_msg_class_rkm,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
static int DSUA = -1;
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_user {
|
2015-12-22 21:22:23 +00:00
|
|
|
/* global list of SUA users? */
|
|
|
|
struct llist_head list;
|
|
|
|
/* set if we are a server */
|
|
|
|
struct osmo_stream_srv_link *server;
|
|
|
|
struct osmo_stream_cli *client;
|
|
|
|
struct llist_head links;
|
|
|
|
/* user call-back function in case of incoming primitives */
|
|
|
|
osmo_prim_cb prim_cb;
|
2015-12-26 22:34:03 +00:00
|
|
|
void *priv;
|
2015-12-22 21:22:23 +00:00
|
|
|
};
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link {
|
2015-12-22 21:22:23 +00:00
|
|
|
/* list of SUA links per sua_user */
|
|
|
|
struct llist_head list;
|
|
|
|
/* sua user to which we belong */
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_user *user;
|
2015-12-22 21:22:23 +00:00
|
|
|
/* local list of (SCCP) connections in this link */
|
|
|
|
struct llist_head connections;
|
|
|
|
/* next connection local reference */
|
|
|
|
uint32_t next_id;
|
|
|
|
int is_server;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum sua_connection_state {
|
|
|
|
S_IDLE,
|
|
|
|
S_CONN_PEND_IN,
|
|
|
|
S_CONN_PEND_OUT,
|
|
|
|
S_ACTIVE,
|
|
|
|
S_DISCONN_PEND,
|
|
|
|
S_RESET_IN,
|
|
|
|
S_RESET_OUT,
|
|
|
|
S_BOTHWAY_RESET,
|
|
|
|
S_WAIT_CONN_CONF,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct value_string conn_state_names[] = {
|
|
|
|
{ S_IDLE, "IDLE" },
|
|
|
|
{ S_CONN_PEND_IN, "CONN_PEND_IN" },
|
|
|
|
{ S_CONN_PEND_OUT, "CONN_PEND_OUT" },
|
|
|
|
{ S_ACTIVE, "ACTIVE" },
|
|
|
|
{ S_DISCONN_PEND, "DISCONN_PEND" },
|
|
|
|
{ S_RESET_IN, "RESET_IN" },
|
|
|
|
{ S_RESET_OUT, "RESET_OUT" },
|
|
|
|
{ S_BOTHWAY_RESET, "BOTHWAY_RESET" },
|
|
|
|
{ S_WAIT_CONN_CONF, "WAIT_CONN_CONF" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sua_connection {
|
|
|
|
struct llist_head list;
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *link;
|
2015-12-22 21:22:23 +00:00
|
|
|
struct osmo_sccp_addr calling_addr;
|
|
|
|
struct osmo_sccp_addr called_addr;
|
|
|
|
uint32_t conn_id;
|
|
|
|
uint32_t remote_ref;
|
|
|
|
enum sua_connection_state state;
|
|
|
|
struct osmo_timer_list timer;
|
|
|
|
/* inactivity timers */
|
|
|
|
struct osmo_timer_list tias;
|
|
|
|
struct osmo_timer_list tiar;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* SUA Link and Connection handling
|
|
|
|
***********************************************************************/
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static struct osmo_sccp_link *sua_link_new(struct osmo_sccp_user *user, int is_server)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *link;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
link = talloc_zero(user, struct osmo_sccp_link);
|
2015-12-22 21:22:23 +00:00
|
|
|
if (!link)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
link->user = user;
|
|
|
|
link->is_server = is_server;
|
|
|
|
INIT_LLIST_HEAD(&link->connections);
|
|
|
|
|
|
|
|
llist_add_tail(&link->list, &user->links);
|
|
|
|
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_destroy(struct sua_connection *conn);
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static void sua_link_destroy(struct osmo_sccp_link *link)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct sua_connection *conn;
|
|
|
|
|
|
|
|
llist_for_each_entry(conn, &link->connections, list)
|
|
|
|
conn_destroy(conn);
|
|
|
|
|
|
|
|
llist_del(&link->list);
|
|
|
|
|
|
|
|
/* FIXME: do we need to cleanup the sccp link? */
|
|
|
|
|
|
|
|
talloc_free(link);
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_link_send(struct osmo_sccp_link *link, struct msgb *msg)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
msgb_sctp_ppid(msg) = SUA_PPID;
|
|
|
|
|
2016-01-04 11:35:49 +00:00
|
|
|
DEBUGP(DSUA, "sua_link_send(%s)\n", osmo_hexdump(msg->data, msgb_length(msg)));
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
if (link->is_server)
|
|
|
|
osmo_stream_srv_send(link->data, msg);
|
|
|
|
else
|
|
|
|
osmo_stream_cli_send(link->data, msg);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static struct sua_connection *conn_find_by_id(struct osmo_sccp_link *link, uint32_t id)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct sua_connection *conn;
|
|
|
|
|
|
|
|
llist_for_each_entry(conn, &link->connections, list) {
|
|
|
|
if (conn->conn_id == id)
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tx_inact_tmr_cb(void *data)
|
|
|
|
{
|
|
|
|
struct sua_connection *conn = data;
|
|
|
|
struct xua_msg *xua = xua_msg_alloc();
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
/* encode + send the CLDT */
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COIT);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, 2);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
2016-03-07 13:15:17 +00:00
|
|
|
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
2015-12-22 21:22:23 +00:00
|
|
|
/* optional: sequence number; credit (both class 3 only) */
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
sua_link_send(conn->link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void rx_inact_tmr_cb(void *data)
|
|
|
|
{
|
|
|
|
struct sua_connection *conn = data;
|
|
|
|
|
|
|
|
/* FIXME: release connection */
|
|
|
|
/* Send N-DISCONNECT.ind to local user */
|
|
|
|
/* Send RLSD to peer */
|
|
|
|
/* enter disconnect pending state with release timer pending */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static struct sua_connection *conn_create_id(struct osmo_sccp_link *link, uint32_t conn_id)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct sua_connection *conn = talloc_zero(link, struct sua_connection);
|
|
|
|
|
|
|
|
conn->conn_id = conn_id;
|
|
|
|
conn->link = link;
|
|
|
|
conn->state = S_IDLE;
|
|
|
|
|
|
|
|
llist_add_tail(&conn->list, &link->connections);
|
|
|
|
|
|
|
|
conn->tias.cb = tx_inact_tmr_cb;
|
|
|
|
conn->tias.data = conn;
|
|
|
|
conn->tiar.cb = rx_inact_tmr_cb;
|
|
|
|
conn->tiar.data = conn;
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static struct sua_connection *conn_create(struct osmo_sccp_link *link)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
uint32_t conn_id;
|
|
|
|
|
|
|
|
do {
|
|
|
|
conn_id = link->next_id++;
|
|
|
|
} while (conn_find_by_id(link, conn_id));
|
|
|
|
|
|
|
|
return conn_create_id(link, conn_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_destroy(struct sua_connection *conn)
|
|
|
|
{
|
|
|
|
/* FIXME: do some cleanup; inform user? */
|
|
|
|
osmo_timer_del(&conn->tias);
|
|
|
|
osmo_timer_del(&conn->tiar);
|
|
|
|
llist_del(&conn->list);
|
|
|
|
talloc_free(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_state_set(struct sua_connection *conn,
|
|
|
|
enum sua_connection_state state)
|
|
|
|
{
|
|
|
|
DEBUGP(DSUA, "(%u) state chg %s->", conn->conn_id,
|
|
|
|
get_value_string(conn_state_names, conn->state));
|
|
|
|
DEBUGPC(DSUA, "%s\n",
|
|
|
|
get_value_string(conn_state_names, state));
|
|
|
|
conn->state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_restart_tx_inact_timer(struct sua_connection *conn)
|
|
|
|
{
|
|
|
|
osmo_timer_schedule(&conn->tias, TX_INACT_TIMER / 100,
|
|
|
|
(TX_INACT_TIMER % 100) * 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_restart_rx_inact_timer(struct sua_connection *conn)
|
|
|
|
{
|
|
|
|
osmo_timer_schedule(&conn->tiar, RX_INACT_TIMER / 100,
|
|
|
|
(RX_INACT_TIMER % 100) * 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_start_inact_timers(struct sua_connection *conn)
|
|
|
|
{
|
|
|
|
conn_restart_tx_inact_timer(conn);
|
|
|
|
conn_restart_rx_inact_timer(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Handling of messages from the User SAP
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/* user program sends us a N-CONNNECT.req to initiate a new connection */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_connect_req(struct osmo_sccp_link *link, struct osmo_scu_prim *prim)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_connect_param *par = &prim->u.connect;
|
|
|
|
struct xua_msg *xua = xua_msg_alloc();
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
if (par->sccp_class != 2) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-CONNECT.req for unsupported "
|
|
|
|
"SCCP class %u\n", par->sccp_class);
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = conn_create_id(link, par->conn_id);
|
|
|
|
if (!conn) {
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&conn->called_addr, &par->called_addr,
|
|
|
|
sizeof(conn->called_addr));
|
|
|
|
memcpy(&conn->calling_addr, &par->calling_addr,
|
|
|
|
sizeof(conn->calling_addr));
|
|
|
|
|
|
|
|
/* encode + send the CLDT */
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, par->sccp_class);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, 0); /* FIXME */
|
|
|
|
/* sequence number */
|
|
|
|
if (par->calling_addr.presence)
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
|
|
|
|
/* optional: hop count; importance; priority; credit */
|
|
|
|
if (msgb_l2(prim->oph.msg))
|
|
|
|
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
|
|
|
msgb_l2(prim->oph.msg));
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
/* FIXME: Start CONNECTION_TIMER */
|
|
|
|
conn_state_set(conn, S_CONN_PEND_OUT);
|
|
|
|
|
|
|
|
return sua_link_send(link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* user program sends us a N-CONNNECT.resp, presumably against a
|
|
|
|
* N-CONNECT.ind */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_connect_resp(struct osmo_sccp_link *link, struct osmo_scu_prim *prim)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_connect_param *par = &prim->u.connect;
|
|
|
|
struct xua_msg *xua = xua_msg_alloc();
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
/* check if we already know a connection for this conn_id */
|
|
|
|
conn = conn_find_by_id(link, par->conn_id);
|
|
|
|
if (!conn) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-CONNECT.resp for unknown "
|
|
|
|
"connection ID %u\n", par->conn_id);
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->state != S_CONN_PEND_IN) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-CONNECT.resp in wrong state %s\n",
|
|
|
|
get_value_string(conn_state_names, conn->state));
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* encode + send the COAK message */
|
|
|
|
xua = xua_msg_alloc();
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_COAK);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, par->sccp_class);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, 0); /* FIXME */
|
|
|
|
/* sequence number */
|
|
|
|
if (par->calling_addr.presence)
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
|
|
|
|
/* optional: hop count; importance; priority */
|
|
|
|
/* FIXME: destination address will be present in case the CORE
|
|
|
|
* message conveys the source address parameter */
|
|
|
|
if (par->called_addr.presence)
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
|
|
|
|
if (msgb_l2(prim->oph.msg))
|
|
|
|
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
|
|
|
msgb_l2(prim->oph.msg));
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
conn_state_set(conn, S_ACTIVE);
|
|
|
|
conn_start_inact_timers(conn);
|
|
|
|
|
|
|
|
return sua_link_send(link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* user wants to send connection-oriented data */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_data_req(struct osmo_sccp_link *link, struct osmo_scu_prim *prim)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_data_param *par = &prim->u.data;
|
|
|
|
struct xua_msg *xua;
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
/* check if we know about this conncetion, and obtain reference */
|
|
|
|
conn = conn_find_by_id(link, par->conn_id);
|
|
|
|
if (!conn) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-DATA.req for unknown "
|
|
|
|
"connection ID %u\n", par->conn_id);
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->state != S_ACTIVE) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-DATA.req in wrong state %s\n",
|
|
|
|
get_value_string(conn_state_names, conn->state));
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_restart_tx_inact_timer(conn);
|
|
|
|
|
|
|
|
/* encode + send the CODT message */
|
|
|
|
xua = xua_msg_alloc();
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CODT);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
/* Sequence number only in expedited data */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
|
|
|
/* optional: priority; correlation id */
|
|
|
|
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
|
|
|
msgb_l2(prim->oph.msg));
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
return sua_link_send(link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* user wants to disconnect a connection */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_disconnect_req(struct osmo_sccp_link *link, struct osmo_scu_prim *prim)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_disconn_param *par = &prim->u.disconnect;
|
|
|
|
struct xua_msg *xua;
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
/* resolve reference of connection */
|
|
|
|
conn = conn_find_by_id(link, par->conn_id);
|
|
|
|
if (!conn) {
|
2015-12-26 22:34:54 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "N-DISCONNECT.req for unknown "
|
2015-12-22 21:22:23 +00:00
|
|
|
"connection ID %u\n", par->conn_id);
|
|
|
|
/* FIXME: Send primitive to user */
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* encode + send the RELRE */
|
|
|
|
xua = xua_msg_alloc();
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_RELRE);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_DEST_REF, conn->remote_ref);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SRC_REF, conn->conn_id);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_CAUSE, par->cause);
|
|
|
|
/* optional: importance */
|
|
|
|
if (msgb_l2(prim->oph.msg))
|
|
|
|
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
|
|
|
msgb_l2(prim->oph.msg));
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
conn_state_set(conn, S_DISCONN_PEND);
|
2015-12-26 22:35:31 +00:00
|
|
|
conn_destroy(conn);
|
2015-12-22 21:22:23 +00:00
|
|
|
|
2016-01-04 11:35:49 +00:00
|
|
|
LOGP(DSUA, LOGL_NOTICE, "About to send the SUA RELRE\n");
|
2015-12-22 21:22:23 +00:00
|
|
|
return sua_link_send(link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* user wants to send connectionless data */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_unitdata_req(struct osmo_sccp_link *link, struct osmo_scu_prim *prim)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_unitdata_param *par = &prim->u.unitdata;
|
|
|
|
struct xua_msg *xua = xua_msg_alloc();
|
|
|
|
struct msgb *outmsg;
|
|
|
|
|
|
|
|
/* encode + send the CLDT */
|
|
|
|
xua->hdr = XUA_HDR(SUA_MSGC_CL, SUA_CL_CLDT);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_ROUTE_CTX, 0); /* FIXME */
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_PROTO_CLASS, 0);
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_SRC_ADDR, &par->calling_addr);
|
|
|
|
xua_msg_add_sccp_addr(xua, SUA_IEI_DEST_ADDR, &par->called_addr);
|
|
|
|
xua_msg_add_u32(xua, SUA_IEI_SEQ_CTRL, par->in_sequence_control);
|
|
|
|
/* optional: importance, ... correlation id? */
|
|
|
|
xua_msg_add_data(xua, SUA_IEI_DATA, msgb_l2len(prim->oph.msg),
|
|
|
|
msgb_l2(prim->oph.msg));
|
|
|
|
|
|
|
|
outmsg = xua_to_msg(1, xua);
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
return sua_link_send(link, outmsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* user hands us a SCCP-USER SAP primitive down into the stack */
|
2016-07-06 13:51:36 +00:00
|
|
|
int osmo_sua_user_link_down(struct osmo_sccp_link *link, struct osmo_prim_hdr *oph)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
|
|
|
struct msgb *msg = prim->oph.msg;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
LOGP(DSUA, LOGL_DEBUG, "Received SCCP User Primitive (%s)\n",
|
|
|
|
osmo_scu_prim_name(&prim->oph));
|
|
|
|
|
|
|
|
switch (OSMO_PRIM_HDR(&prim->oph)) {
|
|
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST):
|
|
|
|
rc = sua_connect_req(link, prim);
|
|
|
|
break;
|
|
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_RESPONSE):
|
|
|
|
rc = sua_connect_resp(link, prim);
|
|
|
|
break;
|
|
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST):
|
|
|
|
rc = sua_data_req(link, prim);
|
|
|
|
break;
|
|
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_REQUEST):
|
|
|
|
rc = sua_disconnect_req(link, prim);
|
|
|
|
break;
|
|
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST):
|
|
|
|
rc = sua_unitdata_req(link, prim);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc != 1)
|
|
|
|
msgb_free(msg);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* Receiving SUA messsages from SCTP
|
|
|
|
***********************************************************************/
|
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
/*! \brief Decode SUA Global Title according to RFC3868 Section 3.10.2.3
|
|
|
|
* \param[out] gt User-allocated structure for decoded output
|
|
|
|
* \param[in] data binary-encoded data
|
|
|
|
* \param[in] datalen length of \ref data in octets
|
|
|
|
*/
|
|
|
|
int sua_parse_gt(struct osmo_sccp_gt *gt, const uint8_t *data, unsigned int datalen)
|
|
|
|
{
|
|
|
|
uint8_t num_digits;
|
|
|
|
char *out_digits;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
/* 8 byte header at minimum, plus digits */
|
|
|
|
if (datalen < 8)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* parse header */
|
|
|
|
gt->gti = data[3];
|
|
|
|
num_digits = data[4];
|
|
|
|
gt->tt = data[5];
|
|
|
|
gt->npi = data[6];
|
|
|
|
gt->nai = data[7];
|
|
|
|
|
|
|
|
/* parse digits */
|
|
|
|
out_digits = gt->digits;
|
|
|
|
for (i = 0; i < datalen-8; i++) {
|
|
|
|
uint8_t byte = data[8+i];
|
|
|
|
*out_digits++ = osmo_bcd2char(byte & 0x0F);
|
|
|
|
if (out_digits - gt->digits >= num_digits)
|
|
|
|
break;
|
|
|
|
*out_digits++ = osmo_bcd2char(byte >> 4);
|
|
|
|
if (out_digits - gt->digits >= num_digits)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*out_digits++ = '\0';
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \brief parse SCCP address from given xUA message part
|
|
|
|
* \param[out] out caller-allocated decoded SCCP address struct
|
|
|
|
* \param[in] param xUA message part containing address
|
|
|
|
\returns 0 on success; negative on error */
|
|
|
|
int sua_addr_parse_part(struct osmo_sccp_addr *out,
|
|
|
|
const struct xua_msg_part *param)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
2016-04-13 17:08:18 +00:00
|
|
|
const struct xua_parameter_hdr *par;
|
|
|
|
uint16_t ri;
|
|
|
|
uint16_t ai;
|
|
|
|
uint16_t pos;
|
|
|
|
uint16_t par_tag, par_len, par_datalen;
|
|
|
|
uint32_t *p32;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
memset(out, 0, sizeof(*out));
|
2015-12-22 21:22:23 +00:00
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
LOGP(DSUA, LOGL_DEBUG, "%s(IEI=0x%04x) (%d) %s\n", __func__,
|
|
|
|
param->tag, param->len,
|
2016-04-13 17:08:18 +00:00
|
|
|
osmo_hexdump(param->dat, param->len));
|
|
|
|
|
|
|
|
if (param->len < 4) {
|
2017-04-03 17:25:45 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "SUA IEI 0x%04x: invalid address length: %d\n",
|
|
|
|
param->tag, param->len);
|
2016-04-13 17:08:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
ri = ntohs(*(uint16_t*) ¶m->dat[pos]);
|
|
|
|
pos += 2;
|
|
|
|
ai = ntohs(*(uint16_t*) ¶m->dat[pos]);
|
|
|
|
pos += 2;
|
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
switch (ri) {
|
|
|
|
case SUA_RI_GT:
|
|
|
|
out->ri = OSMO_SCCP_RI_GT;
|
|
|
|
break;
|
|
|
|
case SUA_RI_SSN_PC:
|
|
|
|
out->ri = OSMO_SCCP_RI_SSN_PC;
|
|
|
|
break;
|
|
|
|
case SUA_RI_SSN_IP:
|
|
|
|
out->ri = OSMO_SCCP_RI_SSN_IP;
|
|
|
|
break;
|
|
|
|
case SUA_RI_HOST:
|
|
|
|
default:
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "SUA IEI 0x%04x: Routing Indicator not supported yet: %d\n",
|
|
|
|
param->tag, ri);
|
2016-04-13 17:08:18 +00:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ai != 7) {
|
2017-04-03 17:25:45 +00:00
|
|
|
#if 0
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "SUA IEI 0x%04x: Address Indicator not supported yet: %x\n",
|
|
|
|
param->tag, ai);
|
2016-04-13 17:08:18 +00:00
|
|
|
return -ENOTSUP;
|
2017-04-03 17:25:45 +00:00
|
|
|
#endif
|
2016-04-13 17:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: this parses the encapsulated T16L16V IEs on the go. We
|
|
|
|
* probably want to have a separate general parsing function storing
|
|
|
|
* the subparts in xua_msg_part. But before we do, we should find more
|
|
|
|
* users of this subpart parsing and be aware of the performance
|
|
|
|
* tradeoff.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (pos + sizeof(*par) < param->len) {
|
|
|
|
par = (struct xua_parameter_hdr *) ¶m->dat[pos];
|
|
|
|
par_tag = ntohs(par->tag);
|
|
|
|
par_len = ntohs(par->len);
|
|
|
|
par_datalen = par_len - sizeof(*par);
|
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
LOGP(DSUA, LOGL_DEBUG, "SUA IEI 0x%04x pos %hu/%hu: subpart tag 0x%04x, len %hu\n",
|
|
|
|
param->tag, pos, param->len, par_tag, par_len);
|
2016-04-13 17:08:18 +00:00
|
|
|
|
|
|
|
switch (par_tag) {
|
|
|
|
case SUA_IEI_PC:
|
|
|
|
if (par_datalen != 4)
|
|
|
|
goto subpar_fail;
|
|
|
|
p32 = (uint32_t*)par->data;
|
|
|
|
out->pc = ntohl(*p32);
|
|
|
|
out->presence |= OSMO_SCCP_ADDR_T_PC;
|
|
|
|
break;
|
|
|
|
case SUA_IEI_SSN:
|
|
|
|
if (par_datalen != 4)
|
|
|
|
goto subpar_fail;
|
|
|
|
/* 24 zero bits, then 8 bits SSN */
|
|
|
|
out->ssn = par->data[3];
|
|
|
|
out->presence |= OSMO_SCCP_ADDR_T_SSN;
|
|
|
|
break;
|
|
|
|
case SUA_IEI_GT:
|
2017-04-03 17:25:45 +00:00
|
|
|
if (par_datalen < 8)
|
|
|
|
goto subpar_fail;
|
|
|
|
sua_parse_gt(&out->gt, par->data, par_datalen);
|
2016-04-13 17:08:18 +00:00
|
|
|
out->presence |= OSMO_SCCP_ADDR_T_GT;
|
|
|
|
break;
|
2017-04-03 17:25:45 +00:00
|
|
|
case SUA_IEI_IPv4:
|
|
|
|
if (par_datalen != 4)
|
|
|
|
goto subpar_fail;
|
|
|
|
p32 = (uint32_t*)par->data;
|
|
|
|
/* no endian conversion, both network order */
|
|
|
|
out->ip.v4.s_addr = *p32;
|
|
|
|
out->presence |= OSMO_SCCP_ADDR_T_IPv4;
|
|
|
|
break;
|
2016-04-13 17:08:18 +00:00
|
|
|
default:
|
2017-04-03 17:25:45 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "SUA IEI 0x%04x: Unknown subpart tag %hd\n",
|
|
|
|
param->tag, par_tag);
|
2016-04-13 17:08:18 +00:00
|
|
|
goto subpar_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += par_len;
|
|
|
|
}
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
return 0;
|
2016-04-13 17:08:18 +00:00
|
|
|
|
|
|
|
subpar_fail:
|
2017-04-03 17:25:45 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "Failed to parse subparts of address IEI=0x%04x\n",
|
|
|
|
param->tag);
|
2016-04-13 17:08:18 +00:00
|
|
|
return -EINVAL;
|
2015-12-22 21:22:23 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 17:25:45 +00:00
|
|
|
/*! \brief parse SCCP address from given xUA message IE
|
|
|
|
* \param[out] out caller-allocated decoded SCCP address struct
|
|
|
|
* \param[in] xua xUA message
|
|
|
|
* \param[in] iei Information Element Identifier inside \ref xua
|
|
|
|
\returns 0 on success; negative on error */
|
|
|
|
int sua_addr_parse(struct osmo_sccp_addr *out, struct xua_msg *xua, uint16_t iei)
|
|
|
|
{
|
|
|
|
const struct xua_msg_part *param = xua_msg_find_tag(xua, iei);
|
|
|
|
if (!param) {
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sua_addr_parse_part(out, param);
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_cldt(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct osmo_scu_unitdata_param *param;
|
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
2017-04-03 17:46:20 +00:00
|
|
|
struct msgb *upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
uint32_t protocol_class;
|
|
|
|
|
|
|
|
/* fill primitive */
|
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
|
|
|
param = &prim->u.unitdata;
|
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_UNITDATA,
|
|
|
|
PRIM_OP_INDICATION, upmsg);
|
2017-04-03 17:25:45 +00:00
|
|
|
sua_addr_parse(¶m->called_addr, xua, SUA_IEI_DEST_ADDR);
|
|
|
|
sua_addr_parse(¶m->calling_addr, xua, SUA_IEI_SRC_ADDR);
|
2015-12-22 21:22:23 +00:00
|
|
|
param->in_sequence_control = xua_msg_get_u32(xua, SUA_IEI_SEQ_CTRL);
|
|
|
|
protocol_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS);
|
|
|
|
param->return_option = protocol_class & 0x80;
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* connectioness messages received from socket */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_cl(struct osmo_sccp_link *link,
|
2015-12-22 21:22:23 +00:00
|
|
|
struct xua_msg *xua, struct msgb *msg)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
switch (xua->hdr.msg_type) {
|
|
|
|
case SUA_CL_CLDT:
|
|
|
|
rc = sua_rx_cldt(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CL_CLDR:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC 3868 3.3.3 / SCCP CR (Connection Request) */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_core(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct osmo_scu_connect_param *param;
|
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
|
|
|
struct msgb *upmsg;
|
|
|
|
struct sua_connection *conn;
|
|
|
|
|
|
|
|
/* fill conn */
|
|
|
|
conn = conn_create(link);
|
2017-04-03 17:25:45 +00:00
|
|
|
sua_addr_parse(&conn->called_addr, xua, SUA_IEI_DEST_ADDR);
|
|
|
|
sua_addr_parse(&conn->calling_addr, xua, SUA_IEI_SRC_ADDR);
|
2015-12-22 21:22:23 +00:00
|
|
|
conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
|
|
|
param = &prim->u.connect;
|
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_CONNECT,
|
|
|
|
PRIM_OP_INDICATION, upmsg);
|
|
|
|
param->conn_id = conn->conn_id;
|
|
|
|
memcpy(¶m->called_addr, &conn->called_addr,
|
|
|
|
sizeof(param->called_addr));
|
|
|
|
memcpy(¶m->calling_addr, &conn->calling_addr,
|
|
|
|
sizeof(param->calling_addr));
|
|
|
|
//param->in_sequence_control;
|
|
|
|
param->sccp_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS) & 3;
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
|
|
|
|
if (data_ie) {
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_state_set(conn, S_CONN_PEND_IN);
|
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC 3868 3.3.4 / SCCP CC (Connection Confirm) */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_coak(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct osmo_scu_connect_param *param;
|
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
|
|
|
struct msgb *upmsg;
|
|
|
|
uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
|
|
|
|
|
|
|
|
/* resolve conn */
|
|
|
|
conn = conn_find_by_id(link, conn_id);
|
|
|
|
if (!conn) {
|
2016-06-13 17:39:42 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "COAK for unknown reference %u\n",
|
2015-12-22 21:22:23 +00:00
|
|
|
conn_id);
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
conn_restart_rx_inact_timer(conn);
|
|
|
|
|
|
|
|
if (conn->state != S_CONN_PEND_OUT) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "COAK in wrong state %s\n",
|
|
|
|
get_value_string(conn_state_names, conn->state));
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* track remote reference */
|
|
|
|
conn->remote_ref = xua_msg_get_u32(xua, SUA_IEI_SRC_REF);
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
|
|
|
param = &prim->u.connect;
|
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_CONNECT,
|
|
|
|
PRIM_OP_CONFIRM, upmsg);
|
|
|
|
param->conn_id = conn->conn_id;
|
|
|
|
memcpy(¶m->called_addr, &conn->called_addr,
|
|
|
|
sizeof(param->called_addr));
|
|
|
|
memcpy(¶m->calling_addr, &conn->calling_addr,
|
|
|
|
sizeof(param->calling_addr));
|
|
|
|
//param->in_sequence_control;
|
|
|
|
param->sccp_class = xua_msg_get_u32(xua, SUA_IEI_PROTO_CLASS) & 3;
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
|
|
|
|
if (data_ie) {
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_state_set(conn, S_ACTIVE);
|
|
|
|
conn_start_inact_timers(conn);
|
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC 3868 3.3.5 / SCCP CREF (Connection Refused) */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_coref(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct sua_connection *conn;
|
2016-05-02 16:02:56 +00:00
|
|
|
struct osmo_scu_disconn_param *param;
|
2015-12-22 21:22:23 +00:00
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
|
|
|
struct msgb *upmsg;
|
|
|
|
uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
|
|
|
|
uint32_t cause;
|
|
|
|
|
|
|
|
/* resolve conn */
|
|
|
|
conn = conn_find_by_id(link, conn_id);
|
|
|
|
if (!conn) {
|
2016-06-13 17:39:42 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "COREF for unknown reference %u\n",
|
2015-12-22 21:22:23 +00:00
|
|
|
conn_id);
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
conn_restart_rx_inact_timer(conn);
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
2016-05-02 16:02:56 +00:00
|
|
|
param = &prim->u.disconnect;
|
2015-12-22 21:22:23 +00:00
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_DISCONNECT,
|
|
|
|
PRIM_OP_INDICATION, upmsg);
|
|
|
|
param->conn_id = conn_id;
|
2016-05-02 16:02:56 +00:00
|
|
|
param->responding_addr = conn->called_addr;
|
|
|
|
param->originator = OSMO_SCCP_ORIG_UNDEFINED;
|
2015-12-22 21:22:23 +00:00
|
|
|
//param->in_sequence_control;
|
2016-04-04 14:30:58 +00:00
|
|
|
/* TODO evaluate cause:
|
|
|
|
* cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE); */
|
2015-12-22 21:22:23 +00:00
|
|
|
/* optional: src addr */
|
|
|
|
/* optional: dest addr */
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
if (data_ie) {
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
conn_state_set(conn, S_IDLE);
|
|
|
|
conn_destroy(conn);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC 3868 3.3.6 / SCCP RLSD (Released) */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_relre(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct sua_connection *conn;
|
2016-05-02 16:02:56 +00:00
|
|
|
struct osmo_scu_disconn_param *param;
|
2015-12-22 21:22:23 +00:00
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
|
|
|
struct msgb *upmsg;
|
|
|
|
uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
|
|
|
|
uint32_t cause;
|
|
|
|
|
|
|
|
/* resolve conn */
|
|
|
|
conn = conn_find_by_id(link, conn_id);
|
|
|
|
if (!conn) {
|
2016-06-13 17:39:42 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "RELRE for unknown reference %u\n",
|
2015-12-22 21:22:23 +00:00
|
|
|
conn_id);
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
2016-05-02 16:02:56 +00:00
|
|
|
param = &prim->u.disconnect;
|
2015-12-22 21:22:23 +00:00
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_DISCONNECT,
|
|
|
|
PRIM_OP_INDICATION, upmsg); /* what primitive? */
|
|
|
|
|
|
|
|
param->conn_id = conn_id;
|
|
|
|
/* source reference */
|
|
|
|
cause = xua_msg_get_u32(xua, SUA_IEI_CAUSE);
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
if (data_ie) {
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
}
|
|
|
|
|
2016-05-02 16:02:56 +00:00
|
|
|
param->responding_addr = conn->called_addr;
|
|
|
|
param->originator = OSMO_SCCP_ORIG_UNDEFINED;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
conn_state_set(conn, S_IDLE);
|
|
|
|
conn_destroy(conn);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC 3868 3.3.7 / SCCP RLC (Release Complete)*/
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_relco(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct sua_connection *conn;
|
2016-05-02 16:02:56 +00:00
|
|
|
struct osmo_scu_disconn_param *param;
|
2015-12-22 21:22:23 +00:00
|
|
|
struct msgb *upmsg;
|
|
|
|
uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
|
|
|
|
|
|
|
|
/* resolve conn */
|
|
|
|
conn = conn_find_by_id(link, conn_id);
|
|
|
|
if (!conn) {
|
2016-06-13 17:39:42 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "RELCO for unknown reference %u\n",
|
2015-12-22 21:22:23 +00:00
|
|
|
conn_id);
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
conn_restart_rx_inact_timer(conn);
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
2016-05-02 16:02:56 +00:00
|
|
|
param = &prim->u.disconnect;
|
2015-12-22 21:22:23 +00:00
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_DISCONNECT,
|
|
|
|
PRIM_OP_CONFIRM, upmsg); /* what primitive? */
|
|
|
|
|
|
|
|
param->conn_id = conn_id;
|
|
|
|
/* source reference */
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
|
2016-05-02 16:02:56 +00:00
|
|
|
param->responding_addr = conn->called_addr;
|
|
|
|
param->originator = OSMO_SCCP_ORIG_UNDEFINED;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
conn_destroy(conn);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-05-02 15:57:48 +00:00
|
|
|
/* RFC3868 3.3.1 / SCCP DT1 (Data Form 1) */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_codt(struct osmo_sccp_link *link, struct xua_msg *xua)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_scu_prim *prim;
|
|
|
|
struct sua_connection *conn;
|
|
|
|
struct osmo_scu_data_param *param;
|
|
|
|
struct xua_msg_part *data_ie = xua_msg_find_tag(xua, SUA_IEI_DATA);
|
|
|
|
struct msgb *upmsg;
|
|
|
|
uint32_t conn_id = xua_msg_get_u32(xua, SUA_IEI_DEST_REF);
|
|
|
|
|
|
|
|
/* resolve conn */
|
|
|
|
conn = conn_find_by_id(link, conn_id);
|
|
|
|
if (!conn) {
|
2016-06-13 17:39:42 +00:00
|
|
|
LOGP(DSUA, LOGL_ERROR, "DT1 for unknown reference %u\n",
|
2015-12-22 21:22:23 +00:00
|
|
|
conn_id);
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->state != S_ACTIVE) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "DT1 in invalid state %s\n",
|
|
|
|
get_value_string(conn_state_names, conn->state));
|
2016-06-13 10:17:44 +00:00
|
|
|
/* FIXME: send error reply down the sua link? */
|
2015-12-22 21:22:23 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_restart_rx_inact_timer(conn);
|
|
|
|
|
|
|
|
/* fill primitive */
|
2017-04-03 17:46:20 +00:00
|
|
|
upmsg = sccp_msgb_alloc(__func__);
|
2015-12-22 21:22:23 +00:00
|
|
|
prim = (struct osmo_scu_prim *) msgb_put(upmsg, sizeof(*prim));
|
|
|
|
param = &prim->u.data;
|
|
|
|
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
|
|
|
OSMO_SCU_PRIM_N_DATA,
|
|
|
|
PRIM_OP_INDICATION, upmsg);
|
|
|
|
param->conn_id = conn_id;
|
|
|
|
param->importance = xua_msg_get_u32(xua, SUA_IEI_IMPORTANCE);
|
|
|
|
|
|
|
|
/* copy data */
|
|
|
|
upmsg->l2h = msgb_put(upmsg, data_ie->len);
|
|
|
|
memcpy(upmsg->l2h, data_ie->dat, data_ie->len);
|
|
|
|
|
|
|
|
/* send to user SAP */
|
|
|
|
link->user->prim_cb(&prim->oph, link);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* connection-oriented messages received from socket */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_co(struct osmo_sccp_link *link,
|
2015-12-22 21:22:23 +00:00
|
|
|
struct xua_msg *xua, struct msgb *msg)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
switch (xua->hdr.msg_type) {
|
|
|
|
case SUA_CO_CORE:
|
|
|
|
rc = sua_rx_core(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_COAK:
|
|
|
|
rc = sua_rx_coak(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_COREF:
|
|
|
|
rc = sua_rx_coref(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_RELRE:
|
|
|
|
rc = sua_rx_relre(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_RELCO:
|
|
|
|
rc = sua_rx_relco(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_CODT:
|
|
|
|
rc = sua_rx_codt(link, xua);
|
|
|
|
break;
|
|
|
|
case SUA_CO_RESCO:
|
|
|
|
case SUA_CO_RESRE:
|
|
|
|
case SUA_CO_CODA:
|
|
|
|
case SUA_CO_COERR:
|
|
|
|
case SUA_CO_COIT:
|
|
|
|
/* FIXME */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process SUA message received from socket */
|
2016-07-06 13:51:36 +00:00
|
|
|
static int sua_rx_msg(struct osmo_sccp_link *link, struct msgb *msg)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct xua_msg *xua;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
xua = xua_from_msg(1, msgb_length(msg), msg->data);
|
|
|
|
if (!xua) {
|
|
|
|
LOGP(DSUA, LOGL_ERROR, "Unable to parse incoming "
|
|
|
|
"SUA message\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2017-04-03 17:24:06 +00:00
|
|
|
LOGP(DSUA, LOGL_DEBUG, "Received SUA Message (%s)\n",
|
|
|
|
xua_hdr_dump(xua, &xua_dialect_sua));
|
|
|
|
|
|
|
|
if (!xua_dialect_check_all_mand_ies(&xua_dialect_sua, xua))
|
|
|
|
return -1;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
switch (xua->hdr.msg_class) {
|
|
|
|
case SUA_MSGC_CL:
|
|
|
|
rc = sua_rx_cl(link, xua, msg);
|
|
|
|
break;
|
|
|
|
case SUA_MSGC_CO:
|
|
|
|
rc = sua_rx_co(link, xua, msg);
|
|
|
|
break;
|
|
|
|
case SUA_MSGC_MGMT:
|
|
|
|
case SUA_MSGC_SNM:
|
|
|
|
case SUA_MSGC_ASPSM:
|
|
|
|
case SUA_MSGC_ASPTM:
|
|
|
|
case SUA_MSGC_RKM:
|
|
|
|
/* FIXME */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
xua_msg_free(xua);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* libosmonetif integration
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
#include <osmocom/netif/stream.h>
|
|
|
|
#include <netinet/sctp.h>
|
|
|
|
|
2016-01-04 11:35:49 +00:00
|
|
|
static const struct value_string sctp_assoc_chg_vals[] = {
|
|
|
|
{ SCTP_COMM_UP, "COMM_UP" },
|
|
|
|
{ SCTP_COMM_LOST, "COMM_LOST" },
|
|
|
|
{ SCTP_RESTART, "RESTART" },
|
|
|
|
{ SCTP_SHUTDOWN_COMP, "SHUTDOWN_COMP" },
|
|
|
|
{ SCTP_CANT_STR_ASSOC, "CANT_STR_ASSOC" },
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct value_string sctp_sn_type_vals[] = {
|
|
|
|
{ SCTP_ASSOC_CHANGE, "ASSOC_CHANGE" },
|
|
|
|
{ SCTP_PEER_ADDR_CHANGE, "PEER_ADDR_CHANGE" },
|
|
|
|
{ SCTP_SHUTDOWN_EVENT, "SHUTDOWN_EVENT" },
|
|
|
|
{ SCTP_SEND_FAILED, "SEND_FAILED" },
|
|
|
|
{ SCTP_REMOTE_ERROR, "REMOTE_ERROR" },
|
|
|
|
{ SCTP_PARTIAL_DELIVERY_EVENT, "PARTIAL_DELIVERY_EVENT" },
|
|
|
|
{ SCTP_ADAPTATION_INDICATION, "ADAPTATION_INDICATION" },
|
|
|
|
#ifdef SCTP_AUTHENTICATION_INDICATION
|
|
|
|
{ SCTP_AUTHENTICATION_INDICATION, "UTHENTICATION_INDICATION" },
|
|
|
|
#endif
|
|
|
|
#ifdef SCTP_SENDER_DRY_EVENT
|
|
|
|
{ SCTP_SENDER_DRY_EVENT, "SENDER_DRY_EVENT" },
|
|
|
|
#endif
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int get_logevel_by_sn_type(int sn_type)
|
|
|
|
{
|
|
|
|
switch (sn_type) {
|
|
|
|
case SCTP_ADAPTATION_INDICATION:
|
|
|
|
case SCTP_PEER_ADDR_CHANGE:
|
|
|
|
#ifdef SCTP_AUTHENTICATION_INDICATION
|
|
|
|
case SCTP_AUTHENTICATION_INDICATION:
|
|
|
|
#endif
|
|
|
|
#ifdef SCTP_SENDER_DRY_EVENT
|
|
|
|
case SCTP_SENDER_DRY_EVENT:
|
|
|
|
#endif
|
|
|
|
return LOGL_INFO;
|
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
case SCTP_PARTIAL_DELIVERY_EVENT:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
case SCTP_SEND_FAILED:
|
|
|
|
case SCTP_REMOTE_ERROR:
|
|
|
|
return LOGL_ERROR;
|
|
|
|
default:
|
|
|
|
return LOGL_NOTICE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void log_sctp_notification(int fd, const char *pfx,
|
|
|
|
union sctp_notification *notif)
|
|
|
|
{
|
|
|
|
int log_level;
|
|
|
|
char *conn_id = osmo_sock_get_name(NULL, fd);
|
|
|
|
|
|
|
|
LOGP(DSUA, LOGL_INFO, "%s %s SCTP NOTIFICATION %u flags=0x%0x\n",
|
|
|
|
conn_id, pfx, notif->sn_header.sn_type,
|
|
|
|
notif->sn_header.sn_flags);
|
|
|
|
|
|
|
|
log_level = get_logevel_by_sn_type(notif->sn_header.sn_type);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_ASSOC_CHANGE:
|
|
|
|
LOGP(DSUA, log_level, "%s %s SCTP_ASSOC_CHANGE: %s\n",
|
|
|
|
conn_id, pfx, get_value_string(sctp_assoc_chg_vals,
|
|
|
|
notif->sn_assoc_change.sac_state));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGP(DSUA, log_level, "%s %s %s\n",
|
|
|
|
conn_id, pfx, get_value_string(sctp_sn_type_vals,
|
|
|
|
notif->sn_header.sn_type));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
talloc_free(conn_id);
|
|
|
|
}
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
/* netif code tells us we can read something from the socket */
|
|
|
|
static int sua_srv_conn_cb(struct osmo_stream_srv *conn)
|
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *link = osmo_stream_srv_get_data(conn);
|
2015-12-22 21:22:23 +00:00
|
|
|
struct msgb *msg = msgb_alloc(SUA_MSGB_SIZE, "SUA Server Rx");
|
|
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
unsigned int ppid;
|
|
|
|
int flags = 0;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* read SUA message from socket and process it */
|
|
|
|
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
|
|
|
|
NULL, NULL, &sinfo, &flags);
|
2016-01-04 11:35:49 +00:00
|
|
|
LOGP(DSUA, LOGL_DEBUG, "sua_srv_conn_cb(): sctp_recvmsg() returned %d\n",
|
|
|
|
rc);
|
2015-12-22 21:22:23 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
return rc;
|
|
|
|
} else if (rc == 0) {
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
} else {
|
|
|
|
msgb_put(msg, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
2016-01-04 11:35:49 +00:00
|
|
|
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
|
|
|
|
|
|
|
log_sctp_notification(ofd->fd, "SUA SRV", notif);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-12-22 21:22:23 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ppid = ntohl(sinfo.sinfo_ppid);
|
|
|
|
msgb_sctp_ppid(msg) = ppid;
|
|
|
|
msgb_sctp_stream(msg) = ntohl(sinfo.sinfo_stream);
|
|
|
|
msg->dst = link;
|
|
|
|
|
|
|
|
switch (ppid) {
|
|
|
|
case SUA_PPID:
|
|
|
|
rc = sua_rx_msg(link, msg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGP(DSUA, LOGL_NOTICE, "SCTP chunk for unknown PPID %u "
|
|
|
|
"received\n", ppid);
|
|
|
|
rc = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msgb_free(msg);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sua_srv_conn_closed_cb(struct osmo_stream_srv *srv)
|
|
|
|
{
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *sual = osmo_stream_srv_get_data(srv);
|
2015-12-22 21:22:23 +00:00
|
|
|
struct sua_connection *conn;
|
|
|
|
|
|
|
|
LOGP(DSUA, LOGL_INFO, "SCTP connection closed\n");
|
|
|
|
|
|
|
|
/* remove from per-user list of sua links */
|
|
|
|
llist_del(&sual->list);
|
|
|
|
|
|
|
|
llist_for_each_entry(conn, &sual->connections, list) {
|
|
|
|
/* FIXME: send RELEASE request */
|
|
|
|
}
|
|
|
|
talloc_free(sual);
|
|
|
|
osmo_stream_srv_set_data(srv, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sua_accept_cb(struct osmo_stream_srv_link *link, int fd)
|
|
|
|
{
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_user *user = osmo_stream_srv_link_get_data(link);
|
2015-12-22 21:22:23 +00:00
|
|
|
struct osmo_stream_srv *srv;
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *sual;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
LOGP(DSUA, LOGL_INFO, "New SCTP connection accepted\n");
|
|
|
|
|
|
|
|
srv = osmo_stream_srv_create(user, link, fd,
|
|
|
|
sua_srv_conn_cb,
|
|
|
|
sua_srv_conn_closed_cb, NULL);
|
2016-12-11 20:14:39 +00:00
|
|
|
if (!srv) {
|
2015-12-22 21:22:23 +00:00
|
|
|
close(fd);
|
2016-12-11 20:14:39 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
/* create new SUA link and connect both data structures */
|
|
|
|
sual = sua_link_new(user, 1);
|
|
|
|
if (!sual) {
|
|
|
|
osmo_stream_srv_destroy(srv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sual->data = srv;
|
|
|
|
osmo_stream_srv_set_data(srv, sual);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
int osmo_sua_server_listen(struct osmo_sccp_user *user, const char *hostname, uint16_t port)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (user->server)
|
|
|
|
osmo_stream_srv_link_close(user->server);
|
|
|
|
else {
|
|
|
|
user->server = osmo_stream_srv_link_create(user);
|
|
|
|
osmo_stream_srv_link_set_data(user->server, user);
|
|
|
|
osmo_stream_srv_link_set_accept_cb(user->server, sua_accept_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
osmo_stream_srv_link_set_addr(user->server, hostname);
|
|
|
|
osmo_stream_srv_link_set_port(user->server, port);
|
|
|
|
osmo_stream_srv_link_set_proto(user->server, IPPROTO_SCTP);
|
|
|
|
|
|
|
|
rc = osmo_stream_srv_link_open(user->server);
|
|
|
|
if (rc < 0) {
|
|
|
|
osmo_stream_srv_link_destroy(user->server);
|
|
|
|
user->server = NULL;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* netif code tells us we can read something from the socket */
|
2016-01-04 11:35:49 +00:00
|
|
|
static int sua_cli_read_cb(struct osmo_stream_cli *conn)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn);
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *link = osmo_stream_cli_get_data(conn);
|
2015-12-22 21:22:23 +00:00
|
|
|
struct msgb *msg = msgb_alloc(SUA_MSGB_SIZE, "SUA Client Rx");
|
|
|
|
struct sctp_sndrcvinfo sinfo;
|
|
|
|
unsigned int ppid;
|
|
|
|
int flags = 0;
|
|
|
|
int rc;
|
|
|
|
|
2016-01-04 11:35:49 +00:00
|
|
|
LOGP(DSUA, LOGL_DEBUG, "sua_cli_read_cb() rx\n");
|
|
|
|
|
2015-12-22 21:22:23 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* read SUA message from socket and process it */
|
|
|
|
rc = sctp_recvmsg(ofd->fd, msgb_data(msg), msgb_tailroom(msg),
|
|
|
|
NULL, NULL, &sinfo, &flags);
|
|
|
|
if (rc < 0) {
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
return rc;
|
|
|
|
} else if (rc == 0) {
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
} else {
|
|
|
|
msgb_put(msg, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & MSG_NOTIFICATION) {
|
2016-01-04 11:35:49 +00:00
|
|
|
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
|
|
|
|
|
|
|
log_sctp_notification(ofd->fd, "SUA CLNT", notif);
|
|
|
|
|
|
|
|
switch (notif->sn_header.sn_type) {
|
|
|
|
case SCTP_SHUTDOWN_EVENT:
|
|
|
|
close(ofd->fd);
|
|
|
|
osmo_fd_unregister(ofd);
|
|
|
|
ofd->fd = -1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2015-12-22 21:22:23 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ppid = ntohl(sinfo.sinfo_ppid);
|
|
|
|
msgb_sctp_ppid(msg) = ppid;
|
|
|
|
msgb_sctp_stream(msg) = ntohl(sinfo.sinfo_stream);
|
|
|
|
msg->dst = link;
|
|
|
|
|
|
|
|
switch (ppid) {
|
|
|
|
case SUA_PPID:
|
|
|
|
rc = sua_rx_msg(link, msg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGP(DSUA, LOGL_NOTICE, "SCTP chunk for unknown PPID %u "
|
|
|
|
"received\n", ppid);
|
|
|
|
rc = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
msgb_free(msg);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
int osmo_sua_client_connect(struct osmo_sccp_user *user, const char *hostname, uint16_t port)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
struct osmo_stream_cli *cli;
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *sual;
|
2015-12-22 21:22:23 +00:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
cli = osmo_stream_cli_create(user);
|
|
|
|
if (!cli)
|
|
|
|
return -1;
|
|
|
|
osmo_stream_cli_set_addr(cli, hostname);
|
|
|
|
osmo_stream_cli_set_port(cli, port);
|
|
|
|
osmo_stream_cli_set_proto(cli, IPPROTO_SCTP);
|
|
|
|
osmo_stream_cli_set_reconnect_timeout(cli, 5);
|
2016-01-04 11:35:49 +00:00
|
|
|
osmo_stream_cli_set_read_cb(cli, sua_cli_read_cb);
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
/* create SUA link and associate it with stream_cli */
|
|
|
|
sual = sua_link_new(user, 0);
|
|
|
|
if (!sual) {
|
|
|
|
osmo_stream_cli_destroy(cli);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sual->data = cli;
|
|
|
|
osmo_stream_cli_set_data(cli, sual);
|
|
|
|
|
2015-12-26 22:36:27 +00:00
|
|
|
rc = osmo_stream_cli_open2(cli, 1);
|
2015-12-22 21:22:23 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
sua_link_destroy(sual);
|
|
|
|
osmo_stream_cli_destroy(cli);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
user->client = cli;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *osmo_sua_client_get_link(struct osmo_sccp_user *user)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
|
|
|
return osmo_stream_cli_get_data(user->client);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LLIST_HEAD(sua_users);
|
|
|
|
|
2015-12-26 22:34:03 +00:00
|
|
|
struct osmo_sccp_user *osmo_sua_user_create(void *ctx, osmo_prim_cb prim_cb,
|
|
|
|
void *priv)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_user *user = talloc_zero(ctx, struct osmo_sccp_user);
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
user->prim_cb = prim_cb;
|
2015-12-26 22:34:03 +00:00
|
|
|
user->priv = priv;
|
2015-12-22 21:22:23 +00:00
|
|
|
INIT_LLIST_HEAD(&user->links);
|
|
|
|
|
|
|
|
llist_add_tail(&user->list, &sua_users);
|
|
|
|
|
|
|
|
return user;
|
|
|
|
}
|
|
|
|
|
2015-12-26 22:34:03 +00:00
|
|
|
void *osmo_sccp_link_get_user_priv(struct osmo_sccp_link *slink)
|
|
|
|
{
|
|
|
|
return slink->user->priv;
|
|
|
|
}
|
|
|
|
|
2016-07-06 13:51:36 +00:00
|
|
|
void osmo_sua_user_destroy(struct osmo_sccp_user *user)
|
2015-12-22 21:22:23 +00:00
|
|
|
{
|
2016-07-06 13:51:36 +00:00
|
|
|
struct osmo_sccp_link *link;
|
2015-12-22 21:22:23 +00:00
|
|
|
|
|
|
|
llist_del(&user->list);
|
|
|
|
|
|
|
|
llist_for_each_entry(link, &user->links, list)
|
|
|
|
sua_link_destroy(link);
|
|
|
|
|
|
|
|
talloc_free(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
void osmo_sua_set_log_area(int area)
|
|
|
|
{
|
|
|
|
xua_set_log_area(area);
|
|
|
|
DSUA = area;
|
|
|
|
}
|