break;
case 'a': /* rta access */
{
+ ACCESS_RTE;
struct rta *rta = (*f_rte)->attrs;
res.type = what->aux;
- switch(res.type) {
- case T_IP:
- res.val.px.ip = * (ip_addr *) ((char *) rta + what->a2.i);
- break;
- case T_ENUM:
- res.val.i = * ((char *) rta + what->a2.i);
- break;
- case T_STRING: /* Warning: this is a special case for proto attribute */
- res.val.s = rta->src->proto->name;
- break;
- case T_PREFIX: /* Warning: this works only for prefix of network */
- {
- res.val.px.ip = (*f_rte)->net->n.prefix;
- res.val.px.len = (*f_rte)->net->n.pxlen;
- break;
- }
+
+ switch (what->a2.i)
+ {
+ case SA_FROM: res.val.px.ip = rta->from; break;
+ case SA_GW: res.val.px.ip = rta->gw; break;
+ case SA_NET: res.val.px.ip = (*f_rte)->net->n.prefix;
+ res.val.px.len = (*f_rte)->net->n.pxlen; break;
- case SA_PROTO: res.val.s = rta->proto->name; break;
++ case SA_PROTO: res.val.s = rta->src->proto->name; break;
+ case SA_SOURCE: res.val.i = rta->source; break;
+ case SA_SCOPE: res.val.i = rta->scope; break;
+ case SA_CAST: res.val.i = rta->cast; break;
+ case SA_DEST: res.val.i = rta->dest; break;
+ case SA_IFNAME: res.val.s = rta->iface ? rta->iface->name : ""; break;
+ case SA_IFINDEX: res.val.i = rta->iface ? rta->iface->index : 0; break;
+
default:
- bug( "Invalid type for rta access (%x)", res.type );
+ bug("Invalid static attribute access (%x)", res.type);
}
}
break;
f_rta_cow();
{
struct rta *rta = (*f_rte)->attrs;
- switch (what->aux) {
- case T_IP:
- * (ip_addr *) ((char *) rta + what->a2.i) = v1.val.px.ip;
+ switch (what->a2.i)
+ {
+ case SA_FROM:
+ rta->from = v1.val.px.ip;
break;
- case T_ENUM_SCOPE:
+ case SA_GW:
+ {
+ ip_addr ip = v1.val.px.ip;
- neighbor *n = neigh_find(rta->proto, &ip, 0);
++ neighbor *n = neigh_find(rta->src->proto, &ip, 0);
+ if (!n || (n->scope == SCOPE_HOST))
+ runtime( "Invalid gw address" );
+
+ rta->dest = RTD_ROUTER;
+ rta->gw = ip;
+ rta->iface = n->iface;
+ rta->nexthops = NULL;
+ rta->hostentry = NULL;
+ }
+ break;
+
+ case SA_SCOPE:
rta->scope = v1.val.i;
break;
p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
p->main_ahook->in_filter = p->cf->in_filter;
p->main_ahook->out_filter = p->cf->out_filter;
+ p->main_ahook->rx_limit = p->cf->rx_limit;
p->main_ahook->in_limit = p->cf->in_limit;
p->main_ahook->out_limit = p->cf->out_limit;
+ p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
+
+ proto_reset_limit(p->main_ahook->rx_limit);
proto_reset_limit(p->main_ahook->in_limit);
proto_reset_limit(p->main_ahook->out_limit);
}
void rt_setup(pool *, rtable *, char *, struct rtable_config *);
static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_find(&tab->fib, &addr, len); }
static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); }
-rte *rte_find(net *net, struct proto *p);
+rte *rte_find(net *net, struct rte_src *src);
rte *rte_get_temp(struct rta *);
-void rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src);
-static inline void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new) { rte_update2(p->main_ahook, net, new, src); }
+void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src);
+static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); }
void rte_discard(rtable *tab, rte *old);
+ int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter);
void rte_dump(rte *);
void rte_free(rte *);
rte *rte_do_cow(rte *);
struct protocol *attr_class_to_protocol[EAP_MAX];
- src_ids = mb_realloc(rta_pool, src_ids, src_id_size * sizeof(u32));
+
+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_SIZE_DEF;
+ src_ids = mb_allocz(rta_pool, src_id_size * sizeof(u32));
+
+ /* ID 0 is reserved */
+ src_ids[0] = 1;
+ src_id_used = 1;
+
+ src_hash_count = 0;
+ src_hash_order = SRC_HASH_ORDER_DEF;
+ src_hash_size = 1 << src_hash_order;
+ src_table = mb_allocz(rta_pool, src_hash_size * sizeof(struct rte_src *));
+}
+
+static inline int u32_cto(unsigned int x) { return ffs(~x) - 1; }
+
+static inline u32
+rte_src_alloc_id(void)
+{
+ int 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--;
+}
+
+static inline u32 rte_src_hash(struct proto *p, u32 x, u32 order)
+{ return (x * 2902958171u) >> (32 - order); }
+
+static void
+rte_src_rehash(int step)
+{
+ struct rte_src **old_tab, *src, *src_next;
+ u32 old_size, hash, i;
+
+ old_tab = src_table;
+ old_size = src_hash_size;
+
+ src_hash_order += step;
+ src_hash_size = 1 << src_hash_order;
+ src_table = mb_allocz(rta_pool, src_hash_size * sizeof(struct rte_src *));
+
+ for (i = 0; i < old_size; i++)
+ for (src = old_tab[i]; src; src = src_next)
+ {
+ src_next = src->next;
+ hash = rte_src_hash(src->proto, src->private_id, src_hash_order);
+ src->next = src_table[hash];
+ src_table[hash] = src;
+ }
+
+ mb_free(old_tab);
+}
+
+struct rte_src *
+rt_find_source(struct proto *p, u32 id)
+{
+ struct rte_src *src;
+ u32 hash = rte_src_hash(p, id, src_hash_order);
+
+ for (src = src_table[hash]; src; src = src->next)
+ if ((src->proto == p) && (src->private_id == id))
+ return src;
+
+ return NULL;
+}
+
+struct rte_src *
+rt_get_source(struct proto *p, u32 id)
+{
+ struct rte_src *src;
+ u32 hash = rte_src_hash(p, id, src_hash_order);
+
+ for (src = src_table[hash]; src; src = src->next)
+ if ((src->proto == p) && (src->private_id == id))
+ 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;
+
+ src->next = src_table[hash];
+ src_table[hash] = src;
+
+ src_hash_count++;
+ if ((src_hash_count > src_hash_size) && (src_hash_order < SRC_HASH_ORDER_MAX))
+ rte_src_rehash(1);
+
+ return src;
+}
+
+static inline void
+rt_remove_source(struct rte_src **sp)
+{
+ struct rte_src *src = *sp;
+
+ *sp = src->next;
+ rte_src_free_id(src->global_id);
+ sl_free(rte_src_slab, src);
+ src_hash_count--;
+}
+
+void
+rt_prune_sources(void)
+{
+ struct rte_src **sp;
+ int i;
+
+ for (i = 0; i < src_hash_size; i++)
+ {
+ sp = &src_table[i];
+ while (*sp)
+ {
+ if ((*sp)->uc == 0)
+ rt_remove_source(sp);
+ else
+ sp = &(*sp)->next;
+ }
+ }
+
+ while ((src_hash_count < (src_hash_size / 4)) && (src_hash_order > SRC_HASH_ORDER_MIN))
+ rte_src_rehash(-1);
+}
+
+
+/*
+ * Multipath Next Hop
+ */
+
static inline unsigned int
mpnh_hash(struct mpnh *x)
{
x->flags == y->flags &&
x->pflags == y->pflags &&
x->pref == y->pref &&
- (!x->attrs->proto->rte_same || x->attrs->proto->rte_same(x, y));
+ (!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y));
}
+ static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
+
static void
-rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src)
+rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct rte_src *src)
{
struct proto *p = ah->proto;
struct rtable *table = ah->table;
{
stats->imp_updates_filtered++;
rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
- }
- tmpa = make_tmp_attrs(new, rte_update_pool);
- if (filter)
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ /* new is a private copy, i could modify it */
+ new->flags |= REF_FILTERED;
+ }
+ else
{
- ea_list *old_tmpa = tmpa;
- int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
- if (fr > F_ACCEPT)
- if (src->make_tmp_attrs)
- tmpa = src->make_tmp_attrs(new, rte_update_pool);
++ tmpa = make_tmp_attrs(new, rte_update_pool);
+ if (filter && (filter != FILTER_REJECT))
{
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
+ ea_list *old_tmpa = tmpa;
+ int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
+ if (fr > F_ACCEPT)
+ {
+ stats->imp_updates_filtered++;
+ rte_trace_in(D_FILTERS, p, new, "filtered out");
+
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ new->flags |= REF_FILTERED;
+ }
- if (tmpa != old_tmpa && src->store_tmp_attrs)
- src->store_tmp_attrs(new, tmpa);
++ if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
++ src->proto->store_tmp_attrs(new, tmpa);
}
- if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
- src->proto->store_tmp_attrs(new, tmpa);
}
--
- if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
+ if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
new->flags |= REF_COW;
}
else
- stats->imp_withdraws_received++;
+ {
+ stats->imp_withdraws_received++;
+
+ if (!net || !src)
+ {
+ stats->imp_withdraws_ignored++;
+ rte_update_unlock();
+ return;
+ }
+ }
+ recalc:
+ rte_hide_dummy_routes(net, &dummy);
rte_recalculate(ah, net, new, tmpa, src);
+ rte_unhide_dummy_routes(net, &dummy);
rte_update_unlock();
return;
rte_update_unlock();
}
- struct proto *src = rt->attrs->proto;
- ea_list *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL;
+ /* Check rtable for best route to given net whether it would be exported do p */
+ int
+ rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter)
+ {
+ net *n = net_find(t, prefix, pxlen);
+ rte *rt = n ? n->routes : NULL;
+
+ if (!rte_is_valid(rt))
+ return 0;
+
+ rte_update_lock();
+
+ /* Rest is stripped down export_filter() */
++ ea_list *tmpa = make_tmp_attrs(rt, rte_update_pool);
+ int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
+ if (v == RIC_PROCESS)
+ v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+
+ /* Discard temporary rte */
+ if (rt != n->routes)
+ rte_free(rt);
+
+ rte_update_unlock();
+
+ return v > 0;
+ }
+
/**
* rte_dump - dump a route
* @e: &rte to be dumped
rescan:
for (e=n->routes; e; e=e->next)
- if (e->sender->proto->core_state != FS_HAPPY &&
- e->sender->proto->core_state != FS_FEEDING)
+ if (e->sender->proto->flushing ||
- (step && e->attrs->proto->flushing))
++ (step && e->attrs->src->proto->flushing))
{
if (*max_feed <= 0)
{
return 0;
}
- n->n.prefix, n->n.pxlen, e->attrs->proto->name, tab->name);
+ if (step)
+ log_rl(&rl_flush, L_WARN "Route %I/%d from %s still in %s after flush",
++ n->n.prefix, n->n.pxlen, e->attrs->src->proto->name, tab->name);
+
rte_discard(tab, e);
(*max_feed)--;
int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
- if (n->routes)
- d->net_counter++;
+
for(e=n->routes; e; e=e->next)
{
+ if (rte_is_filtered(e) != d->filtered)
+ continue;
+
struct ea_list *tmpa;
- struct proto *p0 = e->attrs->proto;
+ struct rte_src *src = e->attrs->src;
struct proto *p1 = d->export_protocol;
struct proto *p2 = d->show_protocol;
+
+ if (ia[0])
+ d->net_counter++;
d->rt_counter++;
ee = e;
rte_update_lock(); /* We use the update buffer for filtering */
- tmpa = p0->make_tmp_attrs ? p0->make_tmp_attrs(e, rte_update_pool) : NULL;
+ tmpa = make_tmp_attrs(e, rte_update_pool);
- ok = (d->filter == FILTER_ACCEPT || f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+ ok = f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT;
- if (p2 && p2 != p0) ok = 0;
+ if (p2 && p2 != src->proto) ok = 0;
if (ok && d->export_mode)
{
int ic;
int passive; /* Do not initiate outgoing connection */
int interpret_communities; /* Hardwired handling of well-known communities */
int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
+ int add_path; /* Use ADD-PATH extension [draft] */
+ int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */
unsigned connect_retry_time;
unsigned hold_time, initial_hold_time;
unsigned keepalive_time;
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
- SECONDARY, ADD, PATHS, RX, TX)
- SECONDARY, ALLOW, BFD)
++ SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX)
CF_GRAMMAR
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
| bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
+ | bgp_proto ADD PATHS RX ';' { BGP_CFG->add_path = ADD_PATH_RX; }
+ | bgp_proto ADD PATHS TX ';' { BGP_CFG->add_path = ADD_PATH_TX; }
+ | bgp_proto ADD PATHS bool ';' { BGP_CFG->add_path = $4 ? ADD_PATH_FULL : 0; }
+ | bgp_proto ALLOW LOCAL AS ';' { BGP_CFG->allow_local_as = -1; }
+ | bgp_proto ALLOW LOCAL AS expr ';' { BGP_CFG->allow_local_as = $5; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
+ | bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
;
CF_ADDTO(dynamic_attr, BGP_ORIGIN
static void
rip_rte_remove(net *net UNUSED, rte *rte)
{
- // struct proto *p = rte->attrs->proto;
+ #ifdef LOCAL_DEBUG
- struct proto *p = rte->attrs->proto;
++ struct proto *p = rte->attrs->src->proto;
CHK_MAGIC;
DBG( "rip_rte_remove: %p\n", rte );
+ #endif
rem_node( &rte->u.rip.garbage );
}