aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2010-10-25 21:08:37 +0200
committerHolger Hans Peter Freyther <zecke@selfish.org>2010-10-25 21:08:37 +0200
commitf3ca2eeedeb624a2c1e1380b7b21d753e67c87a8 (patch)
treefe74120a743afa4a1b605ea9f2e2ad55f4610ce0
parenta3967579f8b290d19030f10aeb9c1c827bbf263c (diff)
parent90bbccfca9f00094e89dd35b42052e80a8318404 (diff)
Merge branch 'zecke/ussd-side-channel'
-rw-r--r--openbsc/include/openbsc/bsc_nat.h20
-rw-r--r--openbsc/include/openbsc/ipaccess.h12
-rw-r--r--openbsc/src/nat/Makefile.am2
-rw-r--r--openbsc/src/nat/bsc_nat.c11
-rw-r--r--openbsc/src/nat/bsc_nat_utils.c16
-rw-r--r--openbsc/src/nat/bsc_nat_vty.c50
-rw-r--r--openbsc/src/nat/bsc_ussd.c361
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;
+}