From f15eeadb21f8c308c4fdb1e5c15b8fbc38264882 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 13 Jan 2023 16:58:17 +0100 Subject: layer23: Move subscriber.{c,h} under common/ Some of those can be reused by other apps (like modem). Change-Id: I3727e40bcc9a4ee93aaf2c4ced070cc789653e80 --- .../layer23/include/osmocom/bb/common/Makefile.am | 1 + src/host/layer23/include/osmocom/bb/common/ms.h | 2 +- .../layer23/include/osmocom/bb/common/subscriber.h | 119 ++ .../layer23/include/osmocom/bb/mobile/Makefile.am | 2 +- .../layer23/include/osmocom/bb/mobile/subscriber.h | 119 -- src/host/layer23/src/common/Makefile.am | 1 + src/host/layer23/src/common/subscriber.c | 1355 ++++++++++++++++++++ src/host/layer23/src/mobile/Makefile.am | 1 - src/host/layer23/src/mobile/gsm48_mm.c | 2 +- src/host/layer23/src/mobile/subscriber.c | 1355 -------------------- 10 files changed, 1479 insertions(+), 1478 deletions(-) create mode 100644 src/host/layer23/include/osmocom/bb/common/subscriber.h delete mode 100644 src/host/layer23/include/osmocom/bb/mobile/subscriber.h create mode 100644 src/host/layer23/src/common/subscriber.c delete mode 100644 src/host/layer23/src/mobile/subscriber.c diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am index 54799bea..f09353bb 100644 --- a/src/host/layer23/include/osmocom/bb/common/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am @@ -13,6 +13,7 @@ noinst_HEADERS = \ sap_fsm.h \ sap_interface.h \ sim.h \ + subscriber.h \ support.h \ vty.h \ $(NULL) diff --git a/src/host/layer23/include/osmocom/bb/common/ms.h b/src/host/layer23/include/osmocom/bb/common/ms.h index 3a162937..db6b276a 100644 --- a/src/host/layer23/include/osmocom/bb/common/ms.h +++ b/src/host/layer23/include/osmocom/bb/common/ms.h @@ -7,8 +7,8 @@ /* FIXME no 'mobile' specific stuff should be here */ #include -#include #include +#include #include #include #include diff --git a/src/host/layer23/include/osmocom/bb/common/subscriber.h b/src/host/layer23/include/osmocom/bb/common/subscriber.h new file mode 100644 index 00000000..698b0fdc --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/subscriber.h @@ -0,0 +1,119 @@ +#ifndef _SUBSCRIBER_H +#define _SUBSCRIBER_H + +/* GSM 04.08 4.1.2.2 SIM update status */ +#define GSM_SIM_U0_NULL 0 +#define GSM_SIM_U1_UPDATED 1 +#define GSM_SIM_U2_NOT_UPDATED 2 +#define GSM_SIM_U3_ROAMING_NA 3 + +struct gsm_sub_plmn_list { + struct llist_head entry; + uint16_t mcc, mnc; +}; + +struct gsm_sub_plmn_na { + struct llist_head entry; + uint16_t mcc, mnc; + uint8_t cause; +}; + +#define GSM_IMSI_LENGTH 16 + +#define GSM_SIM_IS_READER(type) \ + (type == GSM_SIM_TYPE_L1PHY || type == GSM_SIM_TYPE_SAP) + +enum { + GSM_SIM_TYPE_NONE = 0, + GSM_SIM_TYPE_L1PHY, + GSM_SIM_TYPE_TEST, + GSM_SIM_TYPE_SAP +}; + +struct gsm_subscriber { + struct osmocom_ms *ms; + + /* status */ + uint8_t sim_type; /* type of sim */ + uint8_t sim_valid; /* sim inserted and valid */ + uint8_t ustate; /* update status */ + uint8_t imsi_attached; /* attached state */ + + /* IMSI & co */ + char imsi[GSM_IMSI_LENGTH]; + char msisdn[31]; /* may include access codes */ + char iccid[21]; /* 20 + termination */ + + /* TMSI / LAI */ + uint32_t tmsi; /* invalid tmsi: 0xffffffff */ + uint16_t mcc, mnc, lac; /* invalid lac: 0x0000 */ + + + /* key */ + uint8_t key_seq; /* ciphering key sequence number */ + uint8_t key[8]; /* 64 bit */ + + /* other */ + struct llist_head plmn_list; /* PLMN Selector field */ + struct llist_head plmn_na; /* not allowed PLMNs */ + uint8_t t6m_hplmn; /* timer for hplmn search */ + + /* special things */ + uint8_t always_search_hplmn; + /* search hplmn in other countries also (for test cards) */ + uint8_t any_timeout; + /* timer to restart 'any cell selection' */ + char sim_name[31]; /* name to load/save sim */ + char sim_spn[17]; /* name of service privider */ + + /* PLMN last registered */ + uint8_t plmn_valid; + uint16_t plmn_mcc, plmn_mnc; + + /* our access */ + uint8_t acc_barr; /* if we may access, if cell barred */ + uint16_t acc_class; /* bitmask of what we may access */ + + /* talk to SIM */ + uint8_t sim_state; + uint8_t sim_pin_required; /* state: wait for PIN */ + uint8_t sim_file_index; + uint32_t sim_handle_query; + uint32_t sim_handle_update; + uint32_t sim_handle_key; + + /* SMS */ + char sms_sca[22]; +}; + +int gsm_subscr_init(struct osmocom_ms *ms); +int gsm_subscr_exit(struct osmocom_ms *ms); +int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac, uint32_t tmsi, uint8_t imsi_attached); +int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, + uint8_t res_type, uint16_t param_len, const uint8_t *param_val); +int gsm_subscr_sapcard(struct osmocom_ms *ms); +int gsm_subscr_remove_sapcard(struct osmocom_ms *ms); +int gsm_subscr_simcard(struct osmocom_ms *ms); +void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, + int8_t mode); +int gsm_subscr_write_loci(struct osmocom_ms *ms); +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, + uint8_t *rand, uint8_t no_sim); +int gsm_subscr_remove(struct osmocom_ms *ms); +void new_sim_ustate(struct gsm_subscriber *subscr, int state); +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause); +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv); +char *gsm_check_imsi(const char *imsi); +int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr); + +#endif /* _SUBSCRIBER_H */ + diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am index e18b2bd0..6d9950be 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am @@ -1,4 +1,4 @@ noinst_HEADERS = gsm322.h gsm480_ss.h gsm411_sms.h gsm48_cc.h gsm48_mm.h \ - gsm48_rr.h mncc.h settings.h subscriber.h \ + gsm48_rr.h mncc.h settings.h \ transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \ app_mobile.h voice.h gapk_io.h diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h deleted file mode 100644 index 698b0fdc..00000000 --- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef _SUBSCRIBER_H -#define _SUBSCRIBER_H - -/* GSM 04.08 4.1.2.2 SIM update status */ -#define GSM_SIM_U0_NULL 0 -#define GSM_SIM_U1_UPDATED 1 -#define GSM_SIM_U2_NOT_UPDATED 2 -#define GSM_SIM_U3_ROAMING_NA 3 - -struct gsm_sub_plmn_list { - struct llist_head entry; - uint16_t mcc, mnc; -}; - -struct gsm_sub_plmn_na { - struct llist_head entry; - uint16_t mcc, mnc; - uint8_t cause; -}; - -#define GSM_IMSI_LENGTH 16 - -#define GSM_SIM_IS_READER(type) \ - (type == GSM_SIM_TYPE_L1PHY || type == GSM_SIM_TYPE_SAP) - -enum { - GSM_SIM_TYPE_NONE = 0, - GSM_SIM_TYPE_L1PHY, - GSM_SIM_TYPE_TEST, - GSM_SIM_TYPE_SAP -}; - -struct gsm_subscriber { - struct osmocom_ms *ms; - - /* status */ - uint8_t sim_type; /* type of sim */ - uint8_t sim_valid; /* sim inserted and valid */ - uint8_t ustate; /* update status */ - uint8_t imsi_attached; /* attached state */ - - /* IMSI & co */ - char imsi[GSM_IMSI_LENGTH]; - char msisdn[31]; /* may include access codes */ - char iccid[21]; /* 20 + termination */ - - /* TMSI / LAI */ - uint32_t tmsi; /* invalid tmsi: 0xffffffff */ - uint16_t mcc, mnc, lac; /* invalid lac: 0x0000 */ - - - /* key */ - uint8_t key_seq; /* ciphering key sequence number */ - uint8_t key[8]; /* 64 bit */ - - /* other */ - struct llist_head plmn_list; /* PLMN Selector field */ - struct llist_head plmn_na; /* not allowed PLMNs */ - uint8_t t6m_hplmn; /* timer for hplmn search */ - - /* special things */ - uint8_t always_search_hplmn; - /* search hplmn in other countries also (for test cards) */ - uint8_t any_timeout; - /* timer to restart 'any cell selection' */ - char sim_name[31]; /* name to load/save sim */ - char sim_spn[17]; /* name of service privider */ - - /* PLMN last registered */ - uint8_t plmn_valid; - uint16_t plmn_mcc, plmn_mnc; - - /* our access */ - uint8_t acc_barr; /* if we may access, if cell barred */ - uint16_t acc_class; /* bitmask of what we may access */ - - /* talk to SIM */ - uint8_t sim_state; - uint8_t sim_pin_required; /* state: wait for PIN */ - uint8_t sim_file_index; - uint32_t sim_handle_query; - uint32_t sim_handle_update; - uint32_t sim_handle_key; - - /* SMS */ - char sms_sca[22]; -}; - -int gsm_subscr_init(struct osmocom_ms *ms); -int gsm_subscr_exit(struct osmocom_ms *ms); -int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac, uint32_t tmsi, uint8_t imsi_attached); -int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, - uint8_t res_type, uint16_t param_len, const uint8_t *param_val); -int gsm_subscr_sapcard(struct osmocom_ms *ms); -int gsm_subscr_remove_sapcard(struct osmocom_ms *ms); -int gsm_subscr_simcard(struct osmocom_ms *ms); -void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, - int8_t mode); -int gsm_subscr_write_loci(struct osmocom_ms *ms); -int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, - uint8_t *rand, uint8_t no_sim); -int gsm_subscr_remove(struct osmocom_ms *ms); -void new_sim_ustate(struct gsm_subscriber *subscr, int state); -int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc); -int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc, uint8_t cause); -int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc); -int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, - void (*print)(void *, const char *, ...), void *priv); -void gsm_subscr_dump(struct gsm_subscriber *subscr, - void (*print)(void *, const char *, ...), void *priv); -char *gsm_check_imsi(const char *imsi); -int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr); - -#endif /* _SUBSCRIBER_H */ - diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am index 36342df5..4dfa1f35 100644 --- a/src/host/layer23/src/common/Makefile.am +++ b/src/host/layer23/src/common/Makefile.am @@ -24,6 +24,7 @@ liblayer23_a_SOURCES = \ sap_proto.c \ sap_interface.c \ sim.c \ + subscriber.c \ support.c \ sysinfo.c \ utils.c \ diff --git a/src/host/layer23/src/common/subscriber.c b/src/host/layer23/src/common/subscriber.c new file mode 100644 index 00000000..89cacb76 --- /dev/null +++ b/src/host/layer23/src/common/subscriber.c @@ -0,0 +1,1355 @@ +/* + * (C) 2010 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* enable to get an empty list of forbidden PLMNs, even if stored on SIM. + * if list is changed, the result is not written back to SIM */ +//#define TEST_EMPTY_FPLMN + +static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); +static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg); +static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); + +/* + * support + */ + +char *gsm_check_imsi(const char *imsi) +{ + int i; + + if (!imsi || strlen(imsi) != 15) + return "IMSI must have 15 digits!"; + + for (i = 0; i < strlen(imsi); i++) { + if (imsi[i] < '0' || imsi[i] > '9') + return "IMSI must have digits 0 to 9 only!"; + } + + return NULL; +} + +static char *sim_decode_bcd(uint8_t *data, uint8_t length) +{ + int i, j = 0; + static char result[32], c; + + for (i = 0; i < (length << 1); i++) { + if ((i & 1)) + c = (data[i >> 1] >> 4); + else + c = (data[i >> 1] & 0xf); + if (c == 0xf) + break; + result[j++] = c + '0'; + if (j == sizeof(result) - 1) + break; + } + result[j] = '\0'; + + return result; +} + +/* + * init/exit + */ + +int gsm_subscr_init(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + memset(subscr, 0, sizeof(*subscr)); + subscr->ms = ms; + + /* set TMSI / LAC invalid */ + subscr->tmsi = 0xffffffff; + subscr->lac = 0x0000; + + /* set key invalid */ + subscr->key_seq = 7; + + /* any cell selection timer timeout */ + subscr->any_timeout = ms->settings.any_timeout; + + /* init lists */ + INIT_LLIST_HEAD(&subscr->plmn_list); + INIT_LLIST_HEAD(&subscr->plmn_na); + + /* open SIM */ + subscr->sim_handle_query = sim_open(ms, subscr_sim_query_cb); + subscr->sim_handle_update = sim_open(ms, subscr_sim_update_cb); + subscr->sim_handle_key = sim_open(ms, subscr_sim_key_cb); + + return 0; +} + +int gsm_subscr_exit(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct llist_head *lh, *lh2; + + if (subscr->sim_handle_query) { + sim_close(ms, subscr->sim_handle_query); + subscr->sim_handle_query = 0; + } + if (subscr->sim_handle_update) { + sim_close(ms, subscr->sim_handle_update); + subscr->sim_handle_update = 0; + } + if (subscr->sim_handle_key) { + sim_close(ms, subscr->sim_handle_key); + subscr->sim_handle_key = 0; + } + + /* flush lists */ + llist_for_each_safe(lh, lh2, &subscr->plmn_list) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &subscr->plmn_na) { + llist_del(lh); + talloc_free(lh); + } + + return 0; +} + +/* + * test card + */ + +/* Attach test card, no SIM must be currently attached */ +int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac, uint32_t tmsi, uint8_t imsi_attached) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + char *error; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " + "is detached.\n"); + return -EBUSY; + } + + error = gsm_check_imsi(set->test_imsi); + if (error) { + LOGP(DMM, LOGL_ERROR, "%s\n", error); + return -EINVAL; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + subscr->sim_type = GSM_SIM_TYPE_TEST; + sprintf(subscr->sim_name, "test"); + subscr->sim_valid = 1; + if (imsi_attached && set->test_rplmn_valid) { + subscr->imsi_attached = imsi_attached; + subscr->ustate = GSM_SIM_U1_UPDATED; + } else + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + subscr->acc_barr = set->test_barr; /* we may access barred cell */ + subscr->acc_class = 0xffff; /* we have any access class */ + subscr->plmn_valid = set->test_rplmn_valid; + subscr->plmn_mcc = mcc; + subscr->plmn_mnc = mnc; + subscr->mcc = mcc; + subscr->mnc = mnc; + subscr->lac = lac; + subscr->tmsi = tmsi; + subscr->always_search_hplmn = set->test_always; + subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ + strcpy(subscr->imsi, set->test_imsi); + + LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n", + ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), + gsm_imsi_mnc(subscr->imsi)); + + if (subscr->plmn_valid) + LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x" + "(%s, %s)\n", gsm_print_mcc(mcc), + gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc), + gsm_get_mnc(mcc, mnc)); + else + LOGP(DMM, LOGL_INFO, "-> Test card not registered\n"); + if (subscr->imsi_attached) + LOGP(DMM, LOGL_INFO, "-> Test card attached\n"); + + /* insert card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; +} + +/* + * sim card + */ + +static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + strcpy(subscr->iccid, sim_decode_bcd(data, length)); + sprintf(subscr->sim_name, "sim-%s", subscr->iccid); + LOGP(DMM, LOGL_INFO, "received ICCID %s from SIM\n", subscr->iccid); + + return 0; +} + +static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + char *imsi; + + /* get actual length */ + if (length < 1) + return -EINVAL; + if (data[0] + 1 < length) { + LOGP(DMM, LOGL_NOTICE, "invalid length = %d\n", length); + return -EINVAL; + } + length = data[0]; + + /* decode IMSI, skip first digit (parity) */ + imsi = sim_decode_bcd(data + 1, length); + if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) { + LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %zu\n", + strlen(imsi) - 1); + return -EINVAL; + } + + OSMO_STRLCPY_ARRAY(subscr->imsi, imsi + 1); + + LOGP(DMM, LOGL_INFO, "received IMSI %s from SIM\n", subscr->imsi); + + return 0; +} + +static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_loci *loci; + + if (length < 11) + return -EINVAL; + loci = (struct gsm1111_ef_loci *) data; + + /* TMSI */ + subscr->tmsi = ntohl(loci->tmsi); + + /* LAI */ + gsm48_decode_lai_hex(&loci->lai, &subscr->mcc, &subscr->mnc, + &subscr->lac); + + /* location update status */ + switch (loci->lupd_status & 0x07) { + case 0x00: + subscr->ustate = GSM_SIM_U1_UPDATED; + break; + case 0x02: + case 0x03: + subscr->ustate = GSM_SIM_U3_ROAMING_NA; + break; + default: + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + } + + LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x " + "U%d)\n", gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate); + + return 0; +} + +static int subscr_sim_msisdn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_adn *adn; + + if (length < sizeof(*adn)) + return -EINVAL; + adn = (struct gsm1111_ef_adn *) (data + length - sizeof(*adn)); + + /* empty */ + subscr->msisdn[0] = '\0'; + if (adn->len_bcd <= 1) + return 0; + + /* number */ + if (((adn->ton_npi & 0x70) >> 4) == 1) + strcpy(subscr->msisdn, "+"); + if (((adn->ton_npi & 0x70) >> 4) == 2) + strcpy(subscr->msisdn, "0"); + strncat(subscr->msisdn, sim_decode_bcd(adn->number, adn->len_bcd - 1), + sizeof(subscr->msisdn) - 2); + + LOGP(DMM, LOGL_INFO, "received MSISDN %s from SIM\n", subscr->msisdn); + + return 0; +} + +static int subscr_sim_smsp(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_smsp *smsp; + + if (length < sizeof(*smsp)) + return -EINVAL; + smsp = (struct gsm1111_ef_smsp *) (data + length - sizeof(*smsp)); + + /* empty */ + subscr->sms_sca[0] = '\0'; + + /* TS-Service Centre Address */ + if (!(smsp->par_ind & 0x02) && smsp->ts_sca[0] <= 11) { + if (((smsp->ts_sca[1] & 0x70) >> 4) == 1) + strcpy(subscr->sms_sca, "+"); + if (((smsp->ts_sca[1] & 0x70) >> 4) == 2) + strcpy(subscr->sms_sca, "0"); + gsm48_decode_bcd_number2(subscr->sms_sca + strlen(subscr->sms_sca), + sizeof(subscr->sms_sca) - strlen(subscr->sms_sca), + smsp->ts_sca, sizeof(smsp->ts_sca), 1); + } + + LOGP(DMM, LOGL_INFO, "received SMSP from SIM (sca=%s)\n", + subscr->sms_sca); + + return 0; +} + +static int subscr_sim_kc(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (length < 9) + return -EINVAL; + + /* key */ + memcpy(subscr->key, data, 8); + + /* key sequence */ + subscr->key_seq = data[8] & 0x07; + + LOGP(DMM, LOGL_INFO, "received KEY from SIM\n"); + + return 0; +} + +static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_list *plmn; + struct llist_head *lh, *lh2; + uint8_t lai[5]; + uint16_t dummy_lac; + + /* flush list */ + llist_for_each_safe(lh, lh2, &subscr->plmn_list) { + llist_del(lh); + talloc_free(lh); + } + + while(length >= 3) { + /* end of list inside mandatory fields */ + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) + break; + + /* add to list */ + plmn = talloc_zero(ms, struct gsm_sub_plmn_list); + if (!plmn) + return -ENOMEM; + lai[0] = data[0]; + lai[1] = data[1]; + lai[2] = data[2]; + gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, + &plmn->mcc, &plmn->mnc, &dummy_lac); + llist_add_tail(&plmn->entry, &subscr->plmn_list); + + LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) " + "from SIM\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc)); + + data += 3; + length -= 3; + } + + return 0; +} + +static int subscr_sim_hpplmn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (length < 1) + return -EINVAL; + + /* HPLMN search interval */ + subscr->t6m_hplmn = *data; /* multiple of 6 minutes */ + + LOGP(DMM, LOGL_INFO, "received HPPLMN %d (%d mins) from SIM\n", + subscr->t6m_hplmn, subscr->t6m_hplmn * 6); + + return 0; +} + +static int subscr_sim_spn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + int i; + + /* UCS2 code not supported */ + if (length < 17 || data[1] >= 0x80) + return -ENOTSUP; + + data++; + for (i = 0; i < 16; i++) { + if (*data == 0xff) + break; + subscr->sim_spn[i] = *data++; + } + subscr->sim_spn[i] = '\0'; + + LOGP(DMM, LOGL_INFO, "received SPN %s from SIM\n", subscr->sim_spn); + + return 0; +} + +static int subscr_sim_acc(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + uint16_t ac; + + if (length < 2) + return -EINVAL; + + /* cell access */ + memcpy(&ac, data, sizeof(ac)); + subscr->acc_class = ntohs(ac); + + LOGP(DMM, LOGL_INFO, "received ACC %04x from SIM\n", subscr->acc_class); + + return 0; +} + +static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *na; + struct llist_head *lh, *lh2; + uint8_t lai[5]; + uint16_t dummy_lac; + +#ifdef TEST_EMPTY_FPLMN + return 0; +#endif + + /* flush list */ + llist_for_each_safe(lh, lh2, &subscr->plmn_na) { + llist_del(lh); + talloc_free(lh); + } + + while (length >= 3) { + /* end of list inside mandatory fields */ + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) + break; + + /* add to list */ + na = talloc_zero(ms, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + lai[0] = data[0]; + lai[1] = data[1]; + lai[2] = data[2]; + gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &na->mcc, + &na->mnc, &dummy_lac); + LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s %s from SIM\n", + gsm_print_mcc(na->mcc), gsm_print_mnc(na->mnc)); + na->cause = -1; /* must have a value, but SIM stores no cause */ + llist_add_tail(&na->entry, &subscr->plmn_na); + + data += 3; + length -= 3; + } + return 0; +} + +static struct subscr_sim_file { + uint8_t mandatory; + uint16_t path[MAX_SIM_PATH_LENGTH]; + uint16_t file; + uint8_t sim_job; + int (*func)(struct osmocom_ms *ms, uint8_t *data, + uint8_t length); +} subscr_sim_files[] = { + { 1, { 0 }, 0x2fe2, SIM_JOB_READ_BINARY, subscr_sim_iccid }, + { 1, { 0x7f20, 0 }, 0x6f07, SIM_JOB_READ_BINARY, subscr_sim_imsi }, + { 1, { 0x7f20, 0 }, 0x6f7e, SIM_JOB_READ_BINARY, subscr_sim_loci }, + { 0, { 0x7f20, 0 }, 0x6f20, SIM_JOB_READ_BINARY, subscr_sim_kc }, + { 0, { 0x7f20, 0 }, 0x6f30, SIM_JOB_READ_BINARY, subscr_sim_plmnsel }, + { 0, { 0x7f20, 0 }, 0x6f31, SIM_JOB_READ_BINARY, subscr_sim_hpplmn }, + { 0, { 0x7f20, 0 }, 0x6f46, SIM_JOB_READ_BINARY, subscr_sim_spn }, + { 0, { 0x7f20, 0 }, 0x6f78, SIM_JOB_READ_BINARY, subscr_sim_acc }, + { 0, { 0x7f20, 0 }, 0x6f7b, SIM_JOB_READ_BINARY, subscr_sim_fplmn }, + { 0, { 0x7f10, 0 }, 0x6f40, SIM_JOB_READ_RECORD, subscr_sim_msisdn }, + { 0, { 0x7f10, 0 }, 0x6f42, SIM_JOB_READ_RECORD, subscr_sim_smsp }, + { 0, { 0 }, 0, 0, NULL } +}; + +/* request file from SIM */ +static int subscr_sim_request(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; + struct msgb *nmsg; + struct sim_hdr *nsh; + int i; + + /* we are done, fire up PLMN and cell selection process */ + if (!sf->func) { + LOGP(DMM, LOGL_INFO, "(ms %s) Done reading SIM card " + "(IMSI=%s %s, %s)\n", ms->name, subscr->imsi, + gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); + + /* if LAI is valid, set RPLMN */ + if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + subscr->plmn_valid = 1; + subscr->plmn_mcc = subscr->mcc; + subscr->plmn_mnc = subscr->mnc; + LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s " + "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), + gsm_print_mnc(subscr->plmn_mnc), + gsm_get_mcc(subscr->plmn_mcc), + gsm_get_mnc(subscr->plmn_mcc, + subscr->plmn_mnc)); + } else + LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n"); + + /* insert card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; + } + + /* trigger SIM reading */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, + sf->sim_job); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + i = 0; + while (sf->path[i]) { + nsh->path[i] = sf->path[i]; + i++; + } + nsh->path[i] = 0; /* end of path */ + nsh->file = sf->file; + nsh->rec_no = 1; + nsh->rec_mode = 0x04; + LOGP(DMM, LOGL_INFO, "Requesting SIM file 0x%04x\n", nsh->file); + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + uint16_t payload_len = msg->len - sizeof(*sh); + int rc; + struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; + struct msgb *nmsg; + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) { + uint8_t cause = payload[0]; + + switch (cause) { + /* unlocking required */ + case SIM_CAUSE_PIN1_REQUIRED: + LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n", + payload[1]); + + vty_notify(ms, NULL); + vty_notify(ms, "Please give PIN for ICCID %s (you have " + "%d tries left)\n", subscr->iccid, payload[1]); + subscr->sim_pin_required = 1; + break; + case SIM_CAUSE_PIN1_BLOCKED: + LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "PIN is blocked\n"); + if (payload[1]) { + vty_notify(ms, "Please give PUC for ICCID %s " + "(you have %d tries left)\n", + subscr->iccid, payload[1]); + } + subscr->sim_pin_required = 1; + break; + case SIM_CAUSE_PUC_BLOCKED: + LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "PUC is blocked\n"); + subscr->sim_pin_required = 1; + break; + default: + if (sf->func && !sf->mandatory) { + LOGP(DMM, LOGL_NOTICE, "SIM reading failed, " + "ignoring!\n"); + goto ignore; + } + LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n"); + + vty_notify(ms, NULL); + vty_notify(ms, "SIM failed, replace SIM!\n"); + + /* detach simcard */ + subscr->sim_valid = 0; + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!nmsg) + return; + gsm48_mmr_downmsg(ms, nmsg); + } + msgb_free(msg); + + return; + } + + /* if pin was successfully unlocked, then resend request */ + if (subscr->sim_pin_required) { + subscr->sim_pin_required = 0; + subscr_sim_request(ms); + return; + } + + /* done when nothing more to read. this happens on PIN requests */ + if (!sf->func) + return; + + /* call function do decode SIM reply */ + rc = sf->func(ms, payload, payload_len); + if (rc) { + LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n"); + if (subscr_sim_files[subscr->sim_file_index].mandatory) { + vty_notify(ms, NULL); + vty_notify(ms, "SIM failed, data invalid, replace " + "SIM!\n"); + msgb_free(msg); + + return; + } + } + +ignore: + msgb_free(msg); + + /* trigger next file */ + subscr->sim_file_index++; + subscr_sim_request(ms); +} + +/* enter PIN */ +void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, + int8_t mode) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + uint8_t job; + + /* skip, if no real valid SIM */ + if (!GSM_SIM_IS_READER(subscr->sim_type)) + return; + + switch (mode) { + case -1: + job = SIM_JOB_PIN1_DISABLE; + LOGP(DMM, LOGL_INFO, "disabling PIN %s\n", pin1); + break; + case 1: + job = SIM_JOB_PIN1_ENABLE; + LOGP(DMM, LOGL_INFO, "enabling PIN %s\n", pin1); + break; + case 2: + job = SIM_JOB_PIN1_CHANGE; + LOGP(DMM, LOGL_INFO, "changing PIN %s to %s\n", pin1, pin2); + break; + case 99: + job = SIM_JOB_PIN1_UNBLOCK; + LOGP(DMM, LOGL_INFO, "unblocking PIN %s with PUC %s\n", pin1, + pin2); + break; + default: + if (!subscr->sim_pin_required) { + LOGP(DMM, LOGL_ERROR, "No PIN required now\n"); + return; + } + LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1); + job = SIM_JOB_PIN1_UNLOCK; + } + + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job); + if (!nmsg) + return; + memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1); + memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1); + sim_job(ms, nmsg); +} + +/* Attach SIM reader, no SIM must be currently attached */ +int gsm_subscr_simcard(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card " + "is detached.\n"); + return -EBUSY; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + subscr->sim_type = GSM_SIM_TYPE_L1PHY; + sprintf(subscr->sim_name, "sim"); + subscr->sim_valid = 1; + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + + /* start with first index */ + subscr->sim_file_index = 0; + return subscr_sim_request(ms); +} + +/* update plmn not allowed list on SIM */ +static int subscr_write_plmn_na(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL }; + int count = 0, i; + uint8_t *data; + uint8_t lai[5]; + +#ifdef TEST_EMPTY_FPLMN + return 0; +#endif + + /* skip, if no real valid SIM */ + if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) + return 0; + + /* get tail list from "PLMN not allowed" */ + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (count < 4) + nas[count] = na; + else { + nas[0] = nas[1]; + nas[1] = nas[2]; + nas[2] = nas[3]; + nas[3] = na; + } + count++; + } + + /* write to SIM */ + LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n"); + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + data = msgb_put(nmsg, 12); + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f7b; + for (i = 0; i < 4; i++) { + if (nas[i]) { + gsm48_encode_lai_hex((struct gsm48_loc_area_id *)lai, + nas[i]->mcc, nas[i]->mnc, 0); + *data++ = lai[0]; + *data++ = lai[1]; + *data++ = lai[2]; + } else { + *data++ = 0xff; + *data++ = 0xff; + *data++ = 0xff; + } + } + sim_job(ms, nmsg); + + return 0; +} + +/* update LOCI on SIM */ +int gsm_subscr_write_loci(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm1111_ef_loci *loci; + + /* skip, if no real valid SIM */ + if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); + + /* write to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f7e; + loci = (struct gsm1111_ef_loci *)msgb_put(nmsg, sizeof(*loci)); + + /* TMSI */ + loci->tmsi = htonl(subscr->tmsi); + + /* LAI */ + gsm48_encode_lai_hex(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac); + + /* TMSI time */ + loci->tmsi_time = 0xff; + + /* location update status */ + switch (subscr->ustate) { + case GSM_SIM_U1_UPDATED: + loci->lupd_status = 0x00; + break; + case GSM_SIM_U3_ROAMING_NA: + loci->lupd_status = 0x03; + break; + default: + loci->lupd_status = 0x01; + } + + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) + LOGP(DMM, LOGL_NOTICE, "SIM update failed (cause %d)\n", + *payload); + + msgb_free(msg); +} + +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, + uint8_t *rand, uint8_t no_sim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + + /* not a SIM */ + if ((subscr->sim_type != GSM_SIM_TYPE_TEST && !GSM_SIM_IS_READER(subscr->sim_type)) + || !subscr->sim_valid || no_sim) { + struct gsm48_mm_event *nmme; + + LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return -ENOMEM; + nmme = (struct gsm48_mm_event *) nmsg->data; + nmme->sres[0] = 0x12; + nmme->sres[1] = 0x34; + nmme->sres[2] = 0x56; + nmme->sres[3] = 0x78; + gsm48_mmevent_msg(ms, nmsg); + + return 0; + } + + /* test SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_TEST) { + struct gsm48_mm_event *nmme; + struct gsm_settings *set = &ms->settings; + static struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_GSM + }; + struct osmo_auth_vector _vec; + struct osmo_auth_vector *vec = &_vec; + + auth.algo = set->test_ki_type; + memcpy(auth.u.gsm.ki, set->test_ki, sizeof(auth.u.gsm.ki)); + int ret = osmo_auth_gen_vec(vec, &auth, rand); + if (ret < 0) + return ret; + + /* store sequence */ + subscr->key_seq = key_seq; + memcpy(subscr->key, vec->kc, 8); + + LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return -ENOMEM; + nmme = (struct gsm48_mm_event *) nmsg->data; + memcpy(nmme->sres, vec->sres, 4); + gsm48_mmevent_msg(ms, nmsg); + + return 0; + } + + LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n"); + + /* command to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_key, SIM_JOB_RUN_GSM_ALGO); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + + /* random */ + memcpy(msgb_put(nmsg, 16), rand, 16); + + /* store sequence */ + subscr->key_seq = key_seq; + + sim_job(ms, nmsg); + + return 0; +} + +static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct sim_hdr *sh = (struct sim_hdr *) msg->data; + uint8_t *payload = msg->data + sizeof(*sh); + uint16_t payload_len = msg->len - sizeof(*sh); + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm48_mm_event *nmme; + uint8_t *data; + + /* error handling */ + if (sh->job_type == SIM_JOB_ERROR) { + LOGP(DMM, LOGL_NOTICE, "key generation on SIM failed " + "(cause %d)\n", *payload); + + msgb_free(msg); + + return; + } + + if (payload_len < 12) { + LOGP(DMM, LOGL_NOTICE, "response from SIM too short\n"); + return; + } + + /* store key */ + memcpy(subscr->key, payload + 4, 8); + + /* write to SIM */ + LOGP(DMM, LOGL_INFO, "Updating KC on SIM\n"); + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f20; + data = msgb_put(nmsg, 9); + memcpy(data, subscr->key, 8); + data[8] = subscr->key_seq; + sim_job(ms, nmsg); + + /* return signed response */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return; + nmme = (struct gsm48_mm_event *) nmsg->data; + memcpy(nmme->sres, payload, 4); + gsm48_mmevent_msg(ms, nmsg); + + msgb_free(msg); +} + +/* + * detach + */ + +/* Detach card */ +int gsm_subscr_remove(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); + return -EINVAL; + } + + /* remove card */ + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return 0; +} + +/* + * state and lists + */ + +static const char *subscr_ustate_names[] = { + "U0_NULL", + "U1_UPDATED", + "U2_NOT_UPDATED", + "U3_ROAMING_NA" +}; + +/* change to new U state */ +void new_sim_ustate(struct gsm_subscriber *subscr, int state) +{ + LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, + subscr_ustate_names[subscr->ustate], + subscr_ustate_names[state]); + + subscr->ustate = state; +} + +/* del forbidden PLMN. if MCC==0, flush complete list */ +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na, *na2; + int deleted = 0; + + llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { + if (!mcc || (na->mcc == mcc && na->mnc == mnc)) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " + "PLMNs (mcc=%s, mnc=%s)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + llist_del(&na->entry); + talloc_free(na); + deleted = 1; + if (mcc) + break; + } + } + + if (deleted) { + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + } + + return -EINVAL; +} + +/* add forbidden PLMN */ +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause) +{ + struct gsm_sub_plmn_na *na; + + /* if already in the list, remove and add to tail */ + gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc); + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " + "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + na->mcc = mcc; + na->mnc = mnc; + na->cause = cause ? : -1; /* cause 0 is not allowed */ + llist_add_tail(&na->entry, &subscr->plmn_na); + + /* don't add Home PLMN to SIM */ + if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) + return -EINVAL; + + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + + return 0; +} + +/* search forbidden PLMN */ +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (na->mcc == mcc && na->mnc == mnc) + return 1; + } + + return 0; +} + +int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr) +{ + if (ms->settings.force_rekey) + return 7; + else + return subscr->key_seq; +} + +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *temp; + + print(priv, "MCC |MNC |cause\n"); + print(priv, "-------+-------+-------\n"); + llist_for_each_entry(temp, &subscr->plmn_na, entry) + print(priv, "%s |%s%s |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); + + return 0; +} + +/* dump subscriber */ +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm_sub_plmn_list *plmn_list; + struct gsm_sub_plmn_na *plmn_na; + + print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); + + if (!subscr->sim_valid) { + print(priv, " No SIM present.\n"); + return; + } + + print(priv, " IMSI: %s\n", subscr->imsi); + if (subscr->iccid[0]) + print(priv, " ICCID: %s\n", subscr->iccid); + if (subscr->sim_spn[0]) + print(priv, " Service Provider Name: %s\n", subscr->sim_spn); + if (subscr->msisdn[0]) + print(priv, " MSISDN: %s\n", subscr->msisdn); + if (subscr->sms_sca[0]) + print(priv, " SMS Service Center Address: %s\n", + subscr->sms_sca); + print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], + (subscr->imsi_attached) ? "attached" : "detached"); + if (subscr->tmsi != 0xffffffff) + print(priv, " TMSI 0x%08x", subscr->tmsi); + if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + print(priv, "\n"); + print(priv, " LAI: MCC %s MNC %s LAC 0x%04x " + "(%s, %s)\n", gsm_print_mcc(subscr->mcc), + gsm_print_mnc(subscr->mnc), subscr->lac, + gsm_get_mcc(subscr->mcc), + gsm_get_mnc(subscr->mcc, subscr->mnc)); + } else + print(priv, " LAI: invalid\n"); + if (subscr->key_seq != 7) { + print(priv, " Key: sequence %d ", subscr->key_seq); + for (i = 0; i < sizeof(subscr->key); i++) + print(priv, " %02x", subscr->key[i]); + print(priv, "\n"); + } + if (subscr->plmn_valid) + print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n", + gsm_print_mcc(subscr->plmn_mcc), + gsm_print_mnc(subscr->plmn_mnc), + gsm_get_mcc(subscr->plmn_mcc), + gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); + print(priv, " Access barred cells: %s\n", + (subscr->acc_barr) ? "yes" : "no"); + print(priv, " Access classes:"); + for (i = 0; i < 16; i++) + if ((subscr->acc_class & (1 << i))) + print(priv, " C%d", i); + print(priv, "\n"); + if (!llist_empty(&subscr->plmn_list)) { + print(priv, " List of preferred PLMNs:\n"); + print(priv, " MCC |MNC\n"); + print(priv, " -------+-------\n"); + llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) + print(priv, " %s |%s (%s, %s)\n", + gsm_print_mcc(plmn_list->mcc), + gsm_print_mnc(plmn_list->mnc), + gsm_get_mcc(plmn_list->mcc), + gsm_get_mnc(plmn_list->mcc, plmn_list->mnc)); + } + if (!llist_empty(&subscr->plmn_na)) { + print(priv, " List of forbidden PLMNs:\n"); + print(priv, " MCC |MNC |cause\n"); + print(priv, " -------+-------+-------\n"); + llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) + print(priv, " %s |%s%s |#%d " + "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc), + gsm_print_mnc(plmn_na->mnc), + ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", + plmn_na->cause, gsm_get_mcc(plmn_na->mcc), + gsm_get_mnc(plmn_na->mcc, plmn_na->mnc)); + } +} + +/* + * SAP interface integration + */ + +/* Attach SIM card over SAP */ +int gsm_subscr_sapcard(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + int rc; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " + "is detached.\n"); + return -EBUSY; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + subscr->sim_type = GSM_SIM_TYPE_SAP; + sprintf(subscr->sim_name, "sap"); + subscr->sim_valid = 1; + + /* Try to connect to the SAP interface */ + vty_notify(ms, NULL); + vty_notify(ms, "Connecting to the SAP interface...\n"); + rc = sap_open(ms); + if (rc < 0) { + LOGP(DSAP, LOGL_ERROR, "Failed during sap_open(), no SAP based SIM reader\n"); + vty_notify(ms, "SAP connection error!\n"); + ms->sap_wq.bfd.fd = -1; + + /* Detach SIM */ + subscr->sim_valid = 0; + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + + return rc; + } + + return 0; +} + +/* Deattach sapcard */ +int gsm_subscr_remove_sapcard(struct osmocom_ms *ms) +{ + return sap_close(ms); +} + +int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, + uint8_t res_type, uint16_t param_len, const uint8_t *param_val) +{ + struct msgb *msg; + int rc = 0; + + /* Response parameter is not encoded in case of error */ + if (res_code != SAP_RESULT_OK_REQ_PROC_CORR) + goto ignore_rsp; + + switch (res_type) { + case SAP_TRANSFER_APDU_RESP: + /* Prevent NULL-pointer dereference */ + if (!param_len || !param_val) { + rc = -EINVAL; + goto ignore_rsp; + } + + /* FIXME: why do we use this length? */ + msg = msgb_alloc(GSM_SAP_LENGTH, "sap_apdu"); + if (!msg) { + rc = -ENOMEM; + goto ignore_rsp; + } + + msg->data = msgb_put(msg, param_len); + memcpy(msg->data, param_val, param_len); + + return sim_apdu_resp(ms, msg); + + case SAP_TRANSFER_ATR_RESP: + /* TODO: don't read SIM again (if already) */ + LOGP(DSAP, LOGL_INFO, "SAP card is ready, start reading...\n"); + return subscr_sim_request(ms); + + default: + rc = -ENOTSUP; + goto ignore_rsp; + } + + return 0; + +ignore_rsp: + LOGP(DSAP, LOGL_NOTICE, "Ignored SAP response '%s' (code=%d)\n", + get_value_string(sap_msg_names, res_type), res_code); + return rc; +} diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am index 9ee0b72f..dedee5f3 100644 --- a/src/host/layer23/src/mobile/Makefile.am +++ b/src/host/layer23/src/mobile/Makefile.am @@ -28,7 +28,6 @@ libmobile_a_SOURCES = \ mncc_sock.c \ primitives.c \ settings.c \ - subscriber.c \ transaction.c \ vty_interface.c \ voice.c \ diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index 500eff6d..60be5691 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,6 @@ #include #include #include -#include extern void *l23_ctx; diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c deleted file mode 100644 index 164622a2..00000000 --- a/src/host/layer23/src/mobile/subscriber.c +++ /dev/null @@ -1,1355 +0,0 @@ -/* - * (C) 2010 by Andreas Eversberg - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* enable to get an empty list of forbidden PLMNs, even if stored on SIM. - * if list is changed, the result is not written back to SIM */ -//#define TEST_EMPTY_FPLMN - -static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); -static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg); -static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); - -/* - * support - */ - -char *gsm_check_imsi(const char *imsi) -{ - int i; - - if (!imsi || strlen(imsi) != 15) - return "IMSI must have 15 digits!"; - - for (i = 0; i < strlen(imsi); i++) { - if (imsi[i] < '0' || imsi[i] > '9') - return "IMSI must have digits 0 to 9 only!"; - } - - return NULL; -} - -static char *sim_decode_bcd(uint8_t *data, uint8_t length) -{ - int i, j = 0; - static char result[32], c; - - for (i = 0; i < (length << 1); i++) { - if ((i & 1)) - c = (data[i >> 1] >> 4); - else - c = (data[i >> 1] & 0xf); - if (c == 0xf) - break; - result[j++] = c + '0'; - if (j == sizeof(result) - 1) - break; - } - result[j] = '\0'; - - return result; -} - -/* - * init/exit - */ - -int gsm_subscr_init(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - - memset(subscr, 0, sizeof(*subscr)); - subscr->ms = ms; - - /* set TMSI / LAC invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; - - /* set key invalid */ - subscr->key_seq = 7; - - /* any cell selection timer timeout */ - subscr->any_timeout = ms->settings.any_timeout; - - /* init lists */ - INIT_LLIST_HEAD(&subscr->plmn_list); - INIT_LLIST_HEAD(&subscr->plmn_na); - - /* open SIM */ - subscr->sim_handle_query = sim_open(ms, subscr_sim_query_cb); - subscr->sim_handle_update = sim_open(ms, subscr_sim_update_cb); - subscr->sim_handle_key = sim_open(ms, subscr_sim_key_cb); - - return 0; -} - -int gsm_subscr_exit(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct llist_head *lh, *lh2; - - if (subscr->sim_handle_query) { - sim_close(ms, subscr->sim_handle_query); - subscr->sim_handle_query = 0; - } - if (subscr->sim_handle_update) { - sim_close(ms, subscr->sim_handle_update); - subscr->sim_handle_update = 0; - } - if (subscr->sim_handle_key) { - sim_close(ms, subscr->sim_handle_key); - subscr->sim_handle_key = 0; - } - - /* flush lists */ - llist_for_each_safe(lh, lh2, &subscr->plmn_list) { - llist_del(lh); - talloc_free(lh); - } - llist_for_each_safe(lh, lh2, &subscr->plmn_na) { - llist_del(lh); - talloc_free(lh); - } - - return 0; -} - -/* - * test card - */ - -/* Attach test card, no SIM must be currently attached */ -int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac, uint32_t tmsi, uint8_t imsi_attached) -{ - struct gsm_settings *set = &ms->settings; - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - char *error; - - if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " - "is detached.\n"); - return -EBUSY; - } - - error = gsm_check_imsi(set->test_imsi); - if (error) { - LOGP(DMM, LOGL_ERROR, "%s\n", error); - return -EINVAL; - } - - /* reset subscriber */ - gsm_subscr_exit(ms); - gsm_subscr_init(ms); - - subscr->sim_type = GSM_SIM_TYPE_TEST; - sprintf(subscr->sim_name, "test"); - subscr->sim_valid = 1; - if (imsi_attached && set->test_rplmn_valid) { - subscr->imsi_attached = imsi_attached; - subscr->ustate = GSM_SIM_U1_UPDATED; - } else - subscr->ustate = GSM_SIM_U2_NOT_UPDATED; - subscr->acc_barr = set->test_barr; /* we may access barred cell */ - subscr->acc_class = 0xffff; /* we have any access class */ - subscr->plmn_valid = set->test_rplmn_valid; - subscr->plmn_mcc = mcc; - subscr->plmn_mnc = mnc; - subscr->mcc = mcc; - subscr->mnc = mnc; - subscr->lac = lac; - subscr->tmsi = tmsi; - subscr->always_search_hplmn = set->test_always; - subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ - strcpy(subscr->imsi, set->test_imsi); - - LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n", - ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), - gsm_imsi_mnc(subscr->imsi)); - - if (subscr->plmn_valid) - LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x" - "(%s, %s)\n", gsm_print_mcc(mcc), - gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc), - gsm_get_mnc(mcc, mnc)); - else - LOGP(DMM, LOGL_INFO, "-> Test card not registered\n"); - if (subscr->imsi_attached) - LOGP(DMM, LOGL_INFO, "-> Test card attached\n"); - - /* insert card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - - return 0; -} - -/* - * sim card - */ - -static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - - strcpy(subscr->iccid, sim_decode_bcd(data, length)); - sprintf(subscr->sim_name, "sim-%s", subscr->iccid); - LOGP(DMM, LOGL_INFO, "received ICCID %s from SIM\n", subscr->iccid); - - return 0; -} - -static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - char *imsi; - - /* get actual length */ - if (length < 1) - return -EINVAL; - if (data[0] + 1 < length) { - LOGP(DMM, LOGL_NOTICE, "invalid length = %d\n", length); - return -EINVAL; - } - length = data[0]; - - /* decode IMSI, skip first digit (parity) */ - imsi = sim_decode_bcd(data + 1, length); - if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) { - LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %zu\n", - strlen(imsi) - 1); - return -EINVAL; - } - - OSMO_STRLCPY_ARRAY(subscr->imsi, imsi + 1); - - LOGP(DMM, LOGL_INFO, "received IMSI %s from SIM\n", subscr->imsi); - - return 0; -} - -static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm1111_ef_loci *loci; - - if (length < 11) - return -EINVAL; - loci = (struct gsm1111_ef_loci *) data; - - /* TMSI */ - subscr->tmsi = ntohl(loci->tmsi); - - /* LAI */ - gsm48_decode_lai_hex(&loci->lai, &subscr->mcc, &subscr->mnc, - &subscr->lac); - - /* location update status */ - switch (loci->lupd_status & 0x07) { - case 0x00: - subscr->ustate = GSM_SIM_U1_UPDATED; - break; - case 0x02: - case 0x03: - subscr->ustate = GSM_SIM_U3_ROAMING_NA; - break; - default: - subscr->ustate = GSM_SIM_U2_NOT_UPDATED; - } - - LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x " - "U%d)\n", gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate); - - return 0; -} - -static int subscr_sim_msisdn(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm1111_ef_adn *adn; - - if (length < sizeof(*adn)) - return -EINVAL; - adn = (struct gsm1111_ef_adn *) (data + length - sizeof(*adn)); - - /* empty */ - subscr->msisdn[0] = '\0'; - if (adn->len_bcd <= 1) - return 0; - - /* number */ - if (((adn->ton_npi & 0x70) >> 4) == 1) - strcpy(subscr->msisdn, "+"); - if (((adn->ton_npi & 0x70) >> 4) == 2) - strcpy(subscr->msisdn, "0"); - strncat(subscr->msisdn, sim_decode_bcd(adn->number, adn->len_bcd - 1), - sizeof(subscr->msisdn) - 2); - - LOGP(DMM, LOGL_INFO, "received MSISDN %s from SIM\n", subscr->msisdn); - - return 0; -} - -static int subscr_sim_smsp(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm1111_ef_smsp *smsp; - - if (length < sizeof(*smsp)) - return -EINVAL; - smsp = (struct gsm1111_ef_smsp *) (data + length - sizeof(*smsp)); - - /* empty */ - subscr->sms_sca[0] = '\0'; - - /* TS-Service Centre Address */ - if (!(smsp->par_ind & 0x02) && smsp->ts_sca[0] <= 11) { - if (((smsp->ts_sca[1] & 0x70) >> 4) == 1) - strcpy(subscr->sms_sca, "+"); - if (((smsp->ts_sca[1] & 0x70) >> 4) == 2) - strcpy(subscr->sms_sca, "0"); - gsm48_decode_bcd_number2(subscr->sms_sca + strlen(subscr->sms_sca), - sizeof(subscr->sms_sca) - strlen(subscr->sms_sca), - smsp->ts_sca, sizeof(smsp->ts_sca), 1); - } - - LOGP(DMM, LOGL_INFO, "received SMSP from SIM (sca=%s)\n", - subscr->sms_sca); - - return 0; -} - -static int subscr_sim_kc(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - - if (length < 9) - return -EINVAL; - - /* key */ - memcpy(subscr->key, data, 8); - - /* key sequence */ - subscr->key_seq = data[8] & 0x07; - - LOGP(DMM, LOGL_INFO, "received KEY from SIM\n"); - - return 0; -} - -static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_list *plmn; - struct llist_head *lh, *lh2; - uint8_t lai[5]; - uint16_t dummy_lac; - - /* flush list */ - llist_for_each_safe(lh, lh2, &subscr->plmn_list) { - llist_del(lh); - talloc_free(lh); - } - - while(length >= 3) { - /* end of list inside mandatory fields */ - if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) - break; - - /* add to list */ - plmn = talloc_zero(ms, struct gsm_sub_plmn_list); - if (!plmn) - return -ENOMEM; - lai[0] = data[0]; - lai[1] = data[1]; - lai[2] = data[2]; - gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, - &plmn->mcc, &plmn->mnc, &dummy_lac); - llist_add_tail(&plmn->entry, &subscr->plmn_list); - - LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) " - "from SIM\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc)); - - data += 3; - length -= 3; - } - - return 0; -} - -static int subscr_sim_hpplmn(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - - if (length < 1) - return -EINVAL; - - /* HPLMN search interval */ - subscr->t6m_hplmn = *data; /* multiple of 6 minutes */ - - LOGP(DMM, LOGL_INFO, "received HPPLMN %d (%d mins) from SIM\n", - subscr->t6m_hplmn, subscr->t6m_hplmn * 6); - - return 0; -} - -static int subscr_sim_spn(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - int i; - - /* UCS2 code not supported */ - if (length < 17 || data[1] >= 0x80) - return -ENOTSUP; - - data++; - for (i = 0; i < 16; i++) { - if (*data == 0xff) - break; - subscr->sim_spn[i] = *data++; - } - subscr->sim_spn[i] = '\0'; - - LOGP(DMM, LOGL_INFO, "received SPN %s from SIM\n", subscr->sim_spn); - - return 0; -} - -static int subscr_sim_acc(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - uint16_t ac; - - if (length < 2) - return -EINVAL; - - /* cell access */ - memcpy(&ac, data, sizeof(ac)); - subscr->acc_class = ntohs(ac); - - LOGP(DMM, LOGL_INFO, "received ACC %04x from SIM\n", subscr->acc_class); - - return 0; -} - -static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, - uint8_t length) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *na; - struct llist_head *lh, *lh2; - uint8_t lai[5]; - uint16_t dummy_lac; - -#ifdef TEST_EMPTY_FPLMN - return 0; -#endif - - /* flush list */ - llist_for_each_safe(lh, lh2, &subscr->plmn_na) { - llist_del(lh); - talloc_free(lh); - } - - while (length >= 3) { - /* end of list inside mandatory fields */ - if (data[0] == 0xff && data[1] == 0xff && data[2] == 0x0ff) - break; - - /* add to list */ - na = talloc_zero(ms, struct gsm_sub_plmn_na); - if (!na) - return -ENOMEM; - lai[0] = data[0]; - lai[1] = data[1]; - lai[2] = data[2]; - gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &na->mcc, - &na->mnc, &dummy_lac); - LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s %s from SIM\n", - gsm_print_mcc(na->mcc), gsm_print_mnc(na->mnc)); - na->cause = -1; /* must have a value, but SIM stores no cause */ - llist_add_tail(&na->entry, &subscr->plmn_na); - - data += 3; - length -= 3; - } - return 0; -} - -static struct subscr_sim_file { - uint8_t mandatory; - uint16_t path[MAX_SIM_PATH_LENGTH]; - uint16_t file; - uint8_t sim_job; - int (*func)(struct osmocom_ms *ms, uint8_t *data, - uint8_t length); -} subscr_sim_files[] = { - { 1, { 0 }, 0x2fe2, SIM_JOB_READ_BINARY, subscr_sim_iccid }, - { 1, { 0x7f20, 0 }, 0x6f07, SIM_JOB_READ_BINARY, subscr_sim_imsi }, - { 1, { 0x7f20, 0 }, 0x6f7e, SIM_JOB_READ_BINARY, subscr_sim_loci }, - { 0, { 0x7f20, 0 }, 0x6f20, SIM_JOB_READ_BINARY, subscr_sim_kc }, - { 0, { 0x7f20, 0 }, 0x6f30, SIM_JOB_READ_BINARY, subscr_sim_plmnsel }, - { 0, { 0x7f20, 0 }, 0x6f31, SIM_JOB_READ_BINARY, subscr_sim_hpplmn }, - { 0, { 0x7f20, 0 }, 0x6f46, SIM_JOB_READ_BINARY, subscr_sim_spn }, - { 0, { 0x7f20, 0 }, 0x6f78, SIM_JOB_READ_BINARY, subscr_sim_acc }, - { 0, { 0x7f20, 0 }, 0x6f7b, SIM_JOB_READ_BINARY, subscr_sim_fplmn }, - { 0, { 0x7f10, 0 }, 0x6f40, SIM_JOB_READ_RECORD, subscr_sim_msisdn }, - { 0, { 0x7f10, 0 }, 0x6f42, SIM_JOB_READ_RECORD, subscr_sim_smsp }, - { 0, { 0 }, 0, 0, NULL } -}; - -/* request file from SIM */ -static int subscr_sim_request(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; - struct msgb *nmsg; - struct sim_hdr *nsh; - int i; - - /* we are done, fire up PLMN and cell selection process */ - if (!sf->func) { - LOGP(DMM, LOGL_INFO, "(ms %s) Done reading SIM card " - "(IMSI=%s %s, %s)\n", ms->name, subscr->imsi, - gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); - - /* if LAI is valid, set RPLMN */ - if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { - subscr->plmn_valid = 1; - subscr->plmn_mcc = subscr->mcc; - subscr->plmn_mnc = subscr->mnc; - LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s " - "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), - gsm_print_mnc(subscr->plmn_mnc), - gsm_get_mcc(subscr->plmn_mcc), - gsm_get_mnc(subscr->plmn_mcc, - subscr->plmn_mnc)); - } else - LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n"); - - /* insert card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - - return 0; - } - - /* trigger SIM reading */ - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, - sf->sim_job); - if (!nmsg) - return -ENOMEM; - nsh = (struct sim_hdr *) nmsg->data; - i = 0; - while (sf->path[i]) { - nsh->path[i] = sf->path[i]; - i++; - } - nsh->path[i] = 0; /* end of path */ - nsh->file = sf->file; - nsh->rec_no = 1; - nsh->rec_mode = 0x04; - LOGP(DMM, LOGL_INFO, "Requesting SIM file 0x%04x\n", nsh->file); - sim_job(ms, nmsg); - - return 0; -} - -static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct sim_hdr *sh = (struct sim_hdr *) msg->data; - uint8_t *payload = msg->data + sizeof(*sh); - uint16_t payload_len = msg->len - sizeof(*sh); - int rc; - struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; - struct msgb *nmsg; - - /* error handling */ - if (sh->job_type == SIM_JOB_ERROR) { - uint8_t cause = payload[0]; - - switch (cause) { - /* unlocking required */ - case SIM_CAUSE_PIN1_REQUIRED: - LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n", - payload[1]); - - vty_notify(ms, NULL); - vty_notify(ms, "Please give PIN for ICCID %s (you have " - "%d tries left)\n", subscr->iccid, payload[1]); - subscr->sim_pin_required = 1; - break; - case SIM_CAUSE_PIN1_BLOCKED: - LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n"); - - vty_notify(ms, NULL); - vty_notify(ms, "PIN is blocked\n"); - if (payload[1]) { - vty_notify(ms, "Please give PUC for ICCID %s " - "(you have %d tries left)\n", - subscr->iccid, payload[1]); - } - subscr->sim_pin_required = 1; - break; - case SIM_CAUSE_PUC_BLOCKED: - LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n"); - - vty_notify(ms, NULL); - vty_notify(ms, "PUC is blocked\n"); - subscr->sim_pin_required = 1; - break; - default: - if (sf->func && !sf->mandatory) { - LOGP(DMM, LOGL_NOTICE, "SIM reading failed, " - "ignoring!\n"); - goto ignore; - } - LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n"); - - vty_notify(ms, NULL); - vty_notify(ms, "SIM failed, replace SIM!\n"); - - /* detach simcard */ - subscr->sim_valid = 0; - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return; - gsm48_mmr_downmsg(ms, nmsg); - } - msgb_free(msg); - - return; - } - - /* if pin was successfully unlocked, then resend request */ - if (subscr->sim_pin_required) { - subscr->sim_pin_required = 0; - subscr_sim_request(ms); - return; - } - - /* done when nothing more to read. this happens on PIN requests */ - if (!sf->func) - return; - - /* call function do decode SIM reply */ - rc = sf->func(ms, payload, payload_len); - if (rc) { - LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n"); - if (subscr_sim_files[subscr->sim_file_index].mandatory) { - vty_notify(ms, NULL); - vty_notify(ms, "SIM failed, data invalid, replace " - "SIM!\n"); - msgb_free(msg); - - return; - } - } - -ignore: - msgb_free(msg); - - /* trigger next file */ - subscr->sim_file_index++; - subscr_sim_request(ms); -} - -/* enter PIN */ -void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, - int8_t mode) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - uint8_t job; - - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type)) - return; - - switch (mode) { - case -1: - job = SIM_JOB_PIN1_DISABLE; - LOGP(DMM, LOGL_INFO, "disabling PIN %s\n", pin1); - break; - case 1: - job = SIM_JOB_PIN1_ENABLE; - LOGP(DMM, LOGL_INFO, "enabling PIN %s\n", pin1); - break; - case 2: - job = SIM_JOB_PIN1_CHANGE; - LOGP(DMM, LOGL_INFO, "changing PIN %s to %s\n", pin1, pin2); - break; - case 99: - job = SIM_JOB_PIN1_UNBLOCK; - LOGP(DMM, LOGL_INFO, "unblocking PIN %s with PUC %s\n", pin1, - pin2); - break; - default: - if (!subscr->sim_pin_required) { - LOGP(DMM, LOGL_ERROR, "No PIN required now\n"); - return; - } - LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1); - job = SIM_JOB_PIN1_UNLOCK; - } - - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job); - if (!nmsg) - return; - memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1); - memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1); - sim_job(ms, nmsg); -} - -/* Attach SIM reader, no SIM must be currently attached */ -int gsm_subscr_simcard(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - - if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card " - "is detached.\n"); - return -EBUSY; - } - - /* reset subscriber */ - gsm_subscr_exit(ms); - gsm_subscr_init(ms); - - subscr->sim_type = GSM_SIM_TYPE_L1PHY; - sprintf(subscr->sim_name, "sim"); - subscr->sim_valid = 1; - subscr->ustate = GSM_SIM_U2_NOT_UPDATED; - - /* start with first index */ - subscr->sim_file_index = 0; - return subscr_sim_request(ms); -} - -/* update plmn not allowed list on SIM */ -static int subscr_write_plmn_na(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - struct sim_hdr *nsh; - struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL }; - int count = 0, i; - uint8_t *data; - uint8_t lai[5]; - -#ifdef TEST_EMPTY_FPLMN - return 0; -#endif - - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) - return 0; - - /* get tail list from "PLMN not allowed" */ - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (count < 4) - nas[count] = na; - else { - nas[0] = nas[1]; - nas[1] = nas[2]; - nas[2] = nas[3]; - nas[3] = na; - } - count++; - } - - /* write to SIM */ - LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n"); - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, - SIM_JOB_UPDATE_BINARY); - if (!nmsg) - return -ENOMEM; - nsh = (struct sim_hdr *) nmsg->data; - data = msgb_put(nmsg, 12); - nsh->path[0] = 0x7f20; - nsh->path[1] = 0; - nsh->file = 0x6f7b; - for (i = 0; i < 4; i++) { - if (nas[i]) { - gsm48_encode_lai_hex((struct gsm48_loc_area_id *)lai, - nas[i]->mcc, nas[i]->mnc, 0); - *data++ = lai[0]; - *data++ = lai[1]; - *data++ = lai[2]; - } else { - *data++ = 0xff; - *data++ = 0xff; - *data++ = 0xff; - } - } - sim_job(ms, nmsg); - - return 0; -} - -/* update LOCI on SIM */ -int gsm_subscr_write_loci(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - struct sim_hdr *nsh; - struct gsm1111_ef_loci *loci; - - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) - return 0; - - LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); - - /* write to SIM */ - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, - SIM_JOB_UPDATE_BINARY); - if (!nmsg) - return -ENOMEM; - nsh = (struct sim_hdr *) nmsg->data; - nsh->path[0] = 0x7f20; - nsh->path[1] = 0; - nsh->file = 0x6f7e; - loci = (struct gsm1111_ef_loci *)msgb_put(nmsg, sizeof(*loci)); - - /* TMSI */ - loci->tmsi = htonl(subscr->tmsi); - - /* LAI */ - gsm48_encode_lai_hex(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac); - - /* TMSI time */ - loci->tmsi_time = 0xff; - - /* location update status */ - switch (subscr->ustate) { - case GSM_SIM_U1_UPDATED: - loci->lupd_status = 0x00; - break; - case GSM_SIM_U3_ROAMING_NA: - loci->lupd_status = 0x03; - break; - default: - loci->lupd_status = 0x01; - } - - sim_job(ms, nmsg); - - return 0; -} - -static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg) -{ - struct sim_hdr *sh = (struct sim_hdr *) msg->data; - uint8_t *payload = msg->data + sizeof(*sh); - - /* error handling */ - if (sh->job_type == SIM_JOB_ERROR) - LOGP(DMM, LOGL_NOTICE, "SIM update failed (cause %d)\n", - *payload); - - msgb_free(msg); -} - -int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, - uint8_t *rand, uint8_t no_sim) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - struct sim_hdr *nsh; - - /* not a SIM */ - if ((subscr->sim_type != GSM_SIM_TYPE_TEST && !GSM_SIM_IS_READER(subscr->sim_type)) - || !subscr->sim_valid || no_sim) { - struct gsm48_mm_event *nmme; - - LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return -ENOMEM; - nmme = (struct gsm48_mm_event *) nmsg->data; - nmme->sres[0] = 0x12; - nmme->sres[1] = 0x34; - nmme->sres[2] = 0x56; - nmme->sres[3] = 0x78; - gsm48_mmevent_msg(ms, nmsg); - - return 0; - } - - /* test SIM */ - if (subscr->sim_type == GSM_SIM_TYPE_TEST) { - struct gsm48_mm_event *nmme; - struct gsm_settings *set = &ms->settings; - static struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM - }; - struct osmo_auth_vector _vec; - struct osmo_auth_vector *vec = &_vec; - - auth.algo = set->test_ki_type; - memcpy(auth.u.gsm.ki, set->test_ki, sizeof(auth.u.gsm.ki)); - int ret = osmo_auth_gen_vec(vec, &auth, rand); - if (ret < 0) - return ret; - - /* store sequence */ - subscr->key_seq = key_seq; - memcpy(subscr->key, vec->kc, 8); - - LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return -ENOMEM; - nmme = (struct gsm48_mm_event *) nmsg->data; - memcpy(nmme->sres, vec->sres, 4); - gsm48_mmevent_msg(ms, nmsg); - - return 0; - } - - LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n"); - - /* command to SIM */ - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_key, SIM_JOB_RUN_GSM_ALGO); - if (!nmsg) - return -ENOMEM; - nsh = (struct sim_hdr *) nmsg->data; - nsh->path[0] = 0x7f20; - nsh->path[1] = 0; - - /* random */ - memcpy(msgb_put(nmsg, 16), rand, 16); - - /* store sequence */ - subscr->key_seq = key_seq; - - sim_job(ms, nmsg); - - return 0; -} - -static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct sim_hdr *sh = (struct sim_hdr *) msg->data; - uint8_t *payload = msg->data + sizeof(*sh); - uint16_t payload_len = msg->len - sizeof(*sh); - struct msgb *nmsg; - struct sim_hdr *nsh; - struct gsm48_mm_event *nmme; - uint8_t *data; - - /* error handling */ - if (sh->job_type == SIM_JOB_ERROR) { - LOGP(DMM, LOGL_NOTICE, "key generation on SIM failed " - "(cause %d)\n", *payload); - - msgb_free(msg); - - return; - } - - if (payload_len < 12) { - LOGP(DMM, LOGL_NOTICE, "response from SIM too short\n"); - return; - } - - /* store key */ - memcpy(subscr->key, payload + 4, 8); - - /* write to SIM */ - LOGP(DMM, LOGL_INFO, "Updating KC on SIM\n"); - nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, - SIM_JOB_UPDATE_BINARY); - if (!nmsg) - return; - nsh = (struct sim_hdr *) nmsg->data; - nsh->path[0] = 0x7f20; - nsh->path[1] = 0; - nsh->file = 0x6f20; - data = msgb_put(nmsg, 9); - memcpy(data, subscr->key, 8); - data[8] = subscr->key_seq; - sim_job(ms, nmsg); - - /* return signed response */ - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return; - nmme = (struct gsm48_mm_event *) nmsg->data; - memcpy(nmme->sres, payload, 4); - gsm48_mmevent_msg(ms, nmsg); - - msgb_free(msg); -} - -/* - * detach - */ - -/* Detach card */ -int gsm_subscr_remove(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - - if (!subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); - return -EINVAL; - } - - /* remove card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - - return 0; -} - -/* - * state and lists - */ - -static const char *subscr_ustate_names[] = { - "U0_NULL", - "U1_UPDATED", - "U2_NOT_UPDATED", - "U3_ROAMING_NA" -}; - -/* change to new U state */ -void new_sim_ustate(struct gsm_subscriber *subscr, int state) -{ - LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, - subscr_ustate_names[subscr->ustate], - subscr_ustate_names[state]); - - subscr->ustate = state; -} - -/* del forbidden PLMN. if MCC==0, flush complete list */ -int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc) -{ - struct gsm_sub_plmn_na *na, *na2; - int deleted = 0; - - llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { - if (!mcc || (na->mcc == mcc && na->mnc == mnc)) { - LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " - "PLMNs (mcc=%s, mnc=%s)\n", - gsm_print_mcc(mcc), gsm_print_mnc(mnc)); - llist_del(&na->entry); - talloc_free(na); - deleted = 1; - if (mcc) - break; - } - } - - if (deleted) { - /* update plmn not allowed list on SIM */ - subscr_write_plmn_na(subscr->ms); - } - - return -EINVAL; -} - -/* add forbidden PLMN */ -int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc, uint8_t cause) -{ - struct gsm_sub_plmn_na *na; - - /* if already in the list, remove and add to tail */ - gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc); - - LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " - "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); - na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na); - if (!na) - return -ENOMEM; - na->mcc = mcc; - na->mnc = mnc; - na->cause = cause ? : -1; /* cause 0 is not allowed */ - llist_add_tail(&na->entry, &subscr->plmn_na); - - /* don't add Home PLMN to SIM */ - if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) - return -EINVAL; - - /* update plmn not allowed list on SIM */ - subscr_write_plmn_na(subscr->ms); - - return 0; -} - -/* search forbidden PLMN */ -int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc) -{ - struct gsm_sub_plmn_na *na; - - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (na->mcc == mcc && na->mnc == mnc) - return 1; - } - - return 0; -} - -int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr) -{ - if (ms->settings.force_rekey) - return 7; - else - return subscr->key_seq; -} - -int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, - void (*print)(void *, const char *, ...), void *priv) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *temp; - - print(priv, "MCC |MNC |cause\n"); - print(priv, "-------+-------+-------\n"); - llist_for_each_entry(temp, &subscr->plmn_na, entry) - print(priv, "%s |%s%s |#%d\n", - gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), - ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); - - return 0; -} - -/* dump subscriber */ -void gsm_subscr_dump(struct gsm_subscriber *subscr, - void (*print)(void *, const char *, ...), void *priv) -{ - int i; - struct gsm_sub_plmn_list *plmn_list; - struct gsm_sub_plmn_na *plmn_na; - - print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); - - if (!subscr->sim_valid) { - print(priv, " No SIM present.\n"); - return; - } - - print(priv, " IMSI: %s\n", subscr->imsi); - if (subscr->iccid[0]) - print(priv, " ICCID: %s\n", subscr->iccid); - if (subscr->sim_spn[0]) - print(priv, " Service Provider Name: %s\n", subscr->sim_spn); - if (subscr->msisdn[0]) - print(priv, " MSISDN: %s\n", subscr->msisdn); - if (subscr->sms_sca[0]) - print(priv, " SMS Service Center Address: %s\n", - subscr->sms_sca); - print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], - (subscr->imsi_attached) ? "attached" : "detached"); - if (subscr->tmsi != 0xffffffff) - print(priv, " TMSI 0x%08x", subscr->tmsi); - if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { - print(priv, "\n"); - print(priv, " LAI: MCC %s MNC %s LAC 0x%04x " - "(%s, %s)\n", gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac, - gsm_get_mcc(subscr->mcc), - gsm_get_mnc(subscr->mcc, subscr->mnc)); - } else - print(priv, " LAI: invalid\n"); - if (subscr->key_seq != 7) { - print(priv, " Key: sequence %d ", subscr->key_seq); - for (i = 0; i < sizeof(subscr->key); i++) - print(priv, " %02x", subscr->key[i]); - print(priv, "\n"); - } - if (subscr->plmn_valid) - print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n", - gsm_print_mcc(subscr->plmn_mcc), - gsm_print_mnc(subscr->plmn_mnc), - gsm_get_mcc(subscr->plmn_mcc), - gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); - print(priv, " Access barred cells: %s\n", - (subscr->acc_barr) ? "yes" : "no"); - print(priv, " Access classes:"); - for (i = 0; i < 16; i++) - if ((subscr->acc_class & (1 << i))) - print(priv, " C%d", i); - print(priv, "\n"); - if (!llist_empty(&subscr->plmn_list)) { - print(priv, " List of preferred PLMNs:\n"); - print(priv, " MCC |MNC\n"); - print(priv, " -------+-------\n"); - llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) - print(priv, " %s |%s (%s, %s)\n", - gsm_print_mcc(plmn_list->mcc), - gsm_print_mnc(plmn_list->mnc), - gsm_get_mcc(plmn_list->mcc), - gsm_get_mnc(plmn_list->mcc, plmn_list->mnc)); - } - if (!llist_empty(&subscr->plmn_na)) { - print(priv, " List of forbidden PLMNs:\n"); - print(priv, " MCC |MNC |cause\n"); - print(priv, " -------+-------+-------\n"); - llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) - print(priv, " %s |%s%s |#%d " - "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc), - gsm_print_mnc(plmn_na->mnc), - ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", - plmn_na->cause, gsm_get_mcc(plmn_na->mcc), - gsm_get_mnc(plmn_na->mcc, plmn_na->mnc)); - } -} - -/* - * SAP interface integration - */ - -/* Attach SIM card over SAP */ -int gsm_subscr_sapcard(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - int rc; - - if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " - "is detached.\n"); - return -EBUSY; - } - - /* reset subscriber */ - gsm_subscr_exit(ms); - gsm_subscr_init(ms); - - subscr->sim_type = GSM_SIM_TYPE_SAP; - sprintf(subscr->sim_name, "sap"); - subscr->sim_valid = 1; - - /* Try to connect to the SAP interface */ - vty_notify(ms, NULL); - vty_notify(ms, "Connecting to the SAP interface...\n"); - rc = sap_open(ms); - if (rc < 0) { - LOGP(DSAP, LOGL_ERROR, "Failed during sap_open(), no SAP based SIM reader\n"); - vty_notify(ms, "SAP connection error!\n"); - ms->sap_wq.bfd.fd = -1; - - /* Detach SIM */ - subscr->sim_valid = 0; - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - - return rc; - } - - return 0; -} - -/* Deattach sapcard */ -int gsm_subscr_remove_sapcard(struct osmocom_ms *ms) -{ - return sap_close(ms); -} - -int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, - uint8_t res_type, uint16_t param_len, const uint8_t *param_val) -{ - struct msgb *msg; - int rc = 0; - - /* Response parameter is not encoded in case of error */ - if (res_code != SAP_RESULT_OK_REQ_PROC_CORR) - goto ignore_rsp; - - switch (res_type) { - case SAP_TRANSFER_APDU_RESP: - /* Prevent NULL-pointer dereference */ - if (!param_len || !param_val) { - rc = -EINVAL; - goto ignore_rsp; - } - - /* FIXME: why do we use this length? */ - msg = msgb_alloc(GSM_SAP_LENGTH, "sap_apdu"); - if (!msg) { - rc = -ENOMEM; - goto ignore_rsp; - } - - msg->data = msgb_put(msg, param_len); - memcpy(msg->data, param_val, param_len); - - return sim_apdu_resp(ms, msg); - - case SAP_TRANSFER_ATR_RESP: - /* TODO: don't read SIM again (if already) */ - LOGP(DSAP, LOGL_INFO, "SAP card is ready, start reading...\n"); - return subscr_sim_request(ms); - - default: - rc = -ENOTSUP; - goto ignore_rsp; - } - - return 0; - -ignore_rsp: - LOGP(DSAP, LOGL_NOTICE, "Ignored SAP response '%s' (code=%d)\n", - get_value_string(sap_msg_names, res_type), res_code); - return rc; -} -- cgit v1.2.3