This seems to work for normal eBGP. I havn't tested iBGP (inc iBGP mpath),
but have tested eBGP relaxed AS_PATH mpath. The latter seems to be broken,
but the brokenness seems to be extant, AFAICT.
This depends on patches I've previously sent, but I may not have sent the
latest versions.
-----8<---------8<---------8<---------8<---------8<---------8<----
* The BGP decision order in route selection generally needs to be
consistent across routers to avoid creating problems, such as loops
or oscillations. However, it is also the case that changing the
order in certain ways could improve BGP. At present, adding options
that change the order is risky, as enabling it could easily lead to
mixed orders across routers and problems - potentially subtle and
hard to debug.
It would therefore be safer to exchange information on the order
being used, and allow speakers to terminate connections with
incompatible peers.
This commit introduces a SELECT_ORDER capability, which sends an
ordered array of code-points that describe the decision order, when
the order has been changed from the default.
If a peer sends this capability, it is checked against the local
configuration, and the session refused if one is not at least a
matching prefix of the other.
If a peer does not send this capability, and the local configuration
is not the default, then the session is also refused.
* bgpd.h: (enum bgp_select_order_kinds) code-points to describe
the kind of components there can be in the BGP decision order.
(struct bgp) Add a BGP_FLAG_SELECT_ORDER_NON_DEFAULT flag, unset by
default of course.
Add a (struct bgp_select_order) field to store the current local order.
(struct peer) Add flags to record RCV/ADV status of the SELECT_ORDER
cap. and a pointer to dynamic storage for the received
selection-order.
* bgpd.c: (bgp_select_order_default) set the select_order struct according
to the default order.
(bgp_select_order_str) Helper, print out the select_order struct as a
string.
(bgp_select_order_kind2str) map specific select order check/kind to
str.
(bgp_select_order_str2kind) and reverse to previous. These are for the
'bgp bestpath order ...' command.
(bgp_select_order_same) Comparison func helper - strict.
(bgp_select_order_aspath_ignore) Set an order specific to 'bgp
bestpath aspath-ignore'
(bgp_select_order_cmp_router_id) ditto, but for 'compare router-id'.
(bgp_select_order_aspath_confed) ditto, for 'bgp bestpath aspath
confed'
(bgp_select_order_install) Install the given select-order, checking
to see if it is actually different to the current one. If different,
notify all peers and close down sessions. (bgp_config_write) Write
out 'bgp bestpath order ..'.
(bgp_create) init the stored local select-order to the default.
* bgp_open.h: Add a private vendor capability for SELECT_ORDER for now.
(bgp_capability_select_order_kinds_vty_out) print out of a select order
exported for bgp_vty.c.
* bgp_open.c: (cap_select_order_str) kind -> str mapping table.
(bgp_capability_select_order_kinds_vty_out) see above.
(bgp_capability_select_order) parse SELECT_ORDER cap and indicate
compatibility. Bad capability is taken as that, as is the received
and local orders not sharing an identical prefix. The received order
is stored in the (struct peer) for debug/printing.
(bgp_capability_parse) Act on the SELECT_ORDER cap and call previous.
(bgp_open_capability) Send the local order, when not the default.
(bgp_capability_select_order_size) little helper for the protocol
capability size.
(bgp_capability_select_order_write) helper to write out the protocol
capability, for normal OPEN case and for NOTIFY error data.
(bgp_select_order_notify) NOTIFICATION helper. Bit unclear what to
do if we're NOTIFYing because we have sent a non-default select
order, and other side sent nothing - what capability do we send? And
if both? Have chosen to err on the side of sending each side's
capability, where available.
(bgp_open_option_parse) Add SELECT_ORDER. After options are parsed,
check the SELECT_ORDER status and notify on mismatch.
(cap_modsizes) Add SELECT_ORDER
* bgp_route.c: (bgp_info_cmp_funcs) Table to map SELECT_ORDER kinds to
its info_cmp sub-func.
(bgp_info_cmp) Get rid of the hard-coded order, other than for some leading
local checks, and trailing fallback & local tie-breakers. Instead us the
previous table to call the functions. Unfortunately, not completely regular
cause of mpath.
* bgp_vty.c: (bgp_show_peer) print out SELECT_ORDER cap status.
(bgp_bestpath_compare_router_id) No need for flag, just set a
'compare router-id' specific order.
(bgp_bestpath_aspath_ignore) similar change.
(bgp_bestpath_aspath_confed) ditto.
(no_bgp_bestpath_{compare_router_id,aspath_ignore,aspath_confed) Just
install the default order.
(bgp_bestpath_order_cmd) New command to specify the route selection
check order. Unfortunately, the command parser doesn't have a way to
specify an ordered list be accepted at this time, so .LINE has to be
used.
* tests/bgp_capability_test.c: Test cases for the SELECT_ORDER open
capability parser.
bgp bestpath order WIP
---
bgpd/bgp_open.c | 203 +++++++++++++++++++++++++++++++++++++++++-
bgpd/bgp_open.h | 4 +
bgpd/bgp_route.c | 120 ++++++++++++-------------
bgpd/bgp_vty.c | 131 ++++++++++++++++++++++++---
bgpd/bgpd.c | 211 ++++++++++++++++++++++++++++++++++++++++++--
bgpd/bgpd.h | 59 ++++++++++---
lib/memtypes.c | 1 +
tests/bgp_capability_test.c | 62 +++++++++++++
8 files changed, 702 insertions(+), 89 deletions(-)
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 2ebcd04..f041162 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -416,6 +416,139 @@ bgp_capability_as4 (struct peer *peer, struct
capability_header *hdr)
return as4;
}
+#define ENTRY(T,S) [T] = { T, S }
+static const struct message cap_select_order_str [] =
+{
+ ENTRY(BGP_SELECT_ORDER_LOCAL_PREF, "Local-Pref"),
+ ENTRY(BGP_SELECT_ORDER_AS_PATH, "AS-Path"),
+ ENTRY(BGP_SELECT_ORDER_ORIGIN, "Origin"),
+ ENTRY(BGP_SELECT_ORDER_MED, "MED"),
+ ENTRY(BGP_SELECT_ORDER_EXTERNAL, "External/Internal"),
+ ENTRY(BGP_SELECT_ORDER_IGP_COST, "IGP Cost"),
+ ENTRY(BGP_SELECT_ORDER_ROUTER_ID, "Router-ID"),
+ ENTRY(BGP_SELECT_ORDER_CLUSTER_LIST_LEN, "Cluster-List Length"),
+ ENTRY(BGP_SELECT_ORDER_MAX, NULL),
+};
+#undef ENTRY
+static const int cap_select_order_str_max = array_size(cap_select_order_str);
+
+void
+bgp_capability_select_order_kinds_vty_out (struct vty *vty,
+ struct bgp_select_order *so)
+{
+ for (int i = 1; i < so->num; i++)
+ vty_out (vty, " %s%s",
+ LOOKUP(cap_select_order_str, so->kinds[i]), VTY_NEWLINE);
+}
+
+/* Helper to calculate the size of the capability data */
+static size_t
+bgp_capability_select_order_size (struct bgp_select_order *so)
+{
+ return (2 + so->num * sizeof(uint16_t));
+}
+
+static bool
+bgp_capability_select_order (struct peer *peer, struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ uint8_t num;
+
+ SET_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_RCV);
+
+ if (hdr->length % 2 != 0)
+ {
+ zlog_err ("%s SELECT_ORDER capability length is odd %d",
+ peer->host, hdr->length);
+ return false;
+ }
+
+ /* let num be the number of kinds */
+ num = hdr->length >> 1;
+
+ peer->selection_order
+ = XREALLOC(MTYPE_BGP_SELECT_ORDER, peer->selection_order,
+ sizeof(struct bgp_select_order)
+ + MAX(num * sizeof(enum bgp_select_order_kinds),
+ BGP_SELECT_ORDER_MAX));
+ peer->selection_order->num = num;
+
+ for (int i = 0; i < MIN(num, peer->bgp->selection_order.num); i++)
+ peer->selection_order->kinds[i] = stream_getw (s);
+
+ return true;
+}
+
+static size_t
+bgp_capability_select_order_write (struct stream *s,
+ struct bgp_select_order *so)
+{
+ size_t solen = so->num * sizeof(uint16_t);
+
+ stream_putc (s, CAPABILITY_CODE_SELECT_ORDER);
+ stream_putc (s, solen);
+ for (int i = 0; i < so->num; i++)
+ stream_putw (s, so->kinds[i]);
+ return solen;
+}
+
+
+static size_t
+bgp_select_order_notify (struct peer *peer,
+ uint8_t *error_data, size_t len)
+{
+ struct stream *s = NULL;
+
+ /* RFC5492 states:
+ *
+ * "If a BGP speaker that supports a certain capability determines that
+ * its peer doesn't support this capability, the speaker MAY send a
+ * NOTIFICATION message to the peer and terminate peering (see Section
+ * "Extensions to Error Handling" for more details). ... The Error
+ * Subcode in the NOTIFICATION message is then set to Unsupported
+ * Capability. The message MUST contain the capability or capabilities
+ * that cause the speaker to send the message."
+ *
+ * The issue is, whose capability should be sent? If the other side
+ * hasn't sent, but we're non-default, then do we send our capability
+ * again in the error code? If both sides have sent and they're
+ * incompatible, do we send only theirs or both?
+ *
+ * Erring on the side of sending all we can.
+ */
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT))
+ {
+ struct bgp_select_order *so = &peer->bgp->selection_order;
+
+ if (s == NULL)
+ s = stream_new (bgp_capability_select_order_size (so));
+
+ bgp_capability_select_order_write (s, so);
+ memcpy (error_data + len, stream_pnt (s), stream_get_endp (s));
+ len += stream_get_endp (s);
+ }
+ if (CHECK_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_RCV))
+ {
+ struct bgp_select_order *so = peer->selection_order;
+
+ if (s == NULL)
+ s = stream_new (bgp_capability_select_order_size (so));
+ if (STREAM_WRITEABLE(s) < bgp_capability_select_order_size (so))
+ stream_resize (s, STREAM_SIZE(s)
+ + bgp_capability_select_order_size (so));
+
+ bgp_capability_select_order_write (s, so);
+ memcpy (error_data + len, stream_pnt (s), stream_get_endp (s));
+ len += stream_get_endp (s);
+ }
+
+ bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data, len);
+ stream_free (s);
+ return len;
+}
+
static const struct message capcode_str[] =
{
{ CAPABILITY_CODE_MP, "MultiProtocol Extensions"
},
@@ -426,6 +559,7 @@ static const struct message capcode_str[] =
{ CAPABILITY_CODE_DYNAMIC, "Dynamic" },
{ CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)" },
{ CAPABILITY_CODE_ORF_OLD, "ORF (Old)" },
+ { CAPABILITY_CODE_SELECT_ORDER, "Decision Selection Order" },
};
static const int capcode_str_max = array_size(capcode_str);
@@ -440,6 +574,7 @@ static const size_t cap_minsizes[] =
[CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN,
[CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN,
[CAPABILITY_CODE_ORF_OLD] = sizeof (struct capability_orf_entry),
+ [CAPABILITY_CODE_SELECT_ORDER] = CAPABILITY_CODE_SELECT_ORDER_DATA_MINLEN,
};
/* value the capability must be a multiple of.
@@ -457,6 +592,7 @@ static const size_t cap_modsizes[] =
[CAPABILITY_CODE_DYNAMIC] = 1,
[CAPABILITY_CODE_REFRESH_OLD] = 1,
[CAPABILITY_CODE_ORF_OLD] = 1,
+ [CAPABILITY_CODE_SELECT_ORDER] = 2,
};
/**
@@ -519,6 +655,7 @@ bgp_capability_parse (struct peer *peer, size_t length, int
*mp_capability,
case CAPABILITY_CODE_RESTART:
case CAPABILITY_CODE_AS4:
case CAPABILITY_CODE_DYNAMIC:
+ case CAPABILITY_CODE_SELECT_ORDER:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code])
{
@@ -601,7 +738,11 @@ bgp_capability_parse (struct peer *peer, size_t length,
int *mp_capability,
*/
if (!bgp_capability_as4 (peer, &caphdr))
return -1;
- break;
+ break;
+ case CAPABILITY_CODE_SELECT_ORDER:
+ if (!bgp_capability_select_order (peer, &caphdr))
+ return -1;
+ break;
default:
if (caphdr.code > 128)
{
@@ -860,6 +1001,52 @@ bgp_open_option_parse (struct peer *peer, u_char length,
int *mp_capability)
return -1;
}
}
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT)
+ || CHECK_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_RCV))
+ {
+ size_t len = error - error_data;
+
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT)
+ && CHECK_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_RCV))
+ for (int i = 0;
+ i < MIN(peer->selection_order->num,
peer->bgp->selection_order.num);
+ i++)
+ {
+ /* Both sides are non-default. The common prefix should be
+ * identical at least, for the speakers to be compatible.
+ * If one side has further tie-breakers, we'll ignore that.
+ *
+ * Strictly speaking, not guaranteed such further decisions on
+ * one side can cause no problems. However, for sanity, lets
+ * assume all speakers implement a sufficient set of decisions
+ * to resolve between routes in all cases worth caring about.
+ * So assume that speakers that implement further tie-breakers
+ * are wasting their time, and that those will never be hit for
+ * practical purposes.
+ */
+ if (peer->bgp->selection_order.kinds[i] !=
+ peer->selection_order->kinds[i])
+ {
+ size_t nlen;
+ nlen = bgp_select_order_notify (peer, error_data, len);
+
+ error += nlen - len;
+
+ return -1;
+ }
+ }
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT)
+ && !CHECK_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_RCV))
+ {
+ size_t nlen;
+
+ nlen = bgp_select_order_notify (peer, error_data, len);
+
+ error += nlen - len;
+
+ return -1;
+ }
+ }
return 0;
}
@@ -1056,6 +1243,20 @@ bgp_open_capability (struct stream *s, struct peer *peer)
stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN);
}
+ /* Route decision selection order capability. */
+ if (bgp_flag_check (peer->bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT))
+ {
+ size_t solen;
+ size_t caplenp;
+
+ SET_FLAG (peer->cap, PEER_CAP_SELECT_ORDER_ADV);
+ stream_putc (s, BGP_OPEN_OPT_CAP);
+ caplenp = stream_get_endp (s);
+ stream_putc (s, 0);
+ solen = bgp_capability_select_order_write (s, &peer->bgp->selection_order);
+ stream_putc_at (s, caplenp, 2 + solen);
+ }
+
/* Sending base graceful-restart capability irrespective of the config */
SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV);
stream_putc (s, BGP_OPEN_OPT_CAP);
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 2b1382d..fc9cd28 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -75,6 +75,7 @@ struct capability_gr
#define CAPABILITY_CODE_DYNAMIC 66 /* Dynamic Capability */
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering
Capability(cisco) */
+#define CAPABILITY_CODE_SELECT_ORDER 131 /* Route Selection Decision Order
Capability */
/* Capability Length */
#define CAPABILITY_CODE_MP_LEN 4
@@ -82,6 +83,7 @@ struct capability_gr
#define CAPABILITY_CODE_DYNAMIC_LEN 0
#define CAPABILITY_CODE_RESTART_LEN 2 /* Receiving only case */
#define CAPABILITY_CODE_AS4_LEN 4
+#define CAPABILITY_CODE_SELECT_ORDER_DATA_MINLEN 0
/* Cooperative Route Filtering Capability. */
@@ -105,6 +107,8 @@ struct capability_gr
extern int bgp_open_option_parse (struct peer *, u_char, int *);
extern void bgp_open_capability (struct stream *, struct peer *);
extern void bgp_capability_vty_out (struct vty *, struct peer *);
+extern void bgp_capability_select_order_kinds_vty_out (struct vty *,
+ struct bgp_select_order
*);
extern as_t peek_for_as4_capability (struct peer *, u_char);
extern int bgp_afi_safi_valid_indices (afi_t, safi_t *);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 1c04afb42..9d04b85 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -321,7 +321,8 @@ bgp_med_value (struct attr *attr, struct bgp *bgp)
}
}
-/* -1 if i1 is preferred, 1 if i2, 0 otherwise */
+/* Local preference check.
+ * -1 if i1 is preferred, 1 if i2, 0 otherwise */
static int
bgp_info_cmp_local_pref (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -346,6 +347,7 @@ bgp_info_cmp_local_pref (struct bgp *bgp,
return 0;
}
+/* Local route type check: Prefer local over received */
static int
bgp_info_cmp_route_type (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -362,7 +364,9 @@ bgp_info_cmp_route_type (struct bgp *bgp,
return 0;
}
-/* Depends on !bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE)
+
+/* AS_PATH length check, with confed ASes counted.
+ Depends on !bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE)
&& bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED) */
static int
bgp_info_cmp_aspath_confed (struct bgp *bgp,
@@ -383,7 +387,8 @@ bgp_info_cmp_aspath_confed (struct bgp *bgp,
return 0;
}
-/* depends on !bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE) */
+/* AS path length check
+ depends on !bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE) */
static int
bgp_info_cmp_aspath (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -402,6 +407,7 @@ bgp_info_cmp_aspath (struct bgp *bgp,
return 0;
}
+/* Origin check. */
static int
bgp_info_cmp_origin (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -454,6 +460,7 @@ bgp_info_cmp_med (struct bgp *bgp,
return 0;
}
+/* Peer type check: Prefer routes received from external peer */
static int
bgp_info_cmp_peer_type (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -470,6 +477,7 @@ bgp_info_cmp_peer_type (struct bgp *bgp,
return 0;
}
+/* IGP metric check. */
static int
bgp_info_cmp_igp_metric (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -489,8 +497,11 @@ bgp_info_cmp_igp_metric (struct bgp *bgp,
return 0;
}
-/* Bit special this one. Returns 0 if the 2 paths are the same for ECMP
- * purposes, and !0 otherwise. See also comment on call to this below.
+/* Equality for Multipath check
+ *
+ * Bit special/different this one. Returns 0 if the 2 paths are the same
+ * for ECMP purposes, and !0 otherwise. See also comment on call to this
+ * below.
*
* internal behaviour varies on:
* bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)
@@ -533,8 +544,8 @@ bgp_info_cmp_mpath (struct bgp *bgp,
it appears to be selecting the newer, not the older.
*/
static int
-bgp_info_cmp_external_age (struct bgp *bgp,
- struct bgp_info *i1, struct bgp_info *i2)
+bgp_info_cmp_already_selected (struct bgp *bgp,
+ struct bgp_info *i1, struct bgp_info *i2)
{
if (i1->peer->sort == BGP_PEER_EBGP
&& i2->peer->sort == BGP_PEER_EBGP)
@@ -547,7 +558,7 @@ bgp_info_cmp_external_age (struct bgp *bgp,
return 0;
}
- /* 11. Remote ID comparision. Originator-ID or else peer Router-ID */
+ /* Remote ID comparision. Originator-ID or else peer Router-ID */
static int
bgp_info_cmp_remote_id (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -580,6 +591,7 @@ bgp_info_cmp_remote_id (struct bgp *bgp,
return 0;
}
+/* Cluster length comparision. */
static int
bgp_info_cmp_cluster_len (struct bgp *bgp,
struct bgp_info *i1, struct bgp_info *i2)
@@ -604,6 +616,27 @@ bgp_info_cmp_cluster_len (struct bgp *bgp,
return 0;
}
+typedef int (*bgp_info_cmp_sub_func) (struct bgp *, struct bgp_info *, struct
bgp_info *);
+
+/* Map SELECT_ORDER enums to the appropriate info_cmp function */
+bgp_info_cmp_sub_func bgp_info_cmp_funcs[] =
+#define ENT(K,F) [BGP_SELECT_ORDER_ ## K] = bgp_info_cmp_ ## F
+{
+ ENT(LOCAL_PREF, local_pref),
+ ENT(LOCAL_ROUTE, route_type),
+ ENT(AS_PATH, aspath),
+ ENT(AS_PATH_CONFED, aspath_confed),
+ ENT(ORIGIN, origin),
+ ENT(MED, med),
+ ENT(EXTERNAL, peer_type),
+ ENT(IGP_COST, igp_metric),
+ ENT(MPATH_EQUAL, mpath),
+ ENT(ALREADY_SELECTED, already_selected),
+ ENT(ROUTER_ID, remote_id),
+ ENT(CLUSTER_LIST_LEN, cluster_len),
+};
+#undef ENT
+
/* Compare two bgp route entity. Return -1 if new is preferred, 1 if exist
* is preferred, or 0 if they are the same (usually will only occur if
* multipath is enabled */
@@ -650,67 +683,28 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new,
struct bgp_info *exist,
if (new_weight < exist_weight)
return 1;
- /* 2. Local preference check. */
- BGP_INFO_CMP_SUB(bgp, local_pref, new, exist);
-
- /* 3. Local route check. We prefer:
- * - BGP_ROUTE_STATIC
- * - BGP_ROUTE_AGGREGATE
- * - BGP_ROUTE_REDISTRIBUTE
+ /* Act on the core, mostly non-local route selection metrics, that can
+ * be carried out
*/
- BGP_INFO_CMP_SUB(bgp, route_type, new, exist);
-
- /* 4. AS path length check. */
- if (! bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
+ for (int i = 0; i < bgp->selection_order.num; i++)
{
- if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
- {
- BGP_INFO_CMP_SUB(bgp, aspath_confed, new, exist);
- }
- else
+ enum bgp_select_order_kinds k = bgp->selection_order.kinds[i];
+ bgp_info_cmp_sub_func cmpf = bgp_info_cmp_funcs[k];
+
+ switch (k)
{
- BGP_INFO_CMP_SUB(bgp, aspath, new, exist);
+ /* mpath is special, unfortunately */
+ case BGP_SELECT_ORDER_MPATH_EQUAL:
+ if (bgp_mpath_is_configured (bgp, afi, safi))
+ if (cmpf (bgp, new, exist) == 0)
+ return 0;
+ break;
+ default:
+ if ((cmpret = cmpf (bgp, new, exist)) != 0)
+ return cmpret;
}
}
-
- /* 5. Origin check. */
- BGP_INFO_CMP_SUB(bgp, origin, new, exist);
-
- /* 6. MED check. */
- BGP_INFO_CMP_SUB(bgp, med, new, exist);
-
- /* 7. Peer type check. */
- BGP_INFO_CMP_SUB(bgp, peer_type, new, exist);
-
- /* 8. IGP metric check. */
- BGP_INFO_CMP_SUB(bgp, igp_metric, new, exist);
-
- /* 9. Maximum path check. */
- /* Assumes AS_PATHs are equal length, only interested in whether it
- * considers them as same, to set the out param and signal to caller.
- *
- * mpath is still irregular unfortunately.
- */
- if (bgp_mpath_is_configured (bgp, afi, safi))
- if (bgp_info_cmp_mpath (bgp, new, exist) == 0)
- return 0;
-
- /* 10. Local: If both paths are external, prefer the path that was
- received first (the oldest one). This step minimizes route-flap, since
- a newer path won't displace an older one, even if it was the preferred
- route based on the additional decision criteria below.
- */
- if (! bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID))
- {
- BGP_INFO_CMP_SUB(bgp, external_age, new, exist);
- }
-
- /* 11. Router-ID comparision. */
- BGP_INFO_CMP_SUB(bgp, remote_id, new, exist);
- /* 12. Cluster length comparision. */
- BGP_INFO_CMP_SUB(bgp, cluster_len, new, exist);
-
/* 13. Neighbor address comparision. */
/* Do this only if neither path is "stale" as stale paths do not have
* valid peer information (as the connection may or may not be up).
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 4fd255f..00c95c0 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1085,9 +1085,12 @@ DEFUN (bgp_bestpath_compare_router_id,
"Compare router-id for identical EBGP paths\n")
{
struct bgp *bgp;
-
+ struct bgp_select_order so;
+
bgp = vty->index;
- bgp_flag_set (bgp, BGP_FLAG_COMPARE_ROUTER_ID);
+ bgp_select_order_cmp_router_id (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1100,9 +1103,12 @@ DEFUN (no_bgp_bestpath_compare_router_id,
"Compare router-id for identical EBGP paths\n")
{
struct bgp *bgp;
+ struct bgp_select_order so;
bgp = vty->index;
- bgp_flag_unset (bgp, BGP_FLAG_COMPARE_ROUTER_ID);
+ bgp_select_order_default (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1116,9 +1122,12 @@ DEFUN (bgp_bestpath_aspath_ignore,
"Ignore as-path length in selecting a route\n")
{
struct bgp *bgp;
+ struct bgp_select_order so;
bgp = vty->index;
- bgp_flag_set (bgp, BGP_FLAG_ASPATH_IGNORE);
+ bgp_select_order_aspath_ignore (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1132,9 +1141,13 @@ DEFUN (no_bgp_bestpath_aspath_ignore,
"Ignore as-path length in selecting a route\n")
{
struct bgp *bgp;
+ struct bgp_select_order so;
bgp = vty->index;
- bgp_flag_unset (bgp, BGP_FLAG_ASPATH_IGNORE);
+
+ bgp_select_order_default (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1148,9 +1161,12 @@ DEFUN (bgp_bestpath_aspath_confed,
"Compare path lengths including confederation sets & sequences in
selecting a route\n")
{
struct bgp *bgp;
-
+ struct bgp_select_order so;
+
bgp = vty->index;
- bgp_flag_set (bgp, BGP_FLAG_ASPATH_CONFED);
+ bgp_select_order_aspath_confed (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1164,9 +1180,12 @@ DEFUN (no_bgp_bestpath_aspath_confed,
"Compare path lengths including confederation sets & sequences in
selecting a route\n")
{
struct bgp *bgp;
-
+ struct bgp_select_order so;
bgp = vty->index;
- bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED);
+
+ bgp_select_order_default (&so);
+ bgp_select_order_install (bgp, &so);
+
return CMD_SUCCESS;
}
@@ -1230,6 +1249,79 @@ DEFUN (no_bgp_log_neighbor_changes,
return CMD_SUCCESS;
}
+/* There's no way at present to specify the command parser should accept
+ * an ordered set of options
+ */
+#define BGP_BESTPATH_ORDER_OPTS \
+ "default" \
+ "local-pref" \
+ "local-route" \
+ "as-path" \
+ "as-path-confed" \
+ "origin" \
+ "med" \
+ "external" \
+ "igp-cost" \
+ "mpath" \
+ "already-selected" \
+ "router-id" \
+ "cluster-list"
+#define BGP_BESTPATH_ORDER_OPTS_DESC \
+ "Set the default order (other args are then ignored)\n" \
+ "Highest Local-Pref preference check\n" \
+ "Prefer locally originated routes over received routes\n" \
+ "Shortest AS_PATH length preference check\n" \
+ "Shortest AS_PATH length (inc. confed ASes) preference check\n" \
+ "Lowest ORIGIN preference check\n" \
+ "Lowest applicable MED preference check\n" \
+ "Prefer route received from eBGP peer over one from an iBGP peer\n" \
+ "Lowest IGP metric preference check\n" \
+ "Equal preference for multi-path check\n" \
+ "Older selected external route preference check\n" \
+ "Lowest originating Router-ID preference check\n" \
+ "Cluster-List length\n"
+
+DEFUN (bgp_bestpath_order,
+ bgp_bestpath_order_cmd,
+ "bgp bestpath order .LINE",
+ "BGP specific commands\n"
+ "Change the default bestpath selection\n"
+ "Change the relative ordering of checks\n"
+ "Ordered list of the checks to make\n")
+{
+ struct bgp *bgp;
+ bgp = vty->index;
+
+ struct bgp_select_order so;
+
+ if (strcmp(argv[0], "default") == 0)
+ {
+ bgp_select_order_default (&so);
+ bgp_select_order_install (bgp, &so);
+ return CMD_SUCCESS;
+ }
+
+ for (int i = 0; i < argc; i++)
+ {
+ enum bgp_select_order_kinds k = bgp_select_order_str2kind (argv[i]);
+
+ if (k == 0)
+ {
+ zlog_warn ("%% Unrecognised check %s, shouldn't happen!%s",
+ argv[i], VTY_NEWLINE);
+ vty_out (vty, "%% Unrecognised check %s, shouldn't happen!%s",
+ argv[i], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ so.kinds[i] = k;
+ }
+ so.num = argc;
+ bgp_select_order_install (bgp, &so);
+
+ return CMD_SUCCESS;
+}
+
+
/* "bgp bestpath med" configuration. */
DEFUN (bgp_bestpath_med,
bgp_bestpath_med_cmd,
@@ -7747,6 +7839,22 @@ bgp_show_peer (struct vty *vty, struct peer *p)
vty_out (vty, "%s", VTY_NEWLINE);
}
}
+ if (CHECK_FLAG (p->cap, PEER_CAP_SELECT_ORDER_RCV)
+ || CHECK_FLAG (p->cap, PEER_CAP_SELECT_ORDER_ADV))
+ {
+ vty_out (vty, " Route Selection Order Capabilty:");
+ if (CHECK_FLAG (p->cap, PEER_CAP_SELECT_ORDER_ADV))
+ vty_out (vty, " advertised");
+ if (CHECK_FLAG (p->cap, PEER_CAP_SELECT_ORDER_RCV))
+ vty_out (vty, " %sreceived",
+ CHECK_FLAG (p->cap, PEER_CAP_SELECT_ORDER_ADV)
+ ? "and " : "");
+ vty_out (vty, "%s", VTY_NEWLINE);
+ if (bgp_flag_check (bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT))
+ bgp_capability_select_order_kinds_vty_out (vty,
&bgp->selection_order);
+
+ }
+
}
}
@@ -9245,7 +9353,10 @@ bgp_vty_init (void)
install_element (BGP_NODE, &no_bgp_bestpath_med_cmd);
install_element (BGP_NODE, &no_bgp_bestpath_med2_cmd);
install_element (BGP_NODE, &no_bgp_bestpath_med3_cmd);
-
+
+ /* 'bgp bestpath order ...' command */
+ install_element (BGP_NODE, &bgp_bestpath_order_cmd);
+
/* "no bgp default ipv4-unicast" commands. */
install_element (BGP_NODE, &no_bgp_default_ipv4_unicast_cmd);
install_element (BGP_NODE, &bgp_default_ipv4_unicast_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 90e77f2..97fc9bf 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -740,6 +740,9 @@ peer_free (struct peer *peer)
if (peer->notify.data)
XFREE(MTYPE_TMP, peer->notify.data);
+ if (peer->selection_order)
+ XFREE(MTYPE_BGP_SELECT_ORDER, peer->selection_order);
+
bgp_sync_delete (peer);
memset (peer, 0, sizeof (struct peer));
@@ -1936,6 +1939,198 @@ bgp_startup_timer_expire (struct thread *thread)
return 0;
}
+size_t
+bgp_select_order_str (struct bgp_select_order *so, char *buf, size_t len)
+{
+ size_t writen = 0;
+
+ for (int i = 0; i < so->num; i++)
+ {
+ writen += snprintf (buf + writen, len - writen,
+ "%s%s",
+ (i > 0 ? " " : ""),
+ bgp_select_order_kind2str (so->kinds[i]));
+ }
+ return writen;
+}
+
+const char *
+bgp_select_order_kind2str (enum bgp_select_order_kinds kind)
+{
+ const char *strs[BGP_SELECT_ORDER_MAX] =
+ {
+ [BGP_SELECT_ORDER_LOCAL_PREF] = "local-pref",
+ [BGP_SELECT_ORDER_LOCAL_ROUTE] = "local-route",
+ [BGP_SELECT_ORDER_AS_PATH] = "as-path",
+ [BGP_SELECT_ORDER_AS_PATH_CONFED] = "as-path-confed",
+ [BGP_SELECT_ORDER_ORIGIN] = "origin",
+ [BGP_SELECT_ORDER_MED] = "med",
+ [BGP_SELECT_ORDER_EXTERNAL] = "external",
+ [BGP_SELECT_ORDER_IGP_COST] = "igp-cost",
+ [BGP_SELECT_ORDER_MPATH_EQUAL] = "mpath",
+ [BGP_SELECT_ORDER_ALREADY_SELECTED] = "already-selected",
+ [BGP_SELECT_ORDER_ROUTER_ID] = "router-id",
+ [BGP_SELECT_ORDER_CLUSTER_LIST_LEN] = "cluster-list",
+ };
+ if (kind >= BGP_SELECT_ORDER_MAX && kind == 0)
+ return NULL;
+ return strs[kind];
+}
+
+enum bgp_select_order_kinds
+bgp_select_order_str2kind (const char *const str)
+{
+#define CHK(S,E) \
+ if (strcasecmp (str, (S)) == 0) \
+ return BGP_SELECT_ORDER_ ## E
+
+ CHK("local-pref", LOCAL_PREF);
+ CHK("local-route", LOCAL_ROUTE);
+ CHK("as-path", AS_PATH);
+ CHK("as-path-confed", AS_PATH_CONFED);
+ CHK("origin", ORIGIN);
+ CHK("med", MED);
+ CHK("external", EXTERNAL);
+ CHK("igp-cost", IGP_COST);
+ CHK("mpath", MPATH_EQUAL);
+ CHK("already-selected",ALREADY_SELECTED);
+ CHK("router-id", ROUTER_ID);
+ CHK("cluster-list", CLUSTER_LIST_LEN);
+ return 0;
+#undef CHK
+}
+
+static bool
+bgp_select_order_same (struct bgp_select_order *so1,
+ struct bgp_select_order *so2)
+{
+ if ((so1->num == so2->num)
+ && (memcmp (so1->kinds, so1->kinds,
+ sizeof(enum bgp_select_order_kinds) * so1->num) == 0))
+ return true;
+ return false;
+}
+
+void
+bgp_select_order_default (struct bgp_select_order *so)
+{
+ enum bgp_select_order_kinds kinds[] =
+ {
+ BGP_SELECT_ORDER_LOCAL_PREF,
+ BGP_SELECT_ORDER_LOCAL_ROUTE,
+ BGP_SELECT_ORDER_AS_PATH,
+ BGP_SELECT_ORDER_ORIGIN,
+ BGP_SELECT_ORDER_MED,
+ BGP_SELECT_ORDER_EXTERNAL,
+ BGP_SELECT_ORDER_IGP_COST,
+ BGP_SELECT_ORDER_MPATH_EQUAL,
+ BGP_SELECT_ORDER_ALREADY_SELECTED,
+ BGP_SELECT_ORDER_ROUTER_ID,
+ BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ };
+ so->num = sizeof(kinds)/sizeof(kinds[0]);
+ memcpy (&so->kinds, &kinds, sizeof(kinds));
+}
+
+/* Equivalent to BGP_FLAG_ASPATH_IGNORE */
+void
+bgp_select_order_aspath_ignore (struct bgp_select_order *so)
+{
+ so->num = BGP_SELECT_ORDER_MAX - 1;
+ enum bgp_select_order_kinds kinds[] =
+ {
+ BGP_SELECT_ORDER_LOCAL_PREF,
+ BGP_SELECT_ORDER_LOCAL_ROUTE,
+ BGP_SELECT_ORDER_ORIGIN,
+ BGP_SELECT_ORDER_MED,
+ BGP_SELECT_ORDER_EXTERNAL,
+ BGP_SELECT_ORDER_IGP_COST,
+ BGP_SELECT_ORDER_MPATH_EQUAL,
+ BGP_SELECT_ORDER_ALREADY_SELECTED,
+ BGP_SELECT_ORDER_ROUTER_ID,
+ BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ };
+ so->num = sizeof(kinds)/sizeof(kinds[0]);
+ memcpy (&so->kinds, &kinds, sizeof(kinds));
+}
+
+/* Equivalent to the BGP_FLAG_COMPARE_ROUTER_ID flag */
+void
+bgp_select_order_cmp_router_id (struct bgp_select_order *so)
+{
+ so->num = BGP_SELECT_ORDER_MAX - 1;
+ enum bgp_select_order_kinds kinds[] =
+ {
+ BGP_SELECT_ORDER_LOCAL_PREF,
+ BGP_SELECT_ORDER_LOCAL_ROUTE,
+ BGP_SELECT_ORDER_AS_PATH,
+ BGP_SELECT_ORDER_ORIGIN,
+ BGP_SELECT_ORDER_MED,
+ BGP_SELECT_ORDER_EXTERNAL,
+ BGP_SELECT_ORDER_IGP_COST,
+ BGP_SELECT_ORDER_MPATH_EQUAL,
+ BGP_SELECT_ORDER_ROUTER_ID,
+ BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ };
+ so->num = sizeof(kinds)/sizeof(kinds[0]);
+ memcpy (&so->kinds, &kinds, sizeof(kinds));
+}
+
+void
+bgp_select_order_aspath_confed (struct bgp_select_order *so)
+{
+ enum bgp_select_order_kinds kinds[] =
+ {
+ BGP_SELECT_ORDER_LOCAL_PREF,
+ BGP_SELECT_ORDER_LOCAL_ROUTE,
+ BGP_SELECT_ORDER_AS_PATH_CONFED,
+ BGP_SELECT_ORDER_ORIGIN,
+ BGP_SELECT_ORDER_MED,
+ BGP_SELECT_ORDER_EXTERNAL,
+ BGP_SELECT_ORDER_IGP_COST,
+ BGP_SELECT_ORDER_MPATH_EQUAL,
+ BGP_SELECT_ORDER_ALREADY_SELECTED,
+ BGP_SELECT_ORDER_ROUTER_ID,
+ BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ };
+ so->num = sizeof(kinds)/sizeof(kinds[0]);
+ memcpy (&so->kinds, &kinds, sizeof(kinds));
+}
+
+void
+bgp_select_order_install (struct bgp *bgp, struct bgp_select_order *so)
+{
+ struct listnode *node;
+ struct bgp_select_order so_default;
+ struct peer *peer;
+
+ if (bgp_select_order_same (so, &bgp->selection_order))
+ return;
+
+ bgp_select_order_default (&so_default);
+
+ if (bgp_select_order_same (so, &so_default))
+ bgp_flag_unset (bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT);
+ else
+ bgp_flag_set (bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer))
+ {
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ continue;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ {
+ peer->last_reset = PEER_DOWN_SELECT_ORDER_CHANGE;
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ else
+ BGP_EVENT_ADD (peer, BGP_Stop);
+ }
+ bgp->selection_order = *so;
+}
+
/* BGP instance creation by `router bgp' commands. */
static struct bgp *
bgp_create (as_t *as, const char *name)
@@ -1975,7 +2170,9 @@ bgp_create (as_t *as, const char *name)
bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
-
+
+ bgp_select_order_default (&bgp->selection_order);
+
bgp->as = *as;
if (name)
@@ -5319,15 +5516,19 @@ bgp_config_write (struct vty *vty)
vty_out (vty, " bgp graceful-restart%s", VTY_NEWLINE);
/* BGP bestpath method. */
- if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE))
- vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE);
+ if (bgp_flag_check (bgp, BGP_FLAG_SELECT_ORDER_NON_DEFAULT))
+ {
+ vty_out (vty, " bgp bestpath order");
+ for (int i = 0; i < bgp->selection_order.num; i++)
+ vty_out (vty, " %s",
+ bgp_select_order_kind2str
(bgp->selection_order.kinds[i]));
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED))
vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE);
if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
vty_out (vty, " bgp bestpath as-path multipath-relax%s", VTY_NEWLINE);
}
- if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID))
- vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE);
if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)
|| bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST))
{
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 51501f1..c64f7e9 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -62,6 +62,24 @@ struct bgp_master
#define BGP_OPT_NO_LISTEN (1 << 3)
};
+/* Route selection decision codes */
+enum bgp_select_order_kinds
+{
+ BGP_SELECT_ORDER_LOCAL_PREF = 1,
+ BGP_SELECT_ORDER_LOCAL_ROUTE,
+ BGP_SELECT_ORDER_AS_PATH,
+ BGP_SELECT_ORDER_AS_PATH_CONFED,
+ BGP_SELECT_ORDER_ORIGIN,
+ BGP_SELECT_ORDER_MED,
+ BGP_SELECT_ORDER_EXTERNAL,
+ BGP_SELECT_ORDER_IGP_COST,
+ BGP_SELECT_ORDER_ALREADY_SELECTED,
+ BGP_SELECT_ORDER_MPATH_EQUAL,
+ BGP_SELECT_ORDER_ROUTER_ID,
+ BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ BGP_SELECT_ORDER_MAX,
+};
+
/* BGP instance structure. */
struct bgp
{
@@ -115,14 +133,13 @@ struct bgp
#define BGP_FLAG_NO_DEFAULT_IPV4 (1 << 4)
#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 5)
#define BGP_FLAG_ENFORCE_FIRST_AS (1 << 6)
-#define BGP_FLAG_COMPARE_ROUTER_ID (1 << 7)
-#define BGP_FLAG_ASPATH_IGNORE (1 << 8)
-#define BGP_FLAG_IMPORT_CHECK (1 << 9)
-#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 10)
-#define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11)
-#define BGP_FLAG_GRACEFUL_RESTART (1 << 12)
-#define BGP_FLAG_ASPATH_CONFED (1 << 13)
-#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14)
+#define BGP_FLAG_IMPORT_CHECK (1 << 7)
+#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 8)
+#define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 9)
+#define BGP_FLAG_GRACEFUL_RESTART (1 << 10)
+#define BGP_FLAG_ASPATH_CONFED (1 << 11)
+#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 12)
+#define BGP_FLAG_SELECT_ORDER_NON_DEFAULT (1 << 13)
/* BGP Per AF flags */
u_int16_t af_flags[AFI_MAX][SAFI_MAX];
@@ -172,6 +189,12 @@ struct bgp
u_int16_t maxpaths_ebgp;
u_int16_t maxpaths_ibgp;
} maxpaths[AFI_MAX][SAFI_MAX];
+
+ struct bgp_select_order
+ {
+ uint16_t num;
+ enum bgp_select_order_kinds kinds[BGP_SELECT_ORDER_MAX];
+ } selection_order;
};
/* BGP peer-group support. */
@@ -273,6 +296,8 @@ typedef enum
BGP_PEER_CONFED,
} bgp_peer_sort_t;
+
+
/* BGP neighbor structure. */
struct peer
{
@@ -371,6 +396,8 @@ struct peer
#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */
#define PEER_CAP_RESTART_BIT_ADV (1 << 9) /* sent restart state */
#define PEER_CAP_RESTART_BIT_RCV (1 << 10) /* peer restart state */
+#define PEER_CAP_SELECT_ORDER_ADV (1 << 11) /* route selection ... */
+#define PEER_CAP_SELECT_ORDER_RCV (1 << 12) /* .. decision order */
/* Capability flags (reset in bgp_stop) */
u_int16_t af_cap[AFI_MAX][SAFI_MAX];
@@ -518,7 +545,10 @@ struct peer
/* Notify data. */
struct bgp_notify notify;
-
+
+ /* Route decision selection order */
+ struct bgp_select_order *selection_order;
+
/* Whole packet size to be read. */
unsigned long packet_size;
@@ -564,6 +594,7 @@ struct peer
#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */
#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */
#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */
+#define PEER_DOWN_SELECT_ORDER_CHANGE 23 /* BGP route select order changed */
/* The kind of route-map Flags.*/
u_char rmap_type;
@@ -850,7 +881,15 @@ extern struct peer_group *peer_group_lookup (struct bgp *,
const char *);
extern struct peer_group *peer_group_get (struct bgp *, const char *);
extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct
in_addr *,
int *);
-
+size_t bgp_select_order_str (struct bgp_select_order *, char *, size_t);
+enum bgp_select_order_kinds bgp_select_order_str2kind (const char *const);
+const char *bgp_select_order_kind2str (enum bgp_select_order_kinds);
+
+void bgp_select_order_default (struct bgp_select_order *);
+void bgp_select_order_aspath_ignore (struct bgp_select_order *);
+void bgp_select_order_cmp_router_id (struct bgp_select_order *);
+void bgp_select_order_aspath_confed (struct bgp_select_order *);
+void bgp_select_order_install (struct bgp *, struct bgp_select_order *);
/*
* Peers are incredibly easy to memory leak
* due to the various ways that they are actually used
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 57de5c4..fa36e6f 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -155,6 +155,7 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_BGP_REGEXP, "BGP regexp" },
{ MTYPE_BGP_AGGREGATE, "BGP aggregate" },
{ MTYPE_BGP_ADDR, "BGP own address" },
+ { MTYPE_BGP_SELECT_ORDER, "BGP route selection order" },
{ -1, NULL }
};
diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c
index fd7ea6c..4acfb15 100644
--- a/tests/bgp_capability_test.c
+++ b/tests/bgp_capability_test.c
@@ -420,6 +420,68 @@ static struct test_segment misc_segments[] =
{ CAPABILITY_CODE_DYNAMIC, 0x0 },
2, SHOULD_PARSE,
},
+ { "select-order",
+ "Decision Selection Order (Quagga private), default",
+ { CAPABILITY_CODE_SELECT_ORDER, 8*2,
+ 0x0, BGP_SELECT_ORDER_LOCAL_PREF,
+ 0x0, BGP_SELECT_ORDER_AS_PATH,
+ 0x0, BGP_SELECT_ORDER_ORIGIN,
+ 0x0, BGP_SELECT_ORDER_MED,
+ 0x0, BGP_SELECT_ORDER_EXTERNAL,
+ 0x0, BGP_SELECT_ORDER_IGP_COST,
+ 0x0, BGP_SELECT_ORDER_ROUTER_ID,
+ 0x0, BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ },
+ 2 + 8*2, SHOULD_PARSE,
+ },
+ { "select-order-empty",
+ "Decision Selection Order (Quagga private), empty",
+ { CAPABILITY_CODE_SELECT_ORDER, 0,
+ },
+ 2, SHOULD_PARSE,
+ },
+ { "select-order-0-1over",
+ "Decision Selection Order (Quagga private), 0 num, 1 body",
+ { CAPABILITY_CODE_SELECT_ORDER, 0,
+ 0x0, BGP_SELECT_ORDER_LOCAL_PREF,
+ },
+ 4, SHOULD_ERR,
+ },
+ { "select-order-0-over",
+ "Decision Selection Order (Quagga private), 0 num, oversized",
+ { CAPABILITY_CODE_SELECT_ORDER, 0,
+ 0x0, BGP_SELECT_ORDER_LOCAL_PREF,
+ 0x0, BGP_SELECT_ORDER_AS_PATH,
+ 0x0, BGP_SELECT_ORDER_ORIGIN,
+ 0x0, BGP_SELECT_ORDER_MED,
+ 0x0, BGP_SELECT_ORDER_EXTERNAL,
+ 0x0, BGP_SELECT_ORDER_IGP_COST,
+ 0x0, BGP_SELECT_ORDER_ROUTER_ID,
+ 0x0, BGP_SELECT_ORDER_CLUSTER_LIST_LEN,
+ },
+ 18, SHOULD_ERR,
+ },
+ { "select-order-1-empty",
+ "Decision Selection Order (Quagga private), num 1, empty body",
+ { CAPABILITY_CODE_SELECT_ORDER, 1 * 2,
+ },
+ 2, SHOULD_ERR,
+ },
+ { "select-order-1-1",
+ "Decision Selection Order (Quagga private), num 1, 1 kind",
+ { CAPABILITY_CODE_SELECT_ORDER, 1 * 2,
+ 0x0, BGP_SELECT_ORDER_LOCAL_PREF,
+ },
+ 4, SHOULD_PARSE,
+ },
+ { "select-order-1-2",
+ "Decision Selection Order (Quagga private), num 1, 2 kinds",
+ { CAPABILITY_CODE_SELECT_ORDER, 1 * 2,
+ 0x0, BGP_SELECT_ORDER_LOCAL_PREF,
+ 0x0, BGP_SELECT_ORDER_AS_PATH,
+ },
+ 6, SHOULD_ERR,
+ },
{ NULL, NULL, {0}, 0, 0}
};
--
2.5.0
_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev