169 lines
4.5 KiB
C
169 lines
4.5 KiB
C
/* MSC RAN connection implementation */
|
|
|
|
/*
|
|
* (C) 2016-2018 by sysmocom s.m.f.c. <info@sysmocom.de>
|
|
* All Rights Reserved
|
|
*
|
|
* Author: Neels Hofmeyr
|
|
*
|
|
* 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/logging.h>
|
|
#include <osmocom/core/fsm.h>
|
|
#include <osmocom/core/signal.h>
|
|
|
|
#include <osmocom/msc/ran_conn.h>
|
|
#include <osmocom/msc/vlr.h>
|
|
#include <osmocom/msc/debug.h>
|
|
#include <osmocom/msc/transaction.h>
|
|
#include <osmocom/msc/signal.h>
|
|
#include <osmocom/msc/sgs_iface.h>
|
|
#include <osmocom/msc/ran_peer.h>
|
|
#include <osmocom/msc/sccp_ran.h>
|
|
#include <osmocom/msc/ran_infra.h>
|
|
#include <osmocom/msc/msub.h>
|
|
|
|
struct ran_conn *ran_conn_create_incoming(struct ran_peer *ran_peer, uint32_t sccp_conn_id)
|
|
{
|
|
struct ran_conn *conn;
|
|
|
|
conn = talloc(ran_peer, struct ran_conn);
|
|
OSMO_ASSERT(conn);
|
|
|
|
*conn = (struct ran_conn){
|
|
.ran_peer = ran_peer,
|
|
.sccp_conn_id = sccp_conn_id,
|
|
};
|
|
|
|
llist_add(&conn->entry, &ran_peer->sri->ran_conns);
|
|
return conn;
|
|
}
|
|
|
|
struct ran_conn *ran_conn_create_outgoing(struct ran_peer *ran_peer)
|
|
{
|
|
/* FIXME use method being developed in gerrit id Ifd55c6b7ed2558ff072042079cf45f5068a971de */
|
|
static uint32_t next_outgoing_conn_id = 2342;
|
|
uint32_t conn_id = 0;
|
|
int attempts = 1000;
|
|
bool already_used = true;
|
|
while (attempts--) {
|
|
struct ran_conn *conn;
|
|
|
|
conn_id = next_outgoing_conn_id;
|
|
next_outgoing_conn_id++;
|
|
|
|
already_used = false;
|
|
llist_for_each_entry(conn, &ran_peer->sri->ran_conns, entry) {
|
|
if (conn->sccp_conn_id == conn_id) {
|
|
already_used = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!already_used)
|
|
break;
|
|
}
|
|
if (already_used)
|
|
return NULL;
|
|
LOG_RAN_PEER(ran_peer, LOGL_DEBUG, "Outgoing conn id: %u\n", conn_id);
|
|
return ran_conn_create_incoming(ran_peer, conn_id);
|
|
}
|
|
|
|
/* Return statically allocated string of the ran_conn RAT type and id. */
|
|
const char *ran_conn_name(struct ran_conn *conn)
|
|
{
|
|
static char id[42];
|
|
int rc;
|
|
const char *ran_peer_name;
|
|
|
|
if (!conn)
|
|
return "ran_conn==NULL";
|
|
|
|
if (!conn->ran_peer || !conn->ran_peer->sri || !conn->ran_peer->sri->ran)
|
|
ran_peer_name = "no-RAN-peer";
|
|
else
|
|
ran_peer_name = osmo_rat_type_name(conn->ran_peer->sri->ran->type);
|
|
|
|
rc = snprintf(id, sizeof(id), "%s-%u", ran_peer_name, conn->sccp_conn_id);
|
|
/* < 0 is error, == 0 is empty, >= size means truncation. Not really expecting this to catch on in any practical
|
|
* situation. */
|
|
if (rc <= 0 || rc >= sizeof(id))
|
|
return "conn-name-error";
|
|
return id;
|
|
}
|
|
|
|
int ran_conn_down_l2_co(struct ran_conn *conn, struct msgb *l3, bool initial)
|
|
{
|
|
struct ran_peer_ev_ctx co = {
|
|
.conn_id = conn->sccp_conn_id,
|
|
.conn = conn,
|
|
.msg = l3,
|
|
};
|
|
if (!conn->ran_peer)
|
|
return -EIO;
|
|
return osmo_fsm_inst_dispatch(conn->ran_peer->fi,
|
|
initial ? RAN_PEER_EV_MSG_DOWN_CO_INITIAL : RAN_PEER_EV_MSG_DOWN_CO,
|
|
&co);
|
|
}
|
|
|
|
void ran_conn_msc_role_gone(struct ran_conn *conn, struct osmo_fsm_inst *msc_role)
|
|
{
|
|
if (!conn)
|
|
return;
|
|
|
|
if (conn->msc_role != msc_role)
|
|
return;
|
|
|
|
conn->msc_role = NULL;
|
|
ran_conn_close(conn);
|
|
}
|
|
|
|
/* Regularly close the conn */
|
|
void ran_conn_close(struct ran_conn *conn)
|
|
{
|
|
if (!conn)
|
|
return;
|
|
if (conn->closing)
|
|
return;
|
|
conn->closing = true;
|
|
LOG_RAN_PEER(conn->ran_peer, LOGL_DEBUG, "Closing %s\n", ran_conn_name(conn));
|
|
|
|
if (conn->msc_role) {
|
|
osmo_fsm_inst_dispatch(conn->msc_role, MSC_EV_FROM_RAN_CONN_RELEASED, NULL);
|
|
conn->msc_role = NULL;
|
|
}
|
|
|
|
if (conn->ran_peer) {
|
|
/* Todo: pass a useful SCCP cause? */
|
|
sccp_ran_disconnect(conn->ran_peer->sri, conn->sccp_conn_id, 0);
|
|
conn->ran_peer = NULL;
|
|
}
|
|
|
|
LOG_RAN_PEER(conn->ran_peer, LOGL_DEBUG, "Deallocating %s\n", ran_conn_name(conn));
|
|
llist_del(&conn->entry);
|
|
talloc_free(conn);
|
|
}
|
|
|
|
/* Same as ran_conn_close() but without sending any SCCP messages (e.g. after RESET) */
|
|
void ran_conn_discard(struct ran_conn *conn)
|
|
{
|
|
if (!conn)
|
|
return;
|
|
/* Make sure to drop dead and don't dispatch things like DISCONNECT requests on SCCP. */
|
|
conn->ran_peer = NULL;
|
|
ran_conn_close(conn);
|
|
}
|