diff options
Diffstat (limited to 'src/gprs')
-rw-r--r-- | src/gprs/Makefile.am | 5 | ||||
-rw-r--r-- | src/gprs/gb_proxy.c | 3 | ||||
-rw-r--r-- | src/gprs/gb_proxy_main.c | 35 | ||||
-rw-r--r-- | src/gprs/gb_proxy_patch.c | 3 | ||||
-rw-r--r-- | src/gprs/gb_proxy_peer.c | 5 | ||||
-rw-r--r-- | src/gprs/gb_proxy_vty.c | 2 | ||||
-rw-r--r-- | src/gprs/gprs_gmm.c | 7 | ||||
-rw-r--r-- | src/gprs/gprs_llc.c | 3 | ||||
-rw-r--r-- | src/gprs/gprs_llc_parse.c | 1 | ||||
-rw-r--r-- | src/gprs/gprs_llc_vty.c | 1 | ||||
-rw-r--r-- | src/gprs/gprs_sgsn.c | 2 | ||||
-rw-r--r-- | src/gprs/gprs_sndcp.c | 1 | ||||
-rw-r--r-- | src/gprs/gprs_sndcp_vty.c | 1 | ||||
-rw-r--r-- | src/gprs/gsup_client.c | 355 | ||||
-rw-r--r-- | src/gprs/gtphub_main.c | 35 | ||||
-rw-r--r-- | src/gprs/gtphub_vty.c | 1 | ||||
-rw-r--r-- | src/gprs/oap_client.c | 280 | ||||
-rw-r--r-- | src/gprs/sgsn_ares.c | 2 | ||||
-rw-r--r-- | src/gprs/sgsn_ctrl.c | 1 | ||||
-rw-r--r-- | src/gprs/sgsn_main.c | 54 | ||||
-rw-r--r-- | src/gprs/sgsn_vty.c | 2 |
21 files changed, 759 insertions, 40 deletions
diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am index 39a4c12a7..654604b80 100644 --- a/src/gprs/Makefile.am +++ b/src/gprs/Makefile.am @@ -62,7 +62,6 @@ osmo_gbproxy_SOURCES = \ gprs_utils.c \ $(NULL) osmo_gbproxy_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) \ $(LIBCRYPTO_LIBS) \ -lrt \ @@ -93,9 +92,10 @@ osmo_sgsn_SOURCES = \ slhc.c \ gprs_llc_xid.c \ v42bis.c \ + gsup_client.c \ + oap_client.c \ $(NULL) osmo_sgsn_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBCARES_LIBS) \ @@ -122,7 +122,6 @@ osmo_gtphub_SOURCES = \ gprs_utils.c \ $(NULL) osmo_gtphub_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ diff --git a/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c index cd38d23bf..d288cb314 100644 --- a/src/gprs/gb_proxy.c +++ b/src/gprs/gb_proxy.c @@ -47,12 +47,13 @@ #include <openbsc/gb_proxy.h> #include <openbsc/gprs_llc.h> -#include <openbsc/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <openbsc/gprs_utils.h> #include <openssl/rand.h> +extern void *tall_bsc_ctx; + static const struct rate_ctr_desc global_ctr_description[] = { { "inv-bvci", "Invalid BVC Identifier " }, { "inv-lai", "Invalid Location Area Identifier" }, diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c index caff27f6f..853a763a2 100644 --- a/src/gprs/gb_proxy_main.c +++ b/src/gprs/gb_proxy_main.c @@ -190,13 +190,39 @@ static void handle_options(int argc, char **argv) } } -extern int bsc_vty_go_parent(struct vty *vty); +int gbproxy_vty_is_config_node(struct vty *vty, int node) +{ + switch (node) { + /* add items that are not config */ + case CONFIG_NODE: + return 0; + + default: + return 1; + } +} + +int gbproxy_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case GBPROXY_NODE: + default: + if (gbproxy_vty_is_config_node(vty, vty->node)) + vty->node = CONFIG_NODE; + else + vty->node = ENABLE_NODE; + + vty->index = NULL; + } + + return vty->node; +} static struct vty_app_info vty_info = { .name = "OsmoGbProxy", .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, + .go_parent_cb = gbproxy_vty_go_parent, + .is_config_node = gbproxy_vty_is_config_node, }; /* default categories */ @@ -226,7 +252,6 @@ static const struct log_info gprs_log_info = { int main(int argc, char **argv) { - struct gsm_network dummy_network; int rc; tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); @@ -271,7 +296,7 @@ int main(int argc, char **argv) } /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network, + rc = telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); if (rc < 0) exit(1); diff --git a/src/gprs/gb_proxy_patch.c b/src/gprs/gb_proxy_patch.c index 210fb2b96..bee315036 100644 --- a/src/gprs/gb_proxy_patch.c +++ b/src/gprs/gb_proxy_patch.c @@ -23,13 +23,14 @@ #include <openbsc/gprs_utils.h> #include <openbsc/gprs_gb_parse.h> -#include <openbsc/gsm_data.h> #include <openbsc/debug.h> #include <osmocom/gprs/protocol/gsm_08_18.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/gsm/apn.h> +extern void *tall_bsc_ctx; + /* patch RA identifier in place */ static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, int to_bss, const char *log_text) diff --git a/src/gprs/gb_proxy_peer.c b/src/gprs/gb_proxy_peer.c index 890968717..a83630bc1 100644 --- a/src/gprs/gb_proxy_peer.c +++ b/src/gprs/gb_proxy_peer.c @@ -22,17 +22,18 @@ #include <openbsc/gb_proxy.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_data_shared.h> #include <openbsc/debug.h> #include <osmocom/gprs/protocol/gsm_08_18.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/stats.h> #include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> #include <string.h> +extern void *tall_bsc_ctx; + static const struct rate_ctr_desc peer_ctr_description[] = { { "blocked", "BVC Block " }, { "unblocked", "BVC Unblock " }, diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c index 86d65a8e3..bd5bb1b5b 100644 --- a/src/gprs/gb_proxy_vty.c +++ b/src/gprs/gb_proxy_vty.c @@ -26,8 +26,8 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/gsm48.h> -#include <openbsc/gsm_04_08.h> #include <osmocom/gprs/gprs_ns.h> #include <osmocom/gsm/apn.h> diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c index 032137f0b..7301bf1c2 100644 --- a/src/gprs/gprs_gmm.c +++ b/src/gprs/gprs_gmm.c @@ -35,7 +35,6 @@ #include "bscconfig.h" -#include <openbsc/db.h> #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> #include <osmocom/gsm/gsm_utils.h> @@ -56,11 +55,6 @@ #endif #include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/paging.h> -#include <openbsc/transaction.h> #include <openbsc/gprs_llc.h> #include <openbsc/gprs_sgsn.h> #include <openbsc/gprs_gmm.h> @@ -75,6 +69,7 @@ #define PTMSI_ALLOC extern struct sgsn_instance *sgsn; +extern void *tall_bsc_ctx; static const struct tlv_definition gsm48_gmm_att_tlvdef = { .def = { diff --git a/src/gprs/gprs_llc.c b/src/gprs/gprs_llc.c index 2be663f98..904ec7e07 100644 --- a/src/gprs/gprs_llc.c +++ b/src/gprs/gprs_llc.c @@ -31,15 +31,14 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/gprs/gprs_bssgp.h> +#include <osmocom/gsm/gsm_utils.h> -#include <openbsc/gsm_data.h> #include <openbsc/debug.h> #include <openbsc/gprs_sgsn.h> #include <openbsc/gprs_gmm.h> #include <openbsc/gprs_llc.h> #include <openbsc/crc24.h> #include <openbsc/sgsn.h> -#include <openbsc/gsm_subscriber.h> #include <openbsc/gprs_llc_xid.h> #include <openbsc/gprs_sndcp_comp.h> #include <openbsc/gprs_sndcp.h> diff --git a/src/gprs/gprs_llc_parse.c b/src/gprs/gprs_llc_parse.c index a5a7a7122..be634974a 100644 --- a/src/gprs/gprs_llc_parse.c +++ b/src/gprs/gprs_llc_parse.c @@ -28,7 +28,6 @@ #include <osmocom/core/talloc.h> #include <osmocom/gprs/gprs_bssgp.h> -#include <openbsc/gsm_data.h> #include <openbsc/debug.h> #include <openbsc/gprs_sgsn.h> #include <openbsc/gprs_gmm.h> diff --git a/src/gprs/gprs_llc_vty.c b/src/gprs/gprs_llc_vty.c index bf34e9782..626d6ef1b 100644 --- a/src/gprs/gprs_llc_vty.c +++ b/src/gprs/gprs_llc_vty.c @@ -27,7 +27,6 @@ #include <arpa/inet.h> -#include <openbsc/gsm_data.h> #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> #include <osmocom/core/talloc.h> diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c index de79afb1a..560485de5 100644 --- a/src/gprs/gprs_sgsn.c +++ b/src/gprs/gprs_sgsn.c @@ -31,6 +31,7 @@ #include <osmocom/gprs/gprs_bssgp.h> #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> #include <osmocom/gsm/apn.h> +#include <osmocom/gsm/gsm_utils.h> #include <openbsc/gprs_subscriber.h> #include <openbsc/debug.h> @@ -56,6 +57,7 @@ #define GPRS_LLME_CHECK_TICK 30 extern struct sgsn_instance *sgsn; +extern void *tall_bsc_ctx; LLIST_HEAD(sgsn_mm_ctxts); LLIST_HEAD(sgsn_ggsn_ctxts); diff --git a/src/gprs/gprs_sndcp.c b/src/gprs/gprs_sndcp.c index a18998f9e..05dad6603 100644 --- a/src/gprs/gprs_sndcp.c +++ b/src/gprs/gprs_sndcp.c @@ -30,7 +30,6 @@ #include <osmocom/core/talloc.h> #include <osmocom/gprs/gprs_bssgp.h> -#include <openbsc/gsm_data.h> #include <openbsc/debug.h> #include <openbsc/gprs_llc.h> #include <openbsc/sgsn.h> diff --git a/src/gprs/gprs_sndcp_vty.c b/src/gprs/gprs_sndcp_vty.c index 430881fc8..4bad8b040 100644 --- a/src/gprs/gprs_sndcp_vty.c +++ b/src/gprs/gprs_sndcp_vty.c @@ -26,7 +26,6 @@ #include <arpa/inet.h> -#include <openbsc/gsm_data.h> #include <osmocom/core/msgb.h> #include <osmocom/gsm/tlv.h> #include <osmocom/core/talloc.h> diff --git a/src/gprs/gsup_client.c b/src/gprs/gsup_client.c new file mode 100644 index 000000000..258f230cb --- /dev/null +++ b/src/gprs/gsup_client.c @@ -0,0 +1,355 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/gsup_client.h> + +#include <osmocom/abis/ipa.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> + +#include <openbsc/debug.h> + +#include <errno.h> +#include <string.h> + +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_NOTICE, "GSUP connecting to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + return 0; + } + + LOGP(DLGSUP, LOGL_ERROR, "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; + + /* If the oap_state is disabled, this will reject the messages. */ + 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; + struct ipaccess_unit ipa_dev = { + /* see gsup_client_create() on const vs non-const */ + .unit_name = (char*)gsupc->unit_name, + }; + + OSMO_ASSERT(ipa_dev.unit_name); + + 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) +{ + osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); + + 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 *unit_name, + const char *ip_addr, + unsigned int tcp_port, + gsup_client_read_cb_t read_cb, + struct oap_client_config *oapc_config) +{ + struct gsup_client *gsupc; + int rc; + + gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); + OSMO_ASSERT(gsupc); + + /* struct ipaccess_unit has a non-const unit_name, so let's copy to be + * able to have a non-const unit_name here as well. To not taint the + * public gsup_client API, let's store it in a const char* anyway. */ + gsupc->unit_name = talloc_strdup(gsupc, unit_name); + OSMO_ASSERT(gsupc->unit_name); + + /* a NULL oapc_config will mark oap_state disabled. */ + rc = oap_client_init(oapc_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; + + osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); + + 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) { + LOGP(DGPRS, LOGL_NOTICE, "No GSUP client, unable to " + "send %s\n", msgb_hexdump(msg)); + msgb_free(msg); + return -ENOTCONN; + } + + if (!gsupc->is_connected) { + LOGP(DGPRS, LOGL_NOTICE, "GSUP not connected, unable to " + "send %s\n", msgb_hexdump(msg)); + 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/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c index 2b87d19ef..d7b3ba74b 100644 --- a/src/gprs/gtphub_main.c +++ b/src/gprs/gtphub_main.c @@ -39,6 +39,8 @@ #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/ports.h> +#include <osmocom/sigtran/osmo_ss7.h> + #include <openbsc/debug.h> #include <openbsc/gtphub.h> #include <openbsc/vty.h> @@ -46,7 +48,7 @@ #include "../../bscconfig.h" extern void *osmo_gtphub_ctx; - +void *tall_bsc_ctx; const char *gtphub_copyright = "Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n" @@ -113,13 +115,38 @@ static void signal_handler(int signal) } } -extern int bsc_vty_go_parent(struct vty *vty); +int gtphub_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + default: + osmo_ss7_vty_go_parent(vty); + } + + return vty->node; +} + +int gtphub_vty_is_config_node(struct vty *vty, int node) +{ + /* Check if libosmo-sccp declares the node in + * question as config node */ + if (osmo_ss7_is_config_node(vty, node)) + return 1; + + switch (node) { + /* add items that are not config */ + case CONFIG_NODE: + return 0; + + default: + return 1; + } +} static struct vty_app_info vty_info = { .name = "OsmoGTPhub", .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, + .go_parent_cb = gtphub_vty_go_parent, + .is_config_node = gtphub_vty_is_config_node, }; struct cmdline_cfg { diff --git a/src/gprs/gtphub_vty.c b/src/gprs/gtphub_vty.c index a30ad2a54..d611b1f9a 100644 --- a/src/gprs/gtphub_vty.c +++ b/src/gprs/gtphub_vty.c @@ -37,6 +37,7 @@ * globals. */ #include <openbsc/sgsn.h> extern struct sgsn_instance *sgsn; +extern void *tall_bsc_ctx; static struct gtphub *g_hub = 0; static struct gtphub_cfg *g_cfg = 0; diff --git a/src/gprs/oap_client.c b/src/gprs/oap_client.c new file mode 100644 index 000000000..5128ac119 --- /dev/null +++ b/src/gprs/oap_client.c @@ -0,0 +1,280 @@ +/* Osmocom Authentication Protocol API */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/gsm/oap.h> + +#include <openbsc/oap_client.h> +#include <openbsc/debug.h> + +int oap_client_init(struct oap_client_config *config, + struct oap_client_state *state) +{ + OSMO_ASSERT(state->state == OAP_UNINITIALIZED); + + if (!config) + goto disable; + + if (config->client_id == 0) + goto disable; + + if (config->secret_k_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); + goto disable; + } + + if (config->secret_opc_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); + goto disable; + } + + state->client_id = config->client_id; + memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); + memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); + state->state = OAP_INITIALIZED; + return 0; + +disable: + state->state = OAP_DISABLED; + return 0; +} + +/* From the given state and received RAND and AUTN octets, validate the + * server's authenticity and formulate the matching milenage reply octets in + * *tx_xres. The state is not modified. + * On success, and if tx_res is not NULL, exactly 8 octets will be written to + * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 + * octets. The caller will want to send XRES back to the server in a challenge + * response message and update the state. + * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail + * the authentication check; -3 for any other errors. */ +static int oap_evaluate_challenge(const struct oap_client_state *state, + const uint8_t *rx_random, + const uint8_t *rx_autn, + uint8_t *tx_xres) +{ + struct osmo_auth_vector vec; + + struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_UMTS, + .algo = OSMO_AUTH_ALG_MILENAGE, + }; + + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) + == sizeof(state->secret_k), _secret_k_size_match); + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) + == sizeof(state->secret_opc), _secret_opc_size_match); + + switch (state->state) { + case OAP_UNINITIALIZED: + case OAP_DISABLED: + return -1; + default: + break; + } + + memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); + memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); + memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); + auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ + + memset(&vec, 0, sizeof(vec)); + osmo_auth_gen_vec(&vec, &auth, rx_random); + + if (vec.res_len != 8) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", + vec.res_len); + return -3; + } + + if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { + LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", + osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", + osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); + return -2; + } + + if (tx_xres != NULL) + memcpy(tx_xres, vec.res, 8); + return 0; +} + +struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg) +{ + struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); + OSMO_ASSERT(msg); + osmo_oap_encode(msg, oap_msg); + return msg; +} + +/* Create a new msgb containing an OAP registration message. + * On error, return NULL. */ +static struct msgb* oap_msg_register(uint16_t client_id) +{ + struct osmo_oap_message oap_msg = {0}; + + if (client_id < 1) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); + return NULL; + } + + oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; + oap_msg.client_id = client_id; + return oap_client_encoded(&oap_msg); +} + +int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx) +{ + *msg_tx = oap_msg_register(state->client_id); + if (!(*msg_tx)) + return -1; + + state->state = OAP_REQUESTED_CHALLENGE; + return 0; +} + +/* Create a new msgb containing an OAP challenge response message. + * xres must point at 8 octets to return as challenge response. + * On error, return NULL. */ +static struct msgb* oap_msg_challenge_response(uint8_t *xres) +{ + struct osmo_oap_message oap_reply = {0}; + + oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; + memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); + oap_reply.xres_present = 1; + return oap_client_encoded(&oap_reply); +} + +static int handle_challenge(struct oap_client_state *state, + struct osmo_oap_message *oap_rx, + struct msgb **msg_tx) +{ + int rc; + uint8_t xres[8]; + + if (!(oap_rx->rand_present && oap_rx->autn_present)) { + LOGP(DLOAP, LOGL_ERROR, + "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", + oap_rx->rand_present, oap_rx->autn_present); + rc = -2; + goto failure; + } + + rc = oap_evaluate_challenge(state, + oap_rx->rand, + oap_rx->autn, + xres); + if (rc < 0) + goto failure; + + *msg_tx = oap_msg_challenge_response(xres); + if ((*msg_tx) == NULL) { + rc = -1; + goto failure; + } + + state->state = OAP_SENT_CHALLENGE_RESULT; + return 0; + +failure: + OSMO_ASSERT(rc < 0); + state->state = OAP_INITIALIZED; + return rc; +} + +int oap_client_handle(struct oap_client_state *state, + const struct msgb *msg_rx, struct msgb **msg_tx) +{ + uint8_t *data = msgb_l2(msg_rx); + size_t data_len = msgb_l2len(msg_rx); + struct osmo_oap_message oap_msg = {0}; + int rc = 0; + + *msg_tx = NULL; + + OSMO_ASSERT(data); + + rc = osmo_oap_decode(&oap_msg, data, data_len); + if (rc < 0) { + LOGP(DLOAP, LOGL_ERROR, + "Decoding OAP message failed with error '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, -rc), -rc); + return -10; + } + + switch (state->state) { + case OAP_UNINITIALIZED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " not initialized\n", oap_msg.message_type); + return -ENOTCONN; + case OAP_DISABLED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " disabled\n", oap_msg.message_type); + return -ENOTCONN; + default: + break; + } + + switch (oap_msg.message_type) { + case OAP_MSGT_CHALLENGE_REQUEST: + return handle_challenge(state, &oap_msg, msg_tx); + + case OAP_MSGT_REGISTER_RESULT: + /* successfully registered */ + state->state = OAP_REGISTERED; + break; + + case OAP_MSGT_REGISTER_ERROR: + LOGP(DLOAP, LOGL_ERROR, + "OAP registration failed\n"); + state->state = OAP_INITIALIZED; + if (state->registration_failures < 3) { + state->registration_failures ++; + return oap_client_register(state, msg_tx); + } + return -11; + + case OAP_MSGT_REGISTER_REQUEST: + case OAP_MSGT_CHALLENGE_RESULT: + LOGP(DLOAP, LOGL_ERROR, + "Received invalid OAP message type for OAP client side: %d\n", + (int)oap_msg.message_type); + return -12; + + default: + LOGP(DLOAP, LOGL_ERROR, + "Unknown OAP message type: %d\n", + (int)oap_msg.message_type); + return -13; + } + + return 0; +} diff --git a/src/gprs/sgsn_ares.c b/src/gprs/sgsn_ares.c index d94d184a3..623809911 100644 --- a/src/gprs/sgsn_ares.c +++ b/src/gprs/sgsn_ares.c @@ -24,6 +24,8 @@ #include <netdb.h> +extern void *tall_bsc_ctx; + struct cares_event_fd { struct llist_head head; struct osmo_fd fd; diff --git a/src/gprs/sgsn_ctrl.c b/src/gprs/sgsn_ctrl.c index 31ac74f1f..f7b1180be 100644 --- a/src/gprs/sgsn_ctrl.c +++ b/src/gprs/sgsn_ctrl.c @@ -21,7 +21,6 @@ #include <osmocom/ctrl/control_if.h> #include <osmocom/ctrl/control_cmd.h> -#include <openbsc/gsm_data.h> #include <openbsc/gprs_sgsn.h> #include <openbsc/sgsn.h> #include <openbsc/debug.h> diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c index 25ee632cc..e24a57ba2 100644 --- a/src/gprs/sgsn_main.c +++ b/src/gprs/sgsn_main.c @@ -62,13 +62,13 @@ #include <osmocom/ctrl/control_if.h> #include <osmocom/ctrl/ports.h> -#include <osmocom/sigtran/protocol/m3ua.h> - #include <gtp.h> #include "../../bscconfig.h" #if BUILD_IU +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/protocol/m3ua.h> #include <osmocom/ranap/iu_client.h> #endif @@ -173,13 +173,40 @@ static void signal_handler(int signal) /* NSI that BSSGP uses when transmitting on NS */ extern struct gprs_ns_inst *bssgp_nsi; -extern int bsc_vty_go_parent(struct vty *vty); +int sgsn_vty_is_config_node(struct vty *vty, int node) +{ + /* So far the SGSN has no nested nodes that need parent node + * declaration, except for the ss7 vty nodes. */ + switch (node) { + case SGSN_NODE: + return 1; + default: +#if BUILD_IU + return osmo_ss7_is_config_node(vty, node); +#else + return 0; +#endif + } +} + +int sgsn_vty_go_parent(struct vty *vty) +{ + /* So far the SGSN has no nested nodes that need parent node + * declaration, except for the ss7 vty nodes. */ +#if BUILD_IU + return osmo_ss7_vty_go_parent(vty); +#else + vty->node = CONFIG_NODE; + vty->index = NULL; + return 0; +#endif +} static struct vty_app_info vty_info = { .name = "OsmoSGSN", .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, + .go_parent_cb = sgsn_vty_go_parent, + .is_config_node = sgsn_vty_is_config_node, }; static void print_help(void) @@ -325,14 +352,17 @@ static const struct log_info gprs_log_info = { .num_cat = ARRAY_SIZE(gprs_categories), }; -int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data); +#if BUILD_IU +int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data); +#endif int main(int argc, char **argv) { struct ctrl_handle *ctrl; - struct gsm_network dummy_network; - struct osmo_sccp_instance *sccp; int rc; +#if BUILD_IU + struct osmo_sccp_instance *sccp; +#endif srand(time(NULL)); tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); @@ -354,7 +384,11 @@ int main(int argc, char **argv) osmo_stats_vty_add_cmds(&gprs_log_info); sgsn_vty_init(&sgsn_inst.cfg); ctrl_vty_init(tall_bsc_ctx); + +#if BUILD_IU osmo_ss7_init(); + osmo_ss7_vty_init_asp(tall_bsc_ctx); +#endif handle_options(argc, argv); @@ -389,7 +423,7 @@ int main(int argc, char **argv) } /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network, + rc = telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_SGSN); if (rc < 0) exit(1); @@ -442,7 +476,7 @@ int main(int argc, char **argv) } } -#ifdef BUILD_IU +#if BUILD_IU sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN", 2 /* FIXME: configurable */, OSMO_SS7_ASP_PROT_M3UA, 0, diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c index 3a5b2ca64..fce251885 100644 --- a/src/gprs/sgsn_vty.c +++ b/src/gprs/sgsn_vty.c @@ -51,6 +51,8 @@ #include <osmocom/ranap/iu_client.h> #endif +extern void *tall_bsc_ctx; + static struct sgsn_config *g_cfg = NULL; const struct value_string sgsn_auth_pol_strs[] = { |