summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml9
-rw-r--r--proto/bgp/attrs.c24
-rw-r--r--proto/bgp/bgp.c3
-rw-r--r--proto/bgp/bgp.h1
-rw-r--r--proto/bgp/config.Y4
5 files changed, 35 insertions, 6 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index d2a4fc14..aeea613f 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -2405,6 +2405,15 @@ using the following configuration parameters:
malformed and corresponding BGP updates are treated as withdraws.
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>
After the initial route exchange, BGP protocol uses incremental updates
to keep BGP speakers synchronized. Sometimes (e.g., if BGP speaker
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 9b243763..39297dd7 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -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
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)))
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)
{
/* 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);
}
+ /* 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);
}
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 053016dd..2a64958a 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1936,6 +1936,9 @@ bgp_postconfig(struct proto_config *CF)
if (!cf->gr_mode && cf->llgr_mode)
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;
WALK_LIST(cc, CF->channels)
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 1270b8f3..d336132b 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -108,6 +108,7 @@ struct bgp_config {
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_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 llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
int setkey; /* Set MD5 password to system SA/SP database */
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 090ba24e..7279560b 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -30,7 +30,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
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 <i32> bgp_afi
@@ -191,6 +192,7 @@ bgp_proto:
| 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 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: