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; 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 * Described in header
*/ */
void allocate_unique_marks(uint32_t *in, uint32_t *out) 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)) if (MARK_IS_UNIQUE(*in) || MARK_IS_UNIQUE(*out))
{ {
refcount_t mark = 0; uint32_t mark = 0;
bool unique_dir = *in == MARK_UNIQUE_DIR || bool unique_dir = *in == MARK_UNIQUE_DIR ||
*out == MARK_UNIQUE_DIR; *out == MARK_UNIQUE_DIR;
if (!unique_dir) if (!unique_dir)
{ {
mark = ref_get(&unique_mark); mark = allocate_unique_mark();
} }
if (MARK_IS_UNIQUE(*in)) 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)) 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; 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 * Described in header
*/ */
void allocate_unique_if_ids(uint32_t *in, uint32_t *out) 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)) 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 || bool unique_dir = *in == IF_ID_UNIQUE_DIR ||
*out == IF_ID_UNIQUE_DIR; *out == IF_ID_UNIQUE_DIR;
if (!unique_dir) if (!unique_dir)
{ {
if_id = ref_get(&unique_if_id); if_id = allocate_unique_if_id();
} }
if (IF_ID_IS_UNIQUE(*in)) 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)) 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;
} }
} }
} }