aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/hnbgw.c91
-rw-r--r--src/hnbgw.h15
-rw-r--r--src/hnbgw_cn.c371
-rw-r--r--src/hnbgw_cn.h5
-rw-r--r--src/hnbgw_hnbap.c4
-rw-r--r--src/hnbgw_ranap.c102
-rw-r--r--src/hnbgw_rua.c218
-rw-r--r--src/ranap_msg_factory.c72
-rw-r--r--src/ranap_msg_factory.h15
-rw-r--r--src/sccp_helpers.c137
-rw-r--r--src/sccp_helpers.h33
12 files changed, 946 insertions, 119 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 13cdac9..27e0e19 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,7 +8,7 @@ COMMON_LDADD = -lsctp
bin_PROGRAMS = hnbgw
-hnbgw_SOURCES = hnbap_encoder.c hnbap_decoder.c rua_encoder.c rua_decoder.c ranap_common.c rua_common.c hnbap_common.c iu_helpers.c asn1helpers.c hnbgw.c hnbgw_hnbap.c hnbgw_rua.c hnbgw_ranap.c ranap_decoder.c ranap_encoder.c ranap_msg_factory.c context_map.c
+hnbgw_SOURCES = hnbap_encoder.c hnbap_decoder.c rua_encoder.c rua_decoder.c ranap_common.c rua_common.c hnbap_common.c iu_helpers.c asn1helpers.c hnbgw.c hnbgw_hnbap.c hnbgw_rua.c hnbgw_ranap.c ranap_decoder.c ranap_encoder.c ranap_msg_factory.c context_map.c hnbgw_cn.c sccp_helpers.c
hnbgw_LDADD = $(OSMOCORE_LIBS) $(OSMOVTY_LIBS) $(OSMOGSM_LIBS) $(ASN1C_LIBS) $(OSMOSIGTRAN_LIBS) $(COMMON_LDADD) hnbap/libosmo-asn1-hnbap.a rua/libosmo-asn1-rua.a ranap/libosmo-asn1-ranap.a
BUILT_SOURCES = hnbap_decoder.c hnbap_encoder.c rua_decoder.c rua_encoder.c ranap_decoder.c ranap_encoder.c
diff --git a/src/hnbgw.c b/src/hnbgw.c
index bc8be99..ea3e754 100644
--- a/src/hnbgw.c
+++ b/src/hnbgw.c
@@ -53,24 +53,40 @@
#include "hnbgw.h"
#include "hnbgw_hnbap.h"
#include "hnbgw_rua.h"
+#include "hnbgw_cn.h"
#include "context_map.h"
static void *tall_hnb_ctx;
-static void *tall_ue_ctx;
-static void *tall_sua_ctx;
void *talloc_asn1_ctx;
-struct hnb_gw g_hnb_gw = {
- .config = {
- .iuh_listen_port = IUH_DEFAULT_SCTP_PORT,
- },
-};
+static struct hnb_gw *g_hnb_gw;
+
+static int listen_fd_cb(struct osmo_fd *fd, unsigned int what);
+
+static struct hnb_gw *hnb_gw_create(void *ctx)
+{
+ struct hnb_gw *gw = talloc_zero(ctx, struct hnb_gw);
+
+ gw->config.iuh_listen_port = IUH_DEFAULT_SCTP_PORT;
+
+ gw->listen_fd.cb = listen_fd_cb;
+ gw->listen_fd.when = BSC_FD_READ;
+ gw->listen_fd.data = gw;
+ gw->next_ue_ctx_id = 23;
+ INIT_LLIST_HEAD(&gw->hnb_list);
+ INIT_LLIST_HEAD(&gw->ue_list);
+ INIT_LLIST_HEAD(&gw->cn_list);
+
+ context_map_init(gw);
+
+ return gw;
+}
-struct ue_context *ue_context_by_id(uint32_t id)
+struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id)
{
struct ue_context *ue;
- llist_for_each_entry(ue, &g_hnb_gw.ue_list, list) {
+ llist_for_each_entry(ue, &gw->ue_list, list) {
if (ue->context_id == id)
return ue;
}
@@ -78,24 +94,24 @@ struct ue_context *ue_context_by_id(uint32_t id)
}
-struct ue_context *ue_context_by_imsi(const char *imsi)
+struct ue_context *ue_context_by_imsi(struct hnb_gw *gw, const char *imsi)
{
struct ue_context *ue;
- llist_for_each_entry(ue, &g_hnb_gw.ue_list, list) {
+ llist_for_each_entry(ue, &gw->ue_list, list) {
if (!strcmp(ue->imsi, imsi))
return ue;
}
return NULL;
}
-static uint32_t get_next_ue_ctx_id(void)
+static uint32_t get_next_ue_ctx_id(struct hnb_gw *gw)
{
uint32_t id;
do {
- id = g_hnb_gw.next_ue_ctx_id++;
- } while (ue_context_by_id(id));
+ id = gw->next_ue_ctx_id++;
+ } while (ue_context_by_id(gw, id));
return id;
}
@@ -110,8 +126,8 @@ struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi)
ue->hnb = hnb;
strncpy(ue->imsi, imsi, sizeof(ue->imsi));
- ue->context_id = get_next_ue_ctx_id();
- llist_add_tail(&ue->list, &g_hnb_gw.ue_list);
+ ue->context_id = get_next_ue_ctx_id(hnb->gw);
+ llist_add_tail(&ue->list, &hnb->gw->ue_list);
return ue;
}
@@ -227,6 +243,7 @@ struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, int new_fd)
ctx->wqueue.read_cb = hnb_read_cb;
ctx->wqueue.write_cb = hnb_write_cb;
osmo_fd_register(&ctx->wqueue.bfd);
+ INIT_LLIST_HEAD(&ctx->map_list);
llist_add_tail(&ctx->list, &gw->hnb_list);
}
@@ -359,7 +376,7 @@ DEFUN(show_hnb, show_hnb_cmd, "show hnb all", SHOW_STR "Display information abou
{
struct hnb_context *hnb;
- llist_for_each_entry(hnb, &g_hnb_gw.hnb_list, list) {
+ llist_for_each_entry(hnb, &g_hnb_gw->hnb_list, list) {
vty_dump_hnb_info(vty, hnb);
}
@@ -370,7 +387,7 @@ DEFUN(show_ue, show_ue_cmd, "show ue all", SHOW_STR "Display information about a
{
struct ue_context *ue;
- llist_for_each_entry(ue, &g_hnb_gw.ue_list, list) {
+ llist_for_each_entry(ue, &g_hnb_gw->ue_list, list) {
vty_dump_ue_info(vty, ue);
}
@@ -380,7 +397,6 @@ DEFUN(show_ue, show_ue_cmd, "show ue all", SHOW_STR "Display information about a
DEFUN(show_talloc, show_talloc_cmd, "show talloc", SHOW_STR "Display talloc info")
{
talloc_report_full(tall_hnb_ctx, stderr);
- talloc_report_full(tall_ue_ctx, stderr);
talloc_report_full(talloc_asn1_ctx, stderr);
return CMD_SUCCESS;
@@ -400,18 +416,9 @@ int main(int argc, char **argv)
int rc;
tall_hnb_ctx = talloc_named_const(NULL, 0, "hnb_context");
- tall_ue_ctx = talloc_named_const(NULL, 0, "ue_context");
- tall_sua_ctx = talloc_named_const(NULL, 0, "sua");
talloc_asn1_ctx = talloc_named_const(NULL, 0, "asn1_context");
- g_hnb_gw.listen_fd.cb = listen_fd_cb;
- g_hnb_gw.listen_fd.when = BSC_FD_READ;
- g_hnb_gw.listen_fd.data = &g_hnb_gw;
- g_hnb_gw.next_ue_ctx_id = 23;
- INIT_LLIST_HEAD(&g_hnb_gw.hnb_list);
- INIT_LLIST_HEAD(&g_hnb_gw.ue_list);
-
- context_map_init(&g_hnb_gw);
+ g_hnb_gw = hnb_gw_create(tall_hnb_ctx);
rc = osmo_init_logging(&hnbgw_log_info);
if (rc < 0)
@@ -420,37 +427,25 @@ int main(int argc, char **argv)
vty_init(&vty_info);
hnbgw_vty_init();
- rc = telnet_init(NULL, &g_hnb_gw, 2323);
+ rc = telnet_init(NULL, g_hnb_gw, 2323);
if (rc < 0) {
perror("Error binding VTY port");
exit(1);
}
osmo_sua_set_log_area(DSUA);
- sua_user = osmo_sua_user_create(tall_sua_ctx, sccp_sap_up);
- if (!sua_user) {
- perror("Failed to init SUA");
- exit(1);
- }
- rc = osmo_sua_client_connect(sua_user, "127.0.0.1", SUA_PORT);
- if (rc < 0) {
- perror("Failed to connect SUA");
- exit(1);
- }
- sua_link = osmo_sua_client_get_link(sua_user);
- if (!sua_link) {
- perror("Failed to get SUA link");
- exit(1);
- }
- rc = osmo_sock_init_ofd(&g_hnb_gw.listen_fd, AF_INET, SOCK_STREAM,
+ g_hnb_gw->cnlink_cs = hnbgw_cnlink_init(g_hnb_gw, "127.0.0.1", SUA_PORT);
+ g_hnb_gw->cnlink_ps = hnbgw_cnlink_init(g_hnb_gw, "127.0.0.2", SUA_PORT);
+
+ rc = osmo_sock_init_ofd(&g_hnb_gw->listen_fd, AF_INET, SOCK_STREAM,
IPPROTO_SCTP, NULL,
- g_hnb_gw.config.iuh_listen_port, OSMO_SOCK_F_BIND);
+ g_hnb_gw->config.iuh_listen_port, OSMO_SOCK_F_BIND);
if (rc < 0) {
perror("Error binding Iuh port");
exit(1);
}
- sctp_sock_init(g_hnb_gw.listen_fd.fd);
+ sctp_sock_init(g_hnb_gw->listen_fd.fd);
if (daemonize) {
rc = osmo_daemonize();
diff --git a/src/hnbgw.h b/src/hnbgw.h
index 0509415..ad683ee 100644
--- a/src/hnbgw.h
+++ b/src/hnbgw.h
@@ -64,6 +64,7 @@ struct hnbgw_cnlink {
/* timer for re-transmitting the RANAP Reset */
struct osmo_timer_list T_RafC;
/* reference to the SCCP User SAP by which we communicate */
+ struct osmo_sua_user *sua_user;
struct osmo_sua_link *sua_link;
struct osmo_sccp_addr local_addr;
struct osmo_sccp_addr remote_addr;
@@ -115,16 +116,22 @@ struct hnb_gw {
} config;
/*! SCTP listen socket for incoming connections */
struct osmo_fd listen_fd;
+ /* list of struct hnb_context */
struct llist_head hnb_list;
+ /* list of struct ue_context */
struct llist_head ue_list;
+ /* list of struct hnbgw_cnlink */
struct llist_head cn_list;
+ /* next availble UE Context ID */
uint32_t next_ue_ctx_id;
-};
-extern struct hnb_gw g_hnb_gw;
+ /* currently active CN links for CS and PS */
+ struct hnbgw_cnlink *cnlink_cs;
+ struct hnbgw_cnlink *cnlink_ps;
+};
-struct ue_context *ue_context_by_id(uint32_t id);
-struct ue_context *ue_context_by_imsi(const char *imsi);
+struct ue_context *ue_context_by_id(struct hnb_gw *gw, uint32_t id);
+struct ue_context *ue_context_by_imsi(struct hnb_gw *gw, const char *imsi);
struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi);
void ue_context_free(struct ue_context *ue);
diff --git a/src/hnbgw_cn.c b/src/hnbgw_cn.c
new file mode 100644
index 0000000..b69f250
--- /dev/null
+++ b/src/hnbgw_cn.c
@@ -0,0 +1,371 @@
+/* IuCS/IuPS Core Network interface of HNB-GW */
+
+/* (C) 2015 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 Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+
+#include <osmocom/sigtran/protocol/sua.h>
+#include <osmocom/sigtran/sua.h>
+#include <osmocom/sigtran/sccp_sap.h>
+
+#include "hnbgw.h"
+#include "hnbgw_rua.h"
+#include "ranap_ies_defs.h"
+#include "ranap_msg_factory.h"
+#include "context_map.h"
+#include "sccp_helpers.h"
+
+#define SCCP_SSN_RANAP 143
+
+/***********************************************************************
+ * Outbound RANAP RESET to CN
+ ***********************************************************************/
+
+int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state);
+
+static int transmit_rst(struct hnbgw_cnlink *cnlink)
+{
+ struct msgb *msg;
+ struct msgb *msgprim;
+ RANAP_CN_DomainIndicator_t domain;
+ RANAP_Cause_t cause = {
+ .present = RANAP_Cause_PR_transmissionNetwork,
+ .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure,
+ };
+
+ if (cnlink->is_ps)
+ domain = RANAP_CN_DomainIndicator_ps_domain;
+ else
+ domain = RANAP_CN_DomainIndicator_cs_domain;
+
+ msg = ranap_new_msg_reset(domain, &cause);
+
+ return sccp_tx_unitdata_msg(cnlink->sua_link, &cnlink->local_addr,
+ &cnlink->remote_addr, msg);
+}
+
+/* Timer callback once T_RafC expires */
+static void cnlink_trafc_cb(void *data)
+{
+ struct hnbgw_cnlink *cnlink = data;
+
+ transmit_rst(cnlink);
+ hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_RST_TX_WAIT_ACK);
+ /* The spec states that we should abandon after a configurable
+ * number of times. We decide to simply continue trying */
+}
+
+/* change the state of a CN Link */
+int hnbgw_cnlink_change_state(struct hnbgw_cnlink *cnlink, enum hnbgw_cnlink_state state)
+{
+ switch (state) {
+ case CNLINK_S_NULL:
+ case CNLINK_S_EST_PEND:
+ break;
+ case CNLINK_S_EST_CONF:
+ cnlink_trafc_cb(cnlink);
+ break;
+ case CNLINK_S_EST_RST_TX_WAIT_ACK:
+ osmo_timer_schedule(&cnlink->T_RafC, 5, 0);
+ break;
+ case CNLINK_S_EST_ACTIVE:
+ osmo_timer_del(&cnlink->T_RafC);
+ break;
+ }
+}
+
+/***********************************************************************
+ * Incoming primitives from SCCP User SAP
+ ***********************************************************************/
+
+static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink,
+ RANAP_InitiatingMessage_t *imsg)
+{
+ RANAP_ResetIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_reseties(&ies, &imsg->value);
+ /* FIXME: reset resources and return reset ack */
+ return rc;
+}
+
+static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink,
+ RANAP_SuccessfulOutcome_t *omsg)
+{
+ RANAP_ResetAcknowledgeIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value);
+
+ hnbgw_cnlink_change_state(cnlink, CNLINK_S_EST_ACTIVE);
+
+ return rc;
+}
+
+static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink,
+ RANAP_InitiatingMessage_t *imsg)
+{
+ RANAP_PagingIEs_t ies;
+ int rc;
+
+ rc = ranap_decode_pagingies(&ies, &imsg->value);
+
+ /* FIXME: determine which HNBs to send this Paging command */
+ return rc;
+}
+
+static int cn_ranap_rx_initiating_msg(struct hnbgw_cnlink *cnlink,
+ RANAP_InitiatingMessage_t *imsg)
+{
+ int rc;
+
+ switch (imsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset:
+ return cn_ranap_rx_reset_cmd(cnlink, imsg);
+ case RANAP_ProcedureCode_id_Paging:
+ return cn_ranap_rx_paging_cmd(cnlink, imsg);
+ case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
+ break;
+ case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* request */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %u from CN, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %u from CN, ignoring\n", imsg->procedureCode);
+ break;
+ }
+ return 0;
+}
+
+static int cn_ranap_rx_successful_msg(struct hnbgw_cnlink *cnlink,
+ RANAP_SuccessfulOutcome_t *omsg)
+{
+ int rc;
+
+ switch (omsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
+ return cn_ranap_rx_reset_ack(cnlink, omsg);
+ case RANAP_ProcedureCode_id_ResetResource: /* response */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %u from CN, ignoring\n", omsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %u from CN, ignoring\n", omsg->procedureCode);
+ break;
+ }
+ return 0;
+}
+
+
+static int _cn_ranap_rx(struct hnbgw_cnlink *cnlink, RANAP_RANAP_PDU_t *pdu)
+{
+ int rc;
+
+ switch (pdu->present) {
+ case RANAP_RANAP_PDU_PR_initiatingMessage:
+ rc = cn_ranap_rx_initiating_msg(cnlink, &pdu->choice.initiatingMessage);
+ break;
+ case RANAP_RANAP_PDU_PR_successfulOutcome:
+ rc = cn_ranap_rx_successful_msg(cnlink, &pdu->choice.successfulOutcome);
+ break;
+ case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "unsuccessful outcome procedure %u from CN, ignoring\n",
+ pdu->choice.unsuccessfulOutcome.procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "presence %u from CN, ignoring\n", pdu->present);
+ break;
+ }
+}
+
+static int handle_cn_ranap(struct hnbgw_cnlink *cnlink, const uint8_t *data,
+ unsigned int len)
+{
+ RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
+ asn_dec_rval_t dec_ret;
+ int rc;
+
+ memset(pdu, 0, sizeof(*pdu));
+ dec_ret = aper_decode(NULL,&asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
+ data, len, 0, 0);
+ if (dec_ret.code != RC_OK) {
+ LOGP(DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
+ return rc;
+ }
+
+ rc = _cn_ranap_rx(cnlink, pdu);
+
+ return rc;
+}
+
+
+static int handle_cn_unitdata(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_unitdata_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ if (param->called_addr.ssn != SCCP_SSN_RANAP) {
+ LOGP(DMAIN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
+ param->called_addr.ssn);
+ return -1;
+ }
+
+ return handle_cn_ranap(cnlink, msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+static int handle_cn_conn_conf(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_connect_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ /* we don't actually need to do anything, as RUA towards the HNB
+ * doesn't seem to know any confirmations to its CONNECT
+ * operation */
+
+ return 0;
+}
+
+static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_data_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ struct hnbgw_context_map *map;
+
+ /* connection-oriented data is always passed transparently
+ * towards the specific HNB, via a RUA connection identified by
+ * conn_id */
+
+ map = context_map_by_cn(cnlink, param->conn_id);
+ if (!map) {
+ /* FIXME: Return an error / released primitive */
+ return 0;
+ }
+
+ return rua_tx_dt(map->hnb_ctx, map->cn_link->is_ps, map->rua_ctx_id,
+ msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+static int handle_cn_disc_ind(struct hnbgw_cnlink *cnlink,
+ const struct osmo_scu_disconn_param *param,
+ struct osmo_prim_hdr *oph)
+{
+ struct hnbgw_context_map *map;
+
+ RUA_Cause_t rua_cause = {
+ .present = RUA_Cause_PR_NOTHING,
+ /* FIXME: Convert incoming SCCP cause to RUA cause */
+ };
+
+ /* we need to notify the HNB associated with this connection via
+ * a RUA DISCONNECT */
+
+ map = context_map_by_cn(cnlink, param->conn_id);
+ if (!map) {
+ /* FIXME: Return an error / released primitive */
+ return 0;
+ }
+
+ return rua_tx_disc(map->hnb_ctx, map->cn_link->is_ps, map->rua_ctx_id,
+ &rua_cause, msgb_l2(oph->msg), msgb_l2len(oph->msg));
+}
+
+/* Entry point for primitives coming up from SCCP User SAP */
+static int sccp_sap_up(struct osmo_prim_hdr *oph, void *slink)
+{
+ struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
+ int rc;
+
+ LOGP(DMAIN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ rc = handle_cn_unitdata(slink, &prim->u.unitdata, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
+ rc = handle_cn_conn_conf(slink, &prim->u.connect, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+ rc = handle_cn_data_ind(slink, &prim->u.data, oph);
+ break;
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ rc = handle_cn_disc_ind(slink, &prim->u.disconnect, oph);
+ break;
+ defualt:
+ LOGP(DMAIN, LOGL_ERROR,
+ "Received unknown prim %u from SCCP USER SAP\n",
+ OSMO_PRIM_HDR(oph));
+ break;
+ }
+
+ msgb_free(oph->msg);
+
+ return 0;
+}
+
+
+struct hnbgw_cnlink *hnbgw_cnlink_init(struct hnb_gw *gw, const char *host, uint16_t port)
+{
+ struct hnbgw_cnlink *cnlink = talloc_zero(gw, struct hnbgw_cnlink);
+ int rc;
+
+ INIT_LLIST_HEAD(&cnlink->map_list);
+ cnlink->T_RafC.cb = cnlink_trafc_cb;
+ cnlink->T_RafC.data = cnlink;
+ sccp_make_addr_pc_ssn(&cnlink->local_addr, 2, SCCP_SSN_RANAP);
+ sccp_make_addr_pc_ssn(&cnlink->remote_addr, 1, SCCP_SSN_RANAP);
+
+ cnlink->sua_user = osmo_sua_user_create(cnlink, sccp_sap_up);
+ if (!cnlink->sua_user) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to init SUA\n");
+ goto out_free;
+ }
+ rc = osmo_sua_client_connect(cnlink->sua_user, host, port);
+ if (rc < 0) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to connect SUA\n");
+ goto out_user;
+ }
+ cnlink->sua_link = osmo_sua_client_get_link(cnlink->sua_user);
+ if (!cnlink->sua_link) {
+ LOGP(DMAIN, LOGL_ERROR, "Failed to get SUA link\n");
+ goto out_disconnect;
+ }
+
+ llist_add_tail(&cnlink->list, &gw->cn_list);
+
+ return cnlink;
+
+out_disconnect:
+ /* FIXME */
+out_user:
+ osmo_sua_user_destroy(cnlink->sua_user);
+out_free:
+ talloc_free(cnlink);
+}
diff --git a/src/hnbgw_cn.h b/src/hnbgw_cn.h
new file mode 100644
index 0000000..2599fd3
--- /dev/null
+++ b/src/hnbgw_cn.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "hnbgw.h"
+
+struct hnbgw_cnlink *hnbgw_cnlink_init(struct hnb_gw *gw, const char *host, uint16_t port);
diff --git a/src/hnbgw_hnbap.c b/src/hnbgw_hnbap.c
index fb7cfab..f797992 100644
--- a/src/hnbgw_hnbap.c
+++ b/src/hnbgw_hnbap.c
@@ -169,7 +169,7 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
DEBUGP(DHNBAP, "UE-REGSITER-REQ ID_type=%d imsi=%s cause=%ld\n",
ies.uE_Identity.present, imsi, ies.registration_Cause);
- ue = ue_context_by_imsi(imsi);
+ ue = ue_context_by_imsi(ctx->gw, imsi);
if (!ue)
ue = ue_context_alloc(ctx, imsi);
@@ -193,7 +193,7 @@ static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
DEBUGP(DHNBAP, "UE-DE-REGSITER context=%ld cause=%ld\n",
ctxid, ies.cause);
- ue = ue_context_by_id(ctxid);
+ ue = ue_context_by_id(ctx->gw, ctxid);
if (ue)
ue_context_free(ue);
diff --git a/src/hnbgw_ranap.c b/src/hnbgw_ranap.c
index c0b968f..1e38cf0 100644
--- a/src/hnbgw_ranap.c
+++ b/src/hnbgw_ranap.c
@@ -38,31 +38,16 @@
static int ranap_tx_reset_ack(struct hnb_context *hnb,
RANAP_CN_DomainIndicator_t domain)
{
- RANAP_ResetAcknowledge_t out;
- RANAP_ResetAcknowledgeIEs_t ies;
struct msgb *msg;
int rc;
- memset(&ies, 0, sizeof(ies));
- ies.cN_DomainIndicator = domain;
-
- memset(&out, 0, sizeof(out));
- rc = ranap_encode_resetacknowledgeies(&out, &ies);
- if (rc < 0) {
- LOGP(DRANAP, LOGL_ERROR, "error encoding reset ack IEs: %d\n", rc);
- return rc;
- }
-
- msg = ranap_generate_successful_outcome(RANAP_ProcedureCode_id_Reset,
- RANAP_Criticality_reject,
- &asn_DEF_RANAP_ResetAcknowledge,
- &out);
+ msg = ranap_new_msg_reset_ack(domain, NULL);
if (!msg)
return -1;
- msg->dst = hnb;
+ rc = rua_tx_udt(hnb, msg->data, msgb_length(msg));
- rc = rua_tx_udt(msg);
+ msgb_free(msg);
return rc;
}
@@ -110,26 +95,6 @@ int ranap_parse_lai(struct gprs_ra_id *ra_id, const RANAP_LAI_t *lai)
return 0;
}
-static int ranap_rx_init_ue_msg(struct hnb_context *hnb, ANY_t *in)
-{
- RANAP_InitialUE_MessageIEs_t ies;
- struct gprs_ra_id ra_id;
- int rc;
-
- rc = ranap_decode_initialue_messageies(&ies, in);
- if (rc < 0)
- return rc;
-
- /* location area ID of the serving cell */
- ranap_parse_lai(&ra_id, &ies.lai);
-
- DEBUGP(DMAIN, "%u-%u-%u: InitialUE: %s\n", ra_id.mcc, ra_id.mnc,
- ra_id.lac, osmo_hexdump(ies.nas_pdu.buf, ies.nas_pdu.size));
- /* FIXME: hand NAS PDU into MSC */
-
- return 0;
-}
-
static int ranap_rx_dt(struct hnb_context *hnb, ANY_t *in)
{
RANAP_DirectTransferIEs_t ies;
@@ -160,19 +125,66 @@ static int ranap_rx_initiating_msg(struct hnb_context *hnb, RANAP_InitiatingMess
{
int rc;
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
switch (imsg->procedureCode) {
case RANAP_ProcedureCode_id_Reset:
+ /* Reset request */
rc = ranap_rx_init_reset(hnb, &imsg->value);
break;
- case RANAP_ProcedureCode_id_InitialUE_Message:
- rc = ranap_rx_init_ue_msg(hnb, &imsg->value);
+ case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
break;
- case RANAP_ProcedureCode_id_DirectTransfer:
- rc = ranap_rx_dt(hnb, &imsg->value);
+ case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* request */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %u from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %u from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ }
+}
+
+static int ranap_rx_successful_msg(struct hnb_context *hnb, RANAP_SuccessfulOutcome_t *imsg)
+{
+ int rc;
+
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
+ switch (imsg->procedureCode) {
+ case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
+ break;
+ case RANAP_ProcedureCode_id_ResetResource: /* response */
+ case RANAP_ProcedureCode_id_InformationTransfer:
+ case RANAP_ProcedureCode_id_DirectInformationTransfer:
+ case RANAP_ProcedureCode_id_UplinkInformationExchange:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "Procedure %u from HNB, ignoring\n", imsg->procedureCode);
+ break;
+ default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "Procedure %u from HNB, ignoring\n", imsg->procedureCode);
break;
}
}
+
+
static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu)
{
int rc;
@@ -182,10 +194,16 @@ static int _hnbgw_ranap_rx(struct hnb_context *hnb, RANAP_RANAP_PDU_t *pdu)
rc = ranap_rx_initiating_msg(hnb, &pdu->choice.initiatingMessage);
break;
case RANAP_RANAP_PDU_PR_successfulOutcome:
+ rc = ranap_rx_successful_msg(hnb, &pdu->choice.successfulOutcome);
break;
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
+ LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
+ "unsuccessful outcome procedure %u from HNB, ignoring\n",
+ pdu->choice.unsuccessfulOutcome.procedureCode);
break;
default:
+ LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
+ "presence %u from HNB, ignoring\n", pdu->present);
break;
}
}
diff --git a/src/hnbgw_rua.c b/src/hnbgw_rua.c
index eb4a56a..77c391d 100644
--- a/src/hnbgw_rua.c
+++ b/src/hnbgw_rua.c
@@ -23,6 +23,9 @@
#include <osmocom/core/utils.h>
#include <osmocom/netif/stream.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sua.h>
+
#include <unistd.h>
#include <errno.h>
#include <string.h>
@@ -33,6 +36,7 @@
#include "hnbgw_ranap.h"
#include "rua_common.h"
#include "rua_ies_defs.h"
+#include "context_map.h"
static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg)
{
@@ -67,9 +71,9 @@ int rua_tx_udt(struct hnb_context *hnb, const uint8_t *data, unsigned int len)
&out);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_ConnectionlessTransfer, &out);
- DEBUGP(DMAIN, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
+ DEBUGP(DRUA, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
- return hnbgw_rua_tx(msg->dst, msg);
+ return hnbgw_rua_tx(hnb, msg);
}
int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
@@ -103,7 +107,7 @@ int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
&out);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_DirectTransfer, &out);
- DEBUGP(DMAIN, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
+ DEBUGP(DRUA, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
return hnbgw_rua_tx(hnb, msg);
}
@@ -143,16 +147,130 @@ int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
&out);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RUA_Disconnect, &out);
- DEBUGP(DMAIN, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
+ DEBUGP(DRUA, "transmitting RUA payload of %u bytes\n", msgb_length(msg));
return hnbgw_rua_tx(hnb, msg);
}
+/* forward a RUA message to the SCCP User API to SCCP/SUA */
+static int rua_to_scu(struct hnb_context *hnb, struct hnbgw_cnlink *cn,
+ enum osmo_scu_prim_type type,
+ uint32_t context_id, uint32_t cause,
+ const uint8_t *data, unsigned int len)
+{
+ struct msgb *msg = msgb_alloc(1500, "rua_to_sua");
+ struct osmo_scu_prim *prim;
+ struct hnbgw_context_map *map;
+ int rc;
+
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, type, PRIM_OP_REQUEST, msg);
+
+ map = context_map_alloc_by_hnb(hnb, context_id, cn);
+
+ /* add primitive header */
+ switch (type) {
+ case OSMO_SCU_PRIM_N_CONNECT:
+ prim->u.connect.called_addr;
+ prim->u.connect.calling_addr;
+ prim->u.connect.sccp_class = 2;
+ prim->u.connect.conn_id = map->scu_conn_id;
+ break;
+ case OSMO_SCU_PRIM_N_DATA:
+ prim->u.data.conn_id = map->scu_conn_id;
+ break;
+ case OSMO_SCU_PRIM_N_DISCONNECT:
+ prim->u.disconnect.conn_id = map->scu_conn_id;
+ prim->u.disconnect.cause = cause;
+ break;
+ case OSMO_SCU_PRIM_N_UNITDATA:
+ prim->u.unitdata.called_addr;
+ prim->u.unitdata.calling_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* add optional data section, if needed */
+ if (data && len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ }
+
+ rc = osmo_sua_user_link_down(cn->sua_link, &prim->oph);
+
+ return rc;
+}
+
+static uint32_t rua_to_scu_cause(RUA_Cause_t *in)
+{
+ /* FIXME: Implement this! */
+#if 0
+ switch (in->present) {
+ case RUA_Cause_PR_NOTHING:
+ break;
+ case RUA_Cause_PR_radioNetwork:
+ switch (in->choice.radioNetwork) {
+ case RUA_CauseRadioNetwork_normal:
+ case RUA_CauseRadioNetwork_connect_failed:
+ case RUA_CauseRadioNetwork_network_release:
+ case RUA_CauseRadioNetwork_unspecified:
+ }
+ break;
+ case RUA_Cause_PR_transport:
+ switch (in->choice.transport) {
+ case RUA_CauseTransport_transport_resource_unavailable:
+ break;
+ case RUA_CauseTransport_unspecified:
+ break;
+ }
+ break;
+ case RUA_Cause_PR_protocol:
+ switch (in->choice.protocol) {
+ case RUA_CauseProtocol_transfer_syntax_error:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_reject:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_ignore_and_notify:
+ break;
+ case RUA_CauseProtocol_message_not_compatible_with_receiver_state:
+ break;
+ case RUA_CauseProtocol_semantic_error:
+ break;
+ case RUA_CauseProtocol_unspecified:
+ break;
+ case RUA_CauseProtocol_abstract_syntax_error_falsely_constructed_message:
+ break;
+ }
+ break;
+ case RUA_Cause_PR_misc:
+ switch (in->choice.misc) {
+ case RUA_CauseMisc_processing_overload:
+ break;
+ case RUA_CauseMisc_hardware_failure:
+ break;
+ case RUA_CauseMisc_o_and_m_intervention:
+ break;
+ case RUA_CauseMisc_unspecified:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+#else
+ return 0;
+#endif
+
+}
+
static int rua_rx_init_connect(struct msgb *msg, ANY_t *in)
{
RUA_ConnectIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ struct hnbgw_cnlink *cn;
uint32_t context_id;
int rc;
@@ -162,11 +280,24 @@ static int rua_rx_init_connect(struct msgb *msg, ANY_t *in)
context_id = asn1bitstr_to_u32(&ies.context_ID);
- DEBUGP(DMAIN, "Connect.req(ctx=0x%x, %s)\n", context_id,
+ /* route to CS (MSC) or PS (SGSN) domain */
+ switch (ies.cN_DomainIndicator) {
+ case RUA_CN_DomainIndicator_cs_domain:
+ cn = hnb->gw->cnlink_cs;
+ break;
+ case RUA_CN_DomainIndicator_ps_domain:
+ cn = hnb->gw->cnlink_ps;
+ break;
+ }
+
+ DEBUGP(DRUA, "Connect.req(ctx=0x%x, %s)\n", context_id,
ies.establishment_Cause == RUA_Establishment_Cause_emergency_call
? "emergency" : "normal");
- /* FIXME: route to CS (MSC) or PS (SGSN) domain */
- rc = hnbgw_ranap_rx(msg, ies.ranaP_Message.buf, ies.ranaP_Message.size);
+
+ rc = rua_to_scu(hnb, cn, OSMO_SCU_PRIM_N_CONNECT,
+ context_id, 0, ies.ranaP_Message.buf,
+ ies.ranaP_Message.size);
+ /* FIXME: what to do with the asn1c-allocated memory */
return rc;
}
@@ -174,7 +305,12 @@ static int rua_rx_init_connect(struct msgb *msg, ANY_t *in)
static int rua_rx_init_disconnect(struct msgb *msg, ANY_t *in)
{
RUA_DisconnectIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ struct hnbgw_cnlink *cn;
uint32_t context_id;
+ uint32_t scu_cause;
+ uint8_t *ranap_data = NULL;
+ unsigned int ranap_len = 0;
int rc;
rc = rua_decode_disconnecties(&ies, in);
@@ -182,20 +318,38 @@ static int rua_rx_init_disconnect(struct msgb *msg, ANY_t *in)
return rc;
context_id = asn1bitstr_to_u32(&ies.context_ID);
+ scu_cause = rua_to_scu_cause(&ies.cause);
- DEBUGP(DMAIN, "Disconnect.req(ctx=0x%x,cause=%s)\n", context_id,
+ DEBUGP(DRUA, "Disconnect.req(ctx=0x%x,cause=%s)\n", context_id,
rua_cause_str(&ies.cause));
- if (ies.presenceMask & DISCONNECTIES_RUA_RANAP_MESSAGE_PRESENT)
- rc = hnbgw_ranap_rx(msg, ies.ranaP_Message.buf,
- ies.ranaP_Message.size);
- /* FIXME */
+ /* route to CS (MSC) or PS (SGSN) domain */
+ switch (ies.cN_DomainIndicator) {
+ case RUA_CN_DomainIndicator_cs_domain:
+ cn = hnb->gw->cnlink_cs;
+ break;
+ case RUA_CN_DomainIndicator_ps_domain:
+ cn = hnb->gw->cnlink_ps;
+ break;
+ }
+
+ if (ies.presenceMask & DISCONNECTIES_RUA_RANAP_MESSAGE_PRESENT) {
+ ranap_data = ies.ranaP_Message.buf;
+ ranap_len = ies.ranaP_Message.size;
+ }
+
+ rc = rua_to_scu(hnb, cn, OSMO_SCU_PRIM_N_DISCONNECT,
+ context_id, scu_cause, ranap_data, ranap_len);
+ /* FIXME: what to do with the asn1c-allocated memory */
+
return rc;
}
static int rua_rx_init_dt(struct msgb *msg, ANY_t *in)
{
RUA_DirectTransferIEs_t ies;
+ struct hnb_context *hnb = msg->dst;
+ struct hnbgw_cnlink *cn;
uint32_t context_id;
int rc;
@@ -205,9 +359,22 @@ static int rua_rx_init_dt(struct msgb *msg, ANY_t *in)
context_id = asn1bitstr_to_u32(&ies.context_ID);
- DEBUGP(DMAIN, "Data.req(ctx=0x%x)\n", context_id);
- /* FIXME */
- rc = hnbgw_ranap_rx(msg, ies.ranaP_Message.buf, ies.ranaP_Message.size);
+ DEBUGP(DRUA, "Data.req(ctx=0x%x)\n", context_id);
+
+ /* route to CS (MSC) or PS (SGSN) domain */
+ switch (ies.cN_DomainIndicator) {
+ case RUA_CN_DomainIndicator_cs_domain:
+ cn = hnb->gw->cnlink_cs;
+ break;
+ case RUA_CN_DomainIndicator_ps_domain:
+ cn = hnb->gw->cnlink_ps;
+ break;
+ }
+
+ rc = rua_to_scu(hnb, cn, OSMO_SCU_PRIM_N_DATA,
+ context_id, 0, ies.ranaP_Message.buf,
+ ies.ranaP_Message.size);
+ /* FIXME: what to do with the asn1c-allocated memory */
return rc;
@@ -216,17 +383,23 @@ static int rua_rx_init_dt(struct msgb *msg, ANY_t *in)
static int rua_rx_init_udt(struct msgb *msg, ANY_t *in)
{
RUA_ConnectionlessTransferIEs_t ies;
+ RUA_CN_DomainIndicator_t domain;
int rc;
rc = rua_decode_connectionlesstransferies(&ies, in);
if (rc < 0)
return rc;
- DEBUGP(DMAIN, "UData.req()\n");
+ DEBUGP(DRUA, "UData.req()\n");
- /* FIXME: pass on to RANAP */
+ /* according tot the spec, we can primarily receive Overload,
+ * Reset, Reset ACK, Error Indication, reset Resource, Reset
+ * Resurce Acknowledge as connecitonless RANAP. There are some
+ * more messages regarding Information Transfer, Direct
+ * Information Transfer and Uplink Information Trnansfer that we
+ * can ignore. In either case, it is RANAP that we need to
+ * decode... */
rc = hnbgw_ranap_rx(msg, ies.ranaP_Message.buf, ies.ranaP_Message.size);
- /* FIXME: what to do with the asn1c-allocated memory */
return rc;
}
@@ -241,6 +414,9 @@ static int rua_rx_init_err_ind(struct msgb *msg, ANY_t *in)
if (rc < 0)
return rc;
+ DEBUGP(DRUA, "UData.ErrorInd()\n");
+
+ return rc;
}
static int rua_rx_initiating_msg(struct msgb *msg, RUA_InitiatingMessage_t *imsg)
@@ -272,12 +448,12 @@ static int rua_rx_initiating_msg(struct msgb *msg, RUA_InitiatingMessage_t *imsg
static int rua_rx_successful_outcome_msg(struct msgb *msg, RUA_SuccessfulOutcome_t *in)
{
-
+ /* FIXME */
}
static int rua_rx_unsuccessful_outcome_msg(struct msgb *msg, RUA_UnsuccessfulOutcome_t *in)
{
-
+ /* FIXME */
}
@@ -326,5 +502,5 @@ int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg)
int hnbgw_rua_init(void)
{
-
+ return 0;
}
diff --git a/src/ranap_msg_factory.c b/src/ranap_msg_factory.c
index 99a86ff..3202e1a 100644
--- a/src/ranap_msg_factory.c
+++ b/src/ranap_msg_factory.c
@@ -38,6 +38,78 @@ static long *new_long(long in)
return out;
}
+/*! \brief generate RANAP RESET message */
+struct msgb *ranap_new_msg_reset(RANAP_CN_DomainIndicator_t domain,
+ RANAP_Cause_t *cause)
+{
+ RANAP_ResetIEs_t ies;
+ RANAP_Reset_t out;
+ struct msgb *msg;
+ int rc;
+
+ memset(&ies, 0, sizeof(ies));
+ ies.cN_DomainIndicator = domain;
+ if (cause)
+ memcpy(&ies.cause, cause, sizeof(ies.cause));
+
+ memset(&out, 0, sizeof(out));
+ rc = ranap_encode_reseties(&out, &ies);
+ if (rc < 0) {
+ LOGP(DRANAP, LOGL_ERROR, "error encoding reset IEs: %d\n", rc);
+ return NULL;
+ }
+
+ msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_Reset,
+ RANAP_Criticality_reject,
+ &asn_DEF_RANAP_Reset,
+ &out);
+
+ /* release dynamic allocations attached to dt */
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_Reset, &out);
+
+ return msg;
+}
+
+/*! \brief generate RANAP RESET ACK message */
+struct msgb *ranap_new_msg_reset_ack(RANAP_CN_DomainIndicator_t domain,
+ RANAP_GlobalRNC_ID_t *rnc_id)
+{
+ RANAP_ResetAcknowledgeIEs_t ies;
+ RANAP_ResetAcknowledge_t out;
+ struct msgb *msg;
+ int rc;
+
+ memset(&ies, 0, sizeof(ies));
+ ies.cN_DomainIndicator = domain;
+
+ /* The RNC shall include the globalRNC_ID in the RESET
+ * ACKNOWLEDGE message to the CN */
+ if (rnc_id) {
+ ies.presenceMask = RESETACKNOWLEDGEIES_RANAP_GLOBALRNC_ID_PRESENT;
+ /* FIXME: Copy PLMN Identity TBCD-String */
+ ies.globalRNC_ID.rNC_ID = rnc_id->rNC_ID;
+ }
+
+ /* FIXME: Do we need criticalityDiagnostics */
+
+ memset(&out, 0, sizeof(out));
+ rc = ranap_encode_resetacknowledgeies(&out, &ies);
+ if (rc < 0) {
+ LOGP(DRANAP, LOGL_ERROR, "error encoding reset ack IEs: %d\n", rc);
+ return NULL;
+ }
+
+ msg = ranap_generate_successful_outcome(RANAP_ProcedureCode_id_Reset,
+ RANAP_Criticality_reject,
+ &asn_DEF_RANAP_ResetAcknowledge,
+ &out);
+
+ /* release dynamic allocations attached to dt */
+ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_ResetAcknowledge, &out);
+
+ return msg;
+}
+
/*! \brief generate RANAP DIRECT TRANSFER message */
struct msgb *ranap_new_msg_dt(uint8_t sapi, const uint8_t *nas, unsigned int nas_len)
{
diff --git a/src/ranap_msg_factory.h b/src/ranap_msg_factory.h
index b721045..3bc73e1 100644
--- a/src/ranap_msg_factory.h
+++ b/src/ranap_msg_factory.h
@@ -1,4 +1,9 @@
-#pragma once
+#pragma once
+
+#include <stdint.h>
+#include "ranap/RANAP_Cause.h"
+#include "ranap/RANAP_CN-DomainIndicator.h"
+#include "ranap/RANAP_GlobalRNC-ID.h"
/*! \brief generate RANAP DIRECT TRANSFER message */
struct msgb *ranap_new_msg_dt(uint8_t sapi, const uint8_t *nas, unsigned int nas_len);
@@ -20,3 +25,11 @@ struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip, uin
/*! \brief generate RANAP RAB ASSIGNMENT REQUEST message for PS (data) */
struct msgb *ranap_new_msg_rab_assign_data(uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei);
+
+/*! \brief generate RANAP RESET message */
+struct msgb *ranap_new_msg_reset(RANAP_CN_DomainIndicator_t domain,
+ RANAP_Cause_t *cause);
+
+/*! \brief generate RANAP RESET ACK message */
+struct msgb *ranap_new_msg_reset_ack(RANAP_CN_DomainIndicator_t domain,
+ RANAP_GlobalRNC_ID_t *rnc_id);
diff --git a/src/sccp_helpers.c b/src/sccp_helpers.c
new file mode 100644
index 0000000..ca580ea
--- /dev/null
+++ b/src/sccp_helpers.c
@@ -0,0 +1,137 @@
+/* SCCP User SAP helper functions (move to libosmo-sigtran?) */
+
+/* (C) 2015 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 Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sua.h>
+
+#include "sccp_helpers.h"
+
+int sccp_tx_unitdata(struct osmo_sua_link *link,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ uint8_t *data, unsigned int len)
+{
+ struct msgb *msg = msgb_alloc(1024, "sccp_tx_unitdata");
+ struct osmo_scu_prim *prim;
+ struct osmo_scu_unitdata_param *param;
+
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ param = &prim->u.unitdata;
+ param->calling_addr.presence = OSMO_SCCP_ADDR_T_SSN;
+ param->called_addr.presence = OSMO_SCCP_ADDR_T_SSN;
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, msg);
+
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+
+ return osmo_sua_user_link_down(link, &prim->oph);
+}
+
+int sccp_tx_unitdata_msg(struct osmo_sua_link *link,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ struct msgb *msg)
+{
+ int rc;
+
+ rc = sccp_tx_unitdata(link, calling_addr, called_addr,
+ msg->data, msgb_length(msg));
+ msgb_free(msg);
+
+ return rc;
+}
+
+
+#define SSN_RANAP 142
+void sccp_make_addr_pc_ssn(struct osmo_sccp_addr *addr, uint32_t pc, uint32_t ssn)
+{
+ addr->presence = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC;
+ addr->ssn = ssn;
+ addr->pc = pc;
+}
+
+int sccp_tx_conn_req(struct osmo_sua_link *link, uint32_t conn_id,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ uint8_t *data, unsigned int len)
+{
+ struct msgb *msg = msgb_alloc(1024, "sccp_tx_conn_req");
+ struct osmo_scu_prim *prim;
+
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER,
+ OSMO_SCU_PRIM_N_CONNECT,
+ PRIM_OP_REQUEST, msg);
+ sccp_make_addr_pc_ssn(&prim->u.connect.calling_addr, 1, SSN_RANAP);
+ prim->u.connect.sccp_class = 2;
+ prim->u.connect.conn_id = conn_id;
+
+ if (data && len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ }
+
+ return osmo_sua_user_link_down(link, &prim->oph);
+}
+
+int sccp_tx_conn_req_msg(struct osmo_sua_link *link, uint32_t conn_id,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ struct msgb *msg)
+{
+ int rc;
+
+ rc = sccp_tx_conn_req(link, conn_id, calling_addr, called_addr,
+ msg->data, msgb_length(msg));
+ msgb_free(msg);
+
+ return rc;
+}
+
+int sccp_tx_data(struct osmo_sua_link *link, uint32_t conn_id,
+ uint8_t *data, unsigned int len)
+{
+ struct msgb *msg = msgb_alloc(1024, "sccp_tx_data");
+ struct osmo_scu_prim *prim;
+
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER,
+ OSMO_SCU_PRIM_N_DATA,
+ PRIM_OP_REQUEST, msg);
+ prim->u.data.conn_id = conn_id;
+
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+
+ return osmo_sua_user_link_down(link, &prim->oph);
+}
+
+int sccp_tx_data_msg(struct osmo_sua_link *link, uint32_t conn_id,
+ struct msgb *msg)
+{
+ int rc;
+
+ rc = sccp_tx_data(link, conn_id, msg->data, msgb_length(msg));
+ msgb_free(msg);
+
+ return rc;
+}
diff --git a/src/sccp_helpers.h b/src/sccp_helpers.h
new file mode 100644
index 0000000..089d69a
--- /dev/null
+++ b/src/sccp_helpers.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <unistd.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sua.h>
+
+int sccp_tx_unitdata(struct osmo_sua_link *link,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ uint8_t *data, unsigned int len);
+
+int sccp_tx_unitdata_msg(struct osmo_sua_link *link,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ struct msgb *msg);
+
+void sccp_make_addr_pc_ssn(struct osmo_sccp_addr *addr, uint32_t pc, uint32_t ssn);
+
+int sccp_tx_conn_req(struct osmo_sua_link *link, uint32_t conn_id,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ uint8_t *data, unsigned int len);
+
+int sccp_tx_conn_req_msg(struct osmo_sua_link *link, uint32_t conn_id,
+ const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ struct msgb *msg);
+
+int sccp_tx_data(struct osmo_sua_link *link, uint32_t conn_id,
+ uint8_t *data, unsigned int len);
+
+int sccp_tx_data_msg(struct osmo_sua_link *link, uint32_t conn_id,
+ struct msgb *msg);