diff options
author | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-10-25 21:08:37 +0200 |
---|---|---|
committer | Holger Hans Peter Freyther <zecke@selfish.org> | 2010-10-25 21:08:37 +0200 |
commit | f3ca2eeedeb624a2c1e1380b7b21d753e67c87a8 (patch) | |
tree | fe74120a743afa4a1b605ea9f2e2ad55f4610ce0 | |
parent | a3967579f8b290d19030f10aeb9c1c827bbf263c (diff) | |
parent | 90bbccfca9f00094e89dd35b42052e80a8318404 (diff) |
Merge branch 'zecke/ussd-side-channel'
-rw-r--r-- | openbsc/include/openbsc/bsc_nat.h | 20 | ||||
-rw-r--r-- | openbsc/include/openbsc/ipaccess.h | 12 | ||||
-rw-r--r-- | openbsc/src/nat/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_nat.c | 11 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_nat_utils.c | 16 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_nat_vty.c | 50 | ||||
-rw-r--r-- | openbsc/src/nat/bsc_ussd.c | 361 |
7 files changed, 467 insertions, 5 deletions
diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index b6d040433..acecba7de 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -43,12 +43,14 @@ struct sccp_source_reference; struct sccp_connections; struct bsc_nat_parsed; struct bsc_nat; +struct bsc_nat_ussd_con; enum { NAT_CON_TYPE_NONE, NAT_CON_TYPE_LU, NAT_CON_TYPE_CM_SERV_REQ, NAT_CON_TYPE_PAG_RESP, + NAT_CON_TYPE_SSA, NAT_CON_TYPE_LOCAL_REJECT, NAT_CON_TYPE_OTHER, }; @@ -102,6 +104,7 @@ enum bsc_cfg_ctr { BCFG_CTR_CON_TYPE_LU, BCFG_CTR_CON_CMSERV_RQ, BCFG_CTR_CON_PAG_RESP, + BCFG_CTR_CON_SSA, BCFG_CTR_CON_OTHER, }; @@ -163,6 +166,10 @@ struct bsc_nat_statistics { struct { struct counter *reconn; } msc; + + struct { + struct counter *reconn; + } ussd; }; enum bsc_nat_acc_ctr { @@ -230,6 +237,14 @@ struct bsc_nat { /* filter */ char *acc_lst_name; + /* USSD messages we want to match */ + char *ussd_lst_name; + char *ussd_query; + char *ussd_token; + char *ussd_local; + struct bsc_fd ussd_listen; + struct bsc_nat_ussd_con *ussd_con; + /* statistics */ struct bsc_nat_statistics stats; }; @@ -311,6 +326,7 @@ struct bsc_nat_acc_lst *bsc_nat_acc_lst_get(struct bsc_nat *nat, const char *nam void bsc_nat_acc_lst_delete(struct bsc_nat_acc_lst *lst); struct bsc_nat_acc_lst_entry *bsc_nat_acc_lst_entry_create(struct bsc_nat_acc_lst *); +int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *imsi); int bsc_nat_msc_is_connected(struct bsc_nat *nat); @@ -318,4 +334,8 @@ int bsc_conn_type_to_ctr(struct sccp_connections *conn); struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len); +/** USSD filtering */ +int bsc_ussd_init(struct bsc_nat *nat); +int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, struct msgb *msg); + #endif diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h index b36811ce3..b37022038 100644 --- a/openbsc/include/openbsc/ipaccess.h +++ b/openbsc/include/openbsc/ipaccess.h @@ -2,6 +2,7 @@ #define _IPACCESS_H #include "e1_input.h" +#include "gsm_subscriber.h" #include <osmocore/linuxlist.h> #define IPA_TCP_PORT_OML 3002 @@ -29,6 +30,9 @@ enum ipaccess_msgtype { IPAC_MSGT_ID_GET = 0x04, IPAC_MSGT_ID_RESP = 0x05, IPAC_MSGT_ID_ACK = 0x06, + + /* OpenBSC extension */ + IPAC_MSGT_SCCP_STATE = 0xff, }; enum ipaccess_id_tags { @@ -43,6 +47,14 @@ enum ipaccess_id_tags { IPAC_IDTAG_UNIT = 0x08, }; +struct ipac_msgt_sccp_state { + uint8_t src_ref[3]; + uint8_t dst_ref[3]; + uint8_t trans_id; + uint8_t invoke_id; + char imsi[GSM_IMSI_LENGTH]; +} __attribute__((packed)); + int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa); /* diff --git a/openbsc/src/nat/Makefile.am b/openbsc/src/nat/Makefile.am index 2f37f5cd2..c7c4b0e9f 100644 --- a/openbsc/src/nat/Makefile.am +++ b/openbsc/src/nat/Makefile.am @@ -6,7 +6,7 @@ bin_PROGRAMS = bsc_nat bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ - bsc_nat_vty.c bsc_sccp.c \ + bsc_nat_vty.c bsc_sccp.c bsc_ussd.c \ $(top_srcdir)/src/debug.c $(top_srcdir)/src/bsc_msc.c bsc_nat_LDADD = $(top_builddir)/src/libvty.a \ $(top_builddir)/src/libmgcp.a $(top_builddir)/src/libbsc.a \ diff --git a/openbsc/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index b82c4ef25..daabf0976 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -854,12 +854,17 @@ static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) con = NULL; goto exit2; } + + /* hand data to a side channel */ + if (bsc_check_ussd(con, parsed, msg) == 1) + con->con_local = 2; } con_bsc = con->bsc; con_msc = con->msc_con; con_filter = con->con_local; } + break; case SCCP_MSG_TYPE_RLC: con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); @@ -1272,6 +1277,12 @@ int main(int argc, char **argv) exit(1); } + rc = bsc_ussd_init(nat); + if (rc != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); + exit(1); + } + signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGPIPE, SIG_IGN); diff --git a/openbsc/src/nat/bsc_nat_utils.c b/openbsc/src/nat/bsc_nat_utils.c index dc18cffec..0cef01b24 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -54,6 +54,7 @@ static const struct rate_ctr_desc bsc_cfg_ctr_description[] = { [BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "}, [BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "}, [BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "}, + [BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "}, [BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "}, }; @@ -92,6 +93,7 @@ struct bsc_nat *bsc_nat_alloc(void) nat->stats.bsc.reconn = counter_alloc("nat.bsc.conn"); nat->stats.bsc.auth_fail = counter_alloc("nat.bsc.auth_fail"); nat->stats.msc.reconn = counter_alloc("nat.msc.conn"); + nat->stats.ussd.reconn = counter_alloc("nat.ussd.conn"); nat->msc_ip = talloc_strdup(nat, "127.0.0.1"); nat->msc_port = 5000; nat->auth_timeout = 2; @@ -287,7 +289,7 @@ int bsc_write_msg(struct write_queue *queue, struct msgb *msg) return 0; } -static int lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string) +int bsc_nat_lst_check_allow(struct bsc_nat_acc_lst *lst, const char *mi_string) { struct bsc_nat_acc_lst_entry *entry; @@ -334,7 +336,7 @@ static int auth_imsi(struct bsc_connection *bsc, const char *mi_string) if (bsc_lst) { /* 1. BSC allow */ - if (lst_check_allow(bsc_lst, mi_string) == 0) + if (bsc_nat_lst_check_allow(bsc_lst, mi_string) == 0) return 1; /* 2. BSC deny */ @@ -391,7 +393,7 @@ static int _cr_check_loc_upd(struct bsc_connection *bsc, static int _cr_check_cm_serv_req(struct bsc_connection *bsc, uint8_t *data, unsigned int length, - char **imsi) + int *con_type, char **imsi) { static const uint32_t classmark_offset = offsetof(struct gsm48_service_request, classmark); @@ -410,6 +412,8 @@ static int _cr_check_cm_serv_req(struct bsc_connection *bsc, } req = (struct gsm48_service_request *) data; + if (req->cm_service_type == 0x8) + *con_type = NAT_CON_TYPE_SSA; rc = gsm48_extract_mi((uint8_t *) &req->classmark, length - classmark_offset, mi_string, &mi_type); if (rc < 0) { @@ -537,7 +541,9 @@ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, } else if (hdr48->proto_discr == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_CM_SERV_REQ) { *con_type = NAT_CON_TYPE_CM_SERV_REQ; - return _cr_check_cm_serv_req(bsc, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); + return _cr_check_cm_serv_req(bsc, &hdr48->data[0], + hdr48_len - sizeof(*hdr48), + con_type, imsi); } else if (hdr48->proto_discr == GSM48_PDISC_RR && msg_type == GSM48_MT_RR_PAG_RESP) { *con_type = NAT_CON_TYPE_PAG_RESP; @@ -614,6 +620,7 @@ static const char *con_types [] = { [NAT_CON_TYPE_LU] = "Location Update", [NAT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", [NAT_CON_TYPE_PAG_RESP] = "Paging Response", + [NAT_CON_TYPE_SSA] = "Supplementar Service Activation", [NAT_CON_TYPE_LOCAL_REJECT] = "Local Reject", [NAT_CON_TYPE_OTHER] = "Other", }; @@ -693,6 +700,7 @@ static const int con_to_ctr[] = { [NAT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, [NAT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, [NAT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, + [NAT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, [NAT_CON_TYPE_LOCAL_REJECT] = -1, [NAT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, }; diff --git a/openbsc/src/nat/bsc_nat_vty.c b/openbsc/src/nat/bsc_nat_vty.c index 9822e5c57..9eb8ebc75 100644 --- a/openbsc/src/nat/bsc_nat_vty.c +++ b/openbsc/src/nat/bsc_nat_vty.c @@ -78,6 +78,14 @@ static int config_write_nat(struct vty *vty) vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); if (_nat->acc_lst_name) vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); + if (_nat->ussd_lst_name) + vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); + if (_nat->ussd_query) + vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); + if (_nat->ussd_token) + vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); + if (_nat->ussd_local) + vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); llist_for_each_entry(lst, &_nat->access_lists, list) { write_acc_lst(vty, lst); @@ -395,6 +403,44 @@ DEFUN(cfg_nat_acc_lst_name, return CMD_SUCCESS; } +DEFUN(cfg_nat_ussd_lst_name, + cfg_nat_ussd_lst_name_cmd, + "ussd-list-name NAME", + "Set the name of the access list to check for IMSIs for USSD message\n" + "The name of the access list for HLR USSD handling") +{ + bsc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_query, + cfg_nat_ussd_query_cmd, + "ussd-query QUERY", + "Set the USSD query to match with the ussd-list-name\n" + "The query to match") +{ + bsc_replace_string(_nat, &_nat->ussd_query, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_token, + cfg_nat_ussd_token_cmd, + "ussd-token TOKEN", + "Set the token used to identify the USSD module\n" "Secret key\n") +{ + bsc_replace_string(_nat, &_nat->ussd_token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_nat_ussd_local, + cfg_nat_ussd_local_cmd, + "ussd-local-ip A.B.C.D", + "Set the IP to listen for the USSD Provider\n" "IP Address\n") +{ + bsc_replace_string(_nat, &_nat->ussd_local, argv[0]); + return CMD_SUCCESS; +} + /* per BSC configuration */ DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", "Select a BSC to configure") { @@ -632,6 +678,10 @@ int bsc_nat_vty_init(struct bsc_nat *nat) install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); + install_element(NAT_NODE, &cfg_nat_ussd_local_cmd); /* access-list */ install_element(NAT_NODE, &cfg_lst_imsi_allow_cmd); diff --git a/openbsc/src/nat/bsc_ussd.c b/openbsc/src/nat/bsc_ussd.c new file mode 100644 index 000000000..3729d91f3 --- /dev/null +++ b/openbsc/src/nat/bsc_ussd.c @@ -0,0 +1,361 @@ +/* USSD Filter Code */ + +/* + * (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/socket.h> + +#include <osmocore/protocol/gsm_08_08.h> +#include <osmocore/gsm0480.h> +#include <osmocore/talloc.h> +#include <osmocore/tlv.h> + +#include <osmocom/sccp/sccp.h> + +#include <sys/socket.h> +#include <string.h> +#include <unistd.h> + +struct bsc_nat_ussd_con { + struct write_queue queue; + struct bsc_nat *nat; + int authorized; + + struct timer_list auth_timeout; +}; + +static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *); + +static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat) +{ + struct bsc_nat_ussd_con *con; + + con = talloc_zero(nat, struct bsc_nat_ussd_con); + if (!con) + return NULL; + + con->nat = nat; + return con; +} + +static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con) +{ + if (con->nat->ussd_con == con) + con->nat->ussd_con = NULL; + close(con->queue.bfd.fd); + bsc_unregister_fd(&con->queue.bfd); + bsc_del_timer(&con->auth_timeout); + write_queue_clear(&con->queue); + talloc_free(con); +} + +static int forward_sccp(struct bsc_nat *nat, struct msgb *msg) +{ + struct sccp_connections *con; + struct bsc_nat_parsed *parsed; + + + parsed = bsc_nat_parse(msg); + if (!parsed) { + LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n"); + msgb_free(msg); + return -1; + } + + if (!parsed->dest_local_ref) { + LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n"); + msgb_free(msg); + return -1; + } + + con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref); + if (!con || !con->bsc) { + LOGP(DNAT, LOGL_ERROR, "No active connection found.\n"); + msgb_free(msg); + return -1; + } + + talloc_free(parsed); + bsc_write_msg(&con->bsc->write_queue, msg); + return 0; +} + +static int ussd_read_cb(struct bsc_fd *bfd) +{ + int error; + struct bsc_nat_ussd_con *conn = bfd->data; + struct msgb *msg = ipaccess_read_msg(bfd, &error); + struct ipaccess_head *hh; + + if (!msg) { + LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n"); + bsc_nat_ussd_destroy(conn); + return -1; + } + + LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n", + hexdump(msg->data, msg->len), msg->l2h[0]); + hh = (struct ipaccess_head *) msg->data; + + if (hh->proto == IPAC_PROTO_IPACCESS) { + if (msg->l2h[0] == IPAC_MSGT_ID_RESP) { + struct tlv_parsed tvp; + ipaccess_idtag_parse(&tvp, + (unsigned char *) msg->l2h + 2, + msgb_l2len(msg) - 2); + if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) + ussd_auth_con(&tvp, conn); + } + + msgb_free(msg); + } else if (hh->proto == IPAC_PROTO_SCCP) { + forward_sccp(conn->nat, msg); + } else { + msgb_free(msg); + } + + return 0; +} + +static void ussd_auth_cb(void *_data) +{ + LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); + bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); +} + +static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) +{ + const char *token; + int len; + if (!conn->nat->ussd_token) { + LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); + bsc_nat_ussd_destroy(conn); + return; + } + + token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); + len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); + if (strncmp(conn->nat->ussd_token, token, len) != 0) { + LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", + conn->queue.bfd.fd); + bsc_nat_ussd_destroy(conn); + return; + } + + /* it is authenticated now */ + if (conn->nat->ussd_con && conn->nat->ussd_con != conn) + bsc_nat_ussd_destroy(conn->nat->ussd_con); + + LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); + bsc_del_timer(&conn->auth_timeout); + conn->authorized = 1; + conn->nat->ussd_con = conn; +} + +static void ussd_start_auth(struct bsc_nat_ussd_con *conn) +{ + struct msgb *msg; + + conn->auth_timeout.data = conn; + conn->auth_timeout.cb = ussd_auth_cb; + bsc_schedule_timer(&conn->auth_timeout, conn->nat->auth_timeout, 0); + + msg = msgb_alloc_headroom(4096, 128, "auth message"); + if (!msg) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); + return; + } + + msgb_v_put(msg, IPAC_MSGT_ID_GET); + bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); +} + +static int ussd_listen_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct bsc_nat_ussd_con *conn; + struct bsc_nat *nat; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + int fd; + + if (!(what & BSC_FD_READ)) + return 0; + + fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + perror("accept"); + return fd; + } + + nat = (struct bsc_nat *) bfd->data; + counter_inc(nat->stats.ussd.reconn); + + conn = bsc_nat_ussd_alloc(nat); + if (!conn) { + LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); + close(fd); + return -1; + } + + write_queue_init(&conn->queue, 10); + conn->queue.bfd.data = conn; + conn->queue.bfd.fd = fd; + conn->queue.bfd.when = BSC_FD_READ; + conn->queue.read_cb = ussd_read_cb; + conn->queue.write_cb = bsc_write_cb; + + if (bsc_register_fd(&conn->queue.bfd) < 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); + bsc_nat_ussd_destroy(conn); + return -1; + } + + LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", + fd, inet_ntoa(sa.sin_addr)); + + /* do authentication */ + ussd_start_auth(conn); + return 0; +} + +int bsc_ussd_init(struct bsc_nat *nat) +{ + struct in_addr addr; + + addr.s_addr = INADDR_ANY; + if (nat->ussd_local) + inet_aton(nat->ussd_local, &addr); + + nat->ussd_listen.data = nat; + return make_sock(&nat->ussd_listen, IPPROTO_TCP, + ntohl(addr.s_addr), 5001, ussd_listen_cb); +} + +static int forward_ussd(struct sccp_connections *con, const struct ussd_request *req, + struct msgb *input) +{ + struct msgb *msg, *copy; + struct ipac_msgt_sccp_state *state; + struct bsc_nat_ussd_con *ussd; + + if (!con->bsc->nat->ussd_con) + return -1; + + msg = msgb_alloc_headroom(4096, 128, "forward ussd"); + if (!msg) { + LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); + return -1; + } + + copy = msgb_alloc_headroom(4096, 128, "forward bts"); + if (!copy) { + LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); + msgb_free(msg); + return -1; + } + + copy->l2h = msgb_put(copy, msgb_l2len(input)); + memcpy(copy->l2h, input->l2h, msgb_l2len(input)); + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_SCCP_STATE; + + /* fill out the data */ + state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); + state->trans_id = req->transaction_id; + state->invoke_id = req->invoke_id; + memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); + memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); + memcpy(state->imsi, con->imsi, strlen(con->imsi)); + + ussd = con->bsc->nat->ussd_con; + bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS); + bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); + + return 0; +} + +int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, + struct msgb *msg) +{ + uint32_t len; + uint8_t msg_type; + struct gsm48_hdr *hdr48; + struct bsc_nat_acc_lst *lst; + struct ussd_request req; + + /* + * various checks to avoid the decoding work. Right now we only want to + * decode if the connection was created for USSD, we do have a USSD access + * list, a query, a IMSI and such... + */ + if (con->con_type != NAT_CON_TYPE_SSA) + return 0; + + if (!con->imsi) + return 0; + + if (!con->bsc->nat->ussd_lst_name) + return 0; + if (!con->bsc->nat->ussd_query) + return 0; + + if (parsed->bssap != BSSAP_MSG_DTAP) + return 0; + + if (strlen(con->imsi) > GSM_IMSI_LENGTH) + return 0; + + hdr48 = bsc_unpack_dtap(parsed, msg, &len); + if (!hdr48) + return 0; + + msg_type = hdr48->msg_type & 0xbf; + if (hdr48->proto_discr != GSM48_PDISC_NC_SS || msg_type != GSM0480_MTYPE_REGISTER) + return 0; + + /* now check if it is a IMSI we care about */ + lst = bsc_nat_acc_lst_find(con->bsc->nat, con->bsc->nat->ussd_lst_name); + if (!lst) + return 0; + + if (bsc_nat_lst_check_allow(lst, con->imsi) != 0) + return 0; + + /* now decode the message and see if we really want to handle it */ + memset(&req, 0, sizeof(req)); + if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1) + return 0; + if (req.text[0] == 0xff) + return 0; + + if (strcmp(req.text, con->bsc->nat->ussd_query) != 0) + return 0; + + /* found a USSD query for our subscriber */ + LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", con->imsi); + if (forward_ussd(con, &req, msg) != 0) + return 0; + return 1; +} |