aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/bssap.h8
-rw-r--r--openbsc/include/openbsc/gsm_data.h7
-rw-r--r--openbsc/src/bsc_msc_ip.c21
-rw-r--r--openbsc/src/bssap.c173
4 files changed, 195 insertions, 14 deletions
diff --git a/openbsc/include/openbsc/bssap.h b/openbsc/include/openbsc/bssap.h
index 1db026525..d6553409a 100644
--- a/openbsc/include/openbsc/bssap.h
+++ b/openbsc/include/openbsc/bssap.h
@@ -278,4 +278,12 @@ struct msgb *bssmap_create_sapi_reject(u_int8_t link_id);
int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length);
struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id);
+void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg);
+void bsc_free_queued(struct sccp_connection *conn);
+void bsc_send_queued(struct sccp_connection *conn);
+
+void bts_queue_send(struct msgb *msg, int link_id);
+void bts_send_queued(struct bss_sccp_connection_data*);
+void bts_free_queued(struct bss_sccp_connection_data*);
+
#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 8a9446a5e..d01973e4b 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -121,6 +121,13 @@ struct bss_sccp_connection_data {
struct gsm_lchan *lchan;
struct sccp_connection *sccp;
int ciphering_handled : 1;
+
+ /* Queue SCCP and GSM0408 messages */
+ struct llist_head gsm_queue;
+ unsigned int gsm_queue_size;
+
+ struct llist_head sccp_queue;
+ unsigned int sccp_queue_size;
};
#define sccp_get_lchan(data_ctx) ((struct bss_sccp_connection_data *)data_ctx)->lchan
diff --git a/openbsc/src/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c
index 9b726f89d..f3c85904e 100644
--- a/openbsc/src/bsc_msc_ip.c
+++ b/openbsc/src/bsc_msc_ip.c
@@ -59,13 +59,23 @@ extern int bsc_shutdown_net(struct gsm_network *net);
struct bss_sccp_connection_data *bss_sccp_create_data()
{
- return _talloc_zero(tall_bsc_ctx,
+ struct bss_sccp_connection_data *data;
+
+ data = _talloc_zero(tall_bsc_ctx,
sizeof(struct bss_sccp_connection_data),
"bsc<->msc");
+ if (!data)
+ return NULL;
+
+ INIT_LLIST_HEAD(&data->sccp_queue);
+ INIT_LLIST_HEAD(&data->gsm_queue);
+ return data;
}
void bss_sccp_free_data(struct bss_sccp_connection_data *data)
{
+ bsc_free_queued(data->sccp);
+ bts_free_queued(data);
talloc_free(data);
}
@@ -138,6 +148,8 @@ void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
bss_sccp_free_data((struct bss_sccp_connection_data *)conn->data_ctx);
sccp_connection_free(conn);
return;
+ } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
+ bsc_send_queued(conn);
}
}
@@ -204,7 +216,7 @@ static int send_dtap_or_open_connection(struct msgb *msg)
return -1;
}
- sccp_connection_write(lchan_get_sccp(msg->lchan), dtap);
+ bsc_queue_connection_write(lchan_get_sccp(msg->lchan), dtap);
return 1;
} else {
return open_sccp_connection(msg);
@@ -251,8 +263,7 @@ static int handle_cipher_m_complete(struct msgb *msg)
/* handled this message */
- sccp_connection_write(lchan_get_sccp(msg->lchan), resp);
- msgb_free(resp);
+ bsc_queue_connection_write(lchan_get_sccp(msg->lchan), resp);
return 1;
}
@@ -328,7 +339,7 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
return -1;
}
- sccp_connection_write(lchan_get_sccp(msg->lchan), dtap);
+ bsc_queue_connection_write(lchan_get_sccp(msg->lchan), dtap);
}
return rc;
diff --git a/openbsc/src/bssap.c b/openbsc/src/bssap.c
index 061d6cd50..9ecc28bfd 100644
--- a/openbsc/src/bssap.c
+++ b/openbsc/src/bssap.c
@@ -20,6 +20,7 @@
*/
#include <openbsc/bssap.h>
+#include <openbsc/bsc_rll.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
@@ -162,8 +163,7 @@ static int bssmap_handle_clear_command(struct sccp_connection *conn,
return -1;
}
- sccp_connection_write(conn, resp);
- msgb_free(resp);
+ bsc_queue_connection_write(conn, resp);
return 0;
}
@@ -205,8 +205,7 @@ reject:
return -1;
}
- sccp_connection_write(conn, resp);
- msgb_free(resp);
+ bsc_queue_connection_write(conn, resp);
return -1;
}
@@ -260,7 +259,6 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
struct dtap_header *header;
struct msgb *gsm48;
u_int8_t *data;
- int ret = 0;
if (!lchan) {
DEBUGP(DMSC, "No lchan available\n");
@@ -294,9 +292,9 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
gsm48->l3h = gsm48->data;
data = msgb_put(gsm48, length - sizeof(*header));
memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header));
- ret = rsl_data_request(gsm48, header->link_id);
- return ret;
+ bts_queue_send(gsm48, header->link_id);
+ return 0;
}
/* Create messages */
@@ -505,12 +503,169 @@ static int bssap_handle_lchan_signal(unsigned int subsys, unsigned int signal,
msg->l3h[5] = GSM0808_CAUSE_RADIO_INTERFACE_FAILURE;
DEBUGP(DMSC, "Sending clear request on unexpected channel release.\n");
- sccp_connection_write(conn, msg);
- msgb_free(msg);
+ bsc_queue_connection_write(conn, msg);
return 0;
}
+/*
+ * queue handling for BSS AP
+ */
+void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg)
+{
+ struct bss_sccp_connection_data *data;
+
+ data = (struct bss_sccp_connection_data *)conn->data_ctx;
+
+ if (conn->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
+ DEBUGP(DMSC, "Connection closing, dropping packet on: %p\n", conn);
+ msgb_free(msg);
+ } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
+ && data->sccp_queue_size == 0) {
+ sccp_connection_write(conn, msg);
+ msgb_free(msg);
+ } else if (data->sccp_queue_size > 10) {
+ DEBUGP(DMSC, "Dropping packet on %p due queue overflow\n", conn);
+ msgb_free(msg);
+ } else {
+ DEBUGP(DMSC, "Queuing packet on %p. Queue size: %d\n", conn, data->sccp_queue_size);
+ ++data->sccp_queue_size;
+ msgb_enqueue(&data->sccp_queue, msg);
+ }
+}
+
+void bsc_free_queued(struct sccp_connection *conn)
+{
+ struct bss_sccp_connection_data *data;
+ struct msgb *msg;
+
+ data = (struct bss_sccp_connection_data *)conn->data_ctx;
+ while (!llist_empty(&data->sccp_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&data->sccp_queue);
+ msgb_free(msg);
+ }
+
+ data->sccp_queue_size = 0;
+}
+
+void bsc_send_queued(struct sccp_connection *conn)
+{
+ struct bss_sccp_connection_data *data;
+ struct msgb *msg;
+
+ data = (struct bss_sccp_connection_data *)conn->data_ctx;
+
+ DEBUGP(DMSC, "Sending queued items\n");
+ while (!llist_empty(&data->sccp_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&data->sccp_queue);
+ sccp_connection_write(conn, msg);
+ msgb_free(msg);
+ --data->sccp_queue_size;
+ }
+}
+
+/* RLL callback */
+static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
+ void *_data, enum bsc_rllr_ind rllr_ind)
+{
+ struct sccp_source_reference ref = sccp_src_ref_from_int((u_int32_t) _data);
+ struct bss_sccp_connection_data *data = lchan->msc_data;
+
+ if (!data || !data->sccp) {
+ DEBUGP(DMSC, "Time-out/Establish after sccp release? Ind: %d lchan: %p\n",
+ rllr_ind, lchan);
+ return;
+ }
+
+ if (memcmp(&data->sccp->source_local_reference, &ref, sizeof(ref)) != 0) {
+ DEBUGP(DMSC, "Wrong SCCP connection. Not handling RLL callback: %u %u\n",
+ sccp_src_ref_to_int(&ref),
+ sccp_src_ref_to_int(&data->sccp->source_local_reference));
+ return;
+ }
+
+ switch (rllr_ind) {
+ case BSC_RLLR_IND_EST_CONF:
+ /* nothing to do */
+ bts_send_queued(data);
+ break;
+ case BSC_RLLR_IND_REL_IND:
+ case BSC_RLLR_IND_ERR_IND:
+ case BSC_RLLR_IND_TIMEOUT: {
+ /* reject queued messages */
+ struct msgb *sapi_reject;
+
+ bts_free_queued(data);
+ sapi_reject = bssmap_create_sapi_reject(link_id);
+ if (!sapi_reject){
+ DEBUGP(DMSC, "Failed to create SAPI reject\n");
+ return;
+ }
+
+ bsc_queue_connection_write(data->sccp, sapi_reject);
+ break;
+ }
+ }
+}
+
+/* decide if we need to queue because of SAPI != 0 */
+void bts_queue_send(struct msgb *msg, int link_id)
+{
+ struct bss_sccp_connection_data *data = msg->lchan->msc_data;
+
+ if (data->gsm_queue_size == 0) {
+ if (link_id == 0) {
+ rsl_data_request(msg, link_id);
+ } else {
+ msg->smsh = (unsigned char*) link_id;
+ msgb_enqueue(&data->gsm_queue, msg);
+ ++data->gsm_queue_size;
+
+ /* establish link */
+ rll_establish(msg->lchan, link_id & 0x7,
+ rll_ind_cb,
+ (void *)sccp_src_ref_to_int(&data->sccp->source_local_reference));
+ }
+ } else if (data->gsm_queue_size == 10) {
+ DEBUGP(DMSC, "Queue full on %p. Dropping GSM0408.\n", data->sccp);
+ } else {
+ DEBUGP(DMSC, "Queueing GSM0408 message on %p. Queue size: %d\n",
+ data->sccp, data->gsm_queue_size);
+
+ msg->smsh = (unsigned char*) link_id;
+ msgb_enqueue(&data->gsm_queue, msg);
+ ++data->gsm_queue_size;
+ }
+}
+
+void bts_free_queued(struct bss_sccp_connection_data *data)
+{
+ struct msgb *msg;
+
+ while (!llist_empty(&data->gsm_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&data->gsm_queue);
+ msgb_free(msg);
+ }
+
+ data->gsm_queue_size = 0;
+}
+
+void bts_send_queued(struct bss_sccp_connection_data *data)
+{
+ struct msgb *msg;
+
+ while (!llist_empty(&data->gsm_queue)) {
+ /* this is not allowed to fail */
+ msg = msgb_dequeue(&data->gsm_queue);
+ rsl_data_request(msg, (int) msg->smsh);
+ }
+
+ data->gsm_queue_size = 0;
+}
+
static __attribute__((constructor)) void on_dso_load_bssap(void)
{
register_signal_handler(SS_LCHAN, bssap_handle_lchan_signal, NULL);