#include "nest/cli.h"
#include "nest/attrs.h"
#include "lib/alloca.h"
+#include "lib/hash.h"
#include "lib/resource.h"
#include "lib/string.h"
static slab *rta_slab;
static slab *mpnh_slab;
+static slab *rte_src_slab;
+
+/* rte source ID bitmap */
+static u32 *src_ids;
+static u32 src_id_size, src_id_used, src_id_pos;
+#define SRC_ID_INIT_SIZE 4
+
+/* rte source hash */
+
+#define RSH_KEY(n) n->proto, n->private_id
+#define RSH_NEXT(n) n->next
+#define RSH_EQ(p1,n1,p2,n2) p1 == p2 && n1 == n2
+#define RSH_FN(p,n) p->hash_key ^ u32_hash(n)
+
+#define RSH_REHASH rte_src_rehash
+#define RSH_PARAMS /2, *2, 1, 1, 8, 20
+#define RSH_INIT_ORDER 6
+
+static HASH(struct rte_src) src_hash;
struct protocol *attr_class_to_protocol[EAP_MAX];
-static inline unsigned int
+
+static void
+rte_src_init(void)
+{
+ rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src));
+
+ src_id_pos = 0;
+ src_id_size = SRC_ID_INIT_SIZE;
+ src_ids = mb_allocz(rta_pool, src_id_size * sizeof(u32));
+
+ /* ID 0 is reserved */
+ src_ids[0] = 1;
+ src_id_used = 1;
+
+ HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER);
+}
+
+static inline int u32_cto(uint x) { return ffs(~x) - 1; }
+
+static inline u32
+rte_src_alloc_id(void)
+{
+ uint i, j;
+ for (i = src_id_pos; i < src_id_size; i++)
+ if (src_ids[i] != 0xffffffff)
+ goto found;
+
+ /* If we are at least 7/8 full, expand */
+ if (src_id_used > (src_id_size * 28))
+ {
+ src_id_size *= 2;
+ src_ids = mb_realloc(src_ids, src_id_size * sizeof(u32));
+ bzero(src_ids + i, (src_id_size - i) * sizeof(u32));
+ goto found;
+ }
+
+ for (i = 0; i < src_id_pos; i++)
+ if (src_ids[i] != 0xffffffff)
+ goto found;
+
+ ASSERT(0);
+
+ found:
+ ASSERT(i < 0x8000000);
+
+ src_id_pos = i;
+ j = u32_cto(src_ids[i]);
+
+ src_ids[i] |= (1 << j);
+ src_id_used++;
+ return 32 * i + j;
+}
+
+static inline void
+rte_src_free_id(u32 id)
+{
+ int i = id / 32;
+ int j = id % 32;
+
+ ASSERT((i < src_id_size) && (src_ids[i] & (1 << j)));
+ src_ids[i] &= ~(1 << j);
+ src_id_used--;
+}
+
+
+HASH_DEFINE_REHASH_FN(RSH, struct rte_src)
+
+struct rte_src *
+rt_find_source(struct proto *p, u32 id)
+{
+ return HASH_FIND(src_hash, RSH, p, id);
+}
+
+struct rte_src *
+rt_get_source(struct proto *p, u32 id)
+{
+ struct rte_src *src = rt_find_source(p, id);
+
+ if (src)
+ return src;
+
+ src = sl_alloc(rte_src_slab);
+ src->proto = p;
+ src->private_id = id;
+ src->global_id = rte_src_alloc_id();
+ src->uc = 0;
+
+ HASH_INSERT2(src_hash, RSH, rta_pool, src);
+
+ return src;
+}
+
+void
+rt_prune_sources(void)
+{
+ HASH_WALK_FILTER(src_hash, next, src, sp)
+ {
+ if (src->uc == 0)
+ {
+ HASH_DO_REMOVE(src_hash, RSH, sp);
+ rte_src_free_id(src->global_id);
+ sl_free(rte_src_slab, src);
+ }
+ }
+ HASH_WALK_FILTER_END;
+
+ HASH_MAY_RESIZE_DOWN(src_hash, RSH, rta_pool);
+}
+
+
+/*
+ * Multipath Next Hop
+ */
+
+static inline uint
mpnh_hash(struct mpnh *x)
{
- unsigned int h = 0;
+ uint h = 0;
for (; x; x = x->next)
h ^= ipa_hash(x->gw);
return x == y;
}
+static int
+mpnh_compare_node(struct mpnh *x, struct mpnh *y)
+{
+ int r;
+
+ if (!x)
+ return 1;
+
+ if (!y)
+ return -1;
+
+ r = ((int) y->weight) - ((int) x->weight);
+ if (r)
+ return r;
+
+ r = ipa_compare(x->gw, y->gw);
+ if (r)
+ return r;
+
+ return ((int) x->iface->index) - ((int) y->iface->index);
+}
+
+static inline struct mpnh *
+mpnh_copy_node(const struct mpnh *src, linpool *lp)
+{
+ struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh));
+ n->gw = src->gw;
+ n->iface = src->iface;
+ n->next = NULL;
+ n->weight = src->weight;
+ return n;
+}
+
+/**
+ * mpnh_merge - merge nexthop lists
+ * @x: list 1
+ * @y: list 2
+ * @rx: reusability of list @x
+ * @ry: reusability of list @y
+ * @max: max number of nexthops
+ * @lp: linpool for allocating nexthops
+ *
+ * The mpnh_merge() function takes two nexthop lists @x and @y and merges them,
+ * eliminating possible duplicates. The input lists must be sorted and the
+ * result is sorted too. The number of nexthops in result is limited by @max.
+ * New nodes are allocated from linpool @lp.
+ *
+ * The arguments @rx and @ry specify whether corresponding input lists may be
+ * consumed by the function (i.e. their nodes reused in the resulting list), in
+ * that case the caller should not access these lists after that. To eliminate
+ * issues with deallocation of these lists, the caller should use some form of
+ * bulk deallocation (e.g. stack or linpool) to free these nodes when the
+ * resulting list is no longer needed. When reusability is not set, the
+ * corresponding lists are not modified nor linked from the resulting list.
+ */
+struct mpnh *
+mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
+{
+ struct mpnh *root = NULL;
+ struct mpnh **n = &root;
+
+ while ((x || y) && max--)
+ {
+ int cmp = mpnh_compare_node(x, y);
+ if (cmp < 0)
+ {
+ *n = rx ? x : mpnh_copy_node(x, lp);
+ x = x->next;
+ }
+ else if (cmp > 0)
+ {
+ *n = ry ? y : mpnh_copy_node(y, lp);
+ y = y->next;
+ }
+ else
+ {
+ *n = rx ? x : (ry ? y : mpnh_copy_node(x, lp));
+ x = x->next;
+ y = y->next;
+ }
+ n = &((*n)->next);
+ }
+ *n = NULL;
+
+ return root;
+}
+
+void
+mpnh_insert(struct mpnh **n, struct mpnh *x)
+{
+ for (; *n; n = &((*n)->next))
+ {
+ int cmp = mpnh_compare_node(*n, x);
+
+ if (cmp < 0)
+ continue;
+ else if (cmp > 0)
+ break;
+ else
+ return;
+ }
+
+ x->next = *n;
+ *n = x;
+}
+
+int
+mpnh_is_sorted(struct mpnh *x)
+{
+ for (; x && x->next; x = x->next)
+ if (mpnh_compare_node(x, x->next) >= 0)
+ return 0;
+
+ return 1;
+}
+
static struct mpnh *
mpnh_copy(struct mpnh *o)
{
return a;
}
+/**
+ * ea_walk - walk through extended attributes
+ * @s: walk state structure
+ * @id: start of attribute ID interval
+ * @max: length of attribute ID interval
+ *
+ * Given an extended attribute list, ea_walk() walks through the list looking
+ * for first occurrences of attributes with ID in specified interval from @id to
+ * (@id + @max - 1), returning pointers to found &eattr structures, storing its
+ * walk state in @s for subsequent calls.
+ *
+ * The function ea_walk() is supposed to be called in a loop, with initially
+ * zeroed walk state structure @s with filled the initial extended attribute
+ * list, returning one found attribute in each call or %NULL when no other
+ * attribute exists. The extended attribute list or the arguments should not be
+ * modified between calls. The maximum value of @max is 128.
+ */
+eattr *
+ea_walk(struct ea_walk_state *s, uint id, uint max)
+{
+ ea_list *e = s->eattrs;
+ eattr *a = s->ea;
+ eattr *a_max;
+
+ max = id + max;
+
+ if (a)
+ goto step;
+
+ for (; e; e = e->next)
+ {
+ if (e->flags & EALF_BISECT)
+ {
+ int l, r, m;
+
+ l = 0;
+ r = e->count - 1;
+ while (l < r)
+ {
+ m = (l+r) / 2;
+ if (e->attrs[m].id < id)
+ l = m + 1;
+ else
+ r = m;
+ }
+ a = e->attrs + l;
+ }
+ else
+ a = e->attrs;
+
+ step:
+ a_max = e->attrs + e->count;
+ for (; a < a_max; a++)
+ if ((a->id >= id) && (a->id < max))
+ {
+ int n = a->id - id;
+
+ if (BIT32_TEST(s->visited, n))
+ continue;
+
+ BIT32_SET(s->visited, n);
+
+ if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
+ continue;
+
+ s->eattrs = e;
+ s->ea = a;
+ return a;
+ }
+ else if (e->flags & EALF_BISECT)
+ break;
+ }
+
+ return NULL;
+}
+
/**
* ea_get_int - fetch an integer attribute
* @e: attribute list
*buf += bsprintf(*buf, "igp_metric");
return GA_NAME;
}
-
+
return GA_UNKNOWN;
}
+void
+ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
+{
+ byte *bound = buf + bufsize - 32;
+ u32 data = a->u.data;
+ int i;
+
+ for (i = min; i < max; i++)
+ if ((data & (1u << i)) && names[i])
+ {
+ if (buf > bound)
+ {
+ strcpy(buf, " ...");
+ return;
+ }
+
+ buf += bsprintf(buf, " %s", names[i]);
+ data &= ~(1u << i);
+ }
+
+ if (data)
+ bsprintf(buf, " %08x", data);
+
+ return;
+}
+
static inline void
-opaque_format(struct adata *ad, byte *buf, unsigned int size)
+opaque_format(struct adata *ad, byte *buf, uint size)
{
byte *bound = buf + size - 10;
- int i;
+ uint i;
for(i = 0; i < ad->length; i++)
{
}
}
+static inline void
+ea_show_lc_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
+{
+ int i = lc_set_format(ad, 0, pos, end - pos);
+ cli_printf(c, -1012, "\t%s", buf);
+ while (i)
+ {
+ i = lc_set_format(ad, i, buf, end - buf - 1);
+ cli_printf(c, -1012, "\t\t%s", buf);
+ }
+}
+
/**
* ea_show - print an &eattr to CLI
* @c: destination CLI
}
else if (EA_PROTO(e->id))
pos += bsprintf(pos, "%02x.", EA_PROTO(e->id));
- else
+ else
status = get_generic_attr(e, &pos, end - pos);
if (status < GA_NAME)
case EAF_TYPE_AS_PATH:
as_path_format(ad, pos, end - pos);
break;
+ case EAF_TYPE_BITFIELD:
+ bsprintf(pos, "%08x", e->u.data);
+ break;
case EAF_TYPE_INT_SET:
ea_show_int_set(c, ad, 1, pos, buf, end);
return;
case EAF_TYPE_EC_SET:
ea_show_ec_set(c, ad, pos, buf, end);
return;
+ case EAF_TYPE_LC_SET:
+ ea_show_lc_set(c, ad, pos, buf, end);
+ return;
case EAF_TYPE_UNDEF:
default:
bsprintf(pos, "<type %02x>", e->type);
* ea_hash() takes an extended attribute list and calculated a hopefully
* uniformly distributed hash value from its contents.
*/
-inline unsigned int
+inline uint
ea_hash(ea_list *e)
{
u32 h = 0;
* rta's
*/
-static unsigned int rta_cache_count;
-static unsigned int rta_cache_size = 32;
-static unsigned int rta_cache_limit;
-static unsigned int rta_cache_mask;
+static uint rta_cache_count;
+static uint rta_cache_size = 32;
+static uint rta_cache_limit;
+static uint rta_cache_mask;
static rta **rta_hash_table;
static void
rta_cache_mask = rta_cache_size - 1;
}
-static inline unsigned int
+static inline uint
rta_hash(rta *a)
{
- return (a->proto->hash_key ^ ipa_hash(a->gw) ^
+ return (((uint) (uintptr_t) a->src) ^ ipa_hash(a->gw) ^
mpnh_hash(a->nexthops) ^ ea_hash(a->eattrs)) & 0xffff;
}
static inline int
rta_same(rta *x, rta *y)
{
- return (x->proto == y->proto &&
+ return (x->src == y->src &&
x->source == y->source &&
x->scope == y->scope &&
x->cast == y->cast &&
static inline void
rta_insert(rta *r)
{
- unsigned int h = r->hash_key & rta_cache_mask;
+ uint h = r->hash_key & rta_cache_mask;
r->next = rta_hash_table[h];
if (r->next)
r->next->pprev = &r->next;
static void
rta_rehash(void)
{
- unsigned int ohs = rta_cache_size;
- unsigned int h;
+ uint ohs = rta_cache_size;
+ uint h;
rta *r, *n;
rta **oht = rta_hash_table;
rta_lookup(rta *o)
{
rta *r;
- unsigned int h;
+ uint h;
ASSERT(!(o->aflags & RTAF_CACHED));
if (o->eattrs)
r = rta_copy(o);
r->hash_key = h;
r->aflags = RTAF_CACHED;
+ rt_lock_source(r->src);
rt_lock_hostentry(r->hostentry);
rta_insert(r);
a->next->pprev = a->pprev;
a->aflags = 0; /* Poison the entry */
rt_unlock_hostentry(a->hostentry);
+ rt_unlock_source(a->src);
mpnh_free(a->nexthops);
ea_free(a->eattrs);
sl_free(rta_slab, a);
}
+rta *
+rta_do_cow(rta *o, linpool *lp)
+{
+ rta *r = lp_alloc(lp, sizeof(rta));
+ memcpy(r, o, sizeof(rta));
+ r->aflags = 0;
+ r->uc = 0;
+ return r;
+}
+
/**
* rta_dump - dump route attributes
* @a: attribute structure to dump
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
- "RTS_OSPF_EXT2", "RTS_BGP" };
+ "RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
static char *rtc[] = { "", " BC", " MC", " AC" };
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
debug("p=%s uc=%d %s %s%s%s h=%04x",
- a->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope), rtc[a->cast],
+ a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope), rtc[a->cast],
rtd[a->dest], a->hash_key);
if (!(a->aflags & RTAF_CACHED))
debug(" !CACHED");
rta_dump_all(void)
{
rta *a;
- unsigned int h;
+ uint h;
debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit);
for(h=0; h<rta_cache_size; h++)
rta_slab = sl_new(rta_pool, sizeof(rta));
mpnh_slab = sl_new(rta_pool, sizeof(struct mpnh));
rta_alloc_hash();
+ rte_src_init();
}
/*