osmo-epdg: add a UE object together with an in memory db

This commit is contained in:
Alexander Couzens 2024-02-16 22:41:05 +01:00
parent 2dfe87749f
commit 6f3e8f5ecd
6 changed files with 369 additions and 99 deletions

View File

@ -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

View File

@ -19,6 +19,7 @@
#include <daemon.h>
#include <plugins/plugin.h>
#include <collections/hashtable.h>
#include <threading/rwlock.h>
#include <unistd.h>
#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;

View File

@ -28,48 +28,37 @@
#include <bus/listeners/listener.h>
#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_ */

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Alexander Couzens <acouzens@sysmocom.de>
*
* 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 <errno.h>
#include <sa/ike_sa.h>
#include <threading/rwlock.h>
#include <utils/utils.h>
#include <utils/debug.h>
#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;
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Alexander Couzens <acouzens@sysmocom.de>
*
* 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 <stdint.h>
#include <networking/host.h>
/**
* @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_ */

View File

@ -24,38 +24,8 @@
#include <utils/chunk.h>
#include <utils/identification.h>
#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);