aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-01-29 06:27:21 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-01-30 10:33:07 +0100
commit55b4f5cc2e7fd8d97547f2ee4db3a925428e9c25 (patch)
tree2db9fda797ef6e90ccc1f8a7cfd7b3e473fb4e88
parent1ac5ac75a9b15d8a1cea8bf852853eb336ba68fa (diff)
[nat] Prepare more sophisicated filtering and patching
Introduce a bsc_nat_parse method to parse a IP Access method into various parts. Write out the IPA Proto, in case SCCP is used, store the msg type, pointers to the source/dest local reference and other information. Use the result of bsc_nat_parse inside the bsc_nat_filter method to decide if the message should be dropped or not. In the future the bsc_nat_parse result will be used for patching SCCP references and other parts of the message. The filter language should be able to filter the msg type of SCCP messages and gain the "NOT" word in the filter language.
-rw-r--r--openbsc/configure.in1
-rw-r--r--openbsc/include/openbsc/bsc_nat.h46
-rw-r--r--openbsc/src/nat/bsc_filter.c162
-rw-r--r--openbsc/src/nat/bsc_nat.c35
-rw-r--r--openbsc/tests/Makefile.am2
-rw-r--r--openbsc/tests/bsc-nat/bsc_nat_test.c170
6 files changed, 403 insertions, 13 deletions
diff --git a/openbsc/configure.in b/openbsc/configure.in
index 9dcda3633..c89bc8208 100644
--- a/openbsc/configure.in
+++ b/openbsc/configure.in
@@ -51,4 +51,5 @@ AC_OUTPUT(
tests/db/Makefile
tests/channel/Makefile
tests/sccp/Makefile
+ tests/bsc-nat/Makefile
Makefile)
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h
index ea30cae22..b8a533fff 100644
--- a/openbsc/include/openbsc/bsc_nat.h
+++ b/openbsc/include/openbsc/bsc_nat.h
@@ -23,11 +23,55 @@
#define BSC_NAT_H
#include <sys/types.h>
+#include <sccp/sccp_types.h>
#include "msgb.h"
+#define FILTER_NONE 0
+#define FILTER_TO_BSC 1
+#define FILTER_TO_MSC 2
+#define FILTER_TO_BOTH 3
+
+/*
+ * For the NAT we will need to analyze and later patch
+ * the received message. This would require us to parse
+ * the IPA and SCCP header twice. Instead of doing this
+ * we will have one analyze structure and have the patching
+ * and filter operate on the same structure.
+ */
+struct bsc_nat_parsed {
+ /* ip access prototype */
+ int ipa_proto;
+
+ /* source local reference */
+ struct sccp_source_reference *src_local_ref;
+
+ /* destination local reference */
+ struct sccp_source_reference *dest_local_ref;
+
+ /* called ssn number */
+ int called_ssn;
+
+ /* calling ssn number */
+ int calling_ssn;
+
+ /* sccp message type */
+ int sccp_type;
+
+ /* bssap type, e.g. 0 for BSS Management */
+ int bssap;
+
+ /* the gsm0808 message type */
+ int gsm_type;
+};
+
+/**
+ * parse the given message into the above structure
+ */
+struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg);
+
/**
* filter based on IP Access header in both directions
*/
-int bsc_nat_filter_ipa(struct msgb *msg);
+int bsc_nat_filter_ipa(struct msgb *msg, struct bsc_nat_parsed *parsed);
#endif
diff --git a/openbsc/src/nat/bsc_filter.c b/openbsc/src/nat/bsc_filter.c
index 5c59f39a5..0727b33e6 100644
--- a/openbsc/src/nat/bsc_filter.c
+++ b/openbsc/src/nat/bsc_filter.c
@@ -22,13 +22,169 @@
*/
#include <openbsc/bsc_nat.h>
+#include <openbsc/bssap.h>
#include <openbsc/ipaccess.h>
+#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
-int bsc_nat_filter_ipa(struct msgb *msg)
+#include <sccp/sccp.h>
+
+/*
+ * The idea is to have a simple struct describing a IPA packet with
+ * SCCP SSN and the GSM 08.08 payload and decide. We will both have
+ * a white and a blacklist of packets we want to handle.
+ *
+ * TODO: Implement a "NOT" in the filter language.
+ */
+
+#define ALLOW_ANY -1
+
+struct bsc_pkt_filter {
+ int ipa_proto;
+ int dest_ssn;
+ int bssap;
+ int gsm;
+ int filter_dir;
+};
+
+static struct bsc_pkt_filter black_list[] = {
+ /* filter reset messages to the MSC */
+ { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC },
+
+ /* filter reset ack messages to the BSC */
+ { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC },
+
+ /* filter ip access */
+ { IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC },
+};
+
+static struct bsc_pkt_filter white_list[] = {
+ /* allow IPAC_PROTO_SCCP messages to both sides */
+ { IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_NONE },
+};
+
+struct bsc_nat_parsed* bsc_nat_parse(struct msgb *msg)
{
+ struct sccp_parse_result result;
+ struct bsc_nat_parsed *parsed;
struct ipaccess_head *hh;
- /* handle base message handling */
+ /* quick fail */
+ if (msg->len < 4)
+ return NULL;
+
+ parsed = talloc_zero(msg, struct bsc_nat_parsed);
+ if (!parsed)
+ return NULL;
+
+ /* more init */
+ parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1;
+ parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1;
+
+ /* start parsing */
hh = (struct ipaccess_head *) msg->data;
- return hh->proto == IPAC_PROTO_IPACCESS;
+ parsed->ipa_proto = hh->proto;
+
+ msg->l2h = &hh->data[0];
+
+ /* analyze sccp down here */
+ if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
+ memset(&result, 0, sizeof(result));
+ if (sccp_parse_header(msg, &result) != 0) {
+ talloc_free(parsed);
+ return 0;
+ }
+
+ if (msg->l3h && msgb_l3len(msg) < 3) {
+ LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n");
+ talloc_free(parsed);
+ return 0;
+ }
+
+ parsed->sccp_type = sccp_determine_msg_type(msg);
+ parsed->src_local_ref = result.source_local_reference;
+ parsed->dest_local_ref = result.destination_local_reference;
+ parsed->called_ssn = result.called.ssn;
+ parsed->calling_ssn = result.calling.ssn;
+
+ /* in case of connection confirm we have no payload */
+ if (msg->l3h) {
+ parsed->bssap = msg->l3h[0];
+ parsed->gsm_type = msg->l3h[2];
+ }
+ }
+
+ return parsed;
+}
+
+int bsc_nat_filter_ipa(struct msgb *msg, struct bsc_nat_parsed *parsed)
+{
+ int i;
+
+ /* go through the blacklist now */
+ for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
+ /* the proto is not blacklisted */
+ if (black_list[i].ipa_proto != ALLOW_ANY
+ && black_list[i].ipa_proto != parsed->ipa_proto)
+ continue;
+
+ if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
+ /* the SSN is not blacklisted */
+ if (black_list[i].dest_ssn != ALLOW_ANY
+ && black_list[i].dest_ssn != parsed->called_ssn)
+ continue;
+
+ /* bssap */
+ if (black_list[i].bssap != ALLOW_ANY
+ && black_list[i].bssap != parsed->bssap)
+ continue;
+
+ /* gsm */
+ if (black_list[i].gsm != ALLOW_ANY
+ && black_list[i].gsm != parsed->gsm_type)
+ continue;
+
+ /* blacklisted */
+ LOGP(DNAT, LOGL_NOTICE, "Blacklisted with rule %d\n", i);
+ return black_list[i].filter_dir;
+ } else {
+ /* blacklisted, we have no content sniffing yet */
+ LOGP(DNAT, LOGL_NOTICE, "Blacklisted with rule %d\n", i);
+ return black_list[i].filter_dir;
+ }
+ }
+
+ /* go through the whitelust now */
+ for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
+ /* the proto is not whitelisted */
+ if (white_list[i].ipa_proto != ALLOW_ANY
+ && white_list[i].ipa_proto != parsed->ipa_proto)
+ continue;
+
+ if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
+ /* the SSN is not whitelisted */
+ if (white_list[i].dest_ssn != ALLOW_ANY
+ && white_list[i].dest_ssn != parsed->called_ssn)
+ continue;
+
+ /* bssap */
+ if (white_list[i].bssap != ALLOW_ANY
+ && white_list[i].bssap != parsed->bssap)
+ continue;
+
+ /* gsm */
+ if (white_list[i].gsm != ALLOW_ANY
+ && white_list[i].gsm != parsed->gsm_type)
+ continue;
+
+ /* whitelisted */
+ LOGP(DNAT, LOGL_NOTICE, "Whitelisted with rule %d\n", i);
+ return FILTER_NONE;
+ } else {
+ /* whitelisted */
+ return FILTER_NONE;
+ }
+ }
+
+ return FILTER_TO_BOTH;
}
diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c
index 96444884c..609a17d96 100644
--- a/openbsc/src/nat/bsc_nat.c
+++ b/openbsc/src/nat/bsc_nat.c
@@ -102,13 +102,18 @@ static void initialize_msc_if_needed()
static void forward_sccp_to_bts(struct msgb *msg)
{
struct bsc_connection *bsc;
+ struct bsc_nat_parsed *parsed;
int rc;
/* filter, drop, patch the message? */
-
- /* drop packets with the wrong IPA header */
- if (bsc_nat_filter_ipa(msg))
+ parsed = bsc_nat_parse(msg);
+ if (!parsed) {
+ LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
return;
+ }
+
+ if (bsc_nat_filter_ipa(msg, parsed))
+ goto exit;
/* currently send this to every BSC connected */
llist_for_each_entry(bsc, &bsc_connections, list_entry) {
@@ -118,6 +123,9 @@ static void forward_sccp_to_bts(struct msgb *msg)
if (rc < msg->len)
LOGP(DNAT, LOGL_ERROR, "Failed to write message to BTS: %d\n", rc);
}
+
+exit:
+ talloc_free(parsed);
}
static int ipaccess_msc_cb(struct bsc_fd *bfd, unsigned int what)
@@ -171,14 +179,25 @@ static void remove_bsc_connection(struct bsc_connection *connection)
static int forward_sccp_to_msc(struct msgb *msg)
{
- /* FIXME: We need to filter out certain messages */
+ struct bsc_nat_parsed *parsed;
+ int rc = -1;
- /* drop packets with the wrong IPA header */
- if (bsc_nat_filter_ipa(msg))
- return 0;
+ /* Parse and filter messages */
+ parsed = bsc_nat_parse(msg);
+ if (!parsed) {
+ LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
+ return -1;
+ }
+
+ if (bsc_nat_filter_ipa(msg, parsed))
+ goto exit;
/* send the non-filtered but maybe modified msg */
- return write(msc_connection.fd, msg->data, msg->len);
+ rc = write(msc_connection.fd, msg->data, msg->len);
+
+exit:
+ talloc_free(parsed);
+ return rc;
}
static int ipaccess_bsc_cb(struct bsc_fd *bfd, unsigned int what)
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index f40105fbf..d867ec6bb 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1 +1 @@
-SUBDIRS = debug timer sms gsm0408 db channel sccp
+SUBDIRS = debug timer sms gsm0408 db channel sccp bsc-nat
diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/openbsc/tests/bsc-nat/bsc_nat_test.c
new file mode 100644
index 000000000..282f2515b
--- /dev/null
+++ b/openbsc/tests/bsc-nat/bsc_nat_test.c
@@ -0,0 +1,170 @@
+/*
+ * BSC NAT Message filtering
+ *
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by on-waves.com
+ *
+ * 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 <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/bsc_nat.h>
+
+#include <stdio.h>
+
+/* test messages for ipa */
+static u_int8_t ipa_id[] = {
+ 0x00, 0x01, 0xfe, 0x06,
+};
+
+/* SCCP messages are below */
+static u_int8_t gsm_reset[] = {
+ 0x00, 0x12, 0xfd,
+ 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
+ 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
+ 0x01, 0x20,
+};
+
+static const u_int8_t gsm_reset_ack[] = {
+ 0x00, 0x13, 0xfd,
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
+ 0x00, 0x01, 0x31,
+};
+
+static const u_int8_t gsm_paging[] = {
+ 0x00, 0x20, 0xfd,
+ 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
+ 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10,
+ 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10,
+ 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06,
+};
+
+/* BSC -> MSC connection open */
+static const u_int8_t bssmap_cr[] = {
+ 0x00, 0x2c, 0xfd,
+ 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02,
+ 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05,
+ 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3,
+ 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33,
+ 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01,
+ 0x31, 0x97, 0x61, 0x00
+};
+
+/* MSC -> BSC connection confirm */
+static const u_int8_t bssmap_cc[] = {
+ 0x00, 0x0a, 0xfd,
+ 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00,
+};
+
+/* MSC -> BSC released */
+static const u_int8_t bssmap_released[] = {
+ 0x00, 0x0e, 0xfd,
+ 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f,
+ 0x02, 0x23, 0x42, 0x00,
+};
+
+/* BSC -> MSC released */
+static const u_int8_t bssmap_release_complete[] = {
+ 0x00, 0x07, 0xfd,
+ 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03
+};
+
+struct filter_result {
+ const u_int8_t *data;
+ const u_int16_t length;
+ const int result;
+};
+
+static const struct filter_result results[] = {
+ {
+ .data = ipa_id,
+ .length = ARRAY_SIZE(ipa_id),
+ .result = FILTER_TO_MSC,
+ },
+ {
+ .data = gsm_reset,
+ .length = ARRAY_SIZE(gsm_reset),
+ .result = FILTER_TO_MSC,
+ },
+ {
+ .data = gsm_reset_ack,
+ .length = ARRAY_SIZE(gsm_reset_ack),
+ .result = FILTER_TO_BSC,
+ },
+ {
+ .data = gsm_paging,
+ .length = ARRAY_SIZE(gsm_paging),
+ .result = FILTER_NONE,
+ },
+ {
+ .data = bssmap_cr,
+ .length = ARRAY_SIZE(bssmap_cr),
+ .result = FILTER_NONE,
+ },
+ {
+ .data = bssmap_cc,
+ .length = ARRAY_SIZE(bssmap_cc),
+ .result = FILTER_NONE,
+ },
+ {
+ .data = bssmap_released,
+ .length = ARRAY_SIZE(bssmap_released),
+ .result = FILTER_NONE,
+ },
+ {
+ .data = bssmap_release_complete,
+ .length = ARRAY_SIZE(bssmap_release_complete),
+ .result = FILTER_NONE,
+ },
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+
+
+ /* start testinh with proper messages */
+ for (i = 0; i < ARRAY_SIZE(results); ++i) {
+ int result;
+ struct bsc_nat_parsed *parsed;
+ struct msgb *msg = msgb_alloc(4096, "test-message");
+
+ fprintf(stderr, "Going to test item: %d\n", i);
+ memcpy(msg->data, results[i].data, results[i].length);
+ msg->l2h = msgb_put(msg, results[i].length);
+
+ parsed = bsc_nat_parse(msg);
+ if (!parsed) {
+ fprintf(stderr, "FAIL: Failed to parse the message\n");
+ continue;
+ }
+
+ result = bsc_nat_filter_ipa(msg, parsed);
+ if (result != results[i].result) {
+ fprintf(stderr, "FAIL: Not the expected result got: %d wanted: %d\n",
+ result, results[i].result);
+ }
+
+ msgb_free(msg);
+ }
+
+ return 0;
+}