diff options
Diffstat (limited to 'bgpd/bgp_attr.c')
-rw-r--r-- | bgpd/bgp_attr.c | 432 |
1 files changed, 400 insertions, 32 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 4c72d80a..8a6d542e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -56,6 +56,8 @@ struct message attr_str [] = { BGP_ATTR_RCID_PATH, "RCID_PATH" }, { BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI" }, { BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" }, + { BGP_ATTR_AS4_PATH, "AS4_PATH" }, + { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" }, { 0, NULL } }; @@ -655,8 +657,6 @@ static int bgp_attr_aspath (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag, u_char *startp) { - struct bgp *bgp; - struct aspath *aspath; bgp_size_t total; total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); @@ -674,8 +674,16 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, return -1; } + /* + * peer with AS4 => will get 4Byte ASnums + * otherwise, will get 16 Bit + */ + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + attr->aspath = aspath_parse (peer->ibuf, length, 1); + else + attr->aspath = aspath_parse (peer->ibuf, length, 0); + /* In case of IBGP, length will be zero. */ - attr->aspath = aspath_parse (peer->ibuf, length); if (! attr->aspath) { zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length); @@ -685,6 +693,28 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, return -1; } + /* Forward pointer. */ +/* stream_forward_getp (peer->ibuf, length);*/ + + /* Set aspath attribute flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); + + return 0; +} + +static int bgp_attr_aspath_check( struct peer *peer, + struct attr *attr) +{ + /* These checks were part of bgp_attr_aspath, but with + * as4 we should to check aspath things when + * aspath synthesizing with as4_aspath has already taken place. + * Otherwise we check ASPATH and use the synthesized thing, and that is + * not right. + * So do the checks later, i.e. here + */ + struct bgp *bgp = peer->bgp; + struct aspath *aspath; + bgp = peer->bgp; /* First AS check for EBGP. */ @@ -712,11 +742,20 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, attr->aspath = aspath_intern (aspath); } - /* Forward pointer. */ -/* stream_forward_getp (peer->ibuf, length);*/ + return 0; + +} + +/* Parse AS4 path information. This function is another wrapper of + aspath_parse. */ +static int +bgp_attr_as4_aspath (struct peer *peer, bgp_size_t length, + struct attr *attr, u_char flag, u_char *startp) +{ + attr->as4_aspath = aspath_parse (peer->ibuf, length, 1); /* Set aspath attribute flag. */ - attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH); return 0; } @@ -842,16 +881,31 @@ static int bgp_attr_aggregator (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag) { - if (length != 6) + int wantedlen; + + /* + * 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; + else + wantedlen = 6; + + if (length != wantedlen) { - zlog (peer->log, LOG_ERR, "Aggregator length is not 6 [%d]", length); + zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length); bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } - attr->aggregator_as = stream_getw (peer->ibuf); + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + attr->aggregator_as = stream_getl (peer->ibuf); + else + attr->aggregator_as = stream_getw (peer->ibuf); + attr->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf); /* Set atomic aggregate flag. */ @@ -860,6 +914,34 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length, return 0; } +/* New Aggregator attribute */ +static int +bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length, + struct attr *attr, u_char flag) +{ + as_t as4_aggregator_as; + u_int32_t as4_aggregator_address; + + if (length != 8) + { + zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length); + + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + return -1; + } + as4_aggregator_as = stream_getl (peer->ibuf); + as4_aggregator_address = stream_get_ipv4 (peer->ibuf); + /* Set atomic aggregate flag. */ + attr->as4_aggregator_as = as4_aggregator_as; + attr->as4_aggregator_addr.s_addr = as4_aggregator_address; + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR); + + return 0; +} + /* Community attribute. */ static int bgp_attr_community (struct peer *peer, bgp_size_t length, @@ -1238,6 +1320,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_AS_PATH: ret = bgp_attr_aspath (peer, length, attr, flag, startp); break; + case BGP_ATTR_AS4_PATH: + ret = bgp_attr_as4_aspath (peer, length, attr, flag, startp); + break; case BGP_ATTR_NEXT_HOP: ret = bgp_attr_nexthop (peer, length, attr, flag, startp); break; @@ -1253,6 +1338,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_AGGREGATOR: ret = bgp_attr_aggregator (peer, length, attr, flag); break; + case BGP_ATTR_AS4_AGGREGATOR: + ret = bgp_attr_as4_aggregator (peer, length, attr, flag); + break; case BGP_ATTR_COMMUNITIES: ret = bgp_attr_community (peer, length, attr, flag); break; @@ -1303,6 +1391,169 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return -1; } + /* + * At this place we can see whether we got AS4_PATH and/or + * AS4_AGGREGATOR from a 16Bit peer and act accordingly. + * We can not do this before we've read all attributes because + * the as4 handling does not say whether AS4_PATH has to be sent + * after AS_PATH or not - and when AS4_AGGREGATOR will be send + * in relationship to AGGREGATOR. + * 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. + */ + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + { + /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR + * if given. + * It is worth a warning though, because the peer really + * should not send them + */ + if ( CHECK_BITMAP (seen, BGP_ATTR_AS4_PATH) ) + { + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ( "[AS4] %s BGP AS4 capable peer send AS4_PATH", peer->host); + } + if ( CHECK_BITMAP (seen, BGP_ATTR_AS4_AGGREGATOR) ) + { + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ( "[AS4] %s BGP AS4 capable peer send AS4_AGGREGATOR", peer->host); + } + } + else + { + /* We have a asn16 peer. First, look for AS4_AGGREGATOR + * because that may override AS4_PATH + */ + int ignore_as4_aspath = 0; + if ( attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) ) + { + if ( attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) ) + { + /* received both. + * if the as_number in aggregator is not AS_TRANS, + * then AS4_AGGREGATOR and AS4_PATH shall be ignored + * and the Aggregator shall be taken as + * info on the aggregating node, and the AS_PATH + * shall be taken as the AS_PATH + * otherwise + * the Aggregator shall be ignored and the + * AS4_AGGREGATOR shall be taken as the + * Aggregating node and the AS_PATH is to be + * constructed "as in all other cases" + */ + if ( attr->aggregator_as != BGP_AS_TRANS ) + { + /* ignore */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ( + "[AS4] %s BGP not AS4 capable peer send AGGREGATOR != AS_TRANS and AS4_AGGREGATOR, so ignore AS4_AGGREGATOR and AS4_PATH", peer->host); + ignore_as4_aspath = 1; + } + else + { + /* "New_aggregator shall be taken as aggregator" */ + attr->aggregator_as = attr->as4_aggregator_as; + attr->aggregator_addr.s_addr = attr->as4_aggregator_addr.s_addr; + } + } + else + { + /* We received a AS4_AGGREGATOR but no AGGREGATOR. + * That is bogus - but reading the conditions + * we have to handle AS4_AGGREGATOR as if it were + * AGGREGATOR in that case + */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ( + "[AS4] %s BGP not AS4 capable peer send AS4_AGGREGATOR but no AGGREGATOR, will take it as if AGGREGATOR with AS_TRANS had been there", peer->host); + attr->aggregator_as = attr->as4_aggregator_as; + /* sweep it under the carpet and simulate a "good" AGGREGATOR */ + SET_BITMAP (seen, BGP_ATTR_AGGREGATOR); + attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)); + } + } + if ( !ignore_as4_aspath && (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH))) ) + { + if ( ! (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH)))) + { + /* Hu? This is not supposed to happen at all! + * got as4_aspath 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); + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + /* We have AS4_PATH AND AS_PATH + * Calculate the number of as numbers in both attributes! + * What is meant is the number of "hops"! + * if numas_in_aspath < numas_in_as4_aspath => + * ignore AS4_PATH, take AS_PATH. + * else + * take as many as_numbers and path segments from AS_PATH + * and prepend them to AS4_PATH so that + * num_new = numas_in_aspath + * the resulting thing is our AS_PATH + * An AS_SET or AS_CONFED_SET is one hop + * every other thing has as many hops as asnums + */ + int hopnumdiff = aspath_count_hops( attr->aspath ) - aspath_count_hops( attr->as4_aspath ); + + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ( + "[AS4] %s fiddling with aspath and newaspath, hopnumdifference is %d", + peer->host, hopnumdiff); + if ( hopnumdiff >= 0 ) + { + if ( BGP_DEBUG(as4, AS4)) + zlog_debug( + "[AS4] %s got AS_PATH %s and AS4_PATH %s synthesizing now", peer->host, attr->aspath->str, attr->as4_aspath->str); + aspath_truncateathopsandjoin( &attr->aspath, &attr->as4_aspath, hopnumdiff ); + if ( BGP_DEBUG(as4, AS4)) + zlog_debug( + "[AS4] %s result of synthesizing is %s", peer->host, attr->aspath->str); + } + } + } + + /* At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath + * so we can chuck as4_aggregator and as4_aspath alltogether in + * order to save memory + */ + if ( attr->as4_aspath ) + { + aspath_unintern( attr->as4_aspath ); /* unintern - it is in the hash */ + attr->as4_aspath = NULL; + /* The flag that we got this is still there, but that does not + * do any trouble + */ + } + /* + * The "rest" of the code does nothing with as4_aggregator. + * there is no memory attached specifically which is not part + * of the attr. + * so ignoring just means do nothing. + */ + /* + * Finally do the checks on the aspath we did not do yet + * because we waited for a potentially synthesized aspath. + */ + if ( attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH))) + { + ret = bgp_attr_aspath_check( peer, attr ); + if ( ret < 0 ) + return ret; + } + /* Finally intern unknown attribute. */ if (attr->transit) attr->transit = transit_intern (attr->transit); @@ -1354,6 +1605,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, { size_t cp; unsigned int aspath_data_size; + unsigned int numas; struct aspath *aspath; if (! bgp) @@ -1401,21 +1653,92 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, else aspath = attr->aspath; + /* If peer is not AS4 capable, then: + * - send the created AS_PATH out as AS4_PATH (optional, transitive), + * but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path segment + * types are in it (i.e. exclude them if they are there) + * AND do this only if there is at least one asnum > 65535 in the path! + * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and change + * all ASnums > 65535 to BGP_AS_TRANS + * + * Beware: aspath_data_size is ONLY correct if there is no aspath segment + * longer 255, and no two segments which are able to be merged + * That thing is in the code for a looonng time and nobody bothers. + */ + /* AS path attribute extended length bit check. */ aspath_data_size = aspath_size (aspath); - if (aspath_data_size > 255) + numas = aspath_count_numas( aspath ); + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) { - stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putw (s, aspath_data_size); + /* this is the easy part, peer is as4 capable, send 4 byte aspath */ + if (aspath_data_size > 255) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + stream_putw (s, aspath_data_size); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AS_PATH); + stream_putc (s, aspath_data_size); + } + aspath_put (s, aspath, 1); } else { - stream_putc (s, BGP_ATTR_FLAG_TRANS); - stream_putc (s, BGP_ATTR_AS_PATH); - stream_putc (s, aspath_data_size); + /* + * size of 16bit aspath is smaller by number of asnums * 2Bytes + * actually, number os asnums * (sizeof(as_t) - sizeof(as16_t)), + * but that is nit-picking. + */ + if (aspath_data_size - 2*numas > 255) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + stream_putw (s, aspath_data_size - 2*numas ); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AS_PATH); + stream_putc (s, aspath_data_size - 2*numas ); + } + aspath_put (s, aspath, 0); + + if ( aspath_count_num32as( aspath ) > 0 ) + { + /* put out AS4_PATH (only if there are ASnums > 65535 in path */ + + /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET + * path segments! + * Hm, I wonder... confederation things *should* only be at + * the beginning of an aspath, right? Then we should use + * aspath_delete_confed_seq for this, because it is already + * there! (JK) + * Folks, talk to me: what is reasonable here!? + */ + if ( aspath_count_confeds(aspath) > 0 ) { + aspath_cleanoutall_asconfeds( &aspath, &aspath_data_size ); + } + + if (aspath_data_size > 255) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS4_PATH); + stream_putw (s, aspath_data_size); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_AS4_PATH); + stream_putc (s, aspath_data_size); + } + aspath_put (s, aspath, 1); + } } - aspath_put (s, aspath); if (aspath != attr->aspath) aspath_free (aspath); @@ -1467,11 +1790,50 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, /* Aggregator. */ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)) { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); - stream_putc (s, BGP_ATTR_AGGREGATOR); - stream_putc (s, 6); - stream_putw (s, attr->aggregator_as); - stream_put_ipv4 (s, attr->aggregator_addr.s_addr); + /* If peer is AS4 capable, + * then + * send BGP_ATTR_AGGREGATOR with 32 bit AS value + * else + * if attr->aggregator_as > 65535 + * change the aggregator_as in the AGGREGATOR to AS_TRANS + * and send out a AS4_AGGREGATOR (opt, transitional) + * with the correct 4 Bytes thingy. + * else + * proceed as you always did + */ + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AGGREGATOR); + stream_putc (s, 8); + stream_putl (s, attr->aggregator_as); + stream_put_ipv4 (s, attr->aggregator_addr.s_addr); + } + else + { + if ( attr->aggregator_as > 65535 ) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AGGREGATOR); + stream_putc (s, 6); + stream_putw (s, BGP_AS_TRANS); + stream_put_ipv4 (s, attr->aggregator_addr.s_addr); + + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AS4_AGGREGATOR); + stream_putc (s, 8); + stream_putl (s, attr->aggregator_as); + stream_put_ipv4 (s, attr->aggregator_addr.s_addr); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AGGREGATOR); + stream_putc (s, 6); + stream_putw (s, (u_int16_t) attr->aggregator_as); + stream_put_ipv4 (s, attr->aggregator_addr.s_addr); + } + } } /* Community attribute. */ @@ -1758,7 +2120,7 @@ bgp_attr_init (void) /* Make attribute packet. */ void bgp_dump_routes_attr (struct stream *s, struct attr *attr, - struct prefix *prefix) + struct prefix *prefix, int dump_small_mp_nlri) { unsigned long cp; unsigned long len; @@ -1791,7 +2153,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, stream_putc (s, BGP_ATTR_AS_PATH); stream_putc (s, aspathlen); } - aspath_put (s, aspath); + aspath_put (s, aspath, 1); /* always 32bit. aspath_put size remarks apply */ /* Nexthop attribute. */ /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */ @@ -1838,8 +2200,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, { stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_AGGREGATOR); - stream_putc (s, 6); - stream_putw (s, attr->aggregator_as); + stream_putc (s, 8); + stream_putl (s, attr->aggregator_as); stream_put_ipv4 (s, attr->aggregator_addr.s_addr); } @@ -1874,8 +2236,11 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, /* MP header */ stream_putc (s, 0); /* Length of this attribute. */ - stream_putw(s, AFI_IP6); /* AFI */ - stream_putc(s, SAFI_UNICAST); /* SAFI */ + if(!dump_small_mp_nlri) + { + stream_putw(s, AFI_IP6); /* AFI */ + stream_putc(s, SAFI_UNICAST); /* SAFI */ + } /* Next hop */ stream_putc(s, attr->mp_nexthop_len); @@ -1883,11 +2248,14 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, if(attr->mp_nexthop_len == 32) stream_put(s, &attr->mp_nexthop_local, 16); - /* SNPA */ - stream_putc(s, 0); + if(!dump_small_mp_nlri) + { + /* SNPA */ + stream_putc(s, 0); - /* Prefix */ - stream_put_prefix(s, prefix); + /* Prefix */ + stream_put_prefix(s, prefix); + } /* Set MP attribute length. */ stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); |