Add initial remsim-bankd skeleton
This is not a complete program yet, but a rough initial skeleton with the key data structures in place, as well as the thread / locking model in place. Change-Id: I5ad5a1a4918b8eacdaeb7e709ff05dc056346752
This commit is contained in:
parent
e224912d4a
commit
77911b0091
|
@ -33,7 +33,10 @@ fi
|
|||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
PKG_CHECK_MODULES(OSMOCORE, libosmocore >= 0.11.0)
|
||||
PKG_CHECK_MODULES(OSMOGSM, libosmogsm >= 0.11.0)
|
||||
PKG_CHECK_MODULES(OSMOABIS, libosmoabis)
|
||||
PKG_CHECK_MODULES(ASN1C, libasn1c >= 0.9.30)
|
||||
PKG_CHECK_MODULES(PCSC, libpcsclite)
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
|
|
|
@ -36,3 +36,9 @@ noinst_HEADERS = \
|
|||
TpduFlags.h \
|
||||
TpduModemToCard.h \
|
||||
$(NULL)
|
||||
|
||||
|
||||
rspro_HEADERS = \
|
||||
rspro_client.h
|
||||
|
||||
rsprodir = $(includedir)/osmocom/rspro
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* Remote SIM Protocol client */
|
||||
|
||||
/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
/* a loss of RSPRO is considered quite serious, let's try to recover as quickly as
|
||||
* possible. Even one new connection attempt per second should be quite acceptable until the link is
|
||||
* re-established */
|
||||
#define OSMO_RSPRO_CLIENT_RECONNECT_INTERVAL 1
|
||||
#define OSMO_RSPRO_CLIENT_PING_INTERVAL 20
|
||||
|
||||
struct msgb;
|
||||
struct ipa_client_conn;
|
||||
struct osmo_rspro_client;
|
||||
|
||||
/* Expects message in msg->l2h */
|
||||
typedef int (*osmo_rspro_client_read_cb_t)(struct osmo_rspro_client *rsproc, struct msgb *msg);
|
||||
|
||||
struct osmo_rspro_client {
|
||||
const char *unit_name;
|
||||
|
||||
struct ipa_client_conn *link;
|
||||
osmo_rspro_client_read_cb_t read_cb;
|
||||
void *data;
|
||||
|
||||
struct osmo_timer_list ping_timer;
|
||||
struct osmo_timer_list connect_timer;
|
||||
int is_connected;
|
||||
int got_ipa_pong;
|
||||
};
|
||||
|
||||
struct osmo_rspro_client *osmo_rspro_client_create(void *talloc_ctx,
|
||||
const char *unit_name,
|
||||
const char *ip_addr,
|
||||
unsigned int tcp_port,
|
||||
osmo_rspro_client_read_cb_t read_cb);
|
||||
|
||||
void osmo_rspro_client_destroy(struct osmo_rspro_client *rsproc);
|
||||
int osmo_rspro_client_send(struct osmo_rspro_client *rsproc, struct msgb *msg);
|
||||
struct msgb *osmo_rspro_client_msgb_alloc(void);
|
||||
|
|
@ -1,10 +1,24 @@
|
|||
SUBDIRS = rspro
|
||||
|
||||
AM_CFLAGS = -Wall -I$(top_srcdir)/include -I$(top_builddir)/include \
|
||||
$(OSMOCORE_CFLAGS) $(ASN1C_CFLAGS)
|
||||
$(OSMOCORE_CFLAGS) $(OSMOGSM_CFLAGS) $(OSMOABIS_CFLAGS) \
|
||||
$(ASN1C_CFLAGS) $(PCSC_CFLAGS)
|
||||
|
||||
RSPRO_LIBVERSION=0:0:0
|
||||
lib_LTLIBRARIES = libosmo-rspro.la
|
||||
libosmo_rspro_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(RSPRO_LIBVERSION)
|
||||
libosmo_rspro_la_LIBADD = $(OSMOCORE_LIBS) $(ASN1C_LIBS) rspro/libosmo-asn1-rspro.la
|
||||
libosmo_rspro_la_SOURCES = rspro_util.c
|
||||
libosmo_rspro_la_LIBADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOABIS_LIBS) \
|
||||
$(ASN1C_LIBS) rspro/libosmo-asn1-rspro.la
|
||||
libosmo_rspro_la_SOURCES = rspro_util.c rspro_client.c
|
||||
|
||||
noinst_HEADERS = bankd.h internal.h
|
||||
|
||||
bin_PROGRAMS = pcsc_test remsim-bankd
|
||||
|
||||
pcsc_test_SOURCES = driver_core.c driver_pcsc.c main.c
|
||||
pcsc_test_LDADD = $(OSMOCORE_LIBS) \
|
||||
$(ASN1C_LIBS) $(PCSC_LIBS) libosmo-rspro.la
|
||||
|
||||
remsim_bankd_SOURCES = bankd_slotmap.c bankd_main.c
|
||||
remsim_bankd_LDADD = $(OSMOCORE_LIBS) \
|
||||
$(ASN1C_LIBS) $(PCSC_LIBS) libosmo-rspro.la
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <wintypes.h>
|
||||
#include <winscard.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct bankd;
|
||||
|
||||
struct bank_slot {
|
||||
uint16_t bank_id;
|
||||
uint16_t slot_nr;
|
||||
};
|
||||
|
||||
static inline bool bank_slot_equals(const struct bank_slot *a, const struct bank_slot *b)
|
||||
{
|
||||
if (a->bank_id == b->bank_id && a->slot_nr == b->slot_nr)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
struct client_slot {
|
||||
uint16_t client_id;
|
||||
uint16_t slot_nr;
|
||||
};
|
||||
|
||||
static inline bool client_slot_equals(const struct client_slot *a, const struct client_slot *b)
|
||||
{
|
||||
if (a->client_id == b->client_id && a->slot_nr == b->slot_nr)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* slot mappings are created / removed by the server */
|
||||
struct bankd_slot_mapping {
|
||||
/* global lits of bankd slot mappings */
|
||||
struct llist_head list;
|
||||
/* slot on bank side */
|
||||
struct bank_slot bank;
|
||||
/* slot on client side */
|
||||
struct client_slot client;
|
||||
};
|
||||
|
||||
/* thread-safe lookup of map by client:slot */
|
||||
struct bankd_slot_mapping *bankd_slotmap_by_client(struct bankd *bankd,
|
||||
const struct client_slot *client);
|
||||
|
||||
/* thread-safe lookup of map by bank:slot */
|
||||
struct bankd_slot_mapping *bankd_slotmap_by_bank(struct bankd *bankd, const struct bank_slot *bank);
|
||||
|
||||
/* thread-safe creating of a new bank<->client map */
|
||||
int bankd_slotmap_add(struct bankd *bankd, const struct bank_slot *bank,
|
||||
const struct client_slot *client);
|
||||
|
||||
/* thread-safe removal of a bank<->client map */
|
||||
void bankd_slotmap_del(struct bankd *bankd, struct bankd_slot_mapping *map);
|
||||
|
||||
|
||||
/* bankd worker instance; one per card/slot, includes thread */
|
||||
struct bankd_worker {
|
||||
/* global list of workers */
|
||||
struct llist_head list;
|
||||
/* back-pointer to bankd */
|
||||
struct bankd *bankd;
|
||||
|
||||
/* slot number we are representing */
|
||||
struct bank_slot slot;
|
||||
|
||||
/* thread of this worker. */
|
||||
pthread_t thread;
|
||||
|
||||
/* File descriptor of the TCP connection to the remsim-client (modem) */
|
||||
struct {
|
||||
int fd;
|
||||
struct sockaddr_storage peer_addr;
|
||||
socklen_t peer_addr_len;
|
||||
} client;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
union {
|
||||
struct {
|
||||
/* PC/SC context / application handle */
|
||||
SCARDCONTEXT hContext;
|
||||
/* PC/SC card handle */
|
||||
SCARDHANDLE hCard;
|
||||
} pcsc;
|
||||
};
|
||||
} reader;
|
||||
};
|
||||
|
||||
|
||||
/* global bank deamon */
|
||||
struct bankd {
|
||||
struct {
|
||||
uint16_t bank_id;
|
||||
} cfg;
|
||||
|
||||
/* TCP socket at which we are listening */
|
||||
int accept_fd;
|
||||
|
||||
/* list of slit mappings. only ever modified in main thread! */
|
||||
struct llist_head slot_mappings;
|
||||
pthread_rwlock_t slot_mappings_rwlock;
|
||||
|
||||
/* list of bankd_workers. accessed/modified by multiple threads; protected by mutex */
|
||||
struct llist_head workers;
|
||||
pthread_mutex_t workers_mutex;
|
||||
};
|
|
@ -0,0 +1,285 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <wintypes.h>
|
||||
#include <winscard.h>
|
||||
#include <pcsclite.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <asn1c/asn_application.h>
|
||||
#include <osmocom/rspro/RsproPDU.h>
|
||||
|
||||
#include "bankd.h"
|
||||
|
||||
__thread void *talloc_asn1_ctx;
|
||||
|
||||
static void *worker_main(void *arg);
|
||||
|
||||
/***********************************************************************
|
||||
* bankd core / main thread
|
||||
***********************************************************************/
|
||||
|
||||
static void bankd_init(struct bankd *bankd)
|
||||
{
|
||||
/* intialize members of 'bankd' */
|
||||
INIT_LLIST_HEAD(&bankd->slot_mappings);
|
||||
pthread_rwlock_init(&bankd->slot_mappings_rwlock, NULL);
|
||||
INIT_LLIST_HEAD(&bankd->workers);
|
||||
pthread_mutex_init(&bankd->workers_mutex, NULL);
|
||||
}
|
||||
|
||||
/* create + start a new bankd_worker thread */
|
||||
static struct bankd_worker *bankd_create_worker(struct bankd *bankd)
|
||||
{
|
||||
struct bankd_worker *worker;
|
||||
int rc;
|
||||
|
||||
worker = talloc_zero(bankd, struct bankd_worker);
|
||||
if (!worker)
|
||||
return NULL;
|
||||
|
||||
worker->bankd = bankd;
|
||||
|
||||
/* in the initial state, the worker has no client.fd, bank_slot or pcsc handle yet */
|
||||
|
||||
rc = pthread_create(&worker->thread, NULL, worker_main, worker);
|
||||
if (rc != 0) {
|
||||
talloc_free(worker);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&bankd->workers_mutex);
|
||||
llist_add_tail(&worker->list, &bankd->workers);
|
||||
pthread_mutex_unlock(&bankd->workers_mutex);
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
static bool terminate = false;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bankd *bankd = talloc_zero(NULL, struct bankd);
|
||||
int i, rc;
|
||||
|
||||
OSMO_ASSERT(bankd);
|
||||
bankd_init(bankd);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
struct bankd_worker *w;
|
||||
w = bankd_create_worker(bankd);
|
||||
if (!w)
|
||||
exit(21);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (terminate)
|
||||
break;
|
||||
}
|
||||
|
||||
talloc_free(bankd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* bankd worker thread
|
||||
***********************************************************************/
|
||||
|
||||
#define PCSC_ERROR(rv, text) \
|
||||
if (rv != SCARD_S_SUCCESS) { \
|
||||
fprintf(stderr, text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
|
||||
goto end; \
|
||||
} else { \
|
||||
printf(text ": OK\n\n"); \
|
||||
}
|
||||
|
||||
|
||||
static void worker_cleanup(void *arg)
|
||||
{
|
||||
struct bankd_worker *worker = (struct bankd_worker *) arg;
|
||||
struct bankd *bankd = worker->bankd;
|
||||
|
||||
/* FIXME: should we still do this? in the thread ?!? */
|
||||
pthread_mutex_lock(&bankd->workers_mutex);
|
||||
llist_del(&worker->list);
|
||||
talloc_free(worker); /* FIXME: is this safe? */
|
||||
pthread_mutex_unlock(&bankd->workers_mutex);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* function running inside a worker thread; doing some initialization */
|
||||
static void worker_init(struct bankd_worker *worker)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* push cleanup helper */
|
||||
pthread_cleanup_push(&worker_cleanup, worker);
|
||||
|
||||
/* The PC/SC context must be created inside the thread where we'll later use it */
|
||||
rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
|
||||
PCSC_ERROR(rc, "SCardEstablishContext")
|
||||
|
||||
rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, NULL);
|
||||
PCSC_ERROR(rc, "SCardConnect")
|
||||
|
||||
return;
|
||||
end:
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int blocking_ipa_read(int fd, uint8_t *buf, unsigned int buf_size)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
uint16_t len;
|
||||
int needed, rc;
|
||||
|
||||
if (buf_size < sizeof(*hh))
|
||||
return -1;
|
||||
|
||||
hh = (struct ipaccess_head *) buf;
|
||||
|
||||
/* 1) blocking read from the socket (IPA header) */
|
||||
rc = read(fd, buf, sizeof(*hh));
|
||||
if (rc < sizeof(*hh))
|
||||
return -2;
|
||||
|
||||
len = ntohs(hh->len);
|
||||
needed = len; //- sizeof(*hh);
|
||||
|
||||
/* 2) blocking read from the socket (payload) */
|
||||
rc = read(fd, buf+sizeof(*hh), needed);
|
||||
if (rc < needed)
|
||||
return -3;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* handle one incoming RSPRO message from a client inside a worker thread */
|
||||
static int worker_handle_rspro(struct bankd_worker *worker, const RsproPDU_t *pdu)
|
||||
{
|
||||
switch (pdu->msg.present) {
|
||||
case RsproPDUchoice_PR_connectClientReq:
|
||||
/* FIXME */
|
||||
break;
|
||||
case RsproPDUchoice_PR_tpduModemToCard:
|
||||
/* FIXME */
|
||||
break;
|
||||
case RsproPDUchoice_PR_clientSlotStatusInd:
|
||||
/* FIXME */
|
||||
break;
|
||||
default:
|
||||
return -100;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* body of the main transceive loop */
|
||||
static int worker_transceive_loop(struct bankd_worker *worker)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
struct ipaccess_head_ext *hh_ext;
|
||||
uint8_t buf[65536]; /* maximum length expressed in 16bit length field */
|
||||
asn_dec_rval_t rval;
|
||||
int data_len, rc;
|
||||
RsproPDU_t *pdu;
|
||||
|
||||
/* 1) blocking read of entire IPA message from the socket */
|
||||
rc = blocking_ipa_read(worker->client.fd, buf, sizeof(buf));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
data_len = rc;
|
||||
|
||||
hh = (struct ipaccess_head *) buf;
|
||||
if (hh->proto != IPAC_PROTO_OSMO)
|
||||
return -4;
|
||||
|
||||
hh_ext = (struct ipaccess_head_ext *) buf + sizeof(*hh);
|
||||
if (data_len < sizeof(*hh_ext))
|
||||
return -5;
|
||||
data_len -= sizeof(*hh_ext);
|
||||
if (hh_ext->proto != IPAC_PROTO_EXT_RSPRO)
|
||||
return -6;
|
||||
|
||||
/* 2) ASN1 BER decode of the message */
|
||||
rval = ber_decode(NULL, &asn_DEF_RsproPDU, (void **) &pdu, hh_ext->data, data_len);
|
||||
if (rval.code != RC_OK)
|
||||
return -7;
|
||||
|
||||
/* 3) handling of the message, possibly resulting in PCSC commands */
|
||||
rc = worker_handle_rspro(worker, pdu);
|
||||
ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* everything OK if we reach here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* worker thread main function */
|
||||
static void *worker_main(void *arg)
|
||||
{
|
||||
struct bankd_worker *worker = (struct bankd_worker *) arg;
|
||||
void *top_ctx;
|
||||
int rc;
|
||||
|
||||
/* not permitted in multithreaded environment */
|
||||
talloc_disable_null_tracking();
|
||||
top_ctx = talloc_named_const(NULL, 0, "top");
|
||||
talloc_asn1_ctx = talloc_named_const(top_ctx, 0, "asn1");
|
||||
|
||||
/* push cleanup helper */
|
||||
pthread_cleanup_push(&worker_cleanup, worker);
|
||||
|
||||
/* we continuously perform the same loop here, recycling the worker thread
|
||||
* once the client connection is gone or we have some trouble with the card/reader */
|
||||
while (1) {
|
||||
worker->client.peer_addr_len = sizeof(worker->client.peer_addr);
|
||||
|
||||
/* first wait for an incoming TCP connection */
|
||||
rc = accept(worker->bankd->accept_fd, (struct sockaddr *) &worker->client.peer_addr,
|
||||
&worker->client.peer_addr_len);
|
||||
if (rc < 0) {
|
||||
continue;
|
||||
}
|
||||
worker->client.fd = rc;
|
||||
|
||||
/* run the main worker transceive loop body until there was some error */
|
||||
while (1) {
|
||||
rc = worker_transceive_loop(worker);
|
||||
if (rc < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* clean-up: reset to sane state */
|
||||
if (worker->reader.pcsc.hCard) {
|
||||
SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
|
||||
worker->reader.pcsc.hCard = 0;
|
||||
}
|
||||
if (worker->reader.pcsc.hContext) {
|
||||
SCardReleaseContext(worker->reader.pcsc.hContext);
|
||||
worker->reader.pcsc.hContext = 0;
|
||||
}
|
||||
if (worker->client.fd >= 0)
|
||||
close(worker->client.fd);
|
||||
worker->client.fd = -1;
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(1);
|
||||
talloc_free(top_ctx);
|
||||
pthread_exit(NULL);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "bankd.h"
|
||||
|
||||
/* thread-safe lookup of map by client:slot */
|
||||
struct bankd_slot_mapping *bankd_slotmap_by_client(struct bankd *bankd, const struct client_slot *client)
|
||||
{
|
||||
struct bankd_slot_mapping *map;
|
||||
|
||||
pthread_rwlock_rdlock(&bankd->slot_mappings_rwlock);
|
||||
llist_for_each_entry(map, &bankd->slot_mappings, list) {
|
||||
if (client_slot_equals(&map->client, client)) {
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* thread-safe lookup of map by bank:slot */
|
||||
struct bankd_slot_mapping *bankd_slotmap_by_bank(struct bankd *bankd, const struct bank_slot *bank)
|
||||
{
|
||||
struct bankd_slot_mapping *map;
|
||||
|
||||
pthread_rwlock_rdlock(&bankd->slot_mappings_rwlock);
|
||||
llist_for_each_entry(map, &bankd->slot_mappings, list) {
|
||||
if (bank_slot_equals(&map->bank, bank)) {
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/* thread-safe creating of a new bank<->client map */
|
||||
int bankd_slotmap_add(struct bankd *bankd, const struct bank_slot *bank, const struct client_slot *client)
|
||||
{
|
||||
struct bankd_slot_mapping *map;
|
||||
|
||||
/* We assume a single thread (main thread) will ever update the mappings,
|
||||
* and hence we don't have any races by first grabbing + releasing the read
|
||||
* lock twice before grabbing the writelock below */
|
||||
|
||||
map = bankd_slotmap_by_bank(bankd, bank);
|
||||
if (map) {
|
||||
fprintf(stderr, "BANKD %u:%u already in use, cannot add new map\n",
|
||||
bank->bank_id, bank->slot_nr);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
map = bankd_slotmap_by_client(bankd, client);
|
||||
if (map) {
|
||||
fprintf(stderr, "CLIENT %u:%u already in use, cannot add new map\n",
|
||||
client->client_id, client->slot_nr);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* allocate new mapping and add to list of mappings */
|
||||
map = talloc_zero(bankd, struct bankd_slot_mapping);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
map->bank = *bank;
|
||||
map->client = *client;
|
||||
|
||||
pthread_rwlock_wrlock(&bankd->slot_mappings_rwlock);
|
||||
llist_add_tail(&map->list, &bankd->slot_mappings);
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
|
||||
printf("Added Slot Map B(%u:%u) <-> C(%u:%u)\n",
|
||||
map->client.client_id, map->client.slot_nr, map->bank.bank_id, map->bank.slot_nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* thread-safe removal of a bank<->client map */
|
||||
void bankd_slotmap_del(struct bankd *bankd, struct bankd_slot_mapping *map)
|
||||
{
|
||||
printf("Deleting Slot Map B(%u:%u) <-> C(%u:%u)\n",
|
||||
map->client.client_id, map->client.slot_nr, map->bank.bank_id, map->bank.slot_nr);
|
||||
|
||||
pthread_rwlock_wrlock(&bankd->slot_mappings_rwlock);
|
||||
llist_del(&map->list);
|
||||
pthread_rwlock_unlock(&bankd->slot_mappings_rwlock);
|
||||
|
||||
talloc_free(map);
|
||||
}
|
|
@ -83,6 +83,7 @@ end:
|
|||
|
||||
static int pcsc_reader_open_slot(struct card_reader_slot *slot)
|
||||
{
|
||||
#if 0
|
||||
struct osim_card_hdl *card;
|
||||
LONG rc;
|
||||
|
||||
|
@ -101,7 +102,8 @@ static int pcsc_reader_open_slot(struct card_reader_slot *slot)
|
|||
rh->card = card;
|
||||
|
||||
end:
|
||||
return NULL;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,12 @@
|
|||
|
||||
#include "internal.h"
|
||||
|
||||
static void *g_ctx;
|
||||
__thread void *talloc_asn1_ctx;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
card_readers_probe(NULL);
|
||||
g_ctx = talloc_named_const(NULL, 0, "main");
|
||||
talloc_asn1_ctx = talloc_named_const(g_ctx, 0, "asn1_context");
|
||||
card_readers_probe(g_ctx);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/* Generic Subscriber Update Protocol client */
|
||||
|
||||
/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
|
||||
* 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/rspro/rspro_client.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static void start_test_procedure(struct osmo_rspro_client *rsproc);
|
||||
|
||||
static void rspro_client_send_ping(struct osmo_rspro_client *rsproc)
|
||||
{
|
||||
struct msgb *msg = osmo_rspro_client_msgb_alloc();
|
||||
|
||||
msg->l2h = msgb_put(msg, 1);
|
||||
msg->l2h[0] = IPAC_MSGT_PING;
|
||||
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
|
||||
ipa_client_conn_send(rsproc->link, msg);
|
||||
}
|
||||
|
||||
static int rspro_client_connect(struct osmo_rspro_client *rsproc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (rsproc->is_connected)
|
||||
return 0;
|
||||
|
||||
if (osmo_timer_pending(&rsproc->connect_timer)) {
|
||||
LOGP(DLRSPRO, LOGL_DEBUG,
|
||||
"RSPRO connect: connect timer already running\n");
|
||||
osmo_timer_del(&rsproc->connect_timer);
|
||||
}
|
||||
|
||||
if (osmo_timer_pending(&rsproc->ping_timer)) {
|
||||
LOGP(DLRSPRO, LOGL_DEBUG,
|
||||
"RSPRO connect: ping timer already running\n");
|
||||
osmo_timer_del(&rsproc->ping_timer);
|
||||
}
|
||||
|
||||
if (ipa_client_conn_clear_queue(rsproc->link) > 0)
|
||||
LOGP(DLRSPRO, LOGL_DEBUG, "RSPRO connect: discarded stored messages\n");
|
||||
|
||||
rc = ipa_client_conn_open(rsproc->link);
|
||||
|
||||
if (rc >= 0) {
|
||||
LOGP(DLRSPRO, LOGL_NOTICE, "RSPRO connecting to %s:%d\n",
|
||||
rsproc->link->addr, rsproc->link->port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGP(DLRSPRO, LOGL_ERROR, "RSPRO failed to connect to %s:%d: %s\n",
|
||||
rsproc->link->addr, rsproc->link->port, strerror(-rc));
|
||||
|
||||
if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
|
||||
rc == -EINVAL)
|
||||
return rc;
|
||||
|
||||
osmo_timer_schedule(&rsproc->connect_timer,
|
||||
OSMO_RSPRO_CLIENT_RECONNECT_INTERVAL, 0);
|
||||
|
||||
LOGP(DLRSPRO, LOGL_INFO, "Scheduled timer to retry RSPRO connect to %s:%d\n",
|
||||
rsproc->link->addr, rsproc->link->port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void connect_timer_cb(void *rsproc_)
|
||||
{
|
||||
struct osmo_rspro_client *rsproc = rsproc_;
|
||||
|
||||
if (rsproc->is_connected)
|
||||
return;
|
||||
|
||||
rspro_client_connect(rsproc);
|
||||
}
|
||||
|
||||
static void client_send(struct osmo_rspro_client *rsproc, int proto_ext,
|
||||
struct msgb *msg_tx)
|
||||
{
|
||||
ipa_prepend_header_ext(msg_tx, proto_ext);
|
||||
ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
|
||||
ipa_client_conn_send(rsproc->link, msg_tx);
|
||||
/* msg_tx is now queued and will be freed. */
|
||||
}
|
||||
|
||||
static void rspro_client_updown_cb(struct ipa_client_conn *link, int up)
|
||||
{
|
||||
struct osmo_rspro_client *rsproc = link->data;
|
||||
|
||||
LOGP(DLRSPRO, LOGL_INFO, "RSPRO link to %s:%d %s\n",
|
||||
link->addr, link->port, up ? "UP" : "DOWN");
|
||||
|
||||
rsproc->is_connected = up;
|
||||
|
||||
if (up) {
|
||||
start_test_procedure(rsproc);
|
||||
osmo_timer_del(&rsproc->connect_timer);
|
||||
} else {
|
||||
osmo_timer_del(&rsproc->ping_timer);
|
||||
|
||||
osmo_timer_schedule(&rsproc->connect_timer,
|
||||
OSMO_RSPRO_CLIENT_RECONNECT_INTERVAL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int rspro_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
|
||||
struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
|
||||
struct osmo_rspro_client *rsproc = (struct osmo_rspro_client *)link->data;
|
||||
int rc;
|
||||
struct ipaccess_unit ipa_dev = {
|
||||
/* see rspro_client_create() on const vs non-const */
|
||||
.unit_name = (char*)rsproc->unit_name,
|
||||
};
|
||||
|
||||
OSMO_ASSERT(ipa_dev.unit_name);
|
||||
|
||||
msg->l2h = &hh->data[0];
|
||||
|
||||
rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DLRSPRO, LOGL_NOTICE,
|
||||
"RSPRO received an invalid IPA/CCM message from %s:%d\n",
|
||||
link->addr, link->port);
|
||||
/* Link has been closed */
|
||||
rsproc->is_connected = 0;
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == 1) {
|
||||
uint8_t msg_type = *(msg->l2h);
|
||||
/* CCM message */
|
||||
if (msg_type == IPAC_MSGT_PONG) {
|
||||
LOGP(DLRSPRO, LOGL_DEBUG, "RSPRO receiving PONG\n");
|
||||
rsproc->got_ipa_pong = 1;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hh->proto != IPAC_PROTO_OSMO)
|
||||
goto invalid;
|
||||
|
||||
if (!he || msgb_l2len(msg) < sizeof(*he))
|
||||
goto invalid;
|
||||
|
||||
msg->l2h = &he->data[0];
|
||||
|
||||
if (he->proto == IPAC_PROTO_EXT_RSPRO) {
|
||||
OSMO_ASSERT(rsproc->read_cb != NULL);
|
||||
rsproc->read_cb(rsproc, msg);
|
||||
/* expecting read_cb() to free msg */
|
||||
} else
|
||||
goto invalid;
|
||||
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
LOGP(DLRSPRO, LOGL_NOTICE,
|
||||
"RSPRO received an invalid IPA message from %s:%d, size = %d\n",
|
||||
link->addr, link->port, msgb_length(msg));
|
||||
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ping_timer_cb(void *rsproc_)
|
||||
{
|
||||
struct osmo_rspro_client *rsproc = rsproc_;
|
||||
|
||||
LOGP(DLRSPRO, LOGL_INFO, "RSPRO ping callback (%s, %s PONG)\n",
|
||||
rsproc->is_connected ? "connected" : "not connected",
|
||||
rsproc->got_ipa_pong ? "got" : "didn't get");
|
||||
|
||||
if (rsproc->got_ipa_pong) {
|
||||
start_test_procedure(rsproc);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DLRSPRO, LOGL_NOTICE, "RSPRO ping timed out, reconnecting\n");
|
||||
ipa_client_conn_close(rsproc->link);
|
||||
rsproc->is_connected = 0;
|
||||
|
||||
rspro_client_connect(rsproc);
|
||||
}
|
||||
|
||||
static void start_test_procedure(struct osmo_rspro_client *rsproc)
|
||||
{
|
||||
osmo_timer_setup(&rsproc->ping_timer, ping_timer_cb, rsproc);
|
||||
|
||||
rsproc->got_ipa_pong = 0;
|
||||
osmo_timer_schedule(&rsproc->ping_timer, OSMO_RSPRO_CLIENT_PING_INTERVAL, 0);
|
||||
LOGP(DLRSPRO, LOGL_DEBUG, "RSPRO sending PING\n");
|
||||
rspro_client_send_ping(rsproc);
|
||||
}
|
||||
|
||||
struct osmo_rspro_client *osmo_rspro_client_create(void *talloc_ctx,
|
||||
const char *unit_name,
|
||||
const char *ip_addr,
|
||||
unsigned int tcp_port,
|
||||
osmo_rspro_client_read_cb_t read_cb)
|
||||
{
|
||||
struct osmo_rspro_client *rsproc;
|
||||
int rc;
|
||||
|
||||
rsproc = talloc_zero(talloc_ctx, struct osmo_rspro_client);
|
||||
OSMO_ASSERT(rsproc);
|
||||
|
||||
/* struct ipaccess_unit has a non-const unit_name, so let's copy to be
|
||||
* able to have a non-const unit_name here as well. To not taint the
|
||||
* public rspro_client API, let's store it in a const char* anyway. */
|
||||
rsproc->unit_name = talloc_strdup(rsproc, unit_name);
|
||||
OSMO_ASSERT(rsproc->unit_name);
|
||||
|
||||
rsproc->link = ipa_client_conn_create(rsproc,
|
||||
/* no e1inp */ NULL,
|
||||
0,
|
||||
ip_addr, tcp_port,
|
||||
rspro_client_updown_cb,
|
||||
rspro_client_read_cb,
|
||||
/* default write_cb */ NULL,
|
||||
rsproc);
|
||||
if (!rsproc->link)
|
||||
goto failed;
|
||||
|
||||
osmo_timer_setup(&rsproc->connect_timer, connect_timer_cb, rsproc);
|
||||
|
||||
rc = rspro_client_connect(rsproc);
|
||||
if (rc < 0)
|
||||
goto failed;
|
||||
|
||||
rsproc->read_cb = read_cb;
|
||||
|
||||
return rsproc;
|
||||
|
||||
failed:
|
||||
osmo_rspro_client_destroy(rsproc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void osmo_rspro_client_destroy(struct osmo_rspro_client *rsproc)
|
||||
{
|
||||
osmo_timer_del(&rsproc->connect_timer);
|
||||
osmo_timer_del(&rsproc->ping_timer);
|
||||
|
||||
if (rsproc->link) {
|
||||
ipa_client_conn_close(rsproc->link);
|
||||
ipa_client_conn_destroy(rsproc->link);
|
||||
rsproc->link = NULL;
|
||||
}
|
||||
talloc_free(rsproc);
|
||||
}
|
||||
|
||||
int osmo_rspro_client_send(struct osmo_rspro_client *rsproc, struct msgb *msg)
|
||||
{
|
||||
if (!rsproc || !rsproc->is_connected) {
|
||||
LOGP(DLRSPRO, LOGL_ERROR, "RSPRO not connected, unable to send %s\n", msgb_hexdump(msg));
|
||||
msgb_free(msg);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
client_send(rsproc, IPAC_PROTO_EXT_RSPRO, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msgb *osmo_rspro_client_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(4000, 64, __func__);
|
||||
}
|
|
@ -152,5 +152,3 @@ RsproPDU_t *rspro_gen_SetAtrReq(uint16_t client_id, uint16_t slot_nr, const uint
|
|||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue