This was originally a long series of commits converging to the final result seen in this patch. It does not make much sense to review the smaller steps' trial and error, we need to review this entire change as a whole. Implement AoIP in osmo-msc and osmo-bsc. Change over to the new libosmo-sigtran API with support for proper SCCP/M3UA/SCTP stacking, as mandated by 3GPP specifications for the IuCS and IuPS interfaces. From here on, a separate osmo-stp process is required for SCCP routing between OsmoBSC / OsmoHNBGW <-> OsmoMSC / OsmoSGSN jenkins.sh: build from libosmo-sccp and osmo-iuh master branches now for new M3UA SIGTRAN. Patch-by: pmaier, nhofmeyr, laforge Change-Id: I5ae4e05ee7c57cad341ea5e86af37c1f6b0ffa77changes/86/3486/6
parent
09e2c9f07d
commit
fbf6610dc1
@ -0,0 +1,76 @@ |
||||
/* (C) 2017 by Sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <openbsc/a_reset.h> |
||||
|
||||
/* A struct to keep a context information about the BSCs we are associated with */ |
||||
struct bsc_context { |
||||
struct llist_head list; |
||||
|
||||
/* Holds a copy of the sccp address of the BSC,
|
||||
* this address will become known as soon as |
||||
* a remote BSC tries to make a connection or |
||||
* sends a RESET request via UNIDATA */ |
||||
struct osmo_sccp_addr bsc_addr; |
||||
|
||||
/* Holds a copy of the our local MSC address,
|
||||
* this will be the sccp-address that is associated |
||||
* with the A interface */ |
||||
struct osmo_sccp_addr msc_addr; |
||||
|
||||
/* A pointer to the reset handler FSM, the
|
||||
* state machine is allocated when the BSC |
||||
* is registerd. */ |
||||
struct a_reset_ctx *reset; |
||||
|
||||
/* A pointer to the sccp_user that is associated
|
||||
* with the A interface. We need this information |
||||
* to send the resets and to send paging requests */ |
||||
struct osmo_sccp_user *sccp_user; |
||||
}; |
||||
|
||||
/* Initalize A interface connection between to MSC and BSC */ |
||||
int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network); |
||||
|
||||
/* Send DTAP message via A-interface */ |
||||
int a_iface_tx_dtap(struct msgb *msg); |
||||
|
||||
/* Send Cipher mode command via A-interface */ |
||||
int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn, |
||||
int cipher, const const uint8_t *key, int len, int include_imeisv); |
||||
|
||||
/* Page a subscriber via A-interface */ |
||||
int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac); |
||||
|
||||
/* Send assignment request via A-interface */ |
||||
int a_iface_tx_assignment(const struct gsm_trans *trans); |
||||
|
||||
/* Send clear command via A-interface */ |
||||
int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn); |
||||
|
||||
/* Clear all subscriber connections on a specified BSC
|
||||
* (Helper function for a_iface_bssap.c) */ |
||||
void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr); |
||||
|
||||
/* Delete info of a closed connection from the active connection list
|
||||
* (Helper function for a_iface_bssap.c) */ |
||||
void a_delete_bsc_con(uint32_t conn_id); |
@ -0,0 +1,41 @@ |
||||
/* (C) 2017 by sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
/* Note: The structs and functions presented in this header file are intended
|
||||
* to be used only by a_iface.c. */ |
||||
|
||||
/* A structure to hold tha most basic information about a sigtran connection
|
||||
* we use this struct internally here to pass connection data around */ |
||||
struct a_conn_info { |
||||
struct osmo_sccp_addr *msc_addr; |
||||
struct osmo_sccp_addr *bsc_addr; |
||||
uint32_t conn_id; |
||||
struct gsm_network *network; |
||||
struct a_reset_ctx *reset; |
||||
}; |
||||
|
||||
/* Receive incoming connection less data messages via sccp */ |
||||
void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg); |
||||
|
||||
/* Receive incoming connection oriented data messages via sccp */ |
||||
int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg); |
||||
|
@ -0,0 +1,63 @@ |
||||
/* (C) 2017 by sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
|
||||
|
||||
/* Reset context data (callbacks, state machine etc...) */ |
||||
struct a_reset_ctx { |
||||
|
||||
/* FSM instance, which handles the reset procedure */ |
||||
struct osmo_fsm_inst *fsm; |
||||
|
||||
/* Connection failure counter. When this counter
|
||||
* reaches a certain threshold, the reset procedure |
||||
* will be triggered */ |
||||
int conn_loss_counter; |
||||
|
||||
/* A human readable name to display in the logs */ |
||||
char name[256]; |
||||
|
||||
/* Callback function to be called when a connection
|
||||
* failure is detected and a rest must occur */ |
||||
void (*cb)(void *priv); |
||||
|
||||
/* Privated data for the callback function */ |
||||
void *priv; |
||||
}; |
||||
|
||||
/* Create and start state machine which handles the reset/reset-ack procedure */ |
||||
struct a_reset_ctx *a_reset_alloc(const void *ctx, const char *name, void *cb, void *priv); |
||||
|
||||
/* Tear down state machine */ |
||||
void a_reset_free(struct a_reset_ctx *reset); |
||||
|
||||
/* Confirm that we sucessfully received a reset acknowlege message */ |
||||
void a_reset_ack_confirm(struct a_reset_ctx *reset); |
||||
|
||||
/* Report a failed connection */ |
||||
void a_reset_conn_fail(struct a_reset_ctx *reset); |
||||
|
||||
/* Report a successful connection */ |
||||
void a_reset_conn_success(struct a_reset_ctx *reset); |
||||
|
||||
/* Check if we have a connection to a specified msc */ |
||||
bool a_reset_conn_ready(struct a_reset_ctx *reset); |
@ -0,0 +1,34 @@ |
||||
/* (C) 2017 by sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
/* Create and start state machine which handles the reset/reset-ack procedure */ |
||||
void start_reset_fsm(struct bsc_msc_data *msc); |
||||
|
||||
/* Confirm that we sucessfully received a reset acknowlege message */ |
||||
void reset_ack_confirm(struct bsc_msc_data *msc); |
||||
|
||||
/* Report a failed connection */ |
||||
void report_conn_fail(struct bsc_msc_data *msc); |
||||
|
||||
/* Report a successful connection */ |
||||
void report_conn_success(struct bsc_msc_data *msc); |
||||
|
||||
/* Check if we have a connection to a specified msc */ |
||||
bool sccp_conn_ready(struct bsc_msc_data *msc); |
@ -0,0 +1,48 @@ |
||||
/* (C) 2017 by Sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <openbsc/gsm_data.h> |
||||
#include <openbsc/bsc_msc_data.h> |
||||
|
||||
/* Allocate resources to make a new connection oriented sigtran connection
|
||||
* (not the connection ittself!) */ |
||||
enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc); |
||||
|
||||
/* Open a new connection oriented sigtran connection */ |
||||
int osmo_bsc_sigtran_open_conn(const struct osmo_bsc_sccp_con *conn, struct msgb *msg); |
||||
|
||||
/* Send data to MSC */ |
||||
int osmo_bsc_sigtran_send(const struct osmo_bsc_sccp_con *conn, struct msgb *msg); |
||||
|
||||
/* Delete a connection from the list with open connections
|
||||
* (called by osmo_bsc_api.c on failing open connections and |
||||
* locally, when a connection is closed by the MSC */ |
||||
int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *sccp); |
||||
|
||||
/* Initalize osmo sigtran backhaul */ |
||||
int osmo_bsc_sigtran_init(struct llist_head *mscs); |
||||
|
||||
/* Close all open sigtran connections and channels */ |
||||
void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc); |
||||
|
||||
/* Send reset-ack to MSC */ |
||||
void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc); |
@ -0,0 +1,224 @@ |
||||
/* (C) 2017 by sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved |
||||
* |
||||
* Author: Philipp Maier |
||||
* |
||||
* 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/utils.h> |
||||
#include <osmocom/core/timer.h> |
||||
#include <osmocom/core/fsm.h> |
||||
#include <unistd.h> |
||||
#include <errno.h> |
||||
#include <string.h> |
||||
#include <openbsc/debug.h> |
||||
#include <openbsc/bsc_msc_data.h> |
||||
#include <openbsc/osmo_bsc_sigtran.h> |
||||
|
||||
#define RESET_RESEND_INTERVAL 2 /* sec */ |
||||
#define RESET_RESEND_TIMER_NO 1234 /* FIXME: dig out the real timer number */ |
||||
#define BAD_CONNECTION_THRESOLD 3 /* connection failures */ |
||||
|
||||
enum fsm_states { |
||||
ST_DISC, /* Disconnected from remote end */ |
||||
ST_CONN, /* We have a confirmed connection */ |
||||
}; |
||||
|
||||
static const struct value_string fsm_state_names[] = { |
||||
{ST_DISC, "ST_DISC (disconnected)"}, |
||||
{ST_CONN, "ST_CONN (connected)"}, |
||||
{0, NULL}, |
||||
}; |
||||
|
||||
enum fsm_evt { |
||||
EV_RESET_ACK, /* got reset acknowlegement from remote end */ |
||||
EV_N_DISCONNECT, /* lost a connection */ |
||||
EV_N_CONNECT, /* made a successful connection */ |
||||
}; |
||||
|
||||
static const struct value_string fsm_evt_names[] = { |
||||
{EV_RESET_ACK, "EV_RESET_ACK"}, |
||||
{EV_N_DISCONNECT, "EV_N_DISCONNECT"}, |
||||
{EV_N_CONNECT, "EV_N_CONNECT"}, |
||||
{0, NULL}, |
||||
}; |
||||
|
||||
/* Disconnected state */ |
||||
static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
||||
{ |
||||
struct a_reset_ctx *reset = (struct a_reset_ctx *)data; |
||||
OSMO_ASSERT(reset); |
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name, |
||||
get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event)); |
||||
|
||||
reset->conn_loss_counter = 0; |
||||
osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0); |
||||
} |
||||
|
||||
/* Connected state */ |
||||
static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) |
||||
{ |
||||
struct a_reset_ctx *reset = (struct a_reset_ctx *)data; |
||||
OSMO_ASSERT(reset); |
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name, |
||||
get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event)); |
||||
|
||||
switch (event) { |
||||
case EV_N_DISCONNECT: |
||||
if (reset->conn_loss_counter >= BAD_CONNECTION_THRESOLD) { |
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) SIGTRAN connection down, reconnecting...\n", reset->name); |
||||
osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); |
||||
} else |
||||
reset->conn_loss_counter++; |
||||
break; |
||||
case EV_N_CONNECT: |
||||
reset->conn_loss_counter = 0; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/* Timer callback to retransmit the reset signal */ |
||||
static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi) |
||||
{ |
||||
struct a_reset_ctx *reset = (struct a_reset_ctx *)fi->priv; |
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) reset-ack timeout (T%i) in state %s, resending...\n", reset->name, fi->T, |
||||
get_value_string(fsm_state_names, fi->state)); |
||||
|
||||
reset->cb(reset->priv); |
||||
|
||||
osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); |
||||
return 0; |
||||
} |
||||
|
||||
static struct osmo_fsm_state fsm_states[] = { |
||||
[ST_DISC] = { |
||||
.in_event_mask = (1 << EV_RESET_ACK), |
||||
.out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), |
||||
.name = "DISC", |
||||
.action = fsm_disc_cb, |
||||
}, |
||||
[ST_CONN] = { |
||||
.in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT), |
||||
.out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), |
||||
.name = "CONN", |
||||
.action = fsm_conn_cb, |
||||
}, |
||||
}; |
||||
|
||||
/* State machine definition */ |
||||
static struct osmo_fsm fsm = { |
||||
.name = "FSM RESET", |
||||
.states = fsm_states, |
||||
.num_states = ARRAY_SIZE(fsm_states), |
||||
.log_subsys = DMSC, |
||||
.timer_cb = fsm_reset_ack_timeout_cb, |
||||
}; |
||||
|
||||
/* Create and start state machine which handles the reset/reset-ack procedure */ |
||||
struct a_reset_ctx *a_reset_alloc(const void *ctx, const char *name, void *cb, void *priv) |
||||
{ |
||||
OSMO_ASSERT(name); |
||||
|
||||
struct a_reset_ctx *reset; |
||||
|
||||
/* Register the fsm description (if not already done) */ |
||||
if (osmo_fsm_find_by_name(fsm.name) != &fsm) |
||||
osmo_fsm_register(&fsm); |
||||
|
||||
/* Allocate and configure a new fsm instance */ |
||||
reset = talloc_zero(ctx, struct a_reset_ctx); |
||||
OSMO_ASSERT(reset); |
||||
reset->priv = priv; |
||||
reset->cb = cb; |
||||
strncpy(reset->name, name, sizeof(reset->name)); |
||||
reset->conn_loss_counter = 0; |
||||
reset->fsm = osmo_fsm_inst_alloc(&fsm, NULL, NULL, LOGL_DEBUG, "FSM RESET INST"); |
||||
OSMO_ASSERT(reset->fsm); |
||||
reset->fsm->priv = reset; |
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) reset handler fsm created.\n", reset->name); |
||||
|
||||
/* kick off reset-ack sending mechanism */ |
||||
osmo_fsm_inst_state_chg(reset->fsm, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); |
||||
|
||||
return reset; |
||||
} |
||||
|
||||
/* Tear down state machine */ |
||||
void a_reset_free(struct a_reset_ctx *reset) |
||||
{ |
||||
OSMO_ASSERT(reset); |
||||
OSMO_ASSERT(reset->fsm); |
||||
|
||||
osmo_fsm_inst_free(reset->fsm); |
||||
reset->fsm = NULL; |
||||
|
||||
memset(reset, 0, sizeof(*reset)); |
||||
talloc_free(reset); |
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "(%s) reset handler fsm destroyed.\n", reset->name); |
||||
} |
||||
|
||||
/* Confirm that we sucessfully received a reset acknowlege message */ |
||||
void a_reset_ack_confirm(struct a_reset_ctx *reset) |
||||
{ |
||||
OSMO_ASSERT(reset); |
||||
OSMO_ASSERT(reset->fsm); |
||||
|
||||
osmo_fsm_inst_dispatch(reset->fsm, EV_RESET_ACK, reset); |
||||
} |
||||
|
||||
/* Report a failed connection */ |
||||
void a_reset_conn_fail(struct a_reset_ctx *reset) |
||||
{ |
||||
/* If no reset context is supplied, just drop the info */ |
||||
if (!reset) |
||||
return; |
||||
|
||||
OSMO_ASSERT(reset->fsm); |
||||
|
||||
osmo_fsm_inst_dispatch(reset->fsm, EV_N_DISCONNECT, reset); |
||||
} |
||||
|
||||
/* Report a successful connection */ |
||||
void a_reset_conn_success(struct a_reset_ctx *reset) |
||||
{ |
||||
/* If no reset context is supplied, just drop the info */ |
||||
if (!reset) |
||||
return; |
||||
|
||||
OSMO_ASSERT(reset->fsm); |
||||
|
||||
osmo_fsm_inst_dispatch(reset->fsm, EV_N_CONNECT, reset); |
||||
} |
||||
|
||||
/* Check if we have a connection to a specified msc */ |
||||
bool a_reset_conn_ready(struct a_reset_ctx *reset) |
||||
{ |
||||
/* If no reset context is supplied, we assume that
|
||||
* the connection can't be ready! */ |
||||
if (!reset) |
||||
return false; |
||||
|
||||
OSMO_ASSERT(reset->fsm); |
||||
if (reset->fsm->state == ST_CONN) |
||||
return true; |
||||
|
||||
return false; |
||||
} |