This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.
Neels Hofmeyr nhofmeyr at sysmocom.deNeeds 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