aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/nat/bsc_nat.c88
-rw-r--r--openbsc/src/nat/bsc_nat_utils.c79
2 files changed, 153 insertions, 14 deletions
diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c
index 6485102df..738b7ef39 100644
--- a/openbsc/src/nat/bsc_nat.c
+++ b/openbsc/src/nat/bsc_nat.c
@@ -303,6 +303,63 @@ static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsig
bsc_write(bsc, msg, proto);
}
+/*
+ * Release an established connection. We will have to release it to the BSC
+ * and to the network and we do it the following way.
+ * 1.) Give up on the MSC side
+ * 1.1) Send a RLSD message, it is a bit non standard but should work, we
+ * ignore the RLC... we might complain about it. Other options would
+ * be to send a Release Request, handle the Release Complete..
+ * 1.2) Mark the data structure to be con_local and wait for 2nd
+ *
+ * 2.) Give up on the BSC side
+ * 2.1) Depending on the con type reject the service, or just close it
+ */
+static void bsc_send_con_release(struct bsc_connection *bsc, struct sccp_connections *con)
+{
+ struct msgb *rlsd;
+ /* 1. release the network */
+ rlsd = sccp_create_rlsd(&con->patched_ref, &con->remote_ref,
+ SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
+ if (!rlsd)
+ LOGP(DNAT, LOGL_ERROR, "Failed to create RLSD message.\n");
+ else {
+ ipaccess_prepend_header(rlsd, IPAC_PROTO_SCCP);
+ queue_for_msc(con->msc_con, rlsd);
+ }
+ con->con_local = 1;
+
+ /* 2. release the BSC side */
+ if (con->con_type == NAT_CON_TYPE_LU) {
+ struct msgb *payload, *udt;
+ payload = gsm48_create_loc_upd_rej(GSM48_REJECT_PLMN_NOT_ALLOWED);
+
+ if (payload) {
+ gsm0808_prepend_dtap_header(payload, 0);
+ udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len);
+ if (udt)
+ bsc_write(bsc, udt, IPAC_PROTO_SCCP);
+ else
+ LOGP(DNAT, LOGL_ERROR, "Failed to create DT1\n");
+
+ msgb_free(payload);
+ } else {
+ LOGP(DNAT, LOGL_ERROR, "Failed to allocate LU Reject.\n");
+ }
+ }
+
+ rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref,
+ SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
+ if (!rlsd) {
+ LOGP(DNAT, LOGL_ERROR, "Failed to allocate RLSD for the BSC.\n");
+ sccp_connection_destroy(con);
+ return;
+ }
+
+ con->con_type = NAT_CON_TYPE_LOCAL_REJECT;
+ bsc_write(bsc, rlsd, IPAC_PROTO_SCCP);
+}
+
static void bsc_send_con_refuse(struct bsc_connection *bsc,
struct bsc_nat_parsed *parsed, int con_type)
{
@@ -708,10 +765,12 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
/* modify the SCCP entries */
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
+ int filter;
struct sccp_connections *con;
switch (parsed->sccp_type) {
case SCCP_MSG_TYPE_CR:
- if (bsc_nat_filter_sccp_cr(bsc, msg, parsed, &con_type) != 0)
+ filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &con_type);
+ if (filter < 0)
goto exit3;
if (!create_sccp_src_ref(bsc, parsed))
goto exit2;
@@ -719,6 +778,7 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
con->msc_con = bsc->nat->msc_con;
con_msc = con->msc_con;
con->con_type = con_type;
+ con->imsi_checked = filter;
con_bsc = con->bsc;
break;
case SCCP_MSG_TYPE_RLSD:
@@ -728,9 +788,16 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
case SCCP_MSG_TYPE_IT:
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
if (con) {
- con_bsc = con->bsc;
- con_msc = con->msc_con;
- con_filter = con->con_local;
+ filter = bsc_nat_filter_dt(bsc, msg, con, parsed);
+ if (filter == 0) {
+ con_bsc = con->bsc;
+ con_msc = con->msc_con;
+ con_filter = con->con_local;
+ } else {
+ bsc_send_con_release(bsc, con);
+ con = NULL;
+ goto exit2;
+ }
}
break;
case SCCP_MSG_TYPE_RLC:
@@ -766,15 +833,18 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
goto exit2;
}
- if (!con_msc) {
- LOGP(DNAT, LOGL_ERROR, "No connection found, dropping data.\n");
- goto exit2;
- }
-
/* do not forward messages to the MSC */
if (con_filter)
goto exit2;
+ if (!con_msc) {
+ LOGP(DNAT, LOGL_ERROR, "Not forwarding data bsc_nr: %d ipa: %d type: 0x%x\n",
+ bsc->cfg->nr,
+ parsed ? parsed->ipa_proto : -1,
+ parsed ? parsed->sccp_type : -1);
+ goto exit2;
+ }
+
/* send the non-filtered but maybe modified msg */
queue_for_msc(con_msc, msg);
talloc_free(parsed);
diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c
index 058251368..3933f1bf4 100644
--- a/openbsc/src/nat/bsc_nat_utils.c
+++ b/openbsc/src/nat/bsc_nat_utils.c
@@ -271,7 +271,7 @@ static int auth_imsi(struct bsc_connection *bsc, const char *mi_string)
/* 2. BSC allow */
if (lst_check_allow(bsc_lst, mi_string) == 0)
- return 0;
+ return 1;
}
/* 3. NAT deny */
@@ -283,7 +283,7 @@ static int auth_imsi(struct bsc_connection *bsc, const char *mi_string)
}
}
- return 0;
+ return 1;
}
static int _cr_check_loc_upd(struct bsc_connection *bsc, uint8_t *data, unsigned int length)
@@ -369,6 +369,35 @@ static int _cr_check_pag_resp(struct bsc_connection *bsc, uint8_t *data, unsigne
return auth_imsi(bsc, mi_string);
}
+static int _dt_check_id_resp(struct bsc_connection *bsc,
+ uint8_t *data, unsigned int length,
+ struct sccp_connections *con)
+{
+ char mi_string[GSM48_MI_SIZE];
+ uint8_t mi_type;
+ int ret;
+
+ if (length < 2) {
+ LOGP(DNAT, LOGL_ERROR, "mi does not fit.\n");
+ return -1;
+ }
+
+ if (data[0] < length - 1) {
+ LOGP(DNAT, LOGL_ERROR, "mi length too big.\n");
+ return -2;
+ }
+
+ mi_type = data[1] & GSM_MI_TYPE_MASK;
+ gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]);
+
+ if (mi_type != GSM_MI_TYPE_IMSI)
+ return 0;
+
+ ret = auth_imsi(bsc, mi_string);
+ con->imsi_checked = 1;
+ return ret;
+}
+
/* Filter out CR data... */
int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed, int *con_type)
{
@@ -376,6 +405,7 @@ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct
struct gsm48_hdr *hdr48;
int hdr48_len;
int len;
+ uint8_t msg_type;
*con_type = NAT_CON_TYPE_NONE;
@@ -412,16 +442,17 @@ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct
hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION);
+ msg_type = hdr48->msg_type & 0xbf;
if (hdr48->proto_discr == GSM48_PDISC_MM &&
- hdr48->msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) {
+ msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) {
*con_type = NAT_CON_TYPE_LU;
return _cr_check_loc_upd(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else if (hdr48->proto_discr == GSM48_PDISC_MM &&
- hdr48->msg_type == GSM48_MT_MM_CM_SERV_REQ) {
+ msg_type == GSM48_MT_MM_CM_SERV_REQ) {
*con_type = NAT_CON_TYPE_CM_SERV_REQ;
return _cr_check_cm_serv_req(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else if (hdr48->proto_discr == GSM48_PDISC_RR &&
- hdr48->msg_type == GSM48_MT_RR_PAG_RESP) {
+ msg_type == GSM48_MT_RR_PAG_RESP) {
*con_type = NAT_CON_TYPE_PAG_RESP;
return _cr_check_pag_resp(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48));
} else {
@@ -431,6 +462,44 @@ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct
}
}
+int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
+ struct sccp_connections *con, struct bsc_nat_parsed *parsed)
+{
+ uint32_t len;
+ uint8_t msg_type;
+ struct gsm48_hdr *hdr48;
+
+ if (con->imsi_checked)
+ return 0;
+
+ /* only care about DTAP messages */
+ if (parsed->bssap != BSSAP_MSG_DTAP)
+ return 0;
+
+ /* gsm_type is actually the size of the dtap */
+ len = parsed->gsm_type;
+ if (len < msgb_l3len(msg) - 3) {
+ LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n");
+ return -1;
+ }
+
+ if (len < sizeof(*hdr48)) {
+ LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n");
+ return -1;
+ }
+
+ msg->l4h = &msg->l3h[3];
+ hdr48 = (struct gsm48_hdr *) msg->l4h;
+
+ msg_type = hdr48->msg_type & 0xbf;
+ if (hdr48->proto_discr == GSM48_PDISC_MM &&
+ msg_type == GSM48_MT_MM_ID_RESP) {
+ return _dt_check_id_resp(bsc, &hdr48->data[0], len - sizeof(*hdr48), con);
+ } else {
+ return 0;
+ }
+}
+
void bsc_parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv)
{
if (*imsi) {