summaryrefslogtreecommitdiff
path: root/proto/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c76
-rw-r--r--proto/bgp/bgp.c1
-rw-r--r--proto/bgp/bgp.h1
3 files changed, 78 insertions, 0 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index d56c017d..d85afa8f 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -1312,6 +1312,82 @@ bgp_rte_better(rte *new, rte *old)
}
+int
+bgp_rte_mergable(rte *pri, rte *sec)
+{
+ struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
+ struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
+ eattr *x, *y;
+ u32 p, s;
+
+ /* Skip suppressed routes (see bgp_rte_recalculate()) */
+ if (pri->u.bgp.suppressed != sec->u.bgp.suppressed)
+ return 0;
+
+ /* RFC 4271 9.1.2.1. Route resolvability test */
+ if (!rte_resolvable(sec))
+ return 0;
+
+ /* Start with local preferences */
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_LOCAL_PREF));
+ p = x ? x->u.data : pri_bgp->cf->default_local_pref;
+ s = y ? y->u.data : sec_bgp->cf->default_local_pref;
+ if (p != s)
+ return 0;
+
+ /* RFC 4271 9.1.2.2. a) Use AS path lengths */
+ if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths)
+ {
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
+ p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
+ s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
+
+ if (p != s)
+ return 0;
+
+// if (DELTA(p, s) > pri_bgp->cf->relax_multipath)
+// return 0;
+ }
+
+ /* RFC 4271 9.1.2.2. b) Use origins */
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGIN));
+ p = x ? x->u.data : ORIGIN_INCOMPLETE;
+ s = y ? y->u.data : ORIGIN_INCOMPLETE;
+ if (p != s)
+ return 0;
+
+ /* RFC 4271 9.1.2.2. c) Compare MED's */
+ if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric ||
+ (bgp_get_neighbor(pri) == bgp_get_neighbor(sec)))
+ {
+ x = ea_find(pri->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ y = ea_find(sec->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC));
+ p = x ? x->u.data : pri_bgp->cf->default_med;
+ s = y ? y->u.data : sec_bgp->cf->default_med;
+ if (p != s)
+ return 0;
+ }
+
+ /* RFC 4271 9.1.2.2. d) Prefer external peers */
+ if (pri_bgp->is_internal != sec_bgp->is_internal)
+ return 0;
+
+ /* RFC 4271 9.1.2.2. e) Compare IGP metrics */
+ p = pri_bgp->cf->igp_metric ? pri->attrs->igp_metric : 0;
+ s = sec_bgp->cf->igp_metric ? sec->attrs->igp_metric : 0;
+ if (p != s)
+ return 0;
+
+ /* Remaining criteria are ignored */
+
+ return 1;
+}
+
+
+
static inline int
same_group(rte *r, u32 lpref, u32 lasn)
{
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index e48b643b..9e28b278 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1243,6 +1243,7 @@ bgp_init(struct proto_config *C)
P->feed_begin = bgp_feed_begin;
P->feed_end = bgp_feed_end;
P->rte_better = bgp_rte_better;
+ P->rte_mergable = bgp_rte_mergable;
P->rte_recalculate = c->deterministic_med ? bgp_rte_recalculate : NULL;
p->cf = c;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 446fc857..b6e80fe5 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -238,6 +238,7 @@ byte *bgp_attach_attr_wa(struct ea_list **to, struct linpool *pool, unsigned att
struct rta *bgp_decode_attrs(struct bgp_conn *conn, byte *a, uint len, struct linpool *pool, int mandatory);
int bgp_get_attr(struct eattr *e, byte *buf, int buflen);
int bgp_rte_better(struct rte *, struct rte *);
+int bgp_rte_mergable(rte *pri, rte *sec);
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
void bgp_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs);
int bgp_import_control(struct proto *, struct rte **, struct ea_list **, struct linpool *);