summaryrefslogtreecommitdiff
path: root/proto
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2023-11-23 20:54:22 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2023-11-23 20:54:22 +0100
commit3fb06fea1d14ef147a567052391a5b359704e971 (patch)
tree692750a6792e18fc1dcf50c277d0cde275577815 /proto
parentb6923f6386b04340d6b2b6a75fbe83c392f207ca (diff)
BGP: Add options to require BGP capabilities
Some BGP capabilities change the BGP behavior in a significant way, so if the configuration depends on it, it is better to not establish BGP session when the capability is not available. Add several BGP option to require individual BGP capabilities during session negotiation.
Diffstat (limited to 'proto')
-rw-r--r--proto/bgp/bgp.c49
-rw-r--r--proto/bgp/bgp.h10
-rw-r--r--proto/bgp/config.Y9
-rw-r--r--proto/bgp/packets.c38
4 files changed, 95 insertions, 11 deletions
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 914935b9..b14df932 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -2000,6 +2000,21 @@ bgp_postconfig(struct proto_config *CF)
if (interior && (cf->local_role != BGP_ROLE_UNDEFINED))
log(L_WARN "BGP roles are not recommended to be used within AS confederations");
+ if (cf->require_enhanced_refresh && !(cf->enable_refresh && cf->enable_enhanced_refresh))
+ cf_warn("Enhanced refresh required but disabled");
+
+ if (cf->require_as4 && !cf->enable_as4)
+ cf_warn("AS4 support required but disabled");
+
+ if (cf->require_extended_messages && !cf->enable_extended_messages)
+ cf_warn("Extended messages required but not enabled");
+
+ if (cf->require_gr && !cf->gr_mode)
+ cf_warn("Graceful restart required but not enabled");
+
+ if (cf->require_llgr && !cf->llgr_mode)
+ cf_warn("Long-lived graceful restart required but not enabled");
+
if (cf->require_roles && (cf->local_role == BGP_ROLE_UNDEFINED))
cf_error("Local role must be set if roles are required");
@@ -2123,6 +2138,12 @@ bgp_postconfig(struct proto_config *CF)
if (cc->secondary && !cc->c.table->sorted)
cf_error("BGP with secondary option requires sorted table");
+
+ if (cc->require_ext_next_hop && !cc->ext_next_hop)
+ cf_warn("Extended next hop required but not enabled");
+
+ if (cc->require_add_path && !cc->add_path)
+ cf_warn("ADD-PATH required but not enabled");
}
}
@@ -2167,20 +2188,30 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
if (C->stale)
same = proto_configure_channel(P, &C, NULL) && same;
- if (same)
- proto_setup_mpls_map(P, RTS_BGP, 1);
+ /* Reset name counter */
+ p->dynamic_name_counter = 0;
- if (same && (p->start_state > BSS_PREPARE))
- bgp_update_bfd(p, new->bfd);
+ if (!same)
+ return 0;
/* We should update our copy of configuration ptr as old configuration will be freed */
- if (same)
- p->cf = new;
+ p->cf = new;
- /* Reset name counter */
- p->dynamic_name_counter = 0;
+ /* Check whether existing connections are compatible with required capabilities */
+ struct bgp_conn *ci = &p->incoming_conn;
+ if (((ci->state == BS_OPENCONFIRM) || (ci->state == BS_ESTABLISHED)) && !bgp_check_capabilities(ci))
+ return 0;
- return same;
+ struct bgp_conn *co = &p->outgoing_conn;
+ if (((co->state == BS_OPENCONFIRM) || (co->state == BS_ESTABLISHED)) && !bgp_check_capabilities(co))
+ return 0;
+
+ proto_setup_mpls_map(P, RTS_BGP, 1);
+
+ if (p->start_state > BSS_PREPARE)
+ bgp_update_bfd(p, new->bfd);
+
+ return 1;
}
#define TABLE(cf, NAME) ((cf)->NAME ? (cf)->NAME->table : NULL )
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 2e95037c..61127562 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -138,6 +138,13 @@ struct bgp_config {
const char *dynamic_name; /* Name pattern for dynamic BGP */
int dynamic_name_digits; /* Minimum number of digits for dynamic names */
int check_link; /* Use iface link state for liveness detection */
+ int require_refresh; /* Require remote support for route refresh [RFC 2918] */
+ int require_enhanced_refresh; /* Require remote support for enhanced route refresh [RFC 7313] */
+ int require_as4; /* Require remote support for 4B AS numbers [RFC 6793] */
+ int require_extended_messages; /* Require remote support for extended messages [RFC 8654] */
+ int require_hostname; /* Require remote support for hostname [draft] */
+ int require_gr; /* Require remote support for graceful restart [RFC 4724] */
+ int require_llgr; /* Require remote support for long-lived graceful restart [draft] */
struct bfd_options *bfd; /* Use BFD for liveness detection */
};
@@ -159,7 +166,9 @@ struct bgp_channel_config {
u8 llgr_able; /* Allow full long-lived GR for the channel */
uint llgr_time; /* Long-lived graceful restart stale time */
u8 ext_next_hop; /* Allow both IPv4 and IPv6 next hops */
+ u8 require_ext_next_hop; /* Require remote support of IPv4 NLRI with IPv6 next hops [RFC 8950] */
u8 add_path; /* Use ADD-PATH extension [RFC 7911] */
+ u8 require_add_path; /* Require remote support of ADD-PATH extension [RFC 7911] */
u8 aigp; /* AIGP is allowed on this session */
u8 aigp_originate; /* AIGP is originated automatically */
u32 cost; /* IGP cost for direct next hops */
@@ -657,6 +666,7 @@ bgp_total_aigp_metric(rte *r)
void bgp_dump_state_change(struct bgp_conn *conn, uint old, uint new);
void bgp_prepare_capabilities(struct bgp_conn *conn);
+int bgp_check_capabilities(struct bgp_conn *conn);
const struct bgp_af_desc *bgp_get_af_desc(u32 afi);
const struct bgp_af_caps *bgp_find_af_caps(struct bgp_caps *caps, u32 afi);
void bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type);
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 6981a117..0fcbb276 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -191,6 +191,13 @@ bgp_proto:
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
| bgp_proto ADVERTISE HOSTNAME bool ';' { BGP_CFG->enable_hostname = $4; }
+ | bgp_proto REQUIRE ROUTE REFRESH bool ';' { BGP_CFG->require_refresh = $5; }
+ | bgp_proto REQUIRE ENHANCED ROUTE REFRESH bool ';' { BGP_CFG->require_enhanced_refresh = $6; }
+ | bgp_proto REQUIRE AS4 bool ';' { BGP_CFG->require_as4 = $4; }
+ | bgp_proto REQUIRE EXTENDED MESSAGES bool ';' { BGP_CFG->require_extended_messages = $5; }
+ | bgp_proto REQUIRE HOSTNAME bool ';' { BGP_CFG->require_hostname = $4; }
+ | bgp_proto REQUIRE GRACEFUL RESTART bool ';' { BGP_CFG->require_gr = $5; }
+ | bgp_proto REQUIRE LONG LIVED GRACEFUL RESTART bool ';' { BGP_CFG->require_llgr = $7; }
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; }
| bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
@@ -284,9 +291,11 @@ bgp_channel_item:
| LONG LIVED GRACEFUL RESTART bool { BGP_CC->llgr_able = $5; }
| LONG LIVED STALE TIME expr { BGP_CC->llgr_time = $5; }
| EXTENDED NEXT HOP bool { BGP_CC->ext_next_hop = $4; }
+ | REQUIRE EXTENDED NEXT HOP bool { BGP_CC->require_ext_next_hop = $5; if (BGP_AFI(BGP_CC->afi) != BGP_AFI_IPV4) cf_warn("Require extended next hop option ignored for non-IPv4 channels"); }
| ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
| ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
| ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
+ | REQUIRE ADD PATHS bool { BGP_CC->require_add_path = $4; }
| IMPORT TABLE bool { BGP_CC->import_table = $3; }
| EXPORT TABLE bool { BGP_CC->export_table = $3; }
| AIGP bool { BGP_CC->aigp = $2; BGP_CC->aigp_originate = 0; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 395169a4..1e5a226f 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -694,7 +694,7 @@ err:
return -1;
}
-static int
+int
bgp_check_capabilities(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
@@ -706,6 +706,29 @@ bgp_check_capabilities(struct bgp_conn *conn)
/* This is partially overlapping with bgp_conn_enter_established_state(),
but we need to run this just after we receive OPEN message */
+ if (p->cf->require_refresh && !remote->route_refresh)
+ return 0;
+
+ if (p->cf->require_enhanced_refresh && !remote->enhanced_refresh)
+ return 0;
+
+ if (p->cf->require_as4 && !remote->as4_support)
+ return 0;
+
+ if (p->cf->require_extended_messages && !remote->ext_messages)
+ return 0;
+
+ if (p->cf->require_hostname && !remote->hostname)
+ return 0;
+
+ if (p->cf->require_gr && !remote->gr_aware)
+ return 0;
+
+ if (p->cf->require_llgr && !remote->llgr_aware)
+ return 0;
+
+ /* No check for require_roles, as it uses error code 2.11 instead of 2.7 */
+
BGP_WALK_CHANNELS(p, c)
{
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
@@ -719,7 +742,18 @@ bgp_check_capabilities(struct bgp_conn *conn)
return 0;
if (active)
+ {
+ if (c->cf->require_ext_next_hop && !rem->ext_next_hop)
+ return 0;
+
+ if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_RX) && !(rem->add_path & BGP_ADD_PATH_TX))
+ return 0;
+
+ if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_TX) && !(rem->add_path & BGP_ADD_PATH_RX))
+ return 0;
+
count++;
+ }
}
/* We need at least one channel active */
@@ -905,7 +939,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
- /* RFC 5492 4 - check for required capabilities */
+ /* RFC 5492 5 - check for required capabilities */
if (p->cf->capabilities && !bgp_check_capabilities(conn))
{ bgp_error(conn, 2, 7, NULL, 0); return; }