Merge branch 'zecke/number-rewrite'

This commit is contained in:
Holger Hans Peter Freyther 2010-10-27 12:41:19 +02:00
commit 732d9c19bf
6 changed files with 304 additions and 1 deletions

View File

@ -28,6 +28,7 @@
#include <osmocore/select.h>
#include <osmocore/msgb.h>
#include <osmocore/msgfile.h>
#include <osmocore/timer.h>
#include <osmocore/write_queue.h>
#include <osmocore/rate_ctr.h>
@ -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;
@ -339,4 +344,6 @@ int bsc_ussd_init(struct bsc_nat *nat);
int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, struct msgb *msg);
int bsc_close_ussd_connections(struct bsc_nat *nat);
struct msgb *bsc_nat_rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi);
#endif

View File

@ -901,6 +901,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, con->imsi);
talloc_free(parsed);
parsed = NULL;
}
con_bsc = con->bsc;
@ -956,7 +965,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:

View File

@ -721,3 +721,157 @@ 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 *parsed, const char *imsi)
{
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 (called.plan != 1)
return msg;
if (called.type == 1)
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 (entry->mcc[0] == '*' || strncmp(entry->mcc, imsi, 3) != 0)
continue;
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(&reg, entry->option, REG_EXTENDED) != 0) {
LOGP(DNAT, LOGL_ERROR,
"Regexp '%s' is not valid.\n", entry->option);
continue;
}
/* this regexp matches... */
if (regexec(&reg, 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(&reg);
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;
}

View File

@ -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);

View File

@ -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
};

View File

@ -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;
}