218 lines
5.7 KiB
C
218 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 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/bsc_nat.h>
|
|
#include <openbsc/bsc_nat_sccp.h>
|
|
#include <openbsc/ipaccess.h>
|
|
#include <openbsc/debug.h>
|
|
|
|
#include <osmocore/talloc.h>
|
|
#include <osmocore/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 */
|
|
{ NAT_IPAC_PROTO_MGCP, 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(DINP, 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;
|
|
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;
|
|
}
|