osmo-bsc/src/osmo-bsc_nat/bsc_filter.c

219 lines
5.7 KiB
C

/* BSC Multiplexer/NAT */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (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 Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/bsc_nat.h>
#include <openbsc/bsc_nat_sccp.h>
#include <openbsc/ipaccess.h>
#include <openbsc/debug.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/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
#define FILTER_TO_BSC 1
#define FILTER_TO_MSC 2
#define FILTER_TO_BOTH 3
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_TO_BOTH },
/* allow MGCP messages to both sides */
{ IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
};
struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg)
{
struct sccp_parse_result result;
struct bsc_nat_parsed *parsed;
struct ipaccess_head *hh;
/* 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;
parsed->ipa_proto = hh->proto;
msg->l2h = &hh->data[0];
/* do a size check on the input */
if (ntohs(hh->len) != msgb_l2len(msg)) {
LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n");
talloc_free(parsed);
return NULL;
}
/* 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;
if (parsed->dest_local_ref)
parsed->original_dest_ref = *parsed->dest_local_ref;
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(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed)
{
int i;
/* go through the blacklist now */
for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
/* ignore the rule? */
if (black_list[i].filter_dir != FILTER_TO_BOTH
&& black_list[i].filter_dir != dir)
continue;
/* 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_INFO, "Blacklisted with rule %d\n", i);
return 1;
} else {
/* blacklisted, we have no content sniffing yet */
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
return 1;
}
}
/* go through the whitelust now */
for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
/* ignore the rule? */
if (white_list[i].filter_dir != FILTER_TO_BOTH
&& white_list[i].filter_dir != dir)
continue;
/* 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_INFO, "Whitelisted with rule %d\n", i);
return 0;
} else {
/* whitelisted */
return 0;
}
}
return 1;
}