From 2c1f8c8cebe41b30deca29c55290fef7a61e5343 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Thu, 8 Dec 2016 22:27:22 +0100 Subject: move grps_gsup_client.c to libcommon/gsup_client.c This is in preparation for libvlr. Related: OS#1592 Change-Id: I9ad7dc7f17f3b033c779de9ae8bc120655502fce --- openbsc/src/gprs/Makefile.am | 15 -- openbsc/src/gprs/gprs_gsup_client.c | 341 ------------------------------- openbsc/src/gprs/gsup_test_client.c | 299 --------------------------- openbsc/src/libcommon/Makefile.am | 17 ++ openbsc/src/libcommon/gsup_client.c | 341 +++++++++++++++++++++++++++++++ openbsc/src/libcommon/gsup_test_client.c | 299 +++++++++++++++++++++++++++ openbsc/tests/sgsn/Makefile.am | 1 - 7 files changed, 657 insertions(+), 656 deletions(-) delete mode 100644 openbsc/src/gprs/gprs_gsup_client.c delete mode 100644 openbsc/src/gprs/gsup_test_client.c create mode 100644 openbsc/src/libcommon/gsup_client.c create mode 100644 openbsc/src/libcommon/gsup_test_client.c diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index d059aeed0..cff17dde5 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -37,7 +37,6 @@ OSMO_LIBS = \ $(NULL) bin_PROGRAMS = \ - gsup_test_client \ osmo-gbproxy \ $(NULL) if HAVE_LIBGTP @@ -88,7 +87,6 @@ osmo_sgsn_SOURCES = \ sgsn_auth.c \ gprs_subscriber.c \ gprs_utils.c \ - gprs_gsup_client.c \ sgsn_cdr.c \ sgsn_ares.c \ slhc.c \ @@ -132,16 +130,3 @@ osmo_gtphub_LDADD = \ $(LIBGTP_LIBS) \ -lrt \ $(NULL) - -gsup_test_client_SOURCES = \ - gprs_gsup_client.c \ - gsup_test_client.c \ - $(NULL) -gsup_test_client_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - -lrt \ - $(NULL) diff --git a/openbsc/src/gprs/gprs_gsup_client.c b/openbsc/src/gprs/gprs_gsup_client.c deleted file mode 100644 index 0360e0a24..000000000 --- a/openbsc/src/gprs/gprs_gsup_client.c +++ /dev/null @@ -1,341 +0,0 @@ -/* Generic Subscriber Update Protocol client */ - -/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Jacob Erlbeck - * Author: Neels Hofmeyr - * - * 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 . - * - */ - -#include - -#include -#include -#include -#include - -#include - -#include -#include - -extern void *tall_bsc_ctx; - -static void start_test_procedure(struct gsup_client *gsupc); - -static void gsup_client_send_ping(struct gsup_client *gsupc) -{ - struct msgb *msg = gsup_client_msgb_alloc(); - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_PING; - ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); - ipa_client_conn_send(gsupc->link, msg); -} - -static int gsup_client_connect(struct gsup_client *gsupc) -{ - int rc; - - if (gsupc->is_connected) - return 0; - - if (osmo_timer_pending(&gsupc->connect_timer)) { - LOGP(DLGSUP, LOGL_DEBUG, - "GSUP connect: connect timer already running\n"); - osmo_timer_del(&gsupc->connect_timer); - } - - if (osmo_timer_pending(&gsupc->ping_timer)) { - LOGP(DLGSUP, LOGL_DEBUG, - "GSUP connect: ping timer already running\n"); - osmo_timer_del(&gsupc->ping_timer); - } - - if (ipa_client_conn_clear_queue(gsupc->link) > 0) - LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); - - rc = ipa_client_conn_open(gsupc->link); - - if (rc >= 0) { - LOGP(DLGSUP, LOGL_INFO, "GSUP connecting to %s:%d\n", - gsupc->link->addr, gsupc->link->port); - return 0; - } - - LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", - gsupc->link->addr, gsupc->link->port, strerror(-rc)); - - if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || - rc == -EINVAL) - return rc; - - osmo_timer_schedule(&gsupc->connect_timer, - GSUP_CLIENT_RECONNECT_INTERVAL, 0); - - LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", - gsupc->link->addr, gsupc->link->port); - - return 0; -} - -static void connect_timer_cb(void *gsupc_) -{ - struct gsup_client *gsupc = gsupc_; - - if (gsupc->is_connected) - return; - - gsup_client_connect(gsupc); -} - -static void client_send(struct gsup_client *gsupc, int proto_ext, - struct msgb *msg_tx) -{ - ipa_prepend_header_ext(msg_tx, proto_ext); - ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); - ipa_client_conn_send(gsupc->link, msg_tx); - /* msg_tx is now queued and will be freed. */ -} - -static void gsup_client_oap_register(struct gsup_client *gsupc) -{ - struct msgb *msg_tx; - int rc; - rc = oap_client_register(&gsupc->oap_state, &msg_tx); - - if ((rc < 0) || (!msg_tx)) { - LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); - return; - } - - client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); -} - -static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) -{ - struct gsup_client *gsupc = link->data; - - LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", - link->addr, link->port, up ? "UP" : "DOWN"); - - gsupc->is_connected = up; - - if (up) { - start_test_procedure(gsupc); - - if (gsupc->oap_state.state == OAP_INITIALIZED) - gsup_client_oap_register(gsupc); - - osmo_timer_del(&gsupc->connect_timer); - } else { - osmo_timer_del(&gsupc->ping_timer); - - osmo_timer_schedule(&gsupc->connect_timer, - GSUP_CLIENT_RECONNECT_INTERVAL, 0); - } -} - -static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) -{ - int rc; - struct msgb *msg_tx; - - rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); - msgb_free(msg_rx); - if (rc < 0) - return rc; - - if (msg_tx) - client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); - - return 0; -} - -static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) -{ - struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; - struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); - struct gsup_client *gsupc = (struct gsup_client *)link->data; - int rc; - static struct ipaccess_unit ipa_dev = { - .unit_name = "SGSN" - }; - - msg->l2h = &hh->data[0]; - - rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); - - if (rc < 0) { - LOGP(DLGSUP, LOGL_NOTICE, - "GSUP received an invalid IPA/CCM message from %s:%d\n", - link->addr, link->port); - /* Link has been closed */ - gsupc->is_connected = 0; - msgb_free(msg); - return -1; - } - - if (rc == 1) { - uint8_t msg_type = *(msg->l2h); - /* CCM message */ - if (msg_type == IPAC_MSGT_PONG) { - LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); - gsupc->got_ipa_pong = 1; - } - - msgb_free(msg); - return 0; - } - - if (hh->proto != IPAC_PROTO_OSMO) - goto invalid; - - if (!he || msgb_l2len(msg) < sizeof(*he)) - goto invalid; - - msg->l2h = &he->data[0]; - - if (he->proto == IPAC_PROTO_EXT_GSUP) { - OSMO_ASSERT(gsupc->read_cb != NULL); - gsupc->read_cb(gsupc, msg); - /* expecting read_cb() to free msg */ - } else if (he->proto == IPAC_PROTO_EXT_OAP) { - return gsup_client_oap_handle(gsupc, msg); - /* gsup_client_oap_handle frees msg */ - } else - goto invalid; - - return 0; - -invalid: - LOGP(DLGSUP, LOGL_NOTICE, - "GSUP received an invalid IPA message from %s:%d, size = %d\n", - link->addr, link->port, msgb_length(msg)); - - msgb_free(msg); - return -1; -} - -static void ping_timer_cb(void *gsupc_) -{ - struct gsup_client *gsupc = gsupc_; - - LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", - gsupc->is_connected ? "connected" : "not connected", - gsupc->got_ipa_pong ? "got" : "didn't get"); - - if (gsupc->got_ipa_pong) { - start_test_procedure(gsupc); - return; - } - - LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); - ipa_client_conn_close(gsupc->link); - gsupc->is_connected = 0; - - gsup_client_connect(gsupc); -} - -static void start_test_procedure(struct gsup_client *gsupc) -{ - gsupc->ping_timer.data = gsupc; - gsupc->ping_timer.cb = &ping_timer_cb; - - gsupc->got_ipa_pong = 0; - osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); - LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); - gsup_client_send_ping(gsupc); -} - -struct gsup_client *gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gsup_client_read_cb_t read_cb, - struct oap_client_config *oap_config) -{ - struct gsup_client *gsupc; - int rc; - - gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); - OSMO_ASSERT(gsupc); - - rc = oap_client_init(oap_config, &gsupc->oap_state); - if (rc != 0) - goto failed; - - gsupc->link = ipa_client_conn_create(gsupc, - /* no e1inp */ NULL, - 0, - ip_addr, tcp_port, - gsup_client_updown_cb, - gsup_client_read_cb, - /* default write_cb */ NULL, - gsupc); - if (!gsupc->link) - goto failed; - - gsupc->connect_timer.data = gsupc; - gsupc->connect_timer.cb = &connect_timer_cb; - - rc = gsup_client_connect(gsupc); - - if (rc < 0) - goto failed; - - gsupc->read_cb = read_cb; - - return gsupc; - -failed: - gsup_client_destroy(gsupc); - return NULL; -} - -void gsup_client_destroy(struct gsup_client *gsupc) -{ - osmo_timer_del(&gsupc->connect_timer); - osmo_timer_del(&gsupc->ping_timer); - - if (gsupc->link) { - ipa_client_conn_close(gsupc->link); - ipa_client_conn_destroy(gsupc->link); - gsupc->link = NULL; - } - talloc_free(gsupc); -} - -int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) -{ - if (!gsupc) { - msgb_free(msg); - return -ENOTCONN; - } - - if (!gsupc->is_connected) { - msgb_free(msg); - return -EAGAIN; - } - - client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); - - return 0; -} - -struct msgb *gsup_client_msgb_alloc(void) -{ - return msgb_alloc_headroom(4000, 64, __func__); -} diff --git a/openbsc/src/gprs/gsup_test_client.c b/openbsc/src/gprs/gsup_test_client.c deleted file mode 100644 index 1889c6fde..000000000 --- a/openbsc/src/gprs/gsup_test_client.c +++ /dev/null @@ -1,299 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static struct gsup_client *g_gc; - - -/*********************************************************************** - * IMSI Operation - ***********************************************************************/ -static LLIST_HEAD(g_imsi_ops); - -struct imsi_op_stats { - uint32_t num_alloc; - uint32_t num_released; - uint32_t num_rx_success; - uint32_t num_rx_error; - uint32_t num_timeout; -}; - -enum imsi_op_type { - IMSI_OP_SAI, - IMSI_OP_LU, - IMSI_OP_ISD, - _NUM_IMSI_OP -}; - -static const struct value_string imsi_op_names[] = { - { IMSI_OP_SAI, "SAI" }, - { IMSI_OP_LU, "LU" }, - { IMSI_OP_ISD, "ISD" }, - { 0, NULL } -}; - -static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; - -struct imsi_op { - struct llist_head list; - char imsi[17]; - enum imsi_op_type type; - struct osmo_timer_list timer; -}; - -static struct imsi_op *imsi_op_find(const char *imsi, - enum imsi_op_type type) -{ - struct imsi_op *io; - - llist_for_each_entry(io, &g_imsi_ops, list) { - if (!strcmp(io->imsi, imsi) && io->type == type) - return io; - } - return NULL; -} - -static void imsi_op_timer_cb(void *data); - -static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, - enum imsi_op_type type) -{ - struct imsi_op *io; - - if (imsi_op_find(imsi, type)) - return NULL; - - io = talloc_zero(ctx, struct imsi_op); - strncpy(io->imsi, imsi, sizeof(io->imsi)); - io->imsi[sizeof(io->imsi)-1] = '\0'; - io->type = type; - io->timer.cb = imsi_op_timer_cb; - io->timer.data = io; - llist_add(&io->list, &g_imsi_ops); - imsi_op_stats[type].num_alloc++; - - return io; -} - -static void imsi_op_release(struct imsi_op *io) -{ - osmo_timer_del(&io->timer); - llist_del(&io->list); - imsi_op_stats[io->type].num_released++; - talloc_free(io); -} - -static void imsi_op_timer_cb(void *data) -{ - struct imsi_op *io = data; - printf("%s: Timer expiration\n", io->imsi); - imsi_op_stats[io->type].num_timeout++; - imsi_op_release(io); -} - -/* allocate + generate + send Send-Auth-Info */ -int req_auth_info(const char *imsi) -{ - struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; - - osmo_gsup_encode(msg, &gsup); - - return gsup_client_send(g_gc, msg); -} - -/* allocate + generate + send Send-Auth-Info */ -int req_loc_upd(const char *imsi) -{ - struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; - - osmo_gsup_encode(msg, &gsup); - - return gsup_client_send(g_gc, msg); -} - -int resp_isd(struct imsi_op *io) -{ - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; - - osmo_gsup_encode(msg, &gsup); - - imsi_op_release(io); - - return gsup_client_send(g_gc, msg); -} - -/* receive an incoming GSUP message */ -static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) -{ - int is_error = 0; - - if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { - imsi_op_stats[io->type].num_rx_error++; - is_error = 1; - } else - imsi_op_stats[io->type].num_rx_success++; - - switch (io->type) { - case IMSI_OP_SAI: - printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); - /* now that we have auth tuples, request LU */ - req_loc_upd(io->imsi); - imsi_op_release(io); - break; - case IMSI_OP_LU: - printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); - imsi_op_release(io); - break; - case IMSI_OP_ISD: - printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); - resp_isd(io); - break; - default: - printf("%s: Unknown\n", io->imsi); - imsi_op_release(io); - break; - } -} - -static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) -{ - switch (msg_type) { - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: - return IMSI_OP_SAI; - case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: - case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: - return IMSI_OP_LU; - case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: - return IMSI_OP_ISD; - default: - printf("Unknown GSUP msg_type %u\n", msg_type); - return -1; - } -} - -static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg) -{ - struct osmo_gsup_message gsup_msg = {0}; - struct imsi_op *io; - int rc; - - DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); - - rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); - if (rc < 0) - return rc; - - if (!gsup_msg.imsi[0]) - return -1; - - rc = op_type_by_gsup_msgt(gsup_msg.message_type); - if (rc < 0) - return rc; - - switch (rc) { - case IMSI_OP_SAI: - case IMSI_OP_LU: - io = imsi_op_find(gsup_msg.imsi, rc); - if (!io) - return -1; - break; - case IMSI_OP_ISD: - /* ISD is an inbound transaction */ - io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); - break; - } - - imsi_op_rx_gsup(io, &gsup_msg); - msgb_free(msg); - - return 0; -} - -static void print_report(void) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { - struct imsi_op_stats *st = &imsi_op_stats[i]; - const char *name = get_value_string(imsi_op_names, i); - printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", - name, st->num_alloc, st->num_released, st->num_rx_success, - st->num_rx_error, st->num_timeout); - } -} - -static void sig_cb(int sig) -{ - switch (sig) { - case SIGINT: - print_report(); - exit(0); - break; - } -} - -void *tall_bsc_ctx = NULL; - -/* default categories */ -static struct log_info_cat default_categories[] = { -}; - -static const struct log_info gsup_test_client_log_info = { - .cat = default_categories, - .num_cat = ARRAY_SIZE(default_categories), -}; - -int main(int argc, char **argv) -{ - unsigned long long i; - char *server_host = "127.0.0.1"; - uint16_t server_port = 2222; - - osmo_init_logging(&gsup_test_client_log_info); - - g_gc = gsup_client_create(server_host, server_port, gsupc_read_cb, - NULL); - - - signal(SIGINT, sig_cb); - - for (i = 0; i < 10000; i++) { - unsigned long long imsi = 901790000000000 + i; - char imsi_buf[17]; - snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); - req_auth_info(imsi_buf); - osmo_select_main(0); - } - - while (1) { - osmo_select_main(0); - } - - print_report(); - exit(0); -} diff --git a/openbsc/src/libcommon/Makefile.am b/openbsc/src/libcommon/Makefile.am index 6582d1ea3..0b258c08a 100644 --- a/openbsc/src/libcommon/Makefile.am +++ b/openbsc/src/libcommon/Makefile.am @@ -23,8 +23,25 @@ libcommon_a_SOURCES = \ debug.c \ gsm_data.c \ gsm_data_shared.c \ + gsup_client.c \ oap_client.c \ socket.c \ talloc_ctx.c \ gsm_subscriber_base.c \ $(NULL) + +noinst_PROGRAMS = \ + gsup_test_client \ + $(NULL) + +gsup_test_client_SOURCES = \ + gsup_test_client.c \ + $(NULL) +gsup_test_client_LDADD = \ + libcommon.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + -lrt \ + $(NULL) diff --git a/openbsc/src/libcommon/gsup_client.c b/openbsc/src/libcommon/gsup_client.c new file mode 100644 index 000000000..0360e0a24 --- /dev/null +++ b/openbsc/src/libcommon/gsup_client.c @@ -0,0 +1,341 @@ +/* Generic Subscriber Update Protocol client */ + +/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Jacob Erlbeck + * Author: Neels Hofmeyr + * + * 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 . + * + */ + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +extern void *tall_bsc_ctx; + +static void start_test_procedure(struct gsup_client *gsupc); + +static void gsup_client_send_ping(struct gsup_client *gsupc) +{ + struct msgb *msg = gsup_client_msgb_alloc(); + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_PING; + ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); + ipa_client_conn_send(gsupc->link, msg); +} + +static int gsup_client_connect(struct gsup_client *gsupc) +{ + int rc; + + if (gsupc->is_connected) + return 0; + + if (osmo_timer_pending(&gsupc->connect_timer)) { + LOGP(DLGSUP, LOGL_DEBUG, + "GSUP connect: connect timer already running\n"); + osmo_timer_del(&gsupc->connect_timer); + } + + if (osmo_timer_pending(&gsupc->ping_timer)) { + LOGP(DLGSUP, LOGL_DEBUG, + "GSUP connect: ping timer already running\n"); + osmo_timer_del(&gsupc->ping_timer); + } + + if (ipa_client_conn_clear_queue(gsupc->link) > 0) + LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); + + rc = ipa_client_conn_open(gsupc->link); + + if (rc >= 0) { + LOGP(DLGSUP, LOGL_INFO, "GSUP connecting to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + return 0; + } + + LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", + gsupc->link->addr, gsupc->link->port, strerror(-rc)); + + if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || + rc == -EINVAL) + return rc; + + osmo_timer_schedule(&gsupc->connect_timer, + GSUP_CLIENT_RECONNECT_INTERVAL, 0); + + LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + + return 0; +} + +static void connect_timer_cb(void *gsupc_) +{ + struct gsup_client *gsupc = gsupc_; + + if (gsupc->is_connected) + return; + + gsup_client_connect(gsupc); +} + +static void client_send(struct gsup_client *gsupc, int proto_ext, + struct msgb *msg_tx) +{ + ipa_prepend_header_ext(msg_tx, proto_ext); + ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); + ipa_client_conn_send(gsupc->link, msg_tx); + /* msg_tx is now queued and will be freed. */ +} + +static void gsup_client_oap_register(struct gsup_client *gsupc) +{ + struct msgb *msg_tx; + int rc; + rc = oap_client_register(&gsupc->oap_state, &msg_tx); + + if ((rc < 0) || (!msg_tx)) { + LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); + return; + } + + client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); +} + +static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) +{ + struct gsup_client *gsupc = link->data; + + LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", + link->addr, link->port, up ? "UP" : "DOWN"); + + gsupc->is_connected = up; + + if (up) { + start_test_procedure(gsupc); + + if (gsupc->oap_state.state == OAP_INITIALIZED) + gsup_client_oap_register(gsupc); + + osmo_timer_del(&gsupc->connect_timer); + } else { + osmo_timer_del(&gsupc->ping_timer); + + osmo_timer_schedule(&gsupc->connect_timer, + GSUP_CLIENT_RECONNECT_INTERVAL, 0); + } +} + +static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) +{ + int rc; + struct msgb *msg_tx; + + rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); + msgb_free(msg_rx); + if (rc < 0) + return rc; + + if (msg_tx) + client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); + + return 0; +} + +static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) +{ + struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; + struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); + struct gsup_client *gsupc = (struct gsup_client *)link->data; + int rc; + static struct ipaccess_unit ipa_dev = { + .unit_name = "SGSN" + }; + + msg->l2h = &hh->data[0]; + + rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); + + if (rc < 0) { + LOGP(DLGSUP, LOGL_NOTICE, + "GSUP received an invalid IPA/CCM message from %s:%d\n", + link->addr, link->port); + /* Link has been closed */ + gsupc->is_connected = 0; + msgb_free(msg); + return -1; + } + + if (rc == 1) { + uint8_t msg_type = *(msg->l2h); + /* CCM message */ + if (msg_type == IPAC_MSGT_PONG) { + LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); + gsupc->got_ipa_pong = 1; + } + + msgb_free(msg); + return 0; + } + + if (hh->proto != IPAC_PROTO_OSMO) + goto invalid; + + if (!he || msgb_l2len(msg) < sizeof(*he)) + goto invalid; + + msg->l2h = &he->data[0]; + + if (he->proto == IPAC_PROTO_EXT_GSUP) { + OSMO_ASSERT(gsupc->read_cb != NULL); + gsupc->read_cb(gsupc, msg); + /* expecting read_cb() to free msg */ + } else if (he->proto == IPAC_PROTO_EXT_OAP) { + return gsup_client_oap_handle(gsupc, msg); + /* gsup_client_oap_handle frees msg */ + } else + goto invalid; + + return 0; + +invalid: + LOGP(DLGSUP, LOGL_NOTICE, + "GSUP received an invalid IPA message from %s:%d, size = %d\n", + link->addr, link->port, msgb_length(msg)); + + msgb_free(msg); + return -1; +} + +static void ping_timer_cb(void *gsupc_) +{ + struct gsup_client *gsupc = gsupc_; + + LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", + gsupc->is_connected ? "connected" : "not connected", + gsupc->got_ipa_pong ? "got" : "didn't get"); + + if (gsupc->got_ipa_pong) { + start_test_procedure(gsupc); + return; + } + + LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); + ipa_client_conn_close(gsupc->link); + gsupc->is_connected = 0; + + gsup_client_connect(gsupc); +} + +static void start_test_procedure(struct gsup_client *gsupc) +{ + gsupc->ping_timer.data = gsupc; + gsupc->ping_timer.cb = &ping_timer_cb; + + gsupc->got_ipa_pong = 0; + osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); + LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); + gsup_client_send_ping(gsupc); +} + +struct gsup_client *gsup_client_create(const char *ip_addr, + unsigned int tcp_port, + gsup_client_read_cb_t read_cb, + struct oap_client_config *oap_config) +{ + struct gsup_client *gsupc; + int rc; + + gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); + OSMO_ASSERT(gsupc); + + rc = oap_client_init(oap_config, &gsupc->oap_state); + if (rc != 0) + goto failed; + + gsupc->link = ipa_client_conn_create(gsupc, + /* no e1inp */ NULL, + 0, + ip_addr, tcp_port, + gsup_client_updown_cb, + gsup_client_read_cb, + /* default write_cb */ NULL, + gsupc); + if (!gsupc->link) + goto failed; + + gsupc->connect_timer.data = gsupc; + gsupc->connect_timer.cb = &connect_timer_cb; + + rc = gsup_client_connect(gsupc); + + if (rc < 0) + goto failed; + + gsupc->read_cb = read_cb; + + return gsupc; + +failed: + gsup_client_destroy(gsupc); + return NULL; +} + +void gsup_client_destroy(struct gsup_client *gsupc) +{ + osmo_timer_del(&gsupc->connect_timer); + osmo_timer_del(&gsupc->ping_timer); + + if (gsupc->link) { + ipa_client_conn_close(gsupc->link); + ipa_client_conn_destroy(gsupc->link); + gsupc->link = NULL; + } + talloc_free(gsupc); +} + +int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) +{ + if (!gsupc) { + msgb_free(msg); + return -ENOTCONN; + } + + if (!gsupc->is_connected) { + msgb_free(msg); + return -EAGAIN; + } + + client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); + + return 0; +} + +struct msgb *gsup_client_msgb_alloc(void) +{ + return msgb_alloc_headroom(4000, 64, __func__); +} diff --git a/openbsc/src/libcommon/gsup_test_client.c b/openbsc/src/libcommon/gsup_test_client.c new file mode 100644 index 000000000..1889c6fde --- /dev/null +++ b/openbsc/src/libcommon/gsup_test_client.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static struct gsup_client *g_gc; + + +/*********************************************************************** + * IMSI Operation + ***********************************************************************/ +static LLIST_HEAD(g_imsi_ops); + +struct imsi_op_stats { + uint32_t num_alloc; + uint32_t num_released; + uint32_t num_rx_success; + uint32_t num_rx_error; + uint32_t num_timeout; +}; + +enum imsi_op_type { + IMSI_OP_SAI, + IMSI_OP_LU, + IMSI_OP_ISD, + _NUM_IMSI_OP +}; + +static const struct value_string imsi_op_names[] = { + { IMSI_OP_SAI, "SAI" }, + { IMSI_OP_LU, "LU" }, + { IMSI_OP_ISD, "ISD" }, + { 0, NULL } +}; + +static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; + +struct imsi_op { + struct llist_head list; + char imsi[17]; + enum imsi_op_type type; + struct osmo_timer_list timer; +}; + +static struct imsi_op *imsi_op_find(const char *imsi, + enum imsi_op_type type) +{ + struct imsi_op *io; + + llist_for_each_entry(io, &g_imsi_ops, list) { + if (!strcmp(io->imsi, imsi) && io->type == type) + return io; + } + return NULL; +} + +static void imsi_op_timer_cb(void *data); + +static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, + enum imsi_op_type type) +{ + struct imsi_op *io; + + if (imsi_op_find(imsi, type)) + return NULL; + + io = talloc_zero(ctx, struct imsi_op); + strncpy(io->imsi, imsi, sizeof(io->imsi)); + io->imsi[sizeof(io->imsi)-1] = '\0'; + io->type = type; + io->timer.cb = imsi_op_timer_cb; + io->timer.data = io; + llist_add(&io->list, &g_imsi_ops); + imsi_op_stats[type].num_alloc++; + + return io; +} + +static void imsi_op_release(struct imsi_op *io) +{ + osmo_timer_del(&io->timer); + llist_del(&io->list); + imsi_op_stats[io->type].num_released++; + talloc_free(io); +} + +static void imsi_op_timer_cb(void *data) +{ + struct imsi_op *io = data; + printf("%s: Timer expiration\n", io->imsi); + imsi_op_stats[io->type].num_timeout++; + imsi_op_release(io); +} + +/* allocate + generate + send Send-Auth-Info */ +int req_auth_info(const char *imsi) +{ + struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; + + osmo_gsup_encode(msg, &gsup); + + return gsup_client_send(g_gc, msg); +} + +/* allocate + generate + send Send-Auth-Info */ +int req_loc_upd(const char *imsi) +{ + struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; + + osmo_gsup_encode(msg, &gsup); + + return gsup_client_send(g_gc, msg); +} + +int resp_isd(struct imsi_op *io) +{ + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + strncpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; + + osmo_gsup_encode(msg, &gsup); + + imsi_op_release(io); + + return gsup_client_send(g_gc, msg); +} + +/* receive an incoming GSUP message */ +static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) +{ + int is_error = 0; + + if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { + imsi_op_stats[io->type].num_rx_error++; + is_error = 1; + } else + imsi_op_stats[io->type].num_rx_success++; + + switch (io->type) { + case IMSI_OP_SAI: + printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); + /* now that we have auth tuples, request LU */ + req_loc_upd(io->imsi); + imsi_op_release(io); + break; + case IMSI_OP_LU: + printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); + imsi_op_release(io); + break; + case IMSI_OP_ISD: + printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); + resp_isd(io); + break; + default: + printf("%s: Unknown\n", io->imsi); + imsi_op_release(io); + break; + } +} + +static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) +{ + switch (msg_type) { + case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: + case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: + return IMSI_OP_SAI; + case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: + case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: + return IMSI_OP_LU; + case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: + return IMSI_OP_ISD; + default: + printf("Unknown GSUP msg_type %u\n", msg_type); + return -1; + } +} + +static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg) +{ + struct osmo_gsup_message gsup_msg = {0}; + struct imsi_op *io; + int rc; + + DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); + + rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); + if (rc < 0) + return rc; + + if (!gsup_msg.imsi[0]) + return -1; + + rc = op_type_by_gsup_msgt(gsup_msg.message_type); + if (rc < 0) + return rc; + + switch (rc) { + case IMSI_OP_SAI: + case IMSI_OP_LU: + io = imsi_op_find(gsup_msg.imsi, rc); + if (!io) + return -1; + break; + case IMSI_OP_ISD: + /* ISD is an inbound transaction */ + io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); + break; + } + + imsi_op_rx_gsup(io, &gsup_msg); + msgb_free(msg); + + return 0; +} + +static void print_report(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { + struct imsi_op_stats *st = &imsi_op_stats[i]; + const char *name = get_value_string(imsi_op_names, i); + printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", + name, st->num_alloc, st->num_released, st->num_rx_success, + st->num_rx_error, st->num_timeout); + } +} + +static void sig_cb(int sig) +{ + switch (sig) { + case SIGINT: + print_report(); + exit(0); + break; + } +} + +void *tall_bsc_ctx = NULL; + +/* default categories */ +static struct log_info_cat default_categories[] = { +}; + +static const struct log_info gsup_test_client_log_info = { + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +int main(int argc, char **argv) +{ + unsigned long long i; + char *server_host = "127.0.0.1"; + uint16_t server_port = 2222; + + osmo_init_logging(&gsup_test_client_log_info); + + g_gc = gsup_client_create(server_host, server_port, gsupc_read_cb, + NULL); + + + signal(SIGINT, sig_cb); + + for (i = 0; i < 10000; i++) { + unsigned long long imsi = 901790000000000 + i; + char imsi_buf[17]; + snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); + req_auth_info(imsi_buf); + osmo_select_main(0); + } + + while (1) { + osmo_select_main(0); + } + + print_report(); + exit(0); +} diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index 00b80f637..f1606cb96 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am @@ -50,7 +50,6 @@ sgsn_test_LDADD = \ $(top_builddir)/src/gprs/sgsn_libgtp.o \ $(top_builddir)/src/gprs/sgsn_auth.o \ $(top_builddir)/src/gprs/sgsn_ares.o \ - $(top_builddir)/src/gprs/gprs_gsup_client.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/gprs_subscriber.o \ $(top_builddir)/src/gprs/gprs_gb_parse.o \ -- cgit v1.2.3