/* * BIRD -- Route Attribute Cache * * (c) 1998--2000 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include "nest/bird.h" #include "nest/route.h" #include "nest/protocol.h" #include "nest/iface.h" #include "nest/cli.h" #include "nest/attrs.h" #include "lib/resource.h" #include "lib/string.h" static slab *rta_slab; static pool *rta_pool; struct protocol *attr_class_to_protocol[EAP_MAX]; /* * Extended Attributes */ static inline eattr * ea__find(ea_list *e, unsigned id) { eattr *a; int l, r, m; while (e) { if (e->flags & EALF_BISECT) { l = 0; r = e->count + 1; while (l <= r) { m = (l+r) / 2; a = &e->attrs[m]; if (a->id == id) return a; else if (a->id < id) l = m+1; else r = m-1; } } else for(m=0; mcount; m++) if (e->attrs[m].id == id) return &e->attrs[m]; e = e->next; } return NULL; } eattr * ea_find(ea_list *e, unsigned id) { eattr *a = ea__find(e, id & EA_CODE_MASK); if (a && (a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF && !(id & EA_ALLOW_UNDEF)) return NULL; return a; } int ea_get_int(ea_list *e, unsigned id, int def) { eattr *a = ea_find(e, id); if (!a) return def; return a->u.data; } static inline void ea_do_sort(ea_list *e) { unsigned n = e->count; eattr *a = e->attrs; eattr *b = alloca(n * sizeof(eattr)); unsigned s, ss; /* We need to use a stable sorting algorithm, hence mergesort */ do { s = ss = 0; while (s < n) { eattr *p, *q, *lo, *hi; p = b; ss = s; *p++ = a[s++]; while (s < n && p[-1].id <= a[s].id) *p++ = a[s++]; if (s < n) { q = p; *p++ = a[s++]; while (s < n && p[-1].id <= a[s].id) *p++ = a[s++]; lo = b; hi = q; s = ss; while (lo < q && hi < p) if (lo->id <= hi->id) a[s++] = *lo++; else a[s++] = *hi++; while (lo < q) a[s++] = *lo++; while (hi < p) a[s++] = *hi++; } } } while (ss); } static inline void ea_do_prune(ea_list *e) { eattr *s, *d, *l, *s0; int i = 0; /* Discard duplicates and undefs. Do you remember sorting was stable? */ s = d = e->attrs; l = e->attrs + e->count; while (s < l) { s0 = s++; while (s < l && s->id == s[-1].id) s++; /* s0 is the most recent version, s[-1] the oldest one */ if ((s0->type & EAF_TYPE_MASK) != EAF_TYPE_UNDEF) { *d = *s0; d->type = (d->type & ~EAF_ORIGINATED) | (s[-1].type & EAF_ORIGINATED); d++; i++; } } e->count = i; } void ea_sort(ea_list *e) { while (e) { if (!(e->flags & EALF_SORTED)) { ea_do_sort(e); ea_do_prune(e); e->flags |= EALF_SORTED; } if (e->count > 5) e->flags |= EALF_BISECT; e = e->next; } } unsigned ea_scan(ea_list *e) { unsigned cnt = 0; while (e) { cnt += e->count; e = e->next; } return sizeof(ea_list) + sizeof(eattr)*cnt; } void ea_merge(ea_list *e, ea_list *t) { eattr *d = t->attrs; t->flags = 0; t->count = 0; t->next = NULL; while (e) { memcpy(d, e->attrs, sizeof(eattr)*e->count); t->count += e->count; d += e->count; e = e->next; } } int ea_same(ea_list *x, ea_list *y) { int c; if (!x || !y) return x == y; ASSERT(!x->next && !y->next); if (x->count != y->count) return 0; for(c=0; ccount; c++) { eattr *a = &x->attrs[c]; eattr *b = &y->attrs[c]; if (a->id != b->id || a->flags != b->flags || a->type != b->type || ((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : (a->u.ptr->length != b->u.ptr->length || memcmp(a->u.ptr, b->u.ptr, a->u.ptr->length)))) return 0; } return 1; } static inline ea_list * ea_list_copy(ea_list *o) { ea_list *n; unsigned i, len; if (!o) return NULL; ASSERT(!o->next); len = sizeof(ea_list) + sizeof(eattr) * o->count; n = mb_alloc(rta_pool, len); memcpy(n, o, len); n->flags |= EALF_CACHED; for(i=0; icount; i++) { eattr *a = &n->attrs[i]; if (!(a->type & EAF_EMBEDDED)) { unsigned size = sizeof(struct adata) + a->u.ptr->length; struct adata *d = mb_alloc(rta_pool, size); memcpy(d, a->u.ptr, size); a->u.ptr = d; } } return n; } static inline void ea_free(ea_list *o) { int i; if (o) { ASSERT(!o->next); for(i=0; icount; i++) { eattr *a = &o->attrs[i]; if (!(a->type & EAF_EMBEDDED)) mb_free(a->u.ptr); } mb_free(o); } } void ea_format(eattr *e, byte *buf) { struct protocol *p; int status = GA_UNKNOWN; unsigned int i; struct adata *ad = (e->type & EAF_EMBEDDED) ? NULL : e->u.ptr; byte *end = buf + EA_FORMAT_BUF_SIZE - 1; if (p = attr_class_to_protocol[EA_PROTO(e->id)]) { buf += bsprintf(buf, "%s.", p->name); if (p->get_attr) status = p->get_attr(e, buf); buf += strlen(buf); } else if (EA_PROTO(e->id)) buf += bsprintf(buf, "%02x.", EA_PROTO(e->id)); if (status < GA_NAME) buf += bsprintf(buf, "%02x", EA_ID(e->id)); if (status < GA_FULL) { *buf++ = ':'; *buf++ = ' '; switch (e->type & EAF_TYPE_MASK) { case EAF_TYPE_INT: bsprintf(buf, "%d", e->u.data); break; case EAF_TYPE_OPAQUE: for(i=0; ilength; i++) { if (buf > end - 8) { strcpy(buf, " ..."); break; } if (i) *buf++ = ' '; buf += bsprintf(buf, "%02x", ad->data[i]); } break; case EAF_TYPE_IP_ADDRESS: bsprintf(buf, "%I", *(ip_addr *) ad->data); break; case EAF_TYPE_ROUTER_ID: bsprintf(buf, "%d.%d.%d.%d", (e->u.data >> 24) & 0xff, (e->u.data >> 16) & 0xff, (e->u.data >> 8) & 0xff, e->u.data & 0xff); break; case EAF_TYPE_AS_PATH: as_path_format(ad, buf, end - buf); break; case EAF_TYPE_INT_SET: int_set_format(ad, buf, end - buf); break; case EAF_TYPE_UNDEF: default: bsprintf(buf, "", e->type); } } } void ea_dump(ea_list *e) { int i; if (!e) { debug("NONE"); return; } while (e) { debug("[%c%c%c]", (e->flags & EALF_SORTED) ? 'S' : 's', (e->flags & EALF_BISECT) ? 'B' : 'b', (e->flags & EALF_CACHED) ? 'C' : 'c'); for(i=0; icount; i++) { eattr *a = &e->attrs[i]; debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags); if (a->type & EAF_TEMP) debug("T"); debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]); if (a->type & EAF_ORIGINATED) debug("o"); if (a->type & EAF_EMBEDDED) debug(":%08x", a->u.data); else { int j, len = a->u.ptr->length; debug("[%d]:", len); for(j=0; ju.ptr->data[j]); } } if (e = e->next) debug(" | "); } } inline unsigned int ea_hash(ea_list *e) { u32 h = 0; int i; if (e) /* Assuming chain of length 1 */ { for(i=0; icount; i++) { struct eattr *a = &e->attrs[i]; h ^= a->id; if (a->type & EAF_EMBEDDED) h ^= a->u.data; else { struct adata *d = a->u.ptr; int size = d->length; byte *z = d->data; while (size >= 4) { h ^= *(u32 *)z; z += 4; size -= 4; } while (size--) h = (h >> 24) ^ (h << 8) ^ *z++; } } h ^= h >> 16; h ^= h >> 6; h &= 0xffff; } return h; } ea_list * ea_append(ea_list *to, ea_list *what) { ea_list *res; if (!to) return what; res = to; while (to->next) to = to->next; to->next = what; return res; } /* * 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 rta **rta_hash_table; static void rta_alloc_hash(void) { rta_hash_table = mb_allocz(rta_pool, sizeof(rta *) * rta_cache_size); if (rta_cache_size < 32768) rta_cache_limit = rta_cache_size * 2; else rta_cache_limit = ~0; rta_cache_mask = rta_cache_size - 1; } static inline unsigned int rta_hash(rta *a) { return (a->proto->hash_key ^ ipa_hash(a->gw) ^ ea_hash(a->eattrs)) & 0xffff; } static inline int rta_same(rta *x, rta *y) { return (x->proto == y->proto && x->source == y->source && x->scope == y->scope && x->cast == y->cast && x->dest == y->dest && x->flags == y->flags && ipa_equal(x->gw, y->gw) && ipa_equal(x->from, y->from) && x->iface == y->iface && ea_same(x->eattrs, y->eattrs)); } static rta * rta_copy(rta *o) { rta *r = sl_alloc(rta_slab); memcpy(r, o, sizeof(rta)); r->uc = 1; r->eattrs = ea_list_copy(o->eattrs); return r; } static inline void rta_insert(rta *r) { unsigned int h = r->hash_key & rta_cache_mask; r->next = rta_hash_table[h]; if (r->next) r->next->pprev = &r->next; r->pprev = &rta_hash_table[h]; rta_hash_table[h] = r; } static void rta_rehash(void) { unsigned int ohs = rta_cache_size; unsigned int h; rta *r, *n; rta **oht = rta_hash_table; rta_cache_size = 2*rta_cache_size; DBG("Rehashing rta cache from %d to %d entries.\n", ohs, rta_cache_size); rta_alloc_hash(); for(h=0; hnext; rta_insert(r); } mb_free(oht); } rta * rta_lookup(rta *o) { rta *r; unsigned int h; ASSERT(!(o->aflags & RTAF_CACHED)); if (o->eattrs) { if (o->eattrs->next) /* Multiple ea_list's, need to merge them */ { ea_list *ml = alloca(ea_scan(o->eattrs)); ea_merge(o->eattrs, ml); o->eattrs = ml; } ea_sort(o->eattrs); } h = rta_hash(o); for(r=rta_hash_table[h & rta_cache_mask]; r; r=r->next) if (r->hash_key == h && rta_same(r, o)) return rta_clone(r); r = rta_copy(o); r->hash_key = h; r->aflags = RTAF_CACHED; rta_insert(r); if (++rta_cache_count > rta_cache_limit) rta_rehash(); return r; } void rta__free(rta *a) { ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED)); rta_cache_count--; *a->pprev = a->next; if (a->next) a->next->pprev = a->pprev; a->aflags = 0; /* Poison the entry */ ea_free(a->eattrs); sl_free(rta_slab, a); } void rta_dump(rta *a) { static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE", "RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP", "RTS_OSPF", "RTS_OSPF_EXT", "RTS_OSPF_IA", "RTS_OSPF_BOUNDARY", "RTS_BGP" }; 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], rtd[a->dest], a->hash_key); if (!(a->aflags & RTAF_CACHED)) debug(" !CACHED"); debug(" <-%I", a->from); if (a->dest == RTD_ROUTER) debug(" ->%I", a->gw); if (a->dest == RTD_DEVICE || a->dest == RTD_ROUTER) debug(" [%s]", a->iface ? a->iface->name : "???" ); if (a->eattrs) { debug(" EA: "); ea_dump(a->eattrs); } } void rta_dump_all(void) { rta *a; unsigned int h; debug("Route attribute cache (%d entries, rehash at %d):\n", rta_cache_count, rta_cache_limit); for(h=0; hnext) { debug("%p ", a); rta_dump(a); debug("\n"); } debug("\n"); } void rta_show(struct cli *c, rta *a, ea_list *eal) { static char *src_names[] = { "dummy", "static", "inherit", "device", "static-device", "redirect", "RIP", "OSPF", "OSPF-ext", "OSPF-IA", "OSPF-boundary", "BGP" }; static char *cast_names[] = { "unicast", "broadcast", "multicast", "anycast" }; int i; byte buf[EA_FORMAT_BUF_SIZE]; cli_printf(c, -1008, "\tType: %s %s %s", src_names[a->source], cast_names[a->cast], ip_scope_text(a->scope)); if (!eal) eal = a->eattrs; for(; eal; eal=eal->next) for(i=0; icount; i++) { ea_format(&eal->attrs[i], buf); cli_printf(c, -1012, "\t%s", buf); } } void rta_init(void) { rta_pool = rp_new(&root_pool, "Attributes"); rta_slab = sl_new(rta_pool, sizeof(rta)); rta_alloc_hash(); }