diff options
Diffstat (limited to 'src/host/layer23/src/libosmosim/libosmosim.c')
-rw-r--r-- | src/host/layer23/src/libosmosim/libosmosim.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/host/layer23/src/libosmosim/libosmosim.c b/src/host/layer23/src/libosmosim/libosmosim.c new file mode 100644 index 00000000..e6f91646 --- /dev/null +++ b/src/host/layer23/src/libosmosim/libosmosim.c @@ -0,0 +1,296 @@ +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/core/talloc.h> +#include <l1ctl_proto.h> + +#define LIBOSMOSIM_DEBUG 0 + +#define libosmosim_dbg(fmt, ...) \ + do { if (LIBOSMOSIM_DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0) + +#define _GNU_SOURCE +#include <unistd.h> + +#include "libosmosim.h" + +// layer 2 connection variables +static char *layer2_socket_path = "/tmp/osmocom_l2"; +static void *l23_ctx = NULL; +static struct osmocom_ms *ms = NULL; + +// locking variables +struct log_target *stderr_target; +static char lckfile[] = "/tmp/.osmosim_lock"; +static FILE *flckfile; + +// fake stuff that is needed in l1ctl.c and is never used in our code +struct gsmtap_inst *gsmtap_inst = NULL; +const uint16_t gsm610_bitorder[] = {}; + +// sim present in phone identifier +static int sim_present = 0; + +// global APDU buffer +static uint8_t apdu_resp[255]; +static size_t apdu_length = 0; + +/* + * helper locking functions to ensure only 1 operations is performed by a Osmocom BB phone at the same time + */ + +static int in_shutdown = 0; +int i = 0; +void unlock() { + int j = i++; + libosmosim_dbg("unlock() called, tring to unlock %d\n", j); + funlockfile(flckfile); + libosmosim_dbg("unlocked %d, unlock() exiting\n", j); +} + +void dolock() { + int j = i++; + libosmosim_dbg("dolock() called, tring to lock %d\n", j); + flockfile(flckfile); + libosmosim_dbg("locked %d, dolock() exiting\n", j); +} + +void lock() { + libosmosim_dbg("%s\n", "lock() called"); + if (in_shutdown) { + libosmosim_dbg("%s\n", "lock() waiting as in_shutdown is 1"); + sleep(1337); // just wait forever as we're shutdowning already, no action is allowed. + } + dolock(); + libosmosim_dbg("%s\n", "lock() exiting"); +} + +/* + * callback functions + */ + +/* this function is called-back when L1CTL_SIM_ATR message is received (when SIM needs to return ATR) */ +int osmosim_sim_up_resp(struct osmocom_ms *ms, struct msgb *msg) { + libosmosim_dbg("%s\n", "sim_up_resp() called"); + uint16_t len = msg->len - sizeof(struct l1ctl_hdr); + if (len > 0) { + sim_present = 1; + } + + uint8_t *data = msg->data; + int length = msg->len; + LOGP(DSIM, LOGL_INFO, "SIM ATR (len=%d) : %s\n", length, osmo_hexdump(data, length)); + apdu_length = length; + memcpy(apdu_resp, data, length); + unlock(); // unlock a lock performed by osmo_sim_powerup() and osmosim_reset() + libosmosim_dbg("%s\n", "sim_up_resp() exiting"); + return len; +} + +/* this function is called-back when L1CTL_SIM_CONF message is received (when SIM needs to return a response to APDU) */ +int osmosim_sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg) { + libosmosim_dbg("%s\n", "sim_apdu_resp() called"); + uint8_t *data = msg->data; + int length = msg->len; + uint8_t sw1, sw2; + /* process status */ + if (length < 2) { + msgb_free(msg); + return 0; + } + sw1 = data[length - 2]; + sw2 = data[length - 1]; + LOGP(DSIM, LOGL_INFO, "received APDU (len=%d sw1=0x%02x sw2=0x%02x)\n", length, sw1, sw2); + + apdu_length = length; + memcpy(apdu_resp, data, length); + + unlock(); + libosmosim_dbg("%s\n", "sim_apdu_resp() exiting"); + return 0; +} + +/* + * osmosim_* functions that communicate over layer 1 + */ + +/* initialize stderr_target and set default logging to all known debug symbols + LOGL_FATAL */ +void osmosim_log_init() { + libosmosim_dbg("%s\n", "osmosim_log_init() called"); + log_init(&log_info, NULL); + stderr_target = NULL; + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + const char *debug_default = "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DL1C"; + log_parse_category_mask(stderr_target, debug_default); + log_set_log_level(stderr_target, LOGL_FATAL); + libosmosim_dbg("%s\n", "osmosim_log_init() exiting"); +} + +/* open layer2 socket and prepare ms struct for communication with Osmocom BB phone */ +int osmosim_init() +{ + libosmosim_dbg("%s\n", "osmosim_init() called"); + int rc; + + flckfile = fopen(lckfile, "w"); // open a lock file + if (!stderr_target) { + osmosim_log_init(); + } + + l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + ms = talloc_zero(l23_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + return 0; + } + + sprintf(ms->name, "1"); + + rc = layer2_open(ms, layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return 0; + } + + libosmosim_dbg("%s\n", "osmosim_init() exiting"); + return 1; +} + +/* sets a loglevel if log_level != 0 as zero means "don't set loglevel only return the current one" */ +int osmosim_loglevel(int log_level) { + libosmosim_dbg("%s%s%s\n", "osmosim_log_level() called (log_level = ", log_level_str(log_level), ")"); + if (!stderr_target) { + + osmosim_log_init(); + } + + if (log_level) { + log_set_log_level(stderr_target, log_level); + } + + libosmosim_dbg("%s\n", "osmosim_log_level() exiting"); + return stderr_target->loglevel; +} + +/* power up the sim (actual voltage) */ +int osmosim_powerup() { + libosmosim_dbg("%s\n", "osmosim_powerup() called"); + in_shutdown = 0; + lock(); + l1ctl_tx_sim_powerup(ms); + osmo_select_main(0); // wait for write to the socket + osmo_select_main(0); // wait for read from the socket + + libosmosim_dbg("%s\n", "osmosim_powerup() exiting"); + return sim_present; // 1 if sim is in the phone, 0 if sim is absent +} + +/* power down the sim (actual voltage) */ +void osmosim_powerdown() { + libosmosim_dbg("%s\n", "osmosim_powerdown() called"); + in_shutdown = 1; + dolock(); + l1ctl_tx_sim_powerdown(ms); + osmo_select_main(0); // wait for write to the socket (no response required) + unlock(); + libosmosim_dbg("%s\n", "osmosim_powerdown() exiting"); +} + +/* trigger SIM reset (actuall reset pin on the sim) */ +int osmosim_reset() { + libosmosim_dbg("%s\n", "osmosim_reset() called"); + lock(); + l1ctl_tx_sim_reset(ms); + osmo_select_main(0); // wait for write to the socket + osmo_select_main(0); // wait for read from the socket + + libosmosim_dbg("%s\n", "osmosim_reset() exiting"); + return sim_present; +} + +/* transmit a APDU to the SIM and wait for response to arrive (sim_apdu_resp), then read it out of a global buffer) */ +int osmosim_transmit(char* data, unsigned int len, char** out) +{ + libosmosim_dbg("%s\n", "osmosim_transmit() called"); + lock(); + LOGP(DSIM, LOGL_INFO, "sending APDU (class 0x%02x, ins 0x%02x)\n", data[0], data[1]); + l1ctl_tx_sim_req(ms, (uint8_t*)data, len); + osmo_select_main(0); + osmo_select_main(0); + + if (out) { + *out = (char*)&apdu_resp; + } + libosmosim_dbg("%s\n", "osmosim_transmit() exiting"); + return apdu_length; +} + +/* perform lock without checking for shutdown as this is initiated during shutdown */ +void osmosim_exit() { + libosmosim_dbg("%s\n", "osmosim_exit() called"); + dolock(); + layer2_close(ms); + talloc_free(ms); + talloc_free(l23_ctx); + log_target_destroy(stderr_target); + stderr_target = NULL; + unlock(); + fclose(flckfile); + libosmosim_dbg("%s\n", "osmosim_exit() exiting"); +} + +/* + * Java highlevel API wrappers for functions above + */ + +JNIEXPORT jboolean JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_init (JNIEnv *env, jobject obj) { + return osmosim_init(); +} + +JNIEXPORT jint JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_loglevel (JNIEnv *env, jobject obj, jint log_level) { + return osmosim_loglevel(log_level); +} + +JNIEXPORT jbyteArray JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simPowerup (JNIEnv *env, jobject obj) { + + osmosim_powerup(); + + jbyteArray result; + result = (*env)->NewByteArray(env, apdu_length); + (*env)->SetByteArrayRegion(env, result, 0, apdu_length, (jbyte*)apdu_resp); + + return result; +} + +JNIEXPORT void JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simPowerdown (JNIEnv *env, jobject obj) { + osmosim_powerdown(); +} + +JNIEXPORT jboolean JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_simReset (JNIEnv *env, jobject obj) { + return osmosim_reset(); +} + +JNIEXPORT jbyteArray JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_transmit (JNIEnv *env, jobject obj, jbyteArray data) { + + char req[255]; + int len; + char *resp; + + len = (*env)->GetArrayLength(env, data); + (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)req); + + len = osmosim_transmit(req, len, &resp); + + jbyteArray result; + result = (*env)->NewByteArray(env, len); + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)resp); + + return result; +} + +JNIEXPORT void JNICALL Java_de_srlabs_simlib_osmocardprovider_OsmoJNI_exit (JNIEnv *env, jobject obj) { + osmosim_exit(); +} |