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
--
2.1.4