From c16c2dc5d35bce659cf4c9ab542054c9b57517aa Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Wed, 13 Oct 2010 20:22:36 +0200 Subject: nat: Implement accepting a USSD Provider connection, do authentication --- openbsc/include/openbsc/bsc_nat.h | 8 ++ openbsc/src/nat/bsc_nat.c | 6 ++ openbsc/src/nat/bsc_nat_utils.c | 1 + openbsc/src/nat/bsc_ussd.c | 191 +++++++++++++++++++++++++++++++++++++- 4 files changed, 205 insertions(+), 1 deletion(-) diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h index f718766a1..acecba7de 100644 --- a/openbsc/include/openbsc/bsc_nat.h +++ b/openbsc/include/openbsc/bsc_nat.h @@ -43,6 +43,7 @@ struct sccp_source_reference; struct sccp_connections; struct bsc_nat_parsed; struct bsc_nat; +struct bsc_nat_ussd_con; enum { NAT_CON_TYPE_NONE, @@ -165,6 +166,10 @@ struct bsc_nat_statistics { struct { struct counter *reconn; } msc; + + struct { + struct counter *reconn; + } ussd; }; enum bsc_nat_acc_ctr { @@ -237,6 +242,8 @@ struct bsc_nat { 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; @@ -328,6 +335,7 @@ 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/src/nat/bsc_nat.c b/openbsc/src/nat/bsc_nat.c index 515278cba..fb4c1507c 100644 --- a/openbsc/src/nat/bsc_nat.c +++ b/openbsc/src/nat/bsc_nat.c @@ -1279,6 +1279,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 2fca31dcb..0cef01b24 100644 --- a/openbsc/src/nat/bsc_nat_utils.c +++ b/openbsc/src/nat/bsc_nat_utils.c @@ -93,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; diff --git a/openbsc/src/nat/bsc_ussd.c b/openbsc/src/nat/bsc_ussd.c index c4e30d842..6640635e8 100644 --- a/openbsc/src/nat/bsc_ussd.c +++ b/openbsc/src/nat/bsc_ussd.c @@ -23,13 +23,202 @@ #include #include +#include +#include #include - #include +#include +#include +#include #include +#include + +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 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) { + LOGP(DNAT, LOGL_ERROR, "USSD SCCP is not handled\n"); + msgb_free(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); +} int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed, struct msgb *msg) -- cgit v1.2.3