Commit 094d2bdb authored by Ondřej Zajíček's avatar Ondřej Zajíček

Implements ADD-PATH extension for BGP.

Allows to send and receive multiple routes for one network by one BGP
session. Also contains necessary core changes to support this (routing
tables accepting several routes for one network from one protocol).
It needs some more cleanup before merging to the master branch.
parent d760229a
......@@ -831,7 +831,7 @@ interpret(struct f_inst *what)
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->proto->name;
res.val.s = rta->src->proto->name;
break;
case T_PREFIX: /* Warning: this works only for prefix of network */
{
......
......@@ -11,6 +11,7 @@
#include <stdarg.h>
#include <string.h>
#include <strings.h>
int bsprintf(char *str, const char *fmt, ...);
int bvsprintf(char *str, const char *fmt, va_list args);
......
......@@ -219,6 +219,7 @@ proto_free_ahooks(struct proto *p)
p->main_ahook = NULL;
}
/**
* proto_config_new - create a new protocol configuration
* @pr: protocol the configuration will belong to
......@@ -791,11 +792,15 @@ proto_schedule_feed(struct proto *p, int initial)
/* Connect protocol to routing table */
if (initial && !p->proto->multitable)
{
p->main_source = rt_get_source(p, 0);
rt_lock_source(p->main_source);
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->in_limit = p->cf->in_limit;
p->main_ahook->out_limit = p->cf->out_limit;
proto_reset_limit(p->main_ahook->in_limit);
proto_reset_limit(p->main_ahook->out_limit);
}
......@@ -844,6 +849,8 @@ proto_flush_loop(void *unused UNUSED)
return;
}
rt_prune_sources();
again:
WALK_LIST(p, flush_proto_list)
if (p->flushing)
......@@ -1040,6 +1047,12 @@ proto_notify_state(struct proto *p, unsigned ps)
if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
proto_schedule_flush(p);
if (p->proto->multitable)
{
rt_unlock_source(p->main_source);
p->main_source = NULL;
}
neigh_prune(); // FIXME convert neighbors to resource?
rfree(p->pool);
p->pool = NULL;
......
......@@ -183,7 +183,7 @@ struct proto {
int (*reload_routes)(struct proto *);
/*
* Routing entry hooks (called only for rte's belonging to this protocol):
* Routing entry hooks (called only for routes belonging to this protocol):
*
* rte_recalculate Called at the beginning of the best route selection
* rte_better Compare two rte's and decide which one is better (1=first, 0=second).
......@@ -199,6 +199,7 @@ struct proto {
void (*rte_remove)(struct network *, struct rte *);
struct rtable *table; /* Our primary routing table */
struct rte_src *main_source; /* Primary route source */
struct announce_hook *main_ahook; /* Primary announcement hook */
struct announce_hook *ahooks; /* Announcement hooks for this protocol */
......
......@@ -237,10 +237,10 @@ void rt_unlock_table(rtable *);
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);
void rte_dump(rte *);
void rte_free(rte *);
......@@ -286,9 +286,18 @@ struct mpnh {
unsigned char weight;
};
struct rte_src {
struct rte_src *next; /* Hash chain */
struct proto *proto; /* Protocol the source is based on */
u32 private_id; /* Private ID, assigned by the protocol */
u32 global_id; /* Globally unique ID of the source */
unsigned uc; /* Use count */
};
typedef struct rta {
struct rta *next, **pprev; /* Hash chain */
struct proto *proto; /* Protocol instance that originally created the route */
struct rte_src *src; /* Route source that created the route */
unsigned uc; /* Use count */
byte source; /* Route source (RTS_...) */
byte scope; /* Route scope (SCOPE_... -- see ip.h) */
......@@ -403,6 +412,13 @@ typedef struct ea_list {
#define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id);
static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void);
eattr *ea_find(ea_list *, unsigned ea);
int ea_get_int(ea_list *, unsigned ea, int def);
void ea_dump(ea_list *);
......@@ -419,6 +435,7 @@ static inline int mpnh_same(struct mpnh *x, struct mpnh *y)
void rta_init(void);
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
static inline int rta_is_cached(rta *r) { return r->aflags & RTAF_CACHED; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
void rta__free(rta *r);
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
......
......@@ -58,9 +58,194 @@ pool *rta_pool;
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_SIZE_DEF 4
/* rte source hash */
static struct rte_src **src_table;
static u32 src_hash_order, src_hash_size, src_hash_count;
#define SRC_HASH_ORDER_DEF 6
#define SRC_HASH_ORDER_MAX 18
#define SRC_HASH_ORDER_MIN 10
struct protocol *attr_class_to_protocol[EAP_MAX];
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(rta_pool, 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)
{
......@@ -682,14 +867,14 @@ rta_alloc_hash(void)
static inline unsigned int
rta_hash(rta *a)
{
return (a->proto->hash_key ^ ipa_hash(a->gw) ^
return (((unsigned) 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 &&
......@@ -786,6 +971,7 @@ rta_lookup(rta *o)
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);
......@@ -805,6 +991,7 @@ rta__free(rta *a)
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);
......@@ -827,7 +1014,7 @@ rta_dump(rta *a)
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");
......@@ -895,6 +1082,7 @@ rta_init(void)
rta_slab = sl_new(rta_pool, sizeof(rta));
mpnh_slab = sl_new(rta_pool, sizeof(struct mpnh));
rta_alloc_hash();
rte_src_init();
}
/*
......
......@@ -48,29 +48,31 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
DBG("dev_if_notify: device shutdown: prefix not found\n");
return;
}
rte_update(p->table, n, p, p, NULL);
rte_update(p, n, NULL);
}
else if (c & IF_CHANGE_UP)
{
rta *a, A;
rta *a;
net *n;
rte *e;
DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip);
bzero(&A, sizeof(A));
A.proto = p;
A.source = RTS_DEVICE;
A.scope = SCOPE_UNIVERSE;
A.cast = RTC_UNICAST;
A.dest = RTD_DEVICE;
A.iface = ad->iface;
A.eattrs = NULL;
a = rta_lookup(&A);
rta a0 = {
.src = p->main_source,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
.cast = RTC_UNICAST,
.dest = RTD_DEVICE,
.iface = ad->iface
};
a = rta_lookup(&a0);
n = net_get(p->table, ad->prefix, ad->pxlen);
e = rte_get_temp(a);
e->net = n;
e->pflags = 0;
rte_update(p->table, n, p, p, e);
rte_update(p, n, e);
}
}
......
This diff is collapsed.
......@@ -621,12 +621,14 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
return -1;
}
/*
static void
bgp_init_prefix(struct fib_node *N)
{
struct bgp_prefix *p = (struct bgp_prefix *) N;
p->bucket_node.next = NULL;
}
*/
static int
bgp_compare_u32(const u32 *x, const u32 *y)
......@@ -870,30 +872,125 @@ bgp_free_bucket(struct bgp_proto *p, struct bgp_bucket *buck)
mb_free(buck);
}
/* Prefix hash table */
static inline u32 prefix_hash(ip_addr prefix, int pxlen, u32 path_id, u32 order)
{
u32 x = ipa_hash(prefix) + pxlen + path_id;
return (x * 2902958171u) >> (32 - order);
}
static inline u32 px_hash_size(struct bgp_proto *p)
{ return 1 << p->px_hash_order; }
void
bgp_init_prefix_table(struct bgp_proto *p, u32 order)
{
p->px_hash_count = 0;
p->px_hash_order = order;
p->prefix_table = mb_allocz(p->p.pool, px_hash_size(p) * sizeof(struct bgp_prefix *));
p->prefix_slab = sl_new(p->p.pool, sizeof(struct bgp_prefix));
}
static void
bgp_rehash_prefix_table(struct bgp_proto *p, int step)
{
struct bgp_prefix **old_tab, *px, *px_next;
u32 old_size, hash, i;
old_tab = p->prefix_table;
old_size = px_hash_size(p);
p->px_hash_order += step;
p->prefix_table = mb_allocz(p->p.pool, px_hash_size(p) * sizeof(struct bgp_prefix *));
for (i = 0; i < old_size; i++)
for (px = old_tab[i]; px; px = px_next)
{
px_next = px->next;
hash = prefix_hash(px->n.prefix, px->n.pxlen, px->path_id, p->px_hash_order);
px->next = p->prefix_table[hash];
p->prefix_table[hash] = px;
}
mb_free(old_tab);
}
static struct bgp_prefix *
bgp_get_prefix(struct bgp_proto *p, ip_addr prefix, int pxlen, u32 path_id)
{
struct bgp_prefix *bp;
u32 hash = prefix_hash(prefix, pxlen, path_id, p->px_hash_order);
for (bp = p->prefix_table[hash]; bp; bp = bp->next)
if (bp->n.pxlen == pxlen && ipa_equal(bp->n.prefix, prefix) && bp->path_id == path_id)
return bp;
bp = sl_alloc(p->prefix_slab);
bp->n.prefix = prefix;
bp->n.pxlen = pxlen;
bp->path_id = path_id;
bp->next = p->prefix_table[hash];
p->prefix_table[hash] = bp;
bp->bucket_node.next = NULL;
p->px_hash_count++;
if ((p->px_hash_count > px_hash_size(p)) && (p->px_hash_order < 18))
bgp_rehash_prefix_table(p, 1);
return bp;
}
void
bgp_free_prefix(struct bgp_proto *p, struct bgp_prefix *bp)
{
struct bgp_prefix **bpp;
u32 hash = prefix_hash(bp->n.prefix, bp->n.pxlen, bp->path_id, p->px_hash_order);
for (bpp = &p->prefix_table[hash]; *bpp; *bpp = (*bpp)->next)
if (*bpp == bp)
break;
*bpp = bp->next;
sl_free(p->prefix_slab, bp);
p->px_hash_count--;
if ((p->px_hash_count < (px_hash_size(p) / 4)) && (p->px_hash_order > 10))
bgp_rehash_prefix_table(p, -1);
}
void
bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs)
{
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_bucket *buck;
struct bgp_prefix *px;
rte *key;
u32 path_id;
DBG("BGP: Got route %I/%d %s\n", n->n.prefix, n->n.pxlen, new ? "up" : "down");
if (new)
{
key = new;
buck = bgp_get_bucket(p, n, attrs, new->attrs->source != RTS_BGP);
if (!buck) /* Inconsistent attribute list */
return;
}
else
{
key = old;
if (!(buck = p->withdraw_bucket))
{
buck = p->withdraw_bucket = mb_alloc(P->pool, sizeof(struct bgp_bucket));
init_list(&buck->prefixes);
}
}
px = fib_get(&p->prefix_fib, &n->n.prefix, n->n.pxlen);
path_id = p->add_path_tx ? key->attrs->src->global_id : 0;
px = bgp_get_prefix(p, n->n.prefix, n->n.pxlen, path_id);
if (px->bucket_node.next)
{
DBG("\tRemoving old entry.\n");
......@@ -1021,7 +1118,7 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
if (rr)
{
/* Handling route reflection, RFC 4456 */
struct bgp_proto *src = (struct bgp_proto *) e->attrs->proto;
struct bgp_proto *src = (struct bgp_proto *) e->attrs->src->proto;
a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID));
if (!a)
......@@ -1071,7 +1168,8 @@ bgp_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *
{
rte *e = *new;
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_proto *new_bgp = (e->attrs->proto->proto == &proto_bgp) ? (struct bgp_proto *) e->attrs->proto : NULL;
struct bgp_proto *new_bgp = (e->attrs->src->proto->proto == &proto_bgp) ?
(struct bgp_proto *) e->attrs->src->proto : NULL;
if (p == new_bgp) /* Poison reverse updates */
return -1;
......@@ -1110,7 +1208,7 @@ bgp_get_neighbor(rte *r)
if (e && as_path_get_first(e->u.ptr, &as))
return as;
else
return ((struct bgp_proto *) r->attrs->proto)->remote_as;
return ((struct bgp_proto *) r->attrs->src->proto)->remote_as;
}
static inline int
......@@ -1123,8 +1221,8 @@ rte_resolvable(rte *rt)
int
bgp_rte_better(rte *new, rte *old)
{
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->proto;
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->proto;
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
eattr *x, *y;
u32 n, o;
......@@ -1258,7 +1356,7 @@ same_group(rte *r, u32 lpref, u32 lasn)
static inline int
use_deterministic_med(rte *r)
{
struct proto *P = r->attrs->proto;
struct proto *P = r->attrs->src->proto;
return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
}
......@@ -1543,7 +1641,6 @@ bgp_decode_attrs(struct bgp_conn *conn, byte *attr, unsigned int len, struct lin
int withdraw = 0;
bzero(a, sizeof(rta));
a->proto = &bgp->p;
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
a->cast = RTC_UNICAST;
......@@ -1752,14 +1849,14 @@ bgp_get_attr(eattr *a, byte *buf, int buflen)
}
void
bgp_attr_init(struct bgp_proto *p)
bgp_init_bucket_table(struct bgp_proto *p)
{
p->hash_size = 256;
p->hash_limit = p->hash_size * 4;
p->bucket_hash = mb_allocz(p->p.pool, p->hash_size * sizeof(struct bgp_bucket *));
init_list(&p->bucket_queue);
p->withdraw_bucket = NULL;
fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix);
// fib_init(&p->prefix_fib, p->p.pool, sizeof(struct bgp_prefix), 0, bgp_init_prefix);
}
void
......
......@@ -362,7 +362,9 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->conn = conn;
p->last_error_class = 0;
p->last_error_code = 0;
bgp_attr_init(conn->bgp);
bgp_init_bucket_table(p);
bgp_init_prefix_table(p, 8);
bgp_conn_set_state(conn, BS_ESTABLISHED);
proto_notify_state(&p->p, PS_UP);
}
......@@ -410,8 +412,11 @@ static void
bgp_send_open(struct bgp_conn *conn)
{
conn->start_state = conn->bgp->start_state;
conn->want_as4_support = conn->bgp->cf->enable_as4 && (conn->start_state != BSS_CONNECT_NOCAP);
conn->peer_as4_support = 0; // Default value, possibly changed by receiving capability.
// Default values, possibly changed by receiving capabilities.
conn->peer_refresh_support = 0;
conn->peer_as4_support = 0;
conn->peer_add_path = 0;
conn->advertised_as = 0;
DBG("BGP: Sending open\n");
......@@ -920,19 +925,17 @@ get_igp_table(struct bgp_config *cf)
static struct proto *
bgp_init(struct proto_config *C)
{
struct bgp_config *c = (struct bgp_config *) C;
struct proto *P = proto_new(C, sizeof(struct bgp_proto));
struct bgp_config *c = (struct bgp_config *) C;
struct bgp_proto *p = (struct bgp_proto *) P;
P->accept_ra_types = c->secondary ? RA_ACCEPTED : RA_OPTIMAL;
P->rt_notify = bgp_rt_notify;
P->rte_better = bgp_rte_better;
P->import_control = bgp_import_control;
P->neigh_notify = bgp_neigh_notify;
P->reload_routes = bgp_reload_routes;
if (c->deterministic_med)
P->rte_recalculate = bgp_rte_recalculate;
P->rte_better = bgp_rte_better;
P->rte_recalculate = c->deterministic_med ? bgp_rte_recalculate : NULL;
p->cf = c;
p->local_as = c->local_as;
......@@ -1176,15 +1179,19 @@ bgp_show_proto_info(struct proto *P)
else if (P->proto_state == PS_UP)
{
cli_msg(-1006, " Neighbor ID: %R", p->remote_id);
cli_msg(-1006, " Neighbor caps: %s%s",
cli_msg(-1006, " Neighbor caps: %s%s%s%s",
c->peer_refresh_support ? " refresh" : "",
c->peer_as4_support ? " AS4" : "");
cli_msg(-1006, " Session: %s%s%s%s%s",
c->peer_as4_support ? " AS4" : "",
(c->peer_add_path & ADD_PATH_RX) ? " add-path-rx" : "",
(c->peer_add_path & ADD_PATH_TX) ? " add-path-tx" : "");
cli_msg(-1006, " Session: %s%s%s%s%s%s%s",
p->is_internal ? "internal" : "external",
p->cf->multihop ? " multihop" : "",
p->rr_client ? " route-reflector" : "",
p->rs_client ? " route-server" : "",
p->as4_session ? " AS4" : "");
p->as4_session ? " AS4" : "",
p->add_path_rx ? " add-path-rx" : "",
p->add_path_tx ? " add-path-tx" : "");
cli_msg(-1006, " Source address: %I", p->source_addr);
if (P->cf->in_limit)
cli_msg(-1006, " Route limit: %d/%d",
......
......@@ -43,6 +43,7 @@ struct bgp_config {
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] */
unsigned connect_retry_time;
unsigned hold_time, initial_hold_time;
unsigned keepalive_time;
......@@ -62,6 +63,11 @@ struct bgp_config {
#define GW_DIRECT 1
#define GW_RECURSIVE 2
#define ADD_PATH_RX 1
#define ADD_PATH_TX 2
#define ADD_PATH_FULL 3
struct bgp_conn {