]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: Add option to enforce first AS in AS_PATH
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 10 Nov 2019 01:06:07 +0000 (02:06 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 10 Nov 2019 01:06:07 +0000 (02:06 +0100)
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.

doc/bird.sgml
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y

index d2a4fc143b2fc12a8b1ffbfe3a44c7087e7c2ac1..aeea613fd4107a30eee51dcb26c38e4d111741d0 100644 (file)
@@ -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
index 9b243763212738bf3b6aaca98a659d83ff364cb0..39297dd7bf2e877da80582956893294b2c5d9a57 100644 (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
 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);
 }
 
index 053016dd2a73683abac65ad32d30a488983dea0b..2a64958a004a92a094c5ed8499fc1a2ab1c3bf1a 100644 (file)
@@ -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)
index 1270b8f302581618504e28f740b35012e32130e6..d336132bf2c55077ece4366b36904326c0d3052b 100644 (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_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 */
index 090ba24e32986aa06114def2bff0a9ff9b7f4124..7279560bf4f91753c71c0e9cb352570de892c47e 100644 (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,
        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: