child-sa: Handle refcount overflow for unique mark/if_id allocation gracefully

The refcount_t for allocating unique marks and interface IDs may overflow or
hit the special value for unique marks/if_ids, in the worst case not setting it
on CHILD_SAs that should have one.

As (potentially two) marks/if_ids are allocated only for newly created CHILD_SAs,
but not for rekeying, this not very likely. Still, if a setup uses
aggressive re-authentication and or re-creates CHILD_SAs every minute,
a gateway with 100'000 tunnels may hit the overflow within a month uptime.
This commit is contained in:
Martin Willi
2024-02-16 14:04:17 +01:00
committed by Tobias Brunner
parent dde40bcb9e
commit 1a740bf3f3
+41 -11
View File
@@ -150,30 +150,44 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark)
return TRUE;
}
/**
* Allocate a unique mark that is non-zero and not one of the special values.
*/
static uint32_t allocate_unique_mark()
{
static refcount_t unique_mark = 0;
uint32_t m;
m = ref_get_nonzero(&unique_mark);
while (MARK_IS_UNIQUE(m))
{
m = ref_get_nonzero(&unique_mark);
}
return m;
}
/*
* Described in header
*/
void allocate_unique_marks(uint32_t *in, uint32_t *out)
{
static refcount_t unique_mark = 0;
if (MARK_IS_UNIQUE(*in) || MARK_IS_UNIQUE(*out))
{
refcount_t mark = 0;
uint32_t mark = 0;
bool unique_dir = *in == MARK_UNIQUE_DIR ||
*out == MARK_UNIQUE_DIR;
if (!unique_dir)
{
mark = ref_get(&unique_mark);
mark = allocate_unique_mark();
}
if (MARK_IS_UNIQUE(*in))
{
*in = unique_dir ? ref_get(&unique_mark) : mark;
*in = unique_dir ? allocate_unique_mark() : mark;
}
if (MARK_IS_UNIQUE(*out))
{
*out = unique_dir ? ref_get(&unique_mark) : mark;
*out = unique_dir ? allocate_unique_mark() : mark;
}
}
}
@@ -219,30 +233,46 @@ bool if_id_from_string(const char *value, uint32_t *if_id)
return TRUE;
}
/**
* Allocate a unique interface ID that is non-zero and not one of the special
* values.
*/
static uint32_t allocate_unique_if_id()
{
static refcount_t unique_if_id = 0;
uint32_t if_id;
if_id = ref_get_nonzero(&unique_if_id);
while (IF_ID_IS_UNIQUE(if_id))
{
if_id = ref_get_nonzero(&unique_if_id);
}
return if_id;
}
/*
* Described in header
*/
void allocate_unique_if_ids(uint32_t *in, uint32_t *out)
{
static refcount_t unique_if_id = 0;
if (IF_ID_IS_UNIQUE(*in) || IF_ID_IS_UNIQUE(*out))
{
refcount_t if_id = 0;
uint32_t if_id = 0;
bool unique_dir = *in == IF_ID_UNIQUE_DIR ||
*out == IF_ID_UNIQUE_DIR;
if (!unique_dir)
{
if_id = ref_get(&unique_if_id);
if_id = allocate_unique_if_id();
}
if (IF_ID_IS_UNIQUE(*in))
{
*in = unique_dir ? ref_get(&unique_if_id) : if_id;
*in = unique_dir ? allocate_unique_if_id() : if_id;
}
if (IF_ID_IS_UNIQUE(*out))
{
*out = unique_dir ? ref_get(&unique_if_id) : if_id;
*out = unique_dir ? allocate_unique_if_id() : if_id;
}
}
}