From 6f3e8f5ecd6ae08a8e55c042107def15ab0b0904 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Fri, 16 Feb 2024 22:41:05 +0100 Subject: [PATCH] osmo-epdg: add a UE object together with an in memory db --- src/libcharon/plugins/osmo_epdg/Makefile.am | 1 + .../plugins/osmo_epdg/osmo_epdg_db.c | 125 ++++++++----- .../plugins/osmo_epdg/osmo_epdg_db.h | 35 ++-- .../plugins/osmo_epdg/osmo_epdg_ue.c | 169 ++++++++++++++++++ .../plugins/osmo_epdg/osmo_epdg_ue.h | 108 +++++++++++ .../plugins/osmo_epdg/osmo_epdg_utils.h | 30 ---- 6 files changed, 369 insertions(+), 99 deletions(-) create mode 100644 src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.c create mode 100644 src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.h diff --git a/src/libcharon/plugins/osmo_epdg/Makefile.am b/src/libcharon/plugins/osmo_epdg/Makefile.am index 8ca5e00c6..0c204aacc 100644 --- a/src/libcharon/plugins/osmo_epdg/Makefile.am +++ b/src/libcharon/plugins/osmo_epdg/Makefile.am @@ -23,4 +23,5 @@ libstrongswan_osmo_epdg_la_SOURCES = \ gsup_client.h gsup_client.c \ ipa_client.h ipa_client.c \ osmo_epdg_utils.h osmo_epdg_utils.c \ + osmo_epdg_ue.h osmo_epdg_ue.c \ osmo_epdg_db.h osmo_epdg_db.c diff --git a/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.c b/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.c index dcbf68e1f..c8eaf88f5 100644 --- a/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.c +++ b/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "osmo_epdg_plugin.h" @@ -36,94 +37,126 @@ struct private_osmo_epdg_db_t { */ osmo_epdg_db_t public; - /** - * GSUP client - */ - osmo_epdg_gsup_client_t *gsup; - - /** - * subscriber hash by ID - */ - hashtable_t *subscribers; - /** * subscriber hash by imsi (how to handle multiple?) */ hashtable_t *subscribers_imsi; /** - * subscriber by ike_sa + * rwlock to lock access for changes */ - hashtable_t *subscribers_ike_sa_t; + rwlock_t *lock; }; -METHOD(osmo_epdg_db_t, create_subscriber_imsi, osmo_epdg_ue_t *, - private_osmo_epdg_db_t *this, ike_sa_t *ike_sa, - char *imsi) +METHOD(osmo_epdg_db_t, create_subscriber, osmo_epdg_ue_t *, + private_osmo_epdg_db_t *this, ike_sa_t *ike_sa) { - return NULL; + osmo_epdg_ue_t *ue; + char imsi[16] = {0}; + uint32_t unique = ike_sa->get_unique_id(ike_sa); + + if (get_imsi_ike(ike_sa, imsi, sizeof(imsi) - 1)) + { + return NULL; + } + + this->lock->write_lock(this->lock); + ue = this->subscribers_imsi->get(this->subscribers_imsi, imsi); + if (ue) + { + /* TODO: handle dups! */ + this->lock->unlock(this->lock); + return ue; + } + + ue = osmo_epdg_ue_create(unique, imsi); + if (!ue) + { + DBG1(DBG_NET, "epdg_db: failed to create UE!"); + this->lock->unlock(this->lock); + return NULL; + } + + this->subscribers_imsi->put(this->subscribers_imsi, ue->get_imsi(ue), ue); + + ue->get(ue); + this->lock->unlock(this->lock); + return ue; } -METHOD(osmo_epdg_db_t, get_subscriber_imsi, osmo_epdg_ue_t *, - private_osmo_epdg_db_t *this, char *imsi, int offset) +METHOD(osmo_epdg_db_t, get_subscriber, osmo_epdg_ue_t *, + private_osmo_epdg_db_t *this, char *imsi) { - return NULL; -} - -METHOD(osmo_epdg_db_t, get_subscriber_id, osmo_epdg_ue_t *, - private_osmo_epdg_db_t *this, uint32_t id) -{ - return NULL; + osmo_epdg_ue_t *ue; + this->lock->read_lock(this->lock); + ue = this->subscribers_imsi->get(this->subscribers_imsi, imsi); + if (ue) + { + ue->get(ue); + } + this->lock->unlock(this->lock); + return ue; } METHOD(osmo_epdg_db_t, get_subscriber_ike, osmo_epdg_ue_t *, - private_osmo_epdg_db_t *this, ike_sa_t *ike_sa) + private_osmo_epdg_db_t *this, ike_sa_t *ike_sa) { - return NULL; + char imsi[16] = {0}; + + if (get_imsi_ike(ike_sa, imsi, sizeof(imsi))) + { + return NULL; + } + + return this->public.get_subscriber(&this->public, imsi); } -METHOD(osmo_epdg_db_t, destroy_subscriber_id, void, - private_osmo_epdg_db_t *this, uint32_t id) +METHOD(osmo_epdg_db_t, remove_subscriber, void, + private_osmo_epdg_db_t *this, const char *imsi) { - return; + osmo_epdg_ue_t *ue; + + this->lock->write_lock(this->lock); + ue = this->subscribers_imsi->remove(this->subscribers_imsi, imsi); + this->lock->unlock(this->lock); + + if (ue) + { + ue->put(ue); + } } -METHOD(osmo_epdg_db_t, destroy_subscriber_ike, void, - private_osmo_epdg_db_t *this, ike_sa_t *ike_sa) +CALLBACK(destroy_ue, void, + osmo_epdg_ue_t *ue, const void *key) { - return; -} - -METHOD(osmo_epdg_db_t, destroy_subscriber, void, - private_osmo_epdg_db_t *this, osmo_epdg_ue_t *ue) -{ - return; + ue->put(ue); } METHOD(osmo_epdg_db_t, destroy, void, private_osmo_epdg_db_t *this) { + this->subscribers_imsi->destroy_function(this->subscribers_imsi, destroy_ue); + this->lock->destroy(this->lock); free(this); } /** * See header */ -osmo_epdg_db_t *osmo_epdg_db_create(osmo_epdg_gsup_client_t *gsup) +osmo_epdg_db_t *osmo_epdg_db_create() { private_osmo_epdg_db_t *this; INIT(this, .public = { - .create_subscriber = _create_subscriber_imsi, - .get_subscriber_id = _get_subscriber_id, - .get_subscriber_imsi = _get_subscriber_imsi, + .create_subscriber = _create_subscriber, + .get_subscriber = _get_subscriber, .get_subscriber_ike = _get_subscriber_ike, - .destroy_subscriber_ike = _destroy_subscriber_ike, - .destroy_subscriber_id = _destroy_subscriber_id, - .destroy_subscriber = _destroy_subscriber, + .remove_subscriber = _remove_subscriber, .destroy = _destroy, }, + .subscribers_imsi = hashtable_create(hashtable_hash_str, hashtable_equals_str, 128), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); return &this->public; diff --git a/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.h b/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.h index d77460601..eaff7b17d 100644 --- a/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.h +++ b/src/libcharon/plugins/osmo_epdg/osmo_epdg_db.h @@ -28,48 +28,37 @@ #include #include "gsup_client.h" +#include "osmo_epdg_ue.h" #include "osmo_epdg_utils.h" typedef struct osmo_epdg_db_t osmo_epdg_db_t; /** - * SIM listener implementation using a set of AKA functions. + * DB object to store state across different threads. */ struct osmo_epdg_db_t { /** - * Create new subscriber by imsi, before sending authentication + * Create new subscriber by imsi, before sending authentication. + * NULL or UE object. The UE object need to called put() when not used. */ - osmo_epdg_ue_t *(*create_subscriber)(osmo_epdg_db_t *this, ike_sa_t *ike_sa, char *imsi); + osmo_epdg_ue_t *(*create_subscriber)(osmo_epdg_db_t *this, ike_sa_t *ike_sa); /** * Get subscriber by imsi, there might be multiple UE by this IMSI + * NULL or UE object. The UE object need to called put() when not used. */ - osmo_epdg_ue_t *(*get_subscriber_imsi)(osmo_epdg_db_t *this, char *imsi, int offset); + osmo_epdg_ue_t *(*get_subscriber)(osmo_epdg_db_t *this, char *imsi); /** - * Get subscriber by ike + * Get subscriber by imsi via ike_sa, there might be multiple UE by this IMSI + * NULL or UE object. The UE object need to called put() when not used. */ osmo_epdg_ue_t *(*get_subscriber_ike)(osmo_epdg_db_t *this, ike_sa_t *ike_sa); /** - * Get subscriber by id + * Remove a subscriber from the db. */ - osmo_epdg_ue_t *(*get_subscriber_id)(osmo_epdg_db_t *this, uint32_t id); - - /** - * Destroy subscriber by imsi - */ - void (*destroy_subscriber_ike)(osmo_epdg_db_t *this, ike_sa_t *ike_sa); - - /** - * Destroy subscriber by imsi - */ - void (*destroy_subscriber_id)(osmo_epdg_db_t *this, uint32_t id); - - /** - * Destroy subscriber by object - */ - void (*destroy_subscriber)(osmo_epdg_db_t *this, osmo_epdg_ue_t *ue); + void (*remove_subscriber)(osmo_epdg_db_t *this, char *imsi); /** * Destroy a osmo_epdg_db_t. @@ -80,6 +69,6 @@ struct osmo_epdg_db_t { /** * Create a osmo_epdg_db instance. */ -osmo_epdg_db_t *osmo_epdg_db_create(osmo_epdg_gsup_client_t *gsup); +osmo_epdg_db_t *osmo_epdg_db_create(); #endif /* OSMO_EPDG_DB_H_ */ diff --git a/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.c b/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.c new file mode 100644 index 000000000..bf221c413 --- /dev/null +++ b/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH + * Author: Alexander Couzens + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include +#include +#include +#include + +#include "osmo_epdg_ue.h" + +typedef struct private_osmo_epdg_ue_t private_osmo_epdg_ue_t; + + +/** + * Private data of an osmo_epdg_ue_t object. + */ +struct private_osmo_epdg_ue_t { + /** + * Public osmo_epdg_ue_t interface. + */ + osmo_epdg_ue_t public; + + /** + * a unique id. + * Same as ike_sa_t->get_unique_id(). + */ + uint32_t id; + + /** + * IMSI encoded as character + */ + char *imsi; + + /** + * IP address of the client. Might become a llist_t in the future + */ + host_t *address; + + /** + * Refcount to track this object. + * It will call destroy() when refcount reaches 0 + */ + refcount_t refcount; + + /** + * rwlock to lock access for changes + */ + rwlock_t *lock; + + enum osmo_epdg_ue_state state; +}; + +METHOD(osmo_epdg_ue_t, get_imsi, const char *, + private_osmo_epdg_ue_t *this) +{ + return this->imsi; +} + +METHOD(osmo_epdg_ue_t, set_address, void, + private_osmo_epdg_ue_t *this, host_t *address) +{ + this->lock->write_lock(this->lock); + if (this->address) + { + this->address->destroy(this->address); + } + this->address = address->clone(address); + this->lock->unlock(this->lock); +} + +METHOD(osmo_epdg_ue_t, get_address, host_t *, + private_osmo_epdg_ue_t *this) +{ + host_t *address = NULL; + + this->lock->read_lock(this->lock); + if (this->address) + { + address = this->address->clone(this->address); + } + this->lock->unlock(this->lock); + + return address; +} + +METHOD(osmo_epdg_ue_t, set_state, void, + private_osmo_epdg_ue_t *this, enum osmo_epdg_ue_state state) +{ + this->lock->write_lock(this->lock); + /* TODO: implement a FSM. At least we can get debug information out of it. */ + this->state = state; + this->lock->unlock(this->lock); +} + +METHOD(osmo_epdg_ue_t, get_state, enum osmo_epdg_ue_state, + private_osmo_epdg_ue_t *this) +{ + enum osmo_epdg_ue_state state; + this->lock->read_lock(this->lock); + /* TODO: implement a FSM. At least we can get debug information out of it. */ + state = this->state; + this->lock->unlock(this->lock); + + return state; +} + +METHOD(osmo_epdg_ue_t, get, void, + private_osmo_epdg_ue_t *this) +{ + ref_get(&this->refcount); +} + +METHOD(osmo_epdg_ue_t, put, void, + private_osmo_epdg_ue_t *this) +{ + if (ref_put(&this->refcount)) + { + this->public.destroy(&this->public); + } +} + +METHOD(osmo_epdg_ue_t, destroy, void, + private_osmo_epdg_ue_t *this) +{ + this->lock->destroy(this->lock); + free(this); +} + +osmo_epdg_ue_t *osmo_epdg_ue_create(uint32_t id, char *imsi) +{ + private_osmo_epdg_ue_t *this; + + INIT(this, + .public = { + .get = _get, + .put = _put, + .get_imsi = _get_imsi, + .get_address = _get_address, + .set_address = _set_address, + .get_state = _get_state, + .set_state = _set_state, + .destroy = _destroy, + }, + .imsi = strdup(imsi), + .id = id, + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .state = UE_WAIT_LOCATION_UPDATE, + ); + ref_get(&this->refcount); + + return &this->public; +} diff --git a/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.h b/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.h new file mode 100644 index 000000000..350ef3895 --- /dev/null +++ b/src/libcharon/plugins/osmo_epdg/osmo_epdg_ue.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH + * Author: Alexander Couzens + * + * SPDX-License-Identifier: GPL-2.0+ + * + * 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. + * + */ + +#ifndef OSMO_EPDG_UE_H_ +#define OSMO_EPDG_UE_H_ + +#include +#include + +/** + * @defgroup osmo_epdg_ue osmo_epdg_ue + * @{ @ingroup osmo_epdg + */ + +typedef struct osmo_epdg_ue_t osmo_epdg_ue_t; + +enum osmo_epdg_ue_state { + /* Initial */ + UE_UNAUTHENTICATED, + /* Authenticated */ + UE_AUTHENTICATED, + /* Wait for GSUP Update Location Response */ + UE_WAIT_LOCATION_UPDATE, + /* When the LOCATION UPDATE went successful */ + UE_LOCATION_UPDATED, + /* Wait for GSUP Tunnel Response */ + UE_WAIT_TUNNEL, + /* Tunnel Succeeded */ + UE_TUNNEL_READY, + /* Everything ready, data can flow */ + UE_CONNECTED, + /* Notify the osmo-epdg about destruction, wait for an answer */ + UE_DISCONNECTING, + /* When the UE failed, but the IKE_SA hasn't been destroyed */ + UE_FAIL, + UE_DESTROYED, +}; + +/** + * UE object + */ +struct osmo_epdg_ue_t { + /** + * Get IMSI + */ + const char *(*get_imsi)(osmo_epdg_ue_t *this); + + /** + * Get address. Returns NULL or a cloned' host_t object + */ + host_t *(*get_address)(osmo_epdg_ue_t *this); + + /** + * Set address. It will internally clone the given object. + */ + void (*set_address)(osmo_epdg_ue_t *this, host_t *address); + + /** + * Get state + */ + enum osmo_epdg_ue_state (*get_state)(osmo_epdg_ue_t *this); + + /** + * Set state + */ + void (*set_state)(osmo_epdg_ue_t *this, enum osmo_epdg_ue_state state); + + + /** + * Increase the internal refcount. Use put() when done with the object + */ + void (*get)(osmo_epdg_ue_t *this); + + /** + * Decrease the internal refcount. + * When reaching zero, the object will be destroyed. + */ + void (*put)(osmo_epdg_ue_t *this); + + /** + * Destroy a osmo_epdg_db_t. Use get/put to track it. Don't use destroy(). + * TODO: maybe remove destroy() completely. + */ + void (*destroy)(osmo_epdg_ue_t *this); +}; + +/** + * Create a osmo_epdg_ue instance. + * A newly created object will come with refcount = 1. Use put() to destroy it. + */ +osmo_epdg_ue_t *osmo_epdg_ue_create(uint32_t id, char *imsi); + +#endif /* OSMO_EPDG_UE_H_ */ diff --git a/src/libcharon/plugins/osmo_epdg/osmo_epdg_utils.h b/src/libcharon/plugins/osmo_epdg/osmo_epdg_utils.h index 3a8ac3978..6977912af 100644 --- a/src/libcharon/plugins/osmo_epdg/osmo_epdg_utils.h +++ b/src/libcharon/plugins/osmo_epdg/osmo_epdg_utils.h @@ -24,38 +24,8 @@ #include #include - #define IPA_ALLOC_SIZE 1200 -enum ue_state { - /* Initial */ - UE_UNAUTHENTICATED, - /* Authenticated */ - UE_AUTHENTICATED, - /* Wait for GSUP Update Location Request */ - UE_WAIT_LOCATION_UPDATE, - /* Wait for GSUP Tunnel Request */ - UE_WAIT_TUNNEL, - /* Everything ready, data can flow */ - UE_CONNECTED, - /* Notify the osmo-epdg about destruction, wait for an answer */ - UE_DISCONNECTING, - UE_DESTROYED, -}; - -/* TODO: how to clean up/garbage collect */ -struct osmo_epdg_ue { - /* increasing uniq id */ - uint32_t id; - /* imsi should be uniq, need protected against fake UE */ - char *imsi; - enum ue_state state; - - /* TODO: missing strongswan session pointer */ - ike_sa_t *ike_sa; -}; -typedef struct osmo_epdg_ue osmo_epdg_ue_t; - struct msgb *chunk_to_msgb(chunk_t *chunk); int get_imsi(identification_t *id, char *imsi, size_t imsi_len); int get_imsi_ike(ike_sa_t *ike_sa, char *imsi, size_t imsi_len);