diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-09-15 19:00:53 +0800 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-09-15 19:00:53 +0800 |
commit | a594342f71e835d0316a3ee655a6c1a58679e1b4 (patch) | |
tree | 3b2eb213c518ea3434c5f7f60a46246fcce1e79f | |
parent | 5f54075a8be6e041c8fbf56ff6281fc78a6d7929 (diff) | |
parent | 09ecda49d7e12412d50e85109e3f3dc81d76927f (diff) |
Merge branch 'zecke/imsi-filter'
-rw-r--r-- | openbsc/include/openbsc/bsc_nat.h | 2 | ||||
-rw-r--r-- | openbsc/include/openbsc/bsc_nat_sccp.h | 1 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_nat.c | 88 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_nat_utils.c | 79 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/bsc_data.c | 8 | ||||
-rw-r--r-- | openbsc/tests/bsc-nat/bsc_nat_test.c | 64 |
6 files changed, 223 insertions, 19 deletions
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 0c9af9184..6b2a09940 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -243,6 +243,8 @@ struct bsc_connection *bsc_nat_find_bsc(struct bsc_nat *nat, struct msgb *msg, i */ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *, int *con_type); +int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, + struct sccp_connections *con, struct bsc_nat_parsed *parsed); /** * SCCP patching and handling diff --git a/openbsc/include/openbsc/bsc_nat_sccp.h b/openbsc/include/openbsc/bsc_nat_sccp.h index 4f1afcb2b..c4f756e3d 100644 --- a/openbsc/include/openbsc/bsc_nat_sccp.h +++ b/openbsc/include/openbsc/bsc_nat_sccp.h @@ -78,6 +78,7 @@ struct sccp_connections { /* status */ int con_type; int con_local; + int imsi_checked; /* * audio handling. Remember if we have ever send a CRCX, 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) { diff --git a/openbsc/tests/bsc-nat/bsc_data.c b/openbsc/tests/bsc-nat/bsc_data.c index a3239799c..1bc15c873 100644 --- a/openbsc/tests/bsc-nat/bsc_data.c +++ b/openbsc/tests/bsc-nat/bsc_data.c @@ -88,6 +88,14 @@ static const uint8_t ass_cmd[] = { 0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00, 0x15 }; +/* identity response */ +static const uint8_t id_resp[] = { +0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc, +0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59, +0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66, +0x31 +}; + /* * MGCP messages */ diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c index 8f4e2d152..926c97cd7 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ b/openbsc/tests/bsc-nat/bsc_nat_test.c @@ -31,6 +31,7 @@ #include <osmocore/talloc.h> #include <osmocom/sccp/sccp.h> +#include <osmocore/protocol/gsm_08_08.h> #include <stdio.h> @@ -582,19 +583,19 @@ static struct cr_filter cr_filter[] = { { .data = bssmap_cr, .length = sizeof(bssmap_cr), - .result = 0, + .result = 1, .contype = NAT_CON_TYPE_CM_SERV_REQ, }, { .data = bss_lu, .length = sizeof(bss_lu), - .result = 0, + .result = 1, .contype = NAT_CON_TYPE_LU, }, { .data = pag_resp, .length = sizeof(pag_resp), - .result = 0, + .result = 1, .contype = NAT_CON_TYPE_PAG_RESP, }, { @@ -609,7 +610,7 @@ static struct cr_filter cr_filter[] = { /* BSC allow is before NAT deny */ .data = bss_lu, .length = sizeof(bss_lu), - .result = 0, + .result = 1, .nat_imsi_deny = "[0-9]*", .bsc_imsi_allow = "2440[0-9]*", .contype = NAT_CON_TYPE_LU, @@ -618,7 +619,7 @@ static struct cr_filter cr_filter[] = { /* BSC allow is before NAT deny */ .data = bss_lu, .length = sizeof(bss_lu), - .result = 0, + .result = 1, .bsc_imsi_allow = "[0-9]*", .nat_imsi_deny = "[0-9]*", .contype = NAT_CON_TYPE_LU, @@ -696,6 +697,58 @@ static void test_cr_filter() msgb_free(msg); } +static void test_dt_filter() +{ + int i; + struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); + struct bsc_nat_parsed *parsed; + + struct bsc_nat *nat = bsc_nat_alloc(); + struct bsc_connection *bsc = bsc_connection_alloc(nat); + struct sccp_connections *con = talloc_zero(0, struct sccp_connections); + + bsc->cfg = bsc_config_alloc(nat, "foo", 23); + con->bsc = bsc; + + msgb_reset(msg); + copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); + + parsed = bsc_nat_parse(msg); + if (!parsed) { + fprintf(stderr, "FAIL: Could not parse ID resp\n"); + abort(); + } + + if (parsed->bssap != BSSAP_MSG_DTAP) { + fprintf(stderr, "FAIL: It should be dtap\n"); + abort(); + } + + /* gsm_type is actually the size of the dtap */ + if (parsed->gsm_type < msgb_l3len(msg) - 3) { + fprintf(stderr, "FAIL: Not enough space for the content\n"); + abort(); + } + + if (bsc_nat_filter_dt(bsc, msg, con, parsed) != 1) { + fprintf(stderr, "FAIL: Should have passed..\n"); + abort(); + } + + /* just some basic length checking... */ + for (i = ARRAY_SIZE(id_resp); i >= 0; --i) { + msgb_reset(msg); + copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); + + parsed = bsc_nat_parse(msg); + if (!parsed) + continue; + + con->imsi_checked = 0; + bsc_nat_filter_dt(bsc, msg, con, parsed); + } +} + int main(int argc, char **argv) { struct log_target *stderr_target; @@ -714,6 +767,7 @@ int main(int argc, char **argv) test_mgcp_rewrite(); test_mgcp_parse(); test_cr_filter(); + test_dt_filter(); return 0; } |