summaryrefslogtreecommitdiff
path: root/bgpd
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd')
-rw-r--r--bgpd/bgp_attr.c594
-rw-r--r--bgpd/bgp_attr.h19
2 files changed, 294 insertions, 319 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 92c1a09f..b02cfee3 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -710,9 +710,17 @@ bgp_attr_flush (struct attr *attr)
* introduced by the sending neighbour.
*/
static bgp_attr_parse_ret_t
-bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
- u_char subcode, u_char *startp, bgp_size_t length)
-{
+bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode,
+ bgp_size_t length)
+{
+ struct peer *const peer = args->peer;
+ const u_int8_t flags = args->flags;
+ /* startp and length must be special-cased, as whether or not to
+ * send the attribute data with the NOTIFY depends on the error,
+ * the caller therefore signals this with the seperate length argument
+ */
+ u_char *startp = (length > 0 ? args->startp : NULL);
+
/* Only relax error handling for eBGP peers */
if (peer_sort (peer) != BGP_PEER_EBGP)
{
@@ -722,7 +730,7 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
}
- switch (type) {
+ switch (args->type) {
/* where an optional attribute is inconsequential, e.g. it does not affect
* route selection, and can be safely ignored then any such attributes
* which are malformed should just be ignored and the route processed as
@@ -756,9 +764,9 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
* the whole session to be reset. Instead treat it as a withdrawal
* of the routes, if possible.
*/
- if (CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)
- && CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
- && CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
+ if (CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS)
+ && CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+ && CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
return BGP_ATTR_PARSE_WITHDRAW;
/* default to reset */
@@ -771,16 +779,14 @@ bgp_attr_malformed (struct peer *peer, u_char type, u_char flag,
being diagnosed is defined by RFC as either a "well-known" or an "optional,
non-transitive" attribute. */
static void
-bgp_attr_flags_diagnose
-(
- struct peer * peer,
- const u_int8_t attr_code,
- u_int8_t desired_flags, /* how RFC says it must be */
- u_int8_t real_flags /* on wire */
+bgp_attr_flags_diagnose (struct bgp_attr_parser_args *args,
+ u_int8_t desired_flags /* how RFC says it must be */
)
{
u_char seen = 0, i;
-
+ u_char real_flags = args->flags;
+ const u_int8_t attr_code = args->type;
+
desired_flags &= ~BGP_ATTR_FLAG_EXTLEN;
real_flags &= ~BGP_ATTR_FLAG_EXTLEN;
for (i = 0; i <= 2; i++) /* O,T,P, but not E */
@@ -790,7 +796,7 @@ bgp_attr_flags_diagnose
CHECK_FLAG (real_flags, attr_flag_str[i].key)
)
{
- zlog (peer->log, LOG_ERR, "%s attribute must%s be flagged as \"%s\"",
+ zlog (args->peer->log, LOG_ERR, "%s attribute must%s be flagged as \"%s\"",
LOOKUP (attr_str, attr_code),
CHECK_FLAG (desired_flags, attr_flag_str[i].key) ? "" : " not",
attr_flag_str[i].str);
@@ -799,27 +805,102 @@ bgp_attr_flags_diagnose
assert (seen);
}
+/* Required flags for attributes. EXTLEN will be masked off when testing,
+ * as will PARTIAL for optional+transitive attributes.
+ */
+const u_int8_t attr_flags_values [] = {
+ [BGP_ATTR_ORIGIN] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS_PATH] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_NEXT_HOP] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_MULTI_EXIT_DISC] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_LOCAL_PREF] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_ATOMIC_AGGREGATE] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AGGREGATOR] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_ORIGINATOR_ID] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_CLUSTER_LIST] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_MP_REACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_MP_UNREACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_EXT_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS4_PATH] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS4_AGGREGATOR] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+};
+static const size_t attr_flags_values_max =
+ sizeof (attr_flags_values) / sizeof (attr_flags_values[0]);
+
+static int
+bgp_attr_flag_invalid (struct bgp_attr_parser_args *args)
+{
+ u_int8_t mask = BGP_ATTR_FLAG_EXTLEN;
+ const u_int8_t flags = args->flags;
+ const u_int8_t attr_code = args->type;
+ struct peer *const peer = args->peer;
+
+ /* there may be attributes we don't know about */
+ if (attr_code > attr_flags_values_max)
+ return 0;
+ if (attr_flags_values[attr_code] == 0)
+ return 0;
+
+ /* RFC4271, "For well-known attributes, the Transitive bit MUST be set to
+ * 1."
+ */
+ if (!CHECK_FLAG (BGP_ATTR_FLAG_OPTIONAL, flags)
+ && !CHECK_FLAG (BGP_ATTR_FLAG_TRANS, flags))
+ {
+ zlog (peer->log, LOG_ERR,
+ "%s well-known attributes must have transitive flag set (%x)",
+ LOOKUP (attr_str, attr_code), flags);
+ return 1;
+ }
+
+ /* "For well-known attributes and for optional non-transitive attributes,
+ * the Partial bit MUST be set to 0."
+ */
+ if (CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL))
+ {
+ if (!CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL))
+ {
+ zlog (peer->log, LOG_ERR,
+ "%s well-known attribute "
+ "must NOT have the partial flag set (%x)",
+ LOOKUP (attr_str, attr_code), flags);
+ return 1;
+ }
+ if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+ && !CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
+ {
+ zlog (peer->log, LOG_ERR,
+ "%s optional + transitive attribute "
+ "must NOT have the partial flag set (%x)",
+ LOOKUP (attr_str, attr_code), flags);
+ return 1;
+ }
+ }
+
+ /* Optional transitive attributes may go through speakers that don't
+ * reocgnise them and set the Partial bit.
+ */
+ if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)
+ && CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS))
+ SET_FLAG (mask, BGP_ATTR_FLAG_PARTIAL);
+
+ if ((flags & ~attr_flags_values[attr_code])
+ == attr_flags_values[attr_code])
+ return 0;
+
+ bgp_attr_flags_diagnose (args, attr_flags_values[attr_code]);
+ return 1;
+}
+
/* Get origin attribute of the update message. */
static bgp_attr_parse_ret_t
-bgp_attr_origin (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_origin (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- /* total is entire attribute length include Attribute Flags (1),
- Attribute Type code (1) and Attribute length (1 or 2). */
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* If any recognized attribute has Attribute Flags that conflict
- with the Attribute Type Code, then the Error Subcode is set to
- Attribute Flags Error. The Data field contains the erroneous
- attribute (type, length and value). */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_ORIGIN, BGP_ATTR_FLAG_TRANS, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total);
- }
-
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* If any recognized attribute has Attribute Length that conflicts
with the expected length (based on the attribute type code), then
the Error Subcode is set to Attribute Length Error. The Data
@@ -829,9 +910,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
{
zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
length);
- return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
/* Fetch origin attribute. */
@@ -846,9 +927,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
{
zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
attr->origin);
- return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
- startp, total);
+ args->total);
}
/* Set oring attribute flag. */
@@ -860,42 +941,12 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length,
/* Parse AS path information. This function is wrapper of
aspath_parse. */
static int
-bgp_attr_aspath (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_aspath (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* Flags check. */
- if (CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL))
- {
- zlog (peer->log, LOG_ERR,
- "AS_PATH attribute must not be flagged as \"partial\" (%u)", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
- if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
- {
- zlog (peer->log, LOG_ERR,
- "AS_PATH attribute must be flagged as \"transitive\" (%u)", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ struct attr *const attr = args->attr;
+ struct peer *const peer = args->peer;
+ const bgp_size_t length = args->length;
- if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
- {
- zlog (peer->log, LOG_ERR,
- "AS_PATH attribute must not be flagged as \"optional\" (%u)", flag);
-
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
/*
* peer with AS4 => will get 4Byte ASnums
* otherwise, will get 16 Bit
@@ -909,9 +960,7 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR,
"Malformed AS path from %s, length is %d",
peer->host, length);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0);
}
/* Set aspath attribute flag. */
@@ -921,7 +970,7 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length,
}
static bgp_attr_parse_ret_t
-bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
+bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr)
{
/* These checks were part of bgp_attr_aspath, but with
* as4 we should to check aspath things when
@@ -940,9 +989,9 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
(peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath)))
{
zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+ return BGP_ATTR_PARSE_ERROR;
}
/* First AS check for EBGP. */
@@ -953,9 +1002,9 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
{
zlog (peer->log, LOG_ERR,
"%s incorrect first AS (must be %u)", peer->host, peer->as);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+ return BGP_ATTR_PARSE_ERROR;
}
}
@@ -975,25 +1024,12 @@ bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag)
/* Parse AS4 path information. This function is another wrapper of
aspath_parse. */
static int
-bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp,
- struct aspath **as4_path)
+bgp_attr_as4_path (struct bgp_attr_parser_args *args, struct aspath **as4_path)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* Flag check. */
- if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
- || !CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
- {
- zlog (peer->log, LOG_ERR,
- "As4-Path attribute flag isn't optional/transitive %d", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AS4_PATH, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
*as4_path = aspath_parse (peer->ibuf, length, 1);
/* In case of IBGP, length will be zero. */
@@ -1002,9 +1038,9 @@ bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
zlog (peer->log, LOG_ERR,
"Malformed AS4 path from %s, length is %d",
peer->host, length);
- return bgp_attr_malformed (peer, BGP_ATTR_AS4_PATH, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_MAL_AS_PATH,
- NULL, 0);
+ 0);
}
/* Set aspath attribute flag. */
@@ -1016,30 +1052,23 @@ bgp_attr_as4_path (struct peer *peer, bgp_size_t length,
/* Nexthop attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_nexthop (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
in_addr_t nexthop_h, nexthop_n;
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* Flags check. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_NEXT_HOP, BGP_ATTR_FLAG_TRANS, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total);
- }
-
/* Check nexthop attribute length. */
if (length != 4)
{
zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]",
length);
- return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
/* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP
@@ -1054,9 +1083,9 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
char buf[INET_ADDRSTRLEN];
inet_ntop (AF_INET, &nexthop_h, buf, INET_ADDRSTRLEN);
zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf);
- return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP,
- startp, total);
+ args->total);
}
attr->nexthop.s_addr = nexthop_n;
@@ -1067,31 +1096,21 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
/* MED atrribute. */
static bgp_attr_parse_ret_t
-bgp_attr_med (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_med (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_MULTI_EXIT_DISC, BGP_ATTR_FLAG_OPTIONAL, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_MULTI_EXIT_DISC, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* Length check. */
if (length != 4)
{
zlog (peer->log, LOG_ERR,
"MED attribute length isn't four [%d]", length);
- return bgp_attr_malformed (peer, BGP_ATTR_MULTI_EXIT_DISC, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
attr->med = stream_getl (peer->ibuf);
@@ -1103,27 +1122,20 @@ bgp_attr_med (struct peer *peer, bgp_size_t length,
/* Local preference attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_local_pref (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_LOCAL_PREF, BGP_ATTR_FLAG_TRANS, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_LOCAL_PREF, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* Length check. */
if (length != 4)
{
- zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]", length);
- return bgp_attr_malformed (peer, BGP_ATTR_LOCAL_PREF, flag,
+ zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]",
+ length);
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
/* If it is contained in an UPDATE message that is received from an
@@ -1145,28 +1157,20 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
/* Atomic aggregate. */
static int
-bgp_attr_atomic (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_atomic (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_TRANS)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_ATOMIC_AGGREGATE, BGP_ATTR_FLAG_TRANS, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
-
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* Length check. */
if (length != 0)
{
- zlog (peer->log, LOG_ERR, "ATOMIC_AGGREGATE attribute length isn't 0 [%u]", length);
- return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag,
+ zlog (peer->log, LOG_ERR, "ATOMIC_AGGREGATE attribute length isn't 0 [%u]",
+ length);
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
/* Set atomic aggregate flag. */
@@ -1177,41 +1181,26 @@ bgp_attr_atomic (struct peer *peer, bgp_size_t length,
/* Aggregator attribute */
static int
-bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_aggregator (struct bgp_attr_parser_args *args)
{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
int wantedlen = 6;
struct attr_extra *attre = bgp_attr_extra_get (attr);
- bgp_size_t total;
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flags check. */
- if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
- {
- zlog (peer->log, LOG_ERR,
- "AGGREGATOR attribute must be flagged as \"optional\" (%u)", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
- if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
- {
- zlog (peer->log, LOG_ERR,
- "AGGREGATOR attribute must be flagged as \"transitive\" (%u)", flag);
- return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
/* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
wantedlen = 8;
if (length != wantedlen)
{
- zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]", wantedlen, length);
- return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag,
+ zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]",
+ wantedlen, length);
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
@@ -1228,18 +1217,23 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
/* New Aggregator attribute */
static bgp_attr_parse_ret_t
-bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag,
- as_t *as4_aggregator_as,
- struct in_addr *as4_aggregator_addr)
+bgp_attr_as4_aggregator (struct bgp_attr_parser_args *args,
+ as_t *as4_aggregator_as,
+ struct in_addr *as4_aggregator_addr)
{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
if (length != 8)
{
- zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length);
- return bgp_attr_malformed (peer, BGP_ATTR_AS4_AGGREGATOR, flag,
+ zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]",
+ length);
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- NULL, 0);
+ 0);
}
+
*as4_aggregator_as = stream_getl (peer->ibuf);
as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf);
@@ -1251,7 +1245,8 @@ bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length,
/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
*/
static bgp_attr_parse_ret_t
-bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
+bgp_attr_munge_as4_attrs (struct peer *const peer,
+ struct attr *const attr,
struct aspath *as4_path, as_t as4_aggregator,
struct in_addr *as4_aggregator_addr)
{
@@ -1280,24 +1275,6 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
return BGP_ATTR_PARSE_PROCEED;
}
- if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH))
- && !(attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))))
- {
- /* Hu? This is not supposed to happen at all!
- * got as4_path and no aspath,
- * This should already
- * have been handled by 'well known attributes missing'
- * But... yeah, paranoia
- * Take this as a "malformed attribute"
- */
- zlog (peer->log, LOG_ERR,
- "%s BGP not AS4 capable peer sent AS4_PATH but"
- " no AS_PATH, cant do anything here", peer->host);
- return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag,
- BGP_NOTIFY_UPDATE_MAL_ATTR,
- NULL, 0);
- }
-
/* We have a asn16 peer. First, look for AS4_AGGREGATOR
* because that may override AS4_PATH
*/
@@ -1365,11 +1342,11 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag,
/* Community attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_community (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_community (struct bgp_attr_parser_args *args)
{
- bgp_size_t total
- = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
if (length == 0)
{
@@ -1384,9 +1361,9 @@ bgp_attr_community (struct peer *peer, bgp_size_t length,
stream_forward_getp (peer->ibuf, length);
if (!attr->community)
- return bgp_attr_malformed (peer, BGP_ATTR_COMMUNITIES, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
- startp, total);
+ args->total);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
@@ -1395,28 +1372,20 @@ bgp_attr_community (struct peer *peer, bgp_size_t length,
/* Originator ID attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_originator_id (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_ORIGINATOR_ID, BGP_ATTR_FLAG_OPTIONAL, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_ORIGINATOR_ID, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* Length check. */
if (length != 4)
{
zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length);
- return bgp_attr_malformed (peer, BGP_ATTR_ORIGINATOR_ID, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ args->total);
}
(bgp_attr_extra_get (attr))->originator_id.s_addr
@@ -1429,28 +1398,19 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
/* Cluster list attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_cluster_list (struct bgp_attr_parser_args *args)
{
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_CLUSTER_LIST, BGP_ATTR_FLAG_OPTIONAL, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_CLUSTER_LIST, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
/* Check length. */
if (length % 4)
{
zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length);
- return bgp_attr_malformed (peer, BGP_ATTR_CLUSTER_LIST, flag,
- BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
- startp, total);
+ return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
}
(bgp_attr_extra_get (attr))->cluster
@@ -1466,8 +1426,8 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
/* Multiprotocol reachability information parse. */
int
-bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
- struct attr *attr, const u_char flag, u_char *startp, struct bgp_nlri *mp_update)
+bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update)
{
afi_t afi;
safi_t safi;
@@ -1475,18 +1435,11 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
size_t start;
int ret;
struct stream *s;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
struct attr_extra *attre = bgp_attr_extra_get(attr);
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_MP_REACH_NLRI, BGP_ATTR_FLAG_OPTIONAL, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_MP_REACH_NLRI, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+
/* Set end of packet. */
s = BGP_INPUT(peer);
start = stream_get_getp(s);
@@ -1605,8 +1558,7 @@ bgp_mp_reach_parse (struct peer *peer, const bgp_size_t length,
/* Multiprotocol unreachable parse */
int
-bgp_mp_unreach_parse (struct peer *peer, const bgp_size_t length,
- const u_char flag, u_char *startp,
+bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
struct bgp_nlri *mp_withdraw)
{
struct stream *s;
@@ -1614,17 +1566,8 @@ bgp_mp_unreach_parse (struct peer *peer, const bgp_size_t length,
safi_t safi;
u_int16_t withdraw_len;
int ret;
- bgp_size_t total;
-
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
- /* Flag checks. */
- if ((flag & ~BGP_ATTR_FLAG_EXTLEN) != BGP_ATTR_FLAG_OPTIONAL)
- {
- bgp_attr_flags_diagnose (peer, BGP_ATTR_MP_UNREACH_NLRI, BGP_ATTR_FLAG_OPTIONAL, flag);
- return bgp_attr_malformed (peer, BGP_ATTR_MP_UNREACH_NLRI, flag,
- BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
- startp, total);
- }
+ struct peer *const peer = args->peer;
+ const bgp_size_t length = args->length;
s = peer->ibuf;
@@ -1656,11 +1599,11 @@ bgp_mp_unreach_parse (struct peer *peer, const bgp_size_t length,
/* Extended Community attribute. */
static bgp_attr_parse_ret_t
-bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
- struct attr *attr, u_char flag, u_char *startp)
+bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
{
- bgp_size_t total
- = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
if (length == 0)
{
@@ -1676,9 +1619,9 @@ bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
stream_forward_getp (peer->ibuf, length);
if (!attr->extra->ecommunity)
- return bgp_attr_malformed (peer, BGP_ATTR_EXT_COMMUNITIES,
- flag, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
- startp, total);
+ return bgp_attr_malformed (args,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
@@ -1687,12 +1630,18 @@ bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
/* BGP unknown attribute treatment. */
static bgp_attr_parse_ret_t
-bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
- u_char type, bgp_size_t length, u_char *startp)
+bgp_attr_unknown (struct bgp_attr_parser_args *args)
{
bgp_size_t total;
struct transit *transit;
struct attr_extra *attre;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ u_char *const startp = args->startp;
+ const u_char type = args->type;
+ const u_char flag = args->flags;
+ const bgp_size_t length = args->length;
+
if (BGP_DEBUG (normal, NORMAL))
zlog_debug ("%s Unknown attribute is received (type %d, length %d)",
@@ -1705,18 +1654,15 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
/* Forward read pointer of input stream. */
stream_forward_getp (peer->ibuf, length);
- /* Adjest total length to include type and length. */
- total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
-
/* If any of the mandatory well-known attributes are not recognized,
then the Error Subcode is set to Unrecognized Well-known
Attribute. The Data field contains the unrecognized attribute
(type, length and value). */
if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
{
- return bgp_attr_malformed (peer, type, flag,
+ return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_UNREC_ATTR,
- startp, total);
+ args->total);
}
/* Unrecognized non-transitive optional attributes must be quietly
@@ -1813,7 +1759,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
return BGP_ATTR_PARSE_ERROR;
}
-
+
/* Check extended attribue length bit. */
if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN))
length = stream_getw (BGP_INPUT (peer));
@@ -1853,59 +1799,79 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
return BGP_ATTR_PARSE_ERROR;
}
+
+ struct bgp_attr_parser_args attr_args = {
+ .peer = peer,
+ .length = length,
+ .attr = attr,
+ .type = type,
+ .flags = flag,
+ .startp = startp,
+ .total = attr_endp - startp,
+ };
+
+
+ /* If any recognized attribute has Attribute Flags that conflict
+ with the Attribute Type Code, then the Error Subcode is set to
+ Attribute Flags Error. The Data field contains the erroneous
+ attribute (type, length and value). */
+ if (bgp_attr_flag_invalid (&attr_args))
+ return bgp_attr_malformed (&attr_args,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ attr_args.total);
/* OK check attribute and store it's value. */
switch (type)
{
case BGP_ATTR_ORIGIN:
- ret = bgp_attr_origin (peer, length, attr, flag, startp);
+ ret = bgp_attr_origin (&attr_args);
break;
case BGP_ATTR_AS_PATH:
- ret = bgp_attr_aspath (peer, length, attr, flag, startp);
+ ret = bgp_attr_aspath (&attr_args);
break;
case BGP_ATTR_AS4_PATH:
- ret = bgp_attr_as4_path (peer, length, attr, flag, startp, &as4_path);
+ ret = bgp_attr_as4_path (&attr_args, &as4_path);
break;
case BGP_ATTR_NEXT_HOP:
- ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
+ ret = bgp_attr_nexthop (&attr_args);
break;
case BGP_ATTR_MULTI_EXIT_DISC:
- ret = bgp_attr_med (peer, length, attr, flag, startp);
+ ret = bgp_attr_med (&attr_args);
break;
case BGP_ATTR_LOCAL_PREF:
- ret = bgp_attr_local_pref (peer, length, attr, flag, startp);
+ ret = bgp_attr_local_pref (&attr_args);
break;
case BGP_ATTR_ATOMIC_AGGREGATE:
- ret = bgp_attr_atomic (peer, length, attr, flag, startp);
+ ret = bgp_attr_atomic (&attr_args);
break;
case BGP_ATTR_AGGREGATOR:
- ret = bgp_attr_aggregator (peer, length, attr, flag, startp);
+ ret = bgp_attr_aggregator (&attr_args);
break;
case BGP_ATTR_AS4_AGGREGATOR:
- ret = bgp_attr_as4_aggregator (peer, length, attr, flag,
- &as4_aggregator,
+ ret = bgp_attr_as4_aggregator (&attr_args,
+ &as4_aggregator,
&as4_aggregator_addr);
break;
case BGP_ATTR_COMMUNITIES:
- ret = bgp_attr_community (peer, length, attr, flag, startp);
+ ret = bgp_attr_community (&attr_args);
break;
case BGP_ATTR_ORIGINATOR_ID:
- ret = bgp_attr_originator_id (peer, length, attr, flag, startp);
+ ret = bgp_attr_originator_id (&attr_args);
break;
case BGP_ATTR_CLUSTER_LIST:
- ret = bgp_attr_cluster_list (peer, length, attr, flag, startp);
+ ret = bgp_attr_cluster_list (&attr_args);
break;
case BGP_ATTR_MP_REACH_NLRI:
- ret = bgp_mp_reach_parse (peer, length, attr, flag, startp, mp_update);
+ ret = bgp_mp_reach_parse (&attr_args, mp_update);
break;
case BGP_ATTR_MP_UNREACH_NLRI:
- ret = bgp_mp_unreach_parse (peer, length, flag, startp, mp_withdraw);
+ ret = bgp_mp_unreach_parse (&attr_args, mp_withdraw);
break;
case BGP_ATTR_EXT_COMMUNITIES:
- ret = bgp_attr_ext_communities (peer, length, attr, flag, startp);
+ ret = bgp_attr_ext_communities (&attr_args);
break;
default:
- ret = bgp_attr_unknown (peer, attr, flag, type, length, startp);
+ ret = bgp_attr_unknown (&attr_args);
break;
}
@@ -1975,7 +1941,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
* all attributes first, including these 32bit ones, and now,
* afterwards, we look what and if something is to be done for as4.
*/
- if (bgp_attr_munge_as4_attrs (peer, attr, flag, as4_path,
+ if (bgp_attr_munge_as4_attrs (peer, attr, as4_path,
as4_aggregator, &as4_aggregator_addr))
{
if (as4_path)
@@ -2007,7 +1973,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
*/
if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
{
- ret = bgp_attr_aspath_check (peer, attr, flag);
+ ret = bgp_attr_aspath_check (peer, attr);
if (ret != BGP_ATTR_PARSE_PROCEED)
return ret;
}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index e6300740..df87c863 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -179,10 +179,19 @@ extern void cluster_unintern (struct cluster_list *);
/* Transit attribute prototypes. */
void transit_unintern (struct transit *);
-/* Exported for unit-test purposes only */
-extern int bgp_mp_reach_parse (struct peer *, const bgp_size_t, struct attr *,
- const u_char, u_char *, struct bgp_nlri *);
-extern int bgp_mp_unreach_parse (struct peer *, const bgp_size_t, const u_char,
- u_char *, struct bgp_nlri *);
+/* Below exported for unit-test purposes only */
+struct bgp_attr_parser_args {
+ struct peer *peer;
+ bgp_size_t length; /* attribute data length; */
+ bgp_size_t total; /* total length, inc header */
+ struct attr *attr;
+ u_int8_t type;
+ u_int8_t flags;
+ u_char *startp;
+};
+extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
+ struct bgp_nlri *);
+extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
+ struct bgp_nlri *);
#endif /* _QUAGGA_BGP_ATTR_H */