diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index 5ac60df87..9aa994865 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -148,4 +148,12 @@ struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg); int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *parsed); int bsc_nat_vty_init(struct bsc_nat *nat); +/** + * SCCP patching and handling + */ +int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed); +void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed); +struct bsc_connection *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *); +struct bsc_connection *patch_sccp_src_ref_to_msc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *); + #endif diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index 6d45ac0f0..58462324e 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -165,125 +165,6 @@ static void send_id_req(struct bsc_connection *bsc) bsc_write(bsc, id_req, sizeof(id_req)); } -/* - * SCCP patching below - */ - -/* check if we are using this ref for patched already */ -static int sccp_ref_is_free(struct sccp_source_reference *ref) -{ - struct sccp_connections *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0) - return -1; - } - - return 0; -} - -/* copied from sccp.c */ -static int assign_src_local_reference(struct sccp_source_reference *ref) -{ - static u_int32_t last_ref = 0x50000; - int wrapped = 0; - - do { - struct sccp_source_reference reference; - reference.octet1 = (last_ref >> 0) & 0xff; - reference.octet2 = (last_ref >> 8) & 0xff; - reference.octet3 = (last_ref >> 16) & 0xff; - - ++last_ref; - /* do not use the reversed word and wrap around */ - if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { - LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); - last_ref = 0; - ++wrapped; - } - - if (sccp_ref_is_free(&reference) == 0) { - *ref = reference; - return 0; - } - } while (wrapped != 2); - - LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); - return -1; -} - -static int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct sccp_connections *conn; - - conn = talloc_zero(nat, struct sccp_connections); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); - return -1; - } - - conn->real_ref = *parsed->src_local_ref; - if (assign_src_local_reference(&conn->patched_ref) != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); - talloc_free(conn); - return -1; - } - - return 0; -} - -static void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct sccp_connections *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (memcmp(parsed->src_local_ref, - &conn->real_ref, sizeof(conn->real_ref)) == 0) { - if (bsc != conn->bsc) { - LOGP(DNAT, LOGL_ERROR, "Someone else...\n"); - continue; - } - - - llist_del(&conn->list_entry); - talloc_free(conn); - return; - } - } - - LOGP(DNAT, LOGL_ERROR, "Unknown connection.\n"); -} - -static struct bsc_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct sccp_connections *conn; - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (memcmp(parsed->dest_local_ref, - &conn->real_ref, sizeof(*parsed->dest_local_ref)) == 0) { - memcpy(parsed->dest_local_ref, - &conn->patched_ref, sizeof(*parsed->dest_local_ref)); - return conn->bsc; - } - } - - return NULL; -} - -static struct bsc_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct sccp_connections *conn; - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (memcmp(parsed->src_local_ref, - &conn->real_ref, sizeof(*parsed->src_local_ref)) == 0) { - memcpy(parsed->src_local_ref, - &conn->patched_ref, sizeof(*parsed->src_local_ref)); - return conn->bsc; - } - } - - return NULL; -} - /* * Below is the handling of messages coming * from the MSC and need to be forwarded to @@ -349,10 +230,13 @@ static int forward_sccp_to_bts(struct msgb *msg) case SCCP_MSG_TYPE_CREF: case SCCP_MSG_TYPE_DT1: case SCCP_MSG_TYPE_CC: - bsc = patch_sccp_src_ref_to_bsc(msg, parsed); + bsc = patch_sccp_src_ref_to_bsc(msg, parsed, nat); + break; + case SCCP_MSG_TYPE_RLC: + LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); + goto exit; break; case SCCP_MSG_TYPE_CR: - case SCCP_MSG_TYPE_RLC: /* MSC never opens a SCCP connection, fall through */ default: goto exit; @@ -557,22 +441,23 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) case SCCP_MSG_TYPE_CR: if (create_sccp_src_ref(bsc, msg, parsed) != 0) goto exit2; - found_bsc = patch_sccp_src_ref_to_msc(msg, parsed); + found_bsc = patch_sccp_src_ref_to_msc(msg, parsed, nat); break; case SCCP_MSG_TYPE_RLSD: case SCCP_MSG_TYPE_CREF: case SCCP_MSG_TYPE_DT1: case SCCP_MSG_TYPE_CC: - found_bsc = patch_sccp_src_ref_to_msc(msg, parsed); + found_bsc = patch_sccp_src_ref_to_msc(msg, parsed, nat); break; case SCCP_MSG_TYPE_RLC: - found_bsc = patch_sccp_src_ref_to_msc(msg, parsed); + found_bsc = patch_sccp_src_ref_to_msc(msg, parsed, nat); remove_sccp_src_ref(bsc, msg, parsed); break; case SCCP_MSG_TYPE_UDT: /* simply forward everything */ break; default: + LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type); goto exit2; break; } diff --git a/openbsc/src/nat/bsc_sccp.c b/openbsc/src/nat/bsc_sccp.c new file mode 100644 index 000000000..8c14aa892 --- /dev/null +++ b/openbsc/src/nat/bsc_sccp.c @@ -0,0 +1,151 @@ +/* SCCP patching and handling routines */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include + +#include + +/* + * SCCP patching below + */ + +/* check if we are using this ref for patched already */ +static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat) +{ + struct sccp_connections *conn; + + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0) + return -1; + } + + return 0; +} + +/* copied from sccp.c */ +static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) +{ + static u_int32_t last_ref = 0x50000; + int wrapped = 0; + + do { + struct sccp_source_reference reference; + reference.octet1 = (last_ref >> 0) & 0xff; + reference.octet2 = (last_ref >> 8) & 0xff; + reference.octet3 = (last_ref >> 16) & 0xff; + + ++last_ref; + /* do not use the reversed word and wrap around */ + if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { + LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); + last_ref = 0; + ++wrapped; + } + + if (sccp_ref_is_free(&reference, nat) == 0) { + *ref = reference; + return 0; + } + } while (wrapped != 2); + + LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); + return -1; +} + +int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) +{ + struct sccp_connections *conn; + + conn = talloc_zero(bsc->nat, struct sccp_connections); + if (!conn) { + LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); + return -1; + } + + conn->real_ref = *parsed->src_local_ref; + if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); + talloc_free(conn); + return -1; + } + + return 0; +} + +void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) +{ + struct sccp_connections *conn; + + llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { + if (memcmp(parsed->src_local_ref, + &conn->real_ref, sizeof(conn->real_ref)) == 0) { + if (bsc != conn->bsc) { + LOGP(DNAT, LOGL_ERROR, "Someone else...\n"); + continue; + } + + + llist_del(&conn->list_entry); + talloc_free(conn); + return; + } + } + + LOGP(DNAT, LOGL_ERROR, "Unknown connection.\n"); +} + +struct bsc_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, + struct bsc_nat_parsed *parsed, + struct bsc_nat *nat) +{ + struct sccp_connections *conn; + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (memcmp(parsed->dest_local_ref, + &conn->real_ref, sizeof(*parsed->dest_local_ref)) == 0) { + memcpy(parsed->dest_local_ref, + &conn->patched_ref, sizeof(*parsed->dest_local_ref)); + return conn->bsc; + } + } + + return NULL; +} + +struct bsc_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, + struct bsc_nat_parsed *parsed, + struct bsc_nat *nat) +{ + struct sccp_connections *conn; + llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { + if (memcmp(parsed->src_local_ref, + &conn->real_ref, sizeof(*parsed->src_local_ref)) == 0) { + memcpy(parsed->src_local_ref, + &conn->patched_ref, sizeof(*parsed->src_local_ref)); + return conn->bsc; + } + } + + return NULL; +}