aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
committerHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
commit31c00f7d6fa63937f2c973157d196a427f6eef95 (patch)
tree6b7c81d92b6a8b83d0588b2b59d47fd0cca7a052 /openbsc/src/osmo-bsc/osmo_bsc_sccp.c
parent9349d7ff7c5866110a1f2421ccc68a487e4030be (diff)
re-structure the OpenBSC directory layout
The new structure divides the code into a number of libraries for the BSC core functionality, MSC core functionality, Abis transport, TRAU and other bits. This doesn't introduce any functional code change but simply moves around files and alters Makefile.am accordingly. Next step would be to disentangle a lot of the inter-library dependencies and make the individual bits of code more independent.
Diffstat (limited to 'openbsc/src/osmo-bsc/osmo_bsc_sccp.c')
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_sccp.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
new file mode 100644
index 000000000..1abb47394
--- /dev/null
+++ b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
@@ -0,0 +1,288 @@
+/* Interaction with the SCCP subsystem */
+/*
+ * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2010 by On-Waves
+ * 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 <openbsc/gsm_data.h>
+#include <openbsc/osmo_bsc.h>
+#include <openbsc/osmo_bsc_grace.h>
+#include <openbsc/osmo_msc_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/signal.h>
+
+#include <osmocore/gsm0808.h>
+#include <osmocore/talloc.h>
+#include <osmocore/protocol/gsm_08_08.h>
+
+#include <osmocom/sccp/sccp.h>
+
+/* SCCP helper */
+#define SCCP_IT_TIMER 60
+
+static LLIST_HEAD(active_connections);
+
+static void free_queued(struct osmo_bsc_sccp_con *conn)
+{
+ struct msgb *msg;
+
+ while (!llist_empty(&conn->sccp_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&conn->sccp_queue);
+ msgb_free(msg);
+ }
+
+ conn->sccp_queue_size = 0;
+}
+
+static void send_queued(struct osmo_bsc_sccp_con *conn)
+{
+ struct msgb *msg;
+
+ while (!llist_empty(&conn->sccp_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&conn->sccp_queue);
+ sccp_connection_write(conn->sccp, msg);
+ msgb_free(msg);
+ conn->sccp_queue_size -= 1;
+ }
+}
+
+static void msc_outgoing_sccp_data(struct sccp_connection *conn,
+ struct msgb *msg, unsigned int len)
+{
+ struct osmo_bsc_sccp_con *bsc_con =
+ (struct osmo_bsc_sccp_con *) conn->data_ctx;
+
+ bsc_handle_dt1(bsc_con, msg, len);
+}
+
+static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
+{
+ struct osmo_bsc_sccp_con *con_data;
+
+ if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
+ con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
+ if(con_data->conn) {
+ LOGP(DMSC, LOGL_ERROR,
+ "ERROR: The lchan is still associated\n.");
+ gsm0808_clear(con_data->conn);
+ subscr_con_free(con_data->conn);
+ con_data->conn = NULL;
+ }
+
+ con_data->sccp = NULL;
+ free_queued(con_data);
+ sccp_connection_free(conn);
+ bsc_delete_connection(con_data);
+ } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
+ LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
+ con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
+
+ bsc_del_timer(&con_data->sccp_cc_timeout);
+ bsc_schedule_timer(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0);
+
+ send_queued(con_data);
+ }
+}
+
+static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data)
+{
+ if (data->conn) {
+ gsm0808_clear(data->conn);
+ subscr_con_free(data->conn);
+ data->conn = NULL;
+ }
+
+ free_queued(data);
+ sccp_connection_force_free(data->sccp);
+ data->sccp = NULL;
+ bsc_delete_connection(data);
+}
+
+static void sccp_it_timeout(void *_data)
+{
+ struct osmo_bsc_sccp_con *data =
+ (struct osmo_bsc_sccp_con *) _data;
+
+ sccp_connection_send_it(data->sccp);
+ bsc_schedule_timer(&data->sccp_it_timeout, SCCP_IT_TIMER, 0);
+}
+
+static void sccp_cc_timeout(void *_data)
+{
+ struct osmo_bsc_sccp_con *data =
+ (struct osmo_bsc_sccp_con *) _data;
+
+ if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED)
+ return;
+
+ LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n");
+ bsc_sccp_force_free(data);
+}
+
+static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *data)
+{
+ struct gsm_network *net = (struct gsm_network *) data;
+ msc_queue_write(net->msc_data->msc_con, msg, IPAC_PROTO_SCCP);
+}
+
+static int msc_sccp_accept(struct sccp_connection *connection, void *data)
+{
+ LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n");
+ return -1;
+}
+
+static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
+{
+ struct gsm_network *net = (struct gsm_network *) data;
+ return bsc_handle_udt(net, net->msc_data->msc_con, msgb, length);
+}
+
+int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
+{
+ struct sccp_connection *sccp = conn->sccp;
+
+ if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
+ msgb_free(msg);
+ } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
+ && conn->sccp_queue_size == 0) {
+ sccp_connection_write(sccp, msg);
+ msgb_free(msg);
+ } else if (conn->sccp_queue_size > 10) {
+ LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
+ msgb_free(msg);
+ } else {
+ LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size);
+ conn->sccp_queue_size += 1;
+ msgb_enqueue(&conn->sccp_queue, msg);
+ }
+
+ return 0;
+}
+
+int bsc_create_new_connection(struct gsm_subscriber_connection *conn)
+{
+ struct gsm_network *net;
+ struct osmo_bsc_sccp_con *bsc_con;
+ struct sccp_connection *sccp;
+
+ net = conn->bts->network;
+ if (!net->msc_data->msc_con->is_authenticated) {
+ LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n");
+ return -1;
+ }
+
+ if (!bsc_grace_allow_new_connection(net)) {
+ LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
+ return -1;
+ }
+
+ sccp = sccp_connection_socket();
+ if (!sccp) {
+ LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
+ return -ENOMEM;
+ }
+
+ bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
+ if (!bsc_con) {
+ LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
+ sccp_connection_free(sccp);
+ return -1;
+ }
+
+ /* callbacks */
+ sccp->state_cb = msc_outgoing_sccp_state;
+ sccp->data_cb = msc_outgoing_sccp_data;
+ sccp->data_ctx = bsc_con;
+
+ /* prepare the timers */
+ bsc_con->sccp_it_timeout.cb = sccp_it_timeout;
+ bsc_con->sccp_it_timeout.data = bsc_con;
+ bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout;
+ bsc_con->sccp_cc_timeout.data = bsc_con;
+
+ INIT_LLIST_HEAD(&bsc_con->sccp_queue);
+
+ bsc_con->sccp = sccp;
+ bsc_con->msc_con = net->msc_data->msc_con;
+ bsc_con->conn = conn;
+ llist_add(&bsc_con->entry, &active_connections);
+ conn->sccp_con = bsc_con;
+ return 0;
+}
+
+int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
+{
+ bsc_schedule_timer(&conn->sccp_cc_timeout, 10, 0);
+ sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg);
+ msgb_free(msg);
+ return 0;
+}
+
+int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp)
+{
+ if (!sccp)
+ return 0;
+
+ if (sccp->conn)
+ LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n");
+
+ llist_del(&sccp->entry);
+ bsc_del_timer(&sccp->sccp_it_timeout);
+ bsc_del_timer(&sccp->sccp_cc_timeout);
+ talloc_free(sccp);
+ return 0;
+}
+
+static void bsc_close_connections(struct bsc_msc_connection *msc_con)
+{
+ struct osmo_bsc_sccp_con *con, *tmp;
+
+ llist_for_each_entry_safe(con, tmp, &active_connections, entry)
+ bsc_sccp_force_free(con);
+}
+
+static int handle_msc_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct osmo_msc_data *data;
+
+ if (subsys != SS_MSC)
+ return 0;
+
+ data = (struct osmo_msc_data *) signal_data;
+ if (signal == S_MSC_LOST)
+ bsc_close_connections(data->msc_con);
+
+ return 0;
+}
+
+int osmo_bsc_sccp_init(struct gsm_network *gsmnet)
+{
+ sccp_set_log_area(DSCCP);
+ sccp_system_init(msc_sccp_write_ipa, gsmnet);
+ sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
+ sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet);
+
+ register_signal_handler(SS_MSC, handle_msc_signal, gsmnet);
+
+ return 0;
+}