bgpd: Add support for BGP Large Communities
As described by Michael Lambert <lambert@psc.edu> to the list:
Traditional communities are four-octet entities to support two-octet ASNs
and are usually represented as <asn>:<data>. Large communities are an
enhancement to support four-octet ASNs and are 12 octets long, represented
as <asn>:<data-1>:<data-2>.
This issue has been tracked in quagga bugzilla ticket #875, which documents
some of the usage and indicates that some testing has been done.
TODO: Documentation - update doc/bgpd.texi.
* bgp_attr.{c,h}: Add BGP_ATTR_LARGE_COMMUNITIES codepoint. Add
(struct lcommunity *) to (struct bgp_attr_extra).
* bgp_clist.{c,h}: Large community codepoints and routines.
* bgp_route.c: Display support.
* bgp_routemap.c: 'match lcommunity', 'set large-community' and
'set large-comm-list'
* bgp_vty.c: Peer configuration, add 'large' to 'neighbor send-community ..'.
Add "show ip bgp large-community", ""ip large-community-list ...".
Authors: Keyur Patel <keyur@arrcus.com>
Job Snijders <job@instituut.net>
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 6aab50a..a79a03c 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -39,6 +39,7 @@
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
#include "table.h"
#include "bgp_encap_types.h"
@@ -65,6 +66,18 @@
{ BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" },
{ BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" },
{ BGP_ATTR_ENCAP, "ENCAP" },
+ { 21, ""},
+ { 22, ""},
+ { 23, ""},
+ { 24, ""},
+ { 25, ""},
+ { 26, ""},
+ { 27, ""},
+ { 28, ""},
+ { 29, ""},
+ { 30, ""},
+ { 31, ""},
+ { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" }
};
static const int attr_str_max = array_size(attr_str);
@@ -505,6 +518,8 @@
if (extra)
{
+ if (extra->lcommunity)
+ MIX(lcommunity_hash_make (extra->lcommunity));
if (extra->ecommunity)
MIX(ecommunity_hash_make (extra->ecommunity));
if (extra->cluster)
@@ -547,6 +562,7 @@
&& IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local)
&& IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in)
&& ae1->ecommunity == ae2->ecommunity
+ && ae1->lcommunity == ae2->lcommunity
&& ae1->cluster == ae2->cluster
&& ae1->transit == ae2->transit
&& (ae1->encap_tunneltype == ae2->encap_tunneltype)
@@ -658,6 +674,13 @@
attre->ecommunity->refcnt++;
}
+ if (attre->lcommunity)
+ {
+ if (! attre->lcommunity->refcnt)
+ attre->lcommunity = lcommunity_intern (attre->lcommunity);
+ else
+ attre->lcommunity->refcnt++;
+ }
if (attre->cluster)
{
if (! attre->cluster->refcnt)
@@ -791,6 +814,10 @@
if (attr->extra->ecommunity)
ecommunity_unintern (&attr->extra->ecommunity);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
+
+ if (attr->extra->lcommunity)
+ lcommunity_unintern (&attr->extra->lcommunity);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
if (attr->extra->cluster)
cluster_unintern (attr->extra->cluster);
@@ -853,6 +880,8 @@
if (attre->ecommunity && ! attre->ecommunity->refcnt)
ecommunity_free (&attre->ecommunity);
+ if (attre->lcommunity && ! attre->lcommunity->refcnt)
+ lcommunity_free (&attre->lcommunity);
if (attre->cluster && ! attre->cluster->refcnt)
{
cluster_free (attre->cluster);
@@ -1002,6 +1031,7 @@
[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,
+ [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
};
static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
@@ -1791,6 +1821,37 @@
return BGP_ATTR_PARSE_PROCEED;
}
+/* Large Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_large_community (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;
+
+ if (length == 0)
+ {
+ if (attr->extra)
+ attr->extra->lcommunity = NULL;
+ /* Empty extcomm doesn't seem to be invalid per se */
+ return BGP_ATTR_PARSE_PROCEED;
+ }
+
+ (bgp_attr_extra_get (attr))->lcommunity =
+ lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+ /* XXX: fix ecommunity_parse to use stream API */
+ stream_forward_getp (peer->ibuf, length);
+
+ if (attr->extra && !attr->extra->lcommunity)
+ return bgp_attr_malformed (args,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/* Extended Community attribute. */
static bgp_attr_parse_ret_t
bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
@@ -1812,7 +1873,7 @@
/* XXX: fix ecommunity_parse to use stream API */
stream_forward_getp (peer->ibuf, length);
- if (!attr->extra->ecommunity)
+ if (attr->extra && !attr->extra->ecommunity)
return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
args->total);
@@ -2225,6 +2286,9 @@
case BGP_ATTR_COMMUNITIES:
ret = bgp_attr_community (&attr_args);
break;
+ case BGP_ATTR_LARGE_COMMUNITIES:
+ ret = bgp_attr_large_community (&attr_args);
+ break;
case BGP_ATTR_ORIGINATOR_ID:
ret = bgp_attr_originator_id (&attr_args);
break;
@@ -2794,6 +2858,28 @@
stream_put (s, attr->community->val, attr->community->size * 4);
}
+ /*
+ * Large Community attribute.
+ */
+ if (attr->extra &&
+ CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)))
+ {
+ if (attr->extra->lcommunity->size * 12 > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putw (s, attr->extra->lcommunity->size * 12);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putc (s, attr->extra->lcommunity->size * 12);
+ }
+ stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+ }
+
/* Route Reflector. */
if (peer->sort == BGP_PEER_IBGP
&& from
@@ -3002,6 +3088,7 @@
attrhash_init ();
community_init ();
ecommunity_init ();
+ lcommunity_init ();
cluster_init ();
transit_init ();
}
@@ -3013,6 +3100,7 @@
attrhash_finish ();
community_finish ();
ecommunity_finish ();
+ lcommunity_finish ();
cluster_finish ();
transit_finish ();
}
@@ -3115,6 +3203,25 @@
stream_put (s, attr->community->val, attr->community->size * 4);
}
+ /* Large Community attribute. */
+ if (attr->extra && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))
+ {
+ if (attr->extra->lcommunity->size * 12 > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putw (s, attr->extra->lcommunity->size * 12);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putc (s, attr->extra->lcommunity->size * 12);
+ }
+
+ stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+ }
+
/* 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) )