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/gerrit-log@lists.osmocom.org/.
Vadim Yanitskiy gerrit-no-reply at lists.osmocom.orgVadim Yanitskiy has uploaded this change for review. ( https://gerrit.osmocom.org/12441 Change subject: layer23/sap_interface.c: reimplement SAP interface ...................................................................... layer23/sap_interface.c: reimplement SAP interface Change-Id: I77bb108615bb2c94c441568f195b04e0a5421643 --- M src/host/layer23/include/osmocom/bb/common/Makefile.am M src/host/layer23/include/osmocom/bb/common/osmocom_data.h A src/host/layer23/include/osmocom/bb/common/sap_fsm.h M src/host/layer23/include/osmocom/bb/common/sap_interface.h A src/host/layer23/include/osmocom/bb/common/sap_proto.h M src/host/layer23/include/osmocom/bb/mobile/subscriber.h M src/host/layer23/src/common/Makefile.am A src/host/layer23/src/common/sap_fsm.c M src/host/layer23/src/common/sap_interface.c A src/host/layer23/src/common/sap_proto.c M src/host/layer23/src/common/sim.c M src/host/layer23/src/mobile/app_mobile.c M src/host/layer23/src/mobile/subscriber.c 13 files changed, 1,342 insertions(+), 562 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/41/12441/1 diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am index cd3437e..d66be98 100644 --- a/src/host/layer23/include/osmocom/bb/common/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am @@ -1,2 +1,3 @@ noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h logging.h \ - networks.h gps.h sysinfo.h osmocom_data.h utils.h + networks.h gps.h sysinfo.h osmocom_data.h utils.h \ + sap_proto.h sap_fsm.h sap_interface.h diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h index 0399f5f..9bbacd8 100644 --- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -23,8 +23,17 @@ #include <osmocom/bb/common/l1ctl.h> struct osmosap_entity { - uint8_t sap_state; + struct osmo_fsm_inst *fi; uint16_t max_msg_size; + uint8_t card_status; + + /* Optional (connection state) event call-backs */ + sap_idle_cb_t idle_cb; + sap_rel_cb_t rel_cb; + + /* Optional (ATR, APDU) event call-backs */ + sap_apdu_cb_t apdu_cb; + sap_atr_cb_t atr_cb; }; struct osmol1_entity { diff --git a/src/host/layer23/include/osmocom/bb/common/sap_fsm.h b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h new file mode 100644 index 0000000..797e023 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/sap_fsm.h @@ -0,0 +1,32 @@ +#pragma once + +#include <osmocom/bb/common/osmocom_data.h> + +/* How long should we wait for connection establishment */ +#define SAP_FSM_CONN_EST_TIMEOUT 5 +#define SAP_FSM_CONN_EST_T 0 + +/* How long should we wait for connection release */ +#define SAP_FSM_CONN_REL_TIMEOUT 3 +#define SAP_FSM_CONN_REL_T 1 + +/* How long should we wait for request to complete */ +#define SAP_FSM_PROC_REQ_TIMEOUT 5 +#define SAP_FSM_PROC_REQ_T 2 + +enum sap_fsm_state { + SAP_STATE_NOT_CONNECTED, + SAP_STATE_CONNECTING, + SAP_STATE_DISCONNECTING, /* Auxiliary state (not from specs) */ + SAP_STATE_WAIT_FOR_CARD, /* Auxiliary state (not from specs) */ + SAP_STATE_IDLE, + SAP_STATE_PROC_ATR_REQ, + SAP_STATE_PROC_APDU_REQ, + SAP_STATE_PROC_RESET_REQ, + SAP_STATE_PROC_STATUS_REQ, + SAP_STATE_PROC_SET_TP_REQ, + SAP_STATE_PROC_POWERON_REQ, + SAP_STATE_PROC_POWEROFF_REQ, +}; + +int sap_fsm_alloc(struct osmocom_ms *ms); diff --git a/src/host/layer23/include/osmocom/bb/common/sap_interface.h b/src/host/layer23/include/osmocom/bb/common/sap_interface.h index 78e3846..bde6822 100644 --- a/src/host/layer23/include/osmocom/bb/common/sap_interface.h +++ b/src/host/layer23/include/osmocom/bb/common/sap_interface.h @@ -1,98 +1,24 @@ #pragma once +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/bb/common/osmocom_data.h> + +typedef int (*sap_idle_cb_t)(struct osmocom_ms *ms); +typedef int (*sap_rel_cb_t)(struct osmocom_ms *ms); + +typedef int (*sap_apdu_cb_t)(struct osmocom_ms *ms, struct msgb *msg); +typedef int (*sap_atr_cb_t)(struct osmocom_ms *ms, + const uint8_t *atr, uint16_t atr_len); + +void sap_init(struct osmocom_ms *ms); int sap_open(struct osmocom_ms *ms); int sap_close(struct osmocom_ms *ms); -int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length); -int osmosap_init(struct osmocom_ms *ms); +int _sap_close_sock(struct osmocom_ms *ms); -enum osmosap_state { - SAP_SOCKET_ERROR, - SAP_NOT_CONNECTED, - SAP_IDLE, - SAP_CONNECTION_UNDER_NEGOTIATION, - SAP_PROCESSING_ATR_REQUEST, - SAP_PROCESSING_APDU_REQUEST -}; +int sap_send_poweron_req(struct osmocom_ms *ms); +int sap_send_poweroff_req(struct osmocom_ms *ms); -/* Table 5.1: Message Overview */ -enum osmosap_msg_type { - SAP_CONNECT_REQ = 0x00, - SAP_CONNECT_RESP = 0x01, - SAP_DISCONNECT_REQ = 0x02, - SAP_DISCONNECT_RESP = 0x03, - SAP_DISCONNECT_IND = 0x04, - SAP_TRANSFER_APDU_REQ = 0x05, - SAP_TRANSFER_APDU_RESP = 0x06, - SAP_TRANSFER_ATR_REQ = 0x07, - SAP_TRANSFER_ATR_RESP = 0x08, - SAP_POWER_SIM_OFF_REQ = 0x09, - SAP_POWER_SIM_OFF_RESP = 0x0A, - SAP_POWER_SIM_ON_REQ = 0x0B, - SAP_POWER_SIM_ON_RESP = 0x0C, - SAP_RESET_SIM_REQ = 0x0D, - SAP_RESET_SIM_RESP = 0x0E, - SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F, - SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10, - SAP_STATUS_IND = 0x11, - SAP_ERROR_RESP = 0x12, - SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13, - SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14 -}; - -/* Table 5.15: List of Parameter IDs */ -enum osmosap_param_type { - SAP_MAX_MSG_SIZE = 0x00, - SAP_CONNECTION_STATUS = 0x01, - SAP_RESULT_CODE = 0x02, - SAP_DISCONNECTION_TYPE = 0x03, - SAP_COMMAND_APDU = 0x04, - SAP_COMMAND_APDU_7816 = 0x10, - SAP_RESPONSE_APDU = 0x05, - SAP_ATR = 0x06, - SAP_CARD_READER_STATUS = 0x07, - SAP_STATUS_CHANGE = 0x08, - SAP_TRANSPORT_PROTOCOL = 0x09 -}; - -/* Table 5.18: Possible values for ResultCode */ -enum sap_result_type { - SAP_RESULT_OK_REQ_PROC_CORR = 0x00, - SAP_RESULT_ERROR_NO_REASON = 0x01, - SAP_RESULT_ERROR_CARD_NOT_ACC = 0x02, - SAP_RESULT_ERROR_CARD_POWERED_OFF = 0x03, - SAP_RESULT_ERROR_CARD_REMOVED = 0x04, - SAP_RESULT_ERROR_CARD_POWERED_ON = 0x05, - SAP_RESULT_ERROR_DATA_UNAVAIL = 0x06, - SAP_RESULT_ERROR_NOT_SUPPORTED = 0x07, -}; - -/* Table 5.19: Possible values for StatusChange */ -enum sap_status_chg_type { - SAP_STATUS_CHG_UNKNOWN_ERROR = 0x00, - SAP_STATUS_CHG_CARD_RESET = 0x01, - SAP_STATUS_CHG_CARD_NOT_ACC = 0x02, - SAP_STATUS_CHG_CARD_REMOVED = 0x03, - SAP_STATUS_CHG_CARD_INSERTED = 0x04, - SAP_STATUS_CHG_CARD_RECOVERED = 0x05, -}; - -/* Table 5.16: Possible values for ConnectionStatus */ -enum sap_conn_status_type { - SAP_CONN_STATUS_OK_READY = 0x00, - SAP_CONN_STATUS_ERROR_CONN = 0x01, - SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE = 0x02, - SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE = 0x03, - SAP_CONN_STATUS_OK_CALL = 0x04, -}; - -struct sap_param { - uint8_t id; - uint16_t len; - uint8_t *value; -}; - -struct sap_msg { - uint8_t id; - uint8_t num_params; - struct sap_param *params; -}; +int sap_send_atr_req(struct osmocom_ms *ms); +int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len); diff --git a/src/host/layer23/include/osmocom/bb/common/sap_proto.h b/src/host/layer23/include/osmocom/bb/common/sap_proto.h new file mode 100644 index 0000000..1c3b2d5 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/sap_proto.h @@ -0,0 +1,121 @@ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +/* Table 5.1: Message Overview + * NOTE: messages are used as events for SAP FSM */ +enum sap_msg_type { + SAP_CONNECT_REQ = 0x00, + SAP_CONNECT_RESP = 0x01, + SAP_DISCONNECT_REQ = 0x02, + SAP_DISCONNECT_RESP = 0x03, + SAP_DISCONNECT_IND = 0x04, + SAP_TRANSFER_APDU_REQ = 0x05, + SAP_TRANSFER_APDU_RESP = 0x06, + SAP_TRANSFER_ATR_REQ = 0x07, + SAP_TRANSFER_ATR_RESP = 0x08, + SAP_POWER_SIM_OFF_REQ = 0x09, + SAP_POWER_SIM_OFF_RESP = 0x0A, + SAP_POWER_SIM_ON_REQ = 0x0B, + SAP_POWER_SIM_ON_RESP = 0x0C, + SAP_RESET_SIM_REQ = 0x0D, + SAP_RESET_SIM_RESP = 0x0E, + SAP_TRANSFER_CARD_READER_STATUS_REQ = 0x0F, + SAP_TRANSFER_CARD_READER_STATUS_RESP = 0x10, + SAP_STATUS_IND = 0x11, + SAP_ERROR_RESP = 0x12, + SAP_SET_TRANSPORT_PROTOCOL_REQ = 0x13, + SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14 +}; + +/* Table 5.15: List of Parameter IDs */ +enum sap_param_type { + SAP_MAX_MSG_SIZE = 0x00, + SAP_CONNECTION_STATUS = 0x01, + SAP_RESULT_CODE = 0x02, + SAP_DISCONNECTION_TYPE = 0x03, + SAP_COMMAND_APDU = 0x04, + SAP_COMMAND_APDU_7816 = 0x10, + SAP_RESPONSE_APDU = 0x05, + SAP_ATR = 0x06, + SAP_CARD_READER_STATUS = 0x07, + SAP_STATUS_CHANGE = 0x08, + SAP_TRANSPORT_PROTOCOL = 0x09 +}; + +/* Table 5.18: Possible values for ResultCode */ +enum sap_result_type { + SAP_RESULT_OK_REQ_PROC_CORR = 0x00, + SAP_RESULT_ERROR_NO_REASON = 0x01, + SAP_RESULT_ERROR_CARD_NOT_ACC = 0x02, + SAP_RESULT_ERROR_CARD_POWERED_OFF = 0x03, + SAP_RESULT_ERROR_CARD_REMOVED = 0x04, + SAP_RESULT_ERROR_CARD_POWERED_ON = 0x05, + SAP_RESULT_ERROR_DATA_UNAVAIL = 0x06, + SAP_RESULT_ERROR_NOT_SUPPORTED = 0x07, +}; + +/* Table 5.19: Possible values for StatusChange */ +enum sap_card_status_type { + SAP_CARD_STATUS_UNKNOWN_ERROR = 0x00, + SAP_CARD_STATUS_CARD_RESET = 0x01, + SAP_CARD_STATUS_CARD_NOT_ACC = 0x02, + SAP_CARD_STATUS_CARD_REMOVED = 0x03, + SAP_CARD_STATUS_CARD_INSERTED = 0x04, + SAP_CARD_STATUS_CARD_RECOVERED = 0x05, +}; + +/* Table 5.16: Possible values for ConnectionStatus */ +enum sap_conn_status_type { + SAP_CONN_STATUS_OK_READY = 0x00, + SAP_CONN_STATUS_ERROR_CONN = 0x01, + SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE = 0x02, + SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE = 0x03, + SAP_CONN_STATUS_OK_CALL = 0x04, +}; + +extern const struct value_string sap_param_names[]; +extern const struct value_string sap_msg_names[]; +extern const struct value_string sap_result_names[]; +extern const struct value_string sap_card_status_names[]; +extern const struct value_string sap_conn_status_names[]; + +/* Figure 5.2: Payload Coding */ +struct sap_param { + /* Parameter ID, see \ref enum sap_param_type */ + uint8_t param_id; + /* Reserved for further use (shall be set to 0x00) */ + uint8_t reserved[1]; + /* Parameter length */ + uint16_t length; + /* Parameter value (and optional padding) */ + uint8_t value[0]; +} __attribute__((packed)); + +/* Figure 5.1 Message Format */ +struct sap_message { + /* Message ID, see \ref enum sap_msg_type */ + uint8_t msg_id; + /* Number of parameters */ + uint8_t num_params; + /* Reserved for further use (shall be set to 0x00) */ + uint8_t reserved[2]; + /* Payload, see \ref struct sap_param */ + uint8_t payload[0]; +} __attribute__((packed)); + +#define GSM_SAP_LENGTH 300 +#define GSM_SAP_HEADROOM 32 + +struct msgb *sap_msgb_alloc(uint8_t msg_id); +const struct sap_message *sap_msg_parse(const uint8_t *buf, size_t buf_len); +int sap_check_result_code(const struct sap_message *sap_msg); + +void sap_msgb_add_param(struct msgb *msg, + enum sap_param_type param_type, + uint16_t param_len, uint8_t *param_value); +struct sap_param *sap_get_param(const struct sap_message *sap_msg, + enum sap_param_type param_type, uint16_t *param_len); diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h index c747af9..37b6db7 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h +++ b/src/host/layer23/include/osmocom/bb/mobile/subscriber.h @@ -93,6 +93,7 @@ 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); +int subscr_sim_request(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); diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am index b76094c..a8d9f7e 100644 --- a/src/host/layer23/src/common/Makefile.am +++ b/src/host/layer23/src/common/Makefile.am @@ -2,5 +2,5 @@ AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) noinst_LIBRARIES = liblayer23.a -liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_interface.c \ +liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_fsm.c sap_proto.c sap_interface.c \ logging.c networks.c sim.c sysinfo.c gps.c l1ctl_lapdm_glue.c utils.c diff --git a/src/host/layer23/src/common/sap_fsm.c b/src/host/layer23/src/common/sap_fsm.c new file mode 100644 index 0000000..94a46a7 --- /dev/null +++ b/src/host/layer23/src/common/sap_fsm.c @@ -0,0 +1,679 @@ +/* + * SAP (SIM Access Profile) FSM definition + * based on Bluetooth SAP specification + * + * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <arpa/inet.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> + +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/common/sap_proto.h> +#include <osmocom/bb/common/sap_fsm.h> + +/*! Send encoded SAP message to the Server + * \param[in] ms MS instance with active SAP connection + * \param[in] msg encoded SAP message buffer + * \returns 0 in case of success, negative in case of error + */ +static int sap_send_msgb(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + + rc = osmo_wqueue_enqueue(&ms->sap_wq, msg); + if (rc) { + LOGP(DSAP, LOGL_ERROR, "Failed to enqueue SAP message\n"); + msgb_free(msg); + return rc; + } + + return 0; +} + +static void sap_fsm_tear_down(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + + /* Flush buffers, close socket */ + _sap_close_sock(ms); + + /* Call release call-back (if present) */ + if (ms->sap_entity.rel_cb) + ms->sap_entity.rel_cb(ms); + + /* Trigger self-destruction :/ */ + osmo_fsm_inst_free(ms->sap_entity.fi); + ms->sap_entity.fi = NULL; +} + +static void sap_fsm_connect(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct msgb *msg; + uint16_t size; + + /* Section 5.1.1, CONNECT_REQ */ + msg = sap_msgb_alloc(SAP_CONNECT_REQ); + if (!msg) + return; + + /* Section 4.1.1, start MaxMsgSize negotiation */ + size = htons(ms->sap_entity.max_msg_size); + sap_msgb_add_param(msg, SAP_MAX_MSG_SIZE, + sizeof(size), (uint8_t *) &size); + + sap_send_msgb(ms, msg); +} + +static void sap_negotiate_msg_size(struct osmosap_entity *sap, + const struct sap_message *sap_msg) +{ + uint16_t size, param_len; + const char *cause = NULL; + struct sap_param *param; + + param = sap_get_param(sap_msg, SAP_MAX_MSG_SIZE, ¶m_len); + if (!param) { + cause = "missing expected MaxMsgSize parameter"; + goto error; + } + if (param_len != sizeof(size)) { + cause = "MaxMsgSize parameter has wrong length"; + goto error; + } + + /* Parse MaxMsgSize suggested by server */ + size = ntohs((uint16_t *) param->value); + if (size > SAP_MAX_MSG_SIZE) { + cause = "suggested MaxMsgSize is too big for us"; + goto error; + } + + /* Attempt to initiate connection again */ + sap->max_msg_size = size; + sap_fsm_connect(sap->fi, sap->fi->state); + return; + +error: + LOGP(DSAP, LOGL_ERROR, "MaxMsgSize negotiation failed: %s\n", cause); + osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0); +} + +static void sap_fsm_conn_handler(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + const struct sap_message *sap_msg = (const struct sap_message *) data; + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct sap_param *param; + uint16_t param_len; + uint8_t status; + + /* Section 5.1.2, CONNECT_RESP */ + param = sap_get_param(sap_msg, SAP_CONNECTION_STATUS, ¶m_len); + if (!param || param_len != sizeof(status)) { + LOGP(DSAP, LOGL_ERROR, "Missing mandatory connection status\n"); + osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0); + return; + } + + /* Parse connection status */ + status = param->value[0]; + + LOGP(DSAP, LOGL_INFO, "SAP connection status (0x%02x): %s\n", + status, get_value_string(sap_conn_status_names, status)); + + switch (status) { + case SAP_CONN_STATUS_OK_CALL: + ms->sap_entity.card_status = SAP_CARD_STATUS_CARD_NOT_ACC; + /* fall-through */ + case SAP_CONN_STATUS_OK_READY: + osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0); + break; + + case SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE: + case SAP_CONN_STATUS_ERROR_CONN: + osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0); + break; + + /* Section 4.1.1, MaxMsgSize negotiation */ + case SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE: + sap_negotiate_msg_size(&ms->sap_entity, sap_msg); + break; + } +} + +static void sap_fsm_conn_release(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct msgb *msg; + + LOGP(DSAP, LOGL_DEBUG, "Client initiated connection release\n"); + + /* We don't care about possible allocating / sending errors */ + msg = sap_msgb_alloc(SAP_DISCONNECT_REQ); + if (msg != NULL) + sap_send_msgb(ms, msg); +} + +static void sap_fsm_release_handler(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + LOGP(DSAP, LOGL_DEBUG, "Client initiated release complete\n"); + osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0); +} + +static void sap_fsm_idle_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + switch (prev_state) { + case SAP_STATE_CONNECTING: + case SAP_STATE_WAIT_FOR_CARD: + /* Sent ATR request (required by the specs) */ + sap_send_atr_req((struct osmocom_ms *) fi->priv); + break; + default: + /* Do nothing */ + break; + } +} + +static void sap_fsm_idle_handler(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct msgb *msg = (struct msgb *) data; + enum sap_fsm_state state; + int rc; + + /* Map event to the corresponding state */ + switch (event) { + case SAP_TRANSFER_ATR_REQ: + state = SAP_STATE_PROC_ATR_REQ; + break; + case SAP_TRANSFER_APDU_REQ: + state = SAP_STATE_PROC_APDU_REQ; + break; + case SAP_RESET_SIM_REQ: + state = SAP_STATE_PROC_RESET_REQ; + break; + case SAP_TRANSFER_CARD_READER_STATUS_REQ: + state = SAP_STATE_PROC_STATUS_REQ; + break; + case SAP_SET_TRANSPORT_PROTOCOL_REQ: + state = SAP_STATE_PROC_SET_TP_REQ; + break; + case SAP_POWER_SIM_ON_REQ: + state = SAP_STATE_PROC_POWERON_REQ; + break; + case SAP_POWER_SIM_OFF_REQ: + state = SAP_STATE_PROC_POWEROFF_REQ; + break; + default: + /* Shall not happen */ + OSMO_ASSERT(0); + } + + rc = sap_send_msgb(ms, msg); + if (rc) + return; + + osmo_fsm_inst_state_chg(fi, state, + SAP_FSM_PROC_REQ_TIMEOUT, SAP_FSM_PROC_REQ_T); +} + +static void sap_fsm_proc_atr_handler(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + const struct sap_message *sap_msg = (const struct sap_message *) data; + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct sap_param *param; + uint16_t param_len; + int rc; + + if (event != SAP_TRANSFER_ATR_RESP) { + LOGP(DSAP, LOGL_ERROR, "Event unhandled (yet)\n"); + return; + } + + /* We're done with ATR request now */ + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + + /* Check the ResultCode */ + rc = sap_check_result_code(sap_msg); + if (rc != SAP_RESULT_OK_REQ_PROC_CORR) { + LOGP(DSAP, LOGL_NOTICE, "Bad ResultCode: '%s'\n", + get_value_string(sap_result_names, rc)); + return; + } + + param = sap_get_param(sap_msg, SAP_ATR, ¶m_len); + if (!param) { + LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n", + get_value_string(sap_param_names, SAP_ATR)); + return; + } + + LOGP(DSAP, LOGL_DEBUG, "RX ATR response: %s\n", + osmo_hexdump(param->value, param_len)); + + /* Forward to ATR handler (if present) */ + if (ms->sap_entity.atr_cb) + ms->sap_entity.atr_cb(ms, param->value, param_len); + + /* We are ready to process APDU requests now */ + if (ms->sap_entity.idle_cb) + ms->sap_entity.idle_cb(ms); +} + +static void sap_fsm_proc_apdu_handler(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + const struct sap_message *sap_msg = (const struct sap_message *) data; + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct sap_param *param; + uint16_t param_len; + struct msgb *msg; + uint8_t *apdu; + int rc; + + if (event != SAP_TRANSFER_APDU_RESP) { + LOGP(DSAP, LOGL_ERROR, "Event unhandled (yet)\n"); + return; + } + + /* We're done with APDU request now */ + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + + /* Check the ResultCode */ + rc = sap_check_result_code(sap_msg); + if (rc != SAP_RESULT_OK_REQ_PROC_CORR) { + LOGP(DSAP, LOGL_NOTICE, "Bad ResultCode: '%s'\n", + get_value_string(sap_result_names, rc)); + return; + } + + param = sap_get_param(sap_msg, SAP_RESPONSE_APDU, ¶m_len); + if (!param) { + LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n", + get_value_string(sap_param_names, SAP_RESPONSE_APDU)); + return; + } + + /* Forward to APDU handler (if present) */ + if (!ms->sap_entity.apdu_cb) { + LOGP(DSAP, LOGL_NOTICE, "No handler set, response APDU ignored\n"); + return; + } + + /* FIXME: why do we use this length?!? */ + msg = msgb_alloc(GSM_SAP_LENGTH, "sim_apdu"); + if (!msg) { + LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory\n"); + return; + } + + apdu = msgb_put(msg, param_len); + memcpy(apdu, param->value, param_len); + + /* Forward to APDU handler (if present) */ + LOGP(DSAP, LOGL_DEBUG, "Forwarding APDU to the handler\n"); + ms->sap_entity.apdu_cb(ms, msg); +} + +/* Generates mask for a single state or event */ +#define S(x) (1 << x) + +/* Figure 4.13: Simplified State Machine */ +static const struct osmo_fsm_state sap_fsm_states[] = { + [SAP_STATE_NOT_CONNECTED] = { + .name = "NOT_CONNECTED", + .out_state_mask = S(SAP_STATE_CONNECTING), + .onenter = &sap_fsm_tear_down, + }, + [SAP_STATE_CONNECTING] = { + .name = "CONNECTING", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_WAIT_FOR_CARD), + .in_event_mask = S(SAP_CONNECT_RESP), + .onenter = &sap_fsm_connect, + .action = &sap_fsm_conn_handler, + }, + /* NOTE: this is a custom state (i.e. not defined by the specs). + * We need it in order to do release procedure correctly. */ + [SAP_STATE_DISCONNECTING] = { + .name = "DISCONNECTING", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED), + .in_event_mask = S(SAP_DISCONNECT_RESP), + .onenter = &sap_fsm_conn_release, + .action = &sap_fsm_release_handler, + }, + /* NOTE: this is a custom state (i.e. not defined by the specs). + * We need it in order to wait until SIM card becomes available. + * SAP_STATUS_IND event is handled by sap_fsm_allstate_action(). */ + [SAP_STATE_WAIT_FOR_CARD] = { + .name = "WAIT_FOR_CARD", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_IDLE), + }, + [SAP_STATE_IDLE] = { + .name = "IDLE", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_PROC_APDU_REQ) + | S(SAP_STATE_PROC_ATR_REQ) + | S(SAP_STATE_PROC_RESET_REQ) + | S(SAP_STATE_PROC_STATUS_REQ) + | S(SAP_STATE_PROC_SET_TP_REQ) + | S(SAP_STATE_PROC_POWERON_REQ) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_TRANSFER_ATR_REQ) + | S(SAP_TRANSFER_APDU_REQ) + | S(SAP_RESET_SIM_REQ) + | S(SAP_TRANSFER_CARD_READER_STATUS_REQ) + | S(SAP_SET_TRANSPORT_PROTOCOL_REQ) + | S(SAP_POWER_SIM_ON_REQ) + | S(SAP_POWER_SIM_OFF_REQ), + .onenter = &sap_fsm_idle_enter, + .action = &sap_fsm_idle_handler, + }, + [SAP_STATE_PROC_ATR_REQ] = { + .name = "PROC_ATR_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE) + | S(SAP_STATE_PROC_RESET_REQ) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_TRANSFER_ATR_RESP) + | S(SAP_RESET_SIM_REQ) + | S(SAP_POWER_SIM_OFF_REQ), + .action = &sap_fsm_proc_atr_handler, + }, + [SAP_STATE_PROC_APDU_REQ] = { + .name = "PROC_APDU_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE) + | S(SAP_STATE_PROC_RESET_REQ) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_TRANSFER_APDU_RESP) + | S(SAP_RESET_SIM_REQ) + | S(SAP_POWER_SIM_OFF_REQ), + .action = &sap_fsm_proc_apdu_handler, + }, + [SAP_STATE_PROC_RESET_REQ] = { + .name = "PROC_RESET_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_RESET_SIM_RESP) + | S(SAP_POWER_SIM_OFF_REQ), + // .action = sap_fsm_proc_handler, + }, + [SAP_STATE_PROC_STATUS_REQ] = { + .name = "PROC_STATUS_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE) + | S(SAP_STATE_PROC_RESET_REQ) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_TRANSFER_CARD_READER_STATUS_RESP) + | S(SAP_RESET_SIM_REQ) + | S(SAP_POWER_SIM_OFF_REQ), + // .action = sap_fsm_proc_handler, + }, + [SAP_STATE_PROC_SET_TP_REQ] = { + .name = "PROC_SET_TP_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE), + .in_event_mask = S(SAP_SET_TRANSPORT_PROTOCOL_RESP), + // .action = sap_fsm_proc_handler, + }, + [SAP_STATE_PROC_POWERON_REQ] = { + .name = "PROC_POWERON_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE) + | S(SAP_STATE_PROC_POWEROFF_REQ), + .in_event_mask = S(SAP_POWER_SIM_ON_RESP) + | S(SAP_POWER_SIM_OFF_REQ), + // .action = sap_fsm_proc_handler, + }, + [SAP_STATE_PROC_POWEROFF_REQ] = { + .name = "PROC_POWEROFF_REQ", + .out_state_mask = S(SAP_STATE_NOT_CONNECTED) + | S(SAP_STATE_DISCONNECTING) + | S(SAP_STATE_WAIT_FOR_CARD) + | S(SAP_STATE_IDLE), + .in_event_mask = S(SAP_POWER_SIM_OFF_RESP), + // .action = sap_fsm_proc_handler, + }, +}; + +static void sap_fsm_handle_card_status_ind(struct osmo_fsm_inst *fi, + const struct sap_message *sap_msg) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fi->priv; + struct sap_param *param; + uint16_t param_len; + uint8_t status; + + param = sap_get_param(sap_msg, SAP_STATUS_CHANGE, ¶m_len); + if (!param || param_len != sizeof(status)) { + LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n", + get_value_string(sap_param_names, SAP_STATUS_CHANGE)); + return; + } + + status = param->value[0]; + + if (ms->sap_entity.card_status != status) { + LOGP(DSAP, LOGL_NOTICE, "(SIM) card status change '%s' -> '%s'\n", + get_value_string(sap_card_status_names, ms->sap_entity.card_status), + get_value_string(sap_card_status_names, status)); + ms->sap_entity.card_status = status; + } + + switch (status) { + /* SIM card is ready */ + case SAP_CARD_STATUS_CARD_RESET: + if (fi->state != SAP_STATE_IDLE) + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + break; + + /* SIM card has recovered after unaccessful state */ + case SAP_CARD_STATUS_CARD_RECOVERED: + if (fi->state != SAP_STATE_IDLE) + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + break; + + /* SIM care inserted, we need to power it on */ + case SAP_CARD_STATUS_CARD_INSERTED: + if (fi->state != SAP_STATE_IDLE) + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + sap_send_poweron_req(ms); + break; + + case SAP_CARD_STATUS_UNKNOWN_ERROR: + case SAP_CARD_STATUS_CARD_NOT_ACC: + case SAP_CARD_STATUS_CARD_REMOVED: + default: /* Unknown card status */ + if (fi->state != SAP_STATE_WAIT_FOR_CARD) + osmo_fsm_inst_state_chg(fi, SAP_STATE_WAIT_FOR_CARD, 0, 0); + break; + } +} + +static void sap_fsm_allstate_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + switch (event) { + /* Disconnect indication initiated by the Server. + * FIXME: at the moment, immediate release is always assumed, + * but ideally we should check type of release (using *data) */ + case SAP_DISCONNECT_IND: + /* This message may arrive in any of the sub-states of + * the "Connected" state (i.e. connection shall exist) */ + if (fi->state < SAP_STATE_WAIT_FOR_CARD) + goto not_peritted; + /* fall-through */ + + /* Disconnect initiated by the Client */ + case SAP_DISCONNECT_REQ: + /* If we have no active connection, tear-down immediately */ + if (fi->state < SAP_STATE_WAIT_FOR_CARD) { + osmo_fsm_inst_state_chg(fi, + SAP_STATE_NOT_CONNECTED, 0, 0); + return; + } + + /* Trigger Client-initiater connection release */ + osmo_fsm_inst_state_chg(fi, SAP_STATE_DISCONNECTING, + SAP_FSM_CONN_REL_TIMEOUT, SAP_FSM_CONN_REL_T); + break; + + /* SIM status indication (inserted or ejected) */ + case SAP_STATUS_IND: + /* This message may arrive in any of the sub-states of + * the "Connected" state (i.e. connection shall exist) */ + if (fi->state < SAP_STATE_WAIT_FOR_CARD) + goto not_peritted; + + sap_fsm_handle_card_status_ind(fi, + (const struct sap_message *) data); + break; + + case SAP_ERROR_RESP: + if (fi->state == SAP_STATE_CONNECTING) { + /* Connection establishment error */ + osmo_fsm_inst_state_chg(fi, + SAP_STATE_NOT_CONNECTED, 0, 0); + } else if (fi->state > SAP_STATE_IDLE) { + /* Error replaces any Request message */ + osmo_fsm_inst_state_chg(fi, + SAP_STATE_IDLE, 0, 0); + } else { + /* Should not happen in general */ + goto not_peritted; + } + break; + + default: + /* Shall not happen */ + OSMO_ASSERT(0); + } + + return; + +not_peritted: + LOGPFSML(fi, LOGL_NOTICE, "Event '%s' is not " + "permitted in state '%s', please fix!\n", + osmo_fsm_event_name(fi->fsm, event), + osmo_fsm_state_name(fi->fsm, fi->state)); +} + +static int sap_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + switch (fi->state) { + /* Connection establishment / release timeout */ + case SAP_STATE_DISCONNECTING: + case SAP_STATE_CONNECTING: + LOGP(DSAP, LOGL_NOTICE, "Connection timeout\n"); + osmo_fsm_inst_state_chg(fi, SAP_STATE_NOT_CONNECTED, 0, 0); + break; + + /* Request processing timeout */ + case SAP_STATE_PROC_ATR_REQ: + case SAP_STATE_PROC_APDU_REQ: + case SAP_STATE_PROC_RESET_REQ: + case SAP_STATE_PROC_STATUS_REQ: + case SAP_STATE_PROC_SET_TP_REQ: + case SAP_STATE_PROC_POWERON_REQ: + case SAP_STATE_PROC_POWEROFF_REQ: + LOGP(DSAP, LOGL_NOTICE, "Timeout waiting for '%s' to complete, " + "going back to IDLE\n", osmo_fsm_inst_state_name(fi)); + osmo_fsm_inst_state_chg(fi, SAP_STATE_IDLE, 0, 0); + break; + + default: + LOGP(DSAP, LOGL_ERROR, "Unhandled state '%s'\n", + osmo_fsm_inst_state_name(fi)); + } + + /* Do not tear-down FSM */ + return 0; +} + +static struct osmo_fsm sap_fsm_def = { + .name = "sap_fsm", + .log_subsys = DSAP, + .states = sap_fsm_states, + .num_states = ARRAY_SIZE(sap_fsm_states), + .event_names = sap_msg_names, + .timer_cb = &sap_fsm_timer_cb, + .allstate_action = &sap_fsm_allstate_action, + .allstate_event_mask = 0 + | S(SAP_DISCONNECT_REQ) + | S(SAP_DISCONNECT_IND) + | S(SAP_STATUS_IND) + | S(SAP_ERROR_RESP), +}; + +int sap_fsm_alloc(struct osmocom_ms *ms) +{ + struct osmosap_entity *sap; + + sap = &ms->sap_entity; + OSMO_ASSERT(sap->fi == NULL); + + /* Register our FSM (if required) */ + if (!osmo_fsm_find_by_name(sap_fsm_def.name)) + OSMO_ASSERT(osmo_fsm_register(&sap_fsm_def) == 0); + + /* Allocate an instance using ms as talloc context */ + sap->fi = osmo_fsm_inst_alloc(&sap_fsm_def, ms, + ms, LOGL_DEBUG, ms->name); + if (!sap->fi) { + LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP FSM\n"); + return -ENOMEM; + } + + return 0; +} diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c index c31d07b..4ed457f 100644 --- a/src/host/layer23/src/common/sap_interface.c +++ b/src/host/layer23/src/common/sap_interface.c @@ -4,6 +4,7 @@ * (C) 2010,2018 by Harald Welte <laforge at gnumonks.org> * (C) 2010 by Andreas Eversberg <jolly at eversberg.eu> * (C) 2011 by Nico Golde <nico at ngolde.de> + * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com> * * All Rights Reserved * @@ -23,536 +24,272 @@ * */ -#include <osmocom/bb/common/osmocom_data.h> -#include <osmocom/bb/common/logging.h> -#include <osmocom/bb/common/sap_interface.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/socket.h> - -#include <sys/socket.h> -#include <sys/un.h> - -#include <arpa/inet.h> - -#define _GNU_SOURCE #include <unistd.h> #include <errno.h> -#include <string.h> -#include <stdlib.h> -#define GSM_SAP_LENGTH 300 -#define GSM_SAP_HEADROOM 32 +#include <sys/socket.h> -static void sap_connect(struct osmocom_ms *ms); +#include <osmocom/core/write_queue.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/fsm.h> -/* Table 5.15: List of Parameter IDs */ -static const struct value_string sap_param_names[] = { - { SAP_MAX_MSG_SIZE, "MaxMsgSize" }, - { SAP_CONNECTION_STATUS, "ConnectionStatus" }, - { SAP_RESULT_CODE, "ResultCode" }, - { SAP_DISCONNECTION_TYPE, "DisconnectionType" }, - { SAP_COMMAND_APDU, "CommandAPDU" }, - { SAP_COMMAND_APDU_7816, "CommandAPDU7816" }, - { SAP_RESPONSE_APDU, "ResponseAPDU" }, - { SAP_ATR, "ATR" }, - { SAP_CARD_READER_STATUS, "CardReaderStatus" }, - { SAP_STATUS_CHANGE, "StatusChange" }, - { SAP_TRANSPORT_PROTOCOL, "TransportProtocol" }, -}; +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> -/* Table 5.1: Message Overview */ -static const struct value_string sap_msg_names[] = { - { SAP_CONNECT_REQ, "CONNECT_REQ" }, - { SAP_CONNECT_RESP, "CONNECT_RESP" }, - { SAP_DISCONNECT_REQ, "DISCONNECT_REQ" }, - { SAP_DISCONNECT_RESP, "DISCONNECT_RESP" }, - { SAP_DISCONNECT_IND, "DISCONNECT_IND" }, - { SAP_TRANSFER_APDU_REQ, "TRANSFER_APDU_REQ" }, - { SAP_TRANSFER_APDU_RESP, "TRANSFER_APDU_RESP" }, - { SAP_TRANSFER_ATR_REQ, "TRANSFER_ATR_REQ" }, - { SAP_TRANSFER_ATR_RESP, "TRANSFER_ATR_RESP" }, - { SAP_POWER_SIM_OFF_REQ, "POWER_SIM_OFF_REQ" }, - { SAP_POWER_SIM_OFF_RESP, "POWER_SIM_OFF_RESP" }, - { SAP_POWER_SIM_ON_REQ, "POWER_SIM_ON_REQ" }, - { SAP_POWER_SIM_ON_RESP, "POWER_SIM_ON_RESP" }, - { SAP_RESET_SIM_REQ, "RESET_SIM_REQ" }, - { SAP_RESET_SIM_RESP, "RESET_SIM_RESP" }, - { SAP_TRANSFER_CARD_READER_STATUS_REQ, "TRANSFER_CARD_READER_STATUS_REQ" }, - { SAP_TRANSFER_CARD_READER_STATUS_RESP, "TRANSFER_CARD_READER_STATUS_RESP" }, - { SAP_STATUS_IND, "STATUS_IND" }, - { SAP_ERROR_RESP, "ERROR_RESP" }, - { SAP_SET_TRANSPORT_PROTOCOL_REQ, "SET_TRANSPORT_PROTOCOL_REQ" }, - { SAP_SET_TRANSPORT_PROTOCOL_RESP, "SET_TRANSPORT_PROTOCOL_RESP" }, -}; +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/common/sap_proto.h> +#include <osmocom/bb/common/sap_fsm.h> -/* Table 5.18: Possible values for ResultCode */ -static const struct value_string sap_result_names[] = { - { SAP_RESULT_OK_REQ_PROC_CORR, "OK, request processed correctly" }, - { SAP_RESULT_ERROR_NO_REASON, "Error, no reason defined" }, - { SAP_RESULT_ERROR_CARD_NOT_ACC, "Error, card not accessible" }, - { SAP_RESULT_ERROR_CARD_POWERED_OFF, "Error, card (already) powered off" }, - { SAP_RESULT_ERROR_CARD_REMOVED, "Error, card removed" }, - { SAP_RESULT_ERROR_CARD_POWERED_ON, "Error, card already powered on" }, - { SAP_RESULT_ERROR_DATA_UNAVAIL, "Error, data not available" }, - { SAP_RESULT_ERROR_NOT_SUPPORTED, "Error, not supported "} -}; - -/* Table 5.19: Possible values for StatusChange */ -static const struct value_string sap_status_change_names[] = { - { SAP_STATUS_CHG_UNKNOWN_ERROR, "Unknown Error" }, - { SAP_STATUS_CHG_CARD_RESET, "Card reset" }, - { SAP_STATUS_CHG_CARD_NOT_ACC, "Card not accessible" }, - { SAP_STATUS_CHG_CARD_REMOVED, "Card removed" }, - { SAP_STATUS_CHG_CARD_INSERTED, "Card inserted" }, - { SAP_STATUS_CHG_CARD_RECOVERED, "Card recovered" }, -}; - -/* Table 5.16: Possible values for ConnectionStatus */ -static const struct value_string sap_status_names[] = { - { SAP_CONN_STATUS_OK_READY, "OK, Server can fulfill requirements" }, - { SAP_CONN_STATUS_ERROR_CONN, "Error, Server unable to establish connection" }, - { SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE, "Error, Server does not support maximum message size" }, - { SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE, "Error, maximum message size by Client is too small" }, - { SAP_CONN_STATUS_OK_CALL, "OK, ongoing call" }, -}; - -static struct msgb *sap_create_msg(uint8_t id, uint8_t num_params, struct sap_param *params) +/*! Send ATR request to the Server. + * \param[in] ms MS instance with active SAP connection + * \returns 0 in case of success, negative in case of error + */ +int sap_send_atr_req(struct osmocom_ms *ms) { struct msgb *msg; - uint8_t *msgp; - uint8_t i, plen, padding = 0; + int rc; - msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap"); - if (!msg) { - LOGP(DSAP, LOGL_ERROR, "Failed to allocate msg.\n"); - return NULL; - } - - /* BTSAP 5.1 */ - msgb_put_u8(msg, id); - msgb_put_u8(msg, num_params); - msgb_put_u16(msg, 0); - - for(i=0; i<num_params; i++){ - plen = params[i].len; - msgb_put_u8(msg, params[i].id); - msgb_put_u8(msg, 0); - msgb_put_u16(msg, plen); - if(plen % 4){ - padding = 4 - (plen % 4); - } - msgp = msgb_put(msg, plen + padding); - memcpy(msgp, params[i].value, plen); - - if(padding){ - memset(msgp + plen, 0, padding); - } - } - - return msg; -} - -static int osmosap_send(struct osmocom_ms *ms, struct msgb *msg) -{ - if(ms->sap_entity.sap_state == SAP_NOT_CONNECTED) - sap_connect(ms); - - if (ms->sap_wq.bfd.fd <= 0) - return -EINVAL; - - if (osmo_wqueue_enqueue(&ms->sap_wq, msg) != 0) { - LOGP(DSAP, LOGL_ERROR, "Failed to enqueue msg.\n"); - msgb_free(msg); - return -1; - } - - return 0; -} - -static int sap_parse_result(struct sap_param *param) -{ - if(param->id != SAP_RESULT_CODE){ - LOGP(DSAP, LOGL_INFO, "> Parameter id: %u no valid result type\n", param->id); - return -1; - } else { - LOGP(DSAP, LOGL_INFO, "> RESULT CODE: %s\n", - get_value_string(sap_result_names, param->value[0])); - } - - if(param->value[0] > ARRAY_SIZE(sap_result_names)){ - return -1; - } - - return 0; -} - -static uint8_t *sap_get_param(uint8_t *data, struct sap_param *param) -{ - uint8_t *dptr = data; - uint8_t padlen; - - param->id = *dptr++; - /* skip reserved byte */ - dptr++; - param->len = *dptr << 8; - dptr++; - param->len |= *dptr++; - param->value = talloc_zero_size(NULL, param->len); - memcpy(param->value, dptr, param->len); - - /* skip parameter and padding and return pointer to next parameter */ - dptr += param->len; - if(param->len % 4){ - padlen = (4 - param->len % 4); - } else { - padlen = 0; - } - dptr += padlen; - - return dptr; -} - -static void sap_msg_free(struct sap_msg *msg) -{ - uint8_t i; - for(i=0; i<msg->num_params; i++){ - talloc_free(msg->params[i].value); - talloc_free(msg->params); - } - talloc_free(msg); -} - -static struct sap_msg *sap_parse_msg(uint8_t *data) -{ - struct sap_msg *msg = talloc_zero(NULL, struct sap_msg); - uint8_t *ptr = data; - uint8_t i; - - if(!msg){ - return NULL; - } - - msg->id = *ptr++; - LOGP(DSAP, LOGL_INFO, "> %s \n", get_value_string(sap_msg_names, msg->id)); - - msg->num_params = *ptr++; - /* skip two reserved null bytes, BTSAP 5.1 */ - ptr += 2; - - msg->params = talloc_zero_size(NULL, sizeof(struct sap_param) * msg->num_params); - - for(i=0; i<msg->num_params; i++){ - ptr = sap_get_param(ptr, &msg->params[i]); - LOGP(DSAP, LOGL_INFO, "> %s %s\n", - get_value_string(sap_param_names, msg->params[i].id), - osmo_hexdump(msg->params[i].value, msg->params[i].len)); - } - - return msg; -} - -static void sap_apdu_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len) -{ - struct msgb *msg; - uint8_t *apdu; - msg = msgb_alloc(GSM_SAP_LENGTH, "osmosap"); - if(!msg){ - LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory.\n"); - return; - } - - apdu = msgb_put(msg, len); - memcpy(apdu, data, len); - - LOGP(DSAP, LOGL_DEBUG, "Forwarding APDU to SIM handler.\n"); - sim_apdu_resp(ms, msg); -} - -static int sap_adapt_msg_size(struct osmocom_ms *ms, struct sap_param *param) -{ - uint16_t size; - size = (param->value[0] << 8) | param->value[1]; - if(size != ms->sap_entity.max_msg_size && size > 0){ - LOGP(DSAP, LOGL_NOTICE, "Server can not handle max_msg_size, adapting.\n"); - ms->sap_entity.max_msg_size = size; - return -1; - } - return 0; -} - -static void sap_atr(struct osmocom_ms *ms) -{ - struct msgb *msg; - if(ms->sap_entity.sap_state != SAP_IDLE){ - LOGP(DSAP, LOGL_ERROR, "Attempting to send ATR request while not being idle.\n"); - return; - } - - msg = sap_create_msg(SAP_TRANSFER_ATR_REQ, 0, NULL); - if(!msg) - return; - - osmosap_send(ms, msg); - ms->sap_entity.sap_state = SAP_PROCESSING_ATR_REQUEST; -} - -static void sap_parse_resp(struct osmocom_ms *ms, uint8_t *data, uint16_t len) -{ - struct sap_msg *msg; - - msg = sap_parse_msg(data); - if (!msg) { - LOGP(DSAP, LOGL_ERROR, "Failed to parse SAP message\n"); - return; - } - - switch(msg->id){ - case SAP_CONNECT_RESP: - LOGP(DSAP, LOGL_INFO, "Status: %s\n", get_value_string(sap_status_names, msg->params[0].value[0])); - if(msg->params[0].value[0] == 0){ - ms->sap_entity.sap_state = SAP_IDLE; - } - if(msg->num_params == 2 && msg->params[1].len == 2){ - if(sap_adapt_msg_size(ms, &msg->params[1]) < 0) { - ms->sap_entity.sap_state = SAP_NOT_CONNECTED; - } else { - sap_atr(ms); - } - } - break; - case SAP_DISCONNECT_RESP: - ms->sap_entity.sap_state = SAP_NOT_CONNECTED; - break; - case SAP_STATUS_IND: - LOGP(DSAP, LOGL_INFO, "New card state: %s\n", get_value_string(sap_status_change_names, - msg->params[0].value[0])); - if(msg->params[0].value[0] != 1){ - /* TODO: handle case in which the card is not ready yet */ - } - break; - case SAP_TRANSFER_ATR_RESP: - if(ms->sap_entity.sap_state != SAP_PROCESSING_ATR_REQUEST){ - LOGP(DSAP, LOGL_ERROR, "got ATR resp in state: %u\n", ms->sap_entity.sap_state); - return; - } - if(msg->num_params >= 2){ - LOGP(DSAP, LOGL_INFO, "ATR: %s\n", osmo_hexdump(msg->params[1].value, msg->params[1].len)); - } - ms->sap_entity.sap_state = SAP_IDLE; - break; - case SAP_TRANSFER_APDU_RESP: - if(ms->sap_entity.sap_state != SAP_PROCESSING_APDU_REQUEST){ - LOGP(DSAP, LOGL_ERROR, "got APDU resp in state: %u\n", ms->sap_entity.sap_state); - return; - } - if(msg->num_params != 2){ - LOGP(DSAP, LOGL_ERROR, "wrong number of parameters %u in APDU response\n", msg->num_params); - return; - } - ms->sap_entity.sap_state = SAP_IDLE; - if(sap_parse_result(&msg->params[0]) == 0){ - /* back apdu resp to layer23 */ - sap_apdu_resp(ms, msg->params[1].value, msg->params[1].len); - LOGP(DSAP, LOGL_INFO, "sap_apdu_resp called, sending data back to layer23\n"); - } - break; - case SAP_ERROR_RESP: - if(ms->sap_entity.sap_state == SAP_CONNECTION_UNDER_NEGOTIATION){ - ms->sap_entity.sap_state = SAP_NOT_CONNECTED; - } else { - ms->sap_entity.sap_state = SAP_IDLE; - } - break; - default: - LOGP(DSAP, LOGL_ERROR, "got unknown or not implemented SAP msgid: %u\n", msg->id); - break; - } -} - -static int sap_read(struct osmo_fd *fd) -{ - struct msgb *msg = NULL; - struct osmocom_ms *ms = (struct osmocom_ms *) fd->data; - uint8_t *sap_buffer; - ssize_t rc; - - sap_buffer = talloc_zero_size(NULL, ms->sap_entity.max_msg_size); - if(!sap_buffer){ - LOGP(DSAP, LOGL_ERROR, "Failed to allocate memory\n"); + msg = sap_msgb_alloc(SAP_TRANSFER_ATR_REQ); + if (!msg) return -ENOMEM; - } - rc = read(fd->fd, sap_buffer, ms->sap_entity.max_msg_size - 1); - if (rc < 0) { - LOGP(DSAP, LOGL_ERROR,"SAP socket failed\n"); + rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi, + SAP_TRANSFER_ATR_REQ, msg); + if (rc) { msgb_free(msg); - sap_close(ms); return rc; } - if(rc == 0) { - LOGP(DSAP, LOGL_NOTICE, "SAP socket closed by server\n"); - msgb_free(msg); - sap_close(ms); - return -ECONNREFUSED; - } - - sap_buffer[rc] = 0; - LOGP(DSAP, LOGL_INFO, "Received %zd bytes: %s\n", rc, osmo_hexdump(sap_buffer, rc)); - - sap_parse_resp(ms, sap_buffer, rc); - - talloc_free(sap_buffer); return 0; } -static int sap_write(struct osmo_fd *fd, struct msgb *msg) +/*! Send APDU request to the Server. + * \param[in] ms MS instance with active SAP connection + * \param[in] apdu APDU to be send + * \param[in] apdu_len length of APDU + * \returns 0 in case of success, negative in case of error + */ +int sap_send_apdu(struct osmocom_ms *ms, uint8_t *apdu, uint16_t apdu_len) +{ + struct msgb *msg; + int rc; + + msg = sap_msgb_alloc(SAP_TRANSFER_APDU_REQ); + if (!msg) + return -ENOMEM; + + sap_msgb_add_param(msg, SAP_COMMAND_APDU, apdu_len, apdu); + + rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi, + SAP_TRANSFER_APDU_REQ, msg); + if (rc) { + msgb_free(msg); + return rc; + } + + return 0; +} + +/*! Send (SIM) power on request to the Server. + * \param[in] ms MS instance with active SAP connection + * \returns 0 in case of success, negative in case of error + */ +int sap_send_poweron_req(struct osmocom_ms *ms) +{ + struct msgb *msg; + int rc; + + msg = sap_msgb_alloc(SAP_POWER_SIM_ON_REQ); + if (!msg) + return -ENOMEM; + + rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi, + SAP_POWER_SIM_ON_REQ, msg); + if (rc) { + msgb_free(msg); + return rc; + } + + return 0; +} + +/*! Send (SIM) power off request to the Server. + * \param[in] ms MS instance with active SAP connection + * \returns 0 in case of success, negative in case of error + */ +int sap_send_poweroff_req(struct osmocom_ms *ms) +{ + struct msgb *msg; + int rc; + + msg = sap_msgb_alloc(SAP_POWER_SIM_OFF_REQ); + if (!msg) + return -ENOMEM; + + rc = osmo_fsm_inst_dispatch(ms->sap_entity.fi, + SAP_POWER_SIM_OFF_REQ, msg); + if (rc) { + msgb_free(msg); + return rc; + } + + return 0; +} + +static int sap_read_cb(struct osmo_fd *fd) +{ + struct osmocom_ms *ms = (struct osmocom_ms *) fd->data; + struct osmosap_entity *sap = &ms->sap_entity; + const struct sap_message *sap_msg; + uint8_t buf[GSM_SAP_LENGTH]; + ssize_t rc; + + /* Prevent buffer overflow */ + OSMO_ASSERT(sap->max_msg_size <= GSM_SAP_LENGTH); + + rc = read(fd->fd, buf, sap->max_msg_size); + if (rc < 0) { + LOGP(DSAP, LOGL_ERROR, "SAP socket failed\n"); + rc = -EIO; + goto error; + } + if (rc == 0) { + LOGP(DSAP, LOGL_NOTICE, "SAP socket closed by server\n"); + rc = -ECONNREFUSED; + goto error; + } + + LOGP(DSAP, LOGL_DEBUG, "RX SAP message (len=%zd): %s\n", + rc, osmo_hexdump(buf, rc)); + + /* Parse SAP message */ + sap_msg = sap_msg_parse(buf, rc); + if (!sap_msg) { + LOGP(DSAP, LOGL_ERROR, "Failed to parse SAP message\n"); + return -EINVAL; + } + + /* Pass received message to our FSM using message + * type as the FSM event name */ + osmo_fsm_inst_dispatch(sap->fi, sap_msg->msg_id, sap_msg); + + return 0; + +error: + /* Immediately tear-down FSM */ + osmo_fsm_inst_state_chg(sap->fi, SAP_STATE_NOT_CONNECTED, 0, 0); + return rc; +} + +static int sap_write_cb(struct osmo_fd *fd, struct msgb *msg) { ssize_t rc; if (fd->fd <= 0) return -EINVAL; - LOGP(DSAP, LOGL_INFO, "< %s\n", osmo_hexdump(msg->data, msg->len)); rc = write(fd->fd, msg->data, msg->len); if (rc != msg->len) { - LOGP(DSAP, LOGL_ERROR, "Failed to write data: rc: %zd\n", rc); + LOGP(DSAP, LOGL_ERROR, "Failed to write data\n"); return rc; } + LOGP(DSAP, LOGL_DEBUG, "TX SAP message (len=%u): %s\n", + msg->len, osmo_hexdump(msg->data, msg->len)); + return 0; } -static void sap_connect(struct osmocom_ms *ms) -{ - uint8_t buffer[3]; - struct msgb *msg; - uint16_t size = ms->sap_entity.max_msg_size; - struct sap_param params[1]; - - params[0].id = SAP_MAX_MSG_SIZE; - params[0].len = 2; - - if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED) { - LOGP(DSAP, LOGL_ERROR, "Attempting to connect while there is an active connection.\n"); - return; - } - - buffer[0] = (size >> 8) & 0xFF; - buffer[1] = size & 0xFF; - buffer[2] = 0; - params[0].value = buffer; - - msg = sap_create_msg(SAP_CONNECT_REQ, 1, params); - if(!msg) - return; - - osmosap_send(ms, msg); - - ms->sap_entity.sap_state = SAP_CONNECTION_UNDER_NEGOTIATION; -} - -static void sap_disconnect(struct osmocom_ms *ms) -{ - struct msgb *msg; - if(ms->sap_entity.sap_state != SAP_NOT_CONNECTED && ms->sap_entity.sap_state != SAP_CONNECTION_UNDER_NEGOTIATION){ - LOGP(DSAP, LOGL_ERROR, "Attempting to disconnect while no active connection.\n"); - return; - } - - msg = sap_create_msg(SAP_DISCONNECT_REQ, 0, NULL); - if(!msg) - return; - - osmosap_send(ms, msg); - - ms->sap_entity.sap_state = SAP_NOT_CONNECTED; -} - -static int sap_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t len) -{ - struct msgb *msg; - struct sap_param params[1]; - int rc; - - params[0].id = SAP_COMMAND_APDU; - params[0].len = len; - params[0].value = data; - - if(ms->sap_entity.sap_state != SAP_IDLE){ - LOGP(DSAP, LOGL_ERROR, "Attempting to send APDU request while not being idle.\n"); - return -EIO; - } - - msg = sap_create_msg(SAP_TRANSFER_APDU_REQ, 1, params); - if(!msg) - return -ENOMEM; - - rc = osmosap_send(ms, msg); - if (rc) - return rc; - - ms->sap_entity.sap_state = SAP_PROCESSING_APDU_REQUEST; - - return 0; -} - +/*! Establishes SAP connection to the Server, + * allocates SAP FSM, and triggers connection procedure. + * \param[in] ms MS instance with configured SAP socket path + * \returns 0 in case of success, negative in case of error + */ int sap_open(struct osmocom_ms *ms) { int rc; + LOGP(DSAP, LOGL_INFO, "Establishing SAP connection " + "(using socket '%s')\n", ms->settings.sap_socket_path); + rc = osmo_sock_unix_init_ofd(&ms->sap_wq.bfd, SOCK_STREAM, 0, ms->settings.sap_socket_path, OSMO_SOCK_F_CONNECT); if (rc < 0) { LOGP(DSAP, LOGL_ERROR, "Failed to create unix domain socket %s: %s\n", ms->settings.sap_socket_path, strerror(-rc)); - ms->sap_entity.sap_state = SAP_SOCKET_ERROR; return rc; } osmo_wqueue_init(&ms->sap_wq, 100); ms->sap_wq.bfd.data = ms; - ms->sap_wq.read_cb = sap_read; - ms->sap_wq.write_cb = sap_write; + ms->sap_wq.read_cb = &sap_read_cb; + ms->sap_wq.write_cb = &sap_write_cb; - sap_connect(ms); + /* Allocate a SAP FSM for a given ms */ + rc = sap_fsm_alloc(ms); + if (rc) { + _sap_close_sock(ms); + return rc; + } - return 0; + /* Initiate SAP connection with Server */ + LOGP(DSAP, LOGL_DEBUG, "Connecting to the Server...\n"); + return osmo_fsm_inst_state_chg(ms->sap_entity.fi, SAP_STATE_CONNECTING, + SAP_FSM_CONN_EST_TIMEOUT, SAP_FSM_CONN_EST_T); } +/*! Closes SAP connection with the Server. + * \param[in] ms MS instance with active SAP connection + * \returns 0 in case of success, negative in case of error + */ int sap_close(struct osmocom_ms *ms) { + if (ms->sap_entity.fi == NULL) { + LOGP(DSAP, LOGL_NOTICE, "No active SAP connection (no FSM)\n"); + return -EINVAL; + } + + LOGP(DSAP, LOGL_INFO, "Closing SAP connection\n"); + return osmo_fsm_inst_dispatch(ms->sap_entity.fi, + SAP_DISCONNECT_REQ, NULL); +} + +/*! Low-level function for closing SAP (socket) connection. + * \param[in] ms MS instance with active SAP connection + * \returns 0 in case of success, negative in case of error + */ +int _sap_close_sock(struct osmocom_ms *ms) +{ if (ms->sap_wq.bfd.fd <= 0) return -EINVAL; - sap_disconnect(ms); close(ms->sap_wq.bfd.fd); ms->sap_wq.bfd.fd = -1; + osmo_fd_unregister(&ms->sap_wq.bfd); osmo_wqueue_clear(&ms->sap_wq); return 0; } -/* same signature as in L1CTL, so it can be called from sim.c */ -int osmosap_send_apdu(struct osmocom_ms *ms, uint8_t *data, uint16_t length) -{ - //LOGP(DSAP, LOGL_ERROR, "Received the following APDU from sim.c: %s\n" , - // osmo_hexdump(data, length)); - return sap_apdu(ms, data, length); -} - -/* init */ -int osmosap_init(struct osmocom_ms *ms) +/*! Initiates SAP client state for a given MS. */ +void sap_init(struct osmocom_ms *ms) { struct osmosap_entity *sap = &ms->sap_entity; LOGP(DSAP, LOGL_INFO, "init SAP client\n"); - sap->sap_state = SAP_NOT_CONNECTED; + + /* Default MaxMsgSize (to be negotiated) */ sap->max_msg_size = GSM_SAP_LENGTH; + /* SIM card status is not known yet */ + sap->card_status = SAP_CARD_STATUS_CARD_NOT_ACC; - return 0; + /* Optional event call-backs */ + sap->idle_cb = NULL; + sap->rel_cb = NULL; + sap->apdu_cb = NULL; + sap->atr_cb = NULL; } - diff --git a/src/host/layer23/src/common/sap_proto.c b/src/host/layer23/src/common/sap_proto.c new file mode 100644 index 0000000..b170eb6 --- /dev/null +++ b/src/host/layer23/src/common/sap_proto.c @@ -0,0 +1,271 @@ +/* + * SAP (SIM Access Profile) protocol definition + * based on Bluetooth SAP specification + * + * (C) 2011 by Nico Golde <nico at ngolde.de> + * (C) 2018 by Vadim Yanitskiy <axilirator at gmail.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include <arpa/inet.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/bb/common/sap_proto.h> +#include <osmocom/bb/common/logging.h> + +/* Table 5.15: List of Parameter IDs */ +const struct value_string sap_param_names[] = { + { SAP_MAX_MSG_SIZE, "MaxMsgSize" }, + { SAP_CONNECTION_STATUS, "ConnectionStatus" }, + { SAP_RESULT_CODE, "ResultCode" }, + { SAP_DISCONNECTION_TYPE, "DisconnectionType" }, + { SAP_COMMAND_APDU, "CommandAPDU" }, + { SAP_COMMAND_APDU_7816, "CommandAPDU7816" }, + { SAP_RESPONSE_APDU, "ResponseAPDU" }, + { SAP_ATR, "ATR" }, + { SAP_CARD_READER_STATUS, "CardReaderStatus" }, + { SAP_STATUS_CHANGE, "StatusChange" }, + { SAP_TRANSPORT_PROTOCOL, "TransportProtocol" }, +}; + +/* Table 5.1: Message Overview */ +const struct value_string sap_msg_names[] = { + { SAP_CONNECT_REQ, "CONNECT_REQ" }, + { SAP_CONNECT_RESP, "CONNECT_RESP" }, + { SAP_DISCONNECT_REQ, "DISCONNECT_REQ" }, + { SAP_DISCONNECT_RESP, "DISCONNECT_RESP" }, + { SAP_DISCONNECT_IND, "DISCONNECT_IND" }, + { SAP_TRANSFER_APDU_REQ, "TRANSFER_APDU_REQ" }, + { SAP_TRANSFER_APDU_RESP, "TRANSFER_APDU_RESP" }, + { SAP_TRANSFER_ATR_REQ, "TRANSFER_ATR_REQ" }, + { SAP_TRANSFER_ATR_RESP, "TRANSFER_ATR_RESP" }, + { SAP_POWER_SIM_OFF_REQ, "POWER_SIM_OFF_REQ" }, + { SAP_POWER_SIM_OFF_RESP, "POWER_SIM_OFF_RESP" }, + { SAP_POWER_SIM_ON_REQ, "POWER_SIM_ON_REQ" }, + { SAP_POWER_SIM_ON_RESP, "POWER_SIM_ON_RESP" }, + { SAP_RESET_SIM_REQ, "RESET_SIM_REQ" }, + { SAP_RESET_SIM_RESP, "RESET_SIM_RESP" }, + { SAP_TRANSFER_CARD_READER_STATUS_REQ, "TRANSFER_CARD_READER_STATUS_REQ" }, + { SAP_TRANSFER_CARD_READER_STATUS_RESP, "TRANSFER_CARD_READER_STATUS_RESP" }, + { SAP_STATUS_IND, "STATUS_IND" }, + { SAP_ERROR_RESP, "ERROR_RESP" }, + { SAP_SET_TRANSPORT_PROTOCOL_REQ, "SET_TRANSPORT_PROTOCOL_REQ" }, + { SAP_SET_TRANSPORT_PROTOCOL_RESP, "SET_TRANSPORT_PROTOCOL_RESP" }, +}; + +/* Table 5.18: Possible values for ResultCode */ +const struct value_string sap_result_names[] = { + { SAP_RESULT_OK_REQ_PROC_CORR, "OK, request processed correctly" }, + { SAP_RESULT_ERROR_NO_REASON, "Error, no reason defined" }, + { SAP_RESULT_ERROR_CARD_NOT_ACC, "Error, card not accessible" }, + { SAP_RESULT_ERROR_CARD_POWERED_OFF, "Error, card (already) powered off" }, + { SAP_RESULT_ERROR_CARD_REMOVED, "Error, card removed" }, + { SAP_RESULT_ERROR_CARD_POWERED_ON, "Error, card already powered on" }, + { SAP_RESULT_ERROR_DATA_UNAVAIL, "Error, data not available" }, + { SAP_RESULT_ERROR_NOT_SUPPORTED, "Error, not supported "} +}; + +/* Table 5.19: Possible values for StatusChange */ +const struct value_string sap_card_status_names[] = { + { SAP_CARD_STATUS_UNKNOWN_ERROR, "Unknown Error" }, + { SAP_CARD_STATUS_CARD_RESET, "Card reset" }, + { SAP_CARD_STATUS_CARD_NOT_ACC, "Card not accessible" }, + { SAP_CARD_STATUS_CARD_REMOVED, "Card removed" }, + { SAP_CARD_STATUS_CARD_INSERTED, "Card inserted" }, + { SAP_CARD_STATUS_CARD_RECOVERED, "Card recovered" }, +}; + +/* Table 5.16: Possible values for ConnectionStatus */ +const struct value_string sap_conn_status_names[] = { + { SAP_CONN_STATUS_OK_READY, "OK, Server can fulfill requirements" }, + { SAP_CONN_STATUS_ERROR_CONN, "Error, Server unable to establish connection" }, + { SAP_CONN_STATUS_ERROR_MAX_MSG_SIZE, "Error, Server does not support maximum message size" }, + { SAP_CONN_STATUS_ERROR_SMALL_MSG_SIZE, "Error, maximum message size by Client is too small" }, + { SAP_CONN_STATUS_OK_CALL, "OK, ongoing call" }, +}; + +struct msgb *sap_msgb_alloc(uint8_t msg_id) +{ + struct sap_message *sap_msg; + struct msgb *msg; + + msg = msgb_alloc(GSM_SAP_LENGTH, "sap_msg"); + if (!msg) { + LOGP(DSAP, LOGL_ERROR, "Failed to allocate SAP message\n"); + return NULL; + } + + sap_msg = (struct sap_message *) msgb_put(msg, sizeof(*sap_msg)); + sap_msg->msg_id = msg_id; + + return msg; +} + +void sap_msgb_add_param(struct msgb *msg, + enum sap_param_type param_type, + uint16_t param_len, uint8_t *param_value) +{ + struct sap_message *sap_msg; + struct sap_param *param; + uint8_t padding; + uint8_t *buf; + + /* Update number of parameters */ + sap_msg = (struct sap_message *) msg->data; + sap_msg->num_params++; + + /* Allocate a new parameter */ + param = (struct sap_param *) msgb_put(msg, sizeof(*param)); + param->param_id = param_type; + param->reserved[0] = 0x00; + + /* Encode parameter value and length */ + param->length = htons(param_len); + buf = msgb_put(msg, param_len); + memcpy(buf, param_value, param_len); + + /* Optional padding */ + padding = 4 - (param_len % 4); + if (padding) { + buf = msgb_put(msg, padding); + memset(buf, 0x00, padding); + } +} + +struct sap_param *sap_get_param(const struct sap_message *sap_msg, + enum sap_param_type param_type, uint16_t *param_len) +{ + const uint8_t *ptr = sap_msg->payload; + struct sap_param *param = NULL; + uint16_t plen; + int i; + + /* We assume that message is parsed already, + * so we don't check for buffer overflows */ + for (i = 0; i < sap_msg->num_params; i++) { + /* Parse one parameter */ + param = (struct sap_param *) ptr; + plen = ntohs(param->length); + + /* Match against a given ID */ + if (param->param_id == param_type) { + if (param_len != NULL) + *param_len = plen; + return param; + } + + /* Shift pointer to the next parameter */ + ptr += sizeof(*param) + plen; + /* Optional padding */ + ptr += 4 - (plen % 4); + } + + return NULL; +} + +const struct sap_message *sap_msg_parse(const uint8_t *buf, size_t buf_len) +{ + const struct sap_message *sap_msg; + const uint8_t *ptr; + int i; + + /* Message header is mandatory */ + if (buf_len < sizeof(*sap_msg)) { + LOGP(DSAP, LOGL_ERROR, "Missing SAP message header\n"); + return NULL; + } + + sap_msg = (const struct sap_message *) buf; + buf_len -= sizeof(*sap_msg); + ptr = sap_msg->payload; + + LOGP(DSAP, LOGL_DEBUG, "SAP message '%s' has %u parameter(s)\n", + get_value_string(sap_msg_names, sap_msg->msg_id), + sap_msg->num_params); + + for (i = 0; i < sap_msg->num_params; i++) { + struct sap_param *param; + uint16_t param_len; + uint16_t offset; + + /* Prevent buffer overflow */ + if (buf_len < sizeof(*param)) + goto malformed; + + /* Parse one parameter */ + param = (struct sap_param *) ptr; + param_len = ntohs(param->length); + + LOGP(DSAP, LOGL_DEBUG, "SAP parameter '%s' (len=%u): %s\n", + get_value_string(sap_param_names, param->param_id), + param_len, osmo_hexdump(param->value, param_len)); + + /* Calculate relative offset */ + offset = sizeof(*param) + param_len; + offset += 4 - (param_len % 4); /* Optional padding */ + + /* Prevent buffer overflow */ + if (offset > buf_len) + goto malformed; + + buf_len -= offset; + ptr += offset; + } + + return sap_msg; + +malformed: + LOGP(DSAP, LOGL_ERROR, "Malformed SAP message " + "(parameter %i/%u)\n", i + 1, sap_msg->num_params); + return NULL; +} + +int sap_check_result_code(const struct sap_message *sap_msg) +{ + struct sap_param *param; + uint16_t param_len; + uint8_t res_code; + + param = sap_get_param(sap_msg, SAP_RESULT_CODE, ¶m_len); + if (!param || param_len != sizeof(res_code)) { + LOGP(DSAP, LOGL_ERROR, "Missing mandatory '%s' parameter\n", + get_value_string(sap_param_names, SAP_RESULT_CODE)); + return -EINVAL; + } + + res_code = param->value[0]; + if (res_code > ARRAY_SIZE(sap_result_names)) { + LOGP(DSAP, LOGL_ERROR, "Unknown SAP ResultCode=0x%02x\n", res_code); + return -EINVAL; + } + + LOGP(DSAP, LOGL_DEBUG, "SAP ResultCode is '%s'\n", + get_value_string(sap_result_names, res_code)); + + return res_code; +} diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c index 9d14cd3..a63ae2a 100644 --- a/src/host/layer23/src/common/sim.c +++ b/src/host/layer23/src/common/sim.c @@ -205,7 +205,7 @@ * it makes more sense to do it here then in L1CTL */ if (ms->subscr.sim_type == GSM_SIM_TYPE_SAP) { LOGP(DSIM, LOGL_INFO, "Using SAP backend\n"); - rc = osmosap_send_apdu(ms, data, length); + rc = sap_send_apdu(ms, data, length); } else { LOGP(DSIM, LOGL_INFO, "Using built-in SIM reader\n"); rc = l1ctl_tx_sim_req(ms, data, length); diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index 17c0c76..1f473a7 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -200,7 +200,11 @@ lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms); /* init SAP client before SIM card starts up */ - osmosap_init(ms); + sap_init(ms); + + /* TODO: remove SIM card on sap_entity.rel_cb() */ + ms->sap_entity.idle_cb = &subscr_sim_request; + ms->sap_entity.apdu_cb = &sim_apdu_resp; gsm_sim_init(ms); gsm48_cc_init(ms); diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/mobile/subscriber.c index b2be554..e8c0ae3 100644 --- a/src/host/layer23/src/mobile/subscriber.c +++ b/src/host/layer23/src/mobile/subscriber.c @@ -544,7 +544,7 @@ }; /* request file from SIM */ -static int subscr_sim_request(struct osmocom_ms *ms) +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]; @@ -1275,6 +1275,7 @@ gsm_subscr_exit(ms); gsm_subscr_init(ms); + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; subscr->sim_type = GSM_SIM_TYPE_SAP; sprintf(subscr->sim_name, "sap"); subscr->sim_valid = 1; @@ -1297,8 +1298,6 @@ return rc; } - - return 0; } /* Deattach sapcard */ -- To view, visit https://gerrit.osmocom.org/12441 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmocom-bb Gerrit-Branch: master Gerrit-MessageType: newchange Gerrit-Change-Id: I77bb108615bb2c94c441568f195b04e0a5421643 Gerrit-Change-Number: 12441 Gerrit-PatchSet: 1 Gerrit-Owner: Vadim Yanitskiy <axilirator at gmail.com> -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20181224/2381f59f/attachment.htm>