From 76424392e08fcea56e665295830a9fdcc6dba5b5 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Thu, 21 Oct 2010 10:59:54 +0200 Subject: nat: Add vty option for number rewriting Parse a msg file in case we do have a list. --- openbsc/include/openbsc/bsc_nat.h | 5 +++++ openbsc/src/nat/bsc_nat_vty.c | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index acecba7de..083746f68 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -237,6 +238,10 @@ struct bsc_nat { /* filter */ char *acc_lst_name; + /* number rewriting */ + char *num_rewr_name; + struct msg_entries *num_rewr; + /* USSD messages we want to match */ char *ussd_lst_name; char *ussd_query; diff --git a/openbsc/src/nat/bsc_nat_vty.c b/openbsc/src/nat/bsc_nat_vty.c index 9eb8ebc75..af7e94c67 100644 --- a/openbsc/src/nat/bsc_nat_vty.c +++ b/openbsc/src/nat/bsc_nat_vty.c @@ -87,6 +87,9 @@ static int config_write_nat(struct vty *vty) if (_nat->ussd_local) vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); + if (_nat->num_rewr_name) + vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); + llist_for_each_entry(lst, &_nat->access_lists, list) { write_acc_lst(vty, lst); } @@ -403,6 +406,25 @@ DEFUN(cfg_nat_acc_lst_name, return CMD_SUCCESS; } +DEFUN(cfg_nat_number_rewrite, + cfg_nat_number_rewrite_cmd, + "number-rewrite FILENAME", + "Set the file with rewriting rules.\n" "Filename") +{ + bsc_replace_string(_nat, &_nat->num_rewr_name, argv[0]); + if (_nat->num_rewr_name) { + if (_nat->num_rewr) + talloc_free(_nat->num_rewr); + _nat->num_rewr = msg_entry_parse(_nat, _nat->num_rewr_name); + return _nat->num_rewr == NULL ? CMD_WARNING : CMD_SUCCESS; + } else { + if (_nat->num_rewr) + talloc_free(_nat->num_rewr); + _nat->num_rewr = NULL; + return CMD_SUCCESS; + } +} + DEFUN(cfg_nat_ussd_lst_name, cfg_nat_ussd_lst_name_cmd, "ussd-list-name NAME", @@ -688,6 +710,9 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_element(NAT_NODE, &cfg_lst_imsi_deny_cmd); install_element(NAT_NODE, &cfg_lst_no_cmd); + /* number rewriting */ + install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd); + /* BSC subgroups */ install_element(NAT_NODE, &cfg_bsc_cmd); install_node(&bsc_node, config_write_bsc); -- cgit v1.2.3 From a914daf174e9d1a78bdccb1d5bd8b69ec8ff2280 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Thu, 21 Oct 2010 12:12:57 +0200 Subject: nat: Add hook for rewriting a setup message Create a new function, hand the data to this function, take back a possible modified msgb and invalidate parsed at this point. --- openbsc/include/openbsc/bsc_nat.h | 2 ++ openbsc/src/nat/bsc_nat.c | 12 +++++++++++- openbsc/src/nat/bsc_nat_utils.c | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 083746f68..049421716 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -343,4 +343,6 @@ struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *ms int bsc_ussd_init(struct bsc_nat *nat); int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, struct msgb *msg); +struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *); + #endif diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index daabf0976..dcacfb9ab 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -858,6 +858,15 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) /* hand data to a side channel */ if (bsc_check_ussd(con, parsed, msg) == 1) con->con_local = 2; + + /* + * Optionally rewrite setup message. This can + * replace the msg and the parsed structure becomes + * invalid. + */ + msg = bsc_nat_rewrite_setup(bsc->nat, msg, parsed); + talloc_free(parsed); + parsed = NULL; } con_bsc = con->bsc; @@ -913,7 +922,8 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) /* send the non-filtered but maybe modified msg */ queue_for_msc(con_msc, msg); - talloc_free(parsed); + if (parsed) + talloc_free(parsed); return 0; exit: diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index 0cef01b24..a3fd97aaf 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -721,3 +721,10 @@ int bsc_write_cb(struct bsc_fd *bfd, struct msgb *msg) return rc; } +/** + * Rewrite non global numbers... according to rules based on the IMSI + */ +struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *pa) +{ + return msg; +} -- cgit v1.2.3 From 73bbf8924588f5dc9088ac34ac3da2747b18f9dc Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Thu, 21 Oct 2010 14:46:57 +0200 Subject: nat: Implement rewriting, have a very basic test for that feature --- openbsc/include/openbsc/bsc_nat.h | 2 +- openbsc/src/nat/bsc_nat.c | 2 +- openbsc/src/nat/bsc_nat_utils.c | 143 ++++++++++++++++++++++++++++++++++- openbsc/tests/bsc-nat/bsc_data.c | 25 ++++++ openbsc/tests/bsc-nat/bsc_nat_test.c | 82 ++++++++++++++++++++ 5 files changed, 250 insertions(+), 4 deletions(-) diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 049421716..9f26c87a0 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -343,6 +343,6 @@ struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *ms int bsc_ussd_init(struct bsc_nat *nat); int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, struct msgb *msg); -struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *); +struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi); #endif diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index dcacfb9ab..0a5851577 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -864,7 +864,7 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) * replace the msg and the parsed structure becomes * invalid. */ - msg = bsc_nat_rewrite_setup(bsc->nat, msg, parsed); + msg = bsc_nat_rewrite_setup(bsc->nat, msg, parsed, con->imsi); talloc_free(parsed); parsed = NULL; } diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index a3fd97aaf..59c92a38e 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -724,7 +724,146 @@ int bsc_write_cb(struct bsc_fd *bfd, struct msgb *msg) /** * Rewrite non global numbers... according to rules based on the IMSI */ -struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *pa) +struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) { - return msg; + struct tlv_parsed tp; + struct gsm48_hdr *hdr48; + uint32_t len; + uint8_t msg_type; + unsigned int payload_len; + struct gsm_mncc_number called; + struct msg_entry *entry; + char *new_number = NULL; + struct msgb *out, *sccp; + uint8_t *outptr; + const uint8_t *msgptr; + int sec_len; + + if (!imsi || strlen(imsi) < 5) + return msg; + + if (!nat->num_rewr) + return msg; + + /* only care about DTAP messages */ + if (parsed->bssap != BSSAP_MSG_DTAP) + return msg; + if (!parsed->dest_local_ref) + return msg; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return msg; + + msg_type = hdr48->msg_type & 0xbf; + if (hdr48->proto_discr != GSM48_PDISC_CC || + msg_type != GSM48_MT_CC_SETUP) + return msg; + + /* decode and rewrite the message */ + payload_len = len - sizeof(*hdr48); + tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); + + /* no number, well let us ignore it */ + if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) + return msg; + + memset(&called, 0, sizeof(called)); + gsm48_decode_called(&called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); + + /* check if it looks international and stop */ + if (strncmp(called.number, "+", 1) == 0) + return msg; + if (strncmp(called.number, "00", 2) == 0) + return msg; + + /* need to find a replacement and then fix it */ + llist_for_each_entry(entry, &nat->num_rewr->entry, list) { + regex_t reg; + regmatch_t matches[2]; + + if (strncmp(entry->mcc, imsi, 3) != 0) + continue; + if (strncmp(entry->mnc, imsi + 3, 2) != 0) + continue; + + /* We have an entry for the IMSI. Need to match now */ + if (regcomp(®, entry->option, REG_EXTENDED) != 0) { + LOGP(DNAT, LOGL_ERROR, + "Regexp '%s' is not valid.\n", entry->option); + continue; + } + + /* this regexp matches... */ + if (regexec(®, called.number, 2, matches, 0) == 0 && + matches[1].rm_eo != -1) + new_number = talloc_asprintf(msg, "%s%s", + entry->text, + &called.number[matches[1].rm_so]); + regfree(®); + + if (new_number) + break; + } + + if (!new_number) { + LOGP(DNAT, LOGL_DEBUG, "No IMSI match found, returning message.\n"); + return msg; + } + + /* + * Need to create a new message now based on the old onew + * with a new number. We can sadly not patch this in place + * so we will need to regenerate it. + */ + + out = msgb_alloc_headroom(4096, 128, "changed-setup"); + if (!out) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + talloc_free(new_number); + return msg; + } + + /* copy the header */ + outptr = msgb_put(out, sizeof(*hdr48)); + memcpy(outptr, hdr48, sizeof(*hdr48)); + + /* copy everything up to the number */ + sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; + outptr = msgb_put(out, sec_len); + memcpy(outptr, &hdr48->data[0], sec_len); + + /* create the new number */ + strncpy(called.number, new_number, sizeof(called.number)); + gsm48_encode_called(out, &called); + + /* copy thre rest */ + msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + + TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); + sec_len = payload_len - (msgptr - &hdr48->data[0]); + outptr = msgb_put(out, sec_len); + memcpy(outptr, msgptr, sec_len); + + /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ + gsm0808_prepend_dtap_header(out, 0); + sccp = sccp_create_dt1(parsed->dest_local_ref, out->data, out->len); + if (!sccp) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); + talloc_free(new_number); + talloc_free(out); + return msg; + } + + ipaccess_prepend_header(sccp, IPAC_PROTO_SCCP); + + /* give up memory, we are done */ + talloc_free(new_number); + /* the parsed hangs off from msg but it needs to survive */ + talloc_steal(sccp, parsed); + msgb_free(msg); + msgb_free(out); + out = NULL; + return sccp; } + diff --git a/openbsc/tests/bsc-nat/bsc_data.c b/openbsc/tests/bsc-nat/bsc_data.c index f4dba0a3c..3cc22af7e 100644 --- a/openbsc/tests/bsc-nat/bsc_data.c +++ b/openbsc/tests/bsc-nat/bsc_data.c @@ -160,3 +160,28 @@ static const struct mgcp_patch_test mgcp_messages[] = { .port = 5555, }, }; + +/* CC Setup messages */ +static const uint8_t cc_setup_national[] = { + 0x00, 0x20, 0xfd, 0x06, 0x01, 0x12, + 0x6d, 0x00, 0x01, 0x19, 0x01, 0x00, 0x16, 0x03, + 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x06, 0x81, 0x10, 0x27, 0x33, 0x63, + 0x66, 0x15, 0x02, 0x11, 0x01 +}; + +static const uint8_t cc_setup_national_patched[] = { + 0x00, 0x22, 0xfd, 0x06, 0x01, 0x12, + 0x6d, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, + 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x32, + 0x33, 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 +}; + +static const uint8_t cc_setup_international[] = { + 0x00, 0x22, 0xfd, 0x06, 0x01, 0x13, + 0xe7, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, + 0x45, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, + 0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x33, + 0x63, 0x66, 0x03, 0x15, 0x02, 0x11, 0x01 +}; diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c index 141775c7a..7a2557fe6 100644 --- a/openbsc/tests/bsc-nat/bsc_nat_test.c +++ b/openbsc/tests/bsc-nat/bsc_nat_test.c @@ -799,6 +799,87 @@ static void test_dt_filter() } } +static void test_setup_rewrite() +{ + struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); + struct msgb *out; + struct bsc_nat_parsed *parsed; + const char *imsi = "27408000001234"; + + struct bsc_nat *nat = bsc_nat_alloc(); + + /* a fake list */ + struct msg_entries entries; + struct msg_entry entry; + + INIT_LLIST_HEAD(&entries.entry); + entry.mcc = "274"; + entry.mnc = "08"; + entry.option = "^0([1-9])"; + entry.text = "0049"; + llist_add_tail(&entry.list, &entries.entry); + nat->num_rewr = &entries; + + /* verify that nothing changed */ + msgb_reset(msg); + copy_to_msg(msg, cc_setup_international, ARRAY_SIZE(cc_setup_international)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + fprintf(stderr, "FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_setup(nat, msg, parsed, imsi); + if (msg != out) { + fprintf(stderr, "FAIL: The message should not have been changed\n"); + abort(); + } + + if (out->len != ARRAY_SIZE(cc_setup_international)) { + fprintf(stderr, "FAIL: Length of message changed\n"); + abort(); + } + + if (memcmp(out->data, cc_setup_international, out->len) != 0) { + fprintf(stderr, "FAIL: Content modified..\n"); + abort(); + } + talloc_free(parsed); + + /* verify that something in the message changes */ + msgb_reset(msg); + copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); + parsed = bsc_nat_parse(msg); + if (!parsed) { + fprintf(stderr, "FAIL: Could not parse ID resp\n"); + abort(); + } + + out = bsc_nat_rewrite_setup(nat, msg, parsed, imsi); + if (!out) { + fprintf(stderr, "FAIL: A new message should be created.\n"); + abort(); + } + + if (msg == out) { + fprintf(stderr, "FAIL: The message should have changed\n"); + abort(); + } + + if (out->len != ARRAY_SIZE(cc_setup_national_patched)) { + fprintf(stderr, "FAIL: Length is wrong.\n"); + abort(); + } + + if (memcmp(cc_setup_national_patched, out->data, out->len) != 0) { + fprintf(stderr, "FAIL: Data is wrong.\n"); + fprintf(stderr, "Data was: %s\n", hexdump(out->data, out->len)); + abort(); + } + + msgb_free(out); +} + int main(int argc, char **argv) { struct log_target *stderr_target; @@ -818,6 +899,7 @@ int main(int argc, char **argv) test_mgcp_parse(); test_cr_filter(); test_dt_filter(); + test_setup_rewrite(); return 0; } -- cgit v1.2.3 From 25adfceb3a40661d8a0842ade18439bf622c3987 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 27 Oct 2010 10:11:48 +0200 Subject: nat: Add '*' as wildcard for the IMSI matching. In case any rule has a '*' we will always match this rule. --- openbsc/src/nat/bsc_nat_utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index 59c92a38e..e1f1ddcfe 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -783,9 +783,9 @@ struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct regex_t reg; regmatch_t matches[2]; - if (strncmp(entry->mcc, imsi, 3) != 0) + if (entry->mcc[0] == '*' || strncmp(entry->mcc, imsi, 3) != 0) continue; - if (strncmp(entry->mnc, imsi + 3, 2) != 0) + if (entry->mnc[0] == '*' || strncmp(entry->mnc, imsi + 3, 2) != 0) continue; /* We have an entry for the IMSI. Need to match now */ -- cgit v1.2.3 From 1400310f05dd83413fd3a7b9ec27f0c5ee1e0a92 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 27 Oct 2010 10:24:12 +0200 Subject: nat: Explain that we do not want to have a + in the replacement rule --- openbsc/src/nat/bsc_nat_utils.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index e1f1ddcfe..abcb2717d 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -788,6 +788,12 @@ struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct if (entry->mnc[0] == '*' || strncmp(entry->mnc, imsi + 3, 2) != 0) continue; + if (entry->text[0] == '+') { + LOGP(DNAT, LOGL_ERROR, + "Plus is not allowed in the number"); + continue; + } + /* We have an entry for the IMSI. Need to match now */ if (regcomp(®, entry->option, REG_EXTENDED) != 0) { LOGP(DNAT, LOGL_ERROR, -- cgit v1.2.3 From 643931db3d235005d8e1fb818275cc322c002692 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 27 Oct 2010 10:24:34 +0200 Subject: nat: Look at the number type to figure out if we want to rewrite it --- openbsc/src/nat/bsc_nat_utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index abcb2717d..b345a77f5 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -773,7 +773,9 @@ struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); /* check if it looks international and stop */ - if (strncmp(called.number, "+", 1) == 0) + if (called.plan != 1) + return msg; + if (called.type == 1) return msg; if (strncmp(called.number, "00", 2) == 0) return msg; -- cgit v1.2.3