diff options
author | David Lamparter <equinox@opensourcerouting.org> | 2017-01-25 23:04:29 +0100 |
---|---|---|
committer | David Lamparter <equinox@opensourcerouting.org> | 2017-01-25 23:04:29 +0100 |
commit | dcb817fd27f75929b20fcc88167f246b12a42d4f (patch) | |
tree | 9a3219dd2df79f990d3bc7efe4ac2fa760a83d8b /bgpd | |
parent | Merge pull request #116 from bingen/bugfix/zclient_missing_break (diff) | |
parent | bgpd: lcommunity: fix minor issues (diff) | |
download | frr-dcb817fd27f75929b20fcc88167f246b12a42d4f.tar.xz frr-dcb817fd27f75929b20fcc88167f246b12a42d4f.zip |
Merge branch 'frr/pull/92' (BGP Large Community support)
Acked-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/Makefile.am | 6 | ||||
-rw-r--r-- | bgpd/bgp_attr.c | 103 | ||||
-rw-r--r-- | bgpd/bgp_attr.h | 5 | ||||
-rw-r--r-- | bgpd/bgp_clist.c | 320 | ||||
-rw-r--r-- | bgpd/bgp_clist.h | 18 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.c | 1 | ||||
-rw-r--r-- | bgpd/bgp_encap.c | 1 | ||||
-rw-r--r-- | bgpd/bgp_lcommunity.c | 569 | ||||
-rw-r--r-- | bgpd/bgp_lcommunity.h | 74 | ||||
-rw-r--r-- | bgpd/bgp_memory.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_memory.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_mpath.c | 14 | ||||
-rw-r--r-- | bgpd/bgp_packet.c | 1 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 195 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 418 | ||||
-rw-r--r-- | bgpd/bgp_vty.c | 473 | ||||
-rw-r--r-- | bgpd/bgpd.c | 27 | ||||
-rw-r--r-- | bgpd/bgpd.h | 2 |
18 files changed, 2204 insertions, 30 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 69c0504af..6e36a950d 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -75,7 +75,8 @@ libbgp_a_SOURCES = \ bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \ bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \ bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \ - bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ + bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_lcommunity.c \ + bgp_mplsvpn.c bgp_nexthop.c \ bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \ bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) @@ -85,7 +86,8 @@ noinst_HEADERS = \ bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \ bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \ - bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ + bgp_ecommunity.h bgp_lcommunity.h \ + bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \ bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \ $(BGP_VNC_RFAPI_HD) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 7538a5077..f4995faab 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -42,6 +42,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_updgrp.h" #include "bgpd/bgp_encap_types.h" #if ENABLE_BGP_VNC @@ -76,6 +77,7 @@ static const struct message attr_str [] = #if ENABLE_BGP_VNC { BGP_ATTR_VNC, "VNC" }, #endif + { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" } }; static const int attr_str_max = array_size(attr_str); @@ -670,6 +672,8 @@ attrhash_key_make (void *p) if (extra) { + if (extra->lcommunity) + MIX(lcommunity_hash_make (extra->lcommunity)); if (extra->ecommunity) MIX(ecommunity_hash_make (extra->ecommunity)); if (extra->cluster) @@ -718,6 +722,7 @@ attrhash_cmp (const void *p1, const void *p2) && 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) @@ -836,6 +841,13 @@ bgp_attr_intern (struct attr *attr) 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) @@ -1026,7 +1038,11 @@ bgp_attr_unintern_sub (struct attr *attr) 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); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)); @@ -1096,6 +1112,8 @@ bgp_attr_flush (struct attr *attr) 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); @@ -1254,6 +1272,7 @@ const u_int8_t attr_flags_values [] = { [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_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -2044,6 +2063,40 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, 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; + + /* + * Large community follows new attribute format. + */ + 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) @@ -2065,7 +2118,7 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args) /* 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); @@ -2479,6 +2532,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, 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; @@ -3106,6 +3162,28 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, 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 @@ -3338,6 +3416,7 @@ bgp_attr_init (void) attrhash_init (); community_init (); ecommunity_init (); + lcommunity_init (); cluster_init (); transit_init (); encap_init (); @@ -3350,6 +3429,7 @@ bgp_attr_finish (void) attrhash_finish (); community_finish (); ecommunity_finish (); + lcommunity_finish (); cluster_finish (); transit_finish (); encap_finish (); @@ -3453,6 +3533,25 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, 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_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); + } + /* 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 == BGP_ATTR_NHLEN_IPV6_GLOBAL || diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 6e639078d..c5799ccd0 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -92,7 +92,10 @@ struct attr_extra /* Extended Communities attribute. */ struct ecommunity *ecommunity; - + + /* Large Communities attribute. */ + struct lcommunity *lcommunity; + /* Route-Reflector Cluster attribute */ struct cluster_list *cluster; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 9032b1e2f..b37034bf2 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgpd.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" @@ -42,9 +43,11 @@ community_list_master_lookup (struct community_list_handler *ch, int master) switch (master) { case COMMUNITY_LIST_MASTER: - return &ch->community_list; + return &ch->community_list; case EXTCOMMUNITY_LIST_MASTER: - return &ch->extcommunity_list; + return &ch->extcommunity_list; + case LARGE_COMMUNITY_LIST_MASTER: + return &ch->lcommunity_list; } return NULL; } @@ -66,6 +69,10 @@ community_entry_free (struct community_entry *entry) if (entry->u.com) community_free (entry->u.com); break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (entry->u.lcom) + lcommunity_free (&entry->u.lcom); + break; case EXTCOMMUNITY_LIST_STANDARD: /* In case of standard extcommunity-list, configuration string is made by ecommunity_ecom2str(). */ @@ -76,6 +83,7 @@ community_entry_free (struct community_entry *entry) break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: + case LARGE_COMMUNITY_LIST_EXPANDED: if (entry->config) XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config); if (entry->reg) @@ -320,8 +328,13 @@ community_list_entry_lookup (struct community_list *list, const void *arg, if (entry->direct == direct && ecommunity_cmp (entry->u.ecom, arg)) return entry; break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (entry->direct == direct && lcommunity_cmp (entry->u.lcom, arg)) + return entry; + break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: + case LARGE_COMMUNITY_LIST_EXPANDED: if (entry->direct == direct && strcmp (entry->config, arg) == 0) return entry; break; @@ -399,15 +412,14 @@ community_str_get (struct community *com, int i) } /* Internal function to perform regular expression match for - * * a single community. */ + * a single community. */ static int community_regexp_include (regex_t * reg, struct community *com, int i) { char *str; int rv; - /* When there is no communities attribute it is treated as empty - * string. */ + /* When there is no communities attribute it is treated as empty string. */ if (com == NULL || com->size == 0) str = XSTRDUP(MTYPE_COMMUNITY_STR, ""); else @@ -447,6 +459,90 @@ community_regexp_match (struct community *com, regex_t * reg) return 0; } +static char * +lcommunity_str_get (struct lcommunity *lcom, int i) +{ + struct lcommunity_val lcomval; + u_int32_t globaladmin; + u_int32_t localdata1; + u_int32_t localdata2; + char *str; + u_char *ptr; + char *pnt; + + ptr = lcom->val; + ptr += (i * LCOMMUNITY_SIZE); + + memcpy (&lcomval, ptr, LCOMMUNITY_SIZE); + + /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */ + str = pnt = XMALLOC (MTYPE_LCOMMUNITY_STR, 48); + + ptr = (u_char *)lcomval.val; + globaladmin = (*ptr++ << 24); + globaladmin |= (*ptr++ << 16); + globaladmin |= (*ptr++ << 8); + globaladmin |= (*ptr++); + + localdata1 = (*ptr++ << 24); + localdata1 |= (*ptr++ << 16); + localdata1 |= (*ptr++ << 8); + localdata1 |= (*ptr++); + + localdata2 = (*ptr++ << 24); + localdata2 |= (*ptr++ << 16); + localdata2 |= (*ptr++ << 8); + localdata2 |= (*ptr++); + + sprintf (pnt, "%u:%u:%u", globaladmin, localdata1, localdata2); + pnt += strlen (pnt); + *pnt = '\0'; + + return str; +} + +/* Internal function to perform regular expression match for + * a single community. */ +static int +lcommunity_regexp_include (regex_t * reg, struct lcommunity *lcom, int i) +{ + const char *str; + + /* When there is no communities attribute it is treated as empty string. */ + if (lcom == NULL || lcom->size == 0) + str = ""; + else + str = lcommunity_str_get (lcom, i); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + +static int +lcommunity_regexp_match (struct lcommunity *com, regex_t * reg) +{ + const char *str; + + /* When there is no communities attribute it is treated as empty + string. */ + if (com == NULL || com->size == 0) + str = ""; + else + str = lcommunity_str (com); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + + static int ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg) { @@ -547,6 +643,30 @@ community_list_match (struct community *com, struct community_list *list) } int +lcommunity_list_match (struct lcommunity *lcom, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) + { + if (lcommunity_match (lcom, entry->u.lcom)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) + { + if (lcommunity_regexp_match (lcom, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + +int ecommunity_list_match (struct ecommunity *ecom, struct community_list *list) { struct community_entry *entry; @@ -694,12 +814,17 @@ community_list_dup_check (struct community_list *list, if (community_cmp (entry->u.com, new->u.com)) return 1; break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (lcommunity_cmp (entry->u.lcom, new->u.lcom)) + return 1; + break; case EXTCOMMUNITY_LIST_STANDARD: if (ecommunity_cmp (entry->u.ecom, new->u.ecom)) return 1; break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: + case LARGE_COMMUNITY_LIST_EXPANDED: if (strcmp (entry->config, new->config) == 0) return 1; break; @@ -817,6 +942,185 @@ community_list_unset (struct community_list_handler *ch, return 0; } +/* Delete all permitted large communities in the list from com. */ +struct lcommunity * +lcommunity_list_match_delete (struct lcommunity *lcom, + struct community_list *list) +{ + struct community_entry *entry; + u_int32_t com_index_to_delete[lcom->size]; + u_char *ptr; + int delete_index = 0; + int i; + + /* Loop over each lcommunity value and evaluate each against the + * community-list. If we need to delete a community value add its index to + * com_index_to_delete. + */ + ptr = lcom->val; + for (i = 0; i < lcom->size; i++) + { + ptr += (i * LCOMMUNITY_SIZE); + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) + && lcommunity_include (entry->u.lcom, ptr) ) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) + && lcommunity_regexp_include (entry->reg, lcom, i)) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + } + } + + /* Delete all of the communities we flagged for deletion */ + ptr = lcom->val; + for (i = delete_index-1; i >= 0; i--) + { + ptr += (com_index_to_delete[i] * LCOMMUNITY_SIZE); + lcommunity_del_val (lcom, ptr); + } + + return lcom; +} + +/* Set lcommunity-list. */ +int +lcommunity_list_set (struct community_list_handler *ch, + const char *name, const char *str, int direct, int style) +{ + struct community_entry *entry = NULL; + struct community_list *list; + struct lcommunity *lcom = NULL; + regex_t *regex = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, LARGE_COMMUNITY_LIST_MASTER); + + /* When community-list already has entry, new entry should have same + style. If you want to have mixed style community-list, you can + comment out this check. */ + if (!community_list_empty_p (list)) + { + struct community_entry *first; + + first = list->head; + + if (style != first->style) + { + return (first->style == COMMUNITY_LIST_STANDARD + ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT + : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); + } + } + + if (str) + { + if (style == LARGE_COMMUNITY_LIST_STANDARD) + lcom = lcommunity_str2com (str); + else + regex = bgp_regcomp (str); + + if (! lcom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + entry = community_entry_new (); + entry->direct = direct; + entry->style = style; + entry->any = (str ? 0 : 1); + entry->u.lcom = lcom; + entry->reg = regex; + if (lcom) + entry->config = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST); + else if (regex) + entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); + else + entry->config = NULL; + + /* Do not put duplicated community entry. */ + if (community_list_dup_check (list, entry)) + community_entry_free (entry); + else + community_list_entry_add (list, entry); + + return 0; +} + +/* Unset community-list. When str is NULL, delete all of + community-list entry belongs to the specified name. */ +int +lcommunity_list_unset (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style) +{ + struct community_entry *entry = NULL; + struct community_list *list; + struct lcommunity *lcom = NULL; + regex_t *regex = NULL; + + /* Lookup community list. */ + list = community_list_lookup (ch, name, LARGE_COMMUNITY_LIST_MASTER); + if (list == NULL) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + /* Delete all of entry belongs to this community-list. */ + if (!str) + { + community_list_delete (list); + return 0; + } + + if (style == LARGE_COMMUNITY_LIST_STANDARD) + lcom = lcommunity_str2com (str); + else + regex = bgp_regcomp (str); + + if (! lcom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + if (lcom) + entry = community_list_entry_lookup (list, lcom, direct); + else + entry = community_list_entry_lookup (list, str, direct); + + if (lcom) + lcommunity_free (&lcom); + if (regex) + bgp_regex_free (regex); + + if (!entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + return 0; +} + /* Set extcommunity-list. */ int extcommunity_list_set (struct community_list_handler *ch, @@ -959,6 +1263,12 @@ community_list_terminate (struct community_list_handler *ch) while ((list = cm->str.head) != NULL) community_list_delete (list); + cm = &ch->lcommunity_list; + while ((list = cm->num.head) != NULL) + community_list_delete (list); + while ((list = cm->str.head) != NULL) + community_list_delete (list); + cm = &ch->extcommunity_list; while ((list = cm->num.head) != NULL) community_list_delete (list); diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 277ab7226..68e45c8f7 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Master Community-list. */ #define COMMUNITY_LIST_MASTER 0 #define EXTCOMMUNITY_LIST_MASTER 1 +#define LARGE_COMMUNITY_LIST_MASTER 2 /* Community-list deny and permit. */ #define COMMUNITY_DENY 0 @@ -38,6 +39,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define COMMUNITY_LIST_EXPANDED 1 /* Expanded community-list. */ #define EXTCOMMUNITY_LIST_STANDARD 2 /* Standard extcommunity-list. */ #define EXTCOMMUNITY_LIST_EXPANDED 3 /* Expanded extcommunity-list. */ +#define LARGE_COMMUNITY_LIST_STANDARD 4 /* Standard Large community-list. */ +#define LARGE_COMMUNITY_LIST_EXPANDED 5 /* Expanded Large community-list. */ /* Community-list. */ struct community_list @@ -80,6 +83,7 @@ struct community_entry { struct community *com; struct ecommunity *ecom; + struct lcommunity *lcom; } u; /* Configuration string. */ @@ -112,6 +116,9 @@ struct community_list_handler /* Exteded community-list. */ struct community_list_master extcommunity_list; + + /* Large community-list. */ + struct community_list_master lcommunity_list; }; /* Error code of community-list. */ @@ -139,6 +146,12 @@ extern int extcommunity_list_set (struct community_list_handler *ch, extern int extcommunity_list_unset (struct community_list_handler *ch, const char *name, const char *str, int direct, int style, int delete_all); +extern int lcommunity_list_set (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); +extern int lcommunity_list_unset (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); extern struct community_list_master * community_list_master_lookup (struct community_list_handler *, int); @@ -148,9 +161,12 @@ community_list_lookup (struct community_list_handler *, const char *, int); extern int community_list_match (struct community *, struct community_list *); extern int ecommunity_list_match (struct ecommunity *, struct community_list *); +extern int lcommunity_list_match (struct lcommunity *, struct community_list *); extern int community_list_exact_match (struct community *, struct community_list *); extern struct community * community_list_match_delete (struct community *, struct community_list *); - +extern struct lcommunity * +lcommunity_list_match_delete (struct lcommunity *lcom, + struct community_list *list); #endif /* _QUAGGA_BGP_CLIST_H */ diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index b65af9e1f..6689883d9 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_aspath.h" /* Hash of community attribute. */ diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c index 75a348e3f..418f2873c 100644 --- a/bgpd/bgp_encap.c +++ b/bgpd/bgp_encap.c @@ -41,6 +41,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_encap.h" diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c new file mode 100644 index 000000000..549a2ebad --- /dev/null +++ b/bgpd/bgp_lcommunity.c @@ -0,0 +1,569 @@ +/* BGP Large Communities Attribute + * + * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com> + * + * This file is part of FreeRangeRouting (FRR). + * + * FRR is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later version. + * + * FRR is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with FRR; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> + +#include "hash.h" +#include "memory.h" +#include "prefix.h" +#include "command.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_aspath.h" + +/* Hash of community attribute. */ +static struct hash *lcomhash; + +/* Allocate a new lcommunities. */ +static struct lcommunity * +lcommunity_new (void) +{ + return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY, + sizeof (struct lcommunity)); +} + +/* Allocate lcommunities. */ +void +lcommunity_free (struct lcommunity **lcom) +{ + if ((*lcom)->val) + XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val); + if ((*lcom)->str) + XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str); + XFREE (MTYPE_LCOMMUNITY, *lcom); + lcom = NULL; +} + +static void +lcommunity_hash_free (struct lcommunity *lcom) +{ + lcommunity_free (&lcom); +} + +/* Add a new Large Communities value to Large Communities + Attribute structure. When the value is already exists in the + structure, we don't add the value. Newly added value is sorted by + numerical order. When the value is added to the structure return 1 + else return 0. */ +static int +lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval) +{ + u_int8_t *p; + int ret; + int c; + + /* When this is fist value, just add it. */ + if (lcom->val == NULL) + { + lcom->size++; + lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom)); + memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE); + return 1; + } + + /* If the value already exists in the structure return 0. */ + c = 0; + for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) + { + ret = memcmp (p, lval->val, LCOMMUNITY_SIZE); + if (ret == 0) + return 0; + if (ret > 0) + break; + } + + /* Add the value to the structure with numerical sorting. */ + lcom->size++; + lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom)); + + memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE, + lcom->val + c * LCOMMUNITY_SIZE, + (lcom->size - 1 - c) * LCOMMUNITY_SIZE); + memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE); + + return 1; +} + +/* This function takes pointer to Large Communites strucutre then + create a new Large Communities structure by uniq and sort each + Large Communities value. */ +struct lcommunity * +lcommunity_uniq_sort (struct lcommunity *lcom) +{ + int i; + struct lcommunity *new; + struct lcommunity_val *lval; + + if (! lcom) + return NULL; + + new = lcommunity_new (); + + for (i = 0; i < lcom->size; i++) + { + lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE)); + lcommunity_add_val (new, lval); + } + return new; +} + +/* Parse Large Communites Attribute in BGP packet. */ +struct lcommunity * +lcommunity_parse (u_int8_t *pnt, u_short length) +{ + struct lcommunity tmp; + struct lcommunity *new; + + /* Length check. */ + if (length % LCOMMUNITY_SIZE) + return NULL; + + /* Prepare tmporary structure for making a new Large Communities + Attribute. */ + tmp.size = length / LCOMMUNITY_SIZE; + tmp.val = pnt; + + /* Create a new Large Communities Attribute by uniq and sort each + Large Communities value */ + new = lcommunity_uniq_sort (&tmp); + + return lcommunity_intern (new); +} + +/* Duplicate the Large Communities Attribute structure. */ +struct lcommunity * +lcommunity_dup (struct lcommunity *lcom) +{ + struct lcommunity *new; + + new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity)); + new->size = lcom->size; + if (new->size) + { + new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE); + memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE); + } + else + new->val = NULL; + return new; +} + +/* Retrun string representation of communities attribute. */ +char * +lcommunity_str (struct lcommunity *lcom) +{ + if (! lcom->str) + lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY); + return lcom->str; +} + +/* Merge two Large Communities Attribute structure. */ +struct lcommunity * +lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2) +{ + if (lcom1->val) + lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val, + (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE); + else + lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, + (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE); + + memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE), + lcom2->val, lcom2->size * LCOMMUNITY_SIZE); + lcom1->size += lcom2->size; + + return lcom1; +} + +/* Intern Large Communities Attribute. */ +struct lcommunity * +lcommunity_intern (struct lcommunity *lcom) +{ + struct lcommunity *find; + + assert (lcom->refcnt == 0); + + find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern); + + if (find != lcom) + lcommunity_free (&lcom); + + find->refcnt++; + + if (! find->str) + find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY); + + return find; +} + +/* Unintern Large Communities Attribute. */ +void +lcommunity_unintern (struct lcommunity **lcom) +{ + struct lcommunity *ret; + + if ((*lcom)->refcnt) + (*lcom)->refcnt--; + + /* Pull off from hash. */ + if ((*lcom)->refcnt == 0) + { + /* Large community must be in the hash. */ + ret = (struct lcommunity *) hash_release (lcomhash, *lcom); + assert (ret != NULL); + + lcommunity_free (lcom); + } +} + +/* Utility function to make hash key. */ +unsigned int +lcommunity_hash_make (void *arg) +{ + const struct lcommunity *lcom = arg; + int size = lcom->size * LCOMMUNITY_SIZE; + u_int8_t *pnt = lcom->val; + unsigned int key = 0; + int c; + + for (c = 0; c < size; c += LCOMMUNITY_SIZE) + { + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + key += pnt[c + 8]; + key += pnt[c + 9]; + key += pnt[c + 10]; + key += pnt[c + 11]; + } + + return key; +} + +/* Compare two Large Communities Attribute structure. */ +int +lcommunity_cmp (const void *arg1, const void *arg2) +{ + const struct lcommunity *lcom1 = arg1; + const struct lcommunity *lcom2 = arg2; + + return (lcom1->size == lcom2->size + && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0); +} + +/* Return communities hash. */ +struct hash * +lcommunity_hash (void) +{ + return lcomhash; +} + +/* Initialize Large Comminities related hash. */ +void +lcommunity_init (void) +{ + lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp); +} + +void +lcommunity_finish (void) +{ + hash_clean (lcomhash, (void (*)(void *))lcommunity_hash_free); + hash_free (lcomhash); + lcomhash = NULL; +} + +/* Large Communities token enum. */ +enum lcommunity_token +{ + lcommunity_token_unknown = 0, + lcommunity_token_val, +}; + +/* Get next Large Communities token from the string. */ +static const char * +lcommunity_gettoken (const char *str, struct lcommunity_val *lval, + enum lcommunity_token *token) +{ + const char *p = str; + + /* Skip white space. */ + while (isspace ((int) *p)) + { + p++; + str++; + } + + /* Check the end of the line. */ + if (*p == '\0') + return NULL; + + /* Community value. */ + if (isdigit ((int) *p)) + { + int separator = 0; + int digit = 0; + u_int32_t globaladmin = 0; + u_int32_t localdata1 = 0; + u_int32_t localdata2 = 0; + + while (isdigit ((int) *p) || *p == ':') + { + if (*p == ':') + { + if (separator == 2) + { + *token = lcommunity_token_unknown; + return NULL; + } + else + { + separator++; + digit = 0; + if (separator == 1) { + globaladmin = localdata2; + } else { + localdata1 = localdata2; + } + localdata2 = 0; + } + } + else + { + digit = 1; + localdata2 *= 10; + localdata2 += (*p - '0'); + } + p++; + } + if (! digit) + { + *token = lcommunity_token_unknown; + return NULL; + } + + /* + * Copy the large comm. + */ + lval->val[0] = (globaladmin >> 24) & 0xff; + lval->val[1] = (globaladmin >> 16) & 0xff; + lval->val[2] = (globaladmin >> 8) & 0xff; + lval->val[3] = globaladmin & 0xff; + lval->val[4] = (localdata1 >> 24) & 0xff; + lval->val[5] = (localdata1 >> 16) & 0xff; + lval->val[6] = (localdata1 >> 8) & 0xff; + lval->val[7] = localdata1 & 0xff; + lval->val[8] = (localdata2 >> 24) & 0xff; + lval->val[9] = (localdata2 >> 16) & 0xff; + lval->val[10] = (localdata2 >> 8) & 0xff; + lval->val[11] = localdata2 & 0xff; + + *token = lcommunity_token_val; + return p; + } + *token = lcommunity_token_unknown; + return p; +} + +/* + Convert string to large community attribute. + When type is already known, please specify both str and type. + + When string includes keyword for each large community value. + Please specify keyword_included as non-zero value. +*/ +struct lcommunity * +lcommunity_str2com (const char *str) +{ + struct lcommunity *lcom = NULL; + enum lcommunity_token token = lcommunity_token_unknown; + struct lcommunity_val lval; + + while ((str = lcommunity_gettoken (str, &lval, &token))) + { + switch (token) + { + case lcommunity_token_val: + if (lcom == NULL) + lcom = lcommunity_new (); + lcommunity_add_val (lcom, &lval); + break; + case lcommunity_token_unknown: + default: + if (lcom) + lcommunity_free (&lcom); + return NULL; + } + } + return lcom; +} + +int +lcommunity_include (struct lcommunity *lcom, u_char *ptr) +{ + int i; + u_char *lcom_ptr; + + lcom_ptr = lcom->val; + for (i = 0; i < lcom->size; i++) { + lcom_ptr += (i * LCOMMUNITY_SIZE); + if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0) + return 1; + } + return 0; +} + +/* Convert large community attribute to string. + The large coms will be in 65535:65531:0 format. +*/ +char * +lcommunity_lcom2str (struct lcommunity *lcom, int format) +{ + int i; + u_int8_t *pnt; +#define LCOMMUNITY_STR_DEFAULT_LEN 40 + int str_size; + int str_pnt; + char *str_buf; + int len = 0; + int first = 1; + u_int32_t globaladmin, localdata1, localdata2; + + if (lcom->size == 0) + { + str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1); + str_buf[0] = '\0'; + return str_buf; + } + + /* Prepare buffer. */ + str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1); + str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1; + str_pnt = 0; + + for (i = 0; i < lcom->size; i++) + { + /* Make it sure size is enough. */ + while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size) + { + str_size *= 2; + str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size); + } + + /* Space between each value. */ + if (! first) + str_buf[str_pnt++] = ' '; + + pnt = lcom->val + (i * 12); + + globaladmin = (*pnt++ << 24); + globaladmin |= (*pnt++ << 16); + globaladmin |= (*pnt++ << 8); + globaladmin |= (*pnt++); + + localdata1 = (*pnt++ << 24); + localdata1 |= (*pnt++ << 16); + localdata1 |= (*pnt++ << 8); + localdata1 |= (*pnt++); + + localdata2 = (*pnt++ << 24); + localdata2 |= (*pnt++ << 16); + localdata2 |= (*pnt++ << 8); + localdata2 |= (*pnt++); + + len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin, + localdata1, localdata2); + str_pnt += len; + first = 0; + } + return str_buf; +} + +int +lcommunity_match (const struct lcommunity *lcom1, + const struct lcommunity *lcom2) +{ + int i = 0; + int j = 0; + + if (lcom1 == NULL && lcom2 == NULL) + return 1; + + if (lcom1 == NULL || lcom2 == NULL) + return 0; + + if (lcom1->size < lcom2->size) + return 0; + + /* Every community on com2 needs to be on com1 for this to match */ + while (i < lcom1->size && j < lcom2->size) + { + if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0) + j++; + i++; + } + + if (j == lcom2->size) + return 1; + else + return 0; +} + +/* Delete one lcommunity. */ +void +lcommunity_del_val (struct lcommunity *lcom, u_char *ptr) +{ + int i = 0; + int c = 0; + + if (! lcom->val) + return; + + while (i < lcom->size) + { + if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0) + { + c = lcom->size -i -1; + + if (c > 0) + memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE); + + lcom->size--; + + if (lcom->size > 0) + lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val, + lcom_length (lcom)); + else + { + XFREE (MTYPE_COMMUNITY_VAL, lcom->val); + lcom->val = NULL; + } + return; + } + i++; + } +} diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h new file mode 100644 index 000000000..de3697f47 --- /dev/null +++ b/bgpd/bgp_lcommunity.h @@ -0,0 +1,74 @@ +/* BGP Large Communities Attribute. + * + * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com> + * + * This file is part of FreeRangeRouting (FRR). + * + * FRR is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later version. + * + * FRR is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with FRR; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _QUAGGA_BGP_LCOMMUNITY_H +#define _QUAGGA_BGP_LCOMMUNITY_H + +/* Extended communities attribute string format. */ +#define LCOMMUNITY_FORMAT_ROUTE_MAP 0 +#define LCOMMUNITY_FORMAT_COMMUNITY_LIST 1 +#define LCOMMUNITY_FORMAT_DISPLAY 2 + +/* Large Communities value is twelve octets long. */ +#define LCOMMUNITY_SIZE 12 + +/* Large Communities attribute. */ +struct lcommunity +{ + /* Reference counter. */ + unsigned long refcnt; + + /* Size of Extended Communities attribute. */ + int size; + + /* Extended Communities value. */ + u_int8_t *val; + + /* Human readable format string. */ + char *str; +}; + +/* Extended community value is eight octet. */ +struct lcommunity_val +{ + char val[LCOMMUNITY_SIZE]; +}; + +#define lcom_length(X) ((X)->size * LCOMMUNITY_SIZE) + +extern void lcommunity_init (void); +extern void lcommunity_finish (void); +extern void lcommunity_free (struct lcommunity **); +extern struct lcommunity *lcommunity_parse (u_int8_t *, u_short); +extern struct lcommunity *lcommunity_dup (struct lcommunity *); +extern struct lcommunity *lcommunity_merge (struct lcommunity *, struct lcommunity *); +extern struct lcommunity *lcommunity_uniq_sort (struct lcommunity *); +extern struct lcommunity *lcommunity_intern (struct lcommunity *); +extern int lcommunity_cmp (const void *, const void *); +extern void lcommunity_unintern (struct lcommunity **); +extern unsigned int lcommunity_hash_make (void *); +extern struct hash *lcommunity_hash (void); +extern struct lcommunity *lcommunity_str2com (const char *); +extern char *lcommunity_lcom2str (struct lcommunity *, int); +extern int lcommunity_match (const struct lcommunity *, const struct lcommunity *); +extern char *lcommunity_str (struct lcommunity *); +extern int lcommunity_include (struct lcommunity *lcom, u_char *ptr); +extern void lcommunity_del_val (struct lcommunity *lcom, u_char *ptr); +#endif /* _QUAGGA_BGP_LCOMMUNITY_H */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 72c0311c1..85e32645e 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -111,3 +111,7 @@ DEFINE_MTYPE(BGPD, ENCAP_TLV, "ENCAP TLV") DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS, "BGP TEA Options") DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value") + +DEFINE_MTYPE(BGPD, LCOMMUNITY, "Large Community") +DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string") +DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index a4ce8b891..341fb235d 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -108,4 +108,7 @@ DECLARE_MTYPE(ENCAP_TLV) DECLARE_MTYPE(BGP_TEA_OPTIONS) DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE) +DECLARE_MTYPE(LCOMMUNITY) +DECLARE_MTYPE(LCOMMUNITY_STR) +DECLARE_MTYPE(LCOMMUNITY_VAL) #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 1701c7044..f564ff169 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -38,6 +38,7 @@ #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_mpath.h" /* @@ -662,6 +663,7 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best, u_char origin; struct community *community, *commerge; struct ecommunity *ecomm, *ecommerge; + struct lcommunity *lcomm, *lcommerge; struct attr_extra *ae; struct attr attr = { 0 }; @@ -698,6 +700,7 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best, community = attr.community ? community_dup (attr.community) : NULL; ae = attr.extra; ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL; + lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL; for (mpinfo = bgp_info_mpath_first (new_best); mpinfo; mpinfo = bgp_info_mpath_next (mpinfo)) @@ -733,6 +736,17 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best, else ecomm = ecommunity_dup (ae->ecommunity); } + if (ae && ae->lcommunity) + { + if (lcomm) + { + lcommerge = lcommunity_merge (lcomm, ae->lcommunity); + lcomm = lcommunity_uniq_sort (lcommerge); + lcommunity_free (&lcommerge); + } + else + lcomm = lcommunity_dup (ae->lcommunity); + } } attr.aspath = aspath; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index c4eacb504..2df22ab56 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -46,6 +46,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_encap.h" diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5fdda6625..0179cc3d9 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1,5 +1,6 @@ /* BGP routing information Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro + Copyright (C) 2016 Job Snijders <job@instituut.net> This file is part of GNU Zebra. @@ -46,6 +47,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_regex.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_clist.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_filter.h" @@ -7060,7 +7062,12 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, } } - /* Line 6 display Originator, Cluster-id */ + /* Line 6 display Large community */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) + vty_out (vty, " Large Community: %s%s", + attr->extra->lcommunity->str, VTY_NEWLINE); + + /* Line 7 display Originator, Cluster-id */ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { @@ -7116,7 +7123,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (binfo->extra && binfo->extra->damp_info) bgp_damp_info_vty (vty, binfo, json_path); - /* Line 7 display Addpath IDs */ + /* Line 8 display Addpath IDs */ if (binfo->addpath_rx_id || binfo->addpath_tx_id) { if (json_paths) @@ -7171,7 +7178,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, } } - /* Line 8 display Uptime */ + /* Line 9 display Uptime */ tbuf = time(NULL) - (bgp_clock() - binfo->uptime); if (json_paths) { @@ -7228,6 +7235,9 @@ enum bgp_show_type bgp_show_type_community_exact, bgp_show_type_community_list, bgp_show_type_community_list_exact, + bgp_show_type_lcommunity_all, + bgp_show_type_lcommunity, + bgp_show_type_lcommunity_list, bgp_show_type_flap_statistics, bgp_show_type_flap_neighbor, bgp_show_type_dampend_paths, @@ -7417,6 +7427,27 @@ bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table, if (! community_list_exact_match (ri->attr->community, list)) continue; } + if (type == bgp_show_type_lcommunity) + { + struct lcommunity *lcom = output_arg; + + if (! ri->attr->extra || ! ri->attr->extra->lcommunity || + ! lcommunity_match (ri->attr->extra->lcommunity, lcom)) + continue; + } + if (type == bgp_show_type_lcommunity_list) + { + struct community_list *list = output_arg; + + if (! ri->attr->extra || + ! lcommunity_list_match (ri->attr->extra->lcommunity, list)) + continue; + } + if (type == bgp_show_type_lcommunity_all) + { + if (! ri->attr->extra || ! ri->attr->extra->lcommunity) + continue; + } if (type == bgp_show_type_dampend_paths || type == bgp_show_type_damp_neighbor) { @@ -7833,6 +7864,158 @@ bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str, use_json); } +static int +bgp_show_lcommunity (struct vty *vty, struct bgp *bgp, int argc, + struct cmd_token **argv, afi_t afi, safi_t safi, u_char uj) +{ + struct lcommunity *lcom; + struct buffer *b; + int i; + char *str; + int first = 0; + + b = buffer_new (1024); + for (i = 0; i < argc; i++) + { + if (first) + buffer_putc (b, ' '); + else + { + if (strmatch (argv[i]->text, "<AA:BB:CC>")) + { + first = 1; + buffer_putstr (b, argv[i]->arg); + } + } + } + buffer_putc (b, '\0'); + + str = buffer_getstr (b); + buffer_free (b); + + lcom = lcommunity_str2com (str); + XFREE (MTYPE_TMP, str); + if (! lcom) + { + vty_out (vty, "%% Large-community malformed: %s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity, lcom, uj); +} + +static int +bgp_show_lcommunity_list (struct vty *vty, struct bgp *bgp, const char *lcom, + afi_t afi, safi_t safi, u_char uj) +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, lcom, LARGE_COMMUNITY_LIST_MASTER); + if (list == NULL) + { + vty_out (vty, "%% %s is not a valid large-community-list name%s", lcom, + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_list, list, uj); +} + +DEFUN (show_ip_bgp_large_community_list, + show_ip_bgp_large_community_list_cmd, + "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] large-community-list <(1-500)|WORD> [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + "Address Family\n" + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n" + JSON_STR) +{ + char *vrf = NULL; + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + int idx = 0; + + if (argv_find (argv, argc, "ip", &idx)) + afi = AFI_IP; + if (argv_find (argv, argc, "view", &idx) || argv_find (argv, argc, "vrf", &idx)) + vrf = argv[++idx]->arg; + if (argv_find (argv, argc, "ipv4", &idx) || argv_find (argv, argc, "ipv6", &idx)) + { + afi = strmatch(argv[idx]->text, "ipv6") ? AFI_IP6 : AFI_IP; + if (argv_find (argv, argc, "unicast", &idx) || argv_find (argv, argc, "multicast", &idx)) + safi = bgp_vty_safi_from_arg (argv[idx]->text); + } + + int uj = use_json (argc, argv); + + struct bgp *bgp = bgp_lookup_by_name (vrf); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP instance %s%s", vrf, VTY_NEWLINE); + return CMD_WARNING; + } + + argv_find (argv, argc, "large-community-list", &idx); + return bgp_show_lcommunity_list (vty, bgp, argv[idx+1]->arg, afi, safi, uj); +} +DEFUN (show_ip_bgp_large_community, + show_ip_bgp_large_community_cmd, + "show [ip] bgp [<view|vrf> WORD] [<ipv4|ipv6> [<unicast|multicast|vpn|encap>]] large-community [AA:BB:CC] [json]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + "Address Family\n" + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "List of large-community numbers\n" + JSON_STR) +{ + char *vrf = NULL; + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + int idx = 0; + + if (argv_find (argv, argc, "ip", &idx)) + afi = AFI_IP; + if (argv_find (argv, argc, "view", &idx) || argv_find (argv, argc, "vrf", &idx)) + vrf = argv[++idx]->arg; + if (argv_find (argv, argc, "ipv4", &idx) || argv_find (argv, argc, "ipv6", &idx)) + { + afi = strmatch(argv[idx]->text, "ipv6") ? AFI_IP6 : AFI_IP; + if (argv_find (argv, argc, "unicast", &idx) || argv_find (argv, argc, "multicast", &idx)) + safi = bgp_vty_safi_from_arg (argv[idx]->text); + } + + int uj = use_json (argc, argv); + + struct bgp *bgp = bgp_lookup_by_name (vrf); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP instance %s%s", vrf, VTY_NEWLINE); + return CMD_WARNING; + } + + argv_find (argv, argc, "large-community", &idx); + if (strmatch(argv[idx+1]->text, "AA:BB:CC")) + return bgp_show_lcommunity (vty, bgp, argc, argv, afi, safi, uj); + else + return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_all, NULL, uj); +} + /* BGP route print out function. */ DEFUN (show_ip_bgp_ipv4, show_ip_bgp_ipv4_cmd, @@ -10530,6 +10713,7 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd); install_element (VIEW_NODE, &show_ip_bgp_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_regexp_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); @@ -10542,6 +10726,7 @@ bgp_route_init (void) /* BGP dampening clear commands */ install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd); @@ -10611,6 +10796,10 @@ bgp_route_init (void) /* IPv4 Multicast Mode */ install_element (BGP_IPV4M_NODE, &bgp_damp_set_cmd); install_element (BGP_IPV4M_NODE, &bgp_damp_unset_cmd); + + /* Large Communities */ + install_element (VIEW_NODE, &show_ip_bgp_large_community_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_large_community_cmd); } void diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 50524baa0..58788d204 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -52,6 +52,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_filter.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_debug.h" @@ -84,6 +85,8 @@ o Cisco route-map as-path tag : Not yet automatic-tag : (This will not be implemented by bgpd) community : Done + large-community : Done + large-comm-list : Done comm-list : Not yet dampning : Not yet default : (This will not be implemented by bgpd) @@ -847,6 +850,78 @@ struct route_map_rule_cmd route_match_community_cmd = route_match_community_free }; +/* Match function for lcommunity match. */ +static route_map_result_t +route_match_lcommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct community_list *list; + struct bgp_info *bgp_info; + struct rmap_community *rcom; + + if (type == RMAP_BGP) + { + bgp_info = object; + rcom = rule; + + list = community_list_lookup (bgp_clist, rcom->name, + LARGE_COMMUNITY_LIST_MASTER); + if (! list) + return RMAP_NOMATCH; + + if (bgp_info->attr->extra && + lcommunity_list_match (bgp_info->attr->extra->lcommunity, list)) + return RMAP_MATCH; + + } + return RMAP_NOMATCH; +} + +/* Compile function for community match. */ +static void * +route_match_lcommunity_compile (const char *arg) +{ + struct rmap_community *rcom; + int len; + char *p; + + rcom = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_community)); + + p = strchr (arg, ' '); + if (p) + { + len = p - arg; + rcom->name = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1); + memcpy (rcom->name, arg, len); + } + else + { + rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); + rcom->exact = 0; + } + return rcom; +} + +/* Compile function for community match. */ +static void +route_match_lcommunity_free (void *rule) +{ + struct rmap_community *rcom = rule; + + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name); + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom); +} + +/* Route map commands for community matching. */ +struct route_map_rule_cmd route_match_lcommunity_cmd = +{ + "large-community", + route_match_lcommunity, + route_match_lcommunity_compile, + route_match_lcommunity_free +}; + + /* Match function for extcommunity match. */ static route_map_result_t route_match_ecommunity (void *rule, struct prefix *prefix, @@ -1544,6 +1619,224 @@ struct route_map_rule_cmd route_set_community_cmd = route_set_community_free, }; +/* `set community COMMUNITY' */ +struct rmap_lcom_set +{ + struct lcommunity *lcom; + int additive; + int none; +}; + + +/* For lcommunity set mechanism. */ +static route_map_result_t +route_set_lcommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_lcom_set *rcs; + struct bgp_info *binfo; + struct attr *attr; + struct lcommunity *new = NULL; + struct lcommunity *old; + struct lcommunity *merge; + + if (type == RMAP_BGP) + { + rcs = rule; + binfo = object; + attr = binfo->attr; + old = (attr->extra) ? attr->extra->lcommunity : NULL; + + /* "none" case. */ + if (rcs->none) + { + attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)); + if (attr->extra) + attr->extra->lcommunity = NULL; + + /* See the longer comment down below. */ + if (old && old->refcnt == 0) + lcommunity_free(&old); + return RMAP_OKAY; + } + + if (rcs->additive && old) + { + merge = lcommunity_merge (lcommunity_dup (old), rcs->lcom); + + /* HACK: if the old large-community is not intern'd, + * we should free it here, or all reference to it may be lost. + * Really need to cleanup attribute caching sometime. + */ + if (old->refcnt == 0) + lcommunity_free (&old); + new = lcommunity_uniq_sort (merge); + lcommunity_free (&merge); + } + else + new = lcommunity_dup (rcs->lcom); + + /* will be intern()'d or attr_flush()'d by bgp_update_main() */ + (bgp_attr_extra_get (attr))->lcommunity = new; + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + } + + return RMAP_OKAY; +} + +/* Compile function for set community. */ +static void * +route_set_lcommunity_compile (const char *arg) +{ + struct rmap_lcom_set *rcs; + struct lcommunity *lcom = NULL; + char *sp; + int additive = 0; + int none = 0; + + if (strcmp (arg, "none") == 0) + none = 1; + else + { + sp = strstr (arg, "additive"); + + if (sp && sp > arg) + { + /* "additive" keyworkd is included. */ + additive = 1; + *(sp - 1) = '\0'; + } + + lcom = lcommunity_str2com (arg); + + if (additive) + *(sp - 1) = ' '; + + if (! lcom) + return NULL; + } + + rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set)); + rcs->lcom = lcom; + rcs->additive = additive; + rcs->none = none; + + return rcs; +} + +/* Free function for set lcommunity. */ +static void +route_set_lcommunity_free (void *rule) +{ + struct rmap_lcom_set *rcs = rule; + + if (rcs->lcom) { + lcommunity_free (&rcs->lcom); + } + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_lcommunity_cmd = +{ + "large-community", + route_set_lcommunity, + route_set_lcommunity_compile, + route_set_lcommunity_free, +}; + +/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */ + +/* For large community set mechanism. */ +static route_map_result_t +route_set_lcommunity_delete (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct community_list *list; + struct lcommunity *merge; + struct lcommunity *new; + struct lcommunity *old; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + if (! rule) + return RMAP_OKAY; + + binfo = object; + list = community_list_lookup (bgp_clist, rule, + LARGE_COMMUNITY_LIST_MASTER); + old = ((binfo->attr->extra) ? binfo->attr->extra->lcommunity : NULL); + + if (list && old) + { + merge = lcommunity_list_match_delete (lcommunity_dup (old), list); + new = lcommunity_uniq_sort (merge); + lcommunity_free (&merge); + + /* HACK: if the old community is not intern'd, + * we should free it here, or all reference to it may be lost. + * Really need to cleanup attribute caching sometime. + */ + if (old->refcnt == 0) + lcommunity_free (&old); + + if (new->size == 0) + { + binfo->attr->extra->lcommunity = NULL; + binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + lcommunity_free (&new); + } + else + { + binfo->attr->extra->lcommunity = new; + binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + } + } + } + + return RMAP_OKAY; +} + +/* Compile function for set lcommunity. */ +static void * +route_set_lcommunity_delete_compile (const char *arg) +{ + char *p; + char *str; + int len; + + p = strchr (arg, ' '); + if (p) + { + len = p - arg; + str = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1); + memcpy (str, arg, len); + } + else + str = NULL; + + return str; +} + +/* Free function for set lcommunity. */ +static void +route_set_lcommunity_delete_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set lcommunity rule structure. */ +struct route_map_rule_cmd route_set_lcommunity_delete_cmd = +{ + "large-comm-list", + route_set_lcommunity_delete, + route_set_lcommunity_delete_compile, + route_set_lcommunity_delete_free, +}; + + /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ @@ -3164,7 +3457,32 @@ DEFUN (no_match_community, RMAP_EVENT_CLIST_DELETED); } +DEFUN (match_lcommunity, + match_lcommunity_cmd, + "match large-community [<(1-99)|(100-500)|WORD>]", + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n") +{ + return bgp_route_match_add (vty, "large-community", argv[2]->arg, + RMAP_EVENT_LLIST_ADDED); +} +DEFUN (no_match_lcommunity, + no_match_lcommunity_cmd, + "no match large-community [<(1-99)|(100-500)|WORD>]", + NO_STR + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n") +{ + return bgp_route_match_delete (vty, "large-community", NULL, + RMAP_EVENT_LLIST_DELETED); +} DEFUN (match_ecommunity, match_ecommunity_cmd, @@ -3597,6 +3915,95 @@ DEFUN (no_set_community_delete, "comm-list", NULL); } +DEFUN (set_lcommunity, + set_lcommunity_cmd, + "set large-community AA:BB:CC...", + SET_STR + "BGP large community attribute\n" + "Large Community number in aa:bb:cc format or additive\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 2); + ret = generic_set_add (vty, VTY_GET_CONTEXT(route_map_index), "large-community", str); + XFREE (MTYPE_TMP, str); + + return ret; +} + +DEFUN (set_lcommunity_none, + set_lcommunity_none_cmd, + "set large-community none", + SET_STR + "BGP large community attribute\n" + "No large community attribute\n") +{ + return generic_set_add (vty, VTY_GET_CONTEXT(route_map_index), + "large-community", "none"); +} + +DEFUN (no_set_lcommunity, + no_set_lcommunity_cmd, + "no set large-community none", + NO_STR + SET_STR + "BGP large community attribute\n" + "No community attribute\n") +{ + return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index), + "large-community", NULL); +} + +DEFUN (no_set_lcommunity1, + no_set_lcommunity1_cmd, + "no set large-community AA:BB:CC...", + NO_STR + SET_STR + "BGP large community attribute\n" + "Large community in AA:BB:CC... format or additive\n") +{ + return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index), + "large-community", NULL); +} + +DEFUN (set_lcommunity_delete, + set_lcommunity_delete_cmd, + "set large-comm-list <(1-99)|(100-500)|WORD> delete", + SET_STR + "set BGP large community list (for deletion)\n" + "Large Community-list number (standard)\n" + "Large Communitly-list number (expanded)\n" + "Large Community-list name\n" + "Delete matching large communities\n") +{ + char *str; + + str = XCALLOC (MTYPE_TMP, strlen (argv[2]->arg) + strlen (" delete") + 1); + strcpy (str, argv[2]->arg); + strcpy (str + strlen (argv[2]->arg), " delete"); + + generic_set_add (vty, VTY_GET_CONTEXT(route_map_index), + "large-comm-list", str); + + XFREE (MTYPE_TMP, str); + return CMD_SUCCESS; +} + +DEFUN (no_set_lcommunity_delete, + no_set_lcommunity_delete_cmd, + "no set large-comm-list <(1-99|(100-500)|WORD)> [delete]", + NO_STR + SET_STR + "set BGP large community list (for deletion)\n" + "Large Community-list number (standard)\n" + "Large Communitly-list number (expanded)\n" + "Large Community-list name\n" + "Delete matching large communities\n") +{ + return generic_set_delete (vty, VTY_GET_CONTEXT(route_map_index), + "large-comm-list", NULL); +} DEFUN (set_ecommunity_rt, set_ecommunity_rt_cmd, @@ -4139,6 +4546,7 @@ bgp_route_map_init (void) route_map_install_match (&route_match_ip_route_source_prefix_list_cmd); route_map_install_match (&route_match_aspath_cmd); route_map_install_match (&route_match_community_cmd); + route_map_install_match (&route_match_lcommunity_cmd); route_map_install_match (&route_match_ecommunity_cmd); route_map_install_match (&route_match_local_pref_cmd); route_map_install_match (&route_match_metric_cmd); @@ -4158,6 +4566,8 @@ bgp_route_map_init (void) route_map_install_set (&route_set_aggregator_as_cmd); route_map_install_set (&route_set_community_cmd); route_map_install_set (&route_set_community_delete_cmd); + route_map_install_set (&route_set_lcommunity_cmd); + route_map_install_set (&route_set_lcommunity_delete_cmd); route_map_install_set (&route_set_vpnv4_nexthop_cmd); route_map_install_set (&route_set_vpnv6_nexthop_cmd); route_map_install_set (&route_set_originator_id_cmd); @@ -4180,6 +4590,8 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &match_community_cmd); install_element (RMAP_NODE, &match_community_exact_cmd); install_element (RMAP_NODE, &no_match_community_cmd); + install_element (RMAP_NODE, &match_lcommunity_cmd); + install_element (RMAP_NODE, &no_match_lcommunity_cmd); install_element (RMAP_NODE, &match_ecommunity_cmd); install_element (RMAP_NODE, &no_match_ecommunity_cmd); install_element (RMAP_NODE, &match_origin_cmd); @@ -4209,6 +4621,12 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &no_set_community_cmd); install_element (RMAP_NODE, &set_community_delete_cmd); install_element (RMAP_NODE, &no_set_community_delete_cmd); + install_element (RMAP_NODE, &set_lcommunity_cmd); + install_element (RMAP_NODE, &set_lcommunity_none_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_cmd); + install_element (RMAP_NODE, &no_set_lcommunity1_cmd); + install_element (RMAP_NODE, &set_lcommunity_delete_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_delete_cmd); install_element (RMAP_NODE, &set_ecommunity_rt_cmd); install_element (RMAP_NODE, &no_set_ecommunity_rt_cmd); install_element (RMAP_NODE, &set_ecommunity_soo_cmd); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 2e001b3a9..e9c4a131a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -41,6 +41,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_damp.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_fsm.h" @@ -3748,13 +3749,15 @@ DEFUN (no_neighbor_send_community, /* neighbor send-community extended. */ DEFUN (neighbor_send_community_type, neighbor_send_community_type_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|extended|standard>", + "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Send Community attribute to this neighbor\n" "Send Standard and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" "Send Extended Community attributes\n" - "Send Standard Community attributes\n") + "Send Standard Community attributes\n" + "Send Large Community attributes\n") { int idx = 0; u_int32_t flag = 0; @@ -3765,25 +3768,35 @@ DEFUN (neighbor_send_community_type, SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY); else if (argv_find (argv, argc, "extended", &idx)) SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY); + else if (argv_find (argv, argc, "large", &idx)) + SET_FLAG (flag, PEER_FLAG_SEND_LARGE_COMMUNITY); + else if (argv_find (argv, argc, "both", &idx)) + { + SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY); + SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY); + } else - { - SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY); - SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } + { + SET_FLAG (flag, PEER_FLAG_SEND_COMMUNITY); + SET_FLAG (flag, PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG (flag, PEER_FLAG_SEND_LARGE_COMMUNITY); + } return peer_af_flag_set_vty (vty, peer, bgp_node_afi (vty), bgp_node_safi (vty), flag); } DEFUN (no_neighbor_send_community_type, no_neighbor_send_community_type_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|extended|standard>", + "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Send Community attribute to this neighbor\n" "Send Standard and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" "Send Extended Community attributes\n" - "Send Standard Community attributes\n") + "Send Standard Community attributes\n" + "Send Large Community attributes\n") { int idx_peer = 2; int idx_type = 4; @@ -3795,11 +3808,21 @@ DEFUN (no_neighbor_send_community_type, return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_SEND_EXT_COMMUNITY); + if (strncmp (argv[idx_type]->arg, "l", 1) == 0) + return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_LARGE_COMMUNITY); + if (strncmp (argv[idx_type]->arg, "b", 1) == 0) + return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_COMMUNITY | + PEER_FLAG_SEND_EXT_COMMUNITY); return peer_af_flag_unset_vty (vty, argv[idx_peer]->arg, bgp_node_afi (vty), bgp_node_safi (vty), (PEER_FLAG_SEND_COMMUNITY | - PEER_FLAG_SEND_EXT_COMMUNITY)); + PEER_FLAG_SEND_EXT_COMMUNITY| + PEER_FLAG_SEND_LARGE_COMMUNITY)); } /* neighbor soft-reconfig. */ @@ -6156,6 +6179,12 @@ DEFUN (show_bgp_memory, mtype_memstr (memstrbuf, sizeof (memstrbuf), count * sizeof (struct ecommunity)), VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_LCOMMUNITY))) + vty_out (vty, "%ld BGP large-community entries, using %s of memory%s", + count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct lcommunity)), + VTY_NEWLINE); if ((count = mtype_stats_alloc (MTYPE_CLUSTER))) vty_out (vty, "%ld Cluster lists, using %s of memory%s", count, @@ -7133,12 +7162,16 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi, if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) vty_out (vty, " MED is propagated unchanged to this neighbor%s", VTY_NEWLINE); if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) - || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) + || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) + || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) { vty_out (vty, " Community attribute sent to this neighbor"); if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) - && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) - vty_out (vty, "(both)%s", VTY_NEWLINE); + && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) + && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, "(all)%s", VTY_NEWLINE); + else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, "(large)%s", VTY_NEWLINE); else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) vty_out (vty, "(extended)%s", VTY_NEWLINE); else @@ -8666,6 +8699,36 @@ DEFUN (show_ip_bgp_community_info, return CMD_SUCCESS; } +static void +lcommunity_show_all_iterator (struct hash_backet *backet, struct vty *vty) +{ + struct lcommunity *lcom; + + lcom = (struct lcommunity *) backet->data; + vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, lcom->refcnt, + lcommunity_str (lcom), VTY_NEWLINE); +} + +/* Show BGP's community internal data. */ +DEFUN (show_ip_bgp_lcommunity_info, + show_ip_bgp_lcommunity_info_cmd, + "show ip bgp large-community-info", + SHOW_STR + IP_STR + BGP_STR + "List all bgp large-community information\n") +{ + vty_out (vty, "Address Refcnt Large-community%s", VTY_NEWLINE); + + hash_iterate (lcommunity_hash (), + (void (*) (struct hash_backet *, void *)) + lcommunity_show_all_iterator, + vty); + + return CMD_SUCCESS; +} + + DEFUN (show_ip_bgp_attr_info, show_ip_bgp_attr_info_cmd, "show [ip] bgp attribute-info", @@ -10770,6 +10833,8 @@ bgp_vty_init (void) /* "show [ip] bgp community" commands. */ install_element (VIEW_NODE, &show_ip_bgp_community_info_cmd); + /* "show ip bgp large-community" commands. */ + install_element (VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd); /* "show [ip] bgp attribute-info" commands. */ install_element (VIEW_NODE, &show_ip_bgp_attr_info_cmd); @@ -11102,6 +11167,350 @@ DEFUN (show_ip_community_list_arg, return CMD_SUCCESS; } +/* + * Large Community code. + */ +static int +lcommunity_list_set_vty (struct vty *vty, int argc, struct cmd_token **argv, + int style, int reject_all_digit_name) +{ + int ret; + int direct; + char *str; + int idx = 0; + char *cl_name; + + direct = argv_find (argv, argc, "permit", &idx) ? COMMUNITY_PERMIT : COMMUNITY_DENY; + + /* All digit name check. */ + idx = 0; + argv_find (argv, argc, "WORD", &idx); + argv_find (argv, argc, "(1-99)", &idx); + argv_find (argv, argc, "(100-500)", &idx); + cl_name = argv[idx]->arg; + if (reject_all_digit_name && all_digit (cl_name)) + { + vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE); + return CMD_WARNING; + } + + argv_find (argv, argc, "AA:BB:CC", &idx); + argv_find (argv, argc, "LINE", &idx); + /* Concat community string argument. */ + if (idx) + str = argv_concat (argv, argc, idx); + else + str = NULL; + + ret = lcommunity_list_set (bgp_clist, cl_name, str, direct, style); + + /* Free temporary community list string allocated by + argv_concat(). */ + if (str) + XFREE (MTYPE_TMP, str); + + if (ret < 0) + { + community_list_perror (vty, ret); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +static int +lcommunity_list_unset_vty (struct vty *vty, int argc, struct cmd_token **argv, + int style) +{ + int ret; + int direct = 0; + char *str = NULL; + int idx = 0; + + argv_find (argv, argc, "permit", &idx); + argv_find (argv, argc, "deny", &idx); + + if (idx) + { + /* Check the list direct. */ + if (strncmp (argv[idx]->arg, "p", 1) == 0) + direct = COMMUNITY_PERMIT; + else + direct = COMMUNITY_DENY; + + idx = 0; + argv_find (argv, argc, "LINE", &idx); + argv_find (argv, argc, "AA:AA:NN", &idx); + /* Concat community string argument. */ + str = argv_concat (argv, argc, idx); + } + + idx = 0; + argv_find (argv, argc, "(1-99)", &idx); + argv_find (argv, argc, "(100-500)", &idx); + argv_find (argv, argc, "WORD", &idx); + + /* Unset community list. */ + ret = lcommunity_list_unset (bgp_clist, argv[idx]->arg, str, direct, style); + + /* Free temporary community list string allocated by + argv_concat(). */ + if (str) + XFREE (MTYPE_TMP, str); + + if (ret < 0) + { + community_list_perror (vty, ret); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* "large-community-list" keyword help string. */ +#define LCOMMUNITY_LIST_STR "Add a large community list entry\n" +#define LCOMMUNITY_VAL_STR "large community in 'aa:bb:cc' format\n" + +DEFUN (ip_lcommunity_list_standard, + ip_lcommunity_list_standard_cmd, + "ip large-community-list (1-99) <deny|permit>", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 0); +} + +DEFUN (ip_lcommunity_list_standard1, + ip_lcommunity_list_standard1_cmd, + "ip large-community-list (1-99) <deny|permit> AA:BB:CC...", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 0); +} + +DEFUN (ip_lcommunity_list_expanded, + ip_lcommunity_list_expanded_cmd, + "ip large-community-list (100-500) <deny|permit> LINE...", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (expanded)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 0); +} + +DEFUN (ip_lcommunity_list_name_standard, + ip_lcommunity_list_name_standard_cmd, + "ip large-community-list standard WORD <deny|permit>", + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n") +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 1); +} + +DEFUN (ip_lcommunity_list_name_standard1, + ip_lcommunity_list_name_standard1_cmd, + "ip large-community-list standard WORD <deny|permit> AA:BB:CC...", + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 1); +} + +DEFUN (ip_lcommunity_list_name_expanded, + ip_lcommunity_list_name_expanded_cmd, + "ip large-community-list expanded WORD <deny|permit> LINE...", + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 1); +} + +DEFUN (no_ip_lcommunity_list_standard_all, + no_ip_lcommunity_list_standard_all_cmd, + "no ip large-community-list <(1-99)|(100-500)|WORD>", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Large Community list number (expanded)\n" + "Large Community list name\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_name_expanded_all, + no_ip_lcommunity_list_name_expanded_all_cmd, + "no ip large-community-list expanded WORD", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large Community list name\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_lcommunity_list_standard, + no_ip_lcommunity_list_standard_cmd, + "no ip large-community-list (1-99) <deny|permit> AA:AA:NN...", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_expanded, + no_ip_lcommunity_list_expanded_cmd, + "no ip large-community-list (100-500) <deny|permit> LINE...", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (expanded)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_lcommunity_list_name_standard, + no_ip_lcommunity_list_name_standard_cmd, + "no ip large-community-list standard WORD <deny|permit> AA:AA:NN...", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_name_expanded, + no_ip_lcommunity_list_name_expanded_cmd, + "no ip large-community-list expanded WORD <deny|permit> LINE...", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +static void +lcommunity_list_show (struct vty *vty, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry == list->head) + { + if (all_digit (list->name)) + vty_out (vty, "Large community %s list %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + "standard" : "(expanded) access", + list->name, VTY_NEWLINE); + else + vty_out (vty, "Named large community %s list %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + "standard" : "expanded", + list->name, VTY_NEWLINE); + } + if (entry->any) + vty_out (vty, " %s%s", + community_direct_str (entry->direct), VTY_NEWLINE); + else + vty_out (vty, " %s %s%s", + community_direct_str (entry->direct), + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + entry->u.ecom->str : entry->config, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_lcommunity_list, + show_ip_lcommunity_list_cmd, + "show ip large-community-list", + SHOW_STR + IP_STR + "List large-community list\n") +{ + struct community_list *list; + struct community_list_master *cm; + + cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER); + if (! cm) + return CMD_SUCCESS; + + for (list = cm->num.head; list; list = list->next) + lcommunity_list_show (vty, list); + + for (list = cm->str.head; list; list = list->next) + lcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_lcommunity_list_arg, + show_ip_lcommunity_list_arg_cmd, + "show ip large-community-list <(1-500)|WORD>", + SHOW_STR + IP_STR + "List large-community list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, argv[3]->arg, LARGE_COMMUNITY_LIST_MASTER); + if (! list) + { + vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + lcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + /* "extcommunity-list" keyword help string. */ #define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n" #define EXTCOMMUNITY_VAL_STR "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n" @@ -11411,6 +11820,30 @@ community_list_config_write (struct vty *vty) community_list_config_str (entry), VTY_NEWLINE); write++; } + + + /* lcommunity-list. */ + cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER); + + for (list = cm->num.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip large-community-list %s %s %s%s", + list->name, community_direct_str (entry->direct), + community_list_config_str (entry), VTY_NEWLINE); + write++; + } + for (list = cm->str.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip large-community-list %s %s %s %s%s", + entry->style == LARGE_COMMUNITY_LIST_STANDARD + ? "standard" : "expanded", + list->name, community_direct_str (entry->direct), + community_list_config_str (entry), VTY_NEWLINE); + write++; + } + return write; } @@ -11441,4 +11874,20 @@ community_list_vty (void) install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_arg_cmd); + + /* Large Community List */ + install_element (CONFIG_NODE, &ip_lcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_standard1_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard1_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_cmd); + install_element (VIEW_NODE, &show_ip_lcommunity_list_cmd); + install_element (VIEW_NODE, &show_ip_lcommunity_list_arg_cmd); } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 3602a1e18..b0c163f1e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -902,6 +902,7 @@ peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi) { SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); } /* Clear neighbor default_originate_rmap */ @@ -1206,6 +1207,7 @@ peer_new (struct bgp *bgp) { SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); } peer->orf_plist[afi][safi] = NULL; } @@ -3702,6 +3704,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out }, { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, @@ -6982,10 +6985,17 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp, if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) { if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) - && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) + && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) + && peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) { afi_header_vty_out (vty, afi, safi, write, - " neighbor %s send-community both%s", + " neighbor %s send-community all%s", + addr, VTY_NEWLINE); + } + else if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) + { + afi_header_vty_out (vty, afi, safi, write, + " neighbor %s send-community large%s", addr, VTY_NEWLINE); } else if (peergroup_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) @@ -7006,10 +7016,19 @@ bgp_config_write_peer_af (struct vty *vty, struct bgp *bgp, if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) && (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) && !peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) && - (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY))) + (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) && + !peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY) && + (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))) + { + afi_header_vty_out (vty, afi, safi, write, + " no neighbor %s send-community all%s", + addr, VTY_NEWLINE); + } + else if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY) && + (!g_peer || peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY))) { afi_header_vty_out (vty, afi, safi, write, - " no neighbor %s send-community both%s", + " no neighbor %s send-community large%s", addr, VTY_NEWLINE); } else if (!peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) && diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 3dd5523dc..2eef04e1d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -705,6 +705,7 @@ struct peer #define PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS (1 << 23) /* addpath-tx-bestpath-per-AS */ #define PEER_FLAG_WEIGHT (1 << 24) /* weight */ #define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */ +#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */ /* MD5 password */ char *password; @@ -963,6 +964,7 @@ struct bgp_nlri #define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_ENCAP 23 +#define BGP_ATTR_LARGE_COMMUNITIES 32 #if ENABLE_BGP_VNC #define BGP_ATTR_VNC 255 #endif |