Sponsored-by: On-Waves ehf --- openbsc/doc/osmocom-authn-protocol.txt | 191 +++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 openbsc/doc/osmocom-authn-protocol.txt
diff --git a/openbsc/doc/osmocom-authn-protocol.txt b/openbsc/doc/osmocom-authn-protocol.txt new file mode 100644 index 0000000..660fdb6 --- /dev/null +++ b/openbsc/doc/osmocom-authn-protocol.txt @@ -0,0 +1,191 @@ + + Osmocom Authentication Protocol (OAP) + +1. General + +This document describes the remote protocol that is used by the SGSN and MAP +proxy to authenticate each other. The protocol and the messages are designed +after the corresponding MAP messages (see GSM 09.02) with the following +differences: + + - The encoding uses TLV structures instead of ASN.1 encodings + - Segmentation is not used + +See the specification of the Gr interface (GSM 03.60). + +1.1. Connection + +The protocol expects that a reliable, ordered, packet boundaries preserving +connection is used (e.g. IPA over TCP). The remote peer is either a service +that understands the protocol natively or a wrapper service that maps the +messages to/from real MAP messages that can be used to directly communicate +with an HLR. + +1.2. Using IPA + +By default, the following identifiers should be used: + - IPA protocol: 0xee (OSMO) + - IPA OSMO protocol extension: 0x06 + +2. Procedures + +Ideal communication sequence: + + SGSN MAP + | | + | Register (Id) | + |----------------------------------->| + | | + | Challenge (RAND+AUTN) | + |<-----------------------------------| + | | + | Challenge Result (SRES) | + |----------------------------------->| + | | + | Register Result | + |<-----------------------------------| + +2.1. Register + +The SGSN sends a REGISTER_REQ message containing an SGSN identifier number. + +2.2. Challenge + +The OAP server (optionally) sends a CHALLENGE_REQ to the SGSN, containing +random bytes and a milenage authentication token generated from these random +bytes, using a shared secret, to authenticate itself to the OAP client (SGSN). +The server may omit this challenge entirely, based on its configuration, and +immediately reply with a Register Result response. If the SGSN cannot be +registered (e.g. id is invalid), the server sends a REGISTER_ERR response. + +2.3. Challenge Result + +When the SGSN has received a Challenge, it may verify the server's +authenticity, and reply with a CHALLENGE_RES message. This shall contain SRES +(and Kc?) authentication tokens generated by milenage from the same random +bytes received from the server and the same shared secet. If the SGSN cannot +verify the server's authenticity, it shall instead send a CHALLENGE_ERR +message. + +2.4. Register Result + +The MAP sends a REGISTER_RES message to indicate that registration has been +successful. If the MAP proxy cannot register the SGSN (e.g. invalid challenge +response), it sends a REGISTER_ERR message. + +3. Message Format + +3.1. General + +Every message is based on the following message format + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + +The receiver shall be able to receive IEs in any order. Unknown IEs shall be +ignored. + +3.2.1. Register Request + +SGSN -> Network peer + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + 30 SGSN Id big endian int (2 oct) M TLV 4 + +3.2.2. Register Error + +Network peer -> SGSN + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + 02 Cause GMM cause, M TLV 3 + 04.08: 10.5.5.14 + +3.2.6. Register Result + +Network peer -> SGSN + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + +3.2.3. Challenge + +Network peer -> SGSN + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + 20 RAND octet string (16) M TLV 18 + 23 AUTN octet string (16) M TLV 18 + +3.2.4. Challenge Error + +SGSN -> Network peer + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + 02 Cause GMM cause, M TLV 3 + 04.08: 10.5.5.14 + +3.2.5. Challenge Result + +SGSN -> Network peer + + IEI Info Element Type Pres. Format Length + Message type 4.2.1 M V 1 + 21 SRES octet string (4) M TLV 6 + 22 Kc octet string (8) M TLV 10 + +4. Information Elements + +4.1. General + +[...] + +4.2.1. Message Type + + +---------------------------------------------------+ + | 8 7 6 5 4 3 2 1 | + | | + | 0 0 0 0 0 1 0 0 - Register Request | + | 0 0 0 0 0 1 0 1 - Register Error | + | 0 0 0 0 0 1 1 0 - Register Result | + | | + | 0 0 0 0 1 0 0 0 - Challenge Request | + | 0 0 0 0 1 0 0 1 - Challenge Error | + | 0 0 0 0 1 0 1 0 - Challenge Result | + | | + +---------------------------------------------------+ + +4.2.2. IE Identifier (informational) + +These are the standard values for the IEI. + + +---------------------------------------------------------+ + | IEI Info Element Type | + | | + | 0x02 Cause GMM cause, 04.08: 10.5.5.14 | + | 0x20 RAND octet string | + | 0x21 SRES octet string | + | 0x22 Kc octet string | + | 0x23 AUTN octet string | + | 0x30 SGSN Id big endian int (2 octets) | + +---------------------------------------------------------+ + +4.2.3. SGSN Id + + 8 7 6 5 4 3 2 1 + +-----------------------------------------------------+ + | | SGSN Id IEI | octet 1 + +-----------------------------------------------------+ + | Length of SGSN Id IE contents (2) | octet 2 + +-----------------------------------------------------+ + | SGSN Id number, most significant byte | octet 3 + +-----------------------------------------------------+ + | SGSN Id number, least significant byte | octet 4 + +-----------------------------------------------------+ + +The SGSN Id number shall be interpreted as an unsigned 16bit integer, where 0 +indicates an invalid / unset Id. + +
Rename gprs_gsup_client.* to ipa_client.*.
To ease diff reading for the upcoming commit, this commit performs a file rename only.
Background: currently, GSUP is the only protocol spoken on the IPA wire. We will now add OAP, a new protocol to register the SGSN with a MAP proxy. What has been only the GSUP client will mostly become the general IPA client.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/Makefile.am | 2 +- openbsc/include/openbsc/gprs_gsup_client.h | 54 ------ openbsc/include/openbsc/ipa_client.h | 54 ++++++ openbsc/src/gprs/Makefile.am | 2 +- openbsc/src/gprs/gprs_gsup_client.c | 288 ----------------------------- openbsc/src/gprs/gprs_subscriber.c | 2 +- openbsc/src/gprs/ipa_client.c | 288 +++++++++++++++++++++++++++++ openbsc/src/gprs/sgsn_vty.c | 2 +- openbsc/tests/sgsn/Makefile.am | 2 +- openbsc/tests/sgsn/sgsn_test.c | 2 +- 10 files changed, 348 insertions(+), 348 deletions(-) delete mode 100644 openbsc/include/openbsc/gprs_gsup_client.h create mode 100644 openbsc/include/openbsc/ipa_client.h delete mode 100644 openbsc/src/gprs/gprs_gsup_client.c create mode 100644 openbsc/src/gprs/ipa_client.c
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 254f43d..7bc9d95 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -16,7 +16,7 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ osmux.h mgcp_transcode.h gprs_utils.h \ gprs_gb_parse.h smpp.h meas_feed.h gprs_gsup_messages.h \ - gprs_gsup_client.h bsc_msg_filter.h + ipa_client.h bsc_msg_filter.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/gprs_gsup_client.h b/openbsc/include/openbsc/gprs_gsup_client.h deleted file mode 100644 index 9537db4..0000000 --- a/openbsc/include/openbsc/gprs_gsup_client.h +++ /dev/null @@ -1,54 +0,0 @@ -/* GPRS Subscriber Update Protocol client */ - -/* (C) 2014 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Jacob Erlbeck - * - * 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/. - * - */ -#pragma once - -#include <osmocom/core/timer.h> - -#define GPRS_GSUP_RECONNECT_INTERVAL 10 -#define GPRS_GSUP_PING_INTERVAL 20 - -struct msgb; -struct ipa_client_conn; -struct gprs_gsup_client; - -/* Expects message in msg->l2h */ -typedef int (*gprs_gsup_read_cb_t)(struct gprs_gsup_client *gsupc, struct msgb *msg); - -struct gprs_gsup_client { - struct ipa_client_conn *link; - gprs_gsup_read_cb_t read_cb; - void *data; - - struct osmo_timer_list ping_timer; - struct osmo_timer_list connect_timer; - int is_connected; - int got_ipa_pong; -}; - -struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gprs_gsup_read_cb_t read_cb); - -void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc); -int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); -struct msgb *gprs_gsup_msgb_alloc(void); - diff --git a/openbsc/include/openbsc/ipa_client.h b/openbsc/include/openbsc/ipa_client.h new file mode 100644 index 0000000..9537db4 --- /dev/null +++ b/openbsc/include/openbsc/ipa_client.h @@ -0,0 +1,54 @@ +/* GPRS Subscriber Update Protocol client */ + +/* (C) 2014 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Jacob Erlbeck + * + * 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/. + * + */ +#pragma once + +#include <osmocom/core/timer.h> + +#define GPRS_GSUP_RECONNECT_INTERVAL 10 +#define GPRS_GSUP_PING_INTERVAL 20 + +struct msgb; +struct ipa_client_conn; +struct gprs_gsup_client; + +/* Expects message in msg->l2h */ +typedef int (*gprs_gsup_read_cb_t)(struct gprs_gsup_client *gsupc, struct msgb *msg); + +struct gprs_gsup_client { + struct ipa_client_conn *link; + gprs_gsup_read_cb_t read_cb; + void *data; + + struct osmo_timer_list ping_timer; + struct osmo_timer_list connect_timer; + int is_connected; + int got_ipa_pong; +}; + +struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, + unsigned int tcp_port, + gprs_gsup_read_cb_t read_cb); + +void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc); +int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); +struct msgb *gprs_gsup_msgb_alloc(void); + diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index f46a402..b9c3070 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -26,7 +26,7 @@ osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ gprs_llc.c gprs_llc_parse.c gprs_llc_vty.c crc24.c \ sgsn_ctrl.c sgsn_auth.c gprs_subscriber.c \ - gprs_gsup_messages.c gprs_utils.c gprs_gsup_client.c \ + gprs_gsup_messages.c gprs_utils.c ipa_client.c \ gsm_04_08_gprs.c sgsn_cdr.c sgsn_ares.c osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ diff --git a/openbsc/src/gprs/gprs_gsup_client.c b/openbsc/src/gprs/gprs_gsup_client.c deleted file mode 100644 index 807b0c1..0000000 --- a/openbsc/src/gprs/gprs_gsup_client.c +++ /dev/null @@ -1,288 +0,0 @@ -/* GPRS Subscriber Update Protocol client */ - -/* (C) 2014 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Jacob Erlbeck - * - * 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/gprs_gsup_client.h> - -#include <osmocom/abis/ipa.h> -#include <osmocom/gsm/protocol/ipaccess.h> -#include <osmocom/core/msgb.h> - -#include <openbsc/debug.h> - -#include <errno.h> -#include <string.h> - -extern void *tall_bsc_ctx; - -static void start_test_procedure(struct gprs_gsup_client *gsupc); - -static void gsup_client_send_ping(struct gprs_gsup_client *gsupc) -{ - struct msgb *msg = gprs_gsup_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 gprs_gsup_client *gsupc) -{ - int rc; - - if (gsupc->is_connected) - return 0; - - if (osmo_timer_pending(&gsupc->connect_timer)) { - LOGP(DLINP, LOGL_DEBUG, - "GSUP connect: connect timer already running\n"); - osmo_timer_del(&gsupc->connect_timer); - } - - if (osmo_timer_pending(&gsupc->ping_timer)) { - LOGP(DLINP, 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(DLINP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); - - rc = ipa_client_conn_open(gsupc->link); - - if (rc >= 0) { - LOGP(DGPRS, LOGL_INFO, "GSUP connecting to %s:%d\n", - gsupc->link->addr, gsupc->link->port); - return 0; - } - - LOGP(DGPRS, 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, GPRS_GSUP_RECONNECT_INTERVAL, 0); - - LOGP(DGPRS, 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 gprs_gsup_client *gsupc = gsupc_; - - if (gsupc->is_connected) - return; - - gsup_client_connect(gsupc); -} - -static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) -{ - struct gprs_gsup_client *gsupc = link->data; - - LOGP(DGPRS, 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); - - osmo_timer_del(&gsupc->connect_timer); - } else { - osmo_timer_del(&gsupc->ping_timer); - - osmo_timer_schedule(&gsupc->connect_timer, - GPRS_GSUP_RECONNECT_INTERVAL, 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 gprs_gsup_client *gsupc = (struct gprs_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(DGPRS, 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(DGPRS, 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) || - he->proto != IPAC_PROTO_EXT_GSUP) - goto invalid; - - msg->l2h = &he->data[0]; - - OSMO_ASSERT(gsupc->read_cb != NULL); - gsupc->read_cb(gsupc, msg); - - /* Not freeing msg here, because that must be done by the read_cb. */ - return 0; - -invalid: - LOGP(DGPRS, 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 gprs_gsup_client *gsupc = gsupc_; - - LOGP(DGPRS, 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(DGPRS, 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 gprs_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, GPRS_GSUP_PING_INTERVAL, 0); - LOGP(DGPRS, LOGL_DEBUG, "GSUP sending PING\n"); - gsup_client_send_ping(gsupc); -} - -struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gprs_gsup_read_cb_t read_cb) -{ - struct gprs_gsup_client *gsupc; - int rc; - - gsupc = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); - OSMO_ASSERT(gsupc); - - 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: - gprs_gsup_client_destroy(gsupc); - return NULL; -} - -void gprs_gsup_client_destroy(struct gprs_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 gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) -{ - if (!gsupc) { - msgb_free(msg); - return -ENOTCONN; - } - - if (!gsupc->is_connected) { - msgb_free(msg); - return -EAGAIN; - } - - ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_GSUP); - ipa_msg_push_header(msg, IPAC_PROTO_OSMO); - ipa_client_conn_send(gsupc->link, msg); - - return 0; -} - -struct msgb *gprs_gsup_msgb_alloc(void) -{ - return msgb_alloc_headroom(4000, 64, __func__); -} diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c index 8231e8c..0a3fe19 100644 --- a/openbsc/src/gprs/gprs_subscriber.c +++ b/openbsc/src/gprs/gprs_subscriber.c @@ -21,7 +21,7 @@ */
#include <openbsc/gsm_subscriber.h> -#include <openbsc/gprs_gsup_client.h> +#include <openbsc/ipa_client.h>
#include <openbsc/sgsn.h> #include <openbsc/gprs_sgsn.h> diff --git a/openbsc/src/gprs/ipa_client.c b/openbsc/src/gprs/ipa_client.c new file mode 100644 index 0000000..bec33c4 --- /dev/null +++ b/openbsc/src/gprs/ipa_client.c @@ -0,0 +1,288 @@ +/* GPRS Subscriber Update Protocol client */ + +/* (C) 2014 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Jacob Erlbeck + * + * 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/ipa_client.h> + +#include <osmocom/abis/ipa.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/core/msgb.h> + +#include <openbsc/debug.h> + +#include <errno.h> +#include <string.h> + +extern void *tall_bsc_ctx; + +static void start_test_procedure(struct gprs_gsup_client *gsupc); + +static void gsup_client_send_ping(struct gprs_gsup_client *gsupc) +{ + struct msgb *msg = gprs_gsup_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 gprs_gsup_client *gsupc) +{ + int rc; + + if (gsupc->is_connected) + return 0; + + if (osmo_timer_pending(&gsupc->connect_timer)) { + LOGP(DLINP, LOGL_DEBUG, + "GSUP connect: connect timer already running\n"); + osmo_timer_del(&gsupc->connect_timer); + } + + if (osmo_timer_pending(&gsupc->ping_timer)) { + LOGP(DLINP, 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(DLINP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); + + rc = ipa_client_conn_open(gsupc->link); + + if (rc >= 0) { + LOGP(DGPRS, LOGL_INFO, "GSUP connecting to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + return 0; + } + + LOGP(DGPRS, 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, GPRS_GSUP_RECONNECT_INTERVAL, 0); + + LOGP(DGPRS, 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 gprs_gsup_client *gsupc = gsupc_; + + if (gsupc->is_connected) + return; + + gsup_client_connect(gsupc); +} + +static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) +{ + struct gprs_gsup_client *gsupc = link->data; + + LOGP(DGPRS, 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); + + osmo_timer_del(&gsupc->connect_timer); + } else { + osmo_timer_del(&gsupc->ping_timer); + + osmo_timer_schedule(&gsupc->connect_timer, + GPRS_GSUP_RECONNECT_INTERVAL, 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 gprs_gsup_client *gsupc = (struct gprs_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(DGPRS, 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(DGPRS, 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) || + he->proto != IPAC_PROTO_EXT_GSUP) + goto invalid; + + msg->l2h = &he->data[0]; + + OSMO_ASSERT(gsupc->read_cb != NULL); + gsupc->read_cb(gsupc, msg); + + /* Not freeing msg here, because that must be done by the read_cb. */ + return 0; + +invalid: + LOGP(DGPRS, 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 gprs_gsup_client *gsupc = gsupc_; + + LOGP(DGPRS, 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(DGPRS, 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 gprs_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, GPRS_GSUP_PING_INTERVAL, 0); + LOGP(DGPRS, LOGL_DEBUG, "GSUP sending PING\n"); + gsup_client_send_ping(gsupc); +} + +struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, + unsigned int tcp_port, + gprs_gsup_read_cb_t read_cb) +{ + struct gprs_gsup_client *gsupc; + int rc; + + gsupc = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); + OSMO_ASSERT(gsupc); + + 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: + gprs_gsup_client_destroy(gsupc); + return NULL; +} + +void gprs_gsup_client_destroy(struct gprs_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 gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) +{ + if (!gsupc) { + msgb_free(msg); + return -ENOTCONN; + } + + if (!gsupc->is_connected) { + msgb_free(msg); + return -EAGAIN; + } + + ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_GSUP); + ipa_msg_push_header(msg, IPAC_PROTO_OSMO); + ipa_client_conn_send(gsupc->link, msg); + + return 0; +} + +struct msgb *gprs_gsup_msgb_alloc(void) +{ + return msgb_alloc_headroom(4000, 64, __func__); +} diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index b74d01a..00a930f 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -34,7 +34,7 @@ #include <openbsc/gprs_sgsn.h> #include <openbsc/vty.h> #include <openbsc/gsm_04_08_gprs.h> -#include <openbsc/gprs_gsup_client.h> +#include <openbsc/ipa_client.h>
#include <osmocom/vty/command.h> #include <osmocom/vty/vty.h> diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index 3c202dd..ea29fce 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am @@ -24,7 +24,7 @@ sgsn_test_LDADD = \ $(top_builddir)/src/gprs/sgsn_auth.o \ $(top_builddir)/src/gprs/sgsn_ares.o \ $(top_builddir)/src/gprs/gprs_gsup_messages.o \ - $(top_builddir)/src/gprs/gprs_gsup_client.o \ + $(top_builddir)/src/gprs/ipa_client.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/gprs_subscriber.o \ $(top_builddir)/src/gprs/gsm_04_08_gprs.o \ diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c index 7e5ab1a..251772e 100644 --- a/openbsc/tests/sgsn/sgsn_test.c +++ b/openbsc/tests/sgsn/sgsn_test.c @@ -25,7 +25,7 @@ #include <openbsc/debug.h> #include <openbsc/gsm_subscriber.h> #include <openbsc/gprs_gsup_messages.h> -#include <openbsc/gprs_gsup_client.h> +#include <openbsc/ipa_client.h> #include <openbsc/gprs_utils.h>
#include <osmocom/gprs/gprs_bssgp.h>
On 24 Sep 2015, at 13:44, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
Rename gprs_gsup_client.* to ipa_client.*.
To ease diff reading for the upcoming commit, this commit performs a file rename only.
Okay, I should have looked before. Could you re-send this one with -M in the command line? But yes, it is good to just do the rename!
Needs a new ipaccess_proto_ext enum value IPAC_PROTO_EXT_OAP from libosmocore, added in libosmocore.git:5eeb17a0178a72d291cb99f2391d8ea7e9b65dd4.
Implement the Osmocom Authentication Protocol, to allow an SGSN to register with an IPA peer. The aim is to allow multiple SGSNs talking to a single MAP proxy.
Have this API separation: - ipa_client_conn provides a bare connection (unchanged). - ipa_client provides general IPA connection verification using timeouts and an IPA ping/pong. - gprs_ipa_client muxes an ipa_client to the GSUP and OAP APIs.
While ipa_client_conn and ipa_client above are very general, gprs_ipa_client is a specific use "with real data".
ipa_client has previously been gprs_gsup_client. Remove GSUP specifics, change naming and log output to say "IPA" instead. (A previous commit has already renamed the gprs_gsup_client files to make this commit easier to read.)
Add gprs_ipa_client to soak up the GSUP specifics from ipa_client (basically just the protocol numbers). Also soak up gprs_subscr_init() and gsup_read_cb() from gprs_subscriber.c. And, of course, apply the OAP API.
Add gprs_oap_messages.{h,c} and gprs_oap.{h,c} to implement the OAP protocol. Add a gprs_oap_state field to (new) gprs_gsup_client.
Add a gprs_oap_config field to sgsn_config. Rename sgsn_config.gsup_server_* to ipa_server_*. Apply this in sgsn_vty.c.
Change from gprs_gsup_client to the new gprs_ipa_client API in - gprs_subscriber.c - sgsn_main.c (gprs_subscr_init() has become gprs_ipa_client_init())
Move some static functions to gprs_utils.h to avoid code duplication, I hope the location is a sufficiently good choice: - constant_time_cmp() from bsc_nat.c for gprs_oap_evaluate_challenge(), now called gprs_constant_time_cmp(). - encode_big_endian() and decode_big_endian() from gprs_gsup_messages.c for gprs_oap_decode() and gprs_oap_encode(), now called gprs_encode_big_endian() and gps_decode_big_endian(). Apply the function renames in the mentioned .c files.
Add OAP unit tests to sgsn_test.c: test_oap() and test_sgsn_registration(). Update sgsn_test.ok accordingly.
Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/Makefile.am | 8 +- openbsc/include/openbsc/gprs_ipa_client.h | 53 ++++ openbsc/include/openbsc/gprs_oap.h | 65 +++++ openbsc/include/openbsc/gprs_oap_messages.h | 70 ++++++ openbsc/include/openbsc/gprs_utils.h | 5 + openbsc/include/openbsc/ipa_client.h | 43 ++-- openbsc/include/openbsc/sgsn.h | 13 +- openbsc/src/gprs/Makefile.am | 6 +- openbsc/src/gprs/gprs_gsup_messages.c | 44 +--- openbsc/src/gprs/gprs_ipa_client.c | 159 ++++++++++++ openbsc/src/gprs/gprs_oap.c | 207 ++++++++++++++++ openbsc/src/gprs/gprs_oap_messages.c | 179 ++++++++++++++ openbsc/src/gprs/gprs_subscriber.c | 43 +--- openbsc/src/gprs/gprs_utils.c | 39 +++ openbsc/src/gprs/ipa_client.c | 233 +++++++++--------- openbsc/src/gprs/sgsn_main.c | 5 +- openbsc/src/gprs/sgsn_vty.c | 49 ++-- openbsc/src/osmo-bsc_nat/bsc_nat.c | 16 +- openbsc/tests/sgsn/Makefile.am | 6 +- openbsc/tests/sgsn/sgsn_test.c | 361 ++++++++++++++++++++++++++-- openbsc/tests/sgsn/sgsn_test.ok | 5 + 21 files changed, 1343 insertions(+), 266 deletions(-) create mode 100644 openbsc/include/openbsc/gprs_ipa_client.h create mode 100644 openbsc/include/openbsc/gprs_oap.h create mode 100644 openbsc/include/openbsc/gprs_oap_messages.h create mode 100644 openbsc/src/gprs/gprs_ipa_client.c create mode 100644 openbsc/src/gprs/gprs_oap.c create mode 100644 openbsc/src/gprs/gprs_oap_messages.c
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 7bc9d95..492f6e2 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -15,8 +15,12 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ bss.h gsm_data_shared.h ipaccess.h mncc_int.h \ arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ osmux.h mgcp_transcode.h gprs_utils.h \ - gprs_gb_parse.h smpp.h meas_feed.h gprs_gsup_messages.h \ - ipa_client.h bsc_msg_filter.h + gprs_gb_parse.h smpp.h meas_feed.h \ + bsc_msg_filter.h \ + ipa_client.h \ + gprs_ipa_client.h \ + gprs_gsup_messages.h gprs_oap_messages.h \ + gprs_oap.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/gprs_ipa_client.h b/openbsc/include/openbsc/gprs_ipa_client.h new file mode 100644 index 0000000..068d1a1 --- /dev/null +++ b/openbsc/include/openbsc/gprs_ipa_client.h @@ -0,0 +1,53 @@ +/* Specific IPA client for GPRS: Multiplex for GSUP and OAP */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Jacob Erlbeck, 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/. + * + */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/timer.h> + +#include <openbsc/ipa_client.h> +#include <openbsc/gprs_oap.h> + +struct sgsn_instance; + +int gprs_ipa_client_init(struct sgsn_instance *sgsn_inst); + + +struct gprs_ipa_client { + struct ipa_client *ipac; + + // sgsn <-> map proxy registration state + struct gprs_oap_state oap; + + // TODO registration timeout? +}; + +struct gprs_ipa_client *gprs_ipa_client_create(const char *ip_addr, + unsigned int tcp_port); + +int gprs_ipa_client_send_gsup(struct gprs_ipa_client *gipac, struct msgb *msg); +int gprs_ipa_client_send_oap(struct gprs_ipa_client *gipac, struct msgb *msg); + +void gprs_ipa_client_destroy(struct gprs_ipa_client *gipac); + + diff --git a/openbsc/include/openbsc/gprs_oap.h b/openbsc/include/openbsc/gprs_oap.h new file mode 100644 index 0000000..9972a81 --- /dev/null +++ b/openbsc/include/openbsc/gprs_oap.h @@ -0,0 +1,65 @@ +/* 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/. + * + */ + +#pragma once + +#include <stdint.h> + +struct sgsn_instance; +struct gprs_ipa_client; +struct msgb; + +/* This is the config part for vty. It is essentially copied in gprs_oap_state, + * where values are copied over once the config is considered valid. The shared + * secret is converted from hex string to octet buffer, the sgsn_id is simply + * copied. Is this separation really necessary? */ +struct gprs_oap_config { + uint16_t sgsn_id; + const char *shared_secret; +}; + +struct gprs_oap_state { + enum { + oap_uninitialized = 0, // just allocated. + oap_disabled, // disabled by config. + oap_config_error, // <-- TODO really? + oap_initialized, // shared_secret valid. + oap_requested_challenge, + oap_sent_challenge_result, + oap_registered + } state; + uint16_t sgsn_id; + uint8_t shared_secret[16]; + int challenges_count; +}; + +int gprs_oap_init(struct gprs_oap_config *config, struct gprs_oap_state *state); + +int gprs_oap_evaluate_challenge(struct gprs_oap_state *state, + const uint8_t *rx_random, + const uint8_t *rx_autn, + uint8_t *tx_sres, + uint8_t *tx_kc); + +int gprs_oap_register(struct gprs_ipa_client *gipac); +int gprs_oap_rx(struct gprs_ipa_client *gipac, struct msgb *msg); + diff --git a/openbsc/include/openbsc/gprs_oap_messages.h b/openbsc/include/openbsc/gprs_oap_messages.h new file mode 100644 index 0000000..b80e5ed --- /dev/null +++ b/openbsc/include/openbsc/gprs_oap_messages.h @@ -0,0 +1,70 @@ +/* Osmocom Authentication Protocol message encoder/decoder */ + +/* (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/. + * + */ +#pragma once + +#include <stdint.h> +#include <openbsc/gsm_04_08_gprs.h> +#include <openbsc/gsm_data.h> + +/* Some numbers are out of sequence because (so far) they match gprs_gsup_iei. + */ +enum gprs_oap_iei { + GPRS_OAP_CAUSE_IE = 0x02, + GPRS_OAP_RAND_IE = 0x20, + GPRS_OAP_SRES_IE = 0x21, + GPRS_OAP_KC_IE = 0x22, + GPRS_OAP_AUTN_IE = 0x23, + GPRS_OAP_SGSN_ID_IE = 0x30, +}; + +enum gprs_oap_message_type { + GPRS_OAP_MSGT_REGISTER_REQUEST = 0b00000100, + GPRS_OAP_MSGT_REGISTER_ERROR = 0b00000101, + GPRS_OAP_MSGT_REGISTER_RESULT = 0b00000110, + + GPRS_OAP_MSGT_CHALLENGE_REQUEST = 0b00001000, + GPRS_OAP_MSGT_CHALLENGE_ERROR = 0b00001001, + GPRS_OAP_MSGT_CHALLENGE_RESULT = 0b00001010, +}; + +#define GPRS_OAP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00) +#define GPRS_OAP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01) +#define GPRS_OAP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01) + +struct gprs_oap_message { + enum gprs_oap_message_type message_type; + enum gsm48_gmm_cause cause; + uint16_t sgsn_id; + int rand_present; + uint8_t rand[16]; + int autn_present; + uint8_t autn[16]; + int sres_present; + uint8_t sres[4]; + int kc_present; + uint8_t kc[8]; +}; + +int gprs_oap_decode(const uint8_t *data, size_t data_len, + struct gprs_oap_message *oap_msg); +void gprs_oap_encode(struct msgb *msg, const struct gprs_oap_message *oap_msg); + diff --git a/openbsc/include/openbsc/gprs_utils.h b/openbsc/include/openbsc/gprs_utils.h index 6880e05..c67cee2 100644 --- a/openbsc/include/openbsc/gprs_utils.h +++ b/openbsc/include/openbsc/gprs_utils.h @@ -52,3 +52,8 @@ int gprs_match_tlv(uint8_t **data, size_t *data_len, int gprs_shift_lv(uint8_t **data, size_t *data_len, uint8_t **value, size_t *value_len);
+uint64_t gprs_decode_big_endian(const uint8_t *data, size_t data_len); +/* Not thread safe: returns pointer to static buffer. */ +uint8_t *gprs_encode_big_endian(uint64_t value, size_t data_len); + +int gprs_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count); diff --git a/openbsc/include/openbsc/ipa_client.h b/openbsc/include/openbsc/ipa_client.h index 9537db4..531ee5d 100644 --- a/openbsc/include/openbsc/ipa_client.h +++ b/openbsc/include/openbsc/ipa_client.h @@ -1,9 +1,10 @@ -/* GPRS Subscriber Update Protocol client */ +/* General IPA client. + * ipa_client is ping/pong connection checking on an ipa_client_conn. */
-/* (C) 2014 by Sysmocom s.f.m.c. GmbH +/* (C) 2015 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * - * Author: Jacob Erlbeck + * Authors: Jacob Erlbeck, 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 @@ -21,34 +22,44 @@ */ #pragma once
+#include <stdint.h> #include <osmocom/core/timer.h>
-#define GPRS_GSUP_RECONNECT_INTERVAL 10 -#define GPRS_GSUP_PING_INTERVAL 20 +#define IPA_CLIENT_RECONNECT_INTERVAL 10 +#define IPA_CLIENT_PING_INTERVAL 20
struct msgb; struct ipa_client_conn; -struct gprs_gsup_client; +struct ipa_client; + +typedef void (*ipa_client_updown_cb_t)(struct ipa_client *ipac, int up);
/* Expects message in msg->l2h */ -typedef int (*gprs_gsup_read_cb_t)(struct gprs_gsup_client *gsupc, struct msgb *msg); +typedef void (*ipa_client_read_cb_t)(struct ipa_client *ipac, + uint8_t proto, + uint8_t proto_ext, + struct msgb *msg);
-struct gprs_gsup_client { - struct ipa_client_conn *link; - gprs_gsup_read_cb_t read_cb; +struct ipa_client { + ipa_client_updown_cb_t updown_cb; + ipa_client_read_cb_t read_cb; void *data;
+ struct ipa_client_conn *link; + struct osmo_timer_list ping_timer; struct osmo_timer_list connect_timer; int is_connected; int got_ipa_pong; };
-struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gprs_gsup_read_cb_t read_cb); +struct ipa_client *ipa_client_create(const char *ip_addr, + unsigned int tcp_port, + ipa_client_updown_cb_t updown_cb, + ipa_client_read_cb_t read_cb, + void *data);
-void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc); -int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); -struct msgb *gprs_gsup_msgb_alloc(void); +void ipa_client_destroy(struct ipa_client *ipac); +int ipa_client_send(struct ipa_client *ipac, uint8_t proto, uint8_t proto_ext, struct msgb *msg); +struct msgb *ipa_client_msgb_alloc(void);
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h index d4f9913..cff4e9d 100644 --- a/openbsc/include/openbsc/sgsn.h +++ b/openbsc/include/openbsc/sgsn.h @@ -6,10 +6,11 @@
#include <osmocom/gprs/gprs_ns.h> #include <openbsc/gprs_sgsn.h> +#include <openbsc/gprs_oap.h>
#include <ares.h>
-struct gprs_gsup_client; +struct gprs_ipa_client; struct hostent;
enum sgsn_auth_policy { @@ -36,8 +37,8 @@ struct sgsn_config { enum sgsn_auth_policy auth_policy; struct llist_head imsi_acl;
- struct sockaddr_in gsup_server_addr; - int gsup_server_port; + struct sockaddr_in ipa_server_addr; + int ipa_server_port;
int require_authentication; int require_update_location; @@ -61,6 +62,8 @@ struct sgsn_config { } timers;
int dynamic_lookup; + + struct gprs_oap_config oap; };
struct sgsn_instance { @@ -74,8 +77,8 @@ struct sgsn_instance { struct osmo_timer_list gtp_timer; /* GSN instance for libgtp */ struct gsn_t *gsn; - /* Subscriber */ - struct gprs_gsup_client *gsup_client; + /* Subscriber and SGSN registration*/ + struct gprs_ipa_client *gprs_ipa_client; /* LLME inactivity timer */ struct osmo_timer_list llme_timer;
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index b9c3070..6614a08 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -26,8 +26,10 @@ osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ gprs_llc.c gprs_llc_parse.c gprs_llc_vty.c crc24.c \ sgsn_ctrl.c sgsn_auth.c gprs_subscriber.c \ - gprs_gsup_messages.c gprs_utils.c ipa_client.c \ - gsm_04_08_gprs.c sgsn_cdr.c sgsn_ares.c + gprs_gsup_messages.c gprs_oap_messages.c gprs_oap.c \ + ipa_client.c gprs_ipa_client.c \ + gprs_utils.c gsm_04_08_gprs.c sgsn_cdr.c sgsn_ares.c + osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) -lrt diff --git a/openbsc/src/gprs/gprs_gsup_messages.c b/openbsc/src/gprs/gprs_gsup_messages.c index bdcff5f..d3cf058 100644 --- a/openbsc/src/gprs/gprs_gsup_messages.c +++ b/openbsc/src/gprs/gprs_gsup_messages.c @@ -33,34 +33,6 @@ #include <stdint.h>
-static uint64_t decode_big_endian(const uint8_t *data, size_t data_len) -{ - uint64_t value = 0; - - while (data_len > 0) { - value = (value << 8) + *data; - data += 1; - data_len -= 1; - } - - return value; -} - -static uint8_t *encode_big_endian(uint64_t value, size_t data_len) -{ - static uint8_t buf[sizeof(uint64_t)]; - int idx; - - OSMO_ASSERT(data_len <= ARRAY_SIZE(buf)); - - for (idx = data_len - 1; idx >= 0; idx--) { - buf[idx] = (uint8_t)value; - value = value >> 8; - } - - return buf; -} - static int decode_pdp_info(uint8_t *data, size_t data_len, struct gprs_gsup_pdp_info *pdp_info) { @@ -81,12 +53,12 @@ static int decode_pdp_info(uint8_t *data, size_t data_len,
switch (iei) { case GPRS_GSUP_PDP_CONTEXT_ID_IE: - pdp_info->context_id = decode_big_endian(value, value_len); + pdp_info->context_id = gprs_decode_big_endian(value, value_len); break;
case GPRS_GSUP_PDP_TYPE_IE: pdp_info->pdp_type = - decode_big_endian(value, value_len) & 0x0fff; + gprs_decode_big_endian(value, value_len) & 0x0fff; break;
case GPRS_GSUP_ACCESS_POINT_NAME_IE: @@ -187,7 +159,7 @@ int gprs_gsup_decode(const uint8_t *const_data, size_t data_len, if (rc < 0) return -GMM_CAUSE_INV_MAND_INFO;
- gsup_msg->message_type = decode_big_endian(value, 1); + gsup_msg->message_type = gprs_decode_big_endian(value, 1);
rc = gprs_match_tlv(&data, &data_len, GPRS_GSUP_IMSI_IE, &value, &value_len); @@ -231,12 +203,12 @@ int gprs_gsup_decode(const uint8_t *const_data, size_t data_len, continue;
case GPRS_GSUP_CAUSE_IE: - gsup_msg->cause = decode_big_endian(value, value_len); + gsup_msg->cause = gprs_decode_big_endian(value, value_len); break;
case GPRS_GSUP_CANCEL_TYPE_IE: gsup_msg->cancel_type = - decode_big_endian(value, value_len) + 1; + gprs_decode_big_endian(value, value_len) + 1; break;
case GPRS_GSUP_PDP_INFO_COMPL_IE: @@ -272,7 +244,7 @@ int gprs_gsup_decode(const uint8_t *const_data, size_t data_len, pdp_info.have_info = 1; } else { pdp_info.context_id = - decode_big_endian(value, value_len); + gprs_decode_big_endian(value, value_len); }
gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] = @@ -334,8 +306,8 @@ static void encode_pdp_info(struct msgb *msg, enum gprs_gsup_iei iei, if (pdp_info->pdp_type) { msgb_tlv_put(msg, GPRS_GSUP_PDP_TYPE_IE, GPRS_GSUP_PDP_TYPE_SIZE, - encode_big_endian(pdp_info->pdp_type | 0xf000, - GPRS_GSUP_PDP_TYPE_SIZE)); + gprs_encode_big_endian(pdp_info->pdp_type | 0xf000, + GPRS_GSUP_PDP_TYPE_SIZE)); }
if (pdp_info->apn_enc) { diff --git a/openbsc/src/gprs/gprs_ipa_client.c b/openbsc/src/gprs/gprs_ipa_client.c new file mode 100644 index 0000000..9f8f510 --- /dev/null +++ b/openbsc/src/gprs/gprs_ipa_client.c @@ -0,0 +1,159 @@ +/* Specific IPA client for GPRS: Multiplex for GSUP and OAP */ + +/* (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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <openbsc/gprs_ipa_client.h> +#include <openbsc/ipa_client.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_sgsn.h> +#include <openbsc/gprs_oap_messages.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/abis/ipa.h> + +#include <openbsc/debug.h> + +#include <errno.h> +#include <string.h> + + +int gprs_ipa_client_init(struct sgsn_instance *sgi) +{ + const char *addr_str; + + if (!sgi->cfg.ipa_server_addr.sin_addr.s_addr) + return 0; + + addr_str = inet_ntoa(sgi->cfg.ipa_server_addr.sin_addr); + + sgi->gprs_ipa_client = gprs_ipa_client_create( + addr_str, sgi->cfg.ipa_server_port); + + if (!sgi->gprs_ipa_client) + return -1; + + return 1; +} + + + +static void gprs_ipa_client_updown_cb(struct ipa_client *ipac, int up) +{ + struct gprs_ipa_client *gipac = ipac->data; + + if (up && (gipac->oap.sgsn_id != 0)) { + if (gprs_oap_register(gipac) < 0) { + /* TODO: fail fatally */ + } + } +} + +static void gprs_ipa_client_read_cb(struct ipa_client *ipac, + uint8_t proto, + uint8_t proto_ext, + struct msgb *msg) +{ + //int rc = -2; + struct gprs_ipa_client *gipac = ipac->data; + + if (proto != IPAC_PROTO_OSMO) + goto invalid; + + switch (proto_ext) { + case IPAC_PROTO_EXT_GSUP: + /*rc =*/ gprs_subscr_rx_gsup_message(msg); + break; + + case IPAC_PROTO_EXT_OAP: + /*rc =*/ gprs_oap_rx(gipac, msg); + break; + + default: + goto invalid; + } + + /* TODO: error rc? */ + + msgb_free(msg); + return; + +invalid: + LOGP(DGPRS, LOGL_NOTICE, + "received an invalid IPA message from %s:%d: proto=%d proto_ext=%d size=%d\n", + ipac->link->addr, (int)ipac->link->port, + (int)proto, (int)proto_ext, + msgb_length(msg)); + msgb_free(msg); + + /* TODO: error rc? */ +} + +struct gprs_ipa_client *gprs_ipa_client_create(const char *ip_addr, + unsigned int tcp_port) +{ + struct gprs_ipa_client *gipac; + + gipac = talloc_zero(tall_bsc_ctx, struct gprs_ipa_client); + OSMO_ASSERT(gipac); + + gipac->ipac = ipa_client_create(ip_addr, + tcp_port, + gprs_ipa_client_updown_cb, + gprs_ipa_client_read_cb, + /* data */ NULL); + + OSMO_ASSERT(gipac->ipac); + + if (!gipac->ipac) + goto failed; + + return gipac; + +failed: + gprs_ipa_client_destroy(gipac); + return NULL; +} + +void gprs_ipa_client_destroy(struct gprs_ipa_client *gipac) +{ + if (!gipac) + return; + + if (gipac->ipac) + ipa_client_destroy(gipac->ipac); + gipac->ipac = NULL; +} + +int gprs_ipa_client_send_gsup(struct gprs_ipa_client *gipac, struct msgb *msg) +{ + return ipa_client_send(gipac->ipac, IPAC_PROTO_OSMO, IPAC_PROTO_EXT_GSUP, msg); +} + +int gprs_ipa_client_send_oap(struct gprs_ipa_client *gipac, struct msgb *msg) +{ + return ipa_client_send(gipac->ipac, IPAC_PROTO_OSMO, IPAC_PROTO_EXT_OAP, msg); +} + diff --git a/openbsc/src/gprs/gprs_oap.c b/openbsc/src/gprs/gprs_oap.c new file mode 100644 index 0000000..0c30aa8 --- /dev/null +++ b/openbsc/src/gprs/gprs_oap.c @@ -0,0 +1,207 @@ +#include <osmocom/crypt/auth.h> +#include <osmocom/abis/ipa.h> + +#include <openbsc/gprs_oap.h> +#include <openbsc/sgsn.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_utils.h> +#include <openbsc/gprs_ipa_client.h> +#include <openbsc/gprs_oap_messages.h> + +#include <openbsc/gprs_oap.h> + +int gprs_oap_init(struct gprs_oap_config *config, struct gprs_oap_state *state) +{ + OSMO_ASSERT(state->state == oap_uninitialized); + + if (config->sgsn_id == 0) + goto disable; + + if (!(config->shared_secret) || (strlen(config->shared_secret) == 0)) + goto disable; + + /* this should probably happen in config parsing place?? */ + int secret_len = osmo_hexparse(config->shared_secret, + state->shared_secret, + sizeof(state->shared_secret)); + if (secret_len < 0) + goto failure; + + if (secret_len < 1) + goto disable; + + /* zero pad to fill 16 octets */ + for (; secret_len < 16; secret_len++) { + state->shared_secret[secret_len] = 0; + } + + state->sgsn_id = config->sgsn_id; + state->state = oap_initialized; + return 0; + +disable: + state->state = oap_disabled; + return 0; + +failure: + state->state = oap_config_error; + return -1; +} + + +int gprs_oap_evaluate_challenge(struct gprs_oap_state *state, + const uint8_t *rx_random, + const uint8_t *rx_autn, + uint8_t *tx_sres, + uint8_t *tx_kc) +{ + switch(state->state) { + case oap_uninitialized: + case oap_disabled: + case oap_config_error: + return -1; + default: + break; + } + + struct osmo_auth_vector vec; + + struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_GSM, + .algo = OSMO_AUTH_ALG_MILENAGE, + }; + + OSMO_ASSERT(sizeof(auth.u.umts.opc) == sizeof(state->shared_secret)); + OSMO_ASSERT(sizeof(auth.u.umts.k) == sizeof(state->shared_secret)); + + memcpy(auth.u.umts.opc, state->shared_secret, sizeof(auth.u.umts.opc)); + memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k)); + memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k)); + memset(auth.u.umts.amf, 0, 2); + auth.u.umts.sqn = 42; // TODO? + + memset(&vec, 0, sizeof(vec)); + osmo_auth_gen_vec(&vec, &auth, rx_random); + + if (vec.res_len != 8) { + LOGP(DGPRS, LOGL_ERROR, "OAP: generated res length is wrong: %d\n", + vec.res_len); + return -3; + } + + if (gprs_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { + LOGP(DGPRS, LOGL_ERROR, "OAP: AUTN mismatch!\n"); + LOGP(DGPRS, LOGL_INFO, "OAP: AUTN from server: %s\n", + osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); + LOGP(DGPRS, LOGL_INFO, "OAP: AUTN expected: %s\n", + osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); + return -2; + } + + memcpy(tx_sres, vec.sres, sizeof(vec.sres)); + memcpy(tx_kc, vec.kc, sizeof(vec.kc)); + return 0; +} + +int gprs_oap_register(struct gprs_ipa_client *gipac) +{ + struct gprs_oap_state *state = &gipac->oap; + + OSMO_ASSERT(state); + OSMO_ASSERT(state->sgsn_id); + + struct msgb *msg = ipa_client_msgb_alloc(); + + struct gprs_oap_message oap_msg = {0}; + oap_msg.message_type = GPRS_OAP_MSGT_REGISTER_REQUEST; + oap_msg.sgsn_id = state->sgsn_id; + + gprs_oap_encode(msg, &oap_msg); + + state->state = oap_requested_challenge; + return gprs_ipa_client_send_oap(gipac, msg); +} + +int gprs_oap_rx(struct gprs_ipa_client *gipac, struct msgb *msg) +{ + struct gprs_oap_state *state = &gipac->oap; + + uint8_t *data = msgb_l2(msg); + size_t data_len = msgb_l2len(msg); + int rc = 0; + + struct gprs_oap_message oap_msg = {0}; + + rc = gprs_oap_decode(data, data_len, &oap_msg); + if (rc < 0) { + LOGP(DGPRS, LOGL_ERROR, + "Decoding OAP message failed with error '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, -rc), -rc); + return rc; + } + + switch (oap_msg.message_type) { + case GPRS_OAP_MSGT_CHALLENGE_REQUEST: + // reply with challenge result + if (!(oap_msg.rand_present && oap_msg.autn_present)) { + LOGP(DGPRS, LOGL_ERROR, + "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", + oap_msg.rand_present, oap_msg.autn_present); + return -1; + } + + { + struct gprs_oap_message oap_reply = {0}; + oap_reply.message_type = GPRS_OAP_MSGT_CHALLENGE_RESULT; + + rc = gprs_oap_evaluate_challenge(state, + oap_msg.rand, + oap_msg.autn, + oap_reply.sres, + oap_reply.kc); + if (rc < 0) + return rc; + + oap_reply.sres_present = 1; + oap_reply.kc_present = 1; + + struct msgb *oap_reply_msg = ipa_client_msgb_alloc(); + OSMO_ASSERT(oap_reply_msg); + + gprs_oap_encode(oap_reply_msg, &oap_reply); + + state->state = oap_sent_challenge_result; + state->challenges_count ++; + gprs_ipa_client_send_oap(gipac, oap_reply_msg); + } + + break; + + case GPRS_OAP_MSGT_REGISTER_RESULT: + // successfully registered! + state->state = oap_registered; + break; + + case GPRS_OAP_MSGT_REGISTER_ERROR: + LOGP(DGPRS, LOGL_ERROR, + "OAP registration failed, from %s:%d\n", + gipac->ipac->link->addr, (int)gipac->ipac->link->port); + return -1; + break; + + case GPRS_OAP_MSGT_REGISTER_REQUEST: + case GPRS_OAP_MSGT_CHALLENGE_RESULT: + LOGP(DGPRS, LOGL_ERROR, + "Received invalid OAP message type for OAP client side: %d\n", + (int)oap_msg.message_type); + return -1; + + default: + LOGP(DGPRS, LOGL_ERROR, + "Unknown OAP message type: %d\n", + (int)oap_msg.message_type); + return -2; + } + + return 0; +} diff --git a/openbsc/src/gprs/gprs_oap_messages.c b/openbsc/src/gprs/gprs_oap_messages.c new file mode 100644 index 0000000..e79546e --- /dev/null +++ b/openbsc/src/gprs/gprs_oap_messages.c @@ -0,0 +1,179 @@ +/* GPRS Subscriber Update Protocol message encoder/decoder */ + +/* + * (C) 2014 by Sysmocom s.f.m.c. GmbH + * (C) 2015 by Holger Hans Peter Freyther + * All Rights Reserved + * + * Author: Jacob Erlbeck + * + * 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/gprs_oap_messages.h> + +#include <openbsc/debug.h> +#include <openbsc/gprs_utils.h> + +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/msgb.h> + +#include <stdint.h> + + +int gprs_oap_decode(const uint8_t *const_data, size_t data_len, + struct gprs_oap_message *oap_msg) +{ + int rc; + uint8_t tag; + /* the shift/match functions expect non-const pointers, but we'll + * either copy the data or cast pointers back to const before returning + * them + */ + uint8_t *data = (uint8_t *)const_data; + uint8_t *value; + size_t value_len; + static const struct gprs_oap_message empty_oap_message = {0}; + + *oap_msg = empty_oap_message; + + /* message type */ + rc = gprs_shift_v_fixed(&data, &data_len, 1, &value); + if (rc < 0) + return -GMM_CAUSE_INV_MAND_INFO; + oap_msg->message_type = gprs_decode_big_endian(value, 1); + + /* specific parts */ + while (data_len > 0) { + enum gprs_oap_iei iei; + + rc = gprs_shift_tlv(&data, &data_len, &tag, &value, &value_len); + if (rc < 0) + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + + iei = tag; + + switch (iei) { + case GPRS_OAP_SGSN_ID_IE: + if (value_len != 2) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type SGSN Id (%d) should be 2 octets, but has %d\n", + (int)iei, (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + + oap_msg->sgsn_id = gprs_decode_big_endian(value, value_len); + + if (oap_msg->sgsn_id == 0) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type SGSN Id (%d): SGSN Id must be nonzero.\n", + (int)iei); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + break; + + case GPRS_OAP_AUTN_IE: + if (value_len != sizeof(oap_msg->autn)) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type AUTN (%d) should be %d octets, but has %d\n", + (int)iei, (int)sizeof(oap_msg->autn), (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + memcpy(oap_msg->autn, value, value_len); + oap_msg->autn_present = value_len; + break; + + case GPRS_OAP_RAND_IE: + if (value_len != sizeof(oap_msg->rand)) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type RAND (%d) should be %d octets, but has %d\n", + (int)iei, (int)sizeof(oap_msg->rand), (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + memcpy(oap_msg->rand, value, value_len); + oap_msg->rand_present = value_len; + break; + + case GPRS_OAP_SRES_IE: + if (value_len != sizeof(oap_msg->sres)) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type SRES (%d) should be %d octets, but has %d\n", + (int)iei, (int)sizeof(oap_msg->sres), (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + memcpy(oap_msg->sres, value, value_len); + oap_msg->sres_present = value_len; + break; + + case GPRS_OAP_KC_IE: + if (value_len != sizeof(oap_msg->kc)) { + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type Kc (%d) should be %d octets, but has %d\n", + (int)iei, (int)sizeof(oap_msg->kc), (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + memcpy(oap_msg->kc, value, value_len); + oap_msg->kc_present = value_len; + break; + + case GPRS_OAP_CAUSE_IE: + if (value_len > 1) { + LOGP(DGPRS, LOGL_ERROR, + "OAP cause may not exceed one octet, is %d", (int)value_len); + return -GMM_CAUSE_PROTO_ERR_UNSPEC; + } + oap_msg->cause = *value; + break; + + default: + LOGP(DGPRS, LOGL_NOTICE, + "OAP IE type %d unknown\n", iei); + continue; + } + } + + return 0; +} + +void gprs_oap_encode(struct msgb *msg, const struct gprs_oap_message *oap_msg) +{ + uint8_t u8; + + /* generic part */ + OSMO_ASSERT(oap_msg->message_type); + msgb_v_put(msg, oap_msg->message_type); + + /* specific parts */ + if ((u8 = oap_msg->cause)) + msgb_tlv_put(msg, GPRS_OAP_CAUSE_IE, sizeof(u8), &u8); + + if (oap_msg->sgsn_id > 0) + msgb_tlv_put(msg, GPRS_OAP_SGSN_ID_IE, sizeof(oap_msg->sgsn_id), + gprs_encode_big_endian(oap_msg->sgsn_id, sizeof(oap_msg->sgsn_id))); + + if (oap_msg->autn_present) + msgb_tlv_put(msg, GPRS_OAP_AUTN_IE, sizeof(oap_msg->autn), oap_msg->autn); + + if (oap_msg->rand_present) + msgb_tlv_put(msg, GPRS_OAP_RAND_IE, sizeof(oap_msg->rand), oap_msg->rand); + + if (oap_msg->sres_present) + msgb_tlv_put(msg, GPRS_OAP_SRES_IE, sizeof(oap_msg->sres), oap_msg->sres); + + if (oap_msg->kc_present) + msgb_tlv_put(msg, GPRS_OAP_KC_IE, sizeof(oap_msg->kc), oap_msg->kc); +} + + diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c index 0a3fe19..3d76d19 100644 --- a/openbsc/src/gprs/gprs_subscriber.c +++ b/openbsc/src/gprs/gprs_subscriber.c @@ -21,7 +21,7 @@ */
#include <openbsc/gsm_subscriber.h> -#include <openbsc/ipa_client.h> +#include <openbsc/gprs_ipa_client.h>
#include <openbsc/sgsn.h> #include <openbsc/gprs_sgsn.h> @@ -44,45 +44,12 @@
extern void *tall_bsc_ctx;
-static int gsup_read_cb(struct gprs_gsup_client *gsupc, struct msgb *msg); - /* TODO: Some functions are specific to the SGSN, but this file is more general * (it has gprs_* name). Either move these functions elsewhere, split them and * move a part, or replace the gprs_ prefix by sgsn_. The applies to - * gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message. + * gprs_subscr_tx_gsup_message. */
-int gprs_subscr_init(struct sgsn_instance *sgi) -{ - const char *addr_str; - - if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr) - return 0; - - addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr); - - sgi->gsup_client = gprs_gsup_client_create( - addr_str, sgi->cfg.gsup_server_port, - &gsup_read_cb); - - if (!sgi->gsup_client) - return -1; - - return 1; -} - -static int gsup_read_cb(struct gprs_gsup_client *gsupc, struct msgb *msg) -{ - int rc; - - rc = gprs_subscr_rx_gsup_message(msg); - msgb_free(msg); - if (rc < 0) - return -1; - - return rc; -} - int gprs_subscr_purge(struct gsm_subscriber *subscr);
static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx) @@ -159,7 +126,7 @@ void gprs_subscr_cancel(struct gsm_subscriber *subscr) static int gprs_subscr_tx_gsup_message(struct gsm_subscriber *subscr, struct gprs_gsup_message *gsup_msg) { - struct msgb *msg = gprs_gsup_msgb_alloc(); + struct msgb *msg = ipa_client_msgb_alloc();
if (strlen(gsup_msg->imsi) == 0 && subscr) strncpy(gsup_msg->imsi, subscr->imsi, sizeof(gsup_msg->imsi) - 1); @@ -169,12 +136,12 @@ static int gprs_subscr_tx_gsup_message(struct gsm_subscriber *subscr, LOGGSUBSCRP(LOGL_INFO, subscr, "Sending GSUP, will send: %s\n", msgb_hexdump(msg));
- if (!sgsn->gsup_client) { + if (!sgsn->gprs_ipa_client) { msgb_free(msg); return -ENOTSUP; }
- return gprs_gsup_client_send(sgsn->gsup_client, msg); + return gprs_ipa_client_send_gsup(sgsn->gprs_ipa_client, msg); }
static int gprs_subscr_tx_gsup_error_reply(struct gsm_subscriber *subscr, diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c index 2293f02..475a740 100644 --- a/openbsc/src/gprs/gprs_utils.c +++ b/openbsc/src/gprs/gprs_utils.c @@ -397,3 +397,42 @@ fail: return -1; }
+uint64_t gprs_decode_big_endian(const uint8_t *data, size_t data_len) +{ + uint64_t value = 0; + + while (data_len > 0) { + value = (value << 8) + *data; + data += 1; + data_len -= 1; + } + + return value; +} + +uint8_t *gprs_encode_big_endian(uint64_t value, size_t data_len) +{ + static uint8_t buf[sizeof(uint64_t)]; + int idx; + + OSMO_ASSERT(data_len <= ARRAY_SIZE(buf)); + + for (idx = data_len - 1; idx >= 0; idx--) { + buf[idx] = (uint8_t)value; + value = value >> 8; + } + + return buf; +} + +/* Wishful thinking to generate a constant time compare */ +int gprs_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) +{ + int x = 0, i; + + for (i = 0; i < count; ++i) + x |= exp[i] ^ rel[i]; + + return x != 0; +} + diff --git a/openbsc/src/gprs/ipa_client.c b/openbsc/src/gprs/ipa_client.c index bec33c4..9d02b44 100644 --- a/openbsc/src/gprs/ipa_client.c +++ b/openbsc/src/gprs/ipa_client.c @@ -1,9 +1,9 @@ -/* GPRS Subscriber Update Protocol client */ +/* Osmocom Authentication Protocol client */
-/* (C) 2014 by Sysmocom s.f.m.c. GmbH +/* (C) 2015 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * - * Author: Jacob Erlbeck + * Authors: Jakob Erlbeck, 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 @@ -33,99 +33,102 @@
extern void *tall_bsc_ctx;
-static void start_test_procedure(struct gprs_gsup_client *gsupc); +static void start_test_procedure(struct ipa_client *ipac);
-static void gsup_client_send_ping(struct gprs_gsup_client *gsupc) +static void ipa_client_send_ping(struct ipa_client *ipac) { - struct msgb *msg = gprs_gsup_msgb_alloc(); + struct msgb *msg = ipa_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); + ipa_client_conn_send(ipac->link, msg); }
-static int gsup_client_connect(struct gprs_gsup_client *gsupc) +static int ipa_client_connect(struct ipa_client *ipac) { int rc;
- if (gsupc->is_connected) + if (ipac->is_connected) return 0;
- if (osmo_timer_pending(&gsupc->connect_timer)) { + if (osmo_timer_pending(&ipac->connect_timer)) { LOGP(DLINP, LOGL_DEBUG, - "GSUP connect: connect timer already running\n"); - osmo_timer_del(&gsupc->connect_timer); + "IPA connect: connect timer already running\n"); + osmo_timer_del(&ipac->connect_timer); }
- if (osmo_timer_pending(&gsupc->ping_timer)) { + if (osmo_timer_pending(&ipac->ping_timer)) { LOGP(DLINP, LOGL_DEBUG, - "GSUP connect: ping timer already running\n"); - osmo_timer_del(&gsupc->ping_timer); + "IPA connect: ping timer already running\n"); + osmo_timer_del(&ipac->ping_timer); }
- if (ipa_client_conn_clear_queue(gsupc->link) > 0) - LOGP(DLINP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); + if (ipa_client_conn_clear_queue(ipac->link) > 0) + LOGP(DLINP, LOGL_DEBUG, "IPA connect: discarded stored messages\n");
- rc = ipa_client_conn_open(gsupc->link); + rc = ipa_client_conn_open(ipac->link);
if (rc >= 0) { - LOGP(DGPRS, LOGL_INFO, "GSUP connecting to %s:%d\n", - gsupc->link->addr, gsupc->link->port); + LOGP(DGPRS, LOGL_INFO, "IPA connecting to %s:%d\n", + ipac->link->addr, ipac->link->port); return 0; }
- LOGP(DGPRS, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", - gsupc->link->addr, gsupc->link->port, strerror(-rc)); + LOGP(DGPRS, LOGL_INFO, "IPA failed to connect to %s:%d: %s\n", + ipac->link->addr, ipac->link->port, strerror(-rc));
if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || rc == -EINVAL) return rc;
- osmo_timer_schedule(&gsupc->connect_timer, GPRS_GSUP_RECONNECT_INTERVAL, 0); + osmo_timer_schedule(&ipac->connect_timer, IPA_CLIENT_RECONNECT_INTERVAL, 0);
- LOGP(DGPRS, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", - gsupc->link->addr, gsupc->link->port); + LOGP(DGPRS, LOGL_INFO, "Scheduled timer to retry IPA connect to %s:%d\n", + ipac->link->addr, ipac->link->port);
return 0; }
-static void connect_timer_cb(void *gsupc_) +static void connect_timer_cb(void *ipac_) { - struct gprs_gsup_client *gsupc = gsupc_; + struct ipa_client *ipac = ipac_;
- if (gsupc->is_connected) + if (ipac->is_connected) return;
- gsup_client_connect(gsupc); + ipa_client_connect(ipac); }
-static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) +static void ipa_client_updown_cb(struct ipa_client_conn *link, int up) { - struct gprs_gsup_client *gsupc = link->data; + struct ipa_client *ipac = link->data;
- LOGP(DGPRS, LOGL_INFO, "GSUP link to %s:%d %s\n", - link->addr, link->port, up ? "UP" : "DOWN"); + LOGP(DGPRS, LOGL_INFO, "IPA link to %s:%d %s\n", + link->addr, link->port, up ? "UP" : "DOWN");
- gsupc->is_connected = up; + ipac->is_connected = up;
if (up) { - start_test_procedure(gsupc); + start_test_procedure(ipac);
- osmo_timer_del(&gsupc->connect_timer); + osmo_timer_del(&ipac->connect_timer); } else { - osmo_timer_del(&gsupc->ping_timer); + osmo_timer_del(&ipac->ping_timer);
- osmo_timer_schedule(&gsupc->connect_timer, - GPRS_GSUP_RECONNECT_INTERVAL, 0); + osmo_timer_schedule(&ipac->connect_timer, + IPA_CLIENT_RECONNECT_INTERVAL, 0); } + + if (ipac->updown_cb != NULL) + ipac->updown_cb(ipac, up); }
-static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) +static int ipa_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 gprs_gsup_client *gsupc = (struct gprs_gsup_client *)link->data; + struct ipa_client *ipac = (struct ipa_client *)link->data; int rc; static struct ipaccess_unit ipa_dev = { .unit_name = "SGSN" @@ -137,10 +140,10 @@ static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
if (rc < 0) { LOGP(DGPRS, LOGL_NOTICE, - "GSUP received an invalid IPA/CCM message from %s:%d\n", + "received an invalid IPA/CCM message from %s:%d\n", link->addr, link->port); /* Link has been closed */ - gsupc->is_connected = 0; + ipac->is_connected = 0; msgb_free(msg); return -1; } @@ -149,140 +152,154 @@ static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) uint8_t msg_type = *(msg->l2h); /* CCM message */ if (msg_type == IPAC_MSGT_PONG) { - LOGP(DGPRS, LOGL_DEBUG, "GSUP receiving PONG\n"); - gsupc->got_ipa_pong = 1; + LOGP(DGPRS, LOGL_DEBUG, "IPA receiving PONG\n"); + ipac->got_ipa_pong = 1; }
msgb_free(msg); return 0; }
- if (hh->proto != IPAC_PROTO_OSMO) - goto invalid; - - if (!he || msgb_l2len(msg) < sizeof(*he) || - he->proto != IPAC_PROTO_EXT_GSUP) + if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid;
msg->l2h = &he->data[0];
- OSMO_ASSERT(gsupc->read_cb != NULL); - gsupc->read_cb(gsupc, msg); + OSMO_ASSERT(ipac->read_cb != NULL); + ipac->read_cb(ipac, hh->proto, he->proto, msg);
/* Not freeing msg here, because that must be done by the read_cb. */ return 0;
invalid: LOGP(DGPRS, LOGL_NOTICE, - "GSUP received an invalid IPA message from %s:%d, size = %d\n", + "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_) +static void ping_timer_cb(void *ipac_) { - struct gprs_gsup_client *gsupc = gsupc_; + struct ipa_client *ipac = ipac_;
- LOGP(DGPRS, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", - gsupc->is_connected ? "connected" : "not connected", - gsupc->got_ipa_pong ? "got" : "didn't get"); + LOGP(DGPRS, LOGL_INFO, "IPA ping callback (%s, %s PONG)\n", + ipac->is_connected ? "connected" : "not connected", + ipac->got_ipa_pong ? "got" : "didn't get");
- if (gsupc->got_ipa_pong) { - start_test_procedure(gsupc); + if (ipac->got_ipa_pong) { + start_test_procedure(ipac); return; }
- LOGP(DGPRS, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); - ipa_client_conn_close(gsupc->link); - gsupc->is_connected = 0; + LOGP(DGPRS, LOGL_NOTICE, "IPA ping timed out, reconnecting\n"); + ipa_client_conn_close(ipac->link); + ipac->is_connected = 0;
- gsup_client_connect(gsupc); + ipa_client_connect(ipac); }
-static void start_test_procedure(struct gprs_gsup_client *gsupc) +static void start_test_procedure(struct ipa_client *ipac) { - gsupc->ping_timer.data = gsupc; - gsupc->ping_timer.cb = &ping_timer_cb; + ipac->ping_timer.data = ipac; + ipac->ping_timer.cb = &ping_timer_cb;
- gsupc->got_ipa_pong = 0; - osmo_timer_schedule(&gsupc->ping_timer, GPRS_GSUP_PING_INTERVAL, 0); - LOGP(DGPRS, LOGL_DEBUG, "GSUP sending PING\n"); - gsup_client_send_ping(gsupc); + ipac->got_ipa_pong = 0; + osmo_timer_schedule(&ipac->ping_timer, IPA_CLIENT_PING_INTERVAL, 0); + LOGP(DGPRS, LOGL_DEBUG, "IPA sending PING\n"); + ipa_client_send_ping(ipac); }
-struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gprs_gsup_read_cb_t read_cb) +struct ipa_client *ipa_client_create(const char *ip_addr, + unsigned int tcp_port, + ipa_client_updown_cb_t updown_cb, + ipa_client_read_cb_t read_cb, + void *data) { - struct gprs_gsup_client *gsupc; + struct ipa_client *ipac; int rc;
- gsupc = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); - OSMO_ASSERT(gsupc); - - 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) + ipac = talloc_zero(tall_bsc_ctx, struct ipa_client); + OSMO_ASSERT(ipac); + + ipac->updown_cb = updown_cb; + ipac->read_cb = read_cb; + ipac->data = data; + + ipac->link = ipa_client_conn_create(ipac, + /* no e1inp */ NULL, + 0, + ip_addr, tcp_port, + ipa_client_updown_cb, + ipa_client_read_cb, + /* default write_cb */ NULL, + ipac); + if (!ipac->link) goto failed;
- gsupc->connect_timer.data = gsupc; - gsupc->connect_timer.cb = &connect_timer_cb; + ipac->connect_timer.data = ipac; + ipac->connect_timer.cb = &connect_timer_cb;
- rc = gsup_client_connect(gsupc); + rc = ipa_client_connect(ipac);
if (rc < 0) goto failed;
- gsupc->read_cb = read_cb; + ipac->read_cb = read_cb;
- return gsupc; + return ipac;
failed: - gprs_gsup_client_destroy(gsupc); + ipa_client_destroy(ipac); return NULL; }
-void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc) +void ipa_client_destroy(struct ipa_client *ipac) { - osmo_timer_del(&gsupc->connect_timer); - osmo_timer_del(&gsupc->ping_timer); + osmo_timer_del(&ipac->connect_timer); + osmo_timer_del(&ipac->ping_timer);
- if (gsupc->link) { - ipa_client_conn_close(gsupc->link); - ipa_client_conn_destroy(gsupc->link); - gsupc->link = NULL; + if (ipac->link) { + ipa_client_conn_close(ipac->link); + ipa_client_conn_destroy(ipac->link); + ipac->link = NULL; } - talloc_free(gsupc); + talloc_free(ipac); }
-int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) +int ipa_client_send(struct ipa_client *ipac, uint8_t proto, uint8_t proto_ext, + struct msgb *msg) { - if (!gsupc) { + OSMO_ASSERT(msg); + + if (!ipac) { msgb_free(msg); return -ENOTCONN; }
- if (!gsupc->is_connected) { + if (!ipac->is_connected) { msgb_free(msg); return -EAGAIN; }
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_GSUP); - ipa_msg_push_header(msg, IPAC_PROTO_OSMO); - ipa_client_conn_send(gsupc->link, msg); + // l2h is not sent over the wire, but for the test suite it makes sense + // to make l2h point at the IPA message payload. + unsigned char *l2h = msg->data; + + ipa_prepend_header_ext(msg, proto); + ipa_msg_push_header(msg, proto_ext); + + msg->l2h = l2h; + + ipa_client_conn_send(ipac->link, msg);
return 0; }
-struct msgb *gprs_gsup_msgb_alloc(void) +struct msgb *ipa_client_msgb_alloc(void) { return msgb_alloc_headroom(4000, 64, __func__); } + + diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c index 8cb7499..882b6a3 100644 --- a/openbsc/src/gprs/sgsn_main.c +++ b/openbsc/src/gprs/sgsn_main.c @@ -51,6 +51,7 @@ #include <openbsc/sgsn.h> #include <openbsc/gprs_llc.h> #include <openbsc/gprs_gmm.h> +#include <openbsc/gprs_ipa_client.h> #include <osmocom/ctrl/control_if.h> #include <osmocom/ctrl/ports.h>
@@ -359,9 +360,9 @@ int main(int argc, char **argv) exit(2); }
- rc = gprs_subscr_init(&sgsn_inst); + rc = gprs_ipa_client_init(&sgsn_inst); if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n"); + LOGP(DGPRS, LOGL_FATAL, "Cannot establish IPA connection\n"); exit(2); }
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index 00a930f..75974c7 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -34,13 +34,14 @@ #include <openbsc/gprs_sgsn.h> #include <openbsc/vty.h> #include <openbsc/gsm_04_08_gprs.h> -#include <openbsc/ipa_client.h>
#include <osmocom/vty/command.h> #include <osmocom/vty/vty.h> #include <osmocom/vty/misc.h>
#include <osmocom/abis/ipa.h> +#include <openbsc/ipa_client.h> +#include <openbsc/gprs_ipa_client.h>
#include <pdp.h>
@@ -208,12 +209,12 @@ static int config_write_sgsn(struct vty *vty) vty_out(vty, " auth-policy %s%s", get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy), VTY_NEWLINE); - if (g_cfg->gsup_server_addr.sin_addr.s_addr) - vty_out(vty, " gsup remote-ip %s%s", - inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE); - if (g_cfg->gsup_server_port) - vty_out(vty, " gsup remote-port %d%s", - g_cfg->gsup_server_port, VTY_NEWLINE); + if (g_cfg->ipa_server_addr.sin_addr.s_addr) + vty_out(vty, " ipa remote-ip %s%s", + inet_ntoa(g_cfg->ipa_server_addr.sin_addr), VTY_NEWLINE); + if (g_cfg->ipa_server_port) + vty_out(vty, " ipa remote-port %d%s", + g_cfg->ipa_server_port, VTY_NEWLINE); llist_for_each_entry(acl, &g_cfg->imsi_acl, list) vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE);
@@ -434,12 +435,12 @@ static void vty_dump_mmctx(struct vty *vty, const char *pfx, DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", SHOW_STR "Display information about the SGSN") { - if (sgsn->gsup_client) { - struct ipa_client_conn *link = sgsn->gsup_client->link; + if (sgsn->gprs_ipa_client) { + struct ipa_client *ipac = sgsn->gprs_ipa_client->ipac; vty_out(vty, - " Remote authorization: %sconnected to %s:%d via GSUP%s", - sgsn->gsup_client->is_connected ? "" : "not ", - link->addr, link->port, + " Remote authorization: %sconnected to %s:%d via IPA%s", + ipac->is_connected ? "" : "not ", + ipac->link->addr, ipac->link->port, VTY_NEWLINE); } /* FIXME: statistics */ @@ -873,24 +874,24 @@ DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd, return CMD_SUCCESS; }
-DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd, - "gsup remote-ip A.B.C.D", - "GSUP Parameters\n" - "Set the IP address of the remote GSUP server\n" +DEFUN(cfg_ipa_remote_ip, cfg_ipa_remote_ip_cmd, + "ipa remote-ip A.B.C.D", + "IPA Parameters\n" + "Set the IP address of the remote IPA (GSUP+OAP) server\n" "IPv4 Address\n") { - inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr); + inet_aton(argv[0], &g_cfg->ipa_server_addr.sin_addr);
return CMD_SUCCESS; }
-DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd, - "gsup remote-port <0-65535>", - "GSUP Parameters\n" - "Set the TCP port of the remote GSUP server\n" +DEFUN(cfg_ipa_remote_port, cfg_ipa_remote_port_cmd, + "ipa remote-port <0-65535>", + "IPA Parameters\n" + "Set the TCP port of the remote IPA (GSUP+OAP) server\n" "Remote TCP port\n") { - g_cfg->gsup_server_port = atoi(argv[0]); + g_cfg->ipa_server_port = atoi(argv[0]);
return CMD_SUCCESS; } @@ -967,8 +968,8 @@ int sgsn_vty_init(void) install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd); install_element(SGSN_NODE, &cfg_imsi_acl_cmd); install_element(SGSN_NODE, &cfg_auth_policy_cmd); - install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); - install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); + install_element(SGSN_NODE, &cfg_ipa_remote_ip_cmd); + install_element(SGSN_NODE, &cfg_ipa_remote_port_cmd); install_element(SGSN_NODE, &cfg_apn_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_name_cmd); diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c index 1fc262d..42d7c30 100644 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ b/openbsc/src/osmo-bsc_nat/bsc_nat.c @@ -47,6 +47,7 @@ #include <openbsc/abis_nm.h> #include <openbsc/socket.h> #include <openbsc/vty.h> +#include <openbsc/gprs_utils.h>
#include <osmocom/ctrl/control_cmd.h> #include <osmocom/ctrl/control_if.h> @@ -987,17 +988,6 @@ static void ipaccess_close_bsc(void *data) bsc_close_connection(conn); }
-/* Wishful thinking to generate a constant time compare */ -static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) -{ - int x = 0, i; - - for (i = 0; i < count; ++i) - x |= exp[i] ^ rel[i]; - - return x != 0; -} - static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen) { struct osmo_auth_vector vec; @@ -1024,11 +1014,11 @@ static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, cons
if (vec.res_len != 8) { LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n", - keylen, conf->nr); + (int)vec.res_len, conf->nr); return 0; }
- return constant_time_cmp(vec.res, key, 8) == 0; + return gprs_constant_time_cmp(vec.res, key, 8) == 0; }
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index ea29fce..3b45b3f 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am @@ -10,7 +10,8 @@ sgsn_test_LDFLAGS = \ -Wl,--wrap=sgsn_update_subscriber_data \ -Wl,--wrap=gprs_subscr_request_update_location \ -Wl,--wrap=gprs_subscr_request_auth_info \ - -Wl,--wrap=gprs_gsup_client_send + -Wl,--wrap=gprs_ipa_client_send_gsup \ + -Wl,--wrap=ipa_client_conn_send
sgsn_test_LDADD = \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ @@ -24,7 +25,10 @@ sgsn_test_LDADD = \ $(top_builddir)/src/gprs/sgsn_auth.o \ $(top_builddir)/src/gprs/sgsn_ares.o \ $(top_builddir)/src/gprs/gprs_gsup_messages.o \ + $(top_builddir)/src/gprs/gprs_oap_messages.o \ + $(top_builddir)/src/gprs/gprs_oap.o \ $(top_builddir)/src/gprs/ipa_client.o \ + $(top_builddir)/src/gprs/gprs_ipa_client.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/gprs_subscriber.o \ $(top_builddir)/src/gprs/gsm_04_08_gprs.o \ diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c index 251772e..84791d5 100644 --- a/openbsc/tests/sgsn/sgsn_test.c +++ b/openbsc/tests/sgsn/sgsn_test.c @@ -24,10 +24,16 @@ #include <openbsc/gprs_gmm.h> #include <openbsc/debug.h> #include <openbsc/gsm_subscriber.h> -#include <openbsc/gprs_gsup_messages.h> -#include <openbsc/ipa_client.h> #include <openbsc/gprs_utils.h>
+#include <osmocom/abis/ipa.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <openbsc/ipa_client.h> +#include <openbsc/gprs_ipa_client.h> +#include <openbsc/gprs_gsup_messages.h> +#include <openbsc/gprs_oap_messages.h> +#include <openbsc/gprs_oap.h> + #include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/gsm_utils.h> @@ -89,14 +95,24 @@ int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { return (*subscr_request_auth_info_cb)(mmctx); };
-/* override, requires '-Wl,--wrap=gprs_gsup_client_send' */ -int __real_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); -int (*gprs_gsup_client_send_cb)(struct gprs_gsup_client *gsupc, struct msgb *msg) = - &__real_gprs_gsup_client_send; +/* override, requires '-Wl,--wrap=gprs_ipa_client_send_gsup' */ +int __real_gprs_ipa_client_send_gsup(struct gprs_ipa_client *gipac, struct msgb *msg); +int (*gprs_ipa_client_send_gsup_cb)(struct gprs_ipa_client *gipac, struct msgb *msg) = + &__real_gprs_ipa_client_send_gsup;
-int __wrap_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) +int __wrap_gprs_ipa_client_send_gsup(struct gprs_ipa_client *gipac, struct msgb *msg) { - return (*gprs_gsup_client_send_cb)(gsupc, msg); + return (*gprs_ipa_client_send_gsup_cb)(gipac, msg); +}; + +/* override, requires '-Wl,--wrap=ipa_client_conn_send' */ +void __real_ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg); +void (*ipa_client_conn_send_cb)(struct ipa_client_conn *link, struct msgb *msg) = + &__real_ipa_client_conn_send; + +void __wrap_ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg) +{ + return (*ipa_client_conn_send_cb)(link, msg); };
static int count(struct llist_head *head) @@ -651,7 +667,7 @@ static void test_subscriber_gsup(void) update_subscriber_data_cb = __real_sgsn_update_subscriber_data; }
-int my_gprs_gsup_client_send_dummy(struct gprs_gsup_client *gsupc, struct msgb *msg) +int my_gprs_ipa_client_send_gsup_dummy(struct gprs_ipa_client *gipac, struct msgb *msg) { msgb_free(msg); return 0; @@ -1201,7 +1217,7 @@ static void test_gmm_attach_subscr_gsup_auth(int retry) auth_info_skip = 0; }
-int my_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) +int my_gprs_ipa_client_send_gsup(struct gprs_ipa_client *gipac, struct msgb *msg) { struct gprs_gsup_message to_peer = {0}; struct gprs_gsup_message from_peer = {0}; @@ -1243,7 +1259,7 @@ int my_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) return 0; }
- reply_msg = gprs_gsup_msgb_alloc(); + reply_msg = ipa_client_msgb_alloc(); reply_msg->l2h = reply_msg->data; gprs_gsup_encode(reply_msg, &from_peer); gprs_subscr_rx_gsup_message(reply_msg); @@ -1258,9 +1274,14 @@ static void test_gmm_attach_subscr_real_gsup_auth(int retry) struct gsm_subscriber *subscr;
sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; - gprs_gsup_client_send_cb = my_gprs_gsup_client_send; + gprs_ipa_client_send_gsup_cb = my_gprs_ipa_client_send_gsup;
- sgsn->gsup_client = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); + /* The sgsn->gprs_ipa_client will not have been initialized, because + cfg.ipa_server_addr is unset. Allocate empty structs to use them in + these tests. */ + OSMO_ASSERT(sgsn->gprs_ipa_client == NULL); + sgsn->gprs_ipa_client = talloc_zero(tall_bsc_ctx, struct gprs_ipa_client); + sgsn->gprs_ipa_client->ipac = talloc_zero(tall_bsc_ctx, struct ipa_client);
if (retry) { upd_loc_skip = 3; @@ -1275,11 +1296,13 @@ static void test_gmm_attach_subscr_real_gsup_auth(int retry) assert_no_subscrs();
sgsn->cfg.auth_policy = saved_auth_policy; - gprs_gsup_client_send_cb = __real_gprs_gsup_client_send; + gprs_ipa_client_send_gsup_cb = __real_gprs_ipa_client_send_gsup; upd_loc_skip = 0; auth_info_skip = 0; - talloc_free(sgsn->gsup_client); - sgsn->gsup_client = NULL; + + talloc_free(sgsn->gprs_ipa_client->ipac); + talloc_free(sgsn->gprs_ipa_client); + sgsn->gprs_ipa_client = NULL; }
/* @@ -1842,7 +1865,7 @@ static void test_ggsn_selection(void)
printf("Testing GGSN selection\n");
- gprs_gsup_client_send_cb = my_gprs_gsup_client_send_dummy; + gprs_ipa_client_send_gsup_cb = my_gprs_ipa_client_send_gsup_dummy;
/* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); @@ -1961,9 +1984,308 @@ static void test_ggsn_selection(void) sgsn_ggsn_ctx_free(ggcs[1]); sgsn_ggsn_ctx_free(ggcs[2]);
- gprs_gsup_client_send_cb = __real_gprs_gsup_client_send; + gprs_ipa_client_send_gsup_cb = __real_gprs_ipa_client_send_gsup; +} + +static void test_oap(void) +{ + printf("Testing OAP API\n - Config parsing\n"); + + // No ipa_server_addr set, so initialization should do nothing. + OSMO_ASSERT(gprs_ipa_client_init(sgsn) <= 0); + OSMO_ASSERT(sgsn->gprs_ipa_client == NULL); + + struct gprs_oap_config *config = &(sgsn->cfg.oap); + + struct gprs_oap_state _state = {0}; + struct gprs_oap_state *state = &_state; + + // verify uninitialized state at program start + OSMO_ASSERT(state->state == oap_uninitialized); + + // make sure filling with zeros means uninitialized, too + memset(state, 0, sizeof(*state)); + OSMO_ASSERT(state->state == oap_uninitialized); + + + // invalid sgsn_id and shared secret + config->sgsn_id = 0; + config->shared_secret = NULL; + OSMO_ASSERT( gprs_oap_init(config, state) == 0 ); + OSMO_ASSERT(state->state == oap_disabled); + + // reset state + memset(state, 0, sizeof(*state)); + + // only sgsn_id is invalid + config->sgsn_id = 0; + config->shared_secret = "0102030405060708090a0b0c0d0e0f10"; + OSMO_ASSERT( gprs_oap_init(config, state) == 0 ); + OSMO_ASSERT(state->state == oap_disabled); + + memset(state, 0, sizeof(*state)); + + // omitted shared_secret + config->sgsn_id = 12345; + config->shared_secret = NULL; + OSMO_ASSERT( gprs_oap_init(config, state) == 0 ); + OSMO_ASSERT(state->state == oap_disabled); + + memset(state, 0, sizeof(*state)); + + // invalid hex chars in shared_secret config + config->sgsn_id = 12345; + config->shared_secret = "non-hex"; + OSMO_ASSERT( gprs_oap_init(config, state) < 0 ); + OSMO_ASSERT(state->state == oap_config_error); + + memset(state, 0, sizeof(*state)); + + // shared secret too long (defined to be 16 octets) + config->sgsn_id = 12345; + config->shared_secret = "0102030405060708090a0b0c0d0e0f101112131415161718"; + OSMO_ASSERT( gprs_oap_init(config, state) < 0 ); + OSMO_ASSERT(state->state == oap_config_error); + + memset(state, 0, sizeof(*state)); + + // odd number of hex chars + config->sgsn_id = 12345; + config->shared_secret = "01020304050607081"; + OSMO_ASSERT( gprs_oap_init(config, state) < 0 ); + OSMO_ASSERT(state->state == oap_config_error); + + memset(state, 0, sizeof(*state)); + + // zero padding of shared secret (defined to be 16 octets) + config->sgsn_id = 12345; + config->shared_secret = "0102030405060708"; + OSMO_ASSERT( gprs_oap_init(config, state) == 0 ); + OSMO_ASSERT(state->state == oap_initialized); + OSMO_ASSERT(strcmp("01020304050607080000000000000000", + osmo_hexdump_nospc(state->shared_secret, 16)) == 0); + + memset(state, 0, sizeof(*state)); + + + + // mint configuration + config->sgsn_id = 12345; + config->shared_secret = "0102030405060708090a0b0c0d0e0f10"; + OSMO_ASSERT( gprs_oap_init(config, state) == 0 ); + OSMO_ASSERT(state->state == oap_initialized); + OSMO_ASSERT(strcmp(config->shared_secret, + osmo_hexdump_nospc(state->shared_secret, 16)) == 0); + + printf(" - AUTN failure\n"); + uint8_t rx_random[16]; + uint8_t rx_autn[16]; + + uint8_t tx_sres[4]; + uint8_t tx_kc[8]; + + osmo_hexparse("0102030405060708090a0b0c0d0e0f10", + rx_random, 16); + + // wrong autn (by one bit) + osmo_hexparse("247d1e1c7fc1000008cc536e8788b027", + rx_autn, 16); + OSMO_ASSERT(gprs_oap_evaluate_challenge(state, rx_random, rx_autn, + tx_sres, tx_kc) + == -2); + + printf(" - AUTN success\n"); + // all correct + osmo_hexparse("347d1e1c7fc1000008cc536e8788b027", + rx_autn, 16); + // a successful return value here indicates correct rx_autn + OSMO_ASSERT(gprs_oap_evaluate_challenge(state, rx_random, rx_autn, + tx_sres, tx_kc) + == 0); + OSMO_ASSERT(strcmp("ce9da581", osmo_hexdump_nospc(tx_sres, sizeof(tx_sres))) == 0); + OSMO_ASSERT(strcmp("0a8356d779b197dd", osmo_hexdump_nospc(tx_kc, sizeof(tx_kc))) == 0); + + + // refuse to evaluate in uninitialized state + state->state = oap_uninitialized; + OSMO_ASSERT(gprs_oap_evaluate_challenge(state, rx_random, rx_autn, + tx_sres, tx_kc) + == -1); + state->state = oap_disabled; + OSMO_ASSERT(gprs_oap_evaluate_challenge(state, rx_random, rx_autn, + tx_sres, tx_kc) + == -1); + state->state = oap_config_error; + OSMO_ASSERT(gprs_oap_evaluate_challenge(state, rx_random, rx_autn, + tx_sres, tx_kc) + == -1); }
+static int inject_rx_oap_message(const struct gprs_oap_message *oapm) +{ + fprintf(stderr, "inject_rx_oap_message: Injecting OAP Reply: %d\n", (int)oapm->message_type); + + struct gprs_ipa_client *gipac = sgsn->gprs_ipa_client; + OSMO_ASSERT(gipac); + + struct msgb *msg; + int rc; + + msg = ipa_client_msgb_alloc(); + OSMO_ASSERT(msg != NULL); + + gprs_oap_encode(msg, oapm); + + unsigned char *l2h = msg->data; + + ipa_prepend_header_ext(msg, IPAC_PROTO_OSMO); + ipa_msg_push_header(msg, IPAC_PROTO_EXT_OAP); + + msg->l2h = l2h; + OSMO_ASSERT(msg->l2h); + + rc = gprs_oap_rx(gipac, msg); + + msgb_free(msg); + + return rc; +} + + +void my_ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg) +{ + // Simulate the remote side, which replies to registration etc: + // decode the msgb that the sgsn sends, and feed the matching reply to + // gprs_oap_rx(), as msgb. + + uint8_t *data = msgb_l2(msg); + size_t data_len = msgb_l2len(msg); + int rc = 0; + + struct gprs_oap_message oap_msg = {0}; + + OSMO_ASSERT(data); + rc = gprs_oap_decode(data, data_len, &oap_msg); + + if (rc < 0) { + printf("my_ipa_client_conn_send: decoding OAP message fails with error '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, -rc), -rc); + goto dealloc; + } + + fprintf(stderr, "my_ipa_client_conn_send: Caught outgoing IPA message: %d\n", (int)oap_msg.message_type); + + switch (oap_msg.message_type) { + + case GPRS_OAP_MSGT_REGISTER_REQUEST: + // reply with challenge + { + OSMO_ASSERT(oap_msg.sgsn_id == 12345); + OSMO_ASSERT(!oap_msg.rand_present); + OSMO_ASSERT(!oap_msg.autn_present); + OSMO_ASSERT(!oap_msg.sres_present); + OSMO_ASSERT(!oap_msg.kc_present); + + struct gprs_oap_message oap_reply = {0}; + oap_reply.message_type = GPRS_OAP_MSGT_CHALLENGE_REQUEST; + + osmo_hexparse("0102030405060708090a0b0c0d0e0f10", + oap_reply.rand, 16); + oap_reply.rand_present = 1; + + osmo_hexparse("347d1e1c7fc1000008cc536e8788b027", + oap_reply.autn, 16); + oap_reply.autn_present = 1; + + OSMO_ASSERT(inject_rx_oap_message(&oap_reply) >= 0); + } + + break; + + case GPRS_OAP_MSGT_CHALLENGE_RESULT: + // verify challenge reply, reply with registration ack + { + OSMO_ASSERT(!oap_msg.sgsn_id); // not present + OSMO_ASSERT(!oap_msg.rand_present); + OSMO_ASSERT(!oap_msg.autn_present); + OSMO_ASSERT(oap_msg.sres_present); + OSMO_ASSERT(oap_msg.kc_present); + + OSMO_ASSERT(strcmp("ce9da581", osmo_hexdump_nospc(oap_msg.sres, sizeof(oap_msg.sres))) == 0); + OSMO_ASSERT(strcmp("0a8356d779b197dd", osmo_hexdump_nospc(oap_msg.kc, sizeof(oap_msg.kc))) == 0); + + struct gprs_oap_message oap_reply = {0}; + oap_reply.message_type = GPRS_OAP_MSGT_REGISTER_RESULT; + + OSMO_ASSERT(inject_rx_oap_message(&oap_reply) >= 0); + } + break; + + case GPRS_OAP_MSGT_REGISTER_ERROR: + case GPRS_OAP_MSGT_REGISTER_RESULT: + case GPRS_OAP_MSGT_CHALLENGE_REQUEST: + case GPRS_OAP_MSGT_CHALLENGE_ERROR: + printf("Not handled in this test: OAP message type %d\n", (int)oap_msg.message_type); + rc = -1; + goto dealloc; + + default: + printf("Unknown OAP message type: %d\n", (int)oap_msg.message_type); + rc = -2; + goto dealloc; + } + +dealloc: + msgb_free(msg); +} + +static void test_sgsn_registration(void) +{ + printf("Testing SGSN registration\n"); + + /* The sgsn->gprs_ipa_client will not have been initialized, because + cfg.ipa_server_addr is unset. Allocate empty structs to use them in + these tests. */ + OSMO_ASSERT(sgsn->gprs_ipa_client == NULL); + sgsn->gprs_ipa_client = talloc_zero(tall_bsc_ctx, struct gprs_ipa_client); + sgsn->gprs_ipa_client->ipac = talloc_zero(tall_bsc_ctx, struct ipa_client); + OSMO_ASSERT(sgsn->gprs_ipa_client && sgsn->gprs_ipa_client->ipac); + + // simulate working connection. + ipa_client_conn_send_cb = my_ipa_client_conn_send; + sgsn->gprs_ipa_client->ipac->is_connected = 1; + + struct gprs_ipa_client *gipac = sgsn->gprs_ipa_client; + + struct gprs_oap_config *config = &sgsn->cfg.oap; + struct gprs_oap_state *state = &gipac->oap; + + // make sure of clean slate + memset(state, 0, sizeof(*state)); + + config->sgsn_id = 12345; + config->shared_secret = "0102030405060708090a0b0c0d0e0f10"; + OSMO_ASSERT(gprs_oap_init(config, state) == 0); + + OSMO_ASSERT(gprs_oap_register(gipac) == 0); + + // my_ipa_client_conn_send (wrapped) will have caught this registration + // request and sent the test reply to gprs_oap_rx, which has in turn + // sent the next response using my_ipa_client_conn_send, and again + // gprs_oap_rx has evaluated the final result, all of this before above + // gprs_oap_register() has exited. So just evaluate the results. + + OSMO_ASSERT(state->state == oap_registered); + OSMO_ASSERT(state->challenges_count == 1); + + ipa_client_conn_send_cb = __real_ipa_client_conn_send; + + talloc_free(sgsn->gprs_ipa_client->ipac); + talloc_free(sgsn->gprs_ipa_client); + sgsn->gprs_ipa_client = NULL; +} + + static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", @@ -2029,7 +2351,6 @@ int main(int argc, char **argv) tall_msgb_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "msgb");
sgsn_auth_init(); - gprs_subscr_init(sgsn);
test_llme(); test_subscriber(); @@ -2052,6 +2373,8 @@ int main(int argc, char **argv) test_gmm_ptmsi_allocation(); test_apn_matching(); test_ggsn_selection(); + test_oap(); + test_sgsn_registration(); printf("Done\n");
talloc_report_full(osmo_sgsn_ctx, stderr); diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok index 7913a39..0eea1dd 100644 --- a/openbsc/tests/sgsn/sgsn_test.ok +++ b/openbsc/tests/sgsn/sgsn_test.ok @@ -27,4 +27,9 @@ Testing P-TMSI allocation - Repeated RA Update Request Testing APN matching Testing GGSN selection +Testing OAP API + - Config parsing + - AUTN failure + - AUTN success +Testing SGSN registration Done
On 24 Sep 2015, at 13:44, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
Good Morning,
in general this is a bit too much to review in one piece.
+struct gprs_ipa_client {
- struct ipa_client *ipac;
 
In general we subclass by embedding the parent class directly because then you can do upwards casts very easily and can leave the ->data pointer for something else?
+/* This is the config part for vty. It is essentially copied in gprs_oap_state,
- where values are copied over once the config is considered valid. The shared
 
- secret is converted from hex string to octet buffer, the sgsn_id is simply
 
- copied. Is this separation really necessary? */
 +struct gprs_oap_config {
- uint16_t sgsn_id;
 - const char *shared_secret;
 +};
maybe we have two secrets to fill out K and OP(c) with different values?
+struct gprs_oap_state {
- enum {
 oap_uninitialized = 0, // just allocated.oap_disabled, // disabled by config.oap_config_error, // <-- TODO really?oap_initialized, // shared_secret valid.oap_requested_challenge,oap_sent_challenge_result,oap_registered- } state;
 
upper case like all our enums?
- uint16_t sgsn_id;
 - uint8_t shared_secret[16];
 
good point. So here it is 16bytes and up there it is a const char *. Maybe use uint8_t everywhere and then re-write the secrets as hexdump?
-/* (C) 2014 by Sysmocom s.f.m.c. GmbH +/* (C) 2015 by Sysmocom s.f.m.c. GmbH
Domain of copy-right. Is this 2014-2015, 2014,2015? Probably more like 2014,2015 here (yes, the bits from 2014 will expire at some point
encode_big_endian(pdp_info->pdp_type | 0xf000,GPRS_GSUP_PDP_TYPE_SIZE));
gprs_encode_big_endian(pdp_info->pdp_type | 0xf000,GPRS_GSUP_PDP_TYPE_SIZE));
how much effort would it be to split this part and the ipa->gsup client part in two other commits and then adding OAP (even split up with message encoding/decoding)?
+static void gprs_ipa_client_updown_cb(struct ipa_client *ipac, int up) +{
- struct gprs_ipa_client *gipac = ipac->data;
 - if (up && (gipac->oap.sgsn_id != 0)) {
 if (gprs_oap_register(gipac) < 0) {/* TODO: fail fatally */
log an error at least.
+struct gprs_ipa_client *gprs_ipa_client_create(const char *ip_addr,
unsigned int tcp_port)+{
- return gipac;
 +failed:
LOGL_ERROR?
- gprs_ipa_client_destroy(gipac);
 - return NULL;
 +}
+int gprs_oap_init(struct gprs_oap_config *config, struct gprs_oap_state *state) +{
- OSMO_ASSERT(state->state == oap_uninitialized);
 - if (config->sgsn_id == 0)
 goto disable;- if (!(config->shared_secret) || (strlen(config->shared_secret) == 0))
 goto disable;- /* this should probably happen in config parsing place?? */
 
yes ;) the VTY commands should create/parse hex
- int secret_len = osmo_hexparse(config->shared_secret,
 state->shared_secret,sizeof(state->shared_secret));
- struct osmo_sub_auth_data auth = {
 .type = OSMO_AUTH_TYPE_GSM,
UMTS and not GSM ;)
.algo = OSMO_AUTH_ALG_MILENAGE,- };
 - OSMO_ASSERT(sizeof(auth.u.umts.opc) == sizeof(state->shared_secret));
 - OSMO_ASSERT(sizeof(auth.u.umts.k) == sizeof(state->shared_secret));
 - memcpy(auth.u.umts.opc, state->shared_secret, sizeof(auth.u.umts.opc));
 
- memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k));
 - memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k));
 
one copy too many. What did you try to create? But yes k and opc from two diferent keys would be great.
+int gprs_oap_register(struct gprs_ipa_client *gipac) +{
- struct gprs_oap_state *state = &gipac->oap;
 - OSMO_ASSERT(state);
 - OSMO_ASSERT(state->sgsn_id);
 - struct msgb *msg = ipa_client_msgb_alloc();
 - struct gprs_oap_message oap_msg = {0};
 - oap_msg.message_type = GPRS_OAP_MSGT_REGISTER_REQUEST;
 - oap_msg.sgsn_id = state->sgsn_id;
 - gprs_oap_encode(msg, &oap_msg);
 - state->state = oap_requested_challenge;
 - return gprs_ipa_client_send_oap(gipac, msg);
 +}
separating encoding and sending will allow us to unittest the encoder/decoder separately
- switch (oap_msg.message_type) {
 - case GPRS_OAP_MSGT_CHALLENGE_REQUEST:
 
don’t do this in-place. Please call a function
+int gprs_oap_decode(const uint8_t *const_data, size_t data_len,
struct gprs_oap_message *oap_msg)+{
- static const struct gprs_oap_message empty_oap_message = {0};
 - *oap_msg = empty_oap_message;
 
why do we need to copy this? An alternative to memset? Taken from gsup code?
+/* Wishful thinking to generate a constant time compare */ +int gprs_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) +{
don’t copy and paste from the NAT but at least put it into a commonly used function (e.g. somewhere in libcommom/)?
- if (ipac->updown_cb != NULL)
 ipac->updown_cb(ipac, up);
Does it make sense to have the updown_cb be NULL?
- if (hh->proto != IPAC_PROTO_OSMO)
 goto invalid;- if (!he || msgb_l2len(msg) < sizeof(*he) ||
 he->proto != IPAC_PROTO_EXT_GSUP)
- if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid;
 
I think you want to have the first hh->proto check here. As the second part will use he which is only valid/there if it is IPAC_PROTO_OSMO?
- // l2h is not sent over the wire, but for the test suite it makes sense
 - // to make l2h point at the IPA message payload.
 
Please don’t use C99 comments, e.g. use /* */ But why do we add the comment what changed from the previous implementation?
);
- if (g_cfg->gsup_server_addr.sin_addr.s_addr)
 vty_out(vty, " gsup remote-ip %s%s",inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE);- if (g_cfg->gsup_server_port)
 vty_out(vty, " gsup remote-port %d%s",g_cfg->gsup_server_port, VTY_NEWLINE);
- if (g_cfg->ipa_server_addr.sin_addr.s_addr)
 vty_out(vty, " ipa remote-ip %s%s",inet_ntoa(g_cfg->ipa_server_addr.sin_addr), VTY_NEWLINE);- if (g_cfg->ipa_server_port)
 vty_out(vty, " ipa remote-port %d%s",g_cfg->ipa_server_port, VTY_NEWLINE);
“gsup” vs. “ipa”.
* If you change config files you need to provide an upgrade path from a config file that is using “gsup”. E.g. by adding a DEFUN_DEPRECATED for the old parameter
* “ipa” is such a vague name. The SGSN might have several IPA connections to different kind of systems. One might be used for GSUP. Another one might be for sending telemetric data. We use this connection for GSUP so let’s keep the current configuration settings.
And this you probably don’t want the s/gsup_client/gprs_ipa_client/ renaming as well. As we might have multiple IPA connections in the future to different systems.
-/* Wishful thinking to generate a constant time compare */ -static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count)
ah ha, but then please don’t call it gprs_constant_time..
static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen) {
if (vec.res_len != 8) { LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n",
keylen, conf->nr);
(int)vec.res_len, conf->nr);
separate commit. And what type is res_len? size_t? then please change the format identifier.
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index ea29fce..3b45b3f 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am
I am afraid you really need to split this change a bit more. My attention span is over here.
/me replying to two mails in one
In summary, the questions I'd like answered, see below for details: - using memcmp() is ok? (re constant_time_cmp())
On Mon, Sep 28, 2015 at 07:23:56AM +0200, Holger Freyther wrote:
not just remove the above but provide a short general statement about what this protocol should provide.
done, I hope to satisfaction. (patch will follow)
GSM MAP is a protocol, the MAP Proxy is a system to relay these. You could also opt for more generic terms like “Client”/“Server” “Consumer”/“Provider”
Changed to "client"/"server".
Okay, I just said something else to Harald but adding (and not implementing) the following. If the server key matches but the SQN is wrong an AUTS will be included by the client?
Spec'd a Sync Request message, but will not implement yet.
memcpy(auth.u.umts.opc, state->shared_secret, sizeof(auth.u.umts.opc)); memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k));
maybe we have two secrets to fill out K and OP(c) with different values?
&
yes k and opc from two diferent keys would be great.
I can make it one double-length shared secret and use one half each? Ah whatever, I'll add secret_k and secret_opc, who knows if we'd like to have an operator specifc key one day...
On Mon, Sep 28, 2015 at 07:56:17AM +0200, Holger Freyther wrote:
in general this is a bit too much to review in one piece.
you mean the patch is too large... ;)
+struct gprs_ipa_client {
- struct ipa_client *ipac;
 In general we subclass by embedding the parent class directly because then
Problem being, we get a pointer from the constructor:
struct ipa_client *ipa_client_create(const char *ip_addr, [...]
(was gsup_client_create(), i.e. someone else wrote this.) Should I refactor this to
void ipa_client_create(struct ipa_client*, ...)
?? I'm trying to limit API refactoring efforts though.
upper case like all our enums?
ACK
- uint16_t sgsn_id;
 - uint8_t shared_secret[16];
 good point. So here it is 16bytes and up there it is a const char *. Maybe use uint8_t everywhere and then re-write the secrets as hexdump?
&
- /* this should probably happen in config parsing place?? */
 yes ;) the VTY commands should create/parse hex
In a subsequent commit on the branch, the const char * has already disappeared into the vty (patch not posted yet).
-/* (C) 2014 by Sysmocom s.f.m.c. GmbH +/* (C) 2015 by Sysmocom s.f.m.c. GmbH
Domain of copy-right. Is this 2014-2015, 2014,2015? Probably more like 2014,2015 here (yes, the bits from 2014 will expire at some point
I have no idea about notation of copyright years... I'll write "2014,2015", then.
encode_big_endian(pdp_info->pdp_type | 0xf000,GPRS_GSUP_PDP_TYPE_SIZE));
gprs_encode_big_endian(pdp_info->pdp_type | 0xf000,GPRS_GSUP_PDP_TYPE_SIZE));how much effort would it be to split this part and the ipa->gsup client part in two other commits and then adding OAP (even split up with message encoding/decoding)?
Some effort. I had considered it, but decided against it in the end. Slightly feels like unneccessary effort. If you need smaller review chunks though, it adds motivation to further split.
Not sure what you mean by "split with message encoding/decoding", like, commit the oap_messages.c in a separate commit? IMHO it's already separate.
+static void gprs_ipa_client_updown_cb(struct ipa_client *ipac, int up) +{
- struct gprs_ipa_client *gipac = ipac->data;
 - if (up && (gipac->oap.sgsn_id != 0)) {
 if (gprs_oap_register(gipac) < 0) {/* TODO: fail fatally */log an error at least.
Am not clear yet how an error in a protocol callback should propagate to the main loop and barf there. Certainly something grave should happen.
+struct gprs_ipa_client *gprs_ipa_client_create(const char *ip_addr,
unsigned int tcp_port)+{
- return gipac;
 +failed:
LOGL_ERROR?
The only possible failure cause is from ipa_client_create(), so *that* one should log an error instead. But that's "older" code again, and I'm trying to stay on topic... should I add error logging to the older code??
- struct osmo_sub_auth_data auth = {
 .type = OSMO_AUTH_TYPE_GSM,UMTS and not GSM ;)
erm, well, "was just checking if you're paying attention" ^_^
- memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k));
 - memcpy(auth.u.umts.k, state->shared_secret, sizeof(auth.u.umts.k));
 
Seems I had an echo on the 'p' key...
+int gprs_oap_register(struct gprs_ipa_client *gipac) +{
- struct gprs_oap_state *state = &gipac->oap;
 - OSMO_ASSERT(state);
 - OSMO_ASSERT(state->sgsn_id);
 - struct msgb *msg = ipa_client_msgb_alloc();
 - struct gprs_oap_message oap_msg = {0};
 - oap_msg.message_type = GPRS_OAP_MSGT_REGISTER_REQUEST;
 - oap_msg.sgsn_id = state->sgsn_id;
 - gprs_oap_encode(msg, &oap_msg);
 - state->state = oap_requested_challenge;
 - return gprs_ipa_client_send_oap(gipac, msg);
 +}
separating encoding and sending will allow us to unittest the encoder/decoder separately
gprs_oap_encode/_decode() can be called directly to test them? You mean I should have a separate create_oap_register_msg() function and call that from gprs_oap_register()? IMHO that's overkill in this instance... The registration message is part of a unit test already, I see no need to separate it further. Let me know if I'm wrong.
- switch (oap_msg.message_type) {
 - case GPRS_OAP_MSGT_CHALLENGE_REQUEST:
 don’t do this in-place. Please call a function
Don't do what in-place? You mean factor the guts of "case: { ... } break;" out to a new function?
BTW, I'd be faster grasping the context if you left the entire patch in the reply... no filename, no linenr... :)
- static const struct gprs_oap_message empty_oap_message = {0};
 - *oap_msg = empty_oap_message;
 why do we need to copy this? An alternative to memset? Taken from gsup code?
Yes, code pasting syndrome. I have no idea, would have used memset(). Looks interesting though :P
+/* Wishful thinking to generate a constant time compare */ +int gprs_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) +{
don’t copy and paste from the NAT but at least put it into a commonly used function (e.g. somewhere in libcommom/)?
I picked the scope most closely wrapping the two users: the gprs subdir. "gprs_utils" did sound like the proper kitchen sink for this...
Then again, why not use memcmp()? (this is also basically code I copied from GSUP)
- if (ipac->updown_cb != NULL)
 ipac->updown_cb(ipac, up);Does it make sense to have the updown_cb be NULL?
If no action upon connecting is required, no cb is needed. Here we'd like to call the gprs_oap_register() function once IPA is established, see gprs_ipa_client_updown_cb(). For example, GSUP doesn't need an updown_cb.
Note, the "bare IPA client" timers etc. are handled one level deeper, in the function that calls the ipac->updown_cb(): ipa_client_updown_cb().
- if (hh->proto != IPAC_PROTO_OSMO)
 goto invalid;- if (!he || msgb_l2len(msg) < sizeof(*he) ||
 he->proto != IPAC_PROTO_EXT_GSUP)
- if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid;
 I think you want to have the first hh->proto check here. As the second part will use he which is only valid/there if it is IPAC_PROTO_OSMO?
Hmmm. Is it? I'd love to assumed that there is always both a PROTO and PROTO_EXT value available... ipaccess_head_ext is defined in ipaccess.h, so is it really specific to IPAC_PROTO_OSMO?
- // l2h is not sent over the wire, but for the test suite it makes sense
 - // to make l2h point at the IPA message payload.
 Please don’t use C99 comments, e.g. use /* */
hrm. It's so much more effort :P I often disagree on how people use commenting styles... anyway, I'll submit to local rule. Is it: *never* use '//' ?
But why do we add the comment what changed from the previous implementation?
Without the change, l2h pointed at the wrong index and the messages could not be decoded in the test. Re-reading it now, though, makes me think that the order may be wrong entirely.
Last time I concluded that this causes l2h to point at the proto_ext header instead of the payload:
ipa_prepend_header_ext(msg, proto); ipa_msg_push_header(msg, proto_ext)
Like: ipa_prepend_header_ext() prepends the proto_ext byte, and then ipa_msg_push_header() again prepends the "Osmo" protocol header but makes l2h point at the ipa_prepend_header_ext() part.
Only, just now I noticed that the proto and proto_ext seem to be swapped.
I'll take another look at it, wait for next patch mail...
“gsup” vs. “ipa”.
- If you change config files you need to provide an upgrade path from a config file
 that is using “gsup”. E.g. by adding a DEFUN_DEPRECATED for the old parameter
good to know.
- “ipa” is such a vague name. The SGSN might have several IPA connections to
 different kind of systems. One might be used for GSUP. Another one might be for sending telemetric data. We use this connection for GSUP so let’s keep the current configuration settings.
Was not aware of it, my name choice based on the naive assumption that there is just the one IPA connection.
But we're also talking OAP on that IPA line. Can I call it gprs_map_proxy_client, plz?
-/* Wishful thinking to generate a constant time compare */ -static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count)
ah ha, but then please don’t call it gprs_constant_time..
wasn't me, was it. I just copied it around. I guess I'll use memcmp() and leave constant_time_cmp() where it belongs: a dark corner of a static C file context.
if (vec.res_len != 8) { LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n",
keylen, conf->nr);
(int)vec.res_len, conf->nr);separate commit. And what type is res_len? size_t? then please change the format identifier.
Hey, how did that sneak in here? That patch was committed separately on master. I must have mixed up a branchpoint, this change was supposed to be separate.
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index ea29fce..3b45b3f 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am
I am afraid you really need to split this change a bit more. My attention span is over here.
come on, just adding entries to a Makefile? ;) And the tests, true.
Admitted, I have enough to polish as it is. I'll come back with smaller chunks, if the effort remains reasonable.
~Neels
On 29 Sep 2015, at 15:10, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
/me replying to two mails in one
In summary, the questions I'd like answered, see below for details:
- using memcmp() is ok? (re constant_time_cmp())
 
When comparing the result we should use a constant time compare as otherwise the attacker can find out (by timing) how many bytes of the result were correct and by this determine/find the secrets
The classic memcmp will stop on the first byte that is not the same.
yes k and opc from two diferent keys would be great.
I can make it one double-length shared secret and use one half each? Ah whatever, I'll add secret_k and secret_opc, who knows if we'd like to have an operator specifc key one day…
thanks
+struct gprs_ipa_client {
- struct ipa_client *ipac;
 In general we subclass by embedding the parent class directly because then
Problem being, we get a pointer from the constructor:
struct ipa_client *ipa_client_create(const char *ip_addr, [...]
(was gsup_client_create(), i.e. someone else wrote this.) Should I refactor this to
void ipa_client_create(struct ipa_client*, ...)
?? I'm trying to limit API refactoring efforts though.
you are right, then leave it as it is.
Not sure what you mean by "split with message encoding/decoding", like, commit the oap_messages.c in a separate commit? IMHO it's already separate.
but I review top to bottom and then stop somewhere in the middle. if you have separate patches I at least have a natural break somewhere and can easily resume it. :)
log an error at least.Am not clear yet how an error in a protocol callback should propagate to the main loop and barf there. Certainly something grave should happen.
“log an error” means LOGP(BLA, LOGL_ERROR, “Something bad happened %d\n”, rc);
- switch (oap_msg.message_type) {
 - case GPRS_OAP_MSGT_CHALLENGE_REQUEST:
 don’t do this in-place. Please call a function
Don't do what in-place? You mean factor the guts of "case: { ... } break;" out to a new function?
Yes, factor out the significant code between the {}.
- if (hh->proto != IPAC_PROTO_OSMO)
 goto invalid;- if (!he || msgb_l2len(msg) < sizeof(*he) ||
 he->proto != IPAC_PROTO_EXT_GSUP)
- if (!he || msgb_l2len(msg) < sizeof(*he)) goto invalid;
 I think you want to have the first hh->proto check here. As the second part will use he which is only valid/there if it is IPAC_PROTO_OSMO?
Hmmm. Is it? I'd love to assumed that there is always both a PROTO and PROTO_EXT value available... ipaccess_head_ext is defined in ipaccess.h, so is it really specific to IPAC_PROTO_OSMO?
If there is a PROTO_EXT there is a PROTO but a PROTO does not imply the PROTO_EXT.
hrm. It's so much more effort :P I often disagree on how people use commenting styles... anyway, I'll submit to local rule. Is it: *never* use '//‘ ?
Only if the Linux Kernel starts using //
- “ipa” is such a vague name. The SGSN might have several IPA connections to
 different kind of systems. One might be used for GSUP. Another one might be for sending telemetric data. We use this connection for GSUP so let’s keep the current configuration settings.
Was not aware of it, my name choice based on the naive assumption that there is just the one IPA connection.
But we're also talking OAP on that IPA line. Can I call it gprs_map_proxy_client, plz?
Check with Jacob, he had a reason to pick gsup. For me both names are fine (and gsup slightly favoarable as it is less long).
-/* Wishful thinking to generate a constant time compare */ -static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count)
ah ha, but then please don’t call it gprs_constant_time..
wasn't me, was it. I just copied it around. I guess I'll use memcmp() and leave constant_time_cmp() where it belongs: a dark corner of a static C file context.
you probably want a constant time comparison too.
cheers holger
On Tue, Sep 29, 2015 at 03:25:15PM +0200, Holger Freyther wrote:
Am not clear yet how an error in a protocol callback should propagate to the main loop and barf there. Certainly something grave should happen.
“log an error” means LOGP(BLA, LOGL_ERROR, “Something bad happened %d\n”, rc);
yes yes, instead I meant: when a fatal error happens in a callback, how do I make the main loop exit. Specifically, if SGSN registration failed, then do (from the cb), in order of effort: LOGP and... - ignore otherwise, SGSN services may not work. - exit(-ENOTREGISTERED). - set a flag to break main loop. - set a timer at program start, and if we're not registered after a while, exit. - ...
Is there a typical way for the osmo universe?
~Neels
On 30 Sep 2015, at 12:49, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
On Tue, Sep 29, 2015 at 03:25:15PM +0200, Holger Freyther wrote:
Am not clear yet how an error in a protocol callback should propagate to the main loop and barf there. Certainly something grave should happen.
“log an error” means LOGP(BLA, LOGL_ERROR, “Something bad happened %d\n”, rc);
yes yes, instead I meant: when a fatal error happens in a callback, how do I make the main loop exit. Specifically, if SGSN registration failed, then do (from the cb), in order of effort: LOGP and...
- ignore otherwise, SGSN services may not work.
 
mostly this and retry and hope that the machine has fixed itself. ;)
Hello all,
these patches are sitting in branch neels/sgsn-id in openbsc.git. The planned new feature is outlined in the commit log messages.
It's my first larger patch submission for openbsc, which I am developing as a sysmocom employee, while this work is sponsored by On-Waves ehf.
It's not completely done yet, in the sense of more tests and config UI, and the authentication method/direction may be subject to discussion:
Currently, the server (MAP proxy) sends a 16 octet AUTN to authenticate itself. The client (SGSN) only sends a 4 byte SRES in response. IMHO that's not enough, so I've made the SGSN also send the Kc along with the SRES as a challenge response. That's a bit untypical, since the Kc is usually kept secret on both sides, to use as encryption key. We don't use it as encryption key, but it could make sense to turn the authn process around instead: let the *client* send a 16 bit AUTN, and have the server reply with 4 SRES octets (and omit Kc). This would make it more difficult to spoof an SGSN, while keeping Kc private as usual. (If a fake SGSN is accepted, the upstream network infra may be compromised. Guarding against a spoofed MAP proxy is less security sensitive, so 4 octets may suffice there.)
Any comments are more than welcome!
Thanks,
~Neels
On Thu, Sep 24, 2015 at 01:44:06PM +0200, Neels Hofmeyr wrote:
Sponsored-by: On-Waves ehf
openbsc/doc/osmocom-authn-protocol.txt | 191 +++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 openbsc/doc/osmocom-authn-protocol.txt
[...]
On Thu, Sep 24, 2015 at 01:44:06PM +0200, Neels Hofmeyr wrote:
...The remote peer is either a service +that understands the protocol natively or a wrapper service that maps the +messages to/from real MAP messages that can be used to directly communicate +with an HLR.
^ That's copy-pasted from the GSUP spec and not applicable. Will remove.
~Neels
Hi Neels,
welcome to OpenBSC code :)
Osmocom Authentication Protocol (OAP)
I would argue it makes sense to at least specify/define the protocol also to support UMTS AKA, not just plain-old GSM authentication.
This is important * for future compatibility once the SGSN suppots 3G * to use UMTS AKA for increased security over GERAN (GPRS/EDGE RAN)
Regards, Harald
On 24 Sep 2015, at 22:14, Harald Welte laforge@gnumonks.org wrote:
Hi Neels,
welcome to OpenBSC code :)
Osmocom Authentication Protocol (OAP)I would argue it makes sense to at least specify/define the protocol also to support UMTS AKA, not just plain-old GSM authentication.
This is important
- for future compatibility once the SGSN suppots 3G
 - to use UMTS AKA for increased security over GERAN (GPRS/EDGE RAN)
 
OAP is to authenticate something like the A-link, GSUP link or maybe even MNCC over TCP/IP, or a USSD provider, etc. It is using “AKA” right now but in a restricted mode:
* SQN will be 0 (because the clients might have no way to persistently store the SQNs). Yes, this will allow a replay against the client.[1]
* There is not “AuthenticationFailure” message with the AUTS. As the SQN will always be fixed in the first iteration there should not be a need to re-synchronize.
[1] It is a trade off in efforts. The clients can not store a SQN, the last RANDS, etc. They could in theory start with a random RAND and client/server will go through one re-synchronization of the SQN. I obviously made a trade-off here and this protocol allows us to add SQN number handling in the future and client API users are not impacted.
On 24 Sep 2015, at 13:44, Neels Hofmeyr nhofmeyr@sysmocom.de wrote:
+This document describes the remote protocol that is used by the SGSN and MAP +proxy to authenticate each other. The protocol and the messages are designed +after the corresponding MAP messages (see GSM 09.02) with the following +differences:
not just remove the above but provide a short general statement about what this protocol should provide.
+Ideal communication sequence:
- SGSN MAP
 
Proxy
GSM MAP is a protocol, the MAP Proxy is a system to relay these. You could also opt for more generic terms like “Client”/“Server” “Consumer”/“Provider”
+When the SGSN has received a Challenge, it may verify the server's +authenticity, and reply with a CHALLENGE_RES message. This shall contain SRES +(and Kc?) authentication tokens generated by milenage from the same random +bytes received from the server and the same shared secet. If the SGSN cannot +verify the server's authenticity, it shall instead send a CHALLENGE_ERR +message.
Okay, I just said something else to Harald but adding (and not implementing) the following. If the server key matches but the SQN is wrong an AUTS will be included by the client?
No security expert here.. is it better to always include the AUTS or not? The attacker will then know the key was right.. and just needs one more try then. If using the wrong key to get the SQN.. it will always fail *shrug*
So maybe omit the AUTS as we don’t handle SQN failures right now.
the rest reads fine.
holger