[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.
This commit is contained in:
Holger Hans Peter Freyther 2010-06-15 18:45:38 +08:00
parent 418f394b01
commit 0b8f69d839
4 changed files with 401 additions and 12 deletions

View File

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

View File

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

View File

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

View File

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