BGP: Add option to enforce first AS in AS_PATH

This is optional check described in RFC 4271. Although this can be also
done by filters, it is widely implemented option in BGP implementations.

Thanks to Eugene Bogomazov for the original patch.
This commit is contained in:
Ondrej Zajicek (work) 2019-11-10 02:06:07 +01:00
parent becda5638a
commit 0b228fca04
5 changed files with 35 additions and 6 deletions

View file

@ -2405,6 +2405,15 @@ using the following configuration parameters:
malformed and corresponding BGP updates are treated as withdraws. malformed and corresponding BGP updates are treated as withdraws.
Default: on. Default: on.
<tag><label id="bgp-enforce-first-as">enforce first as [<m/switch/]</tag>
Routes received from an EBGP neighbor are generally expected to have the
first (leftmost) AS number in their AS path equal to the neighbor AS
number. This is not enforced by default as there are legitimate cases
where it is not true, e.g. connections to route servers. When this
option is enabled, routes with non-matching first AS number are rejected
and corresponding updates are treated as withdraws. The option is valid
on EBGP sessions only. Default: off.
<tag><label id="bgp-enable-route-refresh">enable route refresh <m/switch/</tag> <tag><label id="bgp-enable-route-refresh">enable route refresh <m/switch/</tag>
After the initial route exchange, BGP protocol uses incremental updates After the initial route exchange, BGP protocol uses incremental updates
to keep BGP speakers synchronized. Sometimes (e.g., if BGP speaker to keep BGP speakers synchronized. Sometimes (e.g., if BGP speaker

View file

@ -404,6 +404,15 @@ bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
} }
static inline int
bgp_as_path_first_as_equal(const byte *data, uint len, u32 asn)
{
return (len >= 6) &&
((data[0] == AS_PATH_SEQUENCE) || (data[0] == AS_PATH_CONFED_SEQUENCE)) &&
(data[1] > 0) &&
(get_u32(data+2) == asn);
}
static int static int
bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size) bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{ {
@ -433,11 +442,6 @@ bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte
if (!as_path_valid(data, len, as_length, as_sets, as_confed, err, sizeof(err))) if (!as_path_valid(data, len, as_length, as_sets, as_confed, err, sizeof(err)))
WITHDRAW("Malformed AS_PATH attribute - %s", err); WITHDRAW("Malformed AS_PATH attribute - %s", err);
/* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
if (p->is_interior && !p->is_internal &&
((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
if (!s->as4_session) if (!s->as4_session)
{ {
/* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */ /* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
@ -446,6 +450,16 @@ bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte
len = as_path_16to32(data, src, len); len = as_path_16to32(data, src, len);
} }
/* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
if (p->is_interior && !p->is_internal &&
((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");
/* Reject routes with first AS in AS_PATH not matching neighbor AS; RFC 4271 6.3 */
if (!p->is_internal && p->cf->enforce_first_as &&
!bgp_as_path_first_as_equal(data, len, p->remote_as))
WITHDRAW("Malformed AS_PATH attribute - %s", "First AS differs from neigbor AS");
bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len); bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);
} }

View file

@ -1936,6 +1936,9 @@ bgp_postconfig(struct proto_config *CF)
if (!cf->gr_mode && cf->llgr_mode) if (!cf->gr_mode && cf->llgr_mode)
cf_error("Long-lived graceful restart requires basic graceful restart"); cf_error("Long-lived graceful restart requires basic graceful restart");
if (internal && cf->enforce_first_as)
cf_error("Enforce first AS check is requires EBGP sessions");
struct bgp_channel_config *cc; struct bgp_channel_config *cc;
WALK_LIST(cc, CF->channels) WALK_LIST(cc, CF->channels)

View file

@ -108,6 +108,7 @@ struct bgp_config {
int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */ int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */
int allow_local_pref; /* Allow LOCAL_PREF in EBGP sessions */ int allow_local_pref; /* Allow LOCAL_PREF in EBGP sessions */
int allow_as_sets; /* Allow AS_SETs in incoming AS_PATHs */ int allow_as_sets; /* Allow AS_SETs in incoming AS_PATHs */
int enforce_first_as; /* Enable check for neighbor AS as first AS in AS_PATH */
int gr_mode; /* Graceful restart mode (BGP_GR_*) */ int gr_mode; /* Graceful restart mode (BGP_GR_*) */
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */ int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
int setkey; /* Set MD5 password to system SA/SP database */ int setkey; /* Set MD5 password to system SA/SP database */

View file

@ -30,7 +30,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY, GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG, STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS, LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST) DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST)
%type <i> bgp_nh %type <i> bgp_nh
%type <i32> bgp_afi %type <i32> bgp_afi
@ -191,6 +192,7 @@ bgp_proto:
| bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; } | bgp_proto CHECK LINK bool ';' { BGP_CFG->check_link = $4; }
| bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); } | bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
| bgp_proto BFD GRACEFUL ';' { BGP_CFG->bfd = BGP_BFD_GRACEFUL; cf_check_bfd(1); } | bgp_proto BFD GRACEFUL ';' { BGP_CFG->bfd = BGP_BFD_GRACEFUL; cf_check_bfd(1); }
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
; ;
bgp_afi: bgp_afi: