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
laforge/bankd-fsm-mistake
Harald Welte 5 years ago
parent e224912d4a
commit 77911b0091
  1. 3
      configure.ac
  2. 6
      include/osmocom/rspro/Makefile.am
  3. 59
      include/osmocom/rspro/rspro_client.h
  4. 20
      src/Makefile.am
  5. 118
      src/bankd.h
  6. 285
      src/bankd_main.c
  7. 100
      src/bankd_slotmap.c
  8. 4
      src/driver_pcsc.c
  9. 7
      src/main.c
  10. 296
      src/rspro_client.c
  11. 2
      src/rspro_util.c

@ -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…
Cancel
Save