--- embedaddon/quagga/bgpd/bgp_attr.c 2012/10/09 09:22:28 1.1.1.2 +++ embedaddon/quagga/bgpd/bgp_attr.c 2016/11/02 10:09:10 1.1.1.4 @@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330 #include "log.h" #include "hash.h" #include "jhash.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -38,7 +39,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330 #include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_ecommunity.h" - +#include "table.h" +#include "bgp_encap_types.h" + /* Attribute strings for logging. */ static const struct message attr_str [] = { @@ -61,8 +64,9 @@ static const struct message attr_str [] = { BGP_ATTR_AS4_PATH, "AS4_PATH" }, { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" }, { BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" }, + { BGP_ATTR_ENCAP, "ENCAP" }, }; -static const int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]); +static const int attr_str_max = array_size(attr_str); static const struct message attr_flag_str[] = { @@ -72,9 +76,7 @@ static const struct message attr_flag_str[] = /* bgp_attr_flags_diagnose() relies on this bit being last in this list */ { BGP_ATTR_FLAG_EXTLEN, "Extended Length" }, }; -static const size_t attr_flag_str_max = - sizeof (attr_flag_str) / sizeof (attr_flag_str[0]); - + static struct hash *cluster_hash; static void * @@ -205,10 +207,110 @@ cluster_init (void) static void cluster_finish (void) { + hash_clean (cluster_hash, (void (*)(void *))cluster_free); hash_free (cluster_hash); cluster_hash = NULL; } - + +struct bgp_attr_encap_subtlv * +encap_tlv_dup(struct bgp_attr_encap_subtlv *orig) +{ + struct bgp_attr_encap_subtlv *new; + struct bgp_attr_encap_subtlv *tail; + struct bgp_attr_encap_subtlv *p; + + for (p = orig, tail = new = NULL; p; p = p->next) { + int size = sizeof(struct bgp_attr_encap_subtlv) - 1 + p->length; + if (tail) { + tail->next = XCALLOC(MTYPE_ENCAP_TLV, size); + tail = tail->next; + } else { + tail = new = XCALLOC(MTYPE_ENCAP_TLV, size); + } + assert(tail); + memcpy(tail, p, size); + tail->next = NULL; + } + + return new; +} + +static void +encap_free(struct bgp_attr_encap_subtlv *p) +{ + struct bgp_attr_encap_subtlv *next; + while (p) { + next = p->next; + p->next = NULL; + XFREE(MTYPE_ENCAP_TLV, p); + p = next; + } +} + +void +bgp_attr_flush_encap(struct attr *attr) +{ + if (!attr || !attr->extra) + return; + + if (attr->extra->encap_subtlvs) { + encap_free(attr->extra->encap_subtlvs); + attr->extra->encap_subtlvs = NULL; + } +} + +/* + * Compare encap sub-tlv chains + * + * 1 = equivalent + * 0 = not equivalent + * + * This algorithm could be made faster if needed + */ +static int +encap_same(struct bgp_attr_encap_subtlv *h1, struct bgp_attr_encap_subtlv *h2) +{ + struct bgp_attr_encap_subtlv *p; + struct bgp_attr_encap_subtlv *q; + + if (!h1 && !h2) + return 1; + if (h1 && !h2) + return 0; + if (!h1 && h2) + return 0; + if (h1 == h2) + return 1; + + for (p = h1; p; p = p->next) { + for (q = h2; q; q = q->next) { + if ((p->type == q->type) && + (p->length == q->length) && + !memcmp(p->value, q->value, p->length)) { + + break; + } + } + if (!q) + return 0; + } + + for (p = h2; p; p = p->next) { + for (q = h1; q; q = q->next) { + if ((p->type == q->type) && + (p->length == q->length) && + !memcmp(p->value, q->value, p->length)) { + + break; + } + } + if (!q) + return 0; + } + + return 1; +} + /* Unknown transit attribute. */ static struct hash *transit_hash; @@ -281,10 +383,11 @@ transit_init (void) static void transit_finish (void) { + hash_clean (transit_hash, (void (*)(void *))transit_free); hash_free (transit_hash); transit_hash = NULL; } - + /* Attribute hash routines. */ static struct hash *attrhash; @@ -299,6 +402,10 @@ bgp_attr_extra_free (struct attr *attr) { if (attr->extra) { + if (attr->extra->encap_subtlvs) { + encap_free(attr->extra->encap_subtlvs); + attr->extra->encap_subtlvs = NULL; + } XFREE (MTYPE_ATTR_EXTRA, attr->extra); attr->extra = NULL; } @@ -319,11 +426,35 @@ bgp_attr_extra_get (struct attr *attr) void bgp_attr_dup (struct attr *new, struct attr *orig) { + struct attr_extra *extra = new->extra; + *new = *orig; - if (orig->extra) + /* if caller provided attr_extra space, use it in any case. + * + * This is neccesary even if orig->extra equals NULL, because otherwise + * memory may be later allocated on the heap by bgp_attr_extra_get. + * + * That memory would eventually be leaked, because the caller must not + * call bgp_attr_extra_free if he provided attr_extra on the stack. + */ + if (extra) { + new->extra = extra; + memset(new->extra, 0, sizeof(struct attr_extra)); + if (orig->extra) { + *new->extra = *orig->extra; + if (orig->extra->encap_subtlvs) { + new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); + } + } + } + else if (orig->extra) + { new->extra = bgp_attr_extra_new(); *new->extra = *orig->extra; + if (orig->extra->encap_subtlvs) { + new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); + } } } @@ -342,7 +473,8 @@ attr_unknown_count (void) unsigned int attrhash_key_make (void *p) { - const struct attr * attr = (struct attr *) p; + const struct attr *attr = (struct attr *) p; + const struct attr_extra *extra = attr->extra; uint32_t key = 0; #define MIX(val) key = jhash_1word(val, key) @@ -356,12 +488,13 @@ attrhash_key_make (void *p) key += attr->med; key += attr->local_pref; - if (attr->extra) + if (extra) { - MIX(attr->extra->aggregator_as); - MIX(attr->extra->aggregator_addr.s_addr); - MIX(attr->extra->weight); - MIX(attr->extra->mp_nexthop_global_in.s_addr); + MIX(extra->aggregator_as); + MIX(extra->aggregator_addr.s_addr); + MIX(extra->weight); + MIX(extra->mp_nexthop_global_in.s_addr); + MIX(extra->originator_id.s_addr); } if (attr->aspath) @@ -369,20 +502,18 @@ attrhash_key_make (void *p) if (attr->community) MIX(community_hash_make (attr->community)); - if (attr->extra) + if (extra) { - if (attr->extra->ecommunity) - MIX(ecommunity_hash_make (attr->extra->ecommunity)); - if (attr->extra->cluster) - MIX(cluster_hash_key_make (attr->extra->cluster)); - if (attr->extra->transit) - MIX(transit_hash_key_make (attr->extra->transit)); + if (extra->ecommunity) + MIX(ecommunity_hash_make (extra->ecommunity)); + if (extra->cluster) + MIX(cluster_hash_key_make (extra->cluster)); + if (extra->transit) + MIX(transit_hash_key_make (extra->transit)); -#ifdef HAVE_IPV6 - MIX(attr->extra->mp_nexthop_len); - key = jhash(attr->extra->mp_nexthop_global.s6_addr, 16, key); - key = jhash(attr->extra->mp_nexthop_local.s6_addr, 16, key); -#endif /* HAVE_IPV6 */ + MIX(extra->mp_nexthop_len); + key = jhash(extra->mp_nexthop_global.s6_addr, 16, key); + key = jhash(extra->mp_nexthop_local.s6_addr, 16, key); } return key; @@ -409,15 +540,16 @@ attrhash_cmp (const void *p1, const void *p2) && ae1->aggregator_as == ae2->aggregator_as && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr && ae1->weight == ae2->weight -#ifdef HAVE_IPV6 && ae1->mp_nexthop_len == ae2->mp_nexthop_len && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global) && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local) -#endif /* HAVE_IPV6 */ && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in) && ae1->ecommunity == ae2->ecommunity && ae1->cluster == ae2->cluster - && ae1->transit == ae2->transit) + && ae1->transit == ae2->transit + && (ae1->encap_tunneltype == ae2->encap_tunneltype) + && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs) + && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)) return 1; else if (ae1 || ae2) return 0; @@ -434,9 +566,20 @@ attrhash_init (void) attrhash = hash_create (attrhash_key_make, attrhash_cmp); } +/* + * special for hash_clean below + */ static void +attr_vfree (void *attr) +{ + bgp_attr_extra_free ((struct attr *)attr); + XFREE (MTYPE_ATTR, attr); +} + +static void attrhash_finish (void) { + hash_clean(attrhash, attr_vfree); hash_free (attrhash); attrhash = NULL; } @@ -471,6 +614,10 @@ bgp_attr_hash_alloc (void *p) { attr->extra = bgp_attr_extra_new (); *attr->extra = *val->extra; + + if (attr->extra->encap_subtlvs) { + attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs); + } } attr->refcnt = 0; return attr; @@ -545,9 +692,7 @@ bgp_attr_default_set (struct attr *attr, u_char origin attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT; attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); -#ifdef HAVE_IPV6 attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN; -#endif return attr; } @@ -559,10 +704,10 @@ bgp_attr_default_intern (u_char origin) { struct attr attr; struct attr *new; - + memset (&attr, 0, sizeof (struct attr)); bgp_attr_extra_get (&attr); - + bgp_attr_default_set(&attr, origin); new = bgp_attr_intern (&attr); @@ -579,11 +724,12 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char ori { struct attr attr; struct attr *new; - struct attr_extra *attre; + struct attr_extra attre; memset (&attr, 0, sizeof (struct attr)); - attre = bgp_attr_extra_get (&attr); - + memset (&attre, 0, sizeof (struct attr_extra)); + attr.extra = &attre; + /* Origin attribute. */ attr.origin = origin; attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN); @@ -604,22 +750,20 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char ori attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); } - attre->weight = BGP_ATTR_DEFAULT_WEIGHT; -#ifdef HAVE_IPV6 - attre->mp_nexthop_len = IPV6_MAX_BYTELEN; -#endif + attre.weight = BGP_ATTR_DEFAULT_WEIGHT; + attre.mp_nexthop_len = IPV6_MAX_BYTELEN; + if (! as_set) attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR); if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) - attre->aggregator_as = bgp->confed_id; + attre.aggregator_as = bgp->confed_id; else - attre->aggregator_as = bgp->as; - attre->aggregator_addr = bgp->router_id; + attre.aggregator_as = bgp->as; + attre.aggregator_addr = bgp->router_id; new = bgp_attr_intern (&attr); - bgp_attr_extra_free (&attr); - + aspath_unintern (&new->aspath); return new; } @@ -631,21 +775,21 @@ bgp_attr_unintern_sub (struct attr *attr) /* aspath refcount shoud be decrement. */ if (attr->aspath) aspath_unintern (&attr->aspath); - UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)); if (attr->community) community_unintern (&attr->community); - UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)); if (attr->extra) { if (attr->extra->ecommunity) ecommunity_unintern (&attr->extra->ecommunity); - UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)); if (attr->extra->cluster) cluster_unintern (attr->extra->cluster); - UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)); if (attr->extra->transit) transit_unintern (attr->extra->transit); @@ -654,43 +798,50 @@ bgp_attr_unintern_sub (struct attr *attr) /* Free bgp attribute and aspath. */ void -bgp_attr_unintern (struct attr **attr) +bgp_attr_unintern (struct attr **pattr) { + struct attr *attr = *pattr; struct attr *ret; struct attr tmp; + struct attr_extra tmp_extra; /* Decrement attribute reference. */ - (*attr)->refcnt--; + attr->refcnt--; - tmp = *(*attr); + tmp = *attr; - if ((*attr)->extra) + if (attr->extra) { - tmp.extra = bgp_attr_extra_new (); - memcpy (tmp.extra, (*attr)->extra, sizeof (struct attr_extra)); + tmp.extra = &tmp_extra; + memcpy (tmp.extra, attr->extra, sizeof (struct attr_extra)); } /* If reference becomes zero then free attribute object. */ - if ((*attr)->refcnt == 0) - { - ret = hash_release (attrhash, *attr); + if (attr->refcnt == 0) + { + ret = hash_release (attrhash, attr); assert (ret != NULL); - bgp_attr_extra_free (*attr); - XFREE (MTYPE_ATTR, *attr); - *attr = NULL; + bgp_attr_extra_free (attr); + XFREE (MTYPE_ATTR, attr); + *pattr = NULL; } bgp_attr_unintern_sub (&tmp); - bgp_attr_extra_free (&tmp); } void bgp_attr_flush (struct attr *attr) { if (attr->aspath && ! attr->aspath->refcnt) - aspath_free (attr->aspath); + { + aspath_free (attr->aspath); + attr->aspath = NULL; + } if (attr->community && ! attr->community->refcnt) - community_free (attr->community); + { + community_free (attr->community); + attr->community = NULL; + } if (attr->extra) { struct attr_extra *attre = attr->extra; @@ -698,9 +849,17 @@ bgp_attr_flush (struct attr *attr) if (attre->ecommunity && ! attre->ecommunity->refcnt) ecommunity_free (&attre->ecommunity); if (attre->cluster && ! attre->cluster->refcnt) - cluster_free (attre->cluster); + { + cluster_free (attre->cluster); + attre->cluster = NULL; + } if (attre->transit && ! attre->transit->refcnt) - transit_free (attre->transit); + { + transit_free (attre->transit); + attre->transit = NULL; + } + encap_free(attre->encap_subtlvs); + attre->encap_subtlvs = NULL; } } @@ -722,7 +881,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char *notify_datap = (length > 0 ? args->startp : NULL); /* Only relax error handling for eBGP peers */ - if (peer_sort (peer) != BGP_PEER_EBGP) + if (peer->sort != BGP_PEER_EBGP) { bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode, notify_datap, length); @@ -777,7 +936,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_WITHDRAW; /* default to reset */ - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Find out what is wrong with the path attribute flag bits and log the error. @@ -996,11 +1155,9 @@ bgp_attr_aspath_check (struct peer *const peer, struct struct bgp *bgp = peer->bgp; struct aspath *aspath; - bgp = peer->bgp; - /* Confederation sanity check. */ - if ((peer_sort (peer) == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) || - (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) + if ((peer->sort == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) || + (peer->sort == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) { zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host); bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, @@ -1011,7 +1168,7 @@ bgp_attr_aspath_check (struct peer *const peer, struct /* First AS check for EBGP. */ if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) { - if (peer_sort (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && ! aspath_firstas_check (attr->aspath, peer->as)) { zlog (peer->log, LOG_ERR, @@ -1095,7 +1252,7 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args) if (IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h)) { char buf[INET_ADDRSTRLEN]; - inet_ntop (AF_INET, &nexthop_h, buf, INET_ADDRSTRLEN); + inet_ntop (AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN); zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf); return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, @@ -1155,7 +1312,7 @@ bgp_attr_local_pref (struct bgp_attr_parser_args *args /* If it is contained in an UPDATE message that is received from an external peer, then this attribute MUST be ignored by the receiving speaker. */ - if (peer_sort (peer) == BGP_PEER_EBGP) + if (peer->sort == BGP_PEER_EBGP) { stream_forward_getp (peer->ibuf, length); return BGP_ATTR_PARSE_PROCEED; @@ -1267,7 +1424,17 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, int ignore_as4_path = 0; struct aspath *newpath; struct attr_extra *attre = attr->extra; - + + if (!attr->aspath) + { + /* NULL aspath shouldn't be possible as bgp_attr_parse should have + * checked that all well-known, mandatory attributes were present. + * + * Can only be a problem with peer itself - hard error + */ + return BGP_ATTR_PARSE_ERROR; + } + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) { /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR @@ -1347,9 +1514,9 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, /* need to reconcile NEW_AS_PATH and AS_PATH */ if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)))) { - newpath = aspath_reconcile_as4 (attr->aspath, as4_path); - aspath_unintern (&attr->aspath); - attr->aspath = aspath_intern (newpath); + newpath = aspath_reconcile_as4 (attr->aspath, as4_path); + aspath_unintern (&attr->aspath); + attr->aspath = aspath_intern (newpath); } return BGP_ATTR_PARSE_PROCEED; } @@ -1447,7 +1614,6 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, safi_t safi; bgp_size_t nlri_len; size_t start; - int ret; struct stream *s; struct peer *const peer = args->peer; struct attr *const attr = args->attr; @@ -1465,7 +1631,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s sent invalid length, %lu", __func__, peer->host, (unsigned long)length); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Load AFI, SAFI. */ @@ -1479,7 +1645,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Nexthop length check. */ @@ -1496,12 +1662,36 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, stream_getl (s); /* RD low */ stream_get (&attre->mp_nexthop_global_in, s, 4); break; -#ifdef HAVE_IPV6 + case 24: + { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } + /* fall through */ case 16: stream_get (&attre->mp_nexthop_global, s, 16); break; case 32: + case 48: + if (attre->mp_nexthop_len == 48) { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } stream_get (&attre->mp_nexthop_global, s, 16); + + if (attre->mp_nexthop_len == 48) { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } stream_get (&attre->mp_nexthop_local, s, 16); if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local)) { @@ -1518,18 +1708,17 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, attre->mp_nexthop_len = 16; } break; -#endif /* HAVE_IPV6 */ default: zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (!LEN_LEFT) { zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } { @@ -1545,20 +1734,9 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: (%s) Failed to read NLRI", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } - - if (safi != SAFI_MPLS_LABELED_VPN) - { - ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len); - if (ret < 0) - { - zlog_info ("%s: (%s) NLRI doesn't pass sanity check", - __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; - } - } - + mp_update->afi = afi; mp_update->safi = safi; mp_update->nlri = stream_pnt (s); @@ -1566,6 +1744,8 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, stream_forward_getp (s, nlri_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; #undef LEN_LEFT } @@ -1579,28 +1759,21 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *arg afi_t afi; safi_t safi; u_int16_t withdraw_len; - int ret; struct peer *const peer = args->peer; + struct attr *const attr = args->attr; const bgp_size_t length = args->length; s = peer->ibuf; #define BGP_MP_UNREACH_MIN_SIZE 3 if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE)) - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; afi = stream_getw (s); safi = stream_getc (s); withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE; - if (safi != SAFI_MPLS_LABELED_VPN) - { - ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len); - if (ret < 0) - return BGP_ATTR_PARSE_ERROR; - } - mp_withdraw->afi = afi; mp_withdraw->safi = safi; mp_withdraw->nlri = stream_pnt (s); @@ -1608,6 +1781,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *arg stream_forward_getp (s, withdraw_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; } @@ -1642,11 +1817,129 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args return BGP_ATTR_PARSE_PROCEED; } +/* Parse Tunnel Encap attribute in an UPDATE */ +static int +bgp_attr_encap( + uint8_t type, + struct peer *peer, /* IN */ + bgp_size_t length, /* IN: attr's length field */ + struct attr *attr, /* IN: caller already allocated */ + u_char flag, /* IN: attr's flags field */ + u_char *startp) +{ + bgp_size_t total; + struct attr_extra *attre = NULL; + struct bgp_attr_encap_subtlv *stlv_last = NULL; + uint16_t tunneltype; + + total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); + + if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS) + || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL)) + { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute flag isn't optional and transitive %d", flag); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); + return -1; + } + + if (BGP_ATTR_ENCAP == type) { + /* read outer TLV type and length */ + uint16_t tlv_length; + + if (length < 4) { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute not long enough to contain outer T,L"); + bgp_notify_send_with_data(peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + tunneltype = stream_getw (BGP_INPUT (peer)); + tlv_length = stream_getw (BGP_INPUT (peer)); + length -= 4; + + if (tlv_length != length) { + zlog (peer->log, LOG_ERR, "%s: tlv_length(%d) != length(%d)", + __func__, tlv_length, length); + } + } + + while (length >= 4) { + uint16_t subtype; + uint16_t sublength; + struct bgp_attr_encap_subtlv *tlv; + + if (BGP_ATTR_ENCAP == type) { + subtype = stream_getc (BGP_INPUT (peer)); + sublength = stream_getc (BGP_INPUT (peer)); + length -= 2; + } + + if (sublength > length) { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d", + sublength, length); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + + /* alloc and copy sub-tlv */ + /* TBD make sure these are freed when attributes are released */ + tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+sublength); + tlv->type = subtype; + tlv->length = sublength; + stream_get(tlv->value, peer->ibuf, sublength); + length -= sublength; + + /* attach tlv to encap chain */ + if (!attre) { + attre = bgp_attr_extra_get(attr); + if (BGP_ATTR_ENCAP == type) { + for (stlv_last = attre->encap_subtlvs; stlv_last && stlv_last->next; + stlv_last = stlv_last->next); + if (stlv_last) { + stlv_last->next = tlv; + } else { + attre->encap_subtlvs = tlv; + } + } + } else { + stlv_last->next = tlv; + } + stlv_last = tlv; + } + + if (attre && (BGP_ATTR_ENCAP == type)) { + attre->encap_tunneltype = tunneltype; + } + + if (length) { + /* spurious leftover data */ + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute length is bad: %d leftover octets", length); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + + return 0; +} + /* BGP unknown attribute treatment. */ static bgp_attr_parse_ret_t bgp_attr_unknown (struct bgp_attr_parser_args *args) { - bgp_size_t total; + bgp_size_t total = args->total; struct transit *transit; struct attr_extra *attre; struct peer *const peer = args->peer; @@ -1708,8 +2001,58 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* Well-known attribute check. */ +static int +bgp_attr_check (struct peer *peer, struct attr *attr) +{ + u_char type = 0; + + /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an + * empty UPDATE. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag) + return BGP_ATTR_PARSE_PROCEED; + + /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required + to carry any other path attributes.", though if MP_REACH_NLRI or NLRI + are present, it should. Check for any other attribute being present + instead. + */ + if (attr->flag == ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI)) + return BGP_ATTR_PARSE_PROCEED; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) + type = BGP_ATTR_ORIGIN; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) + type = BGP_ATTR_AS_PATH; + + /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and + * NLRI is empty. We can't easily check NLRI empty here though. + */ + if (!CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) + && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI))) + type = BGP_ATTR_NEXT_HOP; + + if (peer->sort == BGP_PEER_IBGP + && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + type = BGP_ATTR_LOCAL_PREF; + + if (type) + { + zlog (peer->log, LOG_WARNING, + "%s Missing well-known attribute %d / %s", + peer->host, type, LOOKUP (attr_str, type)); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MISS_ATTR, + &type, 1); + return BGP_ATTR_PARSE_ERROR; + } + return BGP_ATTR_PARSE_PROCEED; +} + /* Read attribute of update packet. This function is called from - bgp_update() in bgpd.c. */ + bgp_update_receive() in bgp_packet.c. */ bgp_attr_parse_ret_t bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw) @@ -1725,7 +2068,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, /* same goes for as4_aggregator */ struct aspath *as4_path = NULL; as_t as4_aggregator = 0; - struct in_addr as4_aggregator_addr = { 0 }; + struct in_addr as4_aggregator_addr = { .s_addr = 0 }; /* Initialize bitmap. */ memset (seen, 0, BGP_ATTR_BITMAP_SIZE); @@ -1890,11 +2233,22 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, case BGP_ATTR_EXT_COMMUNITIES: ret = bgp_attr_ext_communities (&attr_args); break; + case BGP_ATTR_ENCAP: + ret = bgp_attr_encap (type, peer, length, attr, flag, startp); + break; default: ret = bgp_attr_unknown (&attr_args); break; } + if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS) + { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + ret = BGP_ATTR_PARSE_ERROR; + } + /* If hard error occured immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { @@ -1902,9 +2256,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, "%s: Attribute %s, parse error", peer->host, LOOKUP (attr_str, type)); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return ret; @@ -1935,7 +2286,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, return BGP_ATTR_PARSE_ERROR; } } - /* Check final read pointer is same as end pointer. */ if (BGP_INPUT_PNT (peer) != endp) { @@ -1949,7 +2299,18 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; } - + + /* Check all mandatory well-known attributes are present */ + { + bgp_attr_parse_ret_t ret; + if ((ret = bgp_attr_check (peer, attr)) < 0) + { + if (as4_path) + aspath_unintern (&as4_path); + return ret; + } + } + /* * At this place we can see whether we got AS4_PATH and/or * AS4_AGGREGATOR from a 16Bit peer and act accordingly. @@ -1960,10 +2321,19 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, * So, to be defensive, we are not relying on any order and read * all attributes first, including these 32bit ones, and now, * afterwards, we look what and if something is to be done for as4. + * + * It is possible to not have AS_PATH, e.g. GR EoR and sole + * MP_UNREACH_NLRI. */ - if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, + /* actually... this doesn't ever return failure currently, but + * better safe than sorry */ + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)) + && bgp_attr_munge_as4_attrs (peer, attr, as4_path, as4_aggregator, &as4_aggregator_addr)) { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; @@ -2005,47 +2375,228 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, return BGP_ATTR_PARSE_PROCEED; } -/* Well-known attribute check. */ -int -bgp_attr_check (struct peer *peer, struct attr *attr) +int stream_put_prefix (struct stream *, struct prefix *); + +size_t +bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, + struct attr *attr) { - u_char type = 0; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) - type = BGP_ATTR_ORIGIN; + size_t sizep; - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) - type = BGP_ATTR_AS_PATH; + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_MP_REACH_NLRI); + sizep = stream_get_endp (s); + stream_putw (s, 0); /* Marker: Attribute length. */ - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) - type = BGP_ATTR_NEXT_HOP; + stream_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); - if (peer_sort (peer) == BGP_PEER_IBGP - && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) - type = BGP_ATTR_LOCAL_PREF; + /* Nexthop */ + switch (afi) + { + case AFI_IP: + switch (safi) + { + case SAFI_MULTICAST: + stream_putc (s, 4); + stream_put_ipv4 (s, attr->nexthop.s_addr); + break; + case SAFI_MPLS_VPN: + stream_putc (s, 12); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + case SAFI_ENCAP: + stream_putc (s, 4); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + case SAFI_UNICAST: /* invalid for IPv4 */ + default: + break; + } + break; + case AFI_IP6: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + { + struct attr_extra *attre = attr->extra; - if (type) + assert (attr->extra); + stream_putc (s, attre->mp_nexthop_len); + stream_put (s, &attre->mp_nexthop_global, 16); + if (attre->mp_nexthop_len == 32) + stream_put (s, &attre->mp_nexthop_local, 16); + } + break; + case SAFI_MPLS_VPN: + { + struct attr_extra *attre = attr->extra; + + assert (attr->extra); + if (attre->mp_nexthop_len == 16) { + stream_putc (s, 24); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_global, 16); + } else if (attre->mp_nexthop_len == 32) { + stream_putc (s, 48); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_global, 16); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_local, 16); + } + } + break; + case SAFI_ENCAP: + assert (attr->extra); + stream_putc (s, 16); + stream_put (s, &attr->extra->mp_nexthop_global, 16); + break; + default: + break; + } + break; + default: + break; + } + + /* SNPA */ + stream_putc (s, 0); + return sizep; +} + +void +bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, + struct prefix *p, struct prefix_rd *prd, + u_char *tag) +{ + if (safi == SAFI_MPLS_VPN) { - zlog (peer->log, LOG_WARNING, - "%s Missing well-known attribute %d.", - peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, - &type, 1); - return BGP_ATTR_PARSE_ERROR; + /* Tag, RD, Prefix write. */ + stream_putc (s, p->prefixlen + 88); + stream_put (s, tag, 3); + stream_put (s, prd->val, 8); + stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); } - return BGP_ATTR_PARSE_PROCEED; + else + stream_put_prefix (s, p); } - -int stream_put_prefix (struct stream *, struct prefix *); +size_t +bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p) +{ + int size = PSIZE (p->prefixlen); + if (safi == SAFI_MPLS_VPN) + size += 88; + return size; +} + +/* + * Encodes the tunnel encapsulation attribute + */ +static void +bgp_packet_mpattr_tea( + struct bgp *bgp, + struct peer *peer, + struct stream *s, + struct attr *attr, + uint8_t attrtype) +{ + unsigned int attrlenfield = 0; + unsigned int attrhdrlen = 0; + struct bgp_attr_encap_subtlv *subtlvs; + struct bgp_attr_encap_subtlv *st; + const char *attrname; + + if (!attr || !attr->extra) + return; + + switch (attrtype) { + case BGP_ATTR_ENCAP: + attrname = "Tunnel Encap"; + subtlvs = attr->extra->encap_subtlvs; + + /* + * The tunnel encap attr has an "outer" tlv. + * T = tunneltype, + * L = total length of subtlvs, + * V = concatenated subtlvs. + */ + attrlenfield = 2 + 2; /* T + L */ + attrhdrlen = 1 + 1; /* subTLV T + L */ + break; + + default: + assert(0); + } + + + /* if no tlvs, don't make attr */ + if (subtlvs == NULL) + return; + + /* compute attr length */ + for (st = subtlvs; st; st = st->next) { + attrlenfield += (attrhdrlen + st->length); + } + + if (attrlenfield > 0xffff) { + zlog (peer->log, LOG_ERR, + "%s attribute is too long (length=%d), can't send it", + attrname, + attrlenfield); + return; + } + + if (attrlenfield > 0xff) { + /* 2-octet length field */ + stream_putc (s, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, attrtype); + stream_putw (s, attrlenfield & 0xffff); + } else { + /* 1-octet length field */ + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, attrtype); + stream_putc (s, attrlenfield & 0xff); + } + + if (attrtype == BGP_ATTR_ENCAP) { + /* write outer T+L */ + stream_putw(s, attr->extra->encap_tunneltype); + stream_putw(s, attrlenfield - 4); + } + + /* write each sub-tlv */ + for (st = subtlvs; st; st = st->next) { + if (attrtype == BGP_ATTR_ENCAP) { + stream_putc (s, st->type); + stream_putc (s, st->length); + } + stream_put (s, st->value, st->length); + } +} + +void +bgp_packet_mpattr_end (struct stream *s, size_t sizep) +{ + /* Set MP attribute length. Don't count the (2) bytes used to encode + the attr length */ + stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2); +} + /* Make attribute packet. */ bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *peer, - struct stream *s, struct attr *attr, struct prefix *p, - afi_t afi, safi_t safi, struct peer *from, - struct prefix_rd *prd, u_char *tag) + struct stream *s, struct attr *attr, + struct prefix *p, afi_t afi, safi_t safi, + struct peer *from, struct prefix_rd *prd, u_char *tag) { size_t cp; size_t aspath_sizep; @@ -2060,6 +2611,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe /* Remember current pointer. */ cp = stream_get_endp (s); + if (p && !(afi == AFI_IP && safi == SAFI_UNICAST)) + { + size_t mpattrlen_pos = 0; + mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag); + bgp_packet_mpattr_end(s, mpattrlen_pos); + } + /* Origin attribute. */ stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_ORIGIN); @@ -2069,7 +2628,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe /* AS path attribute. */ /* If remote-peer is EBGP */ - if (peer_sort (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) || attr->aspath->segments == NULL) && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))) @@ -2085,12 +2644,19 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe } else { - aspath = aspath_add_seq (aspath, peer->local_as); - if (peer->change_local_as) + if (peer->change_local_as) { + /* If replace-as is specified, we only use the change_local_as when + advertising routes. */ + if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) { + aspath = aspath_add_seq (aspath, peer->local_as); + } aspath = aspath_add_seq (aspath, peer->change_local_as); + } else { + aspath = aspath_add_seq (aspath, peer->local_as); + } } } - else if (peer_sort (peer) == BGP_PEER_CONFED) + else if (peer->sort == BGP_PEER_CONFED) { /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */ aspath = aspath_dup (attr->aspath); @@ -2121,7 +2687,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe send_as4_path = 1; /* we'll do this later, at the correct place */ /* Nexthop attribute. */ - if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP) + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP && + safi == SAFI_UNICAST) /* only write NH attr for unicast safi */ { stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_NEXT_HOP); @@ -2147,8 +2714,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe } /* Local preference. */ - if (peer_sort (peer) == BGP_PEER_IBGP || - peer_sort (peer) == BGP_PEER_CONFED) + if (peer->sort == BGP_PEER_IBGP || + peer->sort == BGP_PEER_CONFED) { stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_LOCAL_PREF); @@ -2221,9 +2788,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe } /* Route Reflector. */ - if (peer_sort (peer) == BGP_PEER_IBGP + if (peer->sort == BGP_PEER_IBGP && from - && peer_sort (from) == BGP_PEER_IBGP) + && from->sort == BGP_PEER_IBGP) { /* Originator ID. */ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); @@ -2261,96 +2828,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe } } -#ifdef HAVE_IPV6 - /* If p is IPv6 address put it into attribute. */ - if (p->family == AF_INET6) - { - unsigned long sizep; - struct attr_extra *attre = attr->extra; - - assert (attr->extra); - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute length. */ - stream_putw (s, AFI_IP6); /* AFI */ - stream_putc (s, safi); /* SAFI */ - - stream_putc (s, attre->mp_nexthop_len); - - if (attre->mp_nexthop_len == 16) - stream_put (s, &attre->mp_nexthop_global, 16); - else if (attre->mp_nexthop_len == 32) - { - stream_put (s, &attre->mp_nexthop_global, 16); - stream_put (s, &attre->mp_nexthop_local, 16); - } - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } -#endif /* HAVE_IPV6 */ - - if (p->family == AF_INET && safi == SAFI_MULTICAST) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute Length. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MULTICAST); /* SAFI */ - - stream_putc (s, 4); - stream_put_ipv4 (s, attr->nexthop.s_addr); - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - - if (p->family == AF_INET && safi == SAFI_MPLS_VPN) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); /* SAFI */ - - stream_putc (s, 12); - stream_putl (s, 0); - stream_putl (s, 0); - stream_put (s, &attr->extra->mp_nexthop_global_in, 4); - - /* SNPA */ - stream_putc (s, 0); - - /* Tag, RD, Prefix write. */ - stream_putc (s, p->prefixlen + 88); - stream_put (s, tag, 3); - stream_put (s, prd->val, 8); - stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - /* Extended Communities attribute. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES))) @@ -2359,8 +2836,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe assert (attre); - if (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if (peer->sort == BGP_PEER_IBGP + || peer->sort == BGP_PEER_CONFED) { if (attre->ecommunity->size * 8 > 255) { @@ -2463,7 +2940,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe stream_putl (s, attr->extra->aggregator_as); stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); } - + + if ((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN)) + { + /* Tunnel Encap attribute */ + bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP); + } + /* Unknown transit attribute. */ if (attr->extra && attr->extra->transit) stream_put (s, attr->extra->transit->val, attr->extra->transit->length); @@ -2472,50 +2956,35 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *pe return stream_get_endp (s) - cp; } -bgp_size_t -bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p, - afi_t afi, safi_t safi, struct prefix_rd *prd, - u_char *tag) +size_t +bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) { - unsigned long cp; unsigned long attrlen_pnt; - bgp_size_t size; - cp = stream_get_endp (s); - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); attrlen_pnt = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ + stream_putw (s, 0); /* Length of this attribute. */ - stream_putw (s, family2afi (p->family)); + stream_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); + return attrlen_pnt; +} - if (safi == SAFI_MPLS_VPN) - { - /* SAFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); +void +bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag) +{ + bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag); +} - /* prefix. */ - stream_putc (s, p->prefixlen + 88); - stream_put (s, tag, 3); - stream_put (s, prd->val, 8); - stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); - } - else - { - /* SAFI */ - stream_putc (s, safi); - - /* prefix */ - stream_put_prefix (s, p); - } - - /* Set MP attribute length. */ - size = stream_get_endp (s) - attrlen_pnt - 1; - stream_putc_at (s, attrlen_pnt, size); - - return stream_get_endp (s) - cp; +void +bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt) +{ + bgp_packet_mpattr_end (s, attrlen_pnt); } /* Initialization of attribute. */ @@ -2575,9 +3044,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *a /* Nexthop attribute. */ /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */ if(prefix != NULL -#ifdef HAVE_IPV6 && prefix->family != AF_INET6 -#endif /* HAVE_IPV6 */ ) { stream_putc (s, BGP_ATTR_FLAG_TRANS); @@ -2641,7 +3108,6 @@ bgp_dump_routes_attr (struct stream *s, struct attr *a stream_put (s, attr->community->val, attr->community->size * 4); } -#ifdef HAVE_IPV6 /* Add a MP_NLRI attribute to dump the IPv6 next hop */ if (prefix != NULL && prefix->family == AF_INET6 && attr->extra && (attr->extra->mp_nexthop_len == 16 || attr->extra->mp_nexthop_len == 32) ) @@ -2673,7 +3139,6 @@ bgp_dump_routes_attr (struct stream *s, struct attr *a /* Set MP attribute length. */ stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); } -#endif /* HAVE_IPV6 */ /* Return total size of attribute. */ len = stream_get_endp (s) - cp - 2;