summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/agentx.c3
-rw-r--r--lib/id_alloc.c406
-rw-r--r--lib/id_alloc.h90
-rw-r--r--lib/lib_errors.h2
-rw-r--r--lib/subdir.am2
-rw-r--r--lib/vrf.c7
-rw-r--r--lib/vrf.h1
-rw-r--r--lib/zclient.c6
-rw-r--r--lib/zclient.h17
9 files changed, 514 insertions, 20 deletions
diff --git a/lib/agentx.c b/lib/agentx.c
index 55bb9e99ec..40cac722ae 100644
--- a/lib/agentx.c
+++ b/lib/agentx.c
@@ -188,9 +188,8 @@ DEFUN (agentx_enable,
events = list_new();
agentx_events_update();
agentx_enabled = 1;
- return CMD_SUCCESS;
}
- vty_out(vty, "SNMP AgentX already enabled\n");
+
return CMD_SUCCESS;
}
diff --git a/lib/id_alloc.c b/lib/id_alloc.c
new file mode 100644
index 0000000000..222ba651b4
--- /dev/null
+++ b/lib/id_alloc.c
@@ -0,0 +1,406 @@
+/*
+ * FRR ID Number Allocator
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "id_alloc.h"
+
+#include "log.h"
+#include "lib_errors.h"
+#include "memory.h"
+
+#include <inttypes.h>
+
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_ALLOCATOR, "ID Number Allocator")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_ALLOCATOR_NAME, "ID Number Allocator Name")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_DIRECTORY, "ID Number Allocator Directory")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_SUBDIRECTORY,
+ "ID Number Allocator Subdirectory")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_PAGE, "ID Number Allocator Page")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_POOL, "ID Number temporary holding pool entry")
+
+#if UINT_MAX >= UINT32_MAX
+#define FFS32(x) ffs(x)
+#else
+/* ints less than 32 bits? Yikes. */
+#define FFS32(x) ffsl(x)
+#endif
+
+#define DIR_MASK ((1<<IDALLOC_DIR_BITS)-1)
+#define SUBDIR_MASK ((1<<IDALLOC_SUBDIR_BITS)-1)
+#define FRR_ID_PAGE_MASK ((1<<IDALLOC_PAGE_BITS)-1)
+#define WORD_MASK ((1<<IDALLOC_WORD_BITS)-1)
+#define OFFSET_MASK ((1<<IDALLOC_OFFSET_BITS)-1)
+
+#define DIR_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS + \
+ IDALLOC_PAGE_BITS + IDALLOC_SUBDIR_BITS)
+#define SUBDIR_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS + \
+ IDALLOC_PAGE_BITS)
+#define FRR_ID_PAGE_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS)
+#define WORD_SHIFT (IDALLOC_OFFSET_BITS)
+#define OFFSET_SHIFT (0)
+
+#define ID_DIR(id) ((id >> DIR_SHIFT) & DIR_MASK)
+#define ID_SUBDIR(id) ((id >> SUBDIR_SHIFT) & SUBDIR_MASK)
+#define ID_PAGE(id) ((id >> FRR_ID_PAGE_SHIFT) & FRR_ID_PAGE_MASK)
+#define ID_WORD(id) ((id >> WORD_SHIFT) & WORD_MASK)
+#define ID_OFFSET(id) ((id >> OFFSET_SHIFT) & OFFSET_MASK)
+
+/*
+ * Find the page that an ID number belongs to in an allocator.
+ * Optionally create the page if it doesn't exist.
+ */
+static struct id_alloc_page *find_or_create_page(struct id_alloc *alloc,
+ uint32_t id, int create)
+{
+ struct id_alloc_dir *dir = NULL;
+ struct id_alloc_subdir *subdir = NULL;
+ struct id_alloc_page *page = NULL;
+
+ dir = alloc->sublevels[ID_DIR(id)];
+ if (dir == NULL) {
+ if (create) {
+ dir = XCALLOC(MTYPE_IDALLOC_DIRECTORY, sizeof(*dir));
+ alloc->sublevels[ID_DIR(id)] = dir;
+ } else {
+ return NULL;
+ }
+ }
+
+ subdir = dir->sublevels[ID_SUBDIR(id)];
+ if (subdir == NULL) {
+ if (create) {
+ subdir = XCALLOC(MTYPE_IDALLOC_SUBDIRECTORY,
+ sizeof(*subdir));
+ dir->sublevels[ID_SUBDIR(id)] = subdir;
+ } else {
+ return NULL;
+ }
+ }
+
+ page = subdir->sublevels[ID_PAGE(id)];
+ if (page == NULL && create) {
+ page = XCALLOC(MTYPE_IDALLOC_PAGE, sizeof(*page));
+ page->base_value = id;
+ subdir->sublevels[ID_PAGE(id)] = page;
+
+ alloc->capacity += 1 << FRR_ID_PAGE_SHIFT;
+ page->next_has_free = alloc->has_free;
+ alloc->has_free = page;
+ } else if (page != NULL && create) {
+ flog_err(
+ EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s attempt to re-create page at %" PRIu32,
+ alloc->name, id);
+ }
+
+ return page;
+}
+
+/*
+ * Return an ID number back to the allocator.
+ * While this ID can be re-assigned through idalloc_allocate, the underlying
+ * memory will not be freed. If this is the first free ID in the page, the page
+ * will be added to the allocator's list of pages with free IDs.
+ */
+void idalloc_free(struct id_alloc *alloc, uint32_t id)
+{
+ struct id_alloc_page *page = NULL;
+
+ int word, offset;
+ uint32_t old_word, old_word_mask;
+
+ page = find_or_create_page(alloc, id, 0);
+ if (!page) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s cannot free #%" PRIu32
+ ". ID Block does not exist.",
+ alloc->name, id);
+ return;
+ }
+
+ word = ID_WORD(id);
+ offset = ID_OFFSET(id);
+
+ if ((page->allocated_mask[word] & (1 << offset)) == 0) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s cannot free #%" PRIu32
+ ". ID was not allocated at the time of free.",
+ alloc->name, id);
+ return;
+ }
+
+ old_word = page->allocated_mask[word];
+ page->allocated_mask[word] &= ~(((uint32_t)1) << offset);
+ alloc->allocated -= 1;
+
+ if (old_word == UINT32_MAX) {
+ /* first bit in this block of 32 to be freed.*/
+
+ old_word_mask = page->full_word_mask;
+ page->full_word_mask &= ~(((uint32_t)1) << word);
+
+ if (old_word_mask == UINT32_MAX) {
+ /* first bit in page freed, add this to the allocator's
+ * list of pages with free space
+ */
+ page->next_has_free = alloc->has_free;
+ alloc->has_free = page;
+ }
+ }
+}
+
+/*
+ * Add a allocation page to the end of the allocator's current range.
+ * Returns null if the allocator has had all possible pages allocated already.
+ */
+static struct id_alloc_page *create_next_page(struct id_alloc *alloc)
+{
+ if (alloc->capacity == 0 && alloc->sublevels[0])
+ return NULL; /* All IDs allocated and the capacity looped. */
+
+ return find_or_create_page(alloc, alloc->capacity, 1);
+}
+
+/*
+ * Marks an ID within an allocator page as in use.
+ * If the ID was the last free ID in the page, the page is removed from the
+ * allocator's list of free IDs. In the typical allocation case, this page is
+ * the first page in the list, and removing the page is fast. If instead an ID
+ * is being reserved by number, this may end up scanning the whole single linked
+ * list of pages in order to remove it.
+ */
+static void reserve_bit(struct id_alloc *alloc, struct id_alloc_page *page,
+ int word, int offset)
+{
+ struct id_alloc_page *itr;
+
+ page->allocated_mask[word] |= ((uint32_t)1) << offset;
+ alloc->allocated += 1;
+
+ if (page->allocated_mask[word] == UINT32_MAX) {
+ page->full_word_mask |= ((uint32_t)1) << word;
+ if (page->full_word_mask == UINT32_MAX) {
+ if (alloc->has_free == page) {
+ /* allocate always pulls from alloc->has_free */
+ alloc->has_free = page->next_has_free;
+ } else {
+ /* reserve could pull from any page with free
+ * bits
+ */
+ itr = alloc->has_free;
+ while (itr) {
+ if (itr->next_has_free == page) {
+ itr->next_has_free =
+ page->next_has_free;
+ return;
+ }
+
+ itr = itr->next_has_free;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Reserve an ID number from the allocator. Returns IDALLOC_INVALID (0) if the
+ * allocator has no more IDs available.
+ */
+uint32_t idalloc_allocate(struct id_alloc *alloc)
+{
+ struct id_alloc_page *page;
+ int word, offset;
+ uint32_t return_value;
+
+ if (alloc->has_free == NULL)
+ create_next_page(alloc);
+
+ if (alloc->has_free == NULL) {
+ flog_err(EC_LIB_ID_EXHAUST,
+ "ID Allocator %s has run out of IDs.", alloc->name);
+ return IDALLOC_INVALID;
+ }
+
+ page = alloc->has_free;
+ word = FFS32(~(page->full_word_mask)) - 1;
+
+ if (word < 0 || word >= 32) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s internal error. Page starting at %d is inconsistent.",
+ alloc->name, page->base_value);
+ return IDALLOC_INVALID;
+ }
+
+ offset = FFS32(~(page->allocated_mask[word])) - 1;
+ if (offset < 0 || offset >= 32) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s internal error. Page starting at %d is inconsistent on word %d",
+ alloc->name, page->base_value, word);
+ return IDALLOC_INVALID;
+ }
+ return_value = page->base_value + word * 32 + offset;
+
+ reserve_bit(alloc, page, word, offset);
+
+ return return_value;
+}
+
+/*
+ * Tries to allocate a specific ID from the allocator. Returns IDALLOC_INVALID
+ * when the ID being "reserved" has allready been assigned/reserved. This should
+ * only be done with low numbered IDs, as the allocator needs to reserve bit-map
+ * pages in order
+ */
+uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id)
+{
+ struct id_alloc_page *page;
+ int word, offset;
+
+ while (alloc->capacity <= id)
+ create_next_page(alloc);
+
+ word = ID_WORD(id);
+ offset = ID_OFFSET(id);
+ page = find_or_create_page(alloc, id, 0);
+ /* page can't be null because the loop above ensured it was created. */
+
+ if (page->allocated_mask[word] & (((uint32_t)1) << offset)) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s could not reserve %" PRIu32
+ " because it is already allocated.",
+ alloc->name, id);
+ return IDALLOC_INVALID;
+ }
+
+ reserve_bit(alloc, page, word, offset);
+ return id;
+}
+
+/*
+ * Set up an empty ID allocator, with IDALLOC_INVALID pre-reserved.
+ */
+struct id_alloc *idalloc_new(const char *name)
+{
+ struct id_alloc *ret;
+
+ ret = XCALLOC(MTYPE_IDALLOC_ALLOCATOR, sizeof(*ret));
+ ret->name = XSTRDUP(MTYPE_IDALLOC_ALLOCATOR_NAME, name);
+
+ idalloc_reserve(ret, IDALLOC_INVALID);
+
+ return ret;
+}
+
+/*
+ * Free a subdir, and all pages below it.
+ */
+static void idalloc_destroy_subdir(struct id_alloc_subdir *subdir)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_PAGE_COUNT; i++) {
+ if (subdir->sublevels[i])
+ XFREE(MTYPE_IDALLOC_PAGE, subdir->sublevels[i]);
+ else
+ break;
+ }
+ XFREE(MTYPE_IDALLOC_SUBDIRECTORY, subdir);
+}
+
+/*
+ * Free a dir, and all subdirs/pages below it.
+ */
+static void idalloc_destroy_dir(struct id_alloc_dir *dir)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_SUBDIR_COUNT; i++) {
+ if (dir->sublevels[i])
+ idalloc_destroy_subdir(dir->sublevels[i]);
+ else
+ break;
+ }
+ XFREE(MTYPE_IDALLOC_DIRECTORY, dir);
+}
+
+/*
+ * Free all memory associated with an ID allocator.
+ */
+void idalloc_destroy(struct id_alloc *alloc)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_DIR_COUNT; i++) {
+ if (alloc->sublevels[i])
+ idalloc_destroy_dir(alloc->sublevels[i]);
+ else
+ break;
+ }
+
+ XFREE(MTYPE_IDALLOC_ALLOCATOR_NAME, alloc->name);
+ XFREE(MTYPE_IDALLOC_ALLOCATOR, alloc);
+}
+
+/*
+ * Give an ID number to temporary holding pool.
+ */
+void idalloc_free_to_pool(struct id_alloc_pool **pool_ptr, uint32_t id)
+{
+ struct id_alloc_pool *new_pool;
+
+ new_pool = XMALLOC(MTYPE_IDALLOC_POOL, sizeof(*new_pool));
+ new_pool->id = id;
+ new_pool->next = *pool_ptr;
+ *pool_ptr = new_pool;
+}
+
+/*
+ * Free all ID numbers held in a holding pool back to the main allocator.
+ */
+void idalloc_drain_pool(struct id_alloc *alloc, struct id_alloc_pool **pool_ptr)
+{
+ struct id_alloc_pool *current, *next;
+
+ while (*pool_ptr) {
+ current = *pool_ptr;
+ next = current->next;
+ idalloc_free(alloc, current->id);
+ XFREE(MTYPE_IDALLOC_POOL, current);
+ *pool_ptr = next;
+ }
+}
+
+/*
+ * Allocate an ID from either a holding pool, or the main allocator. IDs will
+ * only be pulled form the main allocator when the pool is empty.
+ */
+uint32_t idalloc_allocate_prefer_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr)
+{
+ uint32_t ret;
+ struct id_alloc_pool *pool_head = *pool_ptr;
+
+ if (pool_head) {
+ ret = pool_head->id;
+ *pool_ptr = pool_head->next;
+ XFREE(MTYPE_IDALLOC_POOL, pool_head);
+ return ret;
+ } else {
+ return idalloc_allocate(alloc);
+ }
+}
diff --git a/lib/id_alloc.h b/lib/id_alloc.h
new file mode 100644
index 0000000000..efe355658a
--- /dev/null
+++ b/lib/id_alloc.h
@@ -0,0 +1,90 @@
+/*
+ * FRR ID Number Allocator
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_ID_ALLOC_H
+#define _ZEBRA_ID_ALLOC_H
+
+#include <strings.h>
+#include <limits.h>
+#include <stdint.h>
+
+#define IDALLOC_INVALID 0
+
+#define IDALLOC_DIR_BITS 8
+#define IDALLOC_SUBDIR_BITS 7
+#define IDALLOC_PAGE_BITS 7
+#define IDALLOC_WORD_BITS 5
+#define IDALLOC_OFFSET_BITS 5
+
+#define IDALLOC_DIR_COUNT (1 << IDALLOC_DIR_BITS)
+#define IDALLOC_SUBDIR_COUNT (1 << IDALLOC_SUBDIR_BITS)
+#define IDALLOC_PAGE_COUNT (1 << IDALLOC_PAGE_BITS)
+#define IDALLOC_WORD_COUNT (1 << IDALLOC_WORD_BITS)
+
+struct id_alloc_page {
+ /* Bitmask of allocations. 1s indicates the ID is already allocated. */
+ uint32_t allocated_mask[IDALLOC_WORD_COUNT];
+
+ /* Bitmask for free space in allocated_mask. 1s indicate whole 32 bit
+ * section is full.
+ */
+ uint32_t full_word_mask;
+
+ /* The ID that bit 0 in allocated_mask corresponds to. */
+ uint32_t base_value;
+
+ struct id_alloc_page
+ *next_has_free; /* Next page with at least one bit open */
+};
+
+struct id_alloc_subdir {
+ struct id_alloc_page *sublevels[IDALLOC_PAGE_COUNT];
+};
+
+struct id_alloc_dir {
+ struct id_alloc_subdir *sublevels[IDALLOC_SUBDIR_COUNT];
+};
+
+struct id_alloc {
+ struct id_alloc_dir *sublevels[IDALLOC_DIR_COUNT];
+
+ struct id_alloc_page *has_free;
+
+ char *name;
+
+ uint32_t allocated, capacity;
+};
+
+struct id_alloc_pool {
+ struct id_alloc_pool *next;
+ uint32_t id;
+};
+
+void idalloc_free(struct id_alloc *alloc, uint32_t id);
+void idalloc_free_to_pool(struct id_alloc_pool **pool_ptr, uint32_t id);
+void idalloc_drain_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr);
+uint32_t idalloc_allocate(struct id_alloc *alloc);
+uint32_t idalloc_allocate_prefer_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr);
+uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id);
+struct id_alloc *idalloc_new(const char *name);
+void idalloc_destroy(struct id_alloc *alloc);
+
+#endif
diff --git a/lib/lib_errors.h b/lib/lib_errors.h
index 3831116dd3..38c75f913e 100644
--- a/lib/lib_errors.h
+++ b/lib/lib_errors.h
@@ -72,6 +72,8 @@ enum lib_log_refs {
EC_LIB_SYSREPO_INIT,
EC_LIB_SYSREPO_DATA_CONVERT,
EC_LIB_LIBSYSREPO,
+ EC_LIB_ID_CONSISTENCY,
+ EC_LIB_ID_EXHAUST,
};
extern void lib_error_init(void);
diff --git a/lib/subdir.am b/lib/subdir.am
index 356796fbb1..c144c2c2e1 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -29,6 +29,7 @@ lib_libfrr_la_SOURCES = \
lib/graph.c \
lib/hash.c \
lib/hook.c \
+ lib/id_alloc.c \
lib/if.c \
lib/if_rmap.c \
lib/imsg-buffer.c \
@@ -147,6 +148,7 @@ pkginclude_HEADERS += \
lib/graph.h \
lib/hash.h \
lib/hook.h \
+ lib/id_alloc.h \
lib/if.h \
lib/if_rmap.h \
lib/imsg.h \
diff --git a/lib/vrf.c b/lib/vrf.c
index 498aef4580..046e468f20 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -1022,3 +1022,10 @@ int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
}
return ret;
}
+
+vrf_id_t vrf_generate_id(void)
+{
+ static int vrf_id_local;
+
+ return ++vrf_id_local;
+}
diff --git a/lib/vrf.h b/lib/vrf.h
index 04a8f70f5f..3bc88e610a 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -293,5 +293,6 @@ extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
extern void vrf_disable(struct vrf *vrf);
extern int vrf_enable(struct vrf *vrf);
extern void vrf_delete(struct vrf *vrf);
+extern vrf_id_t vrf_generate_id(void);
#endif /*_ZEBRA_VRF_H*/
diff --git a/lib/zclient.c b/lib/zclient.c
index 45a9f7be9e..b879326d71 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -58,8 +58,8 @@ int zclient_debug = 0;
struct zclient_options zclient_options_default = {.receive_notify = false};
/* Allocate zclient structure. */
-struct zclient *zclient_new_notify(struct thread_master *master,
- struct zclient_options *opt)
+struct zclient *zclient_new(struct thread_master *master,
+ struct zclient_options *opt)
{
struct zclient *zclient;
zclient = XCALLOC(MTYPE_ZCLIENT, sizeof(struct zclient));
@@ -199,7 +199,7 @@ void zclient_reset(struct zclient *zclient)
* @param zclient a pointer to zclient structure
* @return socket fd just to make sure that connection established
* @see zclient_init
- * @see zclient_new_notify
+ * @see zclient_new
*/
int zclient_socket_connect(struct zclient *zclient)
{
diff --git a/lib/zclient.h b/lib/zclient.h
index 97ebb0811c..07fe512a3c 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -422,23 +422,10 @@ struct zclient_options {
bool receive_notify;
};
-/* Prototypes of zebra client service functions. */
-extern struct zclient *zclient_new(struct thread_master *);
-
-/* clang-format off */
-#if CONFDATE > 20181101
-CPP_NOTICE("zclient_new_notify can take over or zclient_new now");
-#endif
-/* clang-format on */
-
extern struct zclient_options zclient_options_default;
-extern struct zclient *zclient_new_notify(struct thread_master *m,
- struct zclient_options *opt);
-
-#define zclient_new(A) \
- zclient_new_notify((A), &zclient_options_default); \
- CPP_WARN("Please transition to using zclient_new_notify");
+extern struct zclient *zclient_new(struct thread_master *m,
+ struct zclient_options *opt);
extern void zclient_init(struct zclient *, int, unsigned short,
struct zebra_privs_t *privs);