hnbgw: Bring all parts together

We now have the RUA and SUA parts interconnected by the
context ID mapper, and should be able to pass messages back and forward
between both sides.

Unfortunately this touches a bit of everything, but the structures are
all still very much in flux.  Hopefully they will start to stabilize at
some point soon...
This commit is contained in:
Harald Welte 2015-12-24 00:40:52 +01:00
parent cd5e981490
commit c4338deee9
12 changed files with 946 additions and 119 deletions

View File

@ -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

View File

@ -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;
struct ue_context *ue_context_by_id(uint32_t id)
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(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();

View File

@ -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;
/* currently active CN links for CS and PS */
struct hnbgw_cnlink *cnlink_cs;
struct hnbgw_cnlink *cnlink_ps;
};
extern struct hnb_gw g_hnb_gw;
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);

371
src/hnbgw_cn.c Normal file
View File

@ -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);
}

5
src/hnbgw_cn.h Normal file
View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

137
src/sccp_helpers.c Normal file
View File

@ -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;
}

33
src/sccp_helpers.h Normal file
View File

@ -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);