diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 69c0504af4..6e36a950d6 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 87cff44293..f4995faab0 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; @@ -1849,7 +1868,8 @@ int bgp_mp_reach_parse (struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update) { - afi_t pkt_afi, afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t pkt_safi, safi; bgp_size_t nlri_len; size_t start; @@ -2000,7 +2020,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, struct bgp_nlri *mp_withdraw) { struct stream *s; - afi_t pkt_afi, afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t pkt_safi, safi; u_int16_t withdraw_len; struct peer *const peer = args->peer; @@ -2042,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) @@ -2063,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); @@ -2477,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; @@ -2648,7 +2706,7 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, afi_t nh_afi, struct attr *attr) { size_t sizep; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; /* Set extended bit always to encode the attribute length as 2 bytes */ @@ -3104,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 @@ -3282,7 +3362,7 @@ size_t bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) { unsigned long attrlen_pnt; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; /* Set extended bit always to encode the attribute length as 2 bytes */ @@ -3336,6 +3416,7 @@ bgp_attr_init (void) attrhash_init (); community_init (); ecommunity_init (); + lcommunity_init (); cluster_init (); transit_init (); encap_init (); @@ -3348,6 +3429,7 @@ bgp_attr_finish (void) attrhash_finish (); community_finish (); ecommunity_finish (); + lcommunity_finish (); cluster_finish (); transit_finish (); encap_finish (); @@ -3451,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 6e639078d6..c5799ccd0d 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 9032b1e2f4..b37034bf29 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) { @@ -546,6 +642,30 @@ community_list_match (struct community *com, struct community_list *list) return 0; } +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) { @@ -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 277ab7226c..68e45c8f7b 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 b65af9e1fa..6689883d94 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 201f7bde06..11282a505d 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_encap.h b/bgpd/bgp_encap.h index f06dfc128c..0de737c49b 100644 --- a/bgpd/bgp_encap.h +++ b/bgpd/bgp_encap.h @@ -24,14 +24,8 @@ extern void bgp_encap_init (void); extern int bgp_nlri_parse_encap (struct peer *, struct attr *, struct bgp_nlri *); -int -bgp_show_encap ( - struct vty *vty, - afi_t afi, - struct prefix_rd *prd, - enum bgp_show_type type, - void *output_arg, - int tags); +extern int bgp_show_encap (struct vty *vty, afi_t afi, struct prefix_rd *prd, + enum bgp_show_type type, void *output_arg, int tags); #include "bgp_encap_types.h" #endif /* _QUAGGA_BGP_ENCAP_H */ diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c new file mode 100644 index 0000000000..549a2ebad8 --- /dev/null +++ b/bgpd/bgp_lcommunity.c @@ -0,0 +1,569 @@ +/* BGP Large Communities Attribute + * + * Copyright (C) 2016 Keyur Patel + * + * 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 + +#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 0000000000..de3697f477 --- /dev/null +++ b/bgpd/bgp_lcommunity.h @@ -0,0 +1,74 @@ +/* BGP Large Communities Attribute. + * + * Copyright (C) 2016 Keyur Patel + * + * 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 72c0311c17..85e32645ee 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 a4ce8b891b..341fb235d0 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 1701c70441..f564ff1691 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_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 7a2717acc0..6e722bbb55 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -566,6 +566,7 @@ DEFUN (no_vpnv6_network, return bgp_static_unset_safi (SAFI_MPLS_VPN, vty, argv[idx_ipv6_prefix]->arg, argv[idx_ext_community]->arg, argv[idx_word]->arg); } +#if defined(KEEP_OLD_VPN_COMMANDS) static int show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u_char use_json, afi_t afi) { @@ -732,6 +733,7 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd, u } return CMD_SUCCESS; } +#endif int bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 97eed3c35a..ea6cbcd272 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -30,6 +30,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define RD_ADDRSTRLEN 28 +#ifdef MPLS_LABEL_MAX +# undef MPLS_LABEL_MAX +#endif + typedef enum { MPLS_LABEL_IPV4_EXPLICIT_NULL = 0, /* [RFC3032] */ MPLS_LABEL_ROUTER_ALERT = 1, /* [RFC3032] */ @@ -45,7 +49,9 @@ typedef enum { MPLS_LABEL_UNASSIGNED11 = 11, MPLS_LABEL_GAL = 13, /* [RFC5586] */ MPLS_LABEL_OAM_ALERT = 14, /* [RFC3429] */ - MPLS_LABEL_EXTENSION = 15 /* [RFC7274] */ + MPLS_LABEL_EXTENSION = 15, /* [RFC7274] */ + MPLS_LABEL_MAX = 1048575, + MPLS_LABEL_ILLEGAL = 0xFFFFFFFF /* for internal use only */ } mpls_special_label_t; #define MPLS_LABEL_IS_SPECIAL(label) \ @@ -100,8 +106,7 @@ extern char *prefix_rd2str (struct prefix_rd *, char *, size_t); extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *afi); -int -bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, - enum bgp_show_type type, void *output_arg, int tags, u_char use_json); +extern int bgp_show_mpls_vpn (struct vty *vty, afi_t afi, struct prefix_rd *prd, + enum bgp_show_type type, void *output_arg, int tags, u_char use_json); #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 0a9747b526..0cf96101c2 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -492,7 +492,7 @@ bgp_show_all_instances_nexthops_vty (struct vty *vty) DEFUN (show_ip_bgp_nexthop, show_ip_bgp_nexthop_cmd, - "show [ip] bgp [ VRFNAME] nexthop [detail]", + "show [ip] bgp [ WORD] nexthop [detail]", SHOW_STR IP_STR BGP_STR @@ -501,7 +501,7 @@ DEFUN (show_ip_bgp_nexthop, "Show detailed information\n") { int idx = 0; - char *vrf = argv_find (argv, argc, "VRFNAME", &idx) ? argv[idx]->arg : NULL; + char *vrf = argv_find (argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; int detail = argv_find (argv, argc, "detail", &idx) ? 1 : 0; return show_ip_bgp_nexthop_table (vty, vrf, detail); } diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 4a06881041..7dbb439be1 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -188,7 +188,9 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr) { struct capability_mp_data mpc; struct stream *s = BGP_INPUT (peer); - + afi_t afi; + safi_t safi; + /* Verify length is 4 */ if (hdr->length != 4) { @@ -204,14 +206,14 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr) peer->host, mpc.afi, mpc.safi); /* Convert AFI, SAFI to internal values, check. */ - if (bgp_map_afi_safi_iana2int (mpc.afi, mpc.safi, &mpc.afi, &mpc.safi)) + if (bgp_map_afi_safi_iana2int (mpc.afi, mpc.safi, &afi, &safi)) return -1; /* Now safi remapped, and afi/safi are valid array indices */ - peer->afc_recv[mpc.afi][mpc.safi] = 1; + peer->afc_recv[afi][safi] = 1; - if (peer->afc[mpc.afi][mpc.safi]) - peer->afc_nego[mpc.afi][mpc.safi] = 1; + if (peer->afc[afi][safi]) + peer->afc_nego[afi][safi] = 1; else return -1; @@ -219,7 +221,7 @@ bgp_capability_mp (struct peer *peer, struct capability_header *hdr) } static void -bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi, +bgp_capability_orf_not_support (struct peer *peer, iana_afi_t afi, safi_t safi, u_char type, u_char mode) { if (bgp_debug_neighbor_events(peer)) @@ -247,7 +249,8 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) { struct stream *s = BGP_INPUT (peer); struct capability_orf_entry entry; - afi_t pkt_afi, afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t pkt_safi, safi; u_char type; u_char mode; @@ -274,7 +277,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) return 0; } - entry.mpc.afi = afi; + entry.mpc.afi = pkt_afi; entry.mpc.safi = safi; /* validate number field */ @@ -418,7 +421,7 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) { afi_t afi; safi_t safi; - afi_t pkt_afi = stream_getw (s); + iana_afi_t pkt_afi = stream_getw (s); safi_t pkt_safi = stream_getc (s); u_char flag = stream_getc (s); @@ -496,7 +499,7 @@ bgp_capability_addpath (struct peer *peer, struct capability_header *hdr) { afi_t afi; safi_t safi; - afi_t pkt_afi = stream_getw (s); + iana_afi_t pkt_afi = stream_getw (s); safi_t pkt_safi = stream_getc (s); u_char send_receive = stream_getc (s); @@ -550,9 +553,11 @@ bgp_capability_enhe (struct peer *peer, struct capability_header *hdr) while (stream_get_getp (s) + 6 <= end) { - afi_t afi, pkt_afi = stream_getw (s); + iana_afi_t pkt_afi = stream_getw (s); + afi_t afi; safi_t safi, pkt_safi = stream_getw (s); - afi_t nh_afi, pkt_nh_afi = stream_getw (s); + iana_afi_t pkt_nh_afi = stream_getw (s); + afi_t nh_afi; if (bgp_debug_neighbor_events(peer)) zlog_debug ("%s Received with afi/safi/next-hop afi: %u/%u/%u", @@ -1162,7 +1167,7 @@ bgp_open_capability_orf (struct stream *s, struct peer *peer, unsigned long orfp; unsigned long numberp; int number_of_orfs = 0; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; /* Convert AFI, SAFI to values for packet. */ @@ -1225,7 +1230,8 @@ bgp_open_capability (struct stream *s, struct peer *peer) { u_char len; unsigned long cp, capp, rcapp; - afi_t afi, pkt_afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t safi, pkt_safi; as_t local_as; u_int32_t restart_time; diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index b0a396ec11..9275b3a101 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -31,7 +31,7 @@ struct capability_header /* Generic MP capability data */ struct capability_mp_data { - afi_t afi; + iana_afi_t afi; u_char reserved; safi_t safi; }; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 38470a3c7e..2df22ab568 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" @@ -147,7 +148,7 @@ static struct stream * bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) { struct stream *s; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; if (DISABLE_BGP_ANNOUNCE) @@ -695,7 +696,7 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, struct stream *s; struct bgp_filter *filter; int orf_refresh = 0; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; if (DISABLE_BGP_ANNOUNCE) @@ -782,7 +783,7 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, int capability_code, int action) { struct stream *s; - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; /* Convert AFI, SAFI to values for packet. */ @@ -1711,7 +1712,8 @@ bgp_keepalive_receive (struct peer *peer, bgp_size_t size) static void bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) { - afi_t pkt_afi, afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t pkt_safi, safi; struct stream *s; struct peer_af *paf; @@ -1932,7 +1934,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) struct capability_mp_data mpc; struct capability_header *hdr; u_char action; - afi_t pkt_afi, afi; + iana_afi_t pkt_afi; + afi_t afi; safi_t pkt_safi, safi; end = pnt + length; @@ -2153,15 +2156,6 @@ bgp_marker_all_one (struct stream *s, int length) return 1; } -/* Recent thread time. - On same clock base as bgp_clock (MONOTONIC) - but can be time of last context switch to bgp_read thread. */ -static time_t -bgp_recent_clock (void) -{ - return recent_relative_time().tv_sec; -} - /* Starting point of packet process function. */ int bgp_read (struct thread *thread) @@ -2288,14 +2282,14 @@ bgp_read (struct thread *thread) bgp_open_receive (peer, size); /* XXX return value ignored! */ break; case BGP_MSG_UPDATE: - peer->readtime = bgp_recent_clock (); + peer->readtime = monotime (NULL); bgp_update_receive (peer, size); break; case BGP_MSG_NOTIFY: bgp_notify_receive (peer, size); break; case BGP_MSG_KEEPALIVE: - peer->readtime = bgp_recent_clock (); + peer->readtime = monotime (NULL); bgp_keepalive_receive (peer, size); break; case BGP_MSG_ROUTE_REFRESH_NEW: diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index eea0f86b34..fb33b234f4 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 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" @@ -2112,10 +2114,10 @@ bgp_maximum_prefix_restart_timer (struct thread *thread) } int -bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, +bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, safi_t safi, int always) { - afi_t pkt_afi; + iana_afi_t pkt_afi; safi_t pkt_safi; if (!CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) @@ -7061,7 +7063,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))) { @@ -7117,7 +7124,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) @@ -7172,7 +7179,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) { @@ -7215,30 +7222,30 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, #define BGP_SHOW_FLAP_HEADER " Network From Flaps Duration Reuse Path%s" static int -bgp_show_prefix_list (struct vty *vty, const char *name, +bgp_show_prefix_list (struct vty *vty, struct bgp *bgp, const char *prefix_list_str, afi_t afi, safi_t safi, enum bgp_show_type type); static int -bgp_show_filter_list (struct vty *vty, const char *name, +bgp_show_filter_list (struct vty *vty, struct bgp *bgp, const char *filter, afi_t afi, safi_t safi, enum bgp_show_type type); static int -bgp_show_route_map (struct vty *vty, const char *name, +bgp_show_route_map (struct vty *vty, struct bgp *bgp, const char *rmap_str, afi_t afi, safi_t safi, enum bgp_show_type type); static int -bgp_show_community_list (struct vty *vty, const char *name, +bgp_show_community_list (struct vty *vty, struct bgp *bgp, const char *com, int exact, afi_t afi, safi_t safi); static int -bgp_show_prefix_longer (struct vty *vty, const char *name, +bgp_show_prefix_longer (struct vty *vty, struct bgp *bgp, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type); static int bgp_show_regexp (struct vty *vty, const char *regstr, afi_t afi, safi_t safi, enum bgp_show_type type); static int -bgp_show_community (struct vty *vty, const char *view_name, int argc, +bgp_show_community (struct vty *vty, struct bgp *bgp, int argc, struct cmd_token **argv, int exact, afi_t afi, safi_t safi); static int @@ -7397,6 +7404,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) { @@ -7781,41 +7809,173 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, /* Display specified route of Main RIB */ static int -bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str, +bgp_show_route (struct vty *vty, struct bgp *bgp, const char *ip_str, afi_t afi, safi_t safi, struct prefix_rd *prd, int prefix_check, enum bgp_path_type pathtype, u_char use_json) { - struct bgp *bgp; - - /* BGP structure lookup. */ - if (view_name) - { - bgp = bgp_lookup_by_name (view_name); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP instance %s%s", view_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - else - { - bgp = bgp_get_default (); - if (bgp == NULL) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str, afi, safi, prd, prefix_check, pathtype, 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, "")) + { + 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 [ WORD] [ []] 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 [ WORD] [ []] 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); +} + +static int bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi); + /* BGP route print out function. */ -DEFUN (show_ip_bgp_ipv4, - show_ip_bgp_ipv4_cmd, +DEFUN (show_ip_bgp, + show_ip_bgp_cmd, "show [ip] bgp [ WORD] [ []]\ [<\ cidr-only\ @@ -7823,6 +7983,7 @@ DEFUN (show_ip_bgp_ipv4, |route-map WORD\ |prefix-list WORD\ |filter-list WORD\ + |statistics\ |community [ [exact-match]]\ |community-list <(1-500)|WORD> [exact-match]\ |A.B.C.D/M longer-prefixes\ @@ -7842,13 +8003,14 @@ DEFUN (show_ip_bgp_ipv4, "Display detailed information about dampening\n" "Display flap statistics of routes\n" "Display paths suppressed due to dampening\n" - "Display dampening parameters\n" + "Display detail of configured dampening parameters\n" "Display routes matching the route-map\n" "A route-map to match on\n" "Display routes conforming to the prefix-list\n" "Prefix-list name\n" "Display routes conforming to the filter-list\n" "Regular expression access list name\n" + "BGP RIB advertisement statistics\n" "Display routes matching the communities\n" COMMUNITY_AANN_STR "Do not send outside local AS (well-known community)\n" @@ -7865,87 +8027,80 @@ DEFUN (show_ip_bgp_ipv4, "Display route and more specific routes\n" JSON_STR) { - char *vrf = NULL; + vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int exact_match = 0; enum bgp_show_type sh_type = bgp_show_type_normal; - + struct bgp *bgp = NULL; 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); - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpn", &idx)) - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - } + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; + int uj = use_json (argc, argv); if (uj) argc--; - struct bgp *bgp = bgp_lookup_by_name (vrf); + bgp = bgp_lookup_by_vrf_id (vrf); if (bgp == NULL) - { - vty_out (vty, "Can't find BGP instance %s%s", vrf, VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); + return CMD_WARNING; + } - if (++idx < argc) - { - if (strmatch(argv[idx]->text, "cidr-only")) - return bgp_show (vty, bgp, afi, safi, bgp_show_type_cidr_only, NULL, uj); + if (argv_find(argv, argc, "cidr-only", &idx)) + return bgp_show (vty, bgp, afi, safi, bgp_show_type_cidr_only, NULL, uj); - else if (strmatch(argv[idx]->text, "dampening")) + if (argv_find(argv, argc, "dampening", &idx)) { if (argv_find (argv, argc, "dampened-paths", &idx)) return bgp_show (vty, bgp, afi, safi, bgp_show_type_dampend_paths, NULL, uj); else if (argv_find (argv, argc, "flap-statistics", &idx)) return bgp_show (vty, bgp, afi, safi, bgp_show_type_flap_statistics, NULL, uj); else if (argv_find (argv, argc, "parameters", &idx)) - return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST); + return bgp_show_dampening_parameters (vty, afi, safi); } - else if (strmatch(argv[idx]->text, "prefix-list")) - return bgp_show_prefix_list (vty, vrf, argv[idx + 1]->arg, afi, safi, bgp_show_type_prefix_list); + if (argv_find(argv, argc, "prefix-list", &idx)) + return bgp_show_prefix_list (vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_prefix_list); - else if (strmatch(argv[idx]->text, "filter-list")) - return bgp_show_filter_list (vty, vrf, argv[idx + 1]->arg, afi, safi, bgp_show_type_filter_list); + if (argv_find(argv, argc, "filter-list", &idx)) + return bgp_show_filter_list (vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_filter_list); - else if (strmatch(argv[idx]->text, "route-map")) - return bgp_show_route_map (vty, vrf, argv[idx + 1]->arg, afi, safi, bgp_show_type_route_map); + if (argv_find(argv, argc, "statistics", &idx)) + return bgp_table_stats (vty, bgp, afi, safi); - else if (strmatch(argv[idx]->text, "community")) + if (argv_find(argv, argc, "route-map", &idx)) + return bgp_show_route_map (vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_route_map); + + if (argv_find(argv, argc, "community", &idx)) { /* show a specific community */ - if (argv[idx + 1]->type == VARIABLE_TKN || - strmatch(argv[idx + 1]->text, "local-AS") || - strmatch(argv[idx + 1]->text, "no-advertise") || - strmatch(argv[idx + 1]->text, "no-export")) + if (argv_find (argv, argc, "local-AS", &idx) || + argv_find (argv, argc, "no-advertise", &idx) || + argv_find (argv, argc, "no-export", &idx)) { - if (strmatch(argv[idx + 2]->text, "exact_match")) + if (argv_find (argv, argc, "exact_match", &idx)) exact_match = 1; - return bgp_show_community (vty, vrf, argc, argv, exact_match, afi, safi); + return bgp_show_community (vty, bgp, argc, argv, exact_match, afi, safi); } /* show all communities */ else return bgp_show (vty, bgp, afi, safi, bgp_show_type_community_all, NULL, uj); } - else if (strmatch(argv[idx]->text, "community-list")) - { - const char *clist_number_or_name = argv[++idx]->arg; - if (++idx < argc && strmatch (argv[idx]->arg, "exact-match")) - exact_match = 1; - return bgp_show_community_list (vty, vrf, clist_number_or_name, exact_match, afi, safi); - } - /* prefix-longer */ - else if (argv[idx]->type == IPV4_TKN || argv[idx]->type == IPV6_TKN) - return bgp_show_prefix_longer (vty, vrf, argv[idx + 1]->arg, afi, safi, bgp_show_type_prefix_longer); - } + + if (argv_find(argv, argc, "community-list", &idx)) + { + const char *clist_number_or_name = argv[++idx]->arg; + if (++idx < argc && strmatch (argv[idx]->text, "exact-match")) + exact_match = 1; + return bgp_show_community_list (vty, bgp, clist_number_or_name, exact_match, afi, safi); + } + /* prefix-longer */ + if (argv_find(argv, argc, "A.B.C.D/M", &idx) || argv_find(argv, argc, "X:X::X:X/M", &idx)) + return bgp_show_prefix_longer (vty, bgp, argv[idx + 1]->arg, afi, safi, bgp_show_type_prefix_longer); + if (safi == SAFI_MPLS_VPN) return bgp_show_mpls_vpn (vty, afi, NULL, bgp_show_type_normal, NULL, 0, uj); else if (safi == SAFI_ENCAP) @@ -7980,34 +8135,32 @@ DEFUN (show_ip_bgp_route, afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; - char *vrf = NULL; + vrf_id_t vrf = VRF_DEFAULT;; char *prefix = NULL; - + struct bgp *bgp = NULL; enum bgp_path_type path_type; u_char uj = use_json(argc, argv); int idx = 0; - /* show [ip] bgp */ - if (argv_find (argv, argc, "ip", &idx)) - afi = AFI_IP; - /* [ WORD] */ - if (argv_find (argv, argc, "view", &idx) || argv_find (argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; - /* []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] */ - 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 = strmatch (argv[idx]->text, "unicast") ? SAFI_UNICAST : SAFI_MULTICAST; - } - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpnv4", &idx)) - { - afi = AFI_IP; - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - // advance idx if necessary - argv_find (argv, argc, "unicast", &idx); - } + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; + + if (vrf != VRF_ALL) + { + bgp = bgp_lookup_by_vrf_id (vrf); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + vty_out (vty, "Specified 'all' vrf's but this command currently only works per view/vrf%s", VTY_NEWLINE); + return CMD_WARNING; + } /* */ if (argv_find (argv, argc, "A.B.C.D", &idx) || argv_find (argv, argc, "X:X::X:X", &idx)) @@ -8036,51 +8189,33 @@ DEFUN (show_ip_bgp_route, else path_type = BGP_PATH_ALL; - return bgp_show_route (vty, vrf, prefix, afi, safi, NULL, prefix_check, path_type, uj); + return bgp_show_route (vty, bgp, prefix, afi, safi, NULL, prefix_check, path_type, uj); } DEFUN (show_ip_bgp_regexp, show_ip_bgp_regexp_cmd, - "show [ip] bgp []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] regexp REGEX...", + "show [ip] bgp [ WORD] [ []] regexp REGEX...", 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" - "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Address Family\n" - "Address Family modifier\n" - "Address Family\n" - "Address Family modifier\n" "Display routes matching the AS path regular expression\n" "A regular-expression to match the BGP AS paths\n") { + vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int idx = 0; - - /* []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] */ - 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 = strmatch (argv[idx]->text, "unicast") ? SAFI_UNICAST : SAFI_MULTICAST; - } - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpnv4", &idx)) - { - afi = AFI_IP; - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - // advance idx if necessary - argv_find (argv, argc, "unicast", &idx); - } + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; // get index of regex argv_find (argv, argc, "regexp", &idx); @@ -8094,53 +8229,35 @@ DEFUN (show_ip_bgp_regexp, DEFUN (show_ip_bgp_instance_all, show_ip_bgp_instance_all_cmd, - "show [ip] bgp all []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] [json]", + "show [ip] bgp all [ []] [json]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_ALL_HELP_STR "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" "Address Family\n" "Address Family modifier\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" JSON_STR) { + vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP; safi_t safi = SAFI_UNICAST; int idx = 0; + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; - /* show [ip] bgp */ - if (argv_find (argv, argc, "ip", &idx)) - afi = AFI_IP; - /* []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] */ - 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 = strmatch (argv[idx]->text, "unicast") ? SAFI_UNICAST : SAFI_MULTICAST; - } - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpnv4", &idx)) - { - afi = AFI_IP; - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - // advance idx if necessary - argv_find (argv, argc, "unicast", &idx); - } - - u_char uj = use_json(argc, argv); + int uj = use_json (argc, argv); + if (uj) argc--; bgp_show_all_instances_routes_vty (vty, afi, safi, uj); return CMD_SUCCESS; } - static int bgp_show_regexp (struct vty *vty, const char *regstr, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -8163,18 +8280,11 @@ bgp_show_regexp (struct vty *vty, const char *regstr, afi_t afi, } static int -bgp_show_prefix_list (struct vty *vty, const char *name, +bgp_show_prefix_list (struct vty *vty, struct bgp *bgp, const char *prefix_list_str, afi_t afi, safi_t safi, enum bgp_show_type type) { struct prefix_list *plist; - struct bgp *bgp = NULL; - - if (name && !(bgp = bgp_lookup_by_name(name))) - { - vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); - return CMD_WARNING; - } plist = prefix_list_lookup (afi, prefix_list_str); if (plist == NULL) @@ -8188,18 +8298,11 @@ bgp_show_prefix_list (struct vty *vty, const char *name, } static int -bgp_show_filter_list (struct vty *vty, const char *name, +bgp_show_filter_list (struct vty *vty, struct bgp *bgp, const char *filter, afi_t afi, safi_t safi, enum bgp_show_type type) { struct as_list *as_list; - struct bgp *bgp = NULL; - - if (name && !(bgp = bgp_lookup_by_name(name))) - { - vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); - return CMD_WARNING; - } as_list = as_list_lookup (filter); if (as_list == NULL) @@ -8211,53 +8314,12 @@ bgp_show_filter_list (struct vty *vty, const char *name, return bgp_show (vty, bgp, afi, safi, type, as_list, 0); } -DEFUN (show_ip_bgp_dampening_info, - show_ip_bgp_dampening_params_cmd, - "show [ip] bgp dampening parameters", - SHOW_STR - IP_STR - BGP_STR - "Display detailed information about dampening\n" - "Display detail of configured dampening parameters\n") -{ - return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST); -} - - -DEFUN (show_ip_bgp_ipv4_dampening_parameters, - show_ip_bgp_ipv4_dampening_parameters_cmd, - "show [ip] bgp ipv4 dampening parameters", - SHOW_STR - IP_STR - BGP_STR - "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display detailed information about dampening\n" - "Display detail of configured dampening parameters\n") -{ - int idx_safi = 4; - if (strncmp(argv[idx_safi]->arg, "m", 1) == 0) - return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_MULTICAST); - - return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST); -} - static int -bgp_show_route_map (struct vty *vty, const char *name, +bgp_show_route_map (struct vty *vty, struct bgp *bgp, const char *rmap_str, afi_t afi, safi_t safi, enum bgp_show_type type) { struct route_map *rmap; - struct bgp *bgp = NULL; - - if (name && !(bgp = bgp_lookup_by_name(name))) - { - - - vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); - return CMD_WARNING; - } rmap = route_map_lookup_by_name (rmap_str); if (! rmap) @@ -8271,36 +8333,15 @@ bgp_show_route_map (struct vty *vty, const char *name, } static int -bgp_show_community (struct vty *vty, const char *view_name, int argc, +bgp_show_community (struct vty *vty, struct bgp *bgp, int argc, struct cmd_token **argv, int exact, afi_t afi, safi_t safi) { struct community *com; struct buffer *b; - struct bgp *bgp; int i; char *str; int first = 0; - /* BGP structure lookup */ - if (view_name) - { - bgp = bgp_lookup_by_name (view_name); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP instance %s%s", view_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - else - { - bgp = bgp_get_default (); - if (bgp == NULL) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - b = buffer_new (1024); for (i = 0; i < argc; i++) { @@ -8334,18 +8375,11 @@ bgp_show_community (struct vty *vty, const char *view_name, int argc, } static int -bgp_show_community_list (struct vty *vty, const char *name, +bgp_show_community_list (struct vty *vty, struct bgp *bgp, const char *com, int exact, afi_t afi, safi_t safi) { struct community_list *list; - struct bgp *bgp = NULL; - - if (name && !(bgp = bgp_lookup_by_name(name))) - { - vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); - return CMD_WARNING; - } list = community_list_lookup (bgp_clist, com, COMMUNITY_LIST_MASTER); if (list == NULL) @@ -8361,19 +8395,12 @@ bgp_show_community_list (struct vty *vty, const char *name, } static int -bgp_show_prefix_longer (struct vty *vty, const char *name, +bgp_show_prefix_longer (struct vty *vty, struct bgp *bgp, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type) { int ret; struct prefix *p; - struct bgp *bgp = NULL; - - if (name && !(bgp = bgp_lookup_by_name(name))) - { - vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); - return CMD_WARNING; - } p = prefix_new(); @@ -8390,52 +8417,13 @@ bgp_show_prefix_longer (struct vty *vty, const char *name, } static struct peer * -peer_lookup_in_view (struct vty *vty, const char *view_name, +peer_lookup_in_view (struct vty *vty, struct bgp *bgp, const char *ip_str, u_char use_json) { int ret; - struct bgp *bgp; struct peer *peer; union sockunion su; - /* BGP structure lookup. */ - if (view_name) - { - bgp = bgp_lookup_by_name (view_name); - if (! bgp) - { - if (use_json) - { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", "Can't find BGP view"); - vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); - json_object_free(json_no); - } - else - vty_out (vty, "Can't find BGP instance %s%s", view_name, VTY_NEWLINE); - return NULL; - } - } - else - { - bgp = bgp_get_default (); - if (! bgp) - { - if (use_json) - { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", "No BGP process configured"); - vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); - json_object_free(json_no); - } - else - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return NULL; - } - } - /* Get peer sockunion. */ ret = str2sockunion (ip_str, &su); if (ret < 0) @@ -8731,81 +8719,6 @@ bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) return CMD_SUCCESS; } -static int -bgp_table_stats_vty (struct vty *vty, const char *name, - const char *afi_str, const char *safi_str) -{ - struct bgp *bgp; - afi_t afi; - safi_t safi; - - if (name) - bgp = bgp_lookup_by_name (name); - else - bgp = bgp_get_default (); - - if (!bgp) - { - vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - afi = bgp_vty_afi_from_arg(afi_str); - if (afi == AFI_MAX) - { - vty_out (vty, "%% Invalid address family \"%s\"%s", - afi_str, VTY_NEWLINE); - return CMD_WARNING; - } - safi = bgp_vty_safi_from_arg(safi_str); - if (safi == SAFI_MAX) - { - vty_out (vty, "%% Invalid subsequent address family %s%s", - safi_str, VTY_NEWLINE); - return CMD_WARNING; - } - - return bgp_table_stats (vty, bgp, afi, safi); -} - -DEFUN (show_bgp_statistics, - show_bgp_statistics_cmd, - "show [ip] bgp statistics", - SHOW_STR - IP_STR - BGP_STR - "Address Family\n" - "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Address Family modifier\n" - "BGP RIB advertisement statistics\n") -{ - int idx_afi = 2; - int idx_safi = 3; - return bgp_table_stats_vty (vty, NULL, argv[idx_afi]->arg, argv[idx_safi]->arg); -} - -DEFUN (show_bgp_statistics_view, - show_bgp_statistics_view_cmd, - "show [ip] bgp WORD statistics", - 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" - "BGP RIB advertisement statistics\n") -{ - int idx_word = 3; - int idx_afi = 4; - return bgp_table_stats_vty (vty, NULL, argv[idx_word]->arg, argv[idx_afi]->arg); -} - enum bgp_pcounts { PCOUNT_ADJ_IN = 0, @@ -8997,117 +8910,20 @@ bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, u_c return CMD_SUCCESS; } -DEFUN (show_ip_bgp_neighbor_prefix_counts, - show_ip_bgp_neighbor_prefix_counts_cmd, - "show [ip] bgp neighbors prefix-counts [json]", - SHOW_STR - IP_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - "Display detailed prefix count information\n" - JSON_STR) -{ - int idx_peer = 4; - struct peer *peer; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, NULL, argv[idx_peer]->arg, uj); - if (! peer) - return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST, uj); -} - DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, show_ip_bgp_instance_neighbor_prefix_counts_cmd, - "show [ip] bgp WORD neighbors prefix-counts [json]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - "Display detailed prefix count information\n" - JSON_STR) -{ - int idx_word = 4; - int idx_peer = 6; - struct peer *peer; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, argv[idx_word]->arg, argv[idx_peer]->arg, uj); - if (! peer) - return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST, uj); -} - -DEFUN (show_bgp_ipv6_neighbor_prefix_counts, - show_bgp_ipv6_neighbor_prefix_counts_cmd, - "show [ip] bgp ipv6 neighbors prefix-counts [json]", - SHOW_STR - IP_STR - BGP_STR - "Address Family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - "Display detailed prefix count information\n" - JSON_STR) -{ - int idx_peer = 4; - struct peer *peer; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, NULL, argv[idx_peer]->arg, uj); - if (! peer) - return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST, uj); -} - -DEFUN (show_bgp_instance_ipv6_neighbor_prefix_counts, - show_bgp_instance_ipv6_neighbor_prefix_counts_cmd, - "show [ip] bgp WORD ipv6 neighbors prefix-counts [json]", + "show [ip] bgp [ WORD] [ []] " + "neighbors prefix-counts [json]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR "Address Family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - "Display detailed prefix count information\n" - JSON_STR) -{ - int idx_word = 3; - int idx_peer = 6; - struct peer *peer; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, argv[idx_word]->arg, argv[idx_peer]->arg, uj); - if (! peer) - return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST, uj); -} - -DEFUN (show_ip_bgp_ipv4_neighbor_prefix_counts, - show_ip_bgp_ipv4_neighbor_prefix_counts_cmd, - "show [ip] bgp ipv4 neighbors prefix-counts [json]", - SHOW_STR - IP_STR - BGP_STR "Address Family\n" "Address Family modifier\n" "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -9115,17 +8931,45 @@ DEFUN (show_ip_bgp_ipv4_neighbor_prefix_counts, "Display detailed prefix count information\n" JSON_STR) { - int idx_safi = 4; - int idx_peer = 6; + vrf_id_t vrf = VRF_DEFAULT; + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; struct peer *peer; - u_char uj = use_json(argc, argv); + int idx = 0; + struct bgp *bgp = NULL; - peer = peer_lookup_in_view (vty, NULL, argv[idx_peer]->arg, uj); - if (! peer) + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) return CMD_WARNING; - if (strncmp (argv[idx_safi]->arg, "m", 1) == 0) - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MULTICAST, uj); + int uj = use_json (argc, argv); + if (uj) argc--; + + if (vrf != VRF_ALL) + { + bgp = bgp_lookup_by_vrf_id (vrf); + if (bgp == NULL) + { + if (uj) + { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", "Can't find BGP view"); + vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); + json_object_free(json_no); + } + else + vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + bgp = NULL; + + argv_find (argv, argc, "neighbors", &idx); + peer = peer_lookup_in_view (vty, bgp, argv[idx+1]->arg, uj); + if (! peer) + return CMD_WARNING; return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST, uj); } @@ -9171,9 +9015,15 @@ DEFUN (show_ip_bgp_vpn_all_route_prefix, { int idx = 0; char *network = NULL; + struct bgp *bgp = bgp_get_default(); + if (!bgp) + { + vty_out (vty, "Can't find default instance%s", VTY_NEWLINE); + return CMD_WARNING; + } network = argv_find (argv, argc, "A.B.C.D", &idx) ? argv[idx]->arg : NULL; network = argv_find (argv, argc, "A.B.C.D/M", &idx) ? argv[idx]->arg : NULL; - return bgp_show_route (vty, NULL, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL, use_json(argc, argv)); + return bgp_show_route (vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL, use_json(argc, argv)); } #endif /* KEEP_OLD_VPN_COMMANDS */ @@ -9419,20 +9269,17 @@ peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, DEFUN (show_ip_bgp_instance_neighbor_advertised_route, show_ip_bgp_instance_neighbor_advertised_route_cmd, - "show [ip] bgp [ WORD] []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] neighbors [ [route-map WORD]] [json]", + "show [ip] bgp [ WORD] [ []] " + "neighbors [ [route-map WORD]] [json]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" "Address Family\n" "Address Family modifier\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -9444,51 +9291,47 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, "Name of the route map\n" JSON_STR) { + vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; - char *vrf = NULL; char *rmap_name = NULL; char *peerstr = NULL; int rcvd = 0; - + struct bgp *bgp = NULL; struct peer *peer; int idx = 0; - /* show [ip] bgp */ - if (argv_find (argv, argc, "ip", &idx)) - afi = AFI_IP; - /* [ WORD] */ - if (argv_find (argv, argc, "view", &idx) || argv_find (argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; - /* []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] */ - 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 = strmatch (argv[idx]->text, "unicast") ? SAFI_UNICAST : SAFI_MULTICAST; - } - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpnv4", &idx)) - { - afi = AFI_IP; - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - // advance idx if necessary - argv_find (argv, argc, "unicast", &idx); - } + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; + + int uj = use_json (argc, argv); + if (uj) argc--; + + bgp = bgp_lookup_by_vrf_id (vrf); + if (bgp == NULL) + { + if (uj) + { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", "Can't find BGP view"); + vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); + json_object_free(json_no); + } + else + vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); + return CMD_WARNING; + } /* neighbors */ argv_find (argv, argc, "neighbors", &idx); peerstr = argv[++idx]->arg; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, vrf, peerstr, uj); - + peer = peer_lookup_in_view (vty, bgp, peerstr, uj); if (! peer) - { - vty_out (vty, "No such neighbor%s", VTY_NEWLINE); - return CMD_WARNING; - } + return CMD_WARNING; if (argv_find (argv, argc, "received-routes", &idx)) rcvd = 1; @@ -9611,20 +9454,17 @@ bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi, DEFUN (show_ip_bgp_neighbor_routes, show_ip_bgp_neighbor_routes_cmd, - "show [ip] bgp [ WORD] []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] neighbors [json]", + "show [ip] bgp [ WORD] [ []] " + "neighbors [json]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR "Address Family\n" - "Address Family modifier\n" - "Address Family modifier\n" "Address Family\n" "Address Family modifier\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" - "Address Family\n" "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -9635,9 +9475,9 @@ DEFUN (show_ip_bgp_neighbor_routes, "Display routes learned from neighbor\n" JSON_STR) { - char *vrf = NULL; + vrf_id_t vrf = VRF_DEFAULT; char *peerstr = NULL; - + struct bgp *bgp = NULL; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct peer *peer; @@ -9645,33 +9485,39 @@ DEFUN (show_ip_bgp_neighbor_routes, int idx = 0; - /* show [ip] bgp */ - if (argv_find (argv, argc, "ip", &idx)) - afi = AFI_IP; - /* [ WORD] */ - if (argv_find (argv, argc, "view", &idx) || argv_find (argv, argc, "vrf", &idx)) - vrf = argv[++idx]->arg; - /* []|ipv6 []|encap [unicast]|vpnv4 [unicast]>] */ - 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 = strmatch (argv[idx]->text, "unicast") ? SAFI_UNICAST : SAFI_MULTICAST; - } - else if (argv_find (argv, argc, "encap", &idx) || argv_find (argv, argc, "vpnv4", &idx)) - { - afi = AFI_IP; - safi = strmatch (argv[idx]->text, "encap") ? SAFI_ENCAP : SAFI_MPLS_VPN; - // advance idx if necessary - argv_find (argv, argc, "unicast", &idx); - } + bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + if (!idx) + return CMD_WARNING; + + int uj = use_json (argc, argv); + if (uj) argc--; + + if (vrf != VRF_ALL) + { + bgp = bgp_lookup_by_vrf_id (vrf); + if (bgp == NULL) + { + if (uj) + { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add(json_no, "warning", "Can't find BGP view"); + vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); + json_object_free(json_no); + } + else + vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + bgp = NULL; + /* neighbors */ argv_find (argv, argc, "neighbors", &idx); peerstr = argv[++idx]->arg; - u_char uj = use_json(argc, argv); - - peer = peer_lookup_in_view (vty, vrf, peerstr, uj); + peer = peer_lookup_in_view (vty, bgp, peerstr, uj); if (! peer) { vty_out (vty, "No such neighbor%s", VTY_NEWLINE); @@ -10505,14 +10351,13 @@ bgp_route_init (void) install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_all_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd); + install_element (VIEW_NODE, &show_ip_bgp_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); - install_element (VIEW_NODE, &show_ip_bgp_dampening_params_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_parameters_cmd); #ifdef KEEP_OLD_VPN_COMMANDS install_element (VIEW_NODE, &show_ip_bgp_vpn_all_route_prefix_cmd); #endif /* KEEP_OLD_VPN_COMMANDS */ @@ -10520,18 +10365,15 @@ 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); /* prefix count */ - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_prefix_counts_cmd); install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbor_prefix_counts_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_prefix_counts_cmd); #ifdef KEEP_OLD_VPN_COMMANDS install_element (ENABLE_NODE, &show_ip_bgp_vpn_neighbor_prefix_counts_cmd); #endif /* KEEP_OLD_VPN_COMMANDS */ - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_prefix_counts_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbor_prefix_counts_cmd); /* New config IPv6 BGP commands. */ install_element (BGP_IPV6_NODE, &bgp_table_map_cmd); @@ -10546,10 +10388,6 @@ bgp_route_init (void) install_element (BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); install_element (BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd); - /* Statistics */ - install_element (ENABLE_NODE, &show_bgp_statistics_cmd); - install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd); - install_element (BGP_NODE, &bgp_distance_cmd); install_element (BGP_NODE, &no_bgp_distance_cmd); install_element (BGP_NODE, &bgp_distance_source_cmd); @@ -10589,6 +10427,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_route.h b/bgpd/bgp_route.h index f2e6273b84..2103338b7d 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -41,6 +41,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, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 50524baa01..8f2486314a 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, @@ -3992,7 +4399,7 @@ DEFUN (no_set_vpn_nexthop, DEFUN (set_ipx_vpn_nexthop, set_ipx_vpn_nexthop_cmd, - "set vpn next-hop [A.B.C.D|X:X::X:X]", + "set vpn next-hop []", SET_STR "IPv4 information\n" "IPv6 information\n" @@ -4019,7 +4426,7 @@ DEFUN (set_ipx_vpn_nexthop, DEFUN (no_set_ipx_vpn_nexthop, no_set_ipx_vpn_nexthop_cmd, - "no set vpn next-hop [A.B.C.D|X:X::X:X]", + "no set vpn next-hop []", NO_STR SET_STR "IPv4 information\n" @@ -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 80efe5f591..94809d69f5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -41,14 +41,15 @@ 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" -#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_open.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_vty.h" @@ -167,17 +168,7 @@ bgp_vty_safi_from_arg(const char *safi_str) } int -bgp_parse_safi(const char *str, safi_t *safi) -{ - *safi = bgp_vty_safi_from_arg(str); - if (*safi != SAFI_MAX) - return 0; - else - return -1; -} - -int -argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t *safi) +argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t *safi) { int ret = 0; if (argv_find (argv, argc, "unicast", index)) @@ -207,6 +198,80 @@ argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t * return ret; } +/* + * bgp_vty_find_and_parse_afi_safi_vrf + * + * For a given 'show ...' command, correctly parse the afi/safi/vrf out from it + * This function *assumes* that the calling function pre-sets the afi/safi/vrf + * to appropriate values for the calling function. This is to allow the + * calling function to make decisions appropriate for the show command + * that is being parsed. + * + * The show commands are generally of the form: + * "show [ip] bgp [ WORD] [ []] ..." + * + * Since we use argv_find if the show command in particular doesn't have: + * [ip] + * [ WORD] + * [ []] + * The command parsing should still be ok. + * + * vty -> The vty for the command so we can output some useful data in + * the event of a parse error in the vrf. + * argv -> The command tokens + * argc -> How many command tokens we have + * idx -> The current place in the command, generally should be 0 for this function + * afi -> The parsed afi if it was included in the show command, returned here + * safi -> The parsed safi if it was included in the show command, returned here + * vrf -> The parsed vrf id if it was included in the show command, returned here + * + * The function returns the correct location in the parse tree for the + * last token found. + * + * Returns 0 for failure to parse correctly, else the idx position of where + * it found the last token. + */ +int +bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx, + afi_t *afi, safi_t *safi, vrf_id_t *vrf) +{ + char *vrf_name = NULL; + + assert (afi); + assert (safi); + assert (vrf && *vrf != VRF_UNKNOWN); + + if (argv_find (argv, argc, "ip", idx)) + *afi = AFI_IP; + + if (argv_find (argv, argc, "view", idx) || argv_find (argv, argc, "vrf", idx)) + { + vrf_name = argv[*idx + 1]->arg; + *idx += 2; + } + + if (argv_find_and_parse_afi (argv, argc, idx, afi)) + argv_find_and_parse_safi (argv, argc, idx, safi); + + if (vrf_name) + { + if (strmatch(vrf_name, "all")) + *vrf = VRF_ALL; + else + *vrf = vrf_name_to_id (vrf_name); + } + + if (*vrf == VRF_UNKNOWN) + { + vty_out (vty, "View/Vrf specified is unknown: %s", vrf_name); + *idx = 0; + return 0; + } + + *idx += 1; + return *idx; +} + static int peer_address_self_check (struct bgp *bgp, union sockunion *su) { @@ -3748,13 +3813,15 @@ DEFUN (no_neighbor_send_community, /* neighbor send-community extended. */ DEFUN (neighbor_send_community_type, neighbor_send_community_type_cmd, - "neighbor send-community ", + "neighbor send-community ", 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 +3832,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 send-community ", + "no neighbor send-community ", 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 +3872,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. */ @@ -5586,7 +5673,7 @@ DEFUN (address_family_vpnv6, vty->node = BGP_VPNV6_NODE; return CMD_SUCCESS; } -#endif /* KEEP_OLD_VPN_COMMANDS */ +#endif DEFUN (address_family_encap, address_family_encap_cmd, @@ -6134,6 +6221,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, @@ -7111,12 +7204,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 @@ -8559,7 +8656,7 @@ DEFUN (show_ip_bgp_neighbors, SHOW_STR IP_STR BGP_STR - BGP_INSTANCE_ALL_HELP_STR + BGP_INSTANCE_HELP_STR "Address Family\n" "Address Family\n" "Address Family\n" @@ -8644,6 +8741,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", @@ -9195,7 +9322,7 @@ bgp_show_peer_group_vty (struct vty *vty, const char *name, DEFUN (show_ip_bgp_peer_groups, show_ip_bgp_peer_groups_cmd, - "show [ip] bgp [ VRFNAME] peer-group [PGNAME]", + "show [ip] bgp [ WORD] peer-group [PGNAME]", SHOW_STR IP_STR BGP_STR @@ -9207,7 +9334,7 @@ DEFUN (show_ip_bgp_peer_groups, vrf = pg = NULL; int idx = 0; - vrf = argv_find (argv, argc, "VRFNAME", &idx) ? argv[idx]->arg : NULL; + vrf = argv_find (argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; pg = argv_find (argv, argc, "PGNAME", &idx) ? argv[idx]->arg : NULL; return bgp_show_peer_group_vty (vty, vrf, show_all_groups, pg); @@ -10695,6 +10822,7 @@ bgp_vty_init (void) install_element (BGP_NODE, &address_family_vpnv4_cmd); install_element (BGP_NODE, &address_family_vpnv6_cmd); #endif /* KEEP_OLD_VPN_COMMANDS */ + install_element (BGP_NODE, &address_family_encap_cmd); install_element (BGP_NODE, &address_family_encapv6_cmd); @@ -10745,6 +10873,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); @@ -11077,6 +11207,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) ", + 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) 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) 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 ", + 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 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 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) 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) 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 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 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" @@ -11386,6 +11860,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; } @@ -11416,4 +11914,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/bgp_vty.h b/bgpd/bgp_vty.h index 40c1723218..13e67d112e 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -51,9 +51,6 @@ peer_and_group_lookup_vty (struct vty *vty, const char *peer_str); extern int bgp_parse_afi(const char *str, afi_t *afi); -extern int -bgp_parse_safi(const char *str, safi_t *safi); - extern afi_t bgp_vty_afi_from_arg(const char *afi_str); @@ -66,4 +63,7 @@ argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index, afi_t *af extern int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t *safi); +extern int +bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx, + afi_t *afi, safi_t *safi, vrf_id_t *vrf); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index ec2223d3ed..b0c163f1e3 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -348,7 +348,7 @@ time_t bgp_clock (void) { struct timeval tv; - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv); + monotime(&tv); return tv.tv_sec; } @@ -652,7 +652,7 @@ bgp_listen_limit_unset (struct bgp *bgp) } int -bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi, +bgp_map_afi_safi_iana2int (iana_afi_t pkt_afi, safi_t pkt_safi, afi_t *afi, safi_t *safi) { /* Map from IANA values to internal values, return error if @@ -668,7 +668,7 @@ bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi, int bgp_map_afi_safi_int2iana (afi_t afi, safi_t safi, - afi_t *pkt_afi, safi_t *pkt_safi) + iana_afi_t *pkt_afi, safi_t *pkt_safi) { /* Map from internal values to IANA values, return error if * internal values are bad (unexpected). @@ -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 both%s", + " 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 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 2718805130..2eef04e1d1 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 @@ -1355,11 +1357,11 @@ extern void bgp_route_map_terminate(void); extern int peer_cmp (struct peer *p1, struct peer *p2); extern int -bgp_map_afi_safi_iana2int (afi_t pkt_afi, safi_t pkt_safi, +bgp_map_afi_safi_iana2int (iana_afi_t pkt_afi, safi_t pkt_safi, afi_t *afi, safi_t *safi); extern int bgp_map_afi_safi_int2iana (afi_t afi, safi_t safi, - afi_t *pkt_afi, safi_t *pkt_safi); + iana_afi_t *pkt_afi, safi_t *pkt_safi); extern struct peer_af * peer_af_create (struct peer *, afi_t, safi_t); extern struct peer_af * peer_af_find (struct peer *, afi_t, safi_t); diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index ab9a24e831..8ff28a39d7 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -30,8 +30,8 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" -#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/rfapi/rfapi.h" @@ -561,8 +561,9 @@ DEFUN (vnc_defaults_responselifetime, return CMD_SUCCESS; } -static struct rfapi_nve_group_cfg * -rfapi_group_lookup_byname (struct bgp *bgp, const char *name) +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_byname (struct bgp *bgp, const char *name, + rfapi_group_cfg_type_t type) /* _MAX = any */ { struct rfapi_nve_group_cfg *rfg; struct listnode *node, *nnode; @@ -570,18 +571,29 @@ rfapi_group_lookup_byname (struct bgp *bgp, const char *name) for (ALL_LIST_ELEMENTS (bgp->rfapi_cfg->nve_groups_sequential, node, nnode, rfg)) { - if (!strcmp (rfg->name, name)) + if ((type == RFAPI_GROUP_CFG_MAX || type == rfg->type) && + !strcmp (rfg->name, name)) return rfg; } return NULL; } static struct rfapi_nve_group_cfg * -rfapi_group_new () +rfapi_group_new (struct bgp *bgp, + rfapi_group_cfg_type_t type, + const char *name) { struct rfapi_nve_group_cfg *rfg; rfg = XCALLOC (MTYPE_RFAPI_GROUP_CFG, sizeof (struct rfapi_nve_group_cfg)); + if (rfg) + { + rfg->type = type; + rfg->name = strdup (name); + /* add to tail of list */ + listnode_add (bgp->rfapi_cfg->nve_groups_sequential, rfg); + } + rfg->label = MPLS_LABEL_ILLEGAL; QOBJ_REG (rfg, rfapi_nve_group_cfg); return rfg; @@ -1033,7 +1045,8 @@ DEFUN (vnc_redistribute_nvegroup, * OK if nve group doesn't exist yet; we'll set the pointer * when the group is defined later */ - bgp->rfapi_cfg->rfg_redist = rfapi_group_lookup_byname (bgp, argv[3]->arg); + bgp->rfapi_cfg->rfg_redist = bgp_rfapi_cfg_match_byname (bgp, argv[3]->arg, + RFAPI_GROUP_CFG_NVE); if (bgp->rfapi_cfg->rfg_redist_name) free (bgp->rfapi_cfg->rfg_redist_name); bgp->rfapi_cfg->rfg_redist_name = strdup (argv[3]->arg); @@ -1622,7 +1635,7 @@ DEFUN (vnc_export_nvegroup, return CMD_WARNING; } - rfg_new = rfapi_group_lookup_byname (bgp, argv[5]->arg); + rfg_new = bgp_rfapi_cfg_match_byname (bgp, argv[5]->arg, RFAPI_GROUP_CFG_NVE); if (argv[2]->arg[0] == 'b') { @@ -2417,20 +2430,17 @@ DEFUN (vnc_nve_group, struct rfapi_rfg_name *rfgn; /* Search for name */ - rfg = rfapi_group_lookup_byname (bgp, argv[2]->arg); + rfg = bgp_rfapi_cfg_match_byname (bgp, argv[2]->arg, RFAPI_GROUP_CFG_NVE); if (!rfg) { - rfg = rfapi_group_new (); + rfg = rfapi_group_new (bgp, RFAPI_GROUP_CFG_NVE, argv[2]->arg); if (!rfg) { /* Error out of memory */ vty_out (vty, "Can't allocate memory for NVE group%s", VTY_NEWLINE); return CMD_WARNING; } - rfg->name = strdup (argv[2]->arg); - /* add to tail of list */ - listnode_add (bgp->rfapi_cfg->nve_groups_sequential, rfg); /* Copy defaults from struct rfapi_cfg */ rfg->rd = bgp->rfapi_cfg->default_rd; @@ -2453,7 +2463,7 @@ DEFUN (vnc_nve_group, rfg->rt_import_list = ecommunity_dup (bgp->rfapi_cfg->default_rt_import_list); rfg->rfapi_import_table = - rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + rfapiImportTableRefAdd (bgp, rfg->rt_import_list, rfg); } /* @@ -2629,7 +2639,8 @@ static int bgp_rfapi_delete_named_nve_group ( struct vty *vty, /* NULL = no output */ struct bgp *bgp, - const char *rfg_name) /* NULL = any */ + const char *rfg_name, /* NULL = any */ + rfapi_group_cfg_type_t type) /* _MAX = any */ { struct rfapi_nve_group_cfg *rfg = NULL; struct listnode *node, *nnode; @@ -2638,7 +2649,7 @@ bgp_rfapi_delete_named_nve_group ( /* Search for name */ if (rfg_name) { - rfg = rfapi_group_lookup_byname (bgp, rfg_name); + rfg = bgp_rfapi_cfg_match_byname (bgp, rfg_name, type); if (!rfg) { if (vty) @@ -2665,7 +2676,8 @@ bgp_rfapi_delete_named_nve_group ( for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, rfgn)) { - if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name)) + if (rfg_name == NULL || + (type == RFAPI_GROUP_CFG_NVE && !strcmp (rfgn->name, rfg_name))) { rfgn->rfg = NULL; /* remove exported routes from this group */ @@ -2680,7 +2692,8 @@ bgp_rfapi_delete_named_nve_group ( for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) { - if (rfg_name == NULL || !strcmp (rfgn->name, rfg_name)) + if (rfg_name == NULL || + (type == RFAPI_GROUP_CFG_NVE && !strcmp (rfgn->name, rfg_name))) { rfgn->rfg = NULL; /* remove exported routes from this group */ @@ -2707,7 +2720,7 @@ DEFUN (vnc_no_nve_group, { VTY_DECLVAR_CONTEXT(bgp, bgp); - return bgp_rfapi_delete_named_nve_group (vty, bgp, argv[3]->arg); + return bgp_rfapi_delete_named_nve_group (vty, bgp, argv[3]->arg, RFAPI_GROUP_CFG_NVE); } DEFUN (vnc_nve_group_prefix, @@ -2890,7 +2903,7 @@ DEFUN (vnc_nve_group_rt_import, */ if (rfg->rfapi_import_table) rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); - rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list, rfg); if (is_export_bgp) vnc_direct_bgp_add_group (bgp, rfg); @@ -2997,7 +3010,7 @@ DEFUN (vnc_nve_group_rt_both, */ if (rfg->rfapi_import_table) rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); - rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list, rfg); if (is_export_bgp) vnc_direct_bgp_add_group (bgp, rfg); @@ -3237,6 +3250,492 @@ static struct cmd_node bgp_vnc_nve_group_node = { 1 }; +/*------------------------------------------------------------------------- + * VNC nve-group + * Note there are two types of NVEs, one for VPNs one for RFP NVEs + *-----------------------------------------------------------------------*/ + +DEFUN (vnc_vrf_policy, + vnc_vrf_policy_cmd, + "vrf-policy NAME", + "Configure a VRF policy group\n" + "VRF name\n") +{ + struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Search for name */ + rfg = bgp_rfapi_cfg_match_byname (bgp, argv[1]->arg, RFAPI_GROUP_CFG_VRF); + + if (!rfg) + { + rfg = rfapi_group_new (bgp, RFAPI_GROUP_CFG_VRF, argv[1]->arg); + if (!rfg) + { + /* Error out of memory */ + vty_out (vty, "Can't allocate memory for NVE group%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + VTY_PUSH_CONTEXT_SUB (BGP_VRF_POLICY_NODE, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_no_vrf_policy, + vnc_no_vrf_policy_cmd, + "no vrf-policy NAME", + NO_STR + "Remove a VRF policy group\n" + "VRF name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_rfapi_delete_named_nve_group (vty, bgp, argv[2]->arg, RFAPI_GROUP_CFG_VRF); +} + +DEFUN (vnc_vrf_policy_label, + vnc_vrf_policy_label_cmd, + "label (0-1048575)", + "Default label value for VRF\n" + "Label Value <0-1048575>\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + uint32_t label; + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER_RANGE ("Label value", label, argv[1]->arg, 0, MPLS_LABEL_MAX); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rfg->label = label; + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_no_label, + vnc_vrf_policy_no_label_cmd, + "no label", + "Remove VRF default label\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current VRF group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rfg->label = MPLS_LABEL_ILLEGAL; + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_nexthop, + vnc_vrf_policy_nexthop_cmd, + "nexthop ", + "Specify next hop to use for VRF advertised prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Use configured router-id (default)") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + struct prefix p; + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current VRF no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + if (!str2prefix (argv[1]->arg, &p) && p.family) + { + //vty_out (vty, "Nexthop set to self%s", VTY_NEWLINE); + SET_FLAG (rfg->flags, RFAPI_RFG_VPN_NH_SELF); + memset(&rfg->vn_prefix, 0, sizeof(struct prefix)); + } + else + { + UNSET_FLAG (rfg->flags, RFAPI_RFG_VPN_NH_SELF); + rfg->vn_prefix = p; + } + + /* TBD handle router-id/ nexthop changes when have advertised prefixes */ + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return CMD_SUCCESS; +} + +/* The RT code should be refactored/simplified with above... */ +DEFUN (vnc_vrf_policy_rt_import, + vnc_vrf_policy_rt_import_cmd, + "rt import RTLIST...", + "Specify route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + int is_export_bgp = 0; + int is_export_zebra = 0; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group (bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + vnc_zebra_del_group (bgp, rfg); + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group (bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group (bgp, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_rt_export, + vnc_vrf_policy_rt_export_cmd, + "rt export RTLIST...", + "Specify route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return rc; +} + +DEFUN (vnc_vrf_policy_rt_both, + vnc_vrf_policy_rt_both_cmd, + "rt both RTLIST...", + "Specify route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + int is_export_bgp = 0; + int is_export_zebra = 0; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group (bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO (bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) + { + + if (rfgn->rfg == rfg) + { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + { + vnc_zlog_debug_verbose ("%s: is_export_zebra", __func__); + vnc_zebra_del_group (bgp, rfg); + } + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt (bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = rfapiImportTableRefAdd (bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group (bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group (bgp, rfg); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + + return rc; + +} + +DEFUN (vnc_vrf_policy_rd, + vnc_vrf_policy_rd_cmd, + "rd ASN:nn_or_IP-address:nn", + "Specify default VRF route distinguisher\n" + "Route Distinguisher (: | : | auto:nh: )\n") +{ + int ret; + struct prefix_rd prd; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* make sure it's still in list */ + if (!listnode_lookup (bgp->rfapi_cfg->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strncmp (argv[1]->arg, "auto:nh:", 8)) + { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul (argv[1]->arg + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!*(argv[1]->arg + 5) || *end) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (value32 > 0xffff) + { + vty_out (vty, "%% Malformed rd (must be less than %u%s", + 0x0ffff, VTY_NEWLINE); + return CMD_WARNING; + } + + memset (&prd, 0, sizeof (prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } + else + { + + ret = str2prefix_rd (argv[1]->arg, &prd); + if (!ret) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_prechange (bgp); + } + + rfg->rd = prd; + + if (bgp->rfapi_cfg->rfg_redist == rfg) + { + vnc_redistribute_postchange (bgp); + } + return CMD_SUCCESS; +} + +DEFUN (exit_vrf_policy, + exit_vrf_policy_cmd, + "exit-vrf-policy", + "Exit VRF policy configuration mode\n") +{ + if (vty->node == BGP_VRF_POLICY_NODE) + { + vty->node = BGP_NODE; + } + return CMD_SUCCESS; +} + +static struct cmd_node bgp_vrf_policy_node = { + BGP_VRF_POLICY_NODE, + "%s(config-router-vrf-policy)# ", + 1 +}; + /*------------------------------------------------------------------------- * vnc-l2-group *-----------------------------------------------------------------------*/ @@ -3247,11 +3746,17 @@ DEFUN (vnc_l2_group, "vnc l2-group NAME", VNC_CONFIG_STR "Configure a L2 group\n" "Group name\n") { - VTY_DECLVAR_CONTEXT(bgp, bgp); struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } /* Search for name */ - rfg = rfapi_l2_group_lookup_byname (bgp, argv[2]->arg); + rfg = rfapi_l2_group_lookup_byname (bgp, argv[1]->arg); if (!rfg) { @@ -3262,7 +3767,7 @@ DEFUN (vnc_l2_group, vty_out (vty, "Can't allocate memory for L2 group%s", VTY_NEWLINE); return CMD_WARNING; } - rfg->name = strdup (argv[2]->arg); + rfg->name = strdup (argv[1]->arg); /* add to tail of list */ listnode_add (bgp->rfapi_cfg->l2_groups, rfg); } @@ -3336,18 +3841,29 @@ DEFUN (vnc_no_l2_group, { VTY_DECLVAR_CONTEXT(bgp, bgp); + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } return bgp_rfapi_delete_named_l2_group (vty, bgp, argv[3]->arg); } DEFUN (vnc_l2_group_lni, vnc_l2_group_lni_cmd, - "logical-network-id (0-4294967295)", + "logical-network-id <0-4294967295>", "Specify Logical Network ID associated with group\n" "value\n") { - VTY_DECLVAR_CONTEXT(bgp, bgp); VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) @@ -3364,14 +3880,20 @@ DEFUN (vnc_l2_group_lni, DEFUN (vnc_l2_group_labels, vnc_l2_group_labels_cmd, - "labels LABELLIST...", + "labels .LABELLIST", "Specify label values associated with group\n" "Space separated list of label values <0-1048575>\n") { - VTY_DECLVAR_CONTEXT(bgp, bgp); VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); struct list *ll; + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { @@ -3386,13 +3908,12 @@ DEFUN (vnc_l2_group_labels, ll = list_new (); rfg->labels = ll; } - - argc -= 1; - argv += 1; + argc--; + argv++; for (; argc; --argc, ++argv) { uint32_t label; - VTY_GET_INTEGER_RANGE ("Label value", label, argv[0]->arg, 0, 1048575); + VTY_GET_INTEGER_RANGE ("Label value", label, argv[0]->arg, 0, MPLS_LABEL_MAX); if (!listnode_lookup (ll, (void *) (uintptr_t) label)) listnode_add (ll, (void *) (uintptr_t) label); } @@ -3402,16 +3923,22 @@ DEFUN (vnc_l2_group_labels, DEFUN (vnc_l2_group_no_labels, vnc_l2_group_no_labels_cmd, - "no labels LABELLIST...", + "no labels .LABELLIST", NO_STR "Remove label values associated with L2 group\n" "Specify label values associated with L2 group\n" "Space separated list of label values <0-1048575>\n") { - VTY_DECLVAR_CONTEXT(bgp, bgp); VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); struct list *ll; + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + /* make sure it's still in list */ if (!listnode_lookup (bgp->rfapi_cfg->l2_groups, rfg)) { @@ -3427,12 +3954,12 @@ DEFUN (vnc_l2_group_no_labels, return CMD_WARNING; } - argc -= 2; - argv += 2; + argc-=2; + argv+=2; for (; argc; --argc, ++argv) { uint32_t label; - VTY_GET_INTEGER_RANGE ("Label value", label, argv[0]->arg, 0, 1048575); + VTY_GET_INTEGER_RANGE ("Label value", label, argv[0]->arg, 0, MPLS_LABEL_MAX); listnode_delete (ll, (void *) (uintptr_t) label); } @@ -3448,8 +3975,8 @@ DEFUN (vnc_l2_group_rt, "Import filters\n" "A route target\n") { - VTY_DECLVAR_CONTEXT(bgp, bgp); VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); int rc = CMD_SUCCESS; int do_import = 0; int do_export = 0; @@ -3467,10 +3994,8 @@ DEFUN (vnc_l2_group_rt, default: vty_out (vty, "Unknown option, %s%s", argv[1]->arg, VTY_NEWLINE); return CMD_ERR_NO_MATCH; - } - if (argc < 3) - return CMD_ERR_INCOMPLETE; + } if (!bgp) { vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); @@ -3486,9 +4011,9 @@ DEFUN (vnc_l2_group_rt, } if (do_import) - rc = set_ecom_list (vty, argc - 2, argv + 2, &rfg->rt_import_list); + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_import_list); if (rc == CMD_SUCCESS && do_export) - rc = set_ecom_list (vty, argc - 2, argv + 2, &rfg->rt_export_list); + rc = set_ecom_list (vty, argc-2, argv+2, &rfg->rt_export_list); return rc; } @@ -3571,7 +4096,9 @@ bgp_rfapi_cfg_init (void) install_node (&bgp_vnc_defaults_node, NULL); install_node (&bgp_vnc_nve_group_node, NULL); + install_node (&bgp_vrf_policy_node, NULL); install_node (&bgp_vnc_l2_group_node, NULL); + install_default (BGP_VRF_POLICY_NODE); install_default (BGP_VNC_DEFAULTS_NODE); install_default (BGP_VNC_NVE_GROUP_NODE); install_default (BGP_VNC_L2_GROUP_NODE); @@ -3582,6 +4109,8 @@ bgp_rfapi_cfg_init (void) install_element (BGP_NODE, &vnc_defaults_cmd); install_element (BGP_NODE, &vnc_nve_group_cmd); install_element (BGP_NODE, &vnc_no_nve_group_cmd); + install_element (BGP_NODE, &vnc_vrf_policy_cmd); + install_element (BGP_NODE, &vnc_no_vrf_policy_cmd); install_element (BGP_NODE, &vnc_l2_group_cmd); install_element (BGP_NODE, &vnc_no_l2_group_cmd); install_element (BGP_NODE, &vnc_advertise_un_method_cmd); @@ -3645,6 +4174,16 @@ bgp_rfapi_cfg_init (void) &vnc_nve_group_export_no_routemap_cmd); install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_label_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_no_label_cmd); + //Hide per Jan 17 discussion + //install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_nexthop_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_import_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_export_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_both_cmd); + install_element (BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rd_cmd); + install_element (BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); + install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_lni_cmd); install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_labels_cmd); install_element (BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_no_labels_cmd); @@ -3713,7 +4252,7 @@ bgp_rfapi_cfg_destroy (struct bgp *bgp, struct rfapi_cfg *h) if (h == NULL) return; - bgp_rfapi_delete_named_nve_group (NULL, bgp, NULL); + bgp_rfapi_delete_named_nve_group (NULL, bgp, NULL, RFAPI_GROUP_CFG_MAX); bgp_rfapi_delete_named_l2_group (NULL, bgp, NULL); if (h->l2_groups != NULL) list_delete (h->l2_groups); @@ -3741,6 +4280,166 @@ bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp) afi_t afi; int type; + vty_out (vty, "!%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg)) + if (rfg->type == RFAPI_GROUP_CFG_VRF) + { + ++write; + vty_out (vty, " vrf-policy %s%s", rfg->name, VTY_NEWLINE); + if (rfg->label <= MPLS_LABEL_MAX) + { + vty_out (vty, " label %u%s", rfg->label, VTY_NEWLINE); + + } + if (CHECK_FLAG (rfg->flags, RFAPI_RFG_VPN_NH_SELF)) + { + vty_out (vty, " nexthop self%s", VTY_NEWLINE); + + } + else + { + if (rfg->vn_prefix.family) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + inet_ntop(rfg->vn_prefix.family, &rfg->vn_prefix.u.prefix, buf, sizeof(buf)); + if (!buf[0] || buf[BUFSIZ - 1]) + { + //vty_out (vty, "nexthop self%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " nexthop %s%s", buf, VTY_NEWLINE); + } + } + } + + if (rfg->rd.prefixlen) + { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + + if (AF_UNIX == rfg->rd.family) + { + + uint16_t value = 0; + + value = ((rfg->rd.val[6] << 8) & 0x0ff00) | + (rfg->rd.val[7] & 0x0ff); + + vty_out (vty, " rd auto:nh:%d%s", value, VTY_NEWLINE); + + } + else + { + + if (!prefix_rd2str (&rfg->rd, buf, BUFSIZ) || + !buf[0] || buf[BUFSIZ - 1]) + { + + vty_out (vty, "!Error: Can't convert rd%s", VTY_NEWLINE); + } + else + { + vty_out (vty, " rd %s%s", buf, VTY_NEWLINE); + } + } + } + + if (rfg->rt_import_list && rfg->rt_export_list && + ecommunity_cmp (rfg->rt_import_list, rfg->rt_export_list)) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt both %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + else + { + if (rfg->rt_import_list) + { + char *b = ecommunity_ecom2str (rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt import %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + if (rfg->rt_export_list) + { + char *b = ecommunity_ecom2str (rfg->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP); + vty_out (vty, " rt export %s%s", b, VTY_NEWLINE); + XFREE (MTYPE_ECOMMUNITY_STR, b); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + + const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (rfg->plist_export_bgp_name[afi]) + { + vty_out (vty, " export bgp %s prefix-list %s%s", + afistr, rfg->plist_export_bgp_name[afi], + VTY_NEWLINE); + } + if (rfg->plist_export_zebra_name[afi]) + { + vty_out (vty, " export zebra %s prefix-list %s%s", + afistr, rfg->plist_export_zebra_name[afi], + VTY_NEWLINE); + } + /* + * currently we only support redist plists for bgp-direct. + * If we later add plist support for redistributing other + * protocols, we'll need to loop over protocols here + */ + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + { + vty_out (vty, " redistribute bgp-direct %s prefix-list %s%s", + afistr, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi], + VTY_NEWLINE); + } + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT][afi]) + { + vty_out (vty, + " redistribute bgp-direct-to-nve-groups %s prefix-list %s%s", + afistr, + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT] + [afi], VTY_NEWLINE); + } + } + + if (rfg->routemap_export_bgp_name) + { + vty_out (vty, " export bgp route-map %s%s", + rfg->routemap_export_bgp_name, VTY_NEWLINE); + } + if (rfg->routemap_export_zebra_name) + { + vty_out (vty, " export zebra route-map %s%s", + rfg->routemap_export_zebra_name, VTY_NEWLINE); + } + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + { + vty_out (vty, " redistribute bgp-direct route-map %s%s", + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT], + VTY_NEWLINE); + } + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT]) + { + vty_out (vty, + " redistribute bgp-direct-to-nve-groups route-map %s%s", + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT_EXT], + VTY_NEWLINE); + } + vty_out (vty, " exit-vrf-policy%s", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) { vty_out (vty, " vnc advertise-un-method encap-safi%s", VTY_NEWLINE); @@ -3902,6 +4601,7 @@ bgp_rfapi_cfg_write (struct vty *vty, struct bgp *bgp) } for (ALL_LIST_ELEMENTS (hc->nve_groups_sequential, node, nnode, rfg)) + if (rfg->type == RFAPI_GROUP_CFG_NVE) { ++write; vty_out (vty, " vnc nve-group %s%s", rfg->name, VTY_NEWLINE); diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h index 897b4be764..8f93d69f6b 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.h +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -41,12 +41,21 @@ struct rfapi_l2_group_cfg }; DECLARE_QOBJ_TYPE(rfapi_l2_group_cfg) +typedef enum +{ + RFAPI_GROUP_CFG_NVE = 1, + RFAPI_GROUP_CFG_VRF, + RFAPI_GROUP_CFG_L2, + RFAPI_GROUP_CFG_MAX +} rfapi_group_cfg_type_t; + struct rfapi_nve_group_cfg { struct route_node *vn_node; /* backref */ struct route_node *un_node; /* backref */ - char *name; + rfapi_group_cfg_type_t type; /* NVE|VPN */ + char *name; /* unique by type! */ struct prefix vn_prefix; struct prefix un_prefix; @@ -54,8 +63,9 @@ struct rfapi_nve_group_cfg uint8_t l2rd; /* 0 = VN addr LSB */ uint32_t response_lifetime; uint32_t flags; -#define RFAPI_RFG_RESPONSE_LIFETIME 0x1 +#define RFAPI_RFG_RESPONSE_LIFETIME 0x01 /* bits */ #define RFAPI_RFG_L2RD 0x02 +#define RFAPI_RFG_VPN_NH_SELF 0x04 struct ecommunity *rt_import_list; struct ecommunity *rt_export_list; struct rfapi_import_table *rfapi_import_table; @@ -99,6 +109,9 @@ struct rfapi_nve_group_cfg char *routemap_redist_name[ZEBRA_ROUTE_MAX]; struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; + /* for VRF type groups */ + uint32_t label; + struct rfapi_descriptor *rfd; QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(rfapi_nve_group_cfg) @@ -288,6 +301,12 @@ bgp_rfapi_cfg_match_group ( struct prefix *vn, struct prefix *un); +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_byname ( + struct bgp *bgp, + const char *name, + rfapi_group_cfg_type_t type); /* _MAX = any */ + extern void vnc_prefix_list_update (struct bgp *bgp); diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 61da18a308..cc6b555c9d 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -36,13 +36,13 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_attr.h" -#include "bgpd/bgp_mplsvpn.h" #include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgpd/rfapi/rfapi.h" #include "bgpd/rfapi/rfapi_backend.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_vnc_types.h" @@ -335,6 +335,9 @@ is_valid_rfd (struct rfapi_descriptor *rfd) if (!rfd || rfd->bgp == NULL) return 0; + if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */ + return 1; + if (rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh)) return 0; @@ -357,6 +360,9 @@ rfapi_check (void *handle) if (!rfd || rfd->bgp == NULL) return EINVAL; + if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */ + return 0; + if ((rc = rfapi_find_handle (rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh))) return rc; @@ -1347,7 +1353,6 @@ rfapi_rfp_set_cb_methods (void *rfp_start_val, /*********************************************************************** * NVE Sessions ***********************************************************************/ - /* * Caller must supply an already-allocated rfd with the "caller" * fields already set (vn_addr, un_addr, callback, cookie) @@ -1474,6 +1479,57 @@ rfapi_open_inner ( return 0; } +/* moved from rfapi_register */ +int +rfapi_init_and_open( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_nve_group_cfg *rfg) +{ + struct rfapi *h = bgp->rfapi; + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + afi_t afi_vn, afi_un; + struct prefix pfx_un; + struct route_node *rn; + + + rfapi_time (&rfd->open_time); + + if (rfg->type == RFAPI_GROUP_CFG_VRF) + SET_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF); + + rfapiRfapiIpAddr2Str (&rfd->vn_addr, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str (&rfd->un_addr, buf_un, BUFSIZ); + + vnc_zlog_debug_verbose ("%s: new RFD with VN=%s UN=%s cookie=%p", + __func__, buf_vn, buf_un, rfd->cookie); + + if (rfg->type != RFAPI_GROUP_CFG_VRF) /* unclear if needed for VRF */ + { + listnode_add (&h->descriptors, rfd); + if (h->descriptors.count > h->stat.max_descriptors) + { + h->stat.max_descriptors = h->descriptors.count; + } + + /* + * attach to UN radix tree + */ + afi_vn = family2afi (rfd->vn_addr.addr_family); + afi_un = family2afi (rfd->un_addr.addr_family); + assert (afi_vn && afi_un); + assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un)); + + rn = route_node_get (&(h->un[afi_un]), &pfx_un); + assert (rn); + rfd->next = rn->info; + rn->info = rfd; + rfd->un_node = rn; + } + return rfapi_open_inner (rfd, bgp, h, rfg); +} + struct rfapi_vn_option * rfapiVnOptionsDup (struct rfapi_vn_option *orig) { @@ -1991,14 +2047,10 @@ rfapi_open ( struct prefix pfx_vn; struct prefix pfx_un; - struct route_node *rn; int rc; rfapi_handle hh = NULL; int reusing_provisional = 0; - afi_t afi_vn; - afi_t afi_un; - { char buf[2][INET_ADDRSTRLEN]; vnc_zlog_debug_verbose ("%s: VN=%s UN=%s", __func__, @@ -2129,40 +2181,7 @@ rfapi_open ( if (!reusing_provisional) { - rfapi_time (&rfd->open_time); - - { - char buf_vn[BUFSIZ]; - char buf_un[BUFSIZ]; - - rfapiRfapiIpAddr2Str (vn, buf_vn, BUFSIZ); - rfapiRfapiIpAddr2Str (un, buf_un, BUFSIZ); - - vnc_zlog_debug_verbose ("%s: new HD with VN=%s UN=%s cookie=%p", - __func__, buf_vn, buf_un, userdata); - } - - listnode_add (&h->descriptors, rfd); - if (h->descriptors.count > h->stat.max_descriptors) - { - h->stat.max_descriptors = h->descriptors.count; - } - - /* - * attach to UN radix tree - */ - afi_vn = family2afi (rfd->vn_addr.addr_family); - afi_un = family2afi (rfd->un_addr.addr_family); - assert (afi_vn && afi_un); - assert (!rfapiRaddr2Qprefix (&rfd->un_addr, &pfx_un)); - - rn = route_node_get (&(h->un[afi_un]), &pfx_un); - assert (rn); - rfd->next = rn->info; - rn->info = rfd; - rfd->un_node = rn; - - rc = rfapi_open_inner (rfd, bgp, h, rfg); + rc = rfapi_init_and_open(bgp, rfd, rfg); /* * This can fail only if the VN address is IPv6 and the group * specified auto-assignment of RDs, which only works for v4, @@ -2777,7 +2796,7 @@ rfapi_register ( NULL, action == RFAPI_REGISTER_KILL); - if (0 == rfapiApDelete (bgp, rfd, &p, pfx_mac, &adv_tunnel)) + if (0 == rfapiApDelete (bgp, rfd, &p, pfx_mac, &prd, &adv_tunnel)) { if (adv_tunnel) rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime); diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c index 4b8eb9511b..5cc1dd7815 100644 --- a/bgpd/rfapi/rfapi_ap.c +++ b/bgpd/rfapi/rfapi_ap.c @@ -35,13 +35,13 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_attr.h" -#include "bgpd/bgp_mplsvpn.h" #include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgpd/rfapi/rfapi.h" #include "bgpd/rfapi/rfapi_backend.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_advertise.h" @@ -103,12 +103,11 @@ sl_adb_lifetime_cmp (void *adb1, void *adb2) return 0; } - void rfapiApInit (struct rfapi_advertised_prefixes *ap) { - ap->ipN_by_prefix = skiplist_new (0, vnc_prefix_cmp, NULL); - ap->ip0_by_ether = skiplist_new (0, vnc_prefix_cmp, NULL); + ap->ipN_by_prefix = skiplist_new (0, rfapi_rib_key_cmp, NULL); + ap->ip0_by_ether = skiplist_new (0, rfapi_rib_key_cmp, NULL); ap->by_lifetime = skiplist_new (0, sl_adb_lifetime_cmp, NULL); } @@ -192,7 +191,7 @@ rfapiApReadvertiseAll (struct bgp *bgp, struct rfapi_descriptor *rfd) * TBD this is not quite right. When pfx_ip is 0/32 or 0/128, * we need to substitute the VN address as the prefix */ - add_vnc_route (rfd, bgp, SAFI_MPLS_VPN, &adb->prefix_ip, &prd, /* RD to use (0 for ENCAP) */ + add_vnc_route (rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip, &prd, /* RD to use (0 for ENCAP) */ &rfd->vn_addr, /* nexthop */ &local_pref, &adb->lifetime, NULL, NULL, /* struct rfapi_un_option */ NULL, /* struct rfapi_vn_option */ @@ -221,11 +220,11 @@ rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd) struct prefix pfx_vn_buf; struct prefix *pfx_ip; - if (!(RFAPI_0_PREFIX (&adb->prefix_ip) && - RFAPI_HOST_PREFIX (&adb->prefix_ip))) + if (!(RFAPI_0_PREFIX (&adb->u.s.prefix_ip) && + RFAPI_HOST_PREFIX (&adb->u.s.prefix_ip))) { - pfx_ip = &adb->prefix_ip; + pfx_ip = &adb->u.s.prefix_ip; } else @@ -247,7 +246,7 @@ rfapiApWithdrawAll (struct bgp *bgp, struct rfapi_descriptor *rfd) } } - del_vnc_route (rfd, rfd->peer, bgp, SAFI_MPLS_VPN, pfx_ip ? pfx_ip : &pfx_vn_buf, &adb->prd, /* RD to use (0 for ENCAP) */ + del_vnc_route (rfd, rfd->peer, bgp, SAFI_MPLS_VPN, pfx_ip ? pfx_ip : &pfx_vn_buf, &adb->u.s.prd, /* RD to use (0 for ENCAP) */ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); } } @@ -404,19 +403,19 @@ rfapiApAdjustLifetimeStats ( { void *cursor; - struct prefix *prefix; - struct rfapi_adb *adb; + struct rfapi_rib_key rk; + struct rfapi_adb *adb; int rc; vnc_zlog_debug_verbose ("%s: walking to find new min/max", __func__); cursor = NULL; for (rc = skiplist_next (rfd->advertised.ipN_by_prefix, - (void **) &prefix, (void **) &adb, + (void **) &rk, (void **) &adb, &cursor); !rc; rc = skiplist_next (rfd->advertised.ipN_by_prefix, - (void **) &prefix, (void **) &adb, &cursor)) + (void **) &rk, (void **) &adb, &cursor)) { uint32_t lt = adb->lifetime; @@ -428,10 +427,10 @@ rfapiApAdjustLifetimeStats ( } cursor = NULL; for (rc = skiplist_next (rfd->advertised.ip0_by_ether, - (void **) &prefix, (void **) &adb, + (void **) &rk, (void **) &adb, &cursor); !rc; rc = - skiplist_next (rfd->advertised.ip0_by_ether, (void **) &prefix, + skiplist_next (rfd->advertised.ip0_by_ether, (void **) &rk, (void **) &adb, &cursor)) { @@ -483,14 +482,15 @@ rfapiApAdd ( struct rfapi_adb *adb; uint32_t old_lifetime = 0; int use_ip0 = 0; + struct rfapi_rib_key rk; + rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip)) { use_ip0 = 1; assert (pfx_eth); - rc = - skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth, + skiplist_search (rfd->advertised.ip0_by_ether, &rk, (void **) &adb); } @@ -499,7 +499,7 @@ rfapiApAdd ( /* find prefix in advertised prefixes list */ rc = - skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + skiplist_search (rfd->advertised.ipN_by_prefix, &rk, (void **) &adb); } @@ -510,19 +510,17 @@ rfapiApAdd ( adb = XCALLOC (MTYPE_RFAPI_ADB, sizeof (struct rfapi_adb)); assert (adb); adb->lifetime = lifetime; - adb->prefix_ip = *pfx_ip; - if (pfx_eth) - adb->prefix_eth = *pfx_eth; + adb->u.key = rk; if (use_ip0) { assert (pfx_eth); - skiplist_insert (rfd->advertised.ip0_by_ether, &adb->prefix_eth, + skiplist_insert (rfd->advertised.ip0_by_ether, &adb->u.key, adb); } else { - skiplist_insert (rfd->advertised.ipN_by_prefix, &adb->prefix_ip, + skiplist_insert (rfd->advertised.ipN_by_prefix, &adb->u.key, adb); } @@ -537,19 +535,12 @@ rfapiApAdd ( adb->lifetime = lifetime; assert (!skiplist_insert (rfd->advertised.by_lifetime, adb, adb)); } - - if (!use_ip0 && pfx_eth && prefix_cmp (&adb->prefix_eth, pfx_eth)) - { - /* mac address changed */ - adb->prefix_eth = *pfx_eth; - } } adb->cost = cost; if (l2o) adb->l2o = *l2o; else memset (&adb->l2o, 0, sizeof (struct rfapi_l2address_option)); - adb->prd = *prd; if (rfapiApAdjustLifetimeStats (rfd, (rc ? NULL : &old_lifetime), &lifetime)) @@ -568,16 +559,19 @@ rfapiApDelete ( struct rfapi_descriptor *rfd, struct prefix *pfx_ip, struct prefix *pfx_eth, + struct prefix_rd *prd, int *advertise_tunnel) /* out */ { int rc; struct rfapi_adb *adb; uint32_t old_lifetime; int use_ip0 = 0; + struct rfapi_rib_key rk; if (advertise_tunnel) *advertise_tunnel = 0; + rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); /* find prefix in advertised prefixes list */ if (RFAPI_0_PREFIX (pfx_ip) && RFAPI_HOST_PREFIX (pfx_ip)) { @@ -585,7 +579,7 @@ rfapiApDelete ( assert (pfx_eth); rc = - skiplist_search (rfd->advertised.ip0_by_ether, pfx_eth, + skiplist_search (rfd->advertised.ip0_by_ether, &rk, (void **) &adb); } @@ -594,7 +588,7 @@ rfapiApDelete ( /* find prefix in advertised prefixes list */ rc = - skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + skiplist_search (rfd->advertised.ipN_by_prefix, &rk, (void **) &adb); } @@ -607,11 +601,11 @@ rfapiApDelete ( if (use_ip0) { - rc = skiplist_delete (rfd->advertised.ip0_by_ether, pfx_eth, NULL); + rc = skiplist_delete (rfd->advertised.ip0_by_ether, &rk, NULL); } else { - rc = skiplist_delete (rfd->advertised.ipN_by_prefix, pfx_ip, NULL); + rc = skiplist_delete (rfd->advertised.ipN_by_prefix, &rk, NULL); } assert (!rc); diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h index f2805f49cb..8a59f05274 100644 --- a/bgpd/rfapi/rfapi_ap.h +++ b/bgpd/rfapi/rfapi_ap.h @@ -93,6 +93,7 @@ rfapiApDelete ( struct rfapi_descriptor *rfd, struct prefix *pfx_ip, struct prefix *pfx_eth, + struct prefix_rd *prd, int *advertise_tunnel); /* out */ diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h index 788ec73751..9e5b0dc5cb 100644 --- a/bgpd/rfapi/rfapi_backend.h +++ b/bgpd/rfapi/rfapi_backend.h @@ -36,15 +36,6 @@ extern void rfapi_delete (struct bgp *); struct rfapi *bgp_rfapi_new (struct bgp *bgp); void bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h); -struct rfapi_import_table *rfapiImportTableRefAdd (struct bgp *bgp, - struct ecommunity - *rt_import_list); - -void -rfapiImportTableRefDelByIt (struct bgp *bgp, - struct rfapi_import_table *it_target); - - extern void rfapiProcessUpdate (struct peer *peer, void *rfd, diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index e6fdb7180e..1cd12ca0d2 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -4644,7 +4644,8 @@ bgp_rfapi_destroy (struct bgp *bgp, struct rfapi *h) } struct rfapi_import_table * -rfapiImportTableRefAdd (struct bgp *bgp, struct ecommunity *rt_import_list) +rfapiImportTableRefAdd (struct bgp *bgp, struct ecommunity *rt_import_list, + struct rfapi_nve_group_cfg *rfg) { struct rfapi *h; struct rfapi_import_table *it; @@ -4670,6 +4671,7 @@ rfapiImportTableRefAdd (struct bgp *bgp, struct ecommunity *rt_import_list) h->imports = it; it->rt_import_list = ecommunity_dup (rt_import_list); + it->rfg = rfg; it->monitor_exterior_orphans = skiplist_new (0, NULL, (void (*)(void *)) prefix_free); @@ -4943,6 +4945,7 @@ rfapiDeleteRemotePrefixesIt ( * un if set, tunnel must match this prefix * vn if set, nexthop prefix must match this prefix * p if set, prefix must match this prefix + * it if set, only look in this import table * * output * pARcount number of active routes deleted @@ -4958,6 +4961,7 @@ rfapiDeleteRemotePrefixes ( struct prefix *un, struct prefix *vn, struct prefix *p, + struct rfapi_import_table *arg_it, int delete_active, int delete_holddown, uint32_t *pARcount, @@ -4995,7 +4999,11 @@ rfapiDeleteRemotePrefixes ( * for the afi/safi combination */ - for (it = h->imports; it; it = it->next) + if (arg_it) + it = arg_it; + else + it = h->imports; + for (; it; ) { vnc_zlog_debug_verbose @@ -5016,6 +5024,11 @@ rfapiDeleteRemotePrefixes ( &deleted_holddown_nve_count, uniq_active_nves, uniq_holddown_nves); + + if (arg_it) + it = NULL; + else + it = it->next; } /* diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h index 3cf55462a1..3ba76539dd 100644 --- a/bgpd/rfapi/rfapi_import.h +++ b/bgpd/rfapi/rfapi_import.h @@ -38,6 +38,7 @@ struct rfapi_import_table { struct rfapi_import_table *next; + struct rfapi_nve_group_cfg *rfg; struct ecommunity *rt_import_list; /* copied from nve grp */ int refcount; /* nve grps and nves */ uint32_t l2_logical_net_id; /* L2 only: EVPN Eth Seg Id */ @@ -90,6 +91,11 @@ rfapiShowImportTable ( struct route_table *rt, int isvpn); +extern struct rfapi_import_table * +rfapiImportTableRefAdd ( + struct bgp *bgp, + struct ecommunity *rt_import_list, + struct rfapi_nve_group_cfg *rfg); extern void rfapiImportTableRefDelByIt ( @@ -223,6 +229,7 @@ extern int rfapiEcommunityGetEthernetTag ( * un if set, tunnel must match this prefix * vn if set, nexthop prefix must match this prefix * p if set, prefix must match this prefix + * it if set, only look in this import table * * output * pARcount number of active routes deleted @@ -238,6 +245,7 @@ rfapiDeleteRemotePrefixes ( struct prefix *un, struct prefix *vn, struct prefix *p, + struct rfapi_import_table *it, int delete_active, int delete_holddown, uint32_t *pARcount, /* active routes */ diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h index 33390c4f55..ed83ef1e1f 100644 --- a/bgpd/rfapi/rfapi_private.h +++ b/bgpd/rfapi/rfapi_private.h @@ -34,21 +34,6 @@ #include "rfapi.h" -/* - * RFAPI Advertisement Data Block - * - * Holds NVE prefix advertisement information - */ -struct rfapi_adb -{ - struct prefix prefix_ip; - struct prefix prefix_eth; /* now redundant with l2o */ - struct prefix_rd prd; - uint32_t lifetime; - uint8_t cost; - struct rfapi_l2address_option l2o; -}; - /* * Lists of rfapi_adb. Each rfapi_adb is referenced twice: * @@ -62,7 +47,6 @@ struct rfapi_advertised_prefixes struct skiplist *by_lifetime; /* all */ }; - struct rfapi_descriptor { struct route_node *un_node; /* backref to un table */ @@ -151,6 +135,7 @@ struct rfapi_descriptor #define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_ETHER 0x00000004 #define RFAPI_HD_FLAG_PROVISIONAL 0x00000008 #define RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY 0x00000010 +#define RFAPI_HD_FLAG_IS_VRF 0x00000012 }; #define RFAPI_QUEUED_FLAG(afi) ( \ @@ -378,9 +363,6 @@ rfp_cost_to_localpref (uint8_t cost); extern int rfapi_set_autord_from_vn (struct prefix_rd *rd, struct rfapi_ip_addr *vn); -extern void -rfapiAdbFree (struct rfapi_adb *adb); - extern struct rfapi_nexthop * rfapi_nexthop_new (struct rfapi_nexthop *copyme); @@ -452,4 +434,17 @@ DECLARE_MTYPE(RFAPI_L2ADDR_OPT) DECLARE_MTYPE(RFAPI_AP) DECLARE_MTYPE(RFAPI_MONITOR_ETH) + +/* + * Caller must supply an already-allocated rfd with the "caller" + * fields already set (vn_addr, un_addr, callback, cookie) + * The advertised_prefixes[] array elements should be NULL to + * have this function set them to newly-allocated radix trees. + */ +extern int +rfapi_init_and_open( + struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_nve_group_cfg *rfg); + #endif /* _QUAGGA_BGP_RFAPI_PRIVATE_H */ diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 6aae35e635..8e5d47415f 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -405,10 +405,26 @@ rfapiRibStartTimer ( assert (ri->timer); } +extern void +rfapi_rib_key_init (struct prefix *prefix, /* may be NULL */ + struct prefix_rd *rd, /* may be NULL */ + struct prefix *aux, /* may be NULL */ + struct rfapi_rib_key *rk) + +{ + memset((void *)rk, 0, sizeof(struct rfapi_rib_key)); + if (prefix) + rk->vn = *prefix; + if (rd) + rk->rd = *rd; + if (aux) + rk->aux_prefix = *aux; +} + /* * Compares two s */ -static int +int rfapi_rib_key_cmp (void *k1, void *k2) { struct rfapi_rib_key *a = (struct rfapi_rib_key *) k1; @@ -498,9 +514,13 @@ rfapi_info_cmp (struct rfapi_info *a, struct rfapi_info *b) void rfapiRibClear (struct rfapi_descriptor *rfd) { - struct bgp *bgp = bgp_get_default (); + struct bgp *bgp; afi_t afi; + if (rfd->bgp) + bgp = rfd->bgp; + else + bgp = bgp_get_default (); #if DEBUG_L2_EXTRA vnc_zlog_debug_verbose ("%s: rfd=%p", __func__, rfd); #endif diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h index 2a111946f7..74331a28d0 100644 --- a/bgpd/rfapi/rfapi_rib.h +++ b/bgpd/rfapi/rfapi_rib.h @@ -45,6 +45,27 @@ struct rfapi_rib_key */ struct prefix aux_prefix; }; +#include "rfapi.h" + +/* + * RFAPI Advertisement Data Block + * + * Holds NVE prefix advertisement information + */ +struct rfapi_adb +{ + union { + struct { + struct prefix prefix_ip; + struct prefix_rd prd; + struct prefix prefix_eth; + } s; /* mainly for legacy use */ + struct rfapi_rib_key key; + } u; + uint32_t lifetime; + uint8_t cost; + struct rfapi_l2address_option l2o; +}; struct rfapi_info { @@ -151,4 +172,16 @@ rfapiRibCheckCounts ( #define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) #endif +extern void +rfapi_rib_key_init (struct prefix *prefix, /* may be NULL */ + struct prefix_rd *rd, /* may be NULL */ + struct prefix *aux, /* may be NULL */ + struct rfapi_rib_key *rk); + +extern int +rfapi_rib_key_cmp (void *k1, void *k2); + +extern void +rfapiAdbFree (struct rfapi_adb *adb); + #endif /* QUAGGA_HGP_RFAPI_RIB_H */ diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 1e7941a41d..1e92cbcbf1 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1447,17 +1447,24 @@ rfapiShowRemoteRegistrationsIt ( if (pLni) { - fp (out, "%s[%s] L2VPN Network 0x%x (%u) RT={%s}%s", - HVTY_NEWLINE, type, *pLni, (*pLni & 0xfff), s, - HVTY_NEWLINE); + fp (out, "%s[%s] L2VPN Network 0x%x (%u) RT={%s}", + HVTY_NEWLINE, type, *pLni, (*pLni & 0xfff), s); } else { - fp (out, "%s[%s] Prefix RT={%s}%s", - HVTY_NEWLINE, type, s, HVTY_NEWLINE); + fp (out, "%s[%s] Prefix RT={%s}", + HVTY_NEWLINE, type, s); } XFREE (MTYPE_ECOMMUNITY_STR, s); + if (it->rfg && it->rfg->name) + { + fp (out, " %s \"%s\"", + (it->rfg->type == RFAPI_GROUP_CFG_VRF ? + "VRF" : "NVE group"), + it->rfg->name); + } + fp (out, "%s", HVTY_NEWLINE); if (show_expiring) { #if RFAPI_REGISTRATIONS_REPORT_AGE @@ -1847,14 +1854,14 @@ rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd) { /* group like family prefixes together in output */ - if (family != adb->prefix_ip.family) + if (family != adb->u.s.prefix_ip.family) continue; - prefix2str (&adb->prefix_ip, buf, BUFSIZ); + prefix2str (&adb->u.s.prefix_ip, buf, BUFSIZ); buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ vty_out (vty, " Adv Pfx: %s%s", buf, HVTY_NEWLINE); - rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip); + rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->u.s.prefix_ip); } } for (rc = @@ -1865,14 +1872,14 @@ rfapiPrintDescriptor (struct vty *vty, struct rfapi_descriptor *rfd) &cursor)) { - prefix2str (&adb->prefix_eth, buf, BUFSIZ); + prefix2str (&adb->u.s.prefix_eth, buf, BUFSIZ); buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ vty_out (vty, " Adv Pfx: %s%s", buf, HVTY_NEWLINE); /* TBD update the following function to print ethernet info */ /* Also need to pass/use rd */ - rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->prefix_ip); + rfapiPrintAdvertisedInfo (vty, rfd, SAFI_MPLS_VPN, &adb->u.s.prefix_ip); } vty_out (vty, "%s", HVTY_NEWLINE); } @@ -3005,9 +3012,12 @@ struct rfapi_local_reg_delete_arg /* * match parameters */ + struct bgp *bgp; struct rfapi_ip_addr un_address; /* AF==0: wildcard */ struct rfapi_ip_addr vn_address; /* AF==0: wildcard */ struct prefix prefix; /* AF==0: wildcard */ + struct prefix_rd rd; /* plen!=64: wildcard */ + struct rfapi_nve_group_cfg *rfg; /* NULL: wildcard */ struct rfapi_l2address_option_match l2o; @@ -3107,22 +3117,26 @@ nve_addr_cmp (void *k1, void *k2) static int parse_deleter_args ( - struct vty *vty, - struct cmd_token *carg_prefix, - struct cmd_token *carg_vn, - struct cmd_token *carg_un, - struct cmd_token *carg_l2addr, - struct cmd_token *carg_vni, - struct rfapi_local_reg_delete_arg *rcdarg) + struct vty *vty, + struct bgp *bgp, + const char *arg_prefix, + const char *arg_vn, + const char *arg_un, + const char *arg_l2addr, + const char *arg_vni, + const char *arg_rd, + struct rfapi_nve_group_cfg *arg_rfg, + struct rfapi_local_reg_delete_arg *rcdarg) { - const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL; - const char *arg_vn = carg_vn ? carg_vn->arg : NULL; - const char *arg_un = carg_un ? carg_un->arg : NULL; - const char *arg_l2addr = carg_l2addr ? carg_l2addr->arg : NULL; - const char *arg_vni = carg_vni ? carg_vni->arg : NULL; int rc = CMD_WARNING; - memset (rcdarg, 0, sizeof (struct rfapi_local_reg_delete_arg)); + memset (rcdarg, 0, sizeof (struct rfapi_local_reg_delete_arg)); + + rcdarg->vty = vty; + if (bgp == NULL) + bgp = bgp_get_default(); + rcdarg->bgp = bgp; + rcdarg->rfg = arg_rfg; /* may be NULL */ if (arg_vn && strcmp (arg_vn, "*")) { @@ -3168,7 +3182,41 @@ parse_deleter_args ( rcdarg->l2o.flags |= RFAPI_L2O_LNI; } } - return 0; + if (arg_rd) + { + if (!str2prefix_rd (arg_rd, &rcdarg->rd)) + { + vty_out (vty, "Malformed RD \"%s\"%s", + arg_rd, VTY_NEWLINE); + return rc; + } + } + + return CMD_SUCCESS; +} + +static int +parse_deleter_tokens ( + struct vty *vty, + struct bgp *bgp, + struct cmd_token *carg_prefix, + struct cmd_token *carg_vn, + struct cmd_token *carg_un, + struct cmd_token *carg_l2addr, + struct cmd_token *carg_vni, + struct cmd_token *carg_rd, + struct rfapi_nve_group_cfg *arg_rfg, + struct rfapi_local_reg_delete_arg *rcdarg) +{ + const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL; + const char *arg_vn = carg_vn ? carg_vn->arg : NULL; + const char *arg_un = carg_un ? carg_un->arg : NULL; + const char *arg_l2addr = carg_l2addr ? carg_l2addr->arg : NULL; + const char *arg_vni = carg_vni ? carg_vni->arg : NULL; + const char *arg_rd = carg_rd ? carg_rd->arg : NULL; + return parse_deleter_args (vty, bgp,arg_prefix, arg_vn, arg_un, + arg_l2addr, arg_vni, arg_rd, + arg_rfg, rcdarg); } static void @@ -3272,51 +3320,37 @@ clear_vnc_responses (struct rfapi_local_reg_delete_arg *cda) * TBD need to count deleted prefixes and nves? * * ENXIO BGP or VNC not configured - */ + */ static int -rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) +rfapiDeleteLocalPrefixesByRFD (struct rfapi_local_reg_delete_arg *cda, + struct rfapi_descriptor *rfd) { - struct rfapi_ip_addr *pUn; /* NULL = wildcard */ - struct rfapi_ip_addr *pVn; /* NULL = wildcard */ - struct prefix *pPrefix; /* NULL = wildcard */ + struct rfapi_ip_addr *pUn; /* NULL = wildcard */ + struct rfapi_ip_addr *pVn; /* NULL = wildcard */ + struct prefix *pPrefix; /* NULL = wildcard */ + struct prefix_rd *pPrd; /* NULL = wildcard */ - struct rfapi *h; - struct listnode *node; - struct rfapi_descriptor *rfd; struct rfapi_ip_prefix rprefix; - struct bgp *bgp_default = bgp_get_default (); struct rfapi_next_hop_entry *head = NULL; struct rfapi_next_hop_entry *tail = NULL; - struct rfapi_cfg *rfapi_cfg; #if DEBUG_L2_EXTRA - vnc_zlog_debug_verbose ("%s: entry", __func__); + vnc_zlog_debug_verbose ("%s: entry", __func__); #endif - if (!bgp_default) - return ENXIO; - - pUn = (cda->un_address.addr_family ? &cda->un_address : NULL); - pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL); - pPrefix = (cda->prefix.family ? &cda->prefix : NULL); - - h = bgp_default->rfapi; - rfapi_cfg = bgp_default->rfapi_cfg; - - if (!h || !rfapi_cfg) - return ENXIO; + pUn = (cda->un_address.addr_family ? &cda->un_address : NULL); + pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL); + pPrefix = (cda->prefix.family ? &cda->prefix : NULL); + pPrd = (cda->rd.prefixlen == 64 ? &cda->rd : NULL); if (pPrefix) { rfapiQprefix2Rprefix (pPrefix, &rprefix); } -#if DEBUG_L2_EXTRA - vnc_zlog_debug_verbose ("%s: starting descriptor loop", __func__); -#endif - - for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + do /* to preserve old code structure */ { + struct rfapi *h=cda->bgp->rfapi;; struct rfapi_adb *adb; int rc; int deleted_from_this_nve; @@ -3376,11 +3410,22 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) if (pPrefix) { - if (!prefix_same (pPrefix, &adb->prefix_ip)) + if (!prefix_same (pPrefix, &adb->u.s.prefix_ip)) { #if DEBUG_L2_EXTRA vnc_zlog_debug_verbose ("%s: adb=%p, prefix doesn't match, skipping", __func__, adb); +#endif + continue; + } + } + if (pPrd) + { + if (memcmp(pPrd->val, adb->u.s.prd.val, 8) != 0) + { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose ("%s: adb=%p, RD doesn't match, skipping", + __func__, adb); #endif continue; } @@ -3389,7 +3434,7 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) { if (memcmp (cda->l2o.o.macaddr.octet, - adb->prefix_eth.u.prefix_eth.octet, ETHER_ADDR_LEN)) + adb->u.s.prefix_eth.u.prefix_eth.octet, ETHER_ADDR_LEN)) { #if DEBUG_L2_EXTRA vnc_zlog_debug_verbose ("%s: adb=%p, macaddr doesn't match, skipping", @@ -3423,48 +3468,43 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) for (ALL_LIST_ELEMENTS_RO (adb_delete_list, node, adb)) { - - struct rfapi_vn_option vn1; - struct rfapi_vn_option vn2; - struct rfapi_vn_option *pVn; int this_advertisement_prefix_count; + struct rfapi_vn_option optary[3]; + struct rfapi_vn_option *opt = NULL; + int cur_opt = 0; this_advertisement_prefix_count = 1; - rfapiQprefix2Rprefix (&adb->prefix_ip, &rp); + rfapiQprefix2Rprefix (&adb->u.s.prefix_ip, &rp); + + memset (optary, 0, sizeof (optary)); /* if mac addr present in advert, make l2o vn option */ - if (adb->prefix_eth.family == AF_ETHERNET) + if (adb->u.s.prefix_eth.family == AF_ETHERNET) { - - memset (&vn1, 0, sizeof (vn1)); - memset (&vn2, 0, sizeof (vn2)); - - vn1.type = RFAPI_VN_OPTION_TYPE_L2ADDR; - vn1.v.l2addr.macaddr = adb->prefix_eth.u.prefix_eth; - - /* - * use saved RD value instead of trying to invert - * complex L2-style RD computation in rfapi_register() - */ - vn2.type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; - vn2.v.internal_rd = adb->prd; - - vn1.next = &vn2; - - pVn = &vn1; + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + opt->v.l2addr.macaddr = adb->u.s.prefix_eth.u.prefix_eth; ++this_advertisement_prefix_count; } - else - { - pVn = NULL; - } + /* + * use saved RD value instead of trying to invert + * complex RD computation in rfapi_register() + */ + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + opt->v.internal_rd = adb->u.s.prd; #if DEBUG_L2_EXTRA vnc_zlog_debug_verbose ("%s: ipN killing reg from adb %p ", __func__, adb); #endif - rc = rfapi_register (rfd, &rp, 0, NULL, pVn, RFAPI_REGISTER_KILL); + rc = rfapi_register (rfd, &rp, 0, NULL, + (cur_opt ? optary : NULL), RFAPI_REGISTER_KILL); if (!rc) { cda->pfx_count += this_advertisement_prefix_count; @@ -3500,7 +3540,7 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) if (CHECK_FLAG (cda->l2o.flags, RFAPI_L2O_MACADDR)) { if (memcmp (cda->l2o.o.macaddr.octet, - adb->prefix_eth.u.prefix_eth.octet, + adb->u.s.prefix_eth.u.prefix_eth.octet, ETHER_ADDR_LEN)) { @@ -3527,7 +3567,7 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) struct rfapi_vn_option vn; - rfapiQprefix2Rprefix (&adb->prefix_ip, &rp); + rfapiQprefix2Rprefix (&adb->u.s.prefix_ip, &rp); memset (&vn, 0, sizeof (vn)); vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR; @@ -3590,11 +3630,44 @@ rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) skiplist_insert (cda->nves, hap, hap); } } - } + } while (0); /* to preserve old code structure */ return 0; } +static int +rfapiDeleteLocalPrefixes (struct rfapi_local_reg_delete_arg *cda) +{ + int rc = 0; + + if (cda->rfg) + { + if (cda->rfg->rfd) /* if not open, nothing to delete */ + rc = rfapiDeleteLocalPrefixesByRFD (cda, cda->rfg->rfd); + } + else + { + struct bgp *bgp = cda->bgp; + struct rfapi *h; + struct rfapi_cfg *rfapi_cfg; + + struct listnode *node; + struct rfapi_descriptor *rfd; + if (!bgp) + return ENXIO; + h = bgp->rfapi; + rfapi_cfg = bgp->rfapi_cfg; + if (!h || !rfapi_cfg) + return ENXIO; + vnc_zlog_debug_verbose ("%s: starting descriptor loop", __func__); + for (ALL_LIST_ELEMENTS_RO (&h->descriptors, node, rfd)) + { + rc = rfapiDeleteLocalPrefixesByRFD (cda, rfd); + } + } + return rc; +} + /* * clear_vnc_prefix * @@ -3610,6 +3683,8 @@ clear_vnc_prefix (struct rfapi_local_reg_delete_arg *cda) struct prefix *pVN = NULL; struct prefix *pPrefix = NULL; + struct rfapi_import_table *it = NULL; + /* * Delete matching remote prefixes in holddown */ @@ -3627,7 +3702,11 @@ clear_vnc_prefix (struct rfapi_local_reg_delete_arg *cda) { pPrefix = &cda->prefix; } - rfapiDeleteRemotePrefixes (pUN, pVN, pPrefix, + if (cda->rfg) + { + it = cda->rfg->rfapi_import_table; + } + rfapiDeleteRemotePrefixes (pUN, pVN, pPrefix, it, 0, 1, &cda->remote_active_pfx_count, &cda->remote_active_nve_count, &cda->remote_holddown_pfx_count, @@ -3712,7 +3791,7 @@ DEFUN (clear_vnc_nve_all, struct rfapi_local_reg_delete_arg cda; int rc; - if ((rc = parse_deleter_args (vty, NULL, NULL, NULL, NULL, NULL, &cda))) + if ((rc = parse_deleter_args (vty, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; @@ -3745,7 +3824,7 @@ DEFUN (clear_vnc_nve_vn_un, int rc; if ((rc = - parse_deleter_args (vty, NULL, argv[4], argv[6], NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, NULL, argv[4], argv[6], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; @@ -3778,7 +3857,7 @@ DEFUN (clear_vnc_nve_un_vn, int rc; if ((rc = - parse_deleter_args (vty, NULL, argv[6], argv[4], NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, NULL, argv[6], argv[4], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; @@ -3806,7 +3885,7 @@ DEFUN (clear_vnc_nve_vn, struct rfapi_local_reg_delete_arg cda; int rc; - if ((rc = parse_deleter_args (vty, NULL, argv[4], NULL, NULL, NULL, &cda))) + if ((rc = parse_deleter_tokens (vty, NULL, NULL, argv[4], NULL, NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; @@ -3833,7 +3912,7 @@ DEFUN (clear_vnc_nve_un, struct rfapi_local_reg_delete_arg cda; int rc; - if ((rc = parse_deleter_args (vty, NULL, NULL, argv[6], NULL, NULL, &cda))) + if ((rc = parse_deleter_tokens (vty, NULL, NULL, NULL, argv[6], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; @@ -3876,7 +3955,7 @@ DEFUN (clear_vnc_prefix_vn_un, int rc; if ((rc = - parse_deleter_args (vty, argv[3], argv[5], argv[7], NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, argv[3], argv[5], argv[7], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -3906,7 +3985,7 @@ DEFUN (clear_vnc_prefix_un_vn, int rc; if ((rc = - parse_deleter_args (vty, argv[3], argv[7], argv[5], NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, argv[3], argv[7], argv[5], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -3932,7 +4011,7 @@ DEFUN (clear_vnc_prefix_un, int rc; if ((rc = - parse_deleter_args (vty, argv[3], NULL, argv[5], NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, argv[3], NULL, argv[5], NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -3958,7 +4037,7 @@ DEFUN (clear_vnc_prefix_vn, int rc; if ((rc = - parse_deleter_args (vty, argv[3], argv[5], NULL, NULL, NULL, &cda))) + parse_deleter_tokens (vty, NULL, argv[3], argv[5], NULL, NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -3980,7 +4059,7 @@ DEFUN (clear_vnc_prefix_all, struct rfapi_local_reg_delete_arg cda; int rc; - if ((rc = parse_deleter_args (vty, argv[3], NULL, NULL, NULL, NULL, &cda))) + if ((rc = parse_deleter_tokens (vty, NULL, argv[3], NULL, NULL, NULL, NULL, NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4022,8 +4101,8 @@ DEFUN (clear_vnc_mac_vn_un, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, NULL, argv[7], argv[9], argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, NULL, argv[7], argv[9], argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4056,8 +4135,8 @@ DEFUN (clear_vnc_mac_un_vn, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, NULL, argv[9], argv[7], argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, NULL, argv[9], argv[7], argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4086,7 +4165,7 @@ DEFUN (clear_vnc_mac_un, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, NULL, NULL, argv[7], argv[3], argv[5], &cda))) + parse_deleter_tokens (vty, NULL, NULL, NULL, argv[7], argv[3], argv[5], NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4115,7 +4194,7 @@ DEFUN (clear_vnc_mac_vn, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, NULL, argv[7], NULL, argv[3], argv[5], &cda))) + parse_deleter_tokens (vty, NULL, NULL, argv[7], NULL, argv[3], argv[5], NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4141,7 +4220,7 @@ DEFUN (clear_vnc_mac_all, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, NULL, NULL, NULL, argv[3], argv[5], &cda))) + parse_deleter_tokens (vty, NULL, NULL, NULL, NULL, argv[3], argv[5], NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4182,8 +4261,8 @@ DEFUN (clear_vnc_mac_vn_un_prefix, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, argv[11], argv[7], argv[9], argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, argv[11], argv[7], argv[9], argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4224,8 +4303,8 @@ DEFUN (clear_vnc_mac_un_vn_prefix, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, argv[11], argv[9], argv[7], argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, argv[11], argv[9], argv[7], argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4258,8 +4337,8 @@ DEFUN (clear_vnc_mac_un_prefix, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, argv[9], NULL, argv[7], argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, argv[9], NULL, argv[7], argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4292,8 +4371,8 @@ DEFUN (clear_vnc_mac_vn_prefix, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, argv[9], argv[7], NULL, argv[3], argv[5], - &cda))) + parse_deleter_tokens (vty, NULL, argv[9], argv[7], NULL, argv[3], argv[5], + NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4322,7 +4401,7 @@ DEFUN (clear_vnc_mac_all_prefix, /* pfx vn un L2 VNI */ if ((rc = - parse_deleter_args (vty, argv[7], NULL, NULL, argv[3], argv[5], &cda))) + parse_deleter_tokens (vty, NULL, argv[7], NULL, NULL, argv[3], argv[5], NULL, NULL, &cda))) return rc; cda.vty = vty; clear_vnc_prefix (&cda); @@ -4930,6 +5009,361 @@ notcfg: return CMD_WARNING; } +/************************************************************************ + * Add prefix with vrf + * + * add [vrf ] prefix + * [rd ] [label ] [local-preference <0-4294967295>] + ************************************************************************/ +static int +vnc_add_vrf_prefix (struct vty *vty, + const char *arg_vrf, + const char *arg_prefix, + const char *arg_rd, /* optional */ + const char *arg_label, /* optional */ + const char *arg_pref) /* optional */ +{ + struct bgp *bgp; + struct rfapi_nve_group_cfg *rfg; + struct prefix pfx; + struct rfapi_ip_prefix rpfx; + uint32_t pref = 0; + struct rfapi_vn_option optary[3]; + struct rfapi_vn_option *opt = NULL; + int cur_opt = 0; + + bgp = bgp_get_default (); /* assume main instance for now */ + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi || !bgp->rfapi_cfg) + { + vty_out (vty, "VRF support not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rfg = bgp_rfapi_cfg_match_byname (bgp, arg_vrf, RFAPI_GROUP_CFG_VRF); + /* arg checks */ + if (!rfg) + { + vty_out (vty, "VRF \"%s\" appears not to be configured.%s", + arg_vrf, VTY_NEWLINE); + return CMD_WARNING; + } + if (!rfg->rt_export_list || !rfg->rfapi_import_table) + { + vty_out (vty, "VRF \"%s\" is missing RT import/export RT configuration.%s", + arg_vrf, VTY_NEWLINE); + return CMD_WARNING; + } + if (!rfg->rd.family && !arg_rd) + { + vty_out (vty, "VRF \"%s\" isn't configured with an RD, so RD must be provided.%s", + arg_vrf, VTY_NEWLINE); + return CMD_WARNING; + } + if (rfg->label > MPLS_LABEL_MAX && !arg_label) + { + vty_out (vty, "VRF \"%s\" isn't configured with a default labels, so a label must be provided.%s", + arg_vrf, VTY_NEWLINE); + return CMD_WARNING; + } + if (!str2prefix (arg_prefix, &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", + arg_prefix, VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &rpfx); + memset (optary, 0, sizeof (optary)); + if (arg_rd) + { + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + if (!str2prefix_rd (arg_rd, &opt->v.internal_rd)) + { + vty_out (vty, "Malformed RD \"%s\"%s", + arg_rd, VTY_NEWLINE); + return CMD_WARNING; + } + } + if (rfg->label <= MPLS_LABEL_MAX || arg_label) + { + struct rfapi_l2address_option *l2o; + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + l2o = &opt->v.l2addr; + if (arg_label) + { + int32_t label; + VTY_GET_INTEGER_RANGE ("Label value", label, arg_label, 0, MPLS_LABEL_MAX); + l2o->label = label; + } + else + l2o->label = rfg->label; + } + if (arg_pref) + { + char *endptr = NULL; + pref = strtoul (arg_pref, &endptr, 10); + if (*endptr != '\0') + { + vty_out (vty, "%% Invalid local-preference value \"%s\"%s", arg_pref, VTY_NEWLINE); + return CMD_WARNING; + } + } + rpfx.cost = 255 - (pref & 255) ; + if (rfg->rfd == NULL) /* need new rfapi_handle */ + { + /* based on rfapi_open */ + struct rfapi_descriptor *rfd; + rfd = XCALLOC (MTYPE_RFAPI_DESC, sizeof (struct rfapi_descriptor)); + rfd->bgp = bgp; + rfg->rfd = rfd; + /* leave most fields empty as will get from (dynamic) config when needed */ + rfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS; + rfd->cookie = rfg; + if (rfg->vn_prefix.family && + !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) + { + rfapiQprefix2Raddr(&rfg->vn_prefix, &rfd->vn_addr); + } + else + { + memset(&rfd->vn_addr, 0, sizeof(struct rfapi_ip_addr)); + rfd->vn_addr.addr_family = AF_INET; + rfd->vn_addr.addr.v4 = bgp->router_id; + } + rfd->un_addr = rfd->vn_addr; /* sigh, need something in UN for lookups */ + vnc_zlog_debug_verbose ("%s: Opening RFD for VRF %s", + __func__, rfg->name); + rfapi_init_and_open(bgp, rfd, rfg); + } + + if (!rfapi_register (rfg->rfd, &rpfx, RFAPI_INFINITE_LIFETIME, NULL, + (cur_opt ? optary : NULL), RFAPI_REGISTER_ADD)) + { + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct rfapi_vn_option *vn_opt_new; + + vnc_zlog_debug_verbose ("%s: rfapi_register succeeded", __func__); + + if (bgp->rfapi->rfp_methods.local_cb) + { + struct rfapi_descriptor *r = (struct rfapi_descriptor *) rfg->rfd; + vn_opt_new = rfapi_vn_options_dup (opt); + + rfapiAddDeleteLocalRfpPrefix (&r->un_addr, &r->vn_addr, &rpfx, + 1, RFAPI_INFINITE_LIFETIME, + vn_opt_new, &head, &tail); + if (head) + { + bgp->rfapi->flags |= RFAPI_INCALLBACK; + (*bgp->rfapi->rfp_methods.local_cb) (head, r->cookie); + bgp->rfapi->flags &= ~RFAPI_INCALLBACK; + } + head = tail = NULL; + } + vnc_zlog_debug_verbose ("%s completed, count=%d/%d", __func__, + rfg->rfapi_import_table->local_count[AFI_IP], + rfg->rfapi_import_table->local_count[AFI_IP6]); + return CMD_SUCCESS; + } + + vnc_zlog_debug_verbose ("%s: rfapi_register failed", __func__); + vty_out (vty, "Add failed.%s", VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN (add_vrf_prefix_rd_label_pref, + add_vrf_prefix_rd_label_pref_cmd, + "add vrf NAME prefix [rd ASN:nn_or_IP-address] [label (0-1048575)] [preference (0-4294967295)]", + "Add\n" + "To a VRF\n" + "VRF name\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Override configured VRF Route Distinguisher\n" + ": or :\n" + "Override configured VRF label" + "Label Value <0-1048575>\n" + "Set advertised local preference\n" + "local preference (higher=more preferred)\n") +{ + char *arg_vrf = argv[2]->arg; + char *arg_prefix = argv[4]->arg; + char *arg_rd = NULL; /* optional */ + char *arg_label = NULL; /* optional */ + char *arg_pref = NULL; /* optional */ + int pargc = 5; + argc--; /* don't parse argument */ + while (pargc < argc) + { + switch (argv[pargc++]->arg[0]) + { + case 'r': + arg_rd = argv[pargc]->arg; + break; + case 'l': + arg_label = argv[pargc]->arg; + break; + case 'p': + arg_pref = argv[pargc]->arg; + break; + default: + break; + } + pargc ++; + } + + return vnc_add_vrf_prefix (vty, arg_vrf, arg_prefix, arg_rd, arg_label, arg_pref); +} + +/************************************************************************ + * del prefix with vrf + * + * clear [vrf ] prefix [rd ] + ************************************************************************/ +static int +rfapi_cfg_group_it_count(struct rfapi_nve_group_cfg *rfg) +{ + int count = 0; + afi_t afi = AFI_MAX; + while (afi-- > 0) + { + count += rfg->rfapi_import_table->local_count[afi]; + } + return count; +} + +static void +clear_vnc_vrf_closer (struct rfapi_nve_group_cfg *rfg) +{ + struct rfapi_descriptor *rfd = rfg->rfd; + afi_t afi; + + if (rfd == NULL) + return; + /* check if IT is empty */ + for (afi = 0; + afi < AFI_MAX && rfg->rfapi_import_table->local_count[afi] == 0; + afi++); + + if (afi == AFI_MAX) + { + vnc_zlog_debug_verbose ("%s: closing RFD for VRF %s", + __func__, rfg->name); + rfg->rfd = NULL; + rfapi_close(rfd); + } + else + { + vnc_zlog_debug_verbose ("%s: VRF %s afi=%d count=%d", + __func__, rfg->name, afi, + rfg->rfapi_import_table->local_count[afi]); + } +} + +static int +vnc_clear_vrf (struct vty *vty, + struct bgp *bgp, + const char *arg_vrf, + const char *arg_prefix, /* NULL = all */ + const char *arg_rd) /* optional */ +{ + struct rfapi_nve_group_cfg *rfg; + struct rfapi_local_reg_delete_arg cda; + int rc; + int start_count; + + if (bgp == NULL) + bgp = bgp_get_default (); /* assume main instance for now */ + if (!bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi || !bgp->rfapi_cfg) + { + vty_out (vty, "VRF support not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + rfg = bgp_rfapi_cfg_match_byname (bgp, arg_vrf, RFAPI_GROUP_CFG_VRF); + /* arg checks */ + if (!rfg) + { + vty_out (vty, "VRF \"%s\" appears not to be configured.%s", + arg_vrf, VTY_NEWLINE); + return CMD_WARNING; + } + rc = parse_deleter_args (vty, bgp, arg_prefix, NULL, NULL, NULL, NULL, + arg_rd, rfg, &cda); + if (rc != CMD_SUCCESS) /* parse error */ + return rc; + + start_count = rfapi_cfg_group_it_count(rfg); + clear_vnc_prefix (&cda); + clear_vnc_vrf_closer (rfg); + vty_out (vty, "Cleared %u out of %d prefixes.%s", + cda.pfx_count, start_count, VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (clear_vrf_prefix_rd, + clear_vrf_prefix_rd_cmd, + "clear vrf NAME [prefix ] [rd ASN:nn_or_IP-address]", + "Clear stored data\n" + "From a VRF\n" + "VRF name\n" + "Prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Specific VRF Route Distinguisher\n" + ": or :\n") +{ + char *arg_vrf = argv[2]->arg; + char *arg_prefix = NULL; /* optional */ + char *arg_rd = NULL; /* optional */ + int pargc = 3; + argc--; /* don't check parameter */ + while (pargc < argc) + { + switch (argv[pargc++]->arg[0]) + { + case 'r': + arg_rd = argv[pargc]->arg; + break; + case 'p': + arg_prefix = argv[pargc]->arg; + break; + default: + break; + } + pargc ++; + } + return vnc_clear_vrf (vty, NULL, arg_vrf, arg_prefix, arg_rd); +} + +DEFUN (clear_vrf_all, + clear_vrf_all_cmd, + "clear vrf NAME all", + "Clear stored data\n" + "From a VRF\n" + "VRF name\n" + "All prefixes\n") +{ + char *arg_vrf = argv[2]->arg; + return vnc_clear_vrf (vty, NULL, arg_vrf, NULL, NULL); +} + void rfapi_vty_init () { install_element (ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd); @@ -4953,6 +5387,8 @@ void rfapi_vty_init () install_element (ENABLE_NODE, &add_vnc_mac_vni_life_cmd); install_element (ENABLE_NODE, &add_vnc_mac_vni_cmd); + install_element (ENABLE_NODE, &add_vrf_prefix_rd_label_pref_cmd); + install_element (ENABLE_NODE, &clear_vnc_nve_all_cmd); install_element (ENABLE_NODE, &clear_vnc_nve_vn_un_cmd); install_element (ENABLE_NODE, &clear_vnc_nve_un_vn_cmd); @@ -4977,6 +5413,9 @@ void rfapi_vty_init () install_element (ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd); install_element (ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd); + install_element (ENABLE_NODE, &clear_vrf_prefix_rd_cmd); + install_element (ENABLE_NODE, &clear_vrf_all_cmd); + install_element (ENABLE_NODE, &vnc_clear_counters_cmd); install_element (VIEW_NODE, &vnc_show_summary_cmd); diff --git a/configure.ac b/configure.ac index 6ce5d0cc2c..550ac4f926 100755 --- a/configure.ac +++ b/configure.ac @@ -1459,6 +1459,7 @@ case "x${quagga_ac_bison_version}" in x2.7*) BISON_OPENBRACE='"' BISON_CLOSEBRACE='"' + BISON_VERBOSE='' AC_MSG_RESULT([$quagga_ac_bison_version - 2.7 or older]) ;; x2.*|x1.*) @@ -1474,11 +1475,13 @@ case "x${quagga_ac_bison_version}" in *) BISON_OPENBRACE='{' BISON_CLOSEBRACE='}' + BISON_VERBOSE='-Dparse.error=verbose' AC_MSG_RESULT([$quagga_ac_bison_version - 3.0 or newer]) ;; esac AC_SUBST(BISON_OPENBRACE) AC_SUBST(BISON_CLOSEBRACE) +AC_SUBST(BISON_VERBOSE) if $quagga_ac_bison_missing; then YACC="$SHELL $missing_dir/missing bison -y" diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 086f5b23da..c1fb062e55 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1149,7 +1149,7 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) unsigned long long start_time, end_time; /* Get time that can't roll backwards. */ - quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now); + monotime(&time_now); start_time = time_now.tv_sec; start_time = (start_time * 1000000) + time_now.tv_usec; @@ -1243,7 +1243,7 @@ out: spftree->pending = 0; spftree->runcount++; spftree->last_run_timestamp = time (NULL); - quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now); + monotime(&time_now); end_time = time_now.tv_sec; end_time = (end_time * 1000000) + time_now.tv_usec; spftree->last_run_duration = end_time - start_time; diff --git a/lib/Makefile.am b/lib/Makefile.am index f2c076c475..ac1935d731 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_CFLAGS = $(WERROR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ +AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ @BISON_VERBOSE@ command_lex.h: command_lex.c @if test ! -f $@; then rm -f command_lex.c; else :; fi @@ -26,6 +26,7 @@ libzebra_la_SOURCES = \ imsg-buffer.c imsg.c skiplist.c \ qobj.c wheel.c \ event_counter.c \ + grammar_sandbox.c \ strlcpy.c \ strlcat.c @@ -46,14 +47,15 @@ pkginclude_HEADERS = \ ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \ fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \ skiplist.h qobj.h wheel.h \ - event_counter.h + event_counter.h \ + monotime.h noinst_HEADERS = \ plist_int.h noinst_PROGRAMS = grammar_sandbox -grammar_sandbox_SOURCES = grammar_sandbox.c +grammar_sandbox_SOURCES = grammar_sandbox_main.c grammar_sandbox_LDADD = libzebra.la EXTRA_DIST = \ diff --git a/lib/bfd.c b/lib/bfd.c index 5db08fea60..a5edaea217 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -362,7 +362,7 @@ bfd_last_update (time_t last_update, char *buf, size_t len) } /* Get current time. */ - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv); + monotime(&tv); curr = tv.tv_sec; diff = curr - last_update; tm = gmtime (&diff); diff --git a/lib/command.c b/lib/command.c index b5dae5f28e..9485beddd9 100644 --- a/lib/command.c +++ b/lib/command.c @@ -43,7 +43,10 @@ DEFINE_MTYPE( LIB, HOST, "Host config") DEFINE_MTYPE( LIB, STRVEC, "String vector") -DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") +DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens") +DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text") +DEFINE_MTYPE_STATIC(LIB, CMD_TEXT, "Command Token Help") +DEFINE_MTYPE( LIB, CMD_ARG, "Command Argument") /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ @@ -523,7 +526,7 @@ compare_completions (const void *fst, const void *snd) * @param completions linked list of cmd_token * @return deduplicated and sorted vector with */ -static vector +vector completions_to_vec (struct list *completions) { vector comps = vector_init (VECTOR_MIN_SIZE); @@ -745,6 +748,7 @@ node_parent ( enum node_type node ) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1110,6 +1114,7 @@ cmd_exit (struct vty *vty) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1173,6 +1178,7 @@ DEFUN (config_end, case BGP_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1272,7 +1278,7 @@ permute (struct graph_node *start, struct vty *vty) for (ALL_LIST_ELEMENTS_RO (position,ln,gnn)) { struct cmd_token *tt = gnn->data; - if (tt->type < SELECTOR_TKN) + if (tt->type < SPECIAL_TKN) vty_out (vty, " %s", tt->text); } if (gn == start) @@ -2402,16 +2408,21 @@ cmd_init (int terminal) vrf_install_commands (); } srandom(time(NULL)); + +#ifdef DEV_BUILD + grammar_sandbox_init(); +#endif } struct cmd_token * -new_cmd_token (enum cmd_token_type type, u_char attr, char *text, char *desc) +new_cmd_token (enum cmd_token_type type, u_char attr, + const char *text, const char *desc) { struct cmd_token *token = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); token->type = type; token->attr = attr; - token->text = text; - token->desc = desc; + token->text = text ? XSTRDUP (MTYPE_CMD_TEXT, text) : NULL; + token->desc = desc ? XSTRDUP (MTYPE_CMD_DESC, desc) : NULL; token->arg = NULL; token->allowrepeat = false; @@ -2424,11 +2435,11 @@ del_cmd_token (struct cmd_token *token) if (!token) return; if (token->text) - XFREE (MTYPE_CMD_TOKENS, token->text); + XFREE (MTYPE_CMD_TEXT, token->text); if (token->desc) - XFREE (MTYPE_CMD_TOKENS, token->desc); + XFREE (MTYPE_CMD_DESC, token->desc); if (token->arg) - XFREE (MTYPE_CMD_TOKENS, token->arg); + XFREE (MTYPE_CMD_ARG, token->arg); XFREE (MTYPE_CMD_TOKENS, token); } @@ -2439,9 +2450,9 @@ copy_cmd_token (struct cmd_token *token) struct cmd_token *copy = new_cmd_token (token->type, token->attr, NULL, NULL); copy->max = token->max; copy->min = token->min; - copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; - copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL; - copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL; + copy->text = token->text ? XSTRDUP (MTYPE_CMD_TEXT, token->text) : NULL; + copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_DESC, token->desc) : NULL; + copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_ARG, token->arg) : NULL; return copy; } diff --git a/lib/command.h b/lib/command.h index 1e1698fc7d..3c3c3ae370 100644 --- a/lib/command.h +++ b/lib/command.h @@ -31,6 +31,7 @@ #include "hash.h" DECLARE_MTYPE(HOST) +DECLARE_MTYPE(CMD_ARG) /* for test-commands.c */ DECLARE_MTYPE(STRVEC) @@ -99,6 +100,7 @@ enum node_type BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ + BGP_VRF_POLICY_NODE, /* BGP VRF policy */ BGP_VNC_DEFAULTS_NODE, /* BGP VNC nve defaults */ BGP_VNC_NVE_GROUP_NODE, /* BGP VNC nve group */ BGP_VNC_L2_GROUP_NODE, /* BGP VNC L2 group */ @@ -176,11 +178,12 @@ enum cmd_token_type IPV6_PREFIX_TKN, // IPV6 network prefixes /* plumbing types */ - SELECTOR_TKN, // marks beginning of selector - OPTION_TKN, // marks beginning of option - NUL_TKN, // dummy token + FORK_TKN, // marks subgraph beginning + JOIN_TKN, // marks subgraph end START_TKN, // first token in line END_TKN, // last token in line + + SPECIAL_TKN = FORK_TKN, }; /* Command attributes */ @@ -202,6 +205,8 @@ struct cmd_token char *desc; // token description long long min, max; // for ranges char *arg; // user input that matches this token + + struct graph_node *forkjoin; // paired FORK/JOIN for JOIN/FORK }; /* Structure of command element. */ @@ -419,14 +424,16 @@ extern void cmd_terminate (void); extern void cmd_exit (struct vty *vty); extern int cmd_list_cmds (struct vty *vty, int do_permute); -/* memory management for cmd_token */ -struct cmd_token * -new_cmd_token (enum cmd_token_type, u_char attr, char *, char *); -void -del_cmd_token (struct cmd_token *); -struct cmd_token * -copy_cmd_token (struct cmd_token *); +/* NOT safe for general use; call this only if DEV_BUILD! */ +extern void grammar_sandbox_init (void); +/* memory management for cmd_token */ +extern struct cmd_token *new_cmd_token (enum cmd_token_type, u_char attr, + const char *text, const char *desc); +extern void del_cmd_token (struct cmd_token *); +extern struct cmd_token *copy_cmd_token (struct cmd_token *); + +extern vector completions_to_vec (struct list *completions); extern void command_parse_format (struct graph *graph, struct cmd_element *cmd); /* Export typical functions. */ diff --git a/lib/command_lex.l b/lib/command_lex.l index d767926263..2a241abbec 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -24,6 +24,12 @@ %{ #include "command_parse.h" + +#define YY_USER_ACTION yylloc->last_column += yyleng; +#define LOC_STEP do { if (yylloc) { \ + yylloc->first_column = yylloc->last_column; \ + yylloc->first_line = yylloc->last_line; \ + } } while(0) %} WORD (\-|\+)?[a-z0-9\*][-+_a-zA-Z0-9\*]* @@ -45,9 +51,14 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) %option prefix="cmd_yy" %option reentrant %option bison-bridge +%option bison-locations %% -[ /t] /* ignore whitespace */; +%{ + LOC_STEP; +%} + +[ \t]+ LOC_STEP /* ignore whitespace */; {WORD} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return WORD;} {IPV4} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4;} {IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4_PREFIX;} diff --git a/lib/command_match.c b/lib/command_match.c index d228563240..bbd9cd091d 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -27,7 +27,6 @@ #include "command_match.h" #include "memory.h" -DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens") DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack") #define MAXDEPTH 64 @@ -322,7 +321,7 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n, // copy token, set arg and prepend to currbest struct cmd_token *token = start->data; struct cmd_token *copy = copy_cmd_token (token); - copy->arg = XSTRDUP (MTYPE_CMD_TOKENS, input_token); + copy->arg = XSTRDUP (MTYPE_CMD_ARG, input_token); listnode_add_before (currbest, currbest->head, copy); matcher_rv = MATCHER_OK; } @@ -459,7 +458,7 @@ command_complete (struct graph *graph, /** * Adds all children that are reachable by one parser hop to the given list. - * NUL_TKN, SELECTOR_TKN, and OPTION_TKN nodes are treated as transparent. + * special tokens except END_TKN are treated as transparent. * * @param[in] list to add the nexthops to * @param[in] node to start calculating nexthops from @@ -490,26 +489,24 @@ add_nexthops (struct list *list, struct graph_node *node, if (j != stackpos) continue; } - switch (token->type) + if (token->type >= SPECIAL_TKN && token->type != END_TKN) { - case OPTION_TKN: - case SELECTOR_TKN: - case NUL_TKN: - added += add_nexthops (list, child, stack, stackpos); - break; - default: - if (stack) - { - nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK, - (stackpos + 1) * sizeof(struct graph_node *)); - nextstack[0] = child; - memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *)); + added += add_nexthops (list, child, stack, stackpos); + } + else + { + if (stack) + { + nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK, + (stackpos + 1) * sizeof(struct graph_node *)); + nextstack[0] = child; + memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *)); - listnode_add (list, nextstack); - } - else - listnode_add (list, child); - added++; + listnode_add (list, nextstack); + } + else + listnode_add (list, child); + added++; } } diff --git a/lib/command_parse.y b/lib/command_parse.y index c920e11380..e9d36ca41c 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -27,6 +27,8 @@ #define YYDEBUG 1 %} +%locations +/* define parse.error verbose */ %define api.pure full /* define api.prefix {cmd_yy} */ @@ -49,14 +51,20 @@ #include "graph.h" #define YYSTYPE CMD_YYSTYPE + #define YYLTYPE CMD_YYLTYPE struct parser_ctx; + + /* subgraph semantic value */ + struct subgraph { + struct graph_node *start, *end; + }; } %union { long long number; char *string; struct graph_node *node; - struct subgraph *subgraph; + struct subgraph subgraph; } %code provides { @@ -94,28 +102,19 @@ %type literal_token %type placeholder_token %type simple_token -%type option -%type option_token -%type option_token_seq %type selector %type selector_token %type selector_token_seq %type selector_seq_seq -%type compound_token %code { /* bison declarations */ void - cmd_yyerror (struct parser_ctx *ctx, char const *msg); - - /* subgraph semantic value */ - struct subgraph { - struct graph_node *start, *end; - }; + cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg); /* helper functions for parser */ - static char * + static const char * doc_next (struct parser_ctx *ctx); static struct graph_node * @@ -130,11 +129,11 @@ static struct graph_node * new_token_node (struct parser_ctx *, enum cmd_token_type type, - char *text, - char *doc); + const char *text, + const char *doc); static void - terminate_graph (struct parser_ctx *ctx, + terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, struct graph_node *); static void @@ -173,7 +172,7 @@ start: cmd_token_seq { // tack on the command element - terminate_graph (ctx, ctx->currnode); + terminate_graph (&@1, ctx, ctx->currnode); } | cmd_token_seq placeholder_token '.' '.' '.' { @@ -187,7 +186,7 @@ start: add_edge_dedup (ctx->currnode, ctx->currnode); // tack on the command element - terminate_graph (ctx, ctx->currnode); + terminate_graph (&@1, ctx, ctx->currnode); } ; @@ -202,11 +201,10 @@ cmd_token: if ((ctx->currnode = add_edge_dedup (ctx->currnode, $1)) != $1) graph_delete_node (ctx->graph, $1); } -| compound_token +| selector { - graph_add_edge (ctx->currnode, $1->start); - ctx->currnode = $1->end; - free ($1); + graph_add_edge (ctx->currnode, $1.start); + ctx->currnode = $1.end; } ; @@ -215,14 +213,9 @@ simple_token: | placeholder_token ; -compound_token: - selector -| option -; - literal_token: WORD { - $$ = new_token_node (ctx, WORD_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx)); free ($1); } ; @@ -230,32 +223,32 @@ literal_token: WORD placeholder_token: IPV4 { - $$ = new_token_node (ctx, IPV4_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx)); free ($1); } | IPV4_PREFIX { - $$ = new_token_node (ctx, IPV4_PREFIX_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx)); free ($1); } | IPV6 { - $$ = new_token_node (ctx, IPV6_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx)); free ($1); } | IPV6_PREFIX { - $$ = new_token_node (ctx, IPV6_PREFIX_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx)); free ($1); } | VARIABLE { - $$ = new_token_node (ctx, VARIABLE_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx)); free ($1); } | RANGE { - $$ = new_token_node (ctx, RANGE_TKN, strdup($1), doc_next(ctx)); + $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx)); struct cmd_token *token = $$->data; // get the numbers out @@ -265,7 +258,7 @@ placeholder_token: token->max = strtoll (yylval.string, &yylval.string, 10); // validate range - if (token->min > token->max) cmd_yyerror (ctx, "Invalid range."); + if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range."); free ($1); } @@ -273,143 +266,68 @@ placeholder_token: /* productions */ selector: '<' selector_seq_seq '>' { - $$ = malloc (sizeof (struct subgraph)); - $$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL); - $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL); - for (unsigned int i = 0; i < vector_active ($2->start->to); i++) - { - struct graph_node *sn = vector_slot ($2->start->to, i), - *en = vector_slot ($2->end->from, i); - graph_add_edge ($$->start, sn); - graph_add_edge (en, $$->end); - } - graph_delete_node (ctx->graph, $2->start); - graph_delete_node (ctx->graph, $2->end); - free ($2); + $$ = $2; }; selector_seq_seq: selector_seq_seq '|' selector_token_seq { - $$ = malloc (sizeof (struct subgraph)); - $$->start = graph_new_node (ctx->graph, NULL, NULL); - $$->end = graph_new_node (ctx->graph, NULL, NULL); - - // link in last sequence - graph_add_edge ($$->start, $3->start); - graph_add_edge ($3->end, $$->end); - - for (unsigned int i = 0; i < vector_active ($1->start->to); i++) - { - struct graph_node *sn = vector_slot ($1->start->to, i), - *en = vector_slot ($1->end->from, i); - graph_add_edge ($$->start, sn); - graph_add_edge (en, $$->end); - } - graph_delete_node (ctx->graph, $1->start); - graph_delete_node (ctx->graph, $1->end); - free ($1); - free ($3); + $$ = $1; + graph_add_edge ($$.start, $3.start); + graph_add_edge ($3.end, $$.end); } -| selector_token_seq '|' selector_token_seq +| selector_token_seq { - $$ = malloc (sizeof (struct subgraph)); - $$->start = graph_new_node (ctx->graph, NULL, NULL); - $$->end = graph_new_node (ctx->graph, NULL, NULL); - graph_add_edge ($$->start, $1->start); - graph_add_edge ($1->end, $$->end); - graph_add_edge ($$->start, $3->start); - graph_add_edge ($3->end, $$->end); - free ($1); - free ($3); + $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL); + $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL); + ((struct cmd_token *)$$.start->data)->forkjoin = $$.end; + ((struct cmd_token *)$$.end->data)->forkjoin = $$.start; + + graph_add_edge ($$.start, $1.start); + graph_add_edge ($1.end, $$.end); } ; /* {keyword} productions */ selector: '{' selector_seq_seq '}' { - $$ = malloc (sizeof (struct subgraph)); - $$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL); - $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL); - graph_add_edge ($$->start, $$->end); - for (unsigned int i = 0; i < vector_active ($2->start->to); i++) - { - struct graph_node *sn = vector_slot ($2->start->to, i), - *en = vector_slot ($2->end->from, i); - graph_add_edge ($$->start, sn); - graph_add_edge (en, $$->start); - } - graph_delete_node (ctx->graph, $2->start); - graph_delete_node (ctx->graph, $2->end); - free ($2); + $$ = $2; + graph_add_edge ($$.end, $$.start); + /* there is intentionally no start->end link, for two reasons: + * 1) this allows "at least 1 of" semantics, which are otherwise impossible + * 2) this would add a start->end->start loop in the graph that the current + * loop-avoidal fails to handle + * just use [{a|b}] if neccessary, that will work perfectly fine, and reason + * #1 is good enough to keep it this way. */ }; -selector_token_seq: - simple_token -{ - $$ = malloc (sizeof (struct subgraph)); - $$->start = $$->end = $1; -} -| selector_token_seq selector_token -{ - $$ = malloc (sizeof (struct subgraph)); - graph_add_edge ($1->end, $2->start); - $$->start = $1->start; - $$->end = $2->end; - free ($1); - free ($2); -} -; - selector_token: simple_token { - $$ = malloc (sizeof (struct subgraph)); - $$->start = $$->end = $1; + $$.start = $$.end = $1; } -| option | selector ; +selector_token_seq: + selector_token_seq selector_token +{ + graph_add_edge ($1.end, $2.start); + $$.start = $1.start; + $$.end = $2.end; +} +| selector_token +; + /* [option] productions */ -option: '[' option_token_seq ']' +selector: '[' selector_seq_seq ']' { - // make a new option - $$ = malloc (sizeof (struct subgraph)); - $$->start = new_token_node (ctx, OPTION_TKN, NULL, NULL); - $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL); - // add a path through the sequence to the end - graph_add_edge ($$->start, $2->start); - graph_add_edge ($2->end, $$->end); - // add a path directly from the start to the end - graph_add_edge ($$->start, $$->end); - free ($2); + $$ = $2; + graph_add_edge ($$.start, $$.end); } ; -option_token_seq: - option_token -| option_token_seq option_token -{ - $$ = malloc (sizeof (struct subgraph)); - graph_add_edge ($1->end, $2->start); - $$->start = $1->start; - $$->end = $2->end; - free ($1); - free ($2); -} -; - -option_token: - simple_token -{ - $$ = malloc (sizeof (struct subgraph)); - $$->start = $$->end = $1; -} -| compound_token -; - %% #undef scanner @@ -437,11 +355,39 @@ command_parse_format (struct graph *graph, struct cmd_element *cmd) /* parser helper functions */ void -yyerror (struct parser_ctx *ctx, char const *msg) +yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg) { + char *tmpstr = strdup(ctx->el->string); + char *line, *eol; + char spacing[256]; + int lineno = 0; + zlog_err ("%s: FATAL parse error: %s", __func__, msg); - zlog_err ("while parsing this command definition: \n\t%s\n", ctx->el->string); - //exit(EXIT_FAILURE); + zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column); + + line = tmpstr; + do { + lineno++; + eol = strchr(line, '\n'); + if (eol) + *eol++ = '\0'; + + zlog_err ("%s: | %s", __func__, line); + if (lineno == loc->first_line && lineno == loc->last_line + && loc->first_column < (int)sizeof(spacing) - 1 + && loc->last_column < (int)sizeof(spacing) - 1) { + + int len = loc->last_column - loc->first_column; + if (len == 0) + len = 1; + + memset(spacing, ' ', loc->first_column - 1); + memset(spacing + loc->first_column - 1, '^', len); + spacing[loc->first_column - 1 + len] = '\0'; + zlog_err ("%s: | %s", __func__, spacing); + } + } while ((line = eol)); + free(tmpstr); } static void @@ -456,27 +402,25 @@ cleanup (struct parser_ctx *ctx) } static void -terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode) +terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, + struct graph_node *finalnode) { // end of graph should look like this // * -> finalnode -> END_TKN -> cmd_element struct cmd_element *element = ctx->el; struct graph_node *end_token_node = - new_token_node (ctx, - END_TKN, - strdup (CMD_CR_TEXT), - strdup ("")); + new_token_node (ctx, END_TKN, CMD_CR_TEXT, ""); struct graph_node *end_element_node = graph_new_node (ctx->graph, element, NULL); if (node_adjacent (finalnode, end_token_node)) - cmd_yyerror (ctx, "Duplicate command."); + cmd_yyerror (locp, ctx, "Duplicate command."); graph_add_edge (finalnode, end_token_node); graph_add_edge (end_token_node, end_element_node); } -static char * +static const char * doc_next (struct parser_ctx *ctx) { const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : ""; @@ -486,12 +430,12 @@ doc_next (struct parser_ctx *ctx) piece = ""; } - return strdup (piece); + return piece; } static struct graph_node * new_token_node (struct parser_ctx *ctx, enum cmd_token_type type, - char *text, char *doc) + const char *text, const char *doc) { struct cmd_token *token = new_cmd_token (type, ctx->el->attr, text, doc); return graph_new_node (ctx->graph, token, (void (*)(void *)) &del_cmd_token); @@ -575,8 +519,7 @@ cmp_token (struct cmd_token *first, struct cmd_token *second) * cases; ultimately this forks the graph, but the matcher can handle * this regardless */ - case SELECTOR_TKN: - case OPTION_TKN: + case FORK_TKN: return 0; /* end nodes are always considered equal, since each node may only @@ -584,7 +527,7 @@ cmp_token (struct cmd_token *first, struct cmd_token *second) */ case START_TKN: case END_TKN: - case NUL_TKN: + case JOIN_TKN: default: break; } diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 0239ca44ac..315bd4d59c 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -3,11 +3,6 @@ * * This unit defines a number of commands in the old engine that can * be used to test and interact with the new engine. - * - * This shim should be removed upon integration. It is currently hooked in - * vtysh/vtysh.c. It has no header, vtysh.c merely includes this entire unit - * since it clutters up the makefiles less and this is only a temporary shim. - * * -- * Copyright (C) 2016 Cumulus Networks, Inc. * @@ -45,15 +40,15 @@ void grammar_sandbox_init (void); void pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t); +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos); void init_cmdgraph (struct vty *, struct graph **); -vector -completions_to_vec (struct list *); -int -compare_completions (const void *, const void *); /** shim interface commands **/ -struct graph *nodegraph; +struct graph *nodegraph = NULL, *nodegraph_free = NULL; DEFUN (grammar_test, grammar_test_cmd, @@ -240,17 +235,77 @@ DEFUN (grammar_test_show, return CMD_SUCCESS; } +DEFUN (grammar_test_dot, + grammar_test_dot_cmd, + "grammar dotfile OUTNAME", + GRAMMAR_STR + "print current graph for dot\n" + ".dot filename\n") +{ + struct graph_node *stack[MAXDEPTH]; + struct graph_node *visited[MAXDEPTH*MAXDEPTH]; + size_t vpos = 0; + + if (!nodegraph) { + vty_out(vty, "nodegraph uninitialized\r\n"); + return CMD_SUCCESS; + } + FILE *ofd = fopen(argv[2]->arg, "w"); + if (!ofd) { + vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno)); + return CMD_SUCCESS; + } + + fprintf(ofd, "digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n"); + pretty_print_dot (ofd, 0, + vector_slot (nodegraph->nodes, 0), + stack, 0, visited, &vpos); + fprintf(ofd, "}\n"); + fclose(ofd); + return CMD_SUCCESS; +} + DEFUN (grammar_init_graph, grammar_init_graph_cmd, "grammar init", GRAMMAR_STR "(re)initialize graph\n") { - graph_delete_graph (nodegraph); + if (nodegraph_free) + graph_delete_graph (nodegraph_free); + nodegraph_free = NULL; + init_cmdgraph (vty, &nodegraph); return CMD_SUCCESS; } +extern vector cmdvec; + +DEFUN (grammar_access, + grammar_access_cmd, + "grammar access (0-65535)", + GRAMMAR_STR + "access node graph\n" + "node number\n") +{ + if (nodegraph_free) + graph_delete_graph (nodegraph_free); + nodegraph_free = NULL; + + struct cmd_node *cnode; + + cnode = vector_slot (cmdvec, atoi (argv[2]->arg)); + if (!cnode) + { + vty_out (vty, "%% no such node%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out (vty, "node %d%s", (int)cnode->node, VTY_NEWLINE); + nodegraph = cnode->cmdgraph; + return CMD_SUCCESS; +} + /* this is called in vtysh.c to set up the testing shim */ void grammar_sandbox_init(void) { init_cmdgraph (NULL, &nodegraph); @@ -258,10 +313,12 @@ void grammar_sandbox_init(void) { // install all enable elements install_element (ENABLE_NODE, &grammar_test_cmd); install_element (ENABLE_NODE, &grammar_test_show_cmd); + install_element (ENABLE_NODE, &grammar_test_dot_cmd); install_element (ENABLE_NODE, &grammar_test_match_cmd); install_element (ENABLE_NODE, &grammar_test_complete_cmd); install_element (ENABLE_NODE, &grammar_test_doc_cmd); install_element (ENABLE_NODE, &grammar_init_graph_cmd); + install_element (ENABLE_NODE, &grammar_access_cmd); } #define item(x) { x, #x } @@ -275,9 +332,8 @@ struct message tokennames[] = { item(IPV6_PREFIX_TKN), // IPV6 network prefixes /* plumbing types */ - item(SELECTOR_TKN), // marks beginning of selector - item(OPTION_TKN), // marks beginning of option - item(NUL_TKN), // dummy token + item(FORK_TKN), + item(JOIN_TKN), item(START_TKN), // first token in line item(END_TKN), // last token in line { 0, NULL } @@ -292,7 +348,7 @@ size_t tokennames_max = array_size(tokennames); */ void pretty_print_graph (struct vty *vty, struct graph_node *start, int level, - int desc, struct graph_node **stack, size_t stackpos) + int desc, struct graph_node **stack, size_t stackpos) { // print this node char tokennum[32]; @@ -346,92 +402,92 @@ pretty_print_graph (struct vty *vty, struct graph_node *start, int level, vty_out(vty, "%s", VTY_NEWLINE); } +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos) +{ + // print this node + char tokennum[32]; + struct cmd_token *tok = start->data; + const char *color; + + for (size_t i = 0; i < (*visitpos); i++) + if (visited[i] == start) + return; + visited[(*visitpos)++] = start; + if ((*visitpos) == MAXDEPTH*MAXDEPTH) + return; + + snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); + fprintf(ofd, " n%016llx [ shape=box, label=<", (unsigned long long)start); + + fprintf(ofd, "%s", LOOKUP_DEF(tokennames, tok->type, tokennum)); + if (tok->attr == CMD_ATTR_DEPRECATED) + fprintf(ofd, " (d)"); + else if (tok->attr == CMD_ATTR_HIDDEN) + fprintf(ofd, " (h)"); + if (tok->text) { + if (tok->type == WORD_TKN) + fprintf(ofd, "
\"%s\"", tok->text); + else + fprintf(ofd, "
%s", tok->text); + } +/* if (desc) + fprintf(ofd, " ?'%s'", tok->desc); */ + switch (tok->type) { + case START_TKN: color = "#ccffcc"; break; + case FORK_TKN: color = "#aaddff"; break; + case JOIN_TKN: color = "#ddaaff"; break; + case WORD_TKN: color = "#ffffff"; break; + default: color = "#ffffff"; break; + } + fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color); + + if (stackpos == MAXDEPTH) + return; + stack[stackpos++] = start; + + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *adj = vector_slot (start->to, i); + // if this node is a vararg, just print * + if (adj == start) { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)start); + } else if (((struct cmd_token *)adj->data)->type == END_TKN) { + //struct cmd_token *et = adj->data; + fprintf(ofd, " n%016llx -> end%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + fprintf(ofd, " end%016llx [ shape=box, label=, style = filled, fillcolor = \"#ffddaa\" ];\n", + (unsigned long long)adj); + } else { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + size_t k; + for (k = 0; k < stackpos; k++) + if (stack[k] == adj) + break; + if (k == stackpos) { + pretty_print_dot (ofd, opts, adj, stack, stackpos, visited, visitpos); + } + } + } +} + + /** stuff that should go in command.c + command.h */ void init_cmdgraph (struct vty *vty, struct graph **graph) { // initialize graph, add start noe *graph = graph_new (); + nodegraph_free = *graph; struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL); graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token); if (vty) vty_out (vty, "initialized graph%s", VTY_NEWLINE); } - -int -compare_completions (const void *fst, const void *snd) -{ - struct cmd_token *first = *(struct cmd_token **) fst, - *secnd = *(struct cmd_token **) snd; - return strcmp (first->text, secnd->text); -} - -vector -completions_to_vec (struct list *completions) -{ - vector comps = vector_init (VECTOR_MIN_SIZE); - - struct listnode *ln; - struct cmd_token *token; - unsigned int i, exists; - for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) - { - // linear search for token in completions vector - exists = 0; - for (i = 0; i < vector_active (comps) && !exists; i++) - { - struct cmd_token *curr = vector_slot (comps, i); - exists = !strcmp (curr->text, token->text) && - !strcmp (curr->desc, token->desc); - } - - if (!exists) - vector_set (comps, copy_cmd_token (token)); - } - - // sort completions - qsort (comps->index, - vector_active (comps), - sizeof (void *), - &compare_completions); - - return comps; -} - -static void vty_do_exit(void) -{ - printf ("\nend.\n"); - exit (0); -} - -struct thread_master *master; - -int main(int argc, char **argv) -{ - struct thread thread; - - master = thread_master_create (); - - zlog_default = openzlog ("grammar_sandbox", ZLOG_NONE, 0, - LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); - zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG); - zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); - - /* Library inits. */ - cmd_init (1); - host.name = strdup ("test"); - - vty_init (master); - memory_init (); - grammar_sandbox_init(); - - vty_stdio (vty_do_exit); - - /* Fetch next active thread. */ - while (thread_fetch (master, &thread)) - thread_call (&thread); - - /* Not reached. */ - exit (0); -} diff --git a/lib/grammar_sandbox.h b/lib/grammar_sandbox.h deleted file mode 100644 index 5da0b05d09..0000000000 --- a/lib/grammar_sandbox.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _GRAMMAR_SANDBOX_H -#define _GRAMMAR_SANDBOX_H - -/** - * Houses functionality for testing shim as well as code that should go into - * command.h and command.c during integration. - */ -#include "memory.h" - -#define CMD_CR_TEXT "" - -void -grammar_sandbox_init (void); - -/** - * Types for tokens. - * - * The type determines what kind of data the token can match (in the - * matching use case) or hold (in the argv use case). - */ -enum cmd_token_type_t -{ - WORD_TKN, // words - NUMBER_TKN, // integral numbers - VARIABLE_TKN, // almost anything - RANGE_TKN, // integer range - IPV4_TKN, // IPV4 addresses - IPV4_PREFIX_TKN, // IPV4 network prefixes - IPV6_TKN, // IPV6 prefixes - IPV6_PREFIX_TKN, // IPV6 network prefixes - - /* plumbing types */ - SELECTOR_TKN, // marks beginning of selector - OPTION_TKN, // marks beginning of option - NUL_TKN, // dummy token - START_TKN, // first token in line - END_TKN, // last token in line -}; - -/** - * Token struct. - */ -struct cmd_token_t -{ - enum cmd_token_type_t type; // token type - - char *text; // token text - char *desc; // token description - - long long value; // for numeric types - long long min, max; // for ranges - - char *arg; // user input that matches this token -}; - -#endif /* _GRAMMAR_SANDBOX_H */ diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c new file mode 100644 index 0000000000..5deef406c1 --- /dev/null +++ b/lib/grammar_sandbox_main.c @@ -0,0 +1,64 @@ +/* + * Testing shim and API examples for the new CLI backend. + * + * Minimal main() to run grammar_sandbox standalone. + * [split off grammar_sandbox.c 2017-01-23] + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * Copyright (C) 2017 David Lamparter for NetDEF, Inc. + * + * 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 "command.h" +#include "memory_vty.h" + +static void vty_do_exit(void) +{ + printf ("\nend.\n"); + exit (0); +} + +struct thread_master *master; + +int main(int argc, char **argv) +{ + struct thread thread; + + master = thread_master_create (); + + zlog_default = openzlog ("grammar_sandbox", ZLOG_NONE, 0, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG); + zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + + /* Library inits. */ + cmd_init (1); + host.name = strdup ("test"); + + vty_init (master); + memory_init (); + + vty_stdio (vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + exit (0); +} diff --git a/lib/graph.c b/lib/graph.c index 891ecc33c0..0992059ef1 100644 --- a/lib/graph.c +++ b/lib/graph.c @@ -55,11 +55,16 @@ graph_new_node (struct graph *graph, void *data, void (*del) (void*)) static void vector_remove (vector v, unsigned int ix) { - vector_unset (v, ix); - if (ix == vector_active (v)) return; - for (; ix < vector_active (v) - 1; ix++) - v->index[ix] = v->index[ix+1]; + if (ix >= v->active) + return; + + /* v->active is guaranteed >= 1 because ix can't be lower than 0 + * and v->active is > ix. */ v->active--; + /* if ix == v->active--, we set the item to itself, then to NULL... + * still correct, no check neccessary. */ + v->index[ix] = v->index[v->active]; + v->index[v->active] = NULL; } void @@ -71,22 +76,18 @@ graph_delete_node (struct graph *graph, struct graph_node *node) struct graph_node *adj; // remove all edges from other nodes to us - vector edges = vector_copy (node->from); - for (unsigned int i = 0; i < vector_active (edges); i++) + for (unsigned int i = vector_active (node->from); i--; /**/) { - adj = vector_slot (edges, i); + adj = vector_slot (node->from, i); graph_remove_edge (adj, node); } - vector_free (edges); // remove all edges from us to other nodes - edges = vector_copy (node->to); - for (unsigned int i = 0; i < vector_active (edges); i++) + for (unsigned int i = vector_active (node->to); i--; /**/) { - adj = vector_slot (edges, i); + adj = vector_slot (node->to, i); graph_remove_edge (node, adj); } - vector_free (edges); // if there is a deletion callback, call it if (node->del && node->data) @@ -97,9 +98,12 @@ graph_delete_node (struct graph *graph, struct graph_node *node) vector_free (node->from); // remove node from graph->nodes - for (unsigned int i = 0; i < vector_active (graph->nodes); i++) + for (unsigned int i = vector_active (graph->nodes); i--; /**/) if (vector_slot (graph->nodes, i) == node) - vector_remove (graph->nodes, i); + { + vector_remove (graph->nodes, i); + break; + } // free the node itself XFREE (MTYPE_GRAPH_NODE, node); @@ -117,20 +121,26 @@ void graph_remove_edge (struct graph_node *from, struct graph_node *to) { // remove from from to->from - for (unsigned int i = 0; i < vector_active (to->from); i++) + for (unsigned int i = vector_active (to->from); i--; /**/) if (vector_slot (to->from, i) == from) - vector_remove (to->from, i); + { + vector_remove (to->from, i); + break; + } // remove to from from->to - for (unsigned int i = 0; i < vector_active (from->to); i++) + for (unsigned int i = vector_active (from->to); i--; /**/) if (vector_slot (from->to, i) == to) - vector_remove (from->to, i); + { + vector_remove (from->to, i); + break; + } } void graph_delete_graph (struct graph *graph) { // delete each node in the graph - for (unsigned int i = 0; i < vector_active (graph->nodes); i++) + for (unsigned int i = vector_active (graph->nodes); i--; /**/) graph_delete_node (graph, vector_slot (graph->nodes, i)); vector_free (graph->nodes); diff --git a/lib/log.c b/lib/log.c index d48534dc18..b8e505f347 100644 --- a/lib/log.c +++ b/lib/log.c @@ -118,7 +118,6 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) } cache; struct timeval clock; - /* would it be sufficient to use global 'recent_time' here? I fear not... */ gettimeofday(&clock, NULL); /* first, we update the cache if the time has changed */ @@ -989,6 +988,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_DELETE), DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_ADD), DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_DELETE), + DESC_ENTRY (ZEBRA_IPMR_ROUTE_STATS), }; #undef DESC_ENTRY diff --git a/lib/monotime.h b/lib/monotime.h new file mode 100644 index 0000000000..0fd4940431 --- /dev/null +++ b/lib/monotime.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_MONOTIME_H +#define _FRR_MONOTIME_H + +#include +#include +#include + +#ifndef TIMESPEC_TO_TIMEVAL +/* should be in sys/time.h on BSD & Linux libcs */ +#define TIMESPEC_TO_TIMEVAL(tv, ts) do { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ + } while (0) +#endif +#ifndef TIMEVAL_TO_TIMESPEC +/* should be in sys/time.h on BSD & Linux libcs */ +#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ + } while (0) +#endif + +static inline time_t monotime(struct timeval *tvo) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + if (tvo) { + TIMESPEC_TO_TIMEVAL(tvo, &ts); + } + return ts.tv_sec; +} + +/* the following two return microseconds, not time_t! + * + * also, they're negative forms of each other, but having both makes the + * code more readable + */ +static inline int64_t monotime_since(const struct timeval *ref, + struct timeval *out) +{ + struct timeval tv; + monotime(&tv); + timersub(&tv, ref, &tv); + if (out) + *out = tv; + return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; +} + +static inline int64_t monotime_until(const struct timeval *ref, + struct timeval *out) +{ + struct timeval tv; + monotime(&tv); + timersub(ref, &tv, &tv); + if (out) + *out = tv; + return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; +} + +#endif /* _FRR_MONOTIME_H */ diff --git a/lib/network.c b/lib/network.c index 506e019136..2b6f2fbab5 100644 --- a/lib/network.c +++ b/lib/network.c @@ -62,8 +62,13 @@ writen(int fd, const u_char *ptr, int nbytes) while (nleft > 0) { nwritten = write(fd, ptr, nleft); - - if (nwritten <= 0) + + if (nwritten < 0) + { + if (!ERRNO_IO_RETRY(errno)) + return nwritten; + } + if (nwritten == 0) return (nwritten); nleft -= nwritten; diff --git a/lib/routemap.c b/lib/routemap.c index 74bae1fd76..70f3069a36 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -710,6 +710,7 @@ enum route_map_dep_type ROUTE_MAP_DEP_RMAP = 1, ROUTE_MAP_DEP_CLIST, ROUTE_MAP_DEP_ECLIST, + ROUTE_MAP_DEP_LCLIST, ROUTE_MAP_DEP_PLIST, ROUTE_MAP_DEP_ASPATH, ROUTE_MAP_DEP_FILTER, @@ -1819,6 +1820,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name, case RMAP_EVENT_CLIST_ADDED: case RMAP_EVENT_ECLIST_ADDED: case RMAP_EVENT_ASLIST_ADDED: + case RMAP_EVENT_LLIST_ADDED: case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_FILTER_ADDED: if (rmap_debug) @@ -1840,6 +1842,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name, case RMAP_EVENT_CLIST_DELETED: case RMAP_EVENT_ECLIST_DELETED: case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: case RMAP_EVENT_CALL_DELETED: case RMAP_EVENT_FILTER_DELETED: if (rmap_debug) @@ -1902,6 +1905,10 @@ route_map_get_dep_hash (route_map_event_t event) case RMAP_EVENT_ASLIST_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; break; + case RMAP_EVENT_LLIST_ADDED: + case RMAP_EVENT_LLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST]; + break; case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_CALL_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; diff --git a/lib/routemap.h b/lib/routemap.h index b52f7289b0..b378c64eae 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -84,6 +84,8 @@ typedef enum RMAP_EVENT_CLIST_DELETED, RMAP_EVENT_ECLIST_ADDED, RMAP_EVENT_ECLIST_DELETED, + RMAP_EVENT_LLIST_ADDED, + RMAP_EVENT_LLIST_DELETED, RMAP_EVENT_ASLIST_ADDED, RMAP_EVENT_ASLIST_DELETED, RMAP_EVENT_FILTER_ADDED, diff --git a/lib/thread.c b/lib/thread.c index 64eaae45b9..de7066bb82 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -41,151 +41,16 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") #include #endif -/* Recent absolute time of day */ -struct timeval recent_time; /* Relative time, since startup */ -static struct timeval relative_time; - static struct hash *cpu_record = NULL; -/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO). - And change negative values to 0. */ -static struct timeval -timeval_adjust (struct timeval a) -{ - while (a.tv_usec >= TIMER_SECOND_MICRO) - { - a.tv_usec -= TIMER_SECOND_MICRO; - a.tv_sec++; - } - - while (a.tv_usec < 0) - { - a.tv_usec += TIMER_SECOND_MICRO; - a.tv_sec--; - } - - if (a.tv_sec < 0) - /* Change negative timeouts to 0. */ - a.tv_sec = a.tv_usec = 0; - - return a; -} - -static struct timeval -timeval_subtract (struct timeval a, struct timeval b) -{ - struct timeval ret; - - ret.tv_usec = a.tv_usec - b.tv_usec; - ret.tv_sec = a.tv_sec - b.tv_sec; - - return timeval_adjust (ret); -} - -static long -timeval_cmp (struct timeval a, struct timeval b) -{ - return (a.tv_sec == b.tv_sec - ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); -} - -unsigned long +static unsigned long timeval_elapsed (struct timeval a, struct timeval b) { return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + (a.tv_usec - b.tv_usec)); } -/* gettimeofday wrapper, to keep recent_time updated */ -static int -quagga_gettimeofday (struct timeval *tv) -{ - int ret; - - assert (tv); - - if (!(ret = gettimeofday (&recent_time, NULL))) - { - /* avoid copy if user passed recent_time pointer.. */ - if (tv != &recent_time) - *tv = recent_time; - return 0; - } - return ret; -} - -static int -quagga_get_relative (struct timeval *tv) -{ - int ret; - -#ifdef HAVE_CLOCK_MONOTONIC - { - struct timespec tp; - if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp))) - { - relative_time.tv_sec = tp.tv_sec; - relative_time.tv_usec = tp.tv_nsec / 1000; - } - } -#elif defined(__APPLE__) - { - uint64_t ticks; - uint64_t useconds; - static mach_timebase_info_data_t timebase_info; - - ticks = mach_absolute_time(); - if (timebase_info.denom == 0) - mach_timebase_info(&timebase_info); - - useconds = ticks * timebase_info.numer / timebase_info.denom / 1000; - relative_time.tv_sec = useconds / 1000000; - relative_time.tv_usec = useconds % 1000000; - - return 0; - } -#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */ -#error no monotonic clock on this system -#endif /* HAVE_CLOCK_MONOTONIC */ - - if (tv) - *tv = relative_time; - - return ret; -} - -/* Exported Quagga timestamp function. - * Modelled on POSIX clock_gettime. - */ -int -quagga_gettime (enum quagga_clkid clkid, struct timeval *tv) -{ - switch (clkid) - { - case QUAGGA_CLK_MONOTONIC: - return quagga_get_relative (tv); - default: - errno = EINVAL; - return -1; - } -} - -time_t -quagga_monotime (void) -{ - struct timeval tv; - quagga_get_relative(&tv); - return tv.tv_sec; -} - -/* Public export of recent_relative_time by value */ -struct timeval -recent_relative_time (void) -{ - return relative_time; -} - static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -437,11 +302,9 @@ thread_timer_cmp(void *a, void *b) struct thread *thread_a = a; struct thread *thread_b = b; - long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands); - - if (cmp < 0) + if (timercmp (&thread_a->u.sands, &thread_b->u.sands, <)) return -1; - if (cmp > 0) + if (timercmp (&thread_a->u.sands, &thread_b->u.sands, >)) return 1; return 0; } @@ -671,12 +534,8 @@ thread_master_free (struct thread_master *m) unsigned long thread_timer_remain_second (struct thread *thread) { - quagga_get_relative (NULL); - - if (thread->u.sands.tv_sec - relative_time.tv_sec > 0) - return thread->u.sands.tv_sec - relative_time.tv_sec; - else - return 0; + int64_t remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + return remain < 0 ? 0 : remain; } #define debugargdef const char *funcname, const char *schedfrom, int fromln @@ -685,9 +544,9 @@ thread_timer_remain_second (struct thread *thread) struct timeval thread_timer_remain(struct thread *thread) { - quagga_get_relative(NULL); - - return timeval_subtract(thread->u.sands, relative_time); + struct timeval remain; + monotime_until(&thread->u.sands, &remain); + return remain; } /* Get new thread. */ @@ -887,7 +746,6 @@ funcname_thread_add_timer_timeval (struct thread_master *m, { struct thread *thread; struct pqueue *queue; - struct timeval alarm_time; assert (m != NULL); @@ -897,11 +755,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m, queue = ((type == THREAD_TIMER) ? m->timer : m->background); thread = thread_get (m, type, func, arg, debugargpass); - /* Do we need jitter here? */ - quagga_get_relative (NULL); - alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec; - alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; - thread->u.sands = timeval_adjust(alarm_time); + monotime(&thread->u.sands); + timeradd(&thread->u.sands, time_relative, &thread->u.sands); pqueue_enqueue(thread, queue); return thread; @@ -1137,7 +992,7 @@ thread_timer_wait (struct pqueue *queue, struct timeval *timer_val) if (queue->size) { struct thread *next_timer = queue->array[0]; - *timer_val = timeval_subtract (next_timer->u.sands, relative_time); + monotime_until(&next_timer->u.sands, timer_val); return timer_val; } return NULL; @@ -1265,7 +1120,7 @@ thread_timer_process (struct pqueue *queue, struct timeval *timenow) while (queue->size) { thread = queue->array[0]; - if (timeval_cmp (*timenow, thread->u.sands) < 0) + if (timercmp (timenow, &thread->u.sands, <)) return ready; pqueue_dequeue(queue); thread->type = THREAD_READY; @@ -1303,6 +1158,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) thread_fd_set readfd; thread_fd_set writefd; thread_fd_set exceptfd; + struct timeval now; struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 }; struct timeval timer_val_bg; struct timeval *timer_wait = &timer_val; @@ -1339,15 +1195,20 @@ thread_fetch (struct thread_master *m, struct thread *fetch) /* Calculate select wait timer if nothing else to do */ if (m->ready.count == 0) { - quagga_get_relative (NULL); timer_wait = thread_timer_wait (m->timer, &timer_val); timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg); if (timer_wait_bg && - (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) + (!timer_wait || (timercmp (timer_wait, timer_wait_bg, >)))) timer_wait = timer_wait_bg; } + if (timer_wait && timer_wait->tv_sec < 0) + { + timerclear(&timer_val); + timer_wait = &timer_val; + } + num = fd_select (m, FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); /* Signals should get quick treatment */ @@ -1362,8 +1223,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch) /* Check foreground timers. Historically, they have had higher priority than I/O threads, so let's push them onto the ready list in front of the I/O threads. */ - quagga_get_relative (NULL); - thread_timer_process (m->timer, &relative_time); + monotime(&now); + thread_timer_process (m->timer, &now); /* Got IO, process it */ if (num > 0) @@ -1379,7 +1240,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) #endif /* Background timer/events, lowest priority */ - thread_timer_process (m->background, &relative_time); + thread_timer_process (m->background, &now); if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); @@ -1408,9 +1269,7 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) int thread_should_yield (struct thread *thread) { - quagga_get_relative (NULL); - return (timeval_elapsed(relative_time, thread->real) > - thread->yield); + return monotime_since(&thread->real, NULL) > (int64_t)thread->yield; } void @@ -1422,17 +1281,8 @@ thread_set_yield_time (struct thread *thread, unsigned long yield_time) void thread_getrusage (RUSAGE_T *r) { - quagga_get_relative (NULL); + monotime(&r->real); getrusage(RUSAGE_SELF, &(r->cpu)); - r->real = relative_time; - -#ifdef HAVE_CLOCK_MONOTONIC - /* quagga_get_relative() only updates recent_time if gettimeofday - * based, not when using CLOCK_MONOTONIC. As we export recent_time - * and guarantee to update it before threads are run... - */ - quagga_gettimeofday(&recent_time); -#endif /* HAVE_CLOCK_MONOTONIC */ } struct thread *thread_current = NULL; diff --git a/lib/thread.h b/lib/thread.h index c22bb4828d..a0801e1ecd 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -23,6 +23,7 @@ #define _ZEBRA_THREAD_H #include +#include "monotime.h" struct rusage_t { @@ -239,7 +240,6 @@ extern void thread_call (struct thread *); extern unsigned long thread_timer_remain_second (struct thread *); extern struct timeval thread_timer_remain(struct thread*); extern int thread_should_yield (struct thread *); -extern unsigned long timeval_elapsed (struct timeval a, struct timeval b); /* set yield time for thread */ extern void thread_set_yield_time (struct thread *, unsigned long); @@ -247,13 +247,6 @@ extern void thread_set_yield_time (struct thread *, unsigned long); extern void thread_getrusage (RUSAGE_T *); extern void thread_cmd_init (void); -/* replacements for the system gettimeofday(), clock_gettime() and - * time() functions, providing support for non-decrementing clock on - * all systems, and fully monotonic on /some/ systems. - */ -extern int quagga_gettime (enum quagga_clkid, struct timeval *); -extern time_t quagga_monotime (void); - /* Returns elapsed real (wall clock) time. */ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, unsigned long *cpu_time_elapsed); @@ -262,8 +255,6 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, be used instead of calling gettimeofday if a recent value is sufficient. This is guaranteed to be refreshed before a thread is called. */ extern struct timeval recent_time; -/* Similar to recent_time, but a monotonically increasing time value */ -extern struct timeval recent_relative_time (void); /* only for use in logging functions! */ extern struct thread *thread_current; diff --git a/lib/vrf.h b/lib/vrf.h index 96c716a7b8..f8bb07ef48 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -34,6 +34,7 @@ /* The default VRF ID */ #define VRF_DEFAULT 0 #define VRF_UNKNOWN UINT16_MAX +#define VRF_ALL UINT16_MAX - 1 /* Pending: May need to refine this. */ #ifndef IFLA_VRF_MAX diff --git a/lib/vty.c b/lib/vty.c index a435706d1f..9413d003ef 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -742,6 +742,7 @@ vty_end_config (struct vty *vty) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: diff --git a/lib/zclient.c b/lib/zclient.c index a4c5fa4afb..42fa41c9c8 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1626,6 +1626,7 @@ zclient_read (struct thread *thread) case ZEBRA_REDISTRIBUTE_IPV6_DEL: if (zclient->redistribute_route_ipv6_del) (*zclient->redistribute_route_ipv6_del) (command, zclient, length, vrf_id); + break; case ZEBRA_INTERFACE_LINK_PARAMS: if (zclient->interface_link_params) (*zclient->interface_link_params) (command, zclient, length); diff --git a/lib/zebra.h b/lib/zebra.h index c9be1892e4..bb43d062bc 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -405,6 +405,7 @@ typedef enum { ZEBRA_IPV4_NEXTHOP_DELETE, ZEBRA_IPV6_NEXTHOP_ADD, ZEBRA_IPV6_NEXTHOP_DELETE, + ZEBRA_IPMR_ROUTE_STATS, } zebra_message_types_t; /* Marker value used in new Zserv, in the byte location corresponding @@ -477,6 +478,12 @@ typedef enum { #define SAFI_RESERVED_5 5 #define SAFI_MAX 6 +#define IANA_SAFI_RESERVED 0 +#define IANA_SAFI_UNICAST 1 +#define IANA_SAFI_MULTICAST 2 +#define IANA_SAFI_ENCAP 7 +#define IANA_SAFI_MPLS_VPN 128 + /* * The above AFI and SAFI definitions are for internal use. The protocol * definitions (IANA values) as for example used in BGP protocol packets @@ -486,12 +493,14 @@ typedef enum { * not optimal for use in data-structure sizing. * Note: Only useful (i.e., supported) values are defined below. */ -#define IANA_AFI_RESERVED 0 -#define IANA_AFI_IPV4 1 -#define IANA_AFI_IPV6 2 -#define IANA_AFI_L2VPN 25 -#define IANA_AFI_IPMR 128 -#define IANA_AFI_IP6MR 129 +typedef enum { + IANA_AFI_RESERVED = 0, + IANA_AFI_IPV4 = 1, + IANA_AFI_IPV6 = 2, + IANA_AFI_L2VPN = 25, + IANA_AFI_IPMR = 128, + IANA_AFI_IP6MR = 129 +} iana_afi_t; #define IANA_SAFI_RESERVED 0 #define IANA_SAFI_UNICAST 1 @@ -531,7 +540,7 @@ typedef uint32_t route_tag_t; #define ROUTE_TAG_MAX UINT32_MAX #define ROUTE_TAG_PRI PRIu32 -static inline afi_t afi_iana2int (afi_t afi) +static inline afi_t afi_iana2int (iana_afi_t afi) { if (afi == IANA_AFI_IPV4) return AFI_IP; @@ -540,7 +549,7 @@ static inline afi_t afi_iana2int (afi_t afi) return AFI_MAX; } -static inline afi_t afi_int2iana (afi_t afi) +static inline iana_afi_t afi_int2iana (afi_t afi) { if (afi == AFI_IP) return IANA_AFI_IPV4; diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index f75a35fa50..2e31535d24 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -433,7 +433,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route, else { summary->type = route->type; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &summary->changed); + monotime(&summary->changed); } summary->path.router_bits = route->path.router_bits; diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index d9cf97a713..198526a0eb 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -388,7 +388,7 @@ ospf6_area_show (struct vty *vty, struct ospf6_area *oa) if (oa->ts_spf.tv_sec || oa->ts_spf.tv_usec) { - result = timeval_elapsed (recent_relative_time (), oa->ts_spf); + result = monotime_since(&oa->ts_spf, NULL); if (result/TIMER_SECOND_MICRO > 0) { vty_out (vty, "SPF last executed %ld.%lds ago%s", diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index 28126918b5..fed5021208 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -245,7 +245,7 @@ ospf6_bfd_interface_dest_update (int command, struct zclient *zclient, old_status = bfd_info->status; bfd_info->status = status; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv); + monotime(&tv); bfd_info->last_update = tv.tv_sec; if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 14c16c0241..6ac93d8984 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -224,7 +224,7 @@ ospf6_install_lsa (struct ospf6_lsa *lsa) ospf6_flood_clear (old); } - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); if (! OSPF6_LSA_IS_MAXAGE (lsa)) lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa, OSPF_LSA_MAXAGE + lsa->birth.tv_sec - now.tv_sec); @@ -862,7 +862,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, if (old) { struct timeval now, res; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &old->installed, &res); time_delta_ms = (res.tv_sec * 1000) + (int)(res.tv_usec/1000); if (time_delta_ms < from->ospf6_if->area->ospf6->lsa_minarrival) @@ -875,7 +875,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, } } - quagga_gettime (QUAGGA_CLK_MONOTONIC, &new->received); + monotime(&new->received); if (is_debug) zlog_debug ("Install, Flood, Possibly acknowledge the received LSA"); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 0ed8d30a8b..c458098099 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -961,7 +961,7 @@ ospf6_interface_show (struct vty *vty, struct interface *ifp) vty_out (vty, " Number of I/F scoped LSAs is %u%s", oi->lsdb->count, VNL); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timerclear (&res); if (oi->thread_send_lsupdate) diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 586bd77f75..d93406fb68 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1485,11 +1485,11 @@ ospf6_brouter_debug_print (struct ospf6_route *brouter) ospf6_linkstate_prefix2str (&brouter->prefix, destination, sizeof (destination)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &brouter->installed, &res); timerstring (&res, installed, sizeof (installed)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &brouter->changed, &res); timerstring (&res, changed, sizeof (changed)); diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 06962ec069..bea153c928 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -207,9 +207,7 @@ ospf6_lsa_age_set (struct ospf6_lsa *lsa) assert (lsa && lsa->header); - if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0) - zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", - safe_strerror (errno)); + monotime(&now); lsa->birth.tv_sec = now.tv_sec - ntohs (lsa->header->age); lsa->birth.tv_usec = now.tv_usec; @@ -230,9 +228,7 @@ ospf6_lsa_age_current (struct ospf6_lsa *lsa) assert (lsa->header); /* current time */ - if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0) - zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", - safe_strerror (errno)); + monotime(&now); if (ntohs (lsa->header->age) >= OSPF_LSA_MAXAGE) { @@ -513,7 +509,7 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, sizeof (adv_router)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &lsa->installed, &res); timerstring (&res, duration, sizeof (duration)); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index e9a25d37e5..578b39a641 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1817,10 +1817,7 @@ ospf6_dbdesc_send (struct thread *thread) if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) && (on->dbdesc_seqnum == 0)) { - struct timeval tv; - if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) - tv.tv_sec = 1; - on->dbdesc_seqnum = tv.tv_sec; + on->dbdesc_seqnum = monotime(NULL); } dbdesc->options[0] = on->ospf6_if->area->options[0]; diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index e9bb2493ff..ec79a1552b 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -97,7 +97,7 @@ ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) on->ospf6_if = oi; on->state = OSPF6_NEIGHBOR_DOWN; on->state_change = 0; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed); + monotime(&on->last_changed); on->router_id = router_id; on->summary_list = ospf6_lsdb_create (on); @@ -163,7 +163,7 @@ ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on, int e return; on->state_change++; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed); + monotime(&on->last_changed); /* log */ if (IS_OSPF6_DEBUG_NEIGHBOR (STATE)) @@ -633,7 +633,7 @@ ospf6_neighbor_show (struct vty *vty, struct ospf6_neighbor *on) { char router_id[16]; char duration[16]; - struct timeval now, res; + struct timeval res; char nstate[16]; char deadtime[16]; long h, m, s; @@ -645,13 +645,11 @@ ospf6_neighbor_show (struct vty *vty, struct ospf6_neighbor *on) } #endif /*HAVE_GETNAMEINFO*/ - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - /* Dead time */ h = m = s = 0; if (on->inactivity_timer) { - s = on->inactivity_timer->u.sands.tv_sec - recent_relative_time().tv_sec; + s = monotime_until(&on->inactivity_timer->u.sands, NULL) / 1000000LL; h = s / 3600; s -= h * 3600; m = s / 60; @@ -673,7 +671,7 @@ ospf6_neighbor_show (struct vty *vty, struct ospf6_neighbor *on) } /* Duration */ - timersub (&now, &on->last_changed, &res); + monotime_since(&on->last_changed, &res); timerstring (&res, duration, sizeof (duration)); /* @@ -707,7 +705,7 @@ ospf6_neighbor_show_drchoice (struct vty *vty, struct ospf6_neighbor *on) inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter)); inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &on->last_changed, &res); timerstring (&res, duration, sizeof (duration)); @@ -731,7 +729,7 @@ ospf6_neighbor_show_detail (struct vty *vty, struct ospf6_neighbor *on) inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter)); inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &on->last_changed, &res); timerstring (&res, duration, sizeof (duration)); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 2f416e2689..29956c61a0 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -600,7 +600,7 @@ ospf6_route_add (struct ospf6_route *route, else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: %s", ospf6_route_table_name (table), buf); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); node = route_node_get (table->table, &route->prefix); route->rnode = node; @@ -1020,7 +1020,7 @@ ospf6_route_show (struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &route->changed, &res); timerstring (&res, duration, sizeof (duration)); @@ -1068,7 +1068,7 @@ ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); /* destination */ if (route->type == OSPF6_DEST_TYPE_LINKSTATE) diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 04519e7d46..333ce5588e 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -603,7 +603,7 @@ ospf6_spf_calculation_thread (struct thread *t) ospf6->t_spf_calc = NULL; /* execute SPF calculation */ - quagga_gettime (QUAGGA_CLK_MONOTONIC, &start); + monotime(&start); if (ospf6_is_router_abr (ospf6)) ospf6_abr_range_reset_cost (ospf6); @@ -644,7 +644,7 @@ ospf6_spf_calculation_thread (struct thread *t) if (ospf6_is_router_abr (ospf6)) ospf6_abr_defaults_to_stub (ospf6); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &end); + monotime(&end); timersub (&end, &start, &runtime); ospf6->ts_spf_duration = runtime; @@ -670,7 +670,6 @@ void ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason) { unsigned long delay, elapsed, ht; - struct timeval now, result; ospf6_set_spf_reason(ospf6, reason); @@ -694,11 +693,7 @@ ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason) return; } - /* XXX Monotic timers: we only care about relative time here. */ - now = recent_relative_time (); - timersub (&now, &ospf6->ts_spf, &result); - - elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); + elapsed = monotime_since(&ospf6->ts_spf, NULL) / 1000LL; ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier; if (ht > ospf6->spf_max_holdtime) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 6da9680fe2..92111c73fc 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -124,7 +124,7 @@ ospf6_create (void) o = XCALLOC (MTYPE_OSPF6_TOP, sizeof (struct ospf6)); /* initialize */ - quagga_gettime (QUAGGA_CLK_MONOTONIC, &o->starttime); + monotime(&o->starttime); o->area_list = list_new (); o->area_list->cmp = ospf6_area_cmp; o->lsdb = ospf6_lsdb_create (o); @@ -891,7 +891,7 @@ ospf6_show (struct vty *vty, struct ospf6 *o) router_id, VNL); /* running time */ - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + monotime(&now); timersub (&now, &o->starttime, &running); timerstring (&running, duration, sizeof (duration)); vty_out (vty, " Running %s%s", duration, VNL); diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index b41e8ff001..f0bc022749 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -58,21 +58,6 @@ extern struct thread_master *master; #define OSPF6_NEIGHBOR(x) ((struct ospf6_neighbor *) (x)) /* operation on timeval structure */ -#ifndef timerclear -#define timerclear(a) (a)->tv_sec = (tvp)->tv_usec = 0 -#endif /*timerclear*/ -#ifndef timersub -#define timersub(a, b, res) \ - do { \ - (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((res)->tv_usec < 0) \ - { \ - (res)->tv_sec--; \ - (res)->tv_usec += 1000000; \ - } \ - } while (0) -#endif /*timersub*/ #define timerstring(tv, buf, size) \ do { \ if ((tv)->tv_sec / 60 / 60 / 24) \ @@ -87,15 +72,6 @@ extern struct thread_master *master; (tv)->tv_sec / 60LL % 60, \ (tv)->tv_sec % 60LL); \ } while (0) -#define timerstring_local(tv, buf, size) \ - do { \ - int ret; \ - struct tm *tm; \ - tm = localtime (&(tv)->tv_sec); \ - ret = strftime (buf, size, "%Y/%m/%d %H:%M:%S", tm); \ - if (ret == 0) \ - zlog_warn ("strftime error"); \ - } while (0) #define threadtimer_string(now, t, buf, size) \ do { \ diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index fe40b10171..b063f317e2 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -649,7 +649,7 @@ ospf_ase_calculate_timer (struct thread *t) { ospf->ase_calc = 0; - quagga_gettime(QUAGGA_CLK_MONOTONIC, &start_time); + monotime(&start_time); /* Calculate external route for each AS-external-LSA */ LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) @@ -681,7 +681,7 @@ ospf_ase_calculate_timer (struct thread *t) ospf->old_external_route = ospf->new_external_route; ospf->new_external_route = route_table_init (); - quagga_gettime(QUAGGA_CLK_MONOTONIC, &stop_time); + monotime(&stop_time); zlog_info ("SPF Processing Time(usecs): External Routes: %lld\n", (stop_time.tv_sec - start_time.tv_sec)*1000000LL+ diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index b4d50a6b9f..74bc38220b 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -249,7 +249,7 @@ ospf_bfd_interface_dest_update (int command, struct zclient *zclient, old_status = bfd_info->status; bfd_info->status = status; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv); + monotime(&tv); bfd_info->last_update = tv.tv_sec; if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 4e9797184d..6e9e59f9b7 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -22,6 +22,7 @@ #include +#include "monotime.h" #include "linklist.h" #include "thread.h" #include "prefix.h" @@ -317,8 +318,8 @@ ospf_timer_dump (struct thread *t, char *buf, size_t size) struct timeval result; if (!t) return "inactive"; - - result = tv_sub (t->u.sands, recent_relative_time()); + + monotime_until (&t->u.sands, &result); return ospf_timeval_dump (&result, buf, size); } diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 7f83ddeaae..417a7aa8d2 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -22,6 +22,7 @@ #include +#include "monotime.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -277,8 +278,8 @@ ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr, "while local one is initial instance."); ; /* Accept this LSA for quick LSDB resynchronization. */ } - else if (tv_cmp (tv_sub (recent_relative_time (), current->tv_recv), - msec2tv (ospf->min_ls_arrival)) < 0) + else if (monotime_since (¤t->tv_recv, NULL) + < ospf->min_ls_arrival * 1000LL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("LSA[Flooding]: LSA is received recently."); @@ -969,7 +970,7 @@ ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area) more time for the ACK to be received and avoid retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); - lsa->tv_recv = recent_relative_time (); + monotime(&lsa->tv_recv); lsa->tv_orig = lsa->tv_recv; ospf_flood_through_area (area, NULL, lsa); ospf_lsa_maxage (area->ospf, lsa); @@ -982,7 +983,7 @@ ospf_lsa_flush_as (struct ospf *ospf, struct ospf_lsa *lsa) more time for the ACK to be received and avoid retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); - lsa->tv_recv = recent_relative_time (); + monotime(&lsa->tv_recv); lsa->tv_orig = lsa->tv_recv; ospf_flood_through_as (ospf, NULL, lsa); ospf_lsa_maxage (ospf, lsa); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 916d4d01c9..cf9943893a 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -22,6 +22,7 @@ #include +#include "monotime.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -62,40 +63,6 @@ get_metric (u_char *metric) } -struct timeval -tv_adjust (struct timeval a) -{ - while (a.tv_usec >= 1000000) - { - a.tv_usec -= 1000000; - a.tv_sec++; - } - - while (a.tv_usec < 0) - { - a.tv_usec += 1000000; - a.tv_sec--; - } - - return a; -} - -int -tv_ceil (struct timeval a) -{ - a = tv_adjust (a); - - return (a.tv_usec ? a.tv_sec + 1 : a.tv_sec); -} - -int -tv_floor (struct timeval a) -{ - a = tv_adjust (a); - - return a.tv_sec; -} - struct timeval int2tv (int a) { @@ -115,50 +82,22 @@ msec2tv (int a) ret.tv_sec = a/1000; ret.tv_usec = (a%1000) * 1000; - return tv_adjust (ret); -} - -struct timeval -tv_add (struct timeval a, struct timeval b) -{ - struct timeval ret; - - ret.tv_sec = a.tv_sec + b.tv_sec; - ret.tv_usec = a.tv_usec + b.tv_usec; - - return tv_adjust (ret); -} - -struct timeval -tv_sub (struct timeval a, struct timeval b) -{ - struct timeval ret; - - ret.tv_sec = a.tv_sec - b.tv_sec; - ret.tv_usec = a.tv_usec - b.tv_usec; - - return tv_adjust (ret); -} - -int -tv_cmp (struct timeval a, struct timeval b) -{ - return (a.tv_sec == b.tv_sec ? - a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); + return ret; } int ospf_lsa_refresh_delay (struct ospf_lsa *lsa) { - struct timeval delta, now; + struct timeval delta; int delay = 0; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - delta = tv_sub (now, lsa->tv_orig); - - if (tv_cmp (delta, msec2tv (OSPF_MIN_LS_INTERVAL)) < 0) + if (monotime_since (&lsa->tv_orig, &delta) < OSPF_MIN_LS_INTERVAL * 1000LL) { - delay = tv_ceil (tv_sub (msec2tv (OSPF_MIN_LS_INTERVAL), delta)); + struct timeval minv = msec2tv (OSPF_MIN_LS_INTERVAL); + timersub (&minv, &delta, &minv); + + /* TBD: remove padding to full sec, return timeval instead */ + delay = minv.tv_sec + !!minv.tv_usec; if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) zlog_debug ("LSA[Type%d:%s]: Refresh timer delay %d seconds", @@ -174,12 +113,10 @@ ospf_lsa_refresh_delay (struct ospf_lsa *lsa) int get_age (struct ospf_lsa *lsa) { - int age; + struct timeval rel; - age = ntohs (lsa->data->ls_age) - + tv_floor (tv_sub (recent_relative_time (), lsa->tv_recv)); - - return age; + monotime_since (&lsa->tv_recv, &rel); + return ntohs (lsa->data->ls_age) + rel.tv_sec; } @@ -228,7 +165,7 @@ ospf_lsa_new () new->flags = 0; new->lock = 1; new->retransmit_counter = 0; - new->tv_recv = recent_relative_time (); + monotime(&new->tv_recv); new->tv_orig = new->tv_recv; new->refresh_list = -1; @@ -3701,8 +3638,8 @@ ospf_refresher_register_lsa (struct ospf *ospf, struct ospf_lsa *lsa) */ delay = (random() % (max_delay - min_delay)) + min_delay; - current_index = ospf->lsa_refresh_queue.index + (quagga_monotime () - - ospf->lsa_refresher_started)/OSPF_LSA_REFRESHER_GRANULARITY; + current_index = ospf->lsa_refresh_queue.index + (monotime(NULL) + - ospf->lsa_refresher_started)/OSPF_LSA_REFRESHER_GRANULARITY; index = (current_index + delay/OSPF_LSA_REFRESHER_GRANULARITY) % (OSPF_LSA_REFRESHER_SLOTS); @@ -3765,7 +3702,7 @@ ospf_lsa_refresh_walker (struct thread *t) modulus. */ ospf->lsa_refresh_queue.index = ((unsigned long)(ospf->lsa_refresh_queue.index + - (quagga_monotime () - ospf->lsa_refresher_started) + (monotime(NULL) - ospf->lsa_refresher_started) / OSPF_LSA_REFRESHER_GRANULARITY)) % OSPF_LSA_REFRESHER_SLOTS; @@ -3806,7 +3743,7 @@ ospf_lsa_refresh_walker (struct thread *t) ospf->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker, ospf, ospf->lsa_refresh_interval); - ospf->lsa_refresher_started = quagga_monotime (); + ospf->lsa_refresher_started = monotime(NULL); for (ALL_LIST_ELEMENTS (lsa_to_refresh, node, nnode, lsa)) { diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 28ecc9d4d6..8b9a0d4c49 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -227,14 +227,8 @@ struct as_external_lsa /* Prototypes. */ /* XXX: Eek, time functions, similar are in lib/thread.c */ -extern struct timeval tv_adjust (struct timeval); -extern int tv_ceil (struct timeval); -extern int tv_floor (struct timeval); extern struct timeval int2tv (int); extern struct timeval msec2tv (int); -extern struct timeval tv_add (struct timeval, struct timeval); -extern struct timeval tv_sub (struct timeval, struct timeval); -extern int tv_cmp (struct timeval, struct timeval); extern int get_age (struct ospf_lsa *); extern u_int16_t ospf_lsa_checksum (struct lsa_header *); diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index 17cc1f66c2..ccb82bf8fe 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -630,10 +630,10 @@ nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event) /* Advance in NSM */ if (next_state > nbr->state) - nbr->ts_last_progress = recent_relative_time (); + monotime(&nbr->ts_last_progress); else /* regression in NSM */ { - nbr->ts_last_regress = recent_relative_time (); + monotime(&nbr->ts_last_regress); nbr->last_regress_str = ospf_nsm_event_str [event]; } diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index f7d1d0fa7d..bf78336ad5 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -22,6 +22,7 @@ #include +#include "monotime.h" #include "thread.h" #include "memory.h" #include "linklist.h" @@ -521,16 +522,18 @@ ospf_ls_upd_timer (struct thread *thread) struct ospf_lsa *lsa; if ((lsa = rn->info) != NULL) - /* Don't retransmit an LSA if we received it within - the last RxmtInterval seconds - this is to allow the - neighbour a chance to acknowledge the LSA as it may - have ben just received before the retransmit timer - fired. This is a small tweak to what is in the RFC, - but it will cut out out a lot of retransmit traffic - - MAG */ - if (tv_cmp (tv_sub (recent_relative_time (), lsa->tv_recv), - int2tv (retransmit_interval)) >= 0) - listnode_add (update, rn->info); + { + /* Don't retransmit an LSA if we received it within + the last RxmtInterval seconds - this is to allow the + neighbour a chance to acknowledge the LSA as it may + have ben just received before the retransmit timer + fired. This is a small tweak to what is in the RFC, + but it will cut out out a lot of retransmit traffic + - MAG */ + if (monotime_since (&lsa->tv_recv, NULL) + >= retransmit_interval * 1000000LL) + listnode_add (update, rn->info); + } } } @@ -1469,10 +1472,8 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, } else { - struct timeval t, now; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - t = tv_sub (now, nbr->last_send_ts); - if (tv_cmp (t, int2tv (nbr->v_inactivity)) < 0) + if (monotime_since (&nbr->last_send_ts, NULL) + < nbr->v_inactivity * 1000000LL) { /* In states Loading and Full the slave must resend its last Database Description packet in response to @@ -2074,12 +2075,8 @@ ospf_ls_upd (struct ospf *ospf, struct ip *iph, struct ospf_header *ospfh, recent) LSA instance. */ else { - struct timeval now; - - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - - if (tv_cmp (tv_sub (now, current->tv_orig), - msec2tv (ospf->min_ls_arrival)) >= 0) + if (monotime_since (¤t->tv_orig, NULL) + >= ospf->min_ls_arrival * 1000LL) /* Trap NSSA type later.*/ ospf_ls_upd_send_lsa (nbr, current, OSPF_SEND_PACKET_DIRECT); DISCARD_LSA (lsa, 8); @@ -3577,7 +3574,7 @@ ospf_db_desc_send (struct ospf_neighbor *nbr) if (nbr->last_send) ospf_packet_free (nbr->last_send); nbr->last_send = ospf_packet_dup (op); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &nbr->last_send_ts); + monotime(&nbr->last_send_ts); } /* Re-send Database Description. */ diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 5dfd41dd1e..077d0e68ad 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -20,6 +20,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include +#include "monotime.h" #include "thread.h" #include "memory.h" #include "hash.h" @@ -1279,7 +1280,7 @@ ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, /* Increment SPF Calculation Counter. */ area->spf_calculation++; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &area->ospf->ts_spf); + monotime(&area->ospf->ts_spf); area->ts_spf = area->ospf->ts_spf; if (IS_DEBUG_OSPF_EVENT) @@ -1300,7 +1301,7 @@ ospf_spf_calculate_timer (struct thread *thread) struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; - struct timeval start_time, stop_time, spf_start_time; + struct timeval start_time, spf_start_time; int areas_processed = 0; unsigned long ia_time, prune_time, rt_time; unsigned long abr_time, total_spf_time, spf_time; @@ -1311,7 +1312,7 @@ ospf_spf_calculate_timer (struct thread *thread) ospf->t_spf_calc = NULL; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &spf_start_time); + monotime(&spf_start_time); /* Allocate new table tree. */ new_table = route_table_init (); new_rtrs = route_table_init (); @@ -1338,24 +1339,19 @@ ospf_spf_calculate_timer (struct thread *thread) areas_processed++; } - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - spf_time = timeval_elapsed (stop_time, spf_start_time); + spf_time = monotime_since(&spf_start_time, NULL); ospf_vl_shut_unapproved (ospf); - start_time = stop_time; /* saving a call */ - + monotime(&start_time); ospf_ia_routing (ospf, new_table, new_rtrs); + ia_time = monotime_since(&start_time, NULL); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - ia_time = timeval_elapsed (stop_time, start_time); - - quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); + monotime(&start_time); ospf_prune_unreachable_networks (new_table); ospf_prune_unreachable_routers (new_rtrs); + prune_time = monotime_since(&start_time, NULL); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - prune_time = timeval_elapsed (stop_time, start_time); /* AS-external-LSA calculation should not be performed here. */ /* If new Router Route is installed, @@ -1365,13 +1361,11 @@ ospf_spf_calculate_timer (struct thread *thread) ospf_ase_calculate_timer_add (ospf); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); - /* Update routing table. */ + monotime(&start_time); ospf_route_install (ospf, new_table); + rt_time = monotime_since(&start_time, NULL); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - rt_time = timeval_elapsed (stop_time, start_time); /* Update ABR/ASBR routing table */ if (ospf->old_rtrs) { @@ -1383,17 +1377,12 @@ ospf_spf_calculate_timer (struct thread *thread) ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; - quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); + monotime(&start_time); if (IS_OSPF_ABR (ospf)) ospf_abr_task (ospf); + abr_time = monotime_since(&start_time, NULL); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - abr_time = timeval_elapsed (stop_time, start_time); - - quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); - total_spf_time = timeval_elapsed (stop_time, spf_start_time); - ospf->ts_spf_duration.tv_sec = total_spf_time/1000000; - ospf->ts_spf_duration.tv_usec = total_spf_time % 1000000; + total_spf_time = monotime_since(&spf_start_time, &ospf->ts_spf_duration); ospf_get_spf_reason_str (rbuf); @@ -1421,7 +1410,6 @@ void ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; - struct timeval result; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer scheduled"); @@ -1440,11 +1428,9 @@ ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason) (void *)ospf->t_spf_calc); return; } - - /* XXX Monotic timers: we only care about relative time here. */ - result = tv_sub (recent_relative_time (), ospf->ts_spf); - - elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); + + elapsed = monotime_since (&ospf->ts_spf, NULL) / 1000; + ht = ospf->spf_holdtime * ospf->spf_hold_multiplier; if (ht > ospf->spf_max_holdtime) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index ce12974d40..2a627f9221 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -23,6 +23,7 @@ #include #include +#include "monotime.h" #include "memory.h" #include "thread.h" #include "prefix.h" @@ -2809,10 +2810,8 @@ show_ip_ospf_area (struct vty *vty, struct ospf_area *area, json_object *json_ar json_object_boolean_true_add(json_area, "indefiniteActiveAdmin"); if (area->t_stub_router) { - struct timeval result; - unsigned long time_store = 0; - result = tv_sub (area->t_stub_router->u.sands, recent_relative_time()); - time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); + long time_store; + time_store = monotime_until(&area->t_stub_router->u.sands, NULL) / 1000LL; json_object_int_add(json_area, "activeStartupRemainderMsecs", time_store); } } @@ -2971,9 +2970,8 @@ show_ip_ospf_common (struct vty *vty, struct ospf *ospf, u_char use_json) { if (use_json) { - unsigned long time_store = 0; - result = tv_sub (ospf->t_deferred_shutdown->u.sands, recent_relative_time()); - time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); + long time_store; + time_store = monotime_until(&ospf->t_deferred_shutdown->u.sands, NULL) / 1000LL; json_object_int_add(json, "deferredShutdownMsecs", time_store); } else @@ -3066,11 +3064,9 @@ show_ip_ospf_common (struct vty *vty, struct ospf *ospf, u_char use_json) { if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec) { - unsigned long time_store = 0; + long time_store = 0; - result = tv_sub (recent_relative_time(), ospf->ts_spf); - result = tv_sub (result, recent_relative_time()); - time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); + time_store = monotime_since(&ospf->ts_spf, NULL) / 1000LL; json_object_int_add(json, "spfLastExecutedMsecs", time_store); time_store = (1000 * ospf->ts_spf_duration.tv_sec) + (ospf->ts_spf_duration.tv_usec / 1000); @@ -3084,7 +3080,7 @@ show_ip_ospf_common (struct vty *vty, struct ospf *ospf, u_char use_json) vty_out (vty, " SPF algorithm "); if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec) { - result = tv_sub (recent_relative_time(), ospf->ts_spf); + monotime_since(&ospf->ts_spf, &result); vty_out (vty, "last executed %s ago%s", ospf_timeval_dump (&result, timebuf, sizeof (timebuf)), VTY_NEWLINE); @@ -3098,13 +3094,10 @@ show_ip_ospf_common (struct vty *vty, struct ospf *ospf, u_char use_json) if (use_json) { - struct timeval temp_time; - unsigned long time_store = 0; - if (ospf->t_spf_calc) { - temp_time = tv_sub (ospf->t_spf_calc->u.sands, recent_relative_time()); - time_store = (1000 * temp_time.tv_sec) + (temp_time.tv_usec / 1000); + long time_store; + time_store = monotime_until(&ospf->t_spf_calc->u.sands, NULL) / 1000LL; json_object_int_add(json, "spfTimerDueInMsecs", time_store); } @@ -3509,16 +3502,9 @@ show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, struct interface char timebuf[OSPF_TIME_DUMP_SIZE]; if (use_json) { - struct timeval result; - unsigned long time_store = 0; - if (oi->t_hello) - result = tv_sub (oi->t_hello->u.sands, recent_relative_time()); - else - { - result.tv_sec = 0; - result.tv_usec = 0; - } - time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); + long time_store = 0; + if (oi->t_hello) + time_store = monotime_until(&oi->t_hello->u.sands, NULL) / 1000LL; json_object_int_add(json_interface_sub, "timerHelloInMsecs", time_store); } else @@ -3708,11 +3694,9 @@ show_ip_ospf_neighbor_sub (struct vty *vty, struct ospf_interface *oi, json_obje json_neighbor = json_object_new_object(); ospf_nbr_state_message (nbr, msgbuf, 16); - struct timeval result; - unsigned long time_store = 0; + long time_store; - result = tv_sub (nbr->t_inactivity->u.sands, recent_relative_time()); - time_store = (1000 * result.tv_sec) + (result.tv_usec / 1000); + time_store = monotime_until(&nbr->t_inactivity->u.sands, NULL) / 1000LL; json_object_int_add (json_neighbor, "priority", nbr->priority); json_object_string_add (json_neighbor, "state", msgbuf); @@ -4093,9 +4077,8 @@ show_ip_ospf_nbr_nbma_detail_sub (struct vty *vty, struct ospf_interface *oi, st /* Show poll-interval timer. */ if (use_json) { - struct timeval res = tv_sub (nbr_nbma->t_poll->u.sands, recent_relative_time ()); - unsigned long time_store = 0; - time_store = (1000 * res.tv_sec) + (res.tv_usec / 1000); + long time_store; + time_store = monotime_until(&nbr_nbma->t_poll->u.sands, NULL) / 1000LL; json_object_int_add(json_sub, "pollIntervalTimerDueMsec", time_store); } else @@ -4170,11 +4153,12 @@ show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi, if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) { - struct timeval res = tv_sub (recent_relative_time (), nbr->ts_last_progress); + struct timeval res; + long time_store; + + time_store = monotime_since(&nbr->ts_last_progress, &res) / 1000LL; if (use_json) { - unsigned long time_store = 0; - time_store = (1000 * res.tv_sec) + (res.tv_usec / 1000); json_object_int_add(json_sub, "lastPrgrsvChangeMsec", time_store); } else @@ -4189,11 +4173,12 @@ show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi, if (nbr->ts_last_regress.tv_sec || nbr->ts_last_regress.tv_usec) { - struct timeval res = tv_sub (recent_relative_time (), nbr->ts_last_regress); + struct timeval res; + long time_store; + + time_store = monotime_since(&nbr->ts_last_regress, &res) / 1000LL; if (use_json) { - unsigned long time_store = 0; - time_store = (1000 * res.tv_sec) + (res.tv_usec / 1000); json_object_int_add(json_sub, "lastRegressiveChangeMsec", time_store); if (nbr->last_regress_str) json_object_string_add(json_sub, "lastRegressiveChangeReason", nbr->last_regress_str); @@ -4234,9 +4219,8 @@ show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi, { if (nbr->t_inactivity) { - struct timeval res = tv_sub (nbr->t_inactivity->u.sands, recent_relative_time ()); - unsigned long time_store = 0; - time_store = (1000 * res.tv_sec) + (res.tv_usec / 1000); + long time_store; + time_store = monotime_until(&nbr->t_inactivity->u.sands, NULL) / 1000LL; json_object_int_add(json_sub, "routerDeadIntervalTimerDueMsec", time_store); } else diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index b7542c2a8b..0398bc21b8 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -273,7 +273,7 @@ ospf_new (u_short instance) new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; new->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker, new, new->lsa_refresh_interval); - new->lsa_refresher_started = quagga_monotime (); + new->lsa_refresher_started = monotime(NULL); if ((new->fd = ospf_sock_init()) < 0) { @@ -1583,7 +1583,7 @@ ospf_timers_refresh_set (struct ospf *ospf, int interval) return 1; time_left = ospf->lsa_refresh_interval - - (quagga_monotime () - ospf->lsa_refresher_started); + (monotime(NULL) - ospf->lsa_refresher_started); if (time_left > interval) { @@ -1602,7 +1602,7 @@ ospf_timers_refresh_unset (struct ospf *ospf) int time_left; time_left = ospf->lsa_refresh_interval - - (quagga_monotime () - ospf->lsa_refresher_started); + (monotime(NULL) - ospf->lsa_refresher_started); if (time_left > OSPF_LSA_REFRESH_INTERVAL_DEFAULT) { diff --git a/pimd/Makefile.am b/pimd/Makefile.am index bc28aa1e92..314a744339 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -19,7 +19,6 @@ # 330, Boston, MA 02111-1307, USA. # PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands -# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging # PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries @@ -27,9 +26,8 @@ PIM_DEFS = #PIM_DEFS += -DPIM_DEBUG_BYDEFAULT -PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY +#PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY #PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH -PIM_DEFS += -DPIM_ZCLIENT_DEBUG PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL @@ -47,24 +45,26 @@ noinst_PROGRAMS = test_igmpv3_join libpim_a_SOURCES = \ pim_memory.c \ pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \ - pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \ + pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c pim_igmpv2.c \ pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \ pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ pim_msg.c pim_upstream.c pim_rpf.c pim_macro.c \ pim_ssmpingd.c pim_int.c pim_rp.c \ - pim_static.c pim_br.c pim_register.c pim_routemap.c + pim_static.c pim_br.c pim_register.c pim_routemap.c \ + pim_msdp.c pim_msdp_socket.c pim_msdp_packet.c noinst_HEADERS = \ pim_memory.h \ pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ - pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \ + pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h pim_igmpv2.h \ pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \ pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ pim_msg.h pim_upstream.h pim_rpf.h pim_macro.h \ pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_rp.h \ - pim_static.h pim_br.h pim_register.h + pim_static.h pim_br.h pim_register.h \ + pim_msdp.h pim_msdp_socket.h pim_msdp_packet.h pimd_SOURCES = \ pim_main.c $(libpim_a_SOURCES) diff --git a/pimd/README b/pimd/README index c8997808b6..3d03979a9a 100644 --- a/pimd/README +++ b/pimd/README @@ -1,18 +1,17 @@ INTRODUCTION qpimd aims to implement a PIM (Protocol Independent Multicast) - daemon for the Quagga Routing Suite. + daemon for the FRR Routing Suite. - Initially qpimd targets only PIM SSM (Source-Specific - Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only - Routers) of RFC 4601. + qpimd implements PIM-SM (Sparse Mode) of RFC 4601. + Additionally MSDP has been implemented. In order to deliver end-to-end multicast routing control - plane, qpimd includes the router-side of IGMPv3 (RFC 3376). + plane, qpimd includes the router-side of IGMPv[2|3] (RFC 3376). LICENSE - qpimd - pimd for quagga + qpimd - pimd for FRR Copyright (C) 2008 Everton da Silva Marques qpimd is free software; you can redistribute it and/or modify @@ -34,78 +33,16 @@ HOME SITE qpimd lives at: - https://github.com/udhos/qpimd + https://github.com/freerangerouting/frr PLATFORMS - qpimd has been tested with Debian Lenny under Linux 2.6. + qpimd has been tested with Debian Jessie. REQUIREMENTS - qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net) + qpimd requires FRR (2.0 or higher) - The GNU Build System (Autotools) is required to build from - source code repository. - - gawk is also needed to build with Autotools. Any other awk - usually won't work. - -BUILDING FROM QUAGGA GIT REPOSITORY - - 1) Get the latest quagga source tree - - # git clone git://code.quagga.net/quagga.git quagga - - 2) Apply qpimd patch into quagga source tree - - # patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch - - 3) Compile and install quagga - - # cd quagga - # ./bootstrap.sh - # ./configure --prefix=/usr/local/quagga --enable-pimd - # make - # make install - -BUILDING FROM QUAGGA TARBALL - - 1) Get the latest quagga tarball - - # wget http://www.quagga.net/download/quagga-0.99.13.tar.gz - - 2) Unpack the quagga tarball - - # tar xzf quagga-0.99.13.tar.gz - - 3) Apply qpimd patch into quagga source tree - - # patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch - - 4) Compile and install quagga - - # cd quagga-0.99.13 - # ./configure --prefix=/usr/local/quagga --enable-pimd - # make - # make install - -USAGE - - 1) Configure and start the zebra daemon - - # cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf - # vi /usr/local/quagga/etc/zebra.conf - # /usr/local/quagga/sbin/zebra - - 2) Configure and start the pimd daemon - - # cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf - # vi /usr/local/quagga/etc/pimd.conf - # /usr/local/quagga/sbin/pimd - - 3) Access pimd vty interface at port TCP 2611 - - # telnet localhost 2611 CONFIGURATION COMMANDS @@ -120,7 +57,7 @@ SUPPORT Please post comments, questions, patches, bug reports at the support site: - https://github.com/udhos/qpimd + https://freerangerouting/frr RELATED WORK diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index f09540ea00..d6f372cc44 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -54,31 +54,23 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, if (PIM_DEBUG_PIM_EVENTS) { if (ch->ifassert_state != new_state) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, - pim_ifchannel_ifassert_name(ch->ifassert_state), - pim_ifchannel_ifassert_name(new_state), - ch->interface->name); + zlog_debug("%s: (S,G)=%s assert state changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + ch->sg_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + pim_ifchannel_ifassert_name(new_state), + ch->interface->name); } if (winner_changed) { - char src_str[100]; - char grp_str[100]; - char was_str[100]; - char winner_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char was_str[INET_ADDRSTRLEN]; + char winner_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); - zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, - was_str, winner_str, ch->interface->name); + zlog_debug("%s: (S,G)=%s assert winner changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + ch->sg_str, + was_str, winner_str, ch->interface->name); } } /* PIM_DEBUG_PIM_EVENTS */ @@ -98,7 +90,7 @@ static void on_trace(const char *label, struct interface *ifp, struct in_addr src) { if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src, src_str, sizeof(src_str)); zlog_debug("%s: from %s on %s", label, src_str, ifp->name); @@ -138,13 +130,9 @@ static void if_could_assert_do_a1(const char *caller, { if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { if (assert_action_a1(ch)) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + zlog_warn("%s: %s: (S,G)=%s assert_action_a1 failure on interface %s", __PRETTY_FUNCTION__, caller, - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); /* log warning only */ } } @@ -156,16 +144,16 @@ static int dispatch_assert(struct interface *ifp, struct pim_assert_metric recv_metric) { struct pim_ifchannel *ch; + struct prefix_sg sg; - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = source_addr; + sg.grp = group_addr; + ch = pim_ifchannel_add(ifp, &sg, 0); if (!ch) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s", + zlog_warn("%s: (S,G)=%s failure creating channel on interface %s", __PRETTY_FUNCTION__, - source_str, group_str, ifp->name); + pim_str_sg_dump (&sg), ifp->name); return -1; } @@ -193,7 +181,6 @@ static int dispatch_assert(struct interface *ifp, } else { if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ assert_action_a3(ch); } } @@ -222,13 +209,9 @@ static int dispatch_assert(struct interface *ifp, break; default: { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", + zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s", __PRETTY_FUNCTION__, - source_str, group_str, ch->ifassert_state, ifp->name); + ch->sg_str, ch->ifassert_state, ifp->name); } return -2; } @@ -241,7 +224,7 @@ int pim_assert_recv(struct interface *ifp, struct in_addr src_addr, uint8_t *buf, int buf_size) { - struct prefix msg_group_addr; + struct prefix_sg sg; struct prefix msg_source_addr; struct pim_assert_metric msg_metric; int offset; @@ -256,9 +239,10 @@ int pim_assert_recv(struct interface *ifp, /* Parse assert group addr */ - offset = pim_parse_addr_group (&msg_group_addr, curr, curr_size); + memset (&sg, 0, sizeof (struct prefix_sg)); + offset = pim_parse_addr_group (&sg, curr, curr_size); if (offset < 1) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -273,7 +257,7 @@ int pim_assert_recv(struct interface *ifp, */ offset = pim_parse_addr_ucast (&msg_source_addr, curr, curr_size); if (offset < 1) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -284,7 +268,7 @@ int pim_assert_recv(struct interface *ifp, curr_size -= offset; if (curr_size != 8) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s", __PRETTY_FUNCTION__, @@ -311,12 +295,12 @@ int pim_assert_recv(struct interface *ifp, msg_metric.route_metric = pim_read_uint32_host(curr); if (PIM_DEBUG_PIM_TRACE) { - char neigh_str[100]; - char source_str[100]; - char group_str[100]; + char neigh_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, neigh_str, sizeof(neigh_str)); pim_inet4_dump("", msg_source_addr.u.prefix4, source_str, sizeof(source_str)); - pim_inet4_dump("", msg_group_addr.u.prefix4, group_str, sizeof(group_str)); + pim_inet4_dump("", sg.grp, group_str, sizeof(group_str)); zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", __PRETTY_FUNCTION__, neigh_str, ifp->name, source_str, group_str, @@ -329,7 +313,7 @@ int pim_assert_recv(struct interface *ifp, return dispatch_assert(ifp, msg_source_addr.u.prefix4, - msg_group_addr.u.prefix4, + sg.grp, msg_metric); } @@ -399,7 +383,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, remain, group_addr); if (!pim_msg_curr) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); zlog_warn("%s: failure encoding group address %s: space left=%d", __PRETTY_FUNCTION__, group_str, remain); @@ -412,7 +396,7 @@ int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, remain, source_addr); if (!pim_msg_curr) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure encoding source address %s: space left=%d", __PRETTY_FUNCTION__, source_str, remain); @@ -448,17 +432,23 @@ static int pim_assert_do(struct pim_ifchannel *ch, int pim_msg_size; ifp = ch->interface; - zassert(ifp); - + if (!ifp) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: channel%s has no associated interface!", + __PRETTY_FUNCTION__, ch->sg_str); + return -1; + } pim_ifp = ifp->info; if (!pim_ifp) { - zlog_warn("%s: pim not enabled on interface: %s", - __PRETTY_FUNCTION__, ifp->name); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: channel %s pim not enabled on interface: %s", + __PRETTY_FUNCTION__, ch->sg_str, ifp->name); return -1; } pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp, - ch->group_addr, ch->source_addr, + ch->sg.grp, ch->sg.src, metric.metric_preference, metric.route_metric, metric.rpt_bit_flag); @@ -480,19 +470,16 @@ static int pim_assert_do(struct pim_ifchannel *ch, pim_hello_require(ifp); if (PIM_DEBUG_PIM_TRACE) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", + zlog_debug("%s: to %s: (S,G)=%s pref=%u metric=%u rpt_bit=%u", __PRETTY_FUNCTION__, - ifp->name, source_str, group_str, + ifp->name, ch->sg_str, metric.metric_preference, metric.route_metric, PIM_FORCE_BOOLEAN(metric.rpt_bit_flag)); } if (pim_msg_send(pim_ifp->pim_sock_fd, + pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, @@ -523,7 +510,7 @@ static int pim_assert_cancel(struct pim_ifchannel *ch) metric.rpt_bit_flag = 0; metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX; metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX; - metric.ip_address = ch->source_addr; + metric.ip_address = ch->sg.src; return pim_assert_do(ch, metric); } @@ -533,28 +520,20 @@ static int on_assert_timer(struct thread *t) struct pim_ifchannel *ch; struct interface *ifp; - zassert(t); ch = THREAD_ARG(t); - zassert(ch); ifp = ch->interface; - zassert(ifp); if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s", + zlog_debug("%s: (S,G)=%s timer expired on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); } - ch->t_ifassert_timer = 0; + ch->t_ifassert_timer = NULL; switch (ch->ifassert_state) { case PIM_IFASSERT_I_AM_WINNER: - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ assert_action_a3(ch); break; case PIM_IFASSERT_I_AM_LOSER: @@ -562,13 +541,10 @@ static int on_assert_timer(struct thread *t) break; default: { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", - __PRETTY_FUNCTION__, - source_str, group_str, ch->ifassert_state, ifp->name); + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: (S,G)=%s invalid assert state %d on interface %s", + __PRETTY_FUNCTION__, + ch->sg_str, ch->ifassert_state, ifp->name); } } @@ -577,46 +553,25 @@ static int on_assert_timer(struct thread *t) static void assert_timer_off(struct pim_ifchannel *ch) { - struct interface *ifp; - - zassert(ch); - ifp = ch->interface; - zassert(ifp); - if (PIM_DEBUG_PIM_TRACE) { if (ch->t_ifassert_timer) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s", + zlog_debug("%s: (S,G)=%s cancelling timer on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ch->interface->name); } } THREAD_OFF(ch->t_ifassert_timer); - zassert(!ch->t_ifassert_timer); } static void pim_assert_timer_set(struct pim_ifchannel *ch, int interval) { - struct interface *ifp; - - zassert(ch); - ifp = ch->interface; - zassert(ifp); - assert_timer_off(ch); if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s", + zlog_debug("%s: (S,G)=%s starting %u sec timer on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, interval, ifp->name); + ch->sg_str, interval, ch->interface->name); } THREAD_TIMER_ON(master, ch->t_ifassert_timer, @@ -644,17 +599,11 @@ int assert_action_a1(struct pim_ifchannel *ch) struct interface *ifp = ch->interface; struct pim_interface *pim_ifp; - zassert(ifp); - pim_ifp = ifp->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s", + zlog_warn("%s: (S,G)=%s multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); return -1; /* must return since pim_ifp is used below */ } @@ -664,19 +613,19 @@ int assert_action_a1(struct pim_ifchannel *ch) pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address)); - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ if (assert_action_a3(ch)) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s", + zlog_warn("%s: (S,G)=%s assert_action_a3 failure on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); /* warning only */ } - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_WINNER state", + __PRETTY_FUNCTION__, ch->sg_str); + } return 0; } @@ -699,7 +648,12 @@ static void assert_action_a2(struct pim_ifchannel *ch, pim_assert_timer_set(ch, PIM_ASSERT_TIME); - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); + if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s not in expected PIM_IFASSERT_I_AM_LOSER state", + __PRETTY_FUNCTION__, ch->sg_str); + } } /* @@ -712,24 +666,23 @@ static void assert_action_a2(struct pim_ifchannel *ch, */ static int assert_action_a3(struct pim_ifchannel *ch) { - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + if (ch->ifassert_state != PIM_IFASSERT_I_AM_WINNER) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s expected to be in PIM_IFASSERT_I_AM_WINNER state", + __PRETTY_FUNCTION__, ch->sg_str); + return -1; + } pim_assert_timer_reset(ch); if (pim_assert_send(ch)) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - - zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s", + zlog_warn("%s: (S,G)=%s failure sending assert on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); return -1; } - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); - return 0; } @@ -746,19 +699,20 @@ static int assert_action_a3(struct pim_ifchannel *ch) void assert_action_a4(struct pim_ifchannel *ch) { if (pim_assert_cancel(ch)) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s", + zlog_warn("%s: failure sending AssertCancel%s on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); /* log warning only */ } assert_action_a5(ch); - zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); + if (ch->ifassert_state != PIM_IFASSERT_NOINFO) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s not in PIM_IFASSERT_NOINFO state as expected", + __PRETTY_FUNCTION__, ch->sg_str); + } } /* @@ -772,7 +726,12 @@ void assert_action_a4(struct pim_ifchannel *ch) void assert_action_a5(struct pim_ifchannel *ch) { reset_ifassert_state(ch); - zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); + if (ch->ifassert_state != PIM_IFASSERT_NOINFO) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s not in PIM_IFSSERT_NOINFO state as expected", + __PRETTY_FUNCTION__, ch->sg_str); + } } /* @@ -799,6 +758,11 @@ static void assert_action_a6(struct pim_ifchannel *ch, if (ch->upstream->join_state == PIM_UPSTREAM_JOINED) ch->upstream->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; - zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); + if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) + { + if(PIM_DEBUG_PIM_EVENTS) + zlog_warn("%s: channel%s not in PIM_IFASSERT_I_AM_LOSER state as expected", + __PRETTY_FUNCTION__, ch->sg_str); + } } diff --git a/pimd/pim_br.c b/pimd/pim_br.c index 121a45fd16..3f84de79c8 100644 --- a/pimd/pim_br.c +++ b/pimd/pim_br.c @@ -30,8 +30,7 @@ #include "linklist.h" struct pim_br { - struct in_addr source; - struct in_addr group; + struct prefix_sg sg; struct in_addr pmbr; }; @@ -40,14 +39,14 @@ struct in_addr pim_br_unknown = { .s_addr = 0 }; static struct list *pim_br_list = NULL; struct in_addr -pim_br_get_pmbr (struct in_addr source, struct in_addr group) +pim_br_get_pmbr (struct prefix_sg *sg) { struct listnode *node; struct pim_br *pim_br; for (ALL_LIST_ELEMENTS_RO (pim_br_list, node, pim_br)) { - if (source.s_addr == pim_br->source.s_addr && - group.s_addr == pim_br->group.s_addr) + if (sg->src.s_addr == pim_br->sg.src.s_addr && + sg->grp.s_addr == pim_br->sg.grp.s_addr) return pim_br->pmbr; } @@ -55,14 +54,14 @@ pim_br_get_pmbr (struct in_addr source, struct in_addr group) } void -pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br) +pim_br_set_pmbr (struct prefix_sg *sg, struct in_addr br) { struct listnode *node, *next; struct pim_br *pim_br; for (ALL_LIST_ELEMENTS (pim_br_list, node, next, pim_br)) { - if (source.s_addr == pim_br->source.s_addr && - group.s_addr == pim_br->group.s_addr) + if (sg->src.s_addr == pim_br->sg.src.s_addr && + sg->grp.s_addr == pim_br->sg.grp.s_addr) break; } @@ -73,8 +72,7 @@ pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br) return; } - pim_br->source = source; - pim_br->group = group; + pim_br->sg = *sg; listnode_add(pim_br_list, pim_br); } @@ -86,14 +84,14 @@ pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr br) * Remove the (S,G) from the stored values */ void -pim_br_clear_pmbr (struct in_addr source, struct in_addr group) +pim_br_clear_pmbr (struct prefix_sg *sg) { struct listnode *node, *next; struct pim_br *pim_br; for (ALL_LIST_ELEMENTS (pim_br_list, node, next, pim_br)) { - if (source.s_addr == pim_br->source.s_addr && - group.s_addr == pim_br->group.s_addr) + if (sg->src.s_addr == pim_br->sg.src.s_addr && + sg->grp.s_addr == pim_br->sg.grp.s_addr) break; } diff --git a/pimd/pim_br.h b/pimd/pim_br.h index 06b10ada30..8e4f719ed0 100644 --- a/pimd/pim_br.h +++ b/pimd/pim_br.h @@ -21,10 +21,10 @@ #ifndef PIM_BR_H #define PIM_BR_H -struct in_addr pim_br_get_pmbr (struct in_addr source, struct in_addr group); +struct in_addr pim_br_get_pmbr (struct prefix_sg *sg); -void pim_br_set_pmbr (struct in_addr source, struct in_addr group, struct in_addr value); -void pim_br_clear_pmbr (struct in_addr source, struct in_addr group); +void pim_br_set_pmbr (struct prefix_sg *sg, struct in_addr value); +void pim_br_clear_pmbr (struct prefix_sg *sg); void pim_br_init (void); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 088713012e..b8bb694b78 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -20,10 +20,12 @@ #include +#include "lib/json.h" #include "command.h" #include "if.h" #include "prefix.h" #include "zclient.h" +#include "plist.h" #include "pimd.h" #include "pim_mroute.h" @@ -50,6 +52,8 @@ #include "pim_zebra.h" #include "pim_static.h" #include "pim_rp.h" +#include "pim_zlookup.h" +#include "pim_msdp.h" static struct cmd_node pim_global_node = { PIM_NODE, @@ -63,6 +67,13 @@ static struct cmd_node interface_node = { 1 /* vtysh ? yes */ }; +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", + 1 +}; + static void pim_if_membership_clear(struct interface *ifp) { struct pim_interface *pim_ifp; @@ -127,9 +138,12 @@ static void pim_if_membership_refresh(struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) { - pim_ifchannel_local_membership_add(ifp, - src->source_addr, - grp->group_addr); + struct prefix_sg sg; + + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = src->source_addr; + sg.grp = grp->group_addr; + pim_ifchannel_local_membership_add(ifp, &sg); } } /* scan group sources */ @@ -146,9 +160,11 @@ static void pim_if_membership_refresh(struct interface *ifp) static void pim_show_assert(struct vty *vty) { - struct listnode *ifnode; - struct interface *ifp; - time_t now; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + struct listnode *ch_node; + struct in_addr ifaddr; + time_t now; now = pim_time_monotonic_sec(); @@ -156,55 +172,50 @@ static void pim_show_assert(struct vty *vty) "Interface Address Source Group State Winner Uptime Timer%s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; + char winner_str[INET_ADDRSTRLEN]; + char uptime[10]; + char timer[10]; - pim_ifp = ifp->info; + pim_ifp = ch->interface->info; if (!pim_ifp) continue; ifaddr = pim_ifp->primary_address; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; - char winner_str[100]; - char uptime[10]; - char timer[10]; + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->ifassert_winner, + winner_str, sizeof(winner_str)); - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", ch->ifassert_winner, - winner_str, sizeof(winner_str)); + pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation); + pim_time_timer_to_mmss(timer, sizeof(timer), + ch->t_ifassert_timer); - pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation); - pim_time_timer_to_mmss(timer, sizeof(timer), - ch->t_ifassert_timer); - - vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s", - ifp->name, - inet_ntoa(ifaddr), - ch_src_str, - ch_grp_str, - pim_ifchannel_ifassert_name(ch->ifassert_state), - winner_str, - uptime, - timer, - VTY_NEWLINE); - } /* scan interface channels */ - } /* scan interfaces */ + vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s", + ch->interface->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + winner_str, + uptime, + timer, + VTY_NEWLINE); + } /* scan interface channels */ } static void pim_show_assert_internal(struct vty *vty) { - struct listnode *ifnode; - struct interface *ifp; + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + struct in_addr ifaddr; vty_out(vty, "CA: CouldAssert%s" @@ -217,210 +228,301 @@ static void pim_show_assert_internal(struct vty *vty) "Interface Address Source Group CA eCA ATD eATD%s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO(vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; - - pim_ifp = ifp->info; + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; ifaddr = pim_ifp->primary_address; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s", - ifp->name, - inet_ntoa(ifaddr), - ch_src_str, - ch_grp_str, - PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no", - pim_macro_ch_could_assert_eval(ch) ? "yes" : "no", - PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no", - pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no", - VTY_NEWLINE); - } /* scan interface channels */ - } /* scan interfaces */ + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s", + ch->interface->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no", + pim_macro_ch_could_assert_eval(ch) ? "yes" : "no", + PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no", + pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no", + VTY_NEWLINE); + } /* scan interface channels */ } static void pim_show_assert_metric(struct vty *vty) { - struct listnode *ifnode; - struct interface *ifp; - + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + struct in_addr ifaddr; + vty_out(vty, "Interface Address Source Group RPT Pref Metric Address %s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { + pim_ifp = ch->interface->info; - pim_ifp = ifp->info; - if (!pim_ifp) continue; ifaddr = pim_ifp->primary_address; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; - char addr_str[100]; - struct pim_assert_metric am; + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; + char addr_str[INET_ADDRSTRLEN]; + struct pim_assert_metric am; - am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); + am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", am.ip_address, - addr_str, sizeof(addr_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am.ip_address, + addr_str, sizeof(addr_str)); - vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s", - ifp->name, - inet_ntoa(ifaddr), - ch_src_str, - ch_grp_str, - am.rpt_bit_flag ? "yes" : "no", - am.metric_preference, - am.route_metric, - addr_str, - VTY_NEWLINE); + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s", + ch->interface->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am.rpt_bit_flag ? "yes" : "no", + am.metric_preference, + am.route_metric, + addr_str, + VTY_NEWLINE); } /* scan interface channels */ - } /* scan interfaces */ } static void pim_show_assert_winner_metric(struct vty *vty) { - struct listnode *ifnode; - struct interface *ifp; + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + struct in_addr ifaddr; vty_out(vty, "Interface Address Source Group RPT Pref Metric Address %s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; - - pim_ifp = ifp->info; + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; ifaddr = pim_ifp->primary_address; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; - char addr_str[100]; - struct pim_assert_metric *am; - char pref_str[5]; - char metr_str[7]; + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; + char addr_str[INET_ADDRSTRLEN]; + struct pim_assert_metric *am; + char pref_str[5]; + char metr_str[7]; - am = &ch->ifassert_winner_metric; + am = &ch->ifassert_winner_metric; - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", am->ip_address, - addr_str, sizeof(addr_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am->ip_address, + addr_str, sizeof(addr_str)); - if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) - snprintf(pref_str, sizeof(pref_str), "INFI"); - else - snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference); + if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) + snprintf(pref_str, sizeof(pref_str), "INFI"); + else + snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference); - if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX) - snprintf(metr_str, sizeof(metr_str), "INFI"); - else - snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric); + if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX) + snprintf(metr_str, sizeof(metr_str), "INFI"); + else + snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric); - vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s", - ifp->name, - inet_ntoa(ifaddr), - ch_src_str, - ch_grp_str, - am->rpt_bit_flag ? "yes" : "no", - pref_str, - metr_str, - addr_str, - VTY_NEWLINE); - } /* scan interface channels */ - } /* scan interfaces */ + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s", + ch->interface->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am->rpt_bit_flag ? "yes" : "no", + pref_str, + metr_str, + addr_str, + VTY_NEWLINE); + } /* scan interface channels */ } -static void pim_show_membership(struct vty *vty) +static void json_object_pim_ifp_add(struct json_object *json, struct interface *ifp) { - struct listnode *ifnode; - struct interface *ifp; + struct pim_interface *pim_ifp; - vty_out(vty, - "Interface Address Source Group Membership%s", - VTY_NEWLINE); + pim_ifp = ifp->info; + json_object_string_add(json, "name", ifp->name); + json_object_string_add(json, "state", if_is_up(ifp) ? "up" : "down"); + json_object_string_add(json, "address", inet_ntoa(pim_ifp->primary_address)); + json_object_int_add(json, "index", ifp->ifindex); - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; + if (if_is_multicast(ifp)) + json_object_boolean_true_add(json, "flagMulticast"); + + if (if_is_broadcast(ifp)) + json_object_boolean_true_add(json, "flagBroadcast"); + + if (ifp->flags & IFF_ALLMULTI) + json_object_boolean_true_add(json, "flagAllMulticast"); + + if (ifp->flags & IFF_PROMISC) + json_object_boolean_true_add(json, "flagPromiscuous"); + + if (PIM_IF_IS_DELETED(ifp)) + json_object_boolean_true_add(json, "flagDeleted"); + + if (pim_if_lan_delay_enabled(ifp)) + json_object_boolean_true_add(json, "lanDelayEnabled"); +} + +static void pim_show_membership(struct vty *vty, u_char uj) +{ + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + enum json_type type; + json_object *json = NULL; + json_object *json_iface = NULL; + json_object *json_row = NULL; + json_object *json_tmp = NULL; + + json = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { + + pim_ifp = ch->interface->info; - pim_ifp = ifp->info; - if (!pim_ifp) continue; - ifaddr = pim_ifp->primary_address; + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); + json_object_object_get_ex(json, ch->interface->name, &json_iface); - vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s", - ifp->name, - inet_ntoa(ifaddr), - ch_src_str, - ch_grp_str, - ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ? - "NOINFO" : "INCLUDE", - VTY_NEWLINE); - } /* scan interface channels */ - } /* scan interfaces */ + if (!json_iface) { + json_iface = json_object_new_object(); + json_object_pim_ifp_add(json_iface, ch->interface); + json_object_object_add(json, ch->interface->name, json_iface); + } + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", ch_src_str); + json_object_string_add(json_row, "group", ch_grp_str); + json_object_string_add(json_row, "localMembership", + ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ? "NOINFO" : "INCLUDE"); + json_object_object_add(json_iface, ch_grp_str, json_row); + } /* scan interface channels */ + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + } else { + vty_out(vty, + "Interface Address Source Group Membership%s", + VTY_NEWLINE); + + /* + * Example of the json data we are traversing + * + * { + * "swp3":{ + * "name":"swp3", + * "state":"up", + * "address":"10.1.20.1", + * "index":5, + * "flagMulticast":true, + * "flagBroadcast":true, + * "lanDelayEnabled":true, + * "226.10.10.10":{ + * "source":"*", + * "group":"226.10.10.10", + * "localMembership":"INCLUDE" + * } + * } + * } + */ + + /* foreach interface */ + json_object_object_foreach(json, key, val) { + + /* Find all of the keys where the val is an object. In the example + * above the only one is 226.10.10.10 + */ + json_object_object_foreach(val, if_field_key, if_field_val) { + type = json_object_get_type(if_field_val); + + if (type == json_type_object) { + vty_out(vty, "%-9s ", key); + + json_object_object_get_ex(val, "address", &json_tmp); + vty_out(vty, "%-15s ", json_object_get_string(json_tmp)); + + json_object_object_get_ex(if_field_val, "source", &json_tmp); + vty_out(vty, "%-15s ", json_object_get_string(json_tmp)); + + /* Group */ + vty_out(vty, "%-15s ", if_field_key); + + json_object_object_get_ex(if_field_val, "localMembership", &json_tmp); + vty_out(vty, "%-10s%s", json_object_get_string(json_tmp), VTY_NEWLINE); + } + } + } + } + + json_object_free(json); } -static void igmp_show_interfaces(struct vty *vty) +static void pim_print_ifp_flags(struct vty *vty, struct interface *ifp, int mloop) +{ + vty_out(vty, "Flags%s", VTY_NEWLINE); + vty_out(vty, "-----%s", VTY_NEWLINE); + vty_out(vty, "All Multicast : %s%s", (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Broadcast : %s%s", if_is_broadcast(ifp)? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Deleted : %s%s", PIM_IF_IS_DELETED(ifp) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Interface Index : %d%s", ifp->ifindex, VTY_NEWLINE); + vty_out(vty, "Multicast : %s%s", if_is_multicast(ifp) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Multicast Loop : %d%s", mloop, VTY_NEWLINE); + vty_out(vty, "Promiscuous : %s%s", (ifp->flags & IFF_PROMISC) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); +} + +static void igmp_show_interfaces(struct vty *vty, u_char uj) { struct listnode *node; struct interface *ifp; time_t now; - + json_object *json = NULL; + json_object *json_row = NULL; + now = pim_time_monotonic_sec(); - vty_out(vty, - "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", - VTY_NEWLINE); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Interface State Address V Querier Query Timer Uptime%s", + VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { struct pim_interface *pim_ifp; @@ -428,33 +530,176 @@ static void igmp_show_interfaces(struct vty *vty) struct igmp_sock *igmp; pim_ifp = ifp->info; - + if (!pim_ifp) continue; for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { char uptime[10]; - int mloop; + char query_hhmmss[10]; pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation); + pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); - mloop = pim_socket_mcastloop_get(igmp->fd); - - vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", - ifp->name, - inet_ntoa(igmp->ifaddr), - ifp->ifindex, - igmp->fd, - uptime, - if_is_multicast(ifp) ? "yes" : "no", - if_is_broadcast(ifp) ? "yes" : "no", - (mloop < 0) ? "?" : (mloop ? "yes" : "no"), - (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", - (ifp->flags & IFF_PROMISC) ? "yes" : "no", - PIM_IF_IS_DELETED(ifp) ? "yes" : "no", - VTY_NEWLINE); + if (uj) { + json_row = json_object_new_object(); + json_object_pim_ifp_add(json_row, ifp); + json_object_string_add(json_row, "upTime", uptime); + json_object_int_add(json_row, "version", pim_ifp->igmp_version); + + if (igmp->t_igmp_query_timer) { + json_object_boolean_true_add(json_row, "querier"); + json_object_string_add(json_row, "queryTimer", query_hhmmss); + } + + json_object_object_add(json, ifp->name, json_row); + + } else { + vty_out(vty, "%-9s %5s %15s %d %7s %11s %8s%s", + ifp->name, + if_is_up(ifp) ? "up" : "down", + inet_ntoa(igmp->ifaddr), + pim_ifp->igmp_version, + igmp->t_igmp_query_timer ? "local" : "other", + query_hhmmss, + uptime, + VTY_NEWLINE); + } } } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} + +static void igmp_show_interfaces_single(struct vty *vty, const char *ifname, u_char uj) +{ + struct igmp_sock *igmp; + struct interface *ifp; + struct listnode *node; + struct listnode *sock_node; + struct pim_interface *pim_ifp; + char uptime[10]; + char query_hhmmss[10]; + char other_hhmmss[10]; + int found_ifname = 0; + int sqi; + int mloop; + long gmi_msec; /* Group Membership Interval */ + long lmqt_msec; + long ohpi_msec; + long oqpi_msec; /* Other Querier Present Interval */ + long qri_msec; + time_t now; + + json_object *json = NULL; + json_object *json_row = NULL; + + if (uj) + json = json_object_new_object(); + + now = pim_time_monotonic_sec(); + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name)) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + found_ifname = 1; + pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation); + pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); + pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer); + + gmi_msec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + + oqpi_msec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + lmqt_msec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec, + igmp->querier_robustness_variable); + + ohpi_msec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec) * 100; + + qri_msec = pim_ifp->igmp_query_max_response_time_dsec * 100; + mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); + + if (uj) { + json_row = json_object_new_object(); + json_object_pim_ifp_add(json_row, ifp); + json_object_string_add(json_row, "upTime", uptime); + json_object_string_add(json_row, "querier", igmp->t_igmp_query_timer ? "local" : "other"); + json_object_int_add(json_row, "queryStartCount", igmp->startup_query_count); + json_object_string_add(json_row, "queryQueryTimer", query_hhmmss); + json_object_string_add(json_row, "queryOtherTimer", other_hhmmss); + json_object_int_add(json_row, "version", pim_ifp->igmp_version); + json_object_int_add(json_row, "timerGroupMembershipIntervalMsec", gmi_msec); + json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec); + json_object_int_add(json_row, "timerOlderHostPresentIntervalMsec", ohpi_msec); + json_object_int_add(json_row, "timerOtherQuerierPresentIntervalMsec", oqpi_msec); + json_object_int_add(json_row, "timerQueryInterval", igmp->querier_query_interval); + json_object_int_add(json_row, "timerQueryResponseIntervalMsec", qri_msec); + json_object_int_add(json_row, "timerRobustnessVariable", igmp->querier_robustness_variable); + json_object_int_add(json_row, "timerStartupQueryInterval", sqi); + + json_object_object_add(json, ifp->name, json_row); + + } else { + vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE); + vty_out(vty, "State : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE); + vty_out(vty, "Address : %s%s", inet_ntoa(pim_ifp->primary_address), VTY_NEWLINE); + vty_out(vty, "Uptime : %s%s", uptime, VTY_NEWLINE); + vty_out(vty, "Version : %d%s", pim_ifp->igmp_version, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Querier%s", VTY_NEWLINE); + vty_out(vty, "-------%s", VTY_NEWLINE); + vty_out(vty, "Querier : %s%s", igmp->t_igmp_query_timer ? "local" : "other", VTY_NEWLINE); + vty_out(vty, "Start Count : %d%s", igmp->startup_query_count, VTY_NEWLINE); + vty_out(vty, "Query Timer : %s%s", query_hhmmss, VTY_NEWLINE); + vty_out(vty, "Other Timer : %s%s", other_hhmmss, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Timers%s", VTY_NEWLINE); + vty_out(vty, "------%s", VTY_NEWLINE); + vty_out(vty, "Group Membership Interval : %lis%s", gmi_msec/1000, VTY_NEWLINE); + vty_out(vty, "Last Member Query Time : %lis%s", lmqt_msec/1000, VTY_NEWLINE); + vty_out(vty, "Older Host Present Interval : %lis%s", ohpi_msec/1000, VTY_NEWLINE); + vty_out(vty, "Other Querier Present Interval : %lis%s", oqpi_msec/1000, VTY_NEWLINE); + vty_out(vty, "Query Interval : %ds%s", igmp->querier_query_interval, VTY_NEWLINE); + vty_out(vty, "Query Response Interval : %lis%s", qri_msec/1000, VTY_NEWLINE); + vty_out(vty, "Robustness Variable : %d%s", igmp->querier_robustness_variable, VTY_NEWLINE); + vty_out(vty, "Startup Query Interval : %ds%s", sqi, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + pim_print_ifp_flags(vty, ifp, mloop); + } + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } else { + if (!found_ifname) + vty_out (vty, "%% No such interface%s", VTY_NEWLINE); + } } static void igmp_show_interface_join(struct vty *vty) @@ -474,7 +719,7 @@ static void igmp_show_interface_join(struct vty *vty) struct listnode *join_node; struct igmp_join *ij; struct in_addr pri_addr; - char pri_addr_str[100]; + char pri_addr_str[INET_ADDRSTRLEN]; pim_ifp = ifp->info; @@ -488,8 +733,8 @@ static void igmp_show_interface_join(struct vty *vty) pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; char uptime[10]; pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation); @@ -510,159 +755,297 @@ static void igmp_show_interface_join(struct vty *vty) } -static void show_interface_address(struct vty *vty) +static void pim_show_interfaces_single(struct vty *vty, const char *ifname, u_char uj) { - struct listnode *ifpnode; + struct in_addr ifaddr; struct interface *ifp; - - vty_out(vty, - "Interface Primary Secondary %s", - VTY_NEWLINE); + struct listnode *neighnode; + struct listnode*node; + struct listnode *upnode; + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + struct pim_upstream *up; + time_t now; + char dr_str[INET_ADDRSTRLEN]; + char dr_uptime[10]; + char expire[10]; + char grp_str[INET_ADDRSTRLEN]; + char hello_period[10]; + char hello_timer[10]; + char neigh_src_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char stat_uptime[10]; + char uptime[10]; + int mloop; + int found_ifname = 0; + int print_header; + json_object *json = NULL; + json_object *json_row = NULL; + json_object *json_pim_neighbor = NULL; + json_object *json_pim_neighbors = NULL; + json_object *json_group = NULL; + json_object *json_group_source = NULL; + json_object *json_fhr_sources = NULL; + struct pim_secondary_addr *sec_addr; + struct listnode *sec_node; - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifpnode, ifp)) { - struct listnode *ifcnode; - struct connected *ifc; - struct in_addr pri_addr; - char pri_addr_str[100]; - - pri_addr = pim_find_primary_addr(ifp); - - pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); - - for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) { - char sec_addr_str[100]; - struct prefix *p = ifc->address; - - if (p->family != AF_INET) - continue; - - if (p->u.prefix4.s_addr == pri_addr.s_addr) { - sec_addr_str[0] = '\0'; - } - else { - pim_inet4_dump("", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str)); - } - - vty_out(vty, "%-9s %-15s %-15s%s", - ifp->name, - pri_addr_str, - sec_addr_str, - VTY_NEWLINE); - } - } -} - -static void pim_show_dr(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - time_t now; - now = pim_time_monotonic_sec(); - vty_out(vty, - "NonPri: Number of neighbors missing DR Priority hello option%s" - "DrPri: Designated Router Priority sent%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, "Interface Address DR Uptime Elections Changes NonPri DrPri%s", VTY_NEWLINE); + if (uj) + json = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - char dr_str[100]; - char dr_uptime[10]; - pim_ifp = ifp->info; - + if (!pim_ifp) continue; if (pim_ifp->pim_sock_fd < 0) continue; - ifaddr = pim_ifp->primary_address; - - pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), - now, pim_ifp->pim_dr_election_last); - - pim_inet4_dump("", pim_ifp->pim_dr_addr, - dr_str, sizeof(dr_str)); - - vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d %10d%s", - ifp->name, - inet_ntoa(ifaddr), - dr_str, - dr_uptime, - pim_ifp->pim_dr_election_count, - pim_ifp->pim_dr_election_changes, - pim_ifp->pim_dr_num_nondrpri_neighbors, - pim_ifp->pim_dr_priority, - VTY_NEWLINE); - } -} - -static void pim_show_hello(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - time_t now; - - now = pim_time_monotonic_sec(); - - vty_out(vty, "Interface Address Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE); - - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - char hello_period[10]; - char hello_timer[10]; - char stat_uptime[10]; - - pim_ifp = ifp->info; - - if (!pim_ifp) - continue; - - if (pim_ifp->pim_sock_fd < 0) + if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name)) continue; + found_ifname = 1; ifaddr = pim_ifp->primary_address; - - pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_str, sizeof(dr_str)); + pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), now, pim_ifp->pim_dr_election_last); + pim_time_timer_to_hhmmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer); pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period); pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start); + mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); - vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s", - ifp->name, - inet_ntoa(ifaddr), - hello_period, - hello_timer, - stat_uptime, - pim_ifp->pim_ifstat_hello_recv, - pim_ifp->pim_ifstat_hello_recvfail, - pim_ifp->pim_ifstat_hello_sent, - pim_ifp->pim_ifstat_hello_sendfail, - VTY_NEWLINE); + if (uj) { + json_row = json_object_new_object(); + json_object_pim_ifp_add(json_row, ifp); + + if (pim_ifp->update_source.s_addr != INADDR_ANY) { + json_object_string_add(json_row, "useSource", inet_ntoa(pim_ifp->update_source)); + } + if (pim_ifp->sec_addr_list) { + json_object *sec_list = NULL; + + sec_list = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) { + json_object_array_add(sec_list, json_object_new_string(inet_ntoa(sec_addr->addr))); + } + json_object_object_add(json_row, "secondaryAddressList", sec_list); + } + + // PIM neighbors + if (pim_ifp->pim_neighbor_list->count) { + json_pim_neighbors = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + json_pim_neighbor = json_object_new_object(); + pim_inet4_dump("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); + pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); + pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer); + + json_object_string_add(json_pim_neighbor, "address", neigh_src_str); + json_object_string_add(json_pim_neighbor, "upTime", uptime); + json_object_string_add(json_pim_neighbor, "holdtime", expire); + + json_object_object_add(json_pim_neighbors, neigh_src_str, json_pim_neighbor); + } + + json_object_object_add(json_row, "neighbors", json_pim_neighbors); + } + + json_object_string_add(json_row, "drAddress", dr_str); + json_object_int_add(json_row, "drPriority", pim_ifp->pim_dr_priority); + json_object_string_add(json_row, "drUptime", dr_uptime); + json_object_int_add(json_row, "drElections", pim_ifp->pim_dr_election_count); + json_object_int_add(json_row, "drChanges", pim_ifp->pim_dr_election_changes); + + // FHR + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) { + if (ifp == up->rpf.source_nexthop.interface) { + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) { + if (!json_fhr_sources) { + json_fhr_sources = json_object_new_object(); + } + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); + + /* Does this group live in json_fhr_sources? If not create it. */ + json_object_object_get_ex(json_fhr_sources, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json_fhr_sources, grp_str, json_group); + } + + json_group_source = json_object_new_object(); + json_object_string_add(json_group_source, "source", src_str); + json_object_string_add(json_group_source, "group", grp_str); + json_object_string_add(json_group_source, "upTime", uptime); + json_object_object_add(json_group, src_str, json_group_source); + } + } + } + + if (json_fhr_sources) { + json_object_object_add(json_row, "firstHopRouter", json_fhr_sources); + } + + json_object_int_add(json_row, "helloPeriod", pim_ifp->pim_hello_period); + json_object_string_add(json_row, "helloTimer", hello_timer); + json_object_string_add(json_row, "helloStatStart", stat_uptime); + json_object_int_add(json_row, "helloReceived", pim_ifp->pim_ifstat_hello_recv); + json_object_int_add(json_row, "helloReceivedFailed", pim_ifp->pim_ifstat_hello_recvfail); + json_object_int_add(json_row, "helloSend", pim_ifp->pim_ifstat_hello_sent); + json_object_int_add(json_row, "hellosendFailed", pim_ifp->pim_ifstat_hello_sendfail); + json_object_int_add(json_row, "helloGenerationId", pim_ifp->pim_generation_id); + json_object_int_add(json_row, "flagMulticastLoop", mloop); + + json_object_int_add(json_row, "effectivePropagationDelay", pim_if_effective_propagation_delay_msec(ifp)); + json_object_int_add(json_row, "effectiveOverrideInterval", pim_if_effective_override_interval_msec(ifp)); + json_object_int_add(json_row, "joinPruneOverrideInterval", pim_if_jp_override_interval_msec(ifp)); + + json_object_int_add(json_row, "propagationDelay", pim_ifp->pim_propagation_delay_msec); + json_object_int_add(json_row, "propagationDelayHighest", pim_ifp->pim_neighbors_highest_propagation_delay_msec); + json_object_int_add(json_row, "overrideInterval", pim_ifp->pim_override_interval_msec); + json_object_int_add(json_row, "overrideIntervalHighest", pim_ifp->pim_neighbors_highest_override_interval_msec); + json_object_object_add(json, ifp->name, json_row); + + } else { + vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE); + vty_out(vty, "State : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE); + if (pim_ifp->update_source.s_addr != INADDR_ANY) { + vty_out(vty, "Use Source : %s%s", inet_ntoa(pim_ifp->update_source), VTY_NEWLINE); + } + if (pim_ifp->sec_addr_list) { + vty_out(vty, "Address : %s (primary)%s", + inet_ntoa(ifaddr), VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) { + vty_out(vty, " %s%s", + inet_ntoa(sec_addr->addr), VTY_NEWLINE); + } + } else { + vty_out(vty, "Address : %s%s", inet_ntoa(ifaddr), VTY_NEWLINE); + } + vty_out(vty, "%s", VTY_NEWLINE); + + // PIM neighbors + print_header = 1; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + + if (print_header) { + vty_out(vty, "PIM Neighbors%s", VTY_NEWLINE); + vty_out(vty, "-------------%s", VTY_NEWLINE); + print_header = 0; + } + + pim_inet4_dump("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); + pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); + pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer); + vty_out(vty, "%-15s : up for %s, holdtime expires in %s%s", neigh_src_str, uptime, expire, VTY_NEWLINE); + } + + if (!print_header) { + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + } + + vty_out(vty, "Designated Router%s", VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + vty_out(vty, "Address : %s%s", dr_str, VTY_NEWLINE); + vty_out(vty, "Priority : %d%s", pim_ifp->pim_dr_priority, VTY_NEWLINE); + vty_out(vty, "Uptime : %s%s", dr_uptime, VTY_NEWLINE); + vty_out(vty, "Elections : %d%s", pim_ifp->pim_dr_election_count, VTY_NEWLINE); + vty_out(vty, "Changes : %d%s", pim_ifp->pim_dr_election_changes, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + // FHR + print_header = 1; + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) { + if (strcmp(ifp->name, up->rpf.source_nexthop.interface->name) == 0) { + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) { + + if (print_header) { + vty_out(vty, "FHR - First Hop Router%s", VTY_NEWLINE); + vty_out(vty, "----------------------%s", VTY_NEWLINE); + print_header = 0; + } + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); + vty_out(vty, "%s : %s is a source, uptime is %s%s", grp_str, src_str, uptime, VTY_NEWLINE); + } + } + } + + if (!print_header) { + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + } + + vty_out(vty, "Hellos%s", VTY_NEWLINE); + vty_out(vty, "------%s", VTY_NEWLINE); + vty_out(vty, "Period : %d%s", pim_ifp->pim_hello_period, VTY_NEWLINE); + vty_out(vty, "Timer : %s%s", hello_timer, VTY_NEWLINE); + vty_out(vty, "StatStart : %s%s", stat_uptime, VTY_NEWLINE); + vty_out(vty, "Receive : %d%s", pim_ifp->pim_ifstat_hello_recv, VTY_NEWLINE); + vty_out(vty, "Receive Failed : %d%s", pim_ifp->pim_ifstat_hello_recvfail, VTY_NEWLINE); + vty_out(vty, "Send : %d%s", pim_ifp->pim_ifstat_hello_sent, VTY_NEWLINE); + vty_out(vty, "Send Failed : %d%s", pim_ifp->pim_ifstat_hello_sendfail, VTY_NEWLINE); + vty_out(vty, "Generation ID : %08x%s", pim_ifp->pim_generation_id, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + pim_print_ifp_flags(vty, ifp, mloop); + + vty_out(vty, "Join Prune Interval%s", VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + vty_out(vty, "LAN Delay : %s%s", pim_if_lan_delay_enabled(ifp) ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "Effective Propagation Delay : %d msec%s", pim_if_effective_propagation_delay_msec(ifp), VTY_NEWLINE); + vty_out(vty, "Effective Override Interval : %d msec%s", pim_if_effective_override_interval_msec(ifp), VTY_NEWLINE); + vty_out(vty, "Join Prune Override Interval : %d msec%s", pim_if_jp_override_interval_msec(ifp), VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "LAN Prune Delay%s", VTY_NEWLINE); + vty_out(vty, "---------------%s", VTY_NEWLINE); + vty_out(vty, "Propagation Delay : %d msec%s", pim_ifp->pim_propagation_delay_msec, VTY_NEWLINE); + vty_out(vty, "Propagation Delay (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_propagation_delay_msec, VTY_NEWLINE); + vty_out(vty, "Override Interval : %d msec%s", pim_ifp->pim_override_interval_msec, VTY_NEWLINE); + vty_out(vty, "Override Interval (Highest) : %d msec%s", pim_ifp->pim_neighbors_highest_override_interval_msec, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } else { + if (!found_ifname) + vty_out (vty, "%% No such interface%s", VTY_NEWLINE); } } -static void pim_show_interfaces(struct vty *vty) +static void pim_show_interfaces(struct vty *vty, u_char uj) { - struct listnode *node; struct interface *ifp; - time_t now; - - now = pim_time_monotonic_sec(); + struct listnode *node; + struct listnode *upnode; + struct pim_interface *pim_ifp; + struct pim_upstream *up; + int fhr = 0; + int pim_nbrs = 0; + json_object *json = NULL; + json_object *json_row = NULL; + json_object *json_tmp; - vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE); + json = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - char uptime[10]; - int mloop; - pim_ifp = ifp->info; if (!pim_ifp) @@ -671,73 +1054,136 @@ static void pim_show_interfaces(struct vty *vty) if (pim_ifp->pim_sock_fd < 0) continue; - ifaddr = pim_ifp->primary_address; + pim_nbrs = pim_ifp->pim_neighbor_list->count; + fhr = 0; - pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation); + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) + if (ifp == up->rpf.source_nexthop.interface) + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) + fhr++; - mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); - - vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", - ifp->name, - inet_ntoa(ifaddr), - ifp->ifindex, - pim_ifp->pim_sock_fd, - uptime, - if_is_multicast(ifp) ? "yes" : "no", - if_is_broadcast(ifp) ? "yes" : "no", - (mloop < 0) ? "?" : (mloop ? "yes" : "no"), - (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", - (ifp->flags & IFF_PROMISC) ? "yes" : "no", - PIM_IF_IS_DELETED(ifp) ? "yes" : "no", - VTY_NEWLINE); + json_row = json_object_new_object(); + json_object_pim_ifp_add(json_row, ifp); + json_object_int_add(json_row, "pimNeighbors", pim_nbrs); + json_object_int_add(json_row, "firstHopRouter", fhr); + json_object_string_add(json_row, "pimDesignatedRouter", inet_ntoa(pim_ifp->pim_dr_addr)); + + if (pim_ifp->pim_dr_addr.s_addr == pim_ifp->primary_address.s_addr) + json_object_boolean_true_add(json_row, "pimDesignatedRouterLocal"); + + json_object_object_add(json, ifp->name, json_row); } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + } else { + vty_out(vty, "Interface State Address PIM Nbrs PIM DR FHR%s", VTY_NEWLINE); + + json_object_object_foreach(json, key, val) { + vty_out(vty, "%-9s ", key); + + json_object_object_get_ex(val, "state", &json_tmp); + vty_out(vty, "%5s ", json_object_get_string(json_tmp)); + + json_object_object_get_ex(val, "address", &json_tmp); + vty_out(vty, "%15s ", json_object_get_string(json_tmp)); + + json_object_object_get_ex(val, "pimNeighbors", &json_tmp); + vty_out(vty, "%8d ", json_object_get_int(json_tmp)); + + if (json_object_object_get_ex(val, "pimDesignatedRouterLocal", &json_tmp)) { + vty_out(vty, "%15s ", "local"); + } else { + json_object_object_get_ex(val, "pimDesignatedRouter", &json_tmp); + vty_out(vty, "%15s ", json_object_get_string(json_tmp)); + } + + json_object_object_get_ex(val, "firstHopRouter", &json_tmp); + vty_out(vty, "%3d%s", json_object_get_int(json_tmp), VTY_NEWLINE); + } + } + + json_object_free(json); } -static void pim_show_join(struct vty *vty) +static void pim_show_join(struct vty *vty, u_char uj) { - struct listnode *ifnode; - struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; time_t now; + json_object *json = NULL; + json_object *json_iface = NULL; + json_object *json_row = NULL; + json_object *json_grp = NULL; now = pim_time_monotonic_sec(); - vty_out(vty, - "Interface Address Source Group State Uptime Expire Prune%s", - VTY_NEWLINE); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Interface Address Source Group State Uptime Expire Prune%s", + VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *ch_node; - struct pim_ifchannel *ch; + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, ch_node, ch)) { - pim_ifp = ifp->info; + pim_ifp = ch->interface->info; if (!pim_ifp) continue; ifaddr = pim_ifp->primary_address; - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { - char ch_src_str[100]; - char ch_grp_str[100]; - char uptime[10]; - char expire[10]; - char prune[10]; + char ch_src_str[INET_ADDRSTRLEN]; + char ch_grp_str[INET_ADDRSTRLEN]; + char uptime[10]; + char expire[10]; + char prune[10]; - pim_inet4_dump("", ch->source_addr, - ch_src_str, sizeof(ch_src_str)); - pim_inet4_dump("", ch->group_addr, - ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->sg.src, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->sg.grp, + ch_grp_str, sizeof(ch_grp_str)); - pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation); - pim_time_timer_to_mmss(expire, sizeof(expire), - ch->t_ifjoin_expiry_timer); - pim_time_timer_to_mmss(prune, sizeof(prune), - ch->t_ifjoin_prune_pending_timer); + pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation); + pim_time_timer_to_mmss(expire, sizeof(expire), + ch->t_ifjoin_expiry_timer); + pim_time_timer_to_mmss(prune, sizeof(prune), + ch->t_ifjoin_prune_pending_timer); + if (uj) { + json_object_object_get_ex(json, ch->interface->name, &json_iface); + + if (!json_iface) { + json_iface = json_object_new_object(); + json_object_pim_ifp_add(json_iface, ch->interface); + json_object_object_add(json, ch->interface->name, json_iface); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", ch_src_str); + json_object_string_add(json_row, "group", ch_grp_str); + json_object_string_add(json_row, "upTime", uptime); + json_object_string_add(json_row, "expire", expire); + json_object_string_add(json_row, "prune", prune); + json_object_string_add(json_row, "channelJoinName", pim_ifchannel_ifjoin_name(ch->ifjoin_state)); + if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) + json_object_int_add(json_row, "SGRpt", 1); + + json_object_object_get_ex(json_iface, ch_grp_str, &json_grp); + if (!json_grp) + { + json_grp = json_object_new_object(); + json_object_object_add(json_grp, ch_src_str, json_row); + json_object_object_add(json_iface, ch_grp_str, json_grp); + } + else + json_object_object_add(json_grp, ch_src_str, json_row); + } else { vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s", - ifp->name, + ch->interface->name, inet_ntoa(ifaddr), ch_src_str, ch_grp_str, @@ -746,172 +1192,366 @@ static void pim_show_join(struct vty *vty) expire, prune, VTY_NEWLINE); - } /* scan interface channels */ - } /* scan interfaces */ + } + } /* scan interface channels */ + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } } -static void pim_show_neighbors(struct vty *vty) +static void pim_show_neighbors_single(struct vty *vty, const char *neighbor, u_char uj) { struct listnode *node; + struct listnode *neighnode; struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; time_t now; - + int found_neighbor = 0; + int option_address_list; + int option_dr_priority; + int option_generation_id; + int option_holdtime; + int option_lan_prune_delay; + int option_t_bit; + char uptime[10]; + char expire[10]; + char neigh_src_str[INET_ADDRSTRLEN]; + + json_object *json = NULL; + json_object *json_ifp = NULL; + json_object *json_row = NULL; + now = pim_time_monotonic_sec(); - vty_out(vty, - "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s" - " T=can_disable_join_suppression%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, "Interface Address Neighbor Uptime Timer Holdt DrPri GenId Recv %s", VTY_NEWLINE); + if (uj) + json = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *neighnode; - struct pim_neighbor *neigh; - pim_ifp = ifp->info; - + if (!pim_ifp) continue; if (pim_ifp->pim_sock_fd < 0) continue; - ifaddr = pim_ifp->primary_address; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + + /* + * The user can specify either the interface name or the PIM neighbor IP. + * If this pim_ifp matches neither then skip. + */ + if (strcmp(neighbor, "detail") && + strcmp(neighbor, ifp->name) && + strcmp(neighbor, neigh_src_str)) + continue; + + found_neighbor = 1; + pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); + pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer); + + option_address_list = 0; + option_dr_priority = 0; + option_generation_id = 0; + option_holdtime = 0; + option_lan_prune_delay = 0; + option_t_bit = 0; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST)) + option_address_list = 1; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY)) + option_dr_priority = 1; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID)) + option_generation_id = 1; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME)) + option_holdtime = 1; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY)) + option_lan_prune_delay = 1; + + if (PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)) + option_t_bit = 1; + + if (uj) { + + /* Does this ifp live in json? If not create it. */ + json_object_object_get_ex(json, ifp->name, &json_ifp); + + if (!json_ifp) { + json_ifp = json_object_new_object(); + json_object_pim_ifp_add(json_ifp, ifp); + json_object_object_add(json, ifp->name, json_ifp); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "interface", ifp->name); + json_object_string_add(json_row, "address", neigh_src_str); + json_object_string_add(json_row, "upTime", uptime); + json_object_string_add(json_row, "holdtime", expire); + json_object_int_add(json_row, "drPriority", neigh->dr_priority); + json_object_int_add(json_row, "generationId", neigh->generation_id); + + if (option_address_list) + json_object_boolean_true_add(json_row, "helloOptionAddressList"); + + if (option_dr_priority) + json_object_boolean_true_add(json_row, "helloOptionDrPriority"); + + if (option_generation_id) + json_object_boolean_true_add(json_row, "helloOptionGenerationId"); + + if (option_holdtime) + json_object_boolean_true_add(json_row, "helloOptionHoldtime"); + + if (option_lan_prune_delay) + json_object_boolean_true_add(json_row, "helloOptionLanPruneDelay"); + + if (option_t_bit) + json_object_boolean_true_add(json_row, "helloOptionTBit"); + + json_object_object_add(json_ifp, neigh_src_str, json_row); + + } else { + vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE); + vty_out(vty, "Neighbor : %s%s", neigh_src_str, VTY_NEWLINE); + vty_out(vty, " Uptime : %s%s", uptime, VTY_NEWLINE); + vty_out(vty, " Holdtime : %s%s", expire, VTY_NEWLINE); + vty_out(vty, " DR Priority : %d%s", neigh->dr_priority, VTY_NEWLINE); + vty_out(vty, " Generation ID : %08x%s", neigh->generation_id, VTY_NEWLINE); + vty_out(vty, " Override Interval (msec) : %d%s", neigh->override_interval_msec, VTY_NEWLINE); + vty_out(vty, " Propagation Delay (msec) : %d%s", neigh->propagation_delay_msec, VTY_NEWLINE); + vty_out(vty, " Hello Option - Address List : %s%s", option_address_list ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Hello Option - DR Priority : %s%s", option_dr_priority ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Hello Option - Generation ID : %s%s", option_generation_id? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Hello Option - Holdtime : %s%s", option_holdtime ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Hello Option - LAN Prune Delay : %s%s", option_lan_prune_delay ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, " Hello Option - T-bit : %s%s", option_t_bit ? "yes" : "no", VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } else { + { + if (!found_neighbor) + vty_out (vty, "%% No such interface or neighbor%s", VTY_NEWLINE); + } + } +} + +static void +pim_show_state(struct vty *vty, const char *src_or_group, const char *group, u_char uj) +{ + struct channel_oil *c_oil; + struct listnode *node; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_ifp_in = NULL; + json_object *json_ifp_out = NULL; + json_object *json_source = NULL; + time_t now; + int first_oif; + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "%sSource Group IIF OIL%s", VTY_NEWLINE, VTY_NEWLINE); + } + + for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) { + char grp_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char in_ifname[16]; + char out_ifname[16]; + int oif_vif_index; + struct interface *ifp_in; + first_oif = 1; + + if (!c_oil->installed) + continue; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, src_str, sizeof(src_str)); + ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + + if (ifp_in) + strcpy(in_ifname, ifp_in->name); + else + strcpy(in_ifname, ""); + + if (src_or_group) + { + if (strcmp(src_or_group, src_str) && strcmp(src_or_group, grp_str)) + continue; + + if (group && strcmp(group, grp_str)) + continue; + } + + if (uj) { + + /* Find the group, create it if it doesn't exist */ + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + /* Find the source nested under the group, create it if it doesn't exist */ + json_object_object_get_ex(json_group, src_str, &json_source); + + if (!json_source) { + json_source = json_object_new_object(); + json_object_object_add(json_group, src_str, json_source); + } + + /* Find the inbound interface nested under the source, create it if it doesn't exist */ + json_object_object_get_ex(json_source, in_ifname, &json_ifp_in); + + if (!json_ifp_in) { + json_ifp_in = json_object_new_object(); + json_object_object_add(json_source, in_ifname, json_ifp_in); + } + } else { + vty_out(vty, "%-15s %-15s %-5s ", + src_str, + grp_str, + ifp_in->name); + } + + for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { + struct interface *ifp_out; + char oif_uptime[10]; + int ttl; + + ttl = c_oil->oil.mfcc_ttls[oif_vif_index]; + if (ttl < 1) + continue; + + ifp_out = pim_if_find_by_vif_index(oif_vif_index); + pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]); + + if (ifp_out) + strcpy(out_ifname, ifp_out->name); + else + strcpy(out_ifname, ""); + + if (uj) { + json_ifp_out = json_object_new_object(); + json_object_string_add(json_ifp_out, "source", src_str); + json_object_string_add(json_ifp_out, "group", grp_str); + json_object_string_add(json_ifp_out, "inboundInterface", in_ifname); + json_object_string_add(json_ifp_out, "outboundInterface", out_ifname); + + json_object_object_add(json_ifp_in, out_ifname, json_ifp_out); + } else { + if (first_oif) + { + first_oif = 0; + vty_out(vty, "%s", out_ifname); + } + else + vty_out(vty, ",%s", out_ifname); + } + } + + if (!uj) + vty_out(vty, "%s", VTY_NEWLINE); + } + + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } else { + vty_out(vty, "%s", VTY_NEWLINE); + } +} + +static void pim_show_neighbors(struct vty *vty, u_char uj) +{ + struct listnode *node; + struct listnode *neighnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + time_t now; + char uptime[10]; + char expire[10]; + char neigh_src_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + json_object *json_ifp_rows = NULL; + json_object *json_row = NULL; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Interface Neighbor Uptime Holdtime DR Pri%s", VTY_NEWLINE); + } + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + if (uj) + json_ifp_rows = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { - char uptime[10]; - char holdtime[10]; - char expire[10]; - char neigh_src_str[100]; - char recv[7]; - pim_inet4_dump("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); - pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime); - pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer); + pim_time_timer_to_hhmmss(expire, sizeof(expire), neigh->t_expire_timer); - recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME) ? 'H' : ' '; - recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? 'L' : ' '; - recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) ? 'P' : ' '; - recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ? 'G' : ' '; - recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST) ? 'A' : ' '; - recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' '; - recv[6] = '\0'; + if (uj) { + json_row = json_object_new_object(); + json_object_string_add(json_row, "interface", ifp->name); + json_object_string_add(json_row, "neighbor", neigh_src_str); + json_object_string_add(json_row, "upTime", uptime); + json_object_string_add(json_row, "holdTime", expire); + json_object_int_add(json_row, "holdTimeMax", neigh->holdtime); + json_object_int_add(json_row, "drPriority", neigh->dr_priority); + json_object_object_add(json_ifp_rows, neigh_src_str, json_row); - vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s", - ifp->name, - inet_ntoa(ifaddr), - neigh_src_str, - uptime, - expire, - holdtime, - neigh->dr_priority, - neigh->generation_id, - recv, - VTY_NEWLINE); + } else { + vty_out(vty, "%-9s %15s %8s %8s %6d%s", + ifp->name, + neigh_src_str, + uptime, + expire, + neigh->dr_priority, + VTY_NEWLINE); + } } - - } -} - -static void pim_show_lan_prune_delay(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - - vty_out(vty, - "PrDly=propagation_delay (msec) OvInt=override_interval (msec)%s" - "HiDly=highest_propagation_delay (msec) HiInt=highest_override_interval (msec)%s" - "NoDly=number_of_non_lan_delay_neighbors%s" - "T=t_bit LPD=lan_prune_delay_hello_option%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T | Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE); - - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - struct listnode *neighnode; - struct pim_neighbor *neigh; - - pim_ifp = ifp->info; - - if (!pim_ifp) - continue; - - if (pim_ifp->pim_sock_fd < 0) - continue; - - ifaddr = pim_ifp->primary_address; - - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { - char neigh_src_str[100]; - - pim_inet4_dump("", neigh->source_addr, - neigh_src_str, sizeof(neigh_src_str)); - - vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s", - ifp->name, - inet_ntoa(ifaddr), - pim_ifp->pim_propagation_delay_msec, - pim_ifp->pim_override_interval_msec, - pim_ifp->pim_number_of_nonlandelay_neighbors, - pim_ifp->pim_neighbors_highest_propagation_delay_msec, - pim_ifp->pim_neighbors_highest_override_interval_msec, - PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)), - neigh_src_str, - PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no", - neigh->propagation_delay_msec, - neigh->override_interval_msec, - PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options, - PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)), - VTY_NEWLINE); + if (uj) { + json_object_object_add(json, ifp->name, json_ifp_rows); + json_ifp_rows = NULL; } - } -} -static void pim_show_jp_override_interval(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - - vty_out(vty, - "EffPDelay=effective_propagation_delay (msec)%s" - "EffOvrInt=override_interval (msec)%s" - "JPOvrInt=jp_override_interval (msec)%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE); - - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp; - struct in_addr ifaddr; - - pim_ifp = ifp->info; - - if (!pim_ifp) - continue; - - if (pim_ifp->pim_sock_fd < 0) - continue; - - ifaddr = pim_ifp->primary_address; - - vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s", - ifp->name, - inet_ntoa(ifaddr), - pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled", - pim_if_effective_propagation_delay_msec(ifp), - pim_if_effective_override_interval_msec(ifp), - pim_if_jp_override_interval_msec(ifp), - VTY_NEWLINE); + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } } @@ -939,7 +1579,7 @@ static void pim_show_neighbors_secondary(struct vty *vty) ifaddr = pim_ifp->primary_address; for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { - char neigh_src_str[100]; + char neigh_src_str[INET_ADDRSTRLEN]; struct listnode *prefix_node; struct prefix *p; @@ -950,7 +1590,7 @@ static void pim_show_neighbors_secondary(struct vty *vty) neigh_src_str, sizeof(neigh_src_str)); for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) { - char neigh_sec_str[100]; + char neigh_sec_str[INET_ADDRSTRLEN]; if (p->family != AF_INET) continue; @@ -969,68 +1609,169 @@ static void pim_show_neighbors_secondary(struct vty *vty) } } -static void pim_show_upstream(struct vty *vty) +static void +json_object_pim_upstream_add (json_object *json, struct pim_upstream *up) +{ + if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) + json_object_boolean_true_add(json, "drJoinDesired"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + json_object_boolean_true_add(json, "drJoinDesiredUpdated"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) + json_object_boolean_true_add(json, "firstHopRouter"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) + json_object_boolean_true_add(json, "sourceIgmp"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_PIM) + json_object_boolean_true_add(json, "sourcePim"); + + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) + json_object_boolean_true_add(json, "sourceStream"); + + /* XXX: need to print ths flag in the plain text display as well */ + if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) + json_object_boolean_true_add(json, "sourceMsdp"); +} + +static void pim_show_upstream(struct vty *vty, u_char uj) { struct listnode *upnode; struct pim_upstream *up; time_t now; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; now = pim_time_monotonic_sec(); - vty_out(vty, "Iif Source Group State Uptime JoinTimer RefCnt%s", VTY_NEWLINE); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, "Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt%s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { - char src_str[100]; - char grp_str[100]; - char uptime[10]; - char join_timer[10]; + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) { + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char uptime[10]; + char join_timer[10]; + char rs_timer[10]; + char ka_timer[10]; + char msdp_reg_timer[10]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); - pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); + pim_time_timer_to_hhmmss (join_timer, sizeof(join_timer), up->t_join_timer); + pim_time_timer_to_hhmmss (rs_timer, sizeof (rs_timer), up->t_rs_timer); + pim_time_timer_to_hhmmss (ka_timer, sizeof (ka_timer), up->t_ka_timer); + pim_time_timer_to_hhmmss (msdp_reg_timer, sizeof (msdp_reg_timer), up->t_msdp_reg_timer); - vty_out(vty, "%-10s%-15s %-15s %-5s %-8s %-9s %6d%s", - up->rpf.source_nexthop.interface->name, - src_str, - grp_str, - up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd", - uptime, - join_timer, - up->ref_count, - VTY_NEWLINE); + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "inboundInterface", up->rpf.source_nexthop.interface->name); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "state", pim_upstream_state2str (up->join_state)); + json_object_string_add(json_row, "upTime", uptime); + json_object_string_add(json_row, "joinTimer", join_timer); + json_object_string_add(json_row, "resetTimer", rs_timer); + json_object_string_add(json_row, "keepaliveTimer", ka_timer); + json_object_string_add(json_row, "msdpRegTimer", msdp_reg_timer); + json_object_int_add(json_row, "refCount", up->ref_count); + json_object_int_add(json_row, "sptBit", up->sptbit); + json_object_object_add(json_group, src_str, json_row); + } else { + vty_out(vty, "%-10s%-15s %-15s %-11s %-8s %-9s %-9s %-9s %6d%s", + up->rpf.source_nexthop.interface->name, + src_str, + grp_str, + pim_upstream_state2str (up->join_state), + uptime, + join_timer, + rs_timer, + ka_timer, + up->ref_count, + VTY_NEWLINE); + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } } -static void pim_show_join_desired(struct vty *vty) +static void pim_show_join_desired(struct vty *vty, u_char uj) { - struct listnode *ifnode; struct listnode *chnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - char src_str[100]; - char grp_str[100]; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; - vty_out(vty, - "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s", - VTY_NEWLINE); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s", + VTY_NEWLINE); - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - pim_ifp = ifp->info; + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch)) { + /* scan all interfaces */ + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) { - struct pim_upstream *up = ch->upstream; + struct pim_upstream *up = ch->upstream; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "interface", ch->interface->name); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + if (pim_macro_ch_lost_assert(ch)) + json_object_boolean_true_add(json_row, "lostAssert"); + + if (pim_macro_chisin_joins(ch)) + json_object_boolean_true_add(json_row, "joins"); + + if (pim_macro_chisin_pim_include(ch)) + json_object_boolean_true_add(json_row, "pimInclude"); + + if (pim_upstream_evaluate_join_desired(up)) + json_object_boolean_true_add(json_row, "evaluateJoinDesired"); + + json_object_object_add(json_group, src_str, json_row); + + } else { vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s", - ifp->name, + ch->interface->name, src_str, grp_str, pim_macro_ch_lost_assert(ch) ? "yes" : "no", @@ -1041,61 +1782,109 @@ static void pim_show_join_desired(struct vty *vty) VTY_NEWLINE); } } -} -static void pim_show_upstream_rpf(struct vty *vty) -{ - struct listnode *upnode; - struct pim_upstream *up; - - vty_out(vty, - "Source Group RpfIface RibNextHop RpfAddress %s", - VTY_NEWLINE); - - for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { - char src_str[100]; - char grp_str[100]; - char rpf_nexthop_str[100]; - char rpf_addr_str[100]; - struct pim_rpf *rpf; - const char *rpf_ifname; - - rpf = &up->rpf; - - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); - pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); - - rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; - - vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s", - src_str, - grp_str, - rpf_ifname, - rpf_nexthop_str, - rpf_addr_str, - VTY_NEWLINE); + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } } -static void show_rpf_refresh_stats(struct vty *vty, time_t now) +static void pim_show_upstream_rpf(struct vty *vty, u_char uj) +{ + struct listnode *upnode; + struct pim_upstream *up; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Source Group RpfIface RibNextHop RpfAddress %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, upnode, up)) { + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rpf_nexthop_str[PREFIX_STRLEN]; + char rpf_addr_str[PREFIX_STRLEN]; + struct pim_rpf *rpf; + const char *rpf_ifname; + + rpf = &up->rpf; + + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); + pim_addr_dump("", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "rpfInterface", rpf_ifname); + json_object_string_add(json_row, "ribNexthop", rpf_nexthop_str); + json_object_string_add(json_row, "rpfAddress", rpf_addr_str); + json_object_object_add(json_group, src_str, json_row); + } else { + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s", + src_str, + grp_str, + rpf_ifname, + rpf_nexthop_str, + rpf_addr_str, + VTY_NEWLINE); + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} + +static void show_rpf_refresh_stats(struct vty *vty, time_t now, json_object *json) { char refresh_uptime[10]; pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last); - vty_out(vty, - "RPF Cache Refresh Delay: %ld msecs%s" - "RPF Cache Refresh Timer: %ld msecs%s" - "RPF Cache Refresh Requests: %lld%s" - "RPF Cache Refresh Events: %lld%s" - "RPF Cache Refresh Last: %s%s", - qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, - pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, - (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE, - (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE, - refresh_uptime, VTY_NEWLINE); + if (json) { + json_object_int_add(json, "rpfCacheRefreshDelayMsecs", qpim_rpf_cache_refresh_delay_msec); + json_object_int_add(json, "rpfCacheRefreshTimer", pim_time_timer_remain_msec(qpim_rpf_cache_refresher)); + json_object_int_add(json, "rpfCacheRefreshRequests", qpim_rpf_cache_refresh_requests); + json_object_int_add(json, "rpfCacheRefreshEvents", qpim_rpf_cache_refresh_events); + json_object_string_add(json, "rpfCacheRefreshLast", refresh_uptime); + json_object_int_add(json, "nexthopLookups", qpim_nexthop_lookups); + json_object_int_add(json, "nexthopLookupsAvoided", nexthop_lookups_avoided); + } else { + vty_out(vty, + "RPF Cache Refresh Delay: %ld msecs%s" + "RPF Cache Refresh Timer: %ld msecs%s" + "RPF Cache Refresh Requests: %lld%s" + "RPF Cache Refresh Events: %lld%s" + "RPF Cache Refresh Last: %s%s" + "Nexthop Lookups: %lld%s" + "Nexthop Lookups Avoided: %lld%s", + qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, + pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE, + refresh_uptime, VTY_NEWLINE, + (long long) qpim_nexthop_lookups, VTY_NEWLINE, + (long long)nexthop_lookups_avoided, VTY_NEWLINE); + } } static void show_scan_oil_stats(struct vty *vty, time_t now) @@ -1117,90 +1906,93 @@ static void show_scan_oil_stats(struct vty *vty, time_t now) uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE); } -static void pim_show_rpf(struct vty *vty) +static void pim_show_rpf(struct vty *vty, u_char uj) { struct listnode *up_node; struct pim_upstream *up; time_t now = pim_time_monotonic_sec(); + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; - show_rpf_refresh_stats(vty, now); + if (uj) { + json = json_object_new_object(); + show_rpf_refresh_stats(vty, now, json); + } else { + show_rpf_refresh_stats(vty, now, json); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, + "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s", + VTY_NEWLINE); + } - vty_out(vty, "%s", VTY_NEWLINE); - - vty_out(vty, - "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s", - VTY_NEWLINE); - - for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { - char src_str[100]; - char grp_str[100]; - char rpf_addr_str[100]; - char rib_nexthop_str[100]; + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) { + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rpf_addr_str[PREFIX_STRLEN]; + char rib_nexthop_str[PREFIX_STRLEN]; const char *rpf_ifname; struct pim_rpf *rpf = &up->rpf; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); + pim_inet4_dump("", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", up->sg.grp, grp_str, sizeof(grp_str)); + pim_addr_dump("", &rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; - vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s", - src_str, - grp_str, - rpf_ifname, - rpf_addr_str, - rib_nexthop_str, - rpf->source_nexthop.mrib_route_metric, - rpf->source_nexthop.mrib_metric_preference, - VTY_NEWLINE); - } -} + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); -static void igmp_show_querier(struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } - vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE); + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "rpfInterface", rpf_ifname); + json_object_string_add(json_row, "rpfAddress", rpf_addr_str); + json_object_string_add(json_row, "ribNexthop", rib_nexthop_str); + json_object_int_add(json_row, "routeMetric", rpf->source_nexthop.mrib_route_metric); + json_object_int_add(json_row, "routePreference", rpf->source_nexthop.mrib_metric_preference); + json_object_object_add(json_group, src_str, json_row); - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) { - struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; - - if (!pim_ifp) - continue; - - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char query_hhmmss[10]; - char other_hhmmss[10]; - - pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); - pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer); - - vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s", - ifp->name, - inet_ntoa(igmp->ifaddr), - igmp->t_igmp_query_timer ? "THIS" : "OTHER", - igmp->startup_query_count, - query_hhmmss, - other_hhmmss, - VTY_NEWLINE); + } else { + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s", + src_str, + grp_str, + rpf_ifname, + rpf_addr_str, + rib_nexthop_str, + rpf->source_nexthop.mrib_route_metric, + rpf->source_nexthop.mrib_metric_preference, + VTY_NEWLINE); } } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } } -static void igmp_show_groups(struct vty *vty) +static void igmp_show_groups(struct vty *vty, u_char uj) { struct listnode *ifnode; struct interface *ifp; time_t now; + json_object *json = NULL; + json_object *json_iface = NULL; + json_object *json_row = NULL; now = pim_time_monotonic_sec(); - vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE); + if (uj) + json = json_object_new_object(); + else + vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE); /* scan interfaces */ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { @@ -1213,7 +2005,7 @@ static void igmp_show_groups(struct vty *vty) /* scan igmp sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; struct listnode *grpnode; struct igmp_group *grp; @@ -1221,7 +2013,7 @@ static void igmp_show_groups(struct vty *vty) /* scan igmp groups */ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; char hhmmss[10]; char uptime[10]; @@ -1229,20 +2021,48 @@ static void igmp_show_groups(struct vty *vty) pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer); pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation); - vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s", - ifp->name, - ifaddr_str, - group_str, - grp->group_filtermode_isexcl ? "EXCL" : "INCL", - hhmmss, - grp->group_source_list ? listcount(grp->group_source_list) : 0, - igmp_group_compat_mode(igmp, grp), - uptime, - VTY_NEWLINE); + if (uj) { + json_object_object_get_ex(json, ifp->name, &json_iface); + if (!json_iface) { + json_iface = json_object_new_object(); + json_object_pim_ifp_add(json_iface, ifp); + json_object_object_add(json, ifp->name, json_iface); + } + + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", ifaddr_str); + json_object_string_add(json_row, "group", group_str); + + if (grp->igmp_version == 3) + json_object_string_add(json_row, "mode", grp->group_filtermode_isexcl ? "EXCLUDE" : "INCLUDE"); + + json_object_string_add(json_row, "timer", hhmmss); + json_object_int_add(json_row, "sourcesCount", grp->group_source_list ? listcount(grp->group_source_list) : 0); + json_object_int_add(json_row, "version", grp->igmp_version); + json_object_string_add(json_row, "uptime", uptime); + json_object_object_add(json_iface, group_str, json_row); + + } else { + vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s", + ifp->name, + ifaddr_str, + group_str, + grp->igmp_version == 3 ? (grp->group_filtermode_isexcl ? "EXCL" : "INCL") : "----", + hhmmss, + grp->group_source_list ? listcount(grp->group_source_list) : 0, + grp->igmp_version, + uptime, + VTY_NEWLINE); + } } /* scan igmp groups */ } /* scan igmp sockets */ } /* scan interfaces */ + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } } static void igmp_show_group_retransmission(struct vty *vty) @@ -1263,7 +2083,7 @@ static void igmp_show_group_retransmission(struct vty *vty) /* scan igmp sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; struct listnode *grpnode; struct igmp_group *grp; @@ -1271,7 +2091,7 @@ static void igmp_show_group_retransmission(struct vty *vty) /* scan igmp groups */ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; char grp_retr_mmss[10]; struct listnode *src_node; struct igmp_source *src; @@ -1302,80 +2122,6 @@ static void igmp_show_group_retransmission(struct vty *vty) } /* scan interfaces */ } -static void igmp_show_parameters(struct vty *vty) -{ - struct listnode *ifnode; - struct interface *ifp; - - vty_out(vty, - "QRV: Robustness Variable SQI: Startup Query Interval%s" - "QQI: Query Interval OQPI: Other Querier Present Interval%s" - "QRI: Query Response Interval LMQT: Last Member Query Time%s" - "GMI: Group Membership Interval OHPI: Older Host Present Interval%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, - "Interface Address QRV QQI QRI GMI SQI OQPI LMQT OHPI %s", - VTY_NEWLINE); - - /* scan interfaces */ - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp = ifp->info; - struct listnode *sock_node; - struct igmp_sock *igmp; - - if (!pim_ifp) - continue; - - /* scan igmp sockets */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char ifaddr_str[100]; - long gmi_dsec; /* Group Membership Interval */ - long oqpi_dsec; /* Other Querier Present Interval */ - int sqi; - long lmqt_dsec; - long ohpi_dsec; - long qri_dsec; - - pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); - - gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, - igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec) / 100; - - sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); - - oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, - igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec) / 100; - - lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec, - igmp->querier_robustness_variable) / 100; - - ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, - igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); - - qri_dsec = pim_ifp->igmp_query_max_response_time_dsec; - - vty_out(vty, - "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s", - ifp->name, - ifaddr_str, - igmp->querier_robustness_variable, - igmp->querier_query_interval, - qri_dsec / 10, qri_dsec % 10, - gmi_dsec / 10, gmi_dsec % 10, - sqi, - oqpi_dsec / 10, oqpi_dsec % 10, - lmqt_dsec / 10, lmqt_dsec % 10, - ohpi_dsec / 10, ohpi_dsec % 10, - VTY_NEWLINE); - - } /* scan igmp sockets */ - } /* scan interfaces */ -} - static void igmp_show_sources(struct vty *vty) { struct listnode *ifnode; @@ -1397,7 +2143,7 @@ static void igmp_show_sources(struct vty *vty) /* scan igmp sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; struct listnode *grpnode; struct igmp_group *grp; @@ -1405,7 +2151,7 @@ static void igmp_show_sources(struct vty *vty) /* scan igmp groups */ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; struct listnode *srcnode; struct igmp_source *src; @@ -1413,7 +2159,7 @@ static void igmp_show_sources(struct vty *vty) /* scan group sources */ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; char mmss[10]; char uptime[10]; @@ -1457,7 +2203,7 @@ static void igmp_show_source_retransmission(struct vty *vty) /* scan igmp sockets */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; struct listnode *grpnode; struct igmp_group *grp; @@ -1465,7 +2211,7 @@ static void igmp_show_source_retransmission(struct vty *vty) /* scan igmp groups */ for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; struct listnode *srcnode; struct igmp_source *src; @@ -1473,7 +2219,7 @@ static void igmp_show_source_retransmission(struct vty *vty) /* scan group sources */ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src->source_addr, source_str, sizeof(source_str)); @@ -1555,11 +2301,11 @@ static void mroute_add_all() struct listnode *node; struct channel_oil *c_oil; - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - if (pim_mroute_add(c_oil)) { + for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) { + if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { /* just log warning */ - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", @@ -1574,11 +2320,11 @@ static void mroute_del_all() struct listnode *node; struct channel_oil *c_oil; - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - if (pim_mroute_del(c_oil)) { + for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) { + if (pim_mroute_del(c_oil, __PRETTY_FUNCTION__)) { /* just log warning */ - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", @@ -1594,10 +2340,10 @@ static void static_mroute_add_all() struct static_route *s_route; for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - if (pim_mroute_add(&s_route->c_oil)) { + if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { /* just log warning */ - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", @@ -1613,10 +2359,10 @@ static void static_mroute_del_all() struct static_route *s_route; for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - if (pim_mroute_del(&s_route->c_oil)) { + if (pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__)) { /* just log warning */ - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", @@ -1667,13 +2413,20 @@ DEFUN (clear_ip_pim_oil, DEFUN (show_ip_igmp_interface, show_ip_igmp_interface_cmd, - "show ip igmp interface", + "show ip igmp interface [detail|WORD] [json]", SHOW_STR IP_STR IGMP_STR - "IGMP interface information\n") + "IGMP interface information\n" + "Detailed output\n" + "interface name\n" + "JavaScript Object Notation\n") { - igmp_show_interfaces(vty); + u_char uj = use_json(argc, argv); + if (argv[4]->arg) + igmp_show_interfaces_single(vty, argv[4]->arg, uj); + else + igmp_show_interfaces(vty, uj); return CMD_SUCCESS; } @@ -1693,13 +2446,15 @@ DEFUN (show_ip_igmp_join, DEFUN (show_ip_igmp_groups, show_ip_igmp_groups_cmd, - "show ip igmp groups", + "show ip igmp groups [json]", SHOW_STR IP_STR IGMP_STR - IGMP_GROUP_STR) + IGMP_GROUP_STR + "JavaScript Object Notation\n") { - igmp_show_groups(vty); + u_char uj = use_json(argc, argv); + igmp_show_groups(vty, uj); return CMD_SUCCESS; } @@ -1718,19 +2473,6 @@ DEFUN (show_ip_igmp_groups_retransmissions, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_parameters, - show_ip_igmp_parameters_cmd, - "show ip igmp parameters", - SHOW_STR - IP_STR - IGMP_STR - "IGMP parameters information\n") -{ - igmp_show_parameters(vty); - - return CMD_SUCCESS; -} - DEFUN (show_ip_igmp_sources, show_ip_igmp_sources_cmd, "show ip igmp sources", @@ -1758,32 +2500,6 @@ DEFUN (show_ip_igmp_sources_retransmissions, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_querier, - show_ip_igmp_querier_cmd, - "show ip igmp querier", - SHOW_STR - IP_STR - IGMP_STR - "IGMP querier information\n") -{ - igmp_show_querier(vty); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_address, - show_ip_pim_address_cmd, - "show ip pim address", - SHOW_STR - IP_STR - PIM_STR - "PIM interface address\n") -{ - show_interface_address(vty); - - return CMD_SUCCESS; -} - DEFUN (show_ip_pim_assert, show_ip_pim_assert_cmd, "show ip pim assert", @@ -1836,106 +2552,72 @@ DEFUN (show_ip_pim_assert_winner_metric, return CMD_SUCCESS; } -DEFUN (show_ip_pim_dr, - show_ip_pim_dr_cmd, - "show ip pim designated-router", - SHOW_STR - IP_STR - PIM_STR - "PIM interface designated router\n") -{ - pim_show_dr(vty); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_hello, - show_ip_pim_hello_cmd, - "show ip pim hello", - SHOW_STR - IP_STR - PIM_STR - "PIM interface hello information\n") -{ - pim_show_hello(vty); - - return CMD_SUCCESS; -} - DEFUN (show_ip_pim_interface, show_ip_pim_interface_cmd, - "show ip pim interface", + "show ip pim interface [detail|WORD] [json]", SHOW_STR IP_STR PIM_STR - "PIM interface information\n") + "PIM interface information\n" + "Detailed output\n" + "interface name\n" + "JavaScript Object Notation\n") { - pim_show_interfaces(vty); + u_char uj = use_json(argc, argv); + if (argv[4]->arg) + pim_show_interfaces_single(vty, argv[4]->arg, uj); + else + pim_show_interfaces(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_join, show_ip_pim_join_cmd, - "show ip pim join", + "show ip pim join [json]", SHOW_STR IP_STR PIM_STR - "PIM interface join information\n") + "PIM interface join information\n" + JSON_STR) { - pim_show_join(vty); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_lan_prune_delay, - show_ip_pim_lan_prune_delay_cmd, - "show ip pim lan-prune-delay", - SHOW_STR - IP_STR - PIM_STR - "PIM neighbors LAN prune delay parameters\n") -{ - pim_show_lan_prune_delay(vty); + u_char uj = use_json(argc, argv); + pim_show_join(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_local_membership, show_ip_pim_local_membership_cmd, - "show ip pim local-membership", + "show ip pim local-membership [json]", SHOW_STR IP_STR PIM_STR - "PIM interface local-membership\n") + "PIM interface local-membership\n" + JSON_STR) { - pim_show_membership(vty); - - return CMD_SUCCESS; -} - -DEFUN (show_ip_pim_jp_override_interval, - show_ip_pim_jp_override_interval_cmd, - "show ip pim jp-override-interval", - SHOW_STR - IP_STR - PIM_STR - "PIM interface J/P override interval\n") -{ - pim_show_jp_override_interval(vty); + u_char uj = use_json(argc, argv); + pim_show_membership(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_neighbor, show_ip_pim_neighbor_cmd, - "show ip pim neighbor", + "show ip pim neighbor [detail|WORD] [json]", SHOW_STR IP_STR PIM_STR - "PIM neighbor information\n") + "PIM neighbor information\n" + "Detailed output\n" + "Name of interface or neighbor\n" + "JavaScript Object Notation\n") { - pim_show_neighbors(vty); + u_char uj = use_json(argc, argv); + if (argv[4]->arg) + pim_show_neighbors_single(vty, argv[4]->arg, uj); + else + pim_show_neighbors(vty, uj); return CMD_SUCCESS; } @@ -1953,54 +2635,100 @@ DEFUN (show_ip_pim_secondary, return CMD_SUCCESS; } -DEFUN (show_ip_pim_upstream, - show_ip_pim_upstream_cmd, - "show ip pim upstream", +DEFUN (show_ip_pim_state, + show_ip_pim_state_cmd, + "show ip pim state [A.B.C.D] [A.B.C.D] [json]", SHOW_STR IP_STR PIM_STR - "PIM upstream information\n") + "PIM state information\n" + "Unicast or Multicast address\n" + "Multicast address\n" + "JavaScript Object Notation\n") { - pim_show_upstream(vty); + const char *src_or_group = NULL; + const char *group = NULL; + u_char uj = use_json(argc, argv); + + src_or_group = argv[4]->arg; + group = argv[5]->arg; + + pim_show_state(vty, src_or_group, group, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream, + show_ip_pim_upstream_cmd, + "show ip pim upstream [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream information\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + pim_show_upstream(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_upstream_join_desired, show_ip_pim_upstream_join_desired_cmd, - "show ip pim upstream-join-desired", + "show ip pim upstream-join-desired [json]", SHOW_STR IP_STR PIM_STR - "PIM upstream join-desired\n") + "PIM upstream join-desired\n" + "JavaScript Object Notation\n") { - pim_show_join_desired(vty); + u_char uj = use_json(argc, argv); + pim_show_join_desired(vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_upstream_rpf, show_ip_pim_upstream_rpf_cmd, - "show ip pim upstream-rpf", + "show ip pim upstream-rpf [json]", SHOW_STR IP_STR PIM_STR - "PIM upstream source rpf\n") + "PIM upstream source rpf\n" + "JavaScript Object Notation\n") { - pim_show_upstream_rpf(vty); + u_char uj = use_json(argc, argv); + pim_show_upstream_rpf(vty, uj); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_rp, + show_ip_pim_rp_cmd, + "show ip pim rp-info [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM RP information\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + pim_rp_show_information (vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_rpf, show_ip_pim_rpf_cmd, - "show ip pim rpf", + "show ip pim rpf [json]", SHOW_STR IP_STR PIM_STR - "PIM cached source rpf information\n") + "PIM cached source rpf information\n" + "JavaScript Object Notation\n") { - pim_show_rpf(vty); + u_char uj = use_json(argc, argv); + pim_show_rpf(vty, uj); return CMD_SUCCESS; } @@ -2088,14 +2816,8 @@ DEFUN (show_ip_multicast, else { vty_out(vty, "%s", VTY_NEWLINE); } - vty_out(vty, "Zclient lookup socket: "); - if (qpim_zclient_lookup) { - vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock, - qpim_zclient_lookup->fail, VTY_NEWLINE); - } - else { - vty_out(vty, "%s", VTY_NEWLINE); - } + + pim_zlookup_show_ip_multicast (vty); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, "Current highest VifIndex: %d%s", @@ -2115,7 +2837,7 @@ DEFUN (show_ip_multicast, vty_out(vty, "%s", VTY_NEWLINE); - show_rpf_refresh_stats(vty, now); + show_rpf_refresh_stats(vty, now, NULL); vty_out(vty, "%s", VTY_NEWLINE); @@ -2126,127 +2848,299 @@ DEFUN (show_ip_multicast, return CMD_SUCCESS; } -static void show_mroute(struct vty *vty) +static void show_mroute(struct vty *vty, u_char uj) { struct listnode *node; struct channel_oil *c_oil; struct static_route *s_route; time_t now; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_source = NULL; + json_object *json_oil = NULL; + json_object *json_ifp_out = NULL; + int found_oif = 0; + int first = 1; - vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC O=SOURCE%s%s", VTY_NEWLINE, VTY_NEWLINE); - - vty_out(vty, "Source Group Proto Input iVifI Output oVifI TTL Uptime %s", - VTY_NEWLINE); + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Source Group Proto Input Output TTL Uptime%s", + VTY_NEWLINE); + } now = pim_time_monotonic_sec(); /* print list of PIM and IGMP routes */ - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - char group_str[100]; - char source_str[100]; + for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) { + char grp_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char in_ifname[16]; + char out_ifname[16]; int oif_vif_index; - - if (!c_oil->installed) + char proto[100]; + struct interface *ifp_in; + found_oif = 0; + first = 1; + if (!c_oil->installed && !uj) continue; - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, src_str, sizeof(src_str)); + ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + + if (ifp_in) + strcpy(in_ifname, ifp_in->name); + else + strcpy(in_ifname, ""); + + if (uj) { + + /* Find the group, create it if it doesn't exist */ + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + /* Find the source nested under the group, create it if it doesn't exist */ + json_object_object_get_ex(json_group, src_str, &json_source); + + if (!json_source) { + json_source = json_object_new_object(); + json_object_object_add(json_group, src_str, json_source); + } + + /* Find the inbound interface nested under the source, create it if it doesn't exist */ + json_object_int_add(json_source, "installed", c_oil->installed); + json_object_int_add(json_source, "refCount", c_oil->oil_ref_count); + json_object_int_add(json_source, "oilSize", c_oil->oil_size); + json_object_int_add(json_source, "OilInheritedRescan", c_oil->oil_inherited_rescan); + json_object_string_add(json_source, "iif", in_ifname); + json_oil = NULL; + } + for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { - struct interface *ifp_in; struct interface *ifp_out; char oif_uptime[10]; int ttl; - char proto[5]; ttl = c_oil->oil.mfcc_ttls[oif_vif_index]; if (ttl < 1) continue; - ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); ifp_out = pim_if_find_by_vif_index(oif_vif_index); - pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]); + found_oif = 1; - proto[0] = '\0'; - if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { - strcat(proto, "P"); - } - if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { - strcat(proto, "I"); - } - if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) { - strcat(proto, "O"); - } + if (ifp_out) + strcpy(out_ifname, ifp_out->name); + else + strcpy(out_ifname, ""); - vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", - source_str, - group_str, - proto, - ifp_in ? ifp_in->name : "", - c_oil->oil.mfcc_parent, - ifp_out ? ifp_out->name : "", - oif_vif_index, - ttl, - oif_uptime, - VTY_NEWLINE); + if (uj) { + json_ifp_out = json_object_new_object(); + json_object_string_add(json_ifp_out, "source", src_str); + json_object_string_add(json_ifp_out, "group", grp_str); + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) + json_object_boolean_true_add(json_ifp_out, "protocolPim"); + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) + json_object_boolean_true_add(json_ifp_out, "protocolIgmp"); + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) + json_object_boolean_true_add(json_ifp_out, "protocolSource"); + + json_object_string_add(json_ifp_out, "inboundInterface", in_ifname); + json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent); + json_object_string_add(json_ifp_out, "outboundInterface", out_ifname); + json_object_int_add(json_ifp_out, "oVifI", oif_vif_index); + json_object_int_add(json_ifp_out, "ttl", ttl); + json_object_string_add(json_ifp_out, "upTime", oif_uptime); + if (!json_oil) { + json_oil = json_object_new_object(); + json_object_object_add(json_source, "oil", json_oil); + } + json_object_object_add(json_oil, out_ifname, json_ifp_out); + } else { + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { + strcpy(proto, "PIM"); + } + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { + strcpy(proto, "IGMP"); + } + + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_SOURCE) { + strcpy(proto, "SRC"); + } + + vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s", + src_str, + grp_str, + proto, + in_ifname, + out_ifname, + ttl, + oif_uptime, + VTY_NEWLINE); + + if (first) + { + src_str[0] = '\0'; + grp_str[0] = '\0'; + in_ifname[0] = '\0'; + first = 0; + } + } + } + + if (!uj && !found_oif) { + vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s", + src_str, + grp_str, + "none", + in_ifname, + "none", + 0, + "--:--:--", + VTY_NEWLINE); } } /* Print list of static routes */ for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - char group_str[100]; - char source_str[100]; + char grp_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char in_ifname[16]; + char out_ifname[16]; int oif_vif_index; + struct interface *ifp_in; + char proto[100]; + first = 1; if (!s_route->c_oil.installed) continue; - pim_inet4_dump("", s_route->group, group_str, sizeof(group_str)); - pim_inet4_dump("", s_route->source, source_str, sizeof(source_str)); + pim_inet4_dump("", s_route->group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", s_route->source, src_str, sizeof(src_str)); + ifp_in = pim_if_find_by_vif_index(s_route->iif); + found_oif = 0; + + if (ifp_in) + strcpy(in_ifname, ifp_in->name); + else + strcpy(in_ifname, ""); + + if (uj) { + + /* Find the group, create it if it doesn't exist */ + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + /* Find the source nested under the group, create it if it doesn't exist */ + json_object_object_get_ex(json_group, src_str, &json_source); + + if (!json_source) { + json_source = json_object_new_object(); + json_object_object_add(json_group, src_str, json_source); + } + + json_object_string_add(json_source, "iif", in_ifname); + json_oil = NULL; + } else { + strcpy(proto, "STATIC"); + } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { - struct interface *ifp_in; struct interface *ifp_out; char oif_uptime[10]; int ttl; - char proto[5]; ttl = s_route->oif_ttls[oif_vif_index]; if (ttl < 1) continue; - ifp_in = pim_if_find_by_vif_index(s_route->iif); ifp_out = pim_if_find_by_vif_index(oif_vif_index); - pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - s_route->c_oil.oif_creation[oif_vif_index]); + found_oif = 1; - proto[0] = '\0'; - strcat(proto, "S"); + if (ifp_out) + strcpy(out_ifname, ifp_out->name); + else + strcpy(out_ifname, ""); - vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", - source_str, - group_str, - proto, - ifp_in ? ifp_in->name : "", - s_route->iif, - ifp_out ? ifp_out->name : "", - oif_vif_index, - ttl, - oif_uptime, - VTY_NEWLINE); + if (uj) { + json_ifp_out = json_object_new_object(); + json_object_string_add(json_ifp_out, "source", src_str); + json_object_string_add(json_ifp_out, "group", grp_str); + json_object_boolean_true_add(json_ifp_out, "protocolStatic"); + json_object_string_add(json_ifp_out, "inboundInterface", in_ifname); + json_object_int_add(json_ifp_out, "iVifI", c_oil->oil.mfcc_parent); + json_object_string_add(json_ifp_out, "outboundInterface", out_ifname); + json_object_int_add(json_ifp_out, "oVifI", oif_vif_index); + json_object_int_add(json_ifp_out, "ttl", ttl); + json_object_string_add(json_ifp_out, "upTime", oif_uptime); + if (!json_oil) { + json_oil = json_object_new_object(); + json_object_object_add(json_source, "oil", json_oil); + } + json_object_object_add(json_oil, out_ifname, json_ifp_out); + } else { + vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s", + src_str, + grp_str, + proto, + in_ifname, + out_ifname, + ttl, + oif_uptime, + VTY_NEWLINE); + if (first) + { + src_str[0] = '\0'; + grp_str[0] = '\0'; + in_ifname[0] = '\0'; + first = 0; + } + } } + + if (!uj && !found_oif) { + vty_out(vty, "%-15s %-15s %-6s %-10s %-10s %-3d %8s%s", + src_str, + grp_str, + proto, + in_ifname, + "none", + 0, + "--:--:--", + VTY_NEWLINE); + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } } DEFUN (show_ip_mroute, show_ip_mroute_cmd, - "show ip mroute", + "show ip mroute [json]", SHOW_STR IP_STR - MROUTE_STR) + MROUTE_STR + JSON_STR) { - show_mroute(vty); + u_char uj = use_json(argc, argv); + show_mroute(vty, uj); return CMD_SUCCESS; } @@ -2258,13 +3152,13 @@ static void show_mroute_count(struct vty *vty) vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, "Source Group Packets Bytes WrongIf %s", + vty_out(vty, "Source Group LastUsed Packets Bytes WrongIf %s", VTY_NEWLINE); /* Print PIM and IGMP route counts */ - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - char group_str[100]; - char source_str[100]; + for (ALL_LIST_ELEMENTS_RO(pim_channel_oil_list, node, c_oil)) { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; if (!c_oil->installed) continue; @@ -2274,9 +3168,10 @@ static void show_mroute_count(struct vty *vty) pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s", + vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s", source_str, group_str, + c_oil->cc.lastused/100, c_oil->cc.pktcnt, c_oil->cc.bytecnt, c_oil->cc.wrong_if, @@ -2285,8 +3180,8 @@ static void show_mroute_count(struct vty *vty) /* Print static route counts */ for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; if (!s_route->c_oil.installed) continue; @@ -2296,9 +3191,10 @@ static void show_mroute_count(struct vty *vty) pim_inet4_dump("", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); - vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s", + vty_out(vty, "%-15s %-15s %-8llu %-7ld %-10ld %-7ld%s", source_str, group_str, + s_route->c_oil.cc.lastused, s_route->c_oil.cc.pktcnt, s_route->c_oil.cc.bytecnt, s_route->c_oil.cc.wrong_if, @@ -2330,9 +3226,10 @@ DEFUN (show_ip_rib, struct in_addr addr; const char *addr_str; struct pim_nexthop nexthop; - char nexthop_addr_str[100]; + char nexthop_addr_str[PREFIX_STRLEN]; int result; + memset (&nexthop, 0, sizeof (nexthop)); addr_str = argv[idx_ipv4]->arg; result = inet_pton(AF_INET, addr_str, &addr); if (result <= 0) { @@ -2341,7 +3238,7 @@ DEFUN (show_ip_rib, return CMD_WARNING; } - if (pim_nexthop_lookup(&nexthop, addr, NULL)) { + if (pim_nexthop_lookup(&nexthop, addr, 0)) { vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s", addr_str, VTY_NEWLINE); return CMD_WARNING; @@ -2350,8 +3247,8 @@ DEFUN (show_ip_rib, vty_out(vty, "Address NextHop Interface Metric Preference%s", VTY_NEWLINE); - pim_inet4_dump("", nexthop.mrib_nexthop_addr, - nexthop_addr_str, sizeof(nexthop_addr_str)); + pim_addr_dump("", &nexthop.mrib_nexthop_addr, + nexthop_addr_str, sizeof(nexthop_addr_str)); vty_out(vty, "%-15s %-15s %-9s %6d %10d%s", addr_str, @@ -2379,11 +3276,11 @@ static void show_ssmpingd(struct vty *vty) now = pim_time_monotonic_sec(); for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; char ss_uptime[10]; struct sockaddr_in bind_addr; socklen_t len = sizeof(bind_addr); - char bind_addr_str[100]; + char bind_addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); @@ -2417,43 +3314,237 @@ DEFUN (show_ip_ssmpingd, return CMD_SUCCESS; } +static int +pim_rp_cmd_worker (struct vty *vty, const char *rp, const char *group, const char *plist) +{ + int result; + + result = pim_rp_new (rp, group, plist); + + if (result == PIM_MALLOC_FAIL) + { + vty_out (vty, "%% Out of memory%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_GROUP_BAD_ADDRESS) + { + vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_RP_BAD_ADDRESS) + { + vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_RP_NO_PATH) + { + vty_out (vty, "%% No Path to RP address specified: %s%s", rp, VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_GROUP_OVERLAP) + { + vty_out (vty, "%% Group range specified cannot overlap%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_GROUP_PFXLIST_OVERLAP) + { + vty_out (vty, "%% This group is already covered by a RP prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_RP_PFXLIST_IN_USE) + { + vty_out (vty, "%% The same prefix-list cannot be applied to multiple RPs%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (ip_pim_joinprune_time, + ip_pim_joinprune_time_cmd, + "ip pim join-prune-interval <60-600>", + IP_STR + "pim multicast routing\n" + "Join Prune Send Interval\n" + "Seconds\n") +{ + qpim_t_periodic = atoi(argv[3]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_joinprune_time, + no_ip_pim_joinprune_time_cmd, + "no ip pim join-prune-interval <60-600>", + NO_STR + IP_STR + "pim multicast routing\n" + "Join Prune Send Interval\n" + "Seconds\n") +{ + qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; + return CMD_SUCCESS; +} + +DEFUN (ip_pim_register_suppress, + ip_pim_register_suppress_cmd, + "ip pim register-suppress-time <5-60000>", + IP_STR + "pim multicast routing\n" + "Register Suppress Timer\n" + "Seconds\n") +{ + qpim_keep_alive_time = atoi (argv[3]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_register_suppress, + no_ip_pim_register_suppress_cmd, + "no ip pim register-suppress-time <5-60000>", + NO_STR + IP_STR + "pim multicast routing\n" + "Register Suppress Timer\n" + "Seconds\n") +{ + qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT; + return CMD_SUCCESS; +} + +DEFUN (ip_pim_keep_alive, + ip_pim_keep_alive_cmd, + "ip pim keep-alive-timer <31-60000>", + IP_STR + "pim multicast routing\n" + "Keep alive Timer\n" + "Seconds\n") +{ + qpim_rp_keep_alive_time = atoi (argv[4]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_keep_alive, + no_ip_pim_keep_alive_cmd, + "no ip pim keep-alive-timer <31-60000>", + NO_STR + IP_STR + "pim multicast routing\n" + "Keep alive Timer\n" + "Seconds\n") +{ + qpim_keep_alive_time = PIM_KEEPALIVE_PERIOD; + return CMD_SUCCESS; +} + +DEFUN (ip_pim_packets, + ip_pim_packets_cmd, + "ip pim packets <1-100>", + IP_STR + "pim multicast routing\n" + "packets to process at one time per fd\n" + "Number of packets\n") +{ + qpim_packet_process = atoi (argv[3]->arg); + return CMD_SUCCESS; +} + +DEFUN (no_ip_pim_packets, + no_ip_pim_packets_cmd, + "no ip pim packets <1-100>", + NO_STR + IP_STR + "pim multicast routing\n" + "packets to process at one time per fd\n" + "Number of packets\n") +{ + qpim_packet_process = PIM_DEFAULT_PACKET_PROCESS; + return CMD_SUCCESS; +} + DEFUN (ip_pim_rp, ip_pim_rp_cmd, - "ip pim rp A.B.C.D", + "ip pim rp A.B.C.D [A.B.C.D/M]", IP_STR "pim multicast routing\n" "Rendevous Point\n" - "ip address of RP\n") + "ip address of RP\n" + "Group Address range to cover\n") { int idx_ipv4 = 3; - int result; + return pim_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL); +} - result = inet_pton(AF_INET, argv[idx_ipv4]->arg, &qpim_rp.rpf_addr.s_addr); - if (result <= 0) { - vty_out(vty, "%% Bad RP address specified: %s", argv[idx_ipv4]->arg); - return CMD_WARNING; - } +DEFUN (ip_pim_rp_prefix_list, + ip_pim_rp_prefix_list_cmd, + "ip pim rp A.B.C.D prefix-list WORD", + IP_STR + "pim multicast routing\n" + "Rendevous Point\n" + "ip address of RP\n" + "group prefix-list filter\n" + "Name of a prefix-list\n") +{ + return pim_rp_cmd_worker (vty, argv[3]->arg, NULL, argv[5]->arg); +} - if (pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL) != 0) { - vty_out(vty, "%% No Path to RP address specified: %s", argv[idx_ipv4]->arg); - return CMD_WARNING; - } +static int +pim_no_rp_cmd_worker (struct vty *vty, const char *rp, const char *group, + const char *plist) +{ + int result = pim_rp_del (rp, group, plist); + + if (result == PIM_GROUP_BAD_ADDRESS) + { + vty_out (vty, "%% Bad group address specified: %s%s", group, VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_RP_BAD_ADDRESS) + { + vty_out (vty, "%% Bad RP address specified: %s%s", rp, VTY_NEWLINE); + return CMD_WARNING; + } + + if (result == PIM_RP_NOT_FOUND) + { + vty_out (vty, "%% Unable to find specified RP%s", VTY_NEWLINE); + return CMD_WARNING; + } return CMD_SUCCESS; } DEFUN (no_ip_pim_rp, no_ip_pim_rp_cmd, - "no ip pim rp [A.B.C.D]", + "no ip pim rp A.B.C.D [A.B.C.D/M]", NO_STR IP_STR "pim multicast routing\n" "Rendevous Point\n" - "ip address of RP\n") + "ip address of RP\n" + "Group Address range to cover\n") { - qpim_rp.rpf_addr.s_addr = INADDR_NONE; + int idx_ipv4 = 4; + return pim_no_rp_cmd_worker (vty, argv[idx_ipv4]->arg, argv[idx_ipv4 + 1]->arg, NULL); +} - return CMD_SUCCESS; +DEFUN (no_ip_pim_rp_prefix_list, + no_ip_pim_rp_prefix_list_cmd, + "no ip pim rp A.B.C.D prefix-list WORD", + NO_STR + IP_STR + "pim multicast routing\n" + "Rendevous Point\n" + "ip address of RP\n" + "group prefix-list filter\n" + "Name of a prefix-list\n") +{ + return pim_no_rp_cmd_worker (vty, argv[4]->arg, NULL, argv[6]->arg); } DEFUN (ip_multicast_routing, @@ -2718,7 +3809,7 @@ static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp) pim_ifp = ifp->info; if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: Querier %s on %s reconfig query_interval=%d", __PRETTY_FUNCTION__, @@ -2913,16 +4004,66 @@ DEFUN (interface_no_ip_igmp_query_interval, return CMD_SUCCESS; } -#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1) -#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25) +DEFUN (interface_ip_igmp_version, + interface_ip_igmp_version_cmd, + "ip igmp version <2-3>", + IP_STR + IFACE_IGMP_STR + "IGMP version\n" + "IGMP version number\n") +{ + VTY_DECLVAR_CONTEXT(interface,ifp); + struct pim_interface *pim_ifp; + int igmp_version; + + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + igmp_version = atoi(argv[3]->arg); + pim_ifp->igmp_version = igmp_version; + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_version, + interface_no_ip_igmp_version_cmd, + "no ip igmp version <2-3>", + NO_STR + IP_STR + IFACE_IGMP_STR + "IGMP version\n" + "IGMP version number\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; + + return CMD_SUCCESS; +} + +#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10) +#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250) DEFUN (interface_ip_igmp_query_max_response_time, interface_ip_igmp_query_max_response_time_cmd, - "ip igmp query-max-response-time (1-25)", + "ip igmp query-max-response-time (10-250)", IP_STR IFACE_IGMP_STR IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR - "Query response value in seconds\n") + "Query response value in deci-seconds\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; @@ -2940,26 +4081,7 @@ DEFUN (interface_ip_igmp_query_max_response_time, query_max_response_time = atoi(argv[4]->arg); - /* - It seems we don't need to check bounds since command.c does it - already, but we verify them anyway for extra safety. - */ - if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) { - vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s", - query_max_response_time, - IGMP_QUERY_MAX_RESPONSE_TIME_MIN, - VTY_NEWLINE); - return CMD_WARNING; - } - if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) { - vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s", - query_max_response_time, - IGMP_QUERY_MAX_RESPONSE_TIME_MAX, - VTY_NEWLINE); - return CMD_WARNING; - } - - if (query_max_response_time >= pim_ifp->igmp_default_query_interval) { + if (query_max_response_time >= pim_ifp->igmp_default_query_interval * 10) { vty_out(vty, "Can't set query max response time %d sec >= general query interval %d sec%s", query_max_response_time, pim_ifp->igmp_default_query_interval, @@ -2967,38 +4089,28 @@ DEFUN (interface_ip_igmp_query_max_response_time, return CMD_WARNING; } - change_query_max_response_time(pim_ifp, 10 * query_max_response_time); + change_query_max_response_time(pim_ifp, query_max_response_time); return CMD_SUCCESS; } DEFUN (interface_no_ip_igmp_query_max_response_time, interface_no_ip_igmp_query_max_response_time_cmd, - "no ip igmp query-max-response-time", + "no ip igmp query-max-response-time <10-250>", NO_STR IP_STR IFACE_IGMP_STR - IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR) + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR + "Time for response in deci-seconds\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; - int default_query_interval_dsec; pim_ifp = ifp->info; if (!pim_ifp) return CMD_SUCCESS; - default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; - - if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { - vty_out(vty, - "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", - IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, - VTY_NEWLINE); - return CMD_WARNING; - } - change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); return CMD_SUCCESS; @@ -3007,13 +4119,13 @@ DEFUN (interface_no_ip_igmp_query_max_response_time, #define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10) #define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250) -DEFUN (interface_ip_igmp_query_max_response_time_dsec, - interface_ip_igmp_query_max_response_time_dsec_cmd, - "ip igmp query-max-response-time-dsec (10-250)", - IP_STR - IFACE_IGMP_STR - IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR - "Query response value in deciseconds\n") +DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec, + interface_ip_igmp_query_max_response_time_dsec_cmd, + "ip igmp query-max-response-time-dsec (10-250)", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR + "Query response value in deciseconds\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; @@ -3032,25 +4144,6 @@ DEFUN (interface_ip_igmp_query_max_response_time_dsec, query_max_response_time_dsec = atoi(argv[4]->arg); - /* - It seems we don't need to check bounds since command.c does it - already, but we verify them anyway for extra safety. - */ - if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) { - vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s", - query_max_response_time_dsec, - IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC, - VTY_NEWLINE); - return CMD_WARNING; - } - if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) { - vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s", - query_max_response_time_dsec, - IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC, - VTY_NEWLINE); - return CMD_WARNING; - } - default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; if (query_max_response_time_dsec >= default_query_interval_dsec) { @@ -3066,33 +4159,22 @@ DEFUN (interface_ip_igmp_query_max_response_time_dsec, return CMD_SUCCESS; } -DEFUN (interface_no_ip_igmp_query_max_response_time_dsec, - interface_no_ip_igmp_query_max_response_time_dsec_cmd, - "no ip igmp query-max-response-time-dsec", - NO_STR - IP_STR - IFACE_IGMP_STR - IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR) +DEFUN_HIDDEN (interface_no_ip_igmp_query_max_response_time_dsec, + interface_no_ip_igmp_query_max_response_time_dsec_cmd, + "no ip igmp query-max-response-time-dsec", + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; - int default_query_interval_dsec; pim_ifp = ifp->info; if (!pim_ifp) return CMD_SUCCESS; - default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; - - if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { - vty_out(vty, - "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", - IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, - VTY_NEWLINE); - return CMD_WARNING; - } - change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); return CMD_SUCCESS; @@ -3162,7 +4244,6 @@ static int pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype) { struct pim_interface *pim_ifp = ifp->info; - struct in_addr null = { .s_addr = 0 }; if (!pim_ifp) { pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */); @@ -3177,8 +4258,6 @@ pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype) pim_ifp->itype = itype; pim_if_addr_add_all(ifp); pim_if_membership_refresh(ifp); - - pim_rp_check_rp (null, pim_ifp->primary_address); return 1; } @@ -3230,12 +4309,6 @@ pim_cmd_interface_delete (struct interface *ifp) pim_if_membership_clear(ifp); - /* - pim_if_addr_del_all() removes all sockets from - pim_ifp->igmp_socket_list. - */ - pim_if_addr_del_all(ifp); - /* pim_sock_delete() removes all neighbors from pim_ifp->pim_neighbor_list. @@ -3243,6 +4316,7 @@ pim_cmd_interface_delete (struct interface *ifp) pim_sock_delete(ifp, "pim unconfigured on interface"); if (!PIM_IF_TEST_IGMP(pim_ifp->options)) { + pim_if_addr_del_all(ifp); pim_if_delete(ifp); } @@ -3644,6 +4718,17 @@ DEFUN (debug_mroute, return CMD_SUCCESS; } +DEFUN (debug_mroute_detail, + debug_mroute_detail_cmd, + "debug mroute detail", + DEBUG_STR + DEBUG_MROUTE_STR + "detailed\n") +{ + PIM_DO_DEBUG_MROUTE_DETAIL; + return CMD_SUCCESS; +} + DEFUN (no_debug_mroute, no_debug_mroute_cmd, "no debug mroute", @@ -3655,6 +4740,17 @@ DEFUN (no_debug_mroute, return CMD_SUCCESS; } +DEFUN (no_debug_mroute_detail, + no_debug_mroute_detail_cmd, + "no debug mroute detail", + NO_STR + DEBUG_STR + DEBUG_MROUTE_STR + "detailed\n") +{ + PIM_DONT_DEBUG_MROUTE_DETAIL; + return CMD_SUCCESS; +} DEFUN (debug_static, debug_static_cmd, @@ -3687,6 +4783,8 @@ DEFUN (debug_pim, PIM_DO_DEBUG_PIM_EVENTS; PIM_DO_DEBUG_PIM_PACKETS; PIM_DO_DEBUG_PIM_TRACE; + PIM_DO_DEBUG_MSDP_EVENTS; + PIM_DO_DEBUG_MSDP_PACKETS; return CMD_SUCCESS; } @@ -3700,6 +4798,8 @@ DEFUN (no_debug_pim, PIM_DONT_DEBUG_PIM_EVENTS; PIM_DONT_DEBUG_PIM_PACKETS; PIM_DONT_DEBUG_PIM_TRACE; + PIM_DONT_DEBUG_MSDP_EVENTS; + PIM_DONT_DEBUG_MSDP_PACKETS; PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; @@ -3731,79 +4831,71 @@ DEFUN (no_debug_pim_events, return CMD_SUCCESS; } - DEFUN (debug_pim_packets, debug_pim_packets_cmd, - "debug pim packets", - DEBUG_STR - DEBUG_PIM_STR - DEBUG_PIM_PACKETS_STR) -{ - PIM_DO_DEBUG_PIM_PACKETS; - vty_out (vty, "PIM Packet debugging is on %s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN (debug_pim_packets_filter, - debug_pim_packets_filter_cmd, - "debug pim packets ", + "debug pim packets []", DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_PACKETS_STR DEBUG_PIM_HELLO_PACKETS_STR - DEBUG_PIM_J_P_PACKETS_STR) + DEBUG_PIM_J_P_PACKETS_STR + DEBUG_PIM_PIM_REG_PACKETS_STR) { - int idx_hello_join = 3; - if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) + int idx; + if (argv_find (argv, argc, "hello", &idx)) { PIM_DO_DEBUG_PIM_HELLO; - vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE); + vty_out (vty, "PIM Hello debugging is on%s", VTY_NEWLINE); } - else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0) + else if (argv_find (argv, argc ,"joins", &idx)) { PIM_DO_DEBUG_PIM_J_P; - vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE); + vty_out (vty, "PIM Join/Prune debugging is on%s", VTY_NEWLINE); + } + else if (argv_find (argv, argc, "register", &idx)) + { + PIM_DO_DEBUG_PIM_REG; + vty_out (vty, "PIM Register debugging is on%s", VTY_NEWLINE); + } + else + { + PIM_DO_DEBUG_PIM_PACKETS; + vty_out (vty, "PIM Packet debugging is on %s", VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN (no_debug_pim_packets, no_debug_pim_packets_cmd, - "no debug pim packets", + "no debug pim packets []", NO_STR DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_PACKETS_STR DEBUG_PIM_HELLO_PACKETS_STR - DEBUG_PIM_J_P_PACKETS_STR) + DEBUG_PIM_J_P_PACKETS_STR + DEBUG_PIM_PIM_REG_PACKETS_STR) { - PIM_DONT_DEBUG_PIM_PACKETS; - vty_out (vty, "PIM Packet debugging is off %s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN (no_debug_pim_packets_filter, - no_debug_pim_packets_filter_cmd, - "no debug pim packets ", - NO_STR - DEBUG_STR - DEBUG_PIM_STR - DEBUG_PIM_PACKETS_STR - DEBUG_PIM_HELLO_PACKETS_STR - DEBUG_PIM_J_P_PACKETS_STR) -{ - int idx_hello_join = 4; - if (strncmp(argv[idx_hello_join]->arg,"h",1) == 0) + int idx = 0; + if (argv_find (argv, argc,"hello",&idx)) { PIM_DONT_DEBUG_PIM_HELLO; vty_out (vty, "PIM Hello debugging is off %s", VTY_NEWLINE); } - else if (strncmp(argv[idx_hello_join]->arg,"j",1) == 0) + else if (argv_find (argv, argc, "joins", &idx)) { PIM_DONT_DEBUG_PIM_J_P; vty_out (vty, "PIM Join/Prune debugging is off %s", VTY_NEWLINE); } - return CMD_SUCCESS; + else if (argv_find (argv, argc, "register", &idx)) + { + PIM_DONT_DEBUG_PIM_REG; + vty_out (vty, "PIM Register debugging is off%s", VTY_NEWLINE); + } + else + PIM_DONT_DEBUG_PIM_PACKETS; + + return CMD_SUCCESS; } @@ -3931,6 +5023,95 @@ DEFUN (no_debug_pim_zebra, } +DEFUN (debug_msdp, + debug_msdp_cmd, + "debug msdp", + DEBUG_STR + DEBUG_MSDP_STR) +{ + PIM_DO_DEBUG_MSDP_EVENTS; + PIM_DO_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_msdp, + no_debug_msdp_cmd, + "no debug msdp", + NO_STR + DEBUG_STR + DEBUG_MSDP_STR) +{ + PIM_DONT_DEBUG_MSDP_EVENTS; + PIM_DONT_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_msdp, + undebug_msdp_cmd, + "undebug msdp", + UNDEBUG_STR + DEBUG_MSDP_STR) + +DEFUN (debug_msdp_events, + debug_msdp_events_cmd, + "debug msdp events", + DEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_EVENTS_STR) +{ + PIM_DO_DEBUG_MSDP_EVENTS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_msdp_events, + no_debug_msdp_events_cmd, + "no debug msdp events", + NO_STR + DEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_EVENTS_STR) +{ + PIM_DONT_DEBUG_MSDP_EVENTS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_msdp_events, + undebug_msdp_events_cmd, + "undebug msdp events", + UNDEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_EVENTS_STR) + +DEFUN (debug_msdp_packets, + debug_msdp_packets_cmd, + "debug msdp packets", + DEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_PACKETS_STR) +{ + PIM_DO_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_msdp_packets, + no_debug_msdp_packets_cmd, + "no debug msdp packets", + NO_STR + DEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_PACKETS_STR) +{ + PIM_DONT_DEBUG_MSDP_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_msdp_packets, + undebug_msdp_packets_cmd, + "undebug msdp packets", + UNDEBUG_STR + DEBUG_MSDP_STR + DEBUG_MSDP_PACKETS_STR) + DEFUN (show_debugging_pim, show_debugging_pim_cmd, "show debugging pim", @@ -3942,813 +5123,837 @@ DEFUN (show_debugging_pim, return CMD_SUCCESS; } -static struct igmp_sock *find_igmp_sock_by_fd(int fd) +static int +interface_pim_use_src_cmd_worker(struct vty *vty, const char *source) { - struct listnode *ifnode; - struct interface *ifp; + int result; + struct in_addr source_addr; + VTY_DECLVAR_CONTEXT(interface, ifp); - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { - struct pim_interface *pim_ifp; - struct igmp_sock *igmp; - - if (!ifp->info) + result = inet_pton(AF_INET, source, &source_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + source, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_update_source_set(ifp, source_addr); + switch (result) { + case PIM_SUCCESS: + break; + case PIM_IFACE_NOT_FOUND: + vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE); + break; + case PIM_UPDATE_SOURCE_DUP: + vty_out(vty, "%% Source already set to %s%s", source, VTY_NEWLINE); + break; + default: + vty_out(vty, "%% Source set failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +DEFUN (interface_pim_use_source, + interface_pim_use_source_cmd, + "ip pim use-source A.B.C.D", + IP_STR + "pim multicast routing\n" + "Configure primary IP address\n" + "source ip address\n") +{ + return interface_pim_use_src_cmd_worker (vty, argv[3]->arg); +} + +DEFUN (interface_no_pim_use_source, + interface_no_pim_use_source_cmd, + "no ip pim use-source", + NO_STR + IP_STR + "pim multicast routing\n" + "Delete source IP address\n") +{ + return interface_pim_use_src_cmd_worker (vty, "0.0.0.0"); +} + +static int +ip_msdp_peer_cmd_worker (struct vty *vty, const char *peer, const char *local) +{ + enum pim_msdp_err result; + struct in_addr peer_addr; + struct in_addr local_addr; + + result = inet_pton(AF_INET, peer, &peer_addr); + if (result <= 0) { + vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s", + peer, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = inet_pton(AF_INET, local, &local_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + local, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_msdp_peer_add(peer_addr, local_addr, "default", NULL/* mp_p */); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_OOM: + vty_out(vty, "%% Out of memory%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_PEER_EXISTS: + vty_out(vty, "%% Peer exists%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_MAX_MESH_GROUPS: + vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% peer add failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +DEFUN_HIDDEN (ip_msdp_peer, + ip_msdp_peer_cmd, + "ip msdp peer A.B.C.D source A.B.C.D", + IP_STR + CFG_MSDP_STR + "Configure MSDP peer\n" + "peer ip address\n" + "Source address for TCP connection\n" + "local ip address\n") +{ + return ip_msdp_peer_cmd_worker (vty, argv[3]->arg, argv[5]->arg); +} + +static int +ip_no_msdp_peer_cmd_worker (struct vty *vty, const char *peer) +{ + enum pim_msdp_err result; + struct in_addr peer_addr; + + result = inet_pton(AF_INET, peer, &peer_addr); + if (result <= 0) { + vty_out(vty, "%% Bad peer address %s: errno=%d: %s%s", + peer, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_msdp_peer_del(peer_addr); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_NO_PEER: + vty_out(vty, "%% Peer does not exist%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% peer del failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ip_msdp_peer, + no_ip_msdp_peer_cmd, + "no ip msdp peer A.B.C.D", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP peer\n" + "peer ip address\n") +{ + return ip_no_msdp_peer_cmd_worker (vty, argv[4]->arg); +} + +static int +ip_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr) +{ + enum pim_msdp_err result; + struct in_addr mbr_ip; + + result = inet_pton(AF_INET, mbr, &mbr_ip); + if (result <= 0) { + vty_out(vty, "%% Bad member address %s: errno=%d: %s%s", + mbr, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_msdp_mg_mbr_add(mg, mbr_ip); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_OOM: + vty_out(vty, "%% Out of memory%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_MG_MBR_EXISTS: + vty_out(vty, "%% mesh-group member exists%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_MAX_MESH_GROUPS: + vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% member add failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +DEFUN (ip_msdp_mesh_group_member, + ip_msdp_mesh_group_member_cmd, + "ip msdp mesh-group WORD member A.B.C.D", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "mesh group name\n" + "mesh group member\n" + "peer ip address\n") +{ + return ip_msdp_mesh_group_member_cmd_worker(vty, argv[3]->arg, argv[5]->arg); +} + +static int +ip_no_msdp_mesh_group_member_cmd_worker(struct vty *vty, const char *mg, const char *mbr) +{ + enum pim_msdp_err result; + struct in_addr mbr_ip; + + result = inet_pton(AF_INET, mbr, &mbr_ip); + if (result <= 0) { + vty_out(vty, "%% Bad member address %s: errno=%d: %s%s", + mbr, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_msdp_mg_mbr_del(mg, mbr_ip); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_NO_MG: + vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_NO_MG_MBR: + vty_out(vty, "%% mesh-group member does not exist%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% mesh-group member del failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} +DEFUN (no_ip_msdp_mesh_group_member, + no_ip_msdp_mesh_group_member_cmd, + "no ip msdp mesh-group WORD member A.B.C.D", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group member\n" + "mesh group name\n" + "mesh group member\n" + "peer ip address\n") +{ + return ip_no_msdp_mesh_group_member_cmd_worker(vty, argv[4]->arg, argv[6]->arg); +} + +static int +ip_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg, const char *src) +{ + enum pim_msdp_err result; + struct in_addr src_ip; + + result = inet_pton(AF_INET, src, &src_ip); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + src, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_msdp_mg_src_add(mg, src_ip); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_OOM: + vty_out(vty, "%% Out of memory%s", VTY_NEWLINE); + break; + case PIM_MSDP_ERR_MAX_MESH_GROUPS: + vty_out(vty, "%% Only one mesh-group allowed currently%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% source add failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + + +DEFUN (ip_msdp_mesh_group_source, + ip_msdp_mesh_group_source_cmd, + "ip msdp mesh-group WORD source A.B.C.D", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "mesh group name\n" + "mesh group local address\n" + "source ip address for the TCP connection\n") +{ + return ip_msdp_mesh_group_source_cmd_worker(vty, argv[3]->arg, argv[5]->arg); +} + +static int +ip_no_msdp_mesh_group_source_cmd_worker(struct vty *vty, const char *mg) +{ + enum pim_msdp_err result; + + result = pim_msdp_mg_src_del(mg); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_NO_MG: + vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE); + } + + return result?CMD_WARNING:CMD_SUCCESS; +} + +static int +ip_no_msdp_mesh_group_cmd_worker(struct vty *vty, const char *mg) +{ + enum pim_msdp_err result; + + result = pim_msdp_mg_del(mg); + switch (result) { + case PIM_MSDP_ERR_NONE: + break; + case PIM_MSDP_ERR_NO_MG: + vty_out(vty, "%% mesh-group does not exist%s", VTY_NEWLINE); + break; + default: + vty_out(vty, "%% mesh-group source del failed%s", VTY_NEWLINE); + } + + return result ? CMD_WARNING : CMD_SUCCESS; +} + +DEFUN (no_ip_msdp_mesh_group_source, + no_ip_msdp_mesh_group_source_cmd, + "no ip msdp mesh-group WORD source [A.B.C.D]", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group source\n" + "mesh group name\n" + "mesh group source\n" + "mesh group local address\n") +{ + if (argv[6]->arg) + return ip_no_msdp_mesh_group_cmd_worker(vty, argv[6]->arg); + else + return ip_no_msdp_mesh_group_source_cmd_worker(vty, argv[4]->arg); +} + +static void +print_empty_json_obj(struct vty *vty) +{ + json_object *json; + json = json_object_new_object(); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); +} + +static void +ip_msdp_show_mesh_group(struct vty *vty, u_char uj) +{ + struct listnode *mbrnode; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg = msdp->mg; + char mbr_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + char state_str[PIM_MSDP_STATE_STRLEN]; + enum pim_msdp_peer_state state; + json_object *json = NULL; + json_object *json_mg_row = NULL; + json_object *json_members = NULL; + json_object *json_row = NULL; + + if (!mg) { + if (uj) + print_empty_json_obj(vty); + return; + } + + pim_inet4_dump("", mg->src_ip, src_str, sizeof(src_str)); + if (uj) { + json = json_object_new_object(); + /* currently there is only one mesh group but we should still make + * it a dict with mg-name as key */ + json_mg_row = json_object_new_object(); + json_object_string_add(json_mg_row, "name", mg->mesh_group_name); + json_object_string_add(json_mg_row, "source", src_str); + } else { + vty_out(vty, "Mesh group : %s%s", mg->mesh_group_name, VTY_NEWLINE); + vty_out(vty, " Source : %s%s", src_str, VTY_NEWLINE); + vty_out(vty, " Member State%s", VTY_NEWLINE); + } + + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { + pim_inet4_dump("", mbr->mbr_ip, mbr_str, sizeof(mbr_str)); + if (mbr->mp) { + state = mbr->mp->state; + } else { + state = PIM_MSDP_DISABLED; + } + pim_msdp_state_dump(state, state_str, sizeof(state_str)); + if (uj) { + json_row = json_object_new_object(); + json_object_string_add(json_row, "member", mbr_str); + json_object_string_add(json_row, "state", state_str); + if (!json_members) { + json_members = json_object_new_object(); + json_object_object_add(json_mg_row, "members", json_members); + } + json_object_object_add(json_members, mbr_str, json_row); + } else { + vty_out(vty, " %-15s %11s%s", + mbr_str, state_str, VTY_NEWLINE); + } + } + + if (uj) { + json_object_object_add(json, mg->mesh_group_name, json_mg_row); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} + +DEFUN (show_ip_msdp_mesh_group, + show_ip_msdp_mesh_group_cmd, + "show ip msdp mesh-group [json]", + SHOW_STR + IP_STR + MSDP_STR + "MSDP mesh-group information\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + ip_msdp_show_mesh_group(vty, uj); + + return CMD_SUCCESS; +} + +static void +ip_msdp_show_peers(struct vty *vty, u_char uj) +{ + struct listnode *mpnode; + struct pim_msdp_peer *mp; + char peer_str[INET_ADDRSTRLEN]; + char local_str[INET_ADDRSTRLEN]; + char state_str[PIM_MSDP_STATE_STRLEN]; + char timebuf[PIM_MSDP_UPTIME_STRLEN]; + int64_t now; + json_object *json = NULL; + json_object *json_row = NULL; + + + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Peer Local State Uptime SaCnt%s", VTY_NEWLINE); + } + + for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) { + if (mp->state == PIM_MSDP_ESTABLISHED) { + now = pim_time_monotonic_sec(); + pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); + } else { + strcpy(timebuf, "-"); + } + pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); + pim_inet4_dump("", mp->local, local_str, sizeof(local_str)); + pim_msdp_state_dump(mp->state, state_str, sizeof(state_str)); + if (uj) { + json_row = json_object_new_object(); + json_object_string_add(json_row, "peer", peer_str); + json_object_string_add(json_row, "local", local_str); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "upTime", timebuf); + json_object_int_add(json_row, "saCount", mp->sa_cnt); + json_object_object_add(json, peer_str, json_row); + } else { + vty_out(vty, "%-15s %15s %11s %8s %6d%s", + peer_str, local_str, state_str, + timebuf, mp->sa_cnt, VTY_NEWLINE); + } + } + + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} + +static void +ip_msdp_show_peers_detail(struct vty *vty, const char *peer, u_char uj) +{ + struct listnode *mpnode; + struct pim_msdp_peer *mp; + char peer_str[INET_ADDRSTRLEN]; + char local_str[INET_ADDRSTRLEN]; + char state_str[PIM_MSDP_STATE_STRLEN]; + char timebuf[PIM_MSDP_UPTIME_STRLEN]; + char katimer[PIM_MSDP_TIMER_STRLEN]; + char crtimer[PIM_MSDP_TIMER_STRLEN]; + char holdtimer[PIM_MSDP_TIMER_STRLEN]; + int64_t now; + json_object *json = NULL; + json_object *json_row = NULL; + + if (uj) { + json = json_object_new_object(); + } + + for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) { + pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); + if (strcmp(peer, "detail") && + strcmp(peer, peer_str)) continue; - pim_ifp = ifp->info; + if (mp->state == PIM_MSDP_ESTABLISHED) { + now = pim_time_monotonic_sec(); + pim_time_uptime(timebuf, sizeof(timebuf), now - mp->uptime); + } else { + strcpy(timebuf, "-"); + } + pim_inet4_dump("", mp->local, local_str, sizeof(local_str)); + pim_msdp_state_dump(mp->state, state_str, sizeof(state_str)); + pim_time_timer_to_hhmmss(katimer, sizeof(katimer), mp->ka_timer); + pim_time_timer_to_hhmmss(crtimer, sizeof(crtimer), mp->cr_timer); + pim_time_timer_to_hhmmss(holdtimer, sizeof(holdtimer), mp->hold_timer); - /* lookup igmp socket under current interface */ - igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd); - if (igmp) - return igmp; - } + if (uj) { + json_row = json_object_new_object(); + json_object_string_add(json_row, "peer", peer_str); + json_object_string_add(json_row, "local", local_str); + json_object_string_add(json_row, "meshGroupName", mp->mesh_group_name); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "upTime", timebuf); + json_object_string_add(json_row, "keepAliveTimer", katimer); + json_object_string_add(json_row, "connRetryTimer", crtimer); + json_object_string_add(json_row, "holdTimer", holdtimer); + json_object_string_add(json_row, "lastReset", mp->last_reset); + json_object_int_add(json_row, "connAttempts", mp->conn_attempts); + json_object_int_add(json_row, "establishedChanges", mp->est_flaps); + json_object_int_add(json_row, "saCount", mp->sa_cnt); + json_object_int_add(json_row, "kaSent", mp->ka_tx_cnt); + json_object_int_add(json_row, "kaRcvd", mp->ka_rx_cnt); + json_object_int_add(json_row, "saSent", mp->sa_tx_cnt); + json_object_int_add(json_row, "saRcvd", mp->sa_rx_cnt); + json_object_object_add(json, peer_str, json_row); + } else { + vty_out(vty, "Peer : %s%s", peer_str, VTY_NEWLINE); + vty_out(vty, " Local : %s%s", local_str, VTY_NEWLINE); + vty_out(vty, " Mesh Group : %s%s", mp->mesh_group_name, VTY_NEWLINE); + vty_out(vty, " State : %s%s", state_str, VTY_NEWLINE); + vty_out(vty, " Uptime : %s%s", timebuf, VTY_NEWLINE); - return 0; -} - -DEFUN (test_igmp_receive_report, - test_igmp_receive_report_cmd, - "test igmp receive report (0-65535) A.B.C.D (1-6) LINE...", - "Test\n" - "Test IGMP protocol\n" - "Test IGMP message\n" - "Test IGMP report\n" - "Socket\n" - "IGMP group address\n" - "Record type\n" - "Sources\n") -{ - int idx_number = 4; - int idx_ipv4 = 5; - int idx_number_2 = 6; - int idx_line = 7; - char buf[1000]; - char *igmp_msg; - struct ip *ip_hdr; - size_t ip_hlen; /* ip header length in bytes */ - int ip_msg_len; - int igmp_msg_len; - const char *socket; - int socket_fd; - const char *grp_str; - struct in_addr grp_addr; - const char *record_type_str; - int record_type; - const char *src_str; - int result; - struct igmp_sock *igmp; - char *group_record; - int num_sources; - struct in_addr *sources; - struct in_addr *src_addr; - int argi; - - socket = argv[idx_number]->arg; - socket_fd = atoi(socket); - igmp = find_igmp_sock_by_fd(socket_fd); - if (!igmp) { - vty_out(vty, "Could not find IGMP socket %s: fd=%d%s", - socket, socket_fd, VTY_NEWLINE); - return CMD_WARNING; - } - - grp_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, grp_str, &grp_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s%s", - grp_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - record_type_str = argv[idx_number_2]->arg; - record_type = atoi(record_type_str); - - /* - Tweak IP header - */ - ip_hdr = (struct ip *) buf; - ip_hdr->ip_p = PIM_IP_PROTO_IGMP; - ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ - ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ - ip_hdr->ip_src = igmp->ifaddr; - ip_hdr->ip_dst = igmp->ifaddr; - - /* - Build IGMP v3 report message - */ - igmp_msg = buf + ip_hlen; - group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; - *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */ - *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ - *(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */ - *(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type; - memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr)); - - /* Scan LINE sources */ - sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET); - src_addr = sources; - for (argi = idx_line; argi < argc; ++argi,++src_addr) { - src_str = argv[argi]->arg; - result = inet_pton(AF_INET, src_str, src_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s%s", - src_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + vty_out(vty, " Keepalive Timer : %s%s", katimer, VTY_NEWLINE); + vty_out(vty, " Conn Retry Timer : %s%s", crtimer, VTY_NEWLINE); + vty_out(vty, " Hold Timer : %s%s", holdtimer, VTY_NEWLINE); + vty_out(vty, " Last Reset : %s%s", mp->last_reset, VTY_NEWLINE); + vty_out(vty, " Conn Attempts : %d%s", mp->conn_attempts, VTY_NEWLINE); + vty_out(vty, " Established Changes : %d%s", mp->est_flaps, VTY_NEWLINE); + vty_out(vty, " SA Count : %d%s", mp->sa_cnt, VTY_NEWLINE); + vty_out(vty, " Statistics :%s", VTY_NEWLINE); + vty_out(vty, " Sent Rcvd%s", VTY_NEWLINE); + vty_out(vty, " Keepalives : %10d %10d%s", + mp->ka_tx_cnt, mp->ka_rx_cnt, VTY_NEWLINE); + vty_out(vty, " SAs : %10d %10d%s", + mp->sa_tx_cnt, mp->sa_rx_cnt, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); } } - num_sources = src_addr - sources; - *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources); - - igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */ - - /* compute checksum */ - *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len); - - /* "receive" message */ - - ip_msg_len = ip_hlen + igmp_msg_len; - result = pim_igmp_packet(igmp, buf, ip_msg_len); - if (result) { - vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s", - ip_msg_len, result, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } +} + +DEFUN (show_ip_msdp_peer_detail, + show_ip_msdp_peer_detail_cmd, + "show ip msdp peer [detail|A.B.C.D] [json]", + SHOW_STR + IP_STR + MSDP_STR + "MSDP peer information\n" + "Detailed output\n" + "peer ip address\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + if (argv[4]->arg) + ip_msdp_show_peers_detail(vty, argv[4]->arg, uj); + else + ip_msdp_show_peers(vty, uj); return CMD_SUCCESS; } -static int hexval(uint8_t ch) +static void +ip_msdp_show_sa(struct vty *vty, u_char uj) { - return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a'); -} + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rp_str[INET_ADDRSTRLEN]; + char timebuf[PIM_MSDP_UPTIME_STRLEN]; + char spt_str[8]; + char local_str[8]; + int64_t now; + json_object *json = NULL; + json_object *json_group = NULL; + json_object *json_row = NULL; -DEFUN (test_pim_receive_dump, - test_pim_receive_dump_cmd, - "test pim receive dump INTERFACE A.B.C.D LINE...", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test PIM packet dump reception from neighbor\n" - "Interface\n" - "Neighbor address\n" - "Packet dump\n") -{ - int idx_interface = 4; - int idx_ipv4 = 5; - int idx_line = 6; - uint8_t buf[1000]; - uint8_t *pim_msg; - struct ip *ip_hdr; - size_t ip_hlen; /* ip header length in bytes */ - int ip_msg_len; - int pim_msg_size; - const char *neigh_str; - struct in_addr neigh_addr; - const char *ifname; - struct interface *ifp; - int argi; - int result; - - /* Find interface */ - ifname = argv[idx_interface]->arg; - ifp = if_lookup_by_name(ifname); - if (!ifp) { - vty_out(vty, "No such interface name %s%s", - ifname, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + json = json_object_new_object(); + } else { + vty_out(vty, "Source Group RP Local SPT Uptime%s", VTY_NEWLINE); } - /* Neighbor address */ - neigh_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, neigh_str, &neigh_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", - neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - /* - Tweak IP header - */ - ip_hdr = (struct ip *) buf; - ip_hdr->ip_p = PIM_IP_PROTO_PIM; - ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ - ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ - ip_hdr->ip_src = neigh_addr; - ip_hdr->ip_dst = qpim_all_pim_routers_addr; - - /* - Build PIM hello message - */ - pim_msg = buf + ip_hlen; - pim_msg_size = 0; - - /* Scan LINE dump into buffer */ - for (argi = idx_line; argi < argc; ++argi) { - const char *str = argv[argi]->arg; - int str_len = strlen(str); - int str_last = str_len - 1; - int i; - - if (str_len % 2) { - vty_out(vty, "%% Uneven hex array arg %d=%s%s", - argi, str, VTY_NEWLINE); - return CMD_WARNING; - } - - for (i = 0; i < str_last; i += 2) { - uint8_t octet; - int left; - uint8_t h1 = str[i]; - uint8_t h2 = str[i + 1]; - - if (!isxdigit(h1) || !isxdigit(h2)) { - vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s", - h1, h2, argi, str, VTY_NEWLINE); - return CMD_WARNING; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + now = pim_time_monotonic_sec(); + pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime); + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + if (sa->flags & PIM_MSDP_SAF_PEER) { + pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); + if (sa->up) { + strcpy(spt_str, "yes"); + } else { + strcpy(spt_str, "no"); } - octet = (hexval(h1) << 4) + hexval(h2); + } else { + strcpy(rp_str, "-"); + strcpy(spt_str, "-"); + } + if (sa->flags & PIM_MSDP_SAF_LOCAL) { + strcpy(local_str, "yes"); + } else { + strcpy(local_str, "no"); + } + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); - left = sizeof(buf) - ip_hlen - pim_msg_size; - if (left < 1) { - vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s", - sizeof(buf), left, argi, str, octet, VTY_NEWLINE); - return CMD_WARNING; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); } - - pim_msg[pim_msg_size++] = octet; + + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "rp", rp_str); + json_object_string_add(json_row, "local", local_str); + json_object_string_add(json_row, "sptSetup", spt_str); + json_object_string_add(json_row, "upTime", timebuf); + json_object_object_add(json_group, src_str, json_row); + } else { + vty_out(vty, "%-15s %15s %15s %5c %3c %8s%s", + src_str, grp_str, rp_str, local_str[0], spt_str[0], timebuf, VTY_NEWLINE); } } - ip_msg_len = ip_hlen + pim_msg_size; - vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s", - sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE); - - /* "receive" message */ - - result = pim_pim_packet(ifp, buf, ip_msg_len); - if (result) { - vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s", - ip_msg_len, result, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } - - return CMD_SUCCESS; } -DEFUN (test_pim_receive_hello, - test_pim_receive_hello_cmd, - "test pim receive hello INTERFACE A.B.C.D (0-65535) (0-65535) (0-65535) (0-32767) (0-65535) (0-1) [LINE]", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test PIM hello reception from neighbor\n" - "Interface\n" - "Neighbor address\n" - "Neighbor holdtime\n" - "Neighbor DR priority\n" - "Neighbor generation ID\n" - "Neighbor propagation delay (msec)\n" - "Neighbor override interval (msec)\n" - "Neighbor LAN prune delay T-bit\n" - "Neighbor secondary addresses\n") +static void +ip_msdp_show_sa_entry_detail(struct pim_msdp_sa *sa, const char *src_str, + const char *grp_str, struct vty *vty, + u_char uj, json_object *json) { - int idx_interface = 4; - int idx_ipv4 = 5; - int idx_number = 6; - int idx_number_2 = 7; - int idx_number_3 = 8; - int idx_number_4 = 9; - int idx_number_5 = 10; - int idx_number_6 = 11; - int idx_line = 12; - uint8_t buf[1000]; - uint8_t *pim_msg; - struct ip *ip_hdr; - size_t ip_hlen; /* ip header length in bytes */ - int ip_msg_len; - int pim_tlv_size; - int pim_msg_size; - const char *neigh_str; - struct in_addr neigh_addr; - const char *ifname; - struct interface *ifp; - uint16_t neigh_holdtime; - uint16_t neigh_propagation_delay; - uint16_t neigh_override_interval; - int neigh_can_disable_join_suppression; - uint32_t neigh_dr_priority; - uint32_t neigh_generation_id; - int argi; - int result; + char rp_str[INET_ADDRSTRLEN]; + char peer_str[INET_ADDRSTRLEN]; + char timebuf[PIM_MSDP_UPTIME_STRLEN]; + char spt_str[8]; + char local_str[8]; + char statetimer[PIM_MSDP_TIMER_STRLEN]; + int64_t now; + json_object *json_group = NULL; + json_object *json_row = NULL; - /* Find interface */ - ifname = argv[idx_interface]->arg; - ifp = if_lookup_by_name(ifname); - if (!ifp) { - vty_out(vty, "No such interface name %s%s", - ifname, VTY_NEWLINE); - return CMD_WARNING; + now = pim_time_monotonic_sec(); + pim_time_uptime(timebuf, sizeof(timebuf), now - sa->uptime); + if (sa->flags & PIM_MSDP_SAF_PEER) { + pim_inet4_dump("", sa->rp, rp_str, sizeof(rp_str)); + pim_inet4_dump("", sa->peer, peer_str, sizeof(peer_str)); + if (sa->up) { + strcpy(spt_str, "yes"); + } else { + strcpy(spt_str, "no"); + } + } else { + strcpy(rp_str, "-"); + strcpy(peer_str, "-"); + strcpy(spt_str, "-"); } - - /* Neighbor address */ - neigh_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, neigh_str, &neigh_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", - neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + if (sa->flags & PIM_MSDP_SAF_LOCAL) { + strcpy(local_str, "yes"); + } else { + strcpy(local_str, "no"); } + pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer), sa->sa_state_timer); + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); - neigh_holdtime = atoi(argv[idx_number]->arg); - neigh_dr_priority = atoi(argv[idx_number_2]->arg); - neigh_generation_id = atoi(argv[idx_number_3]->arg); - neigh_propagation_delay = atoi(argv[idx_number_4]->arg); - neigh_override_interval = atoi(argv[idx_number_5]->arg); - neigh_can_disable_join_suppression = atoi(argv[idx_number_6]->arg); - - /* - Tweak IP header - */ - ip_hdr = (struct ip *) buf; - ip_hdr->ip_p = PIM_IP_PROTO_PIM; - ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ - ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ - ip_hdr->ip_src = neigh_addr; - ip_hdr->ip_dst = qpim_all_pim_routers_addr; - - /* - Build PIM hello message - */ - pim_msg = buf + ip_hlen; - - /* Scan LINE addresses */ - for (argi = idx_line; argi < argc; ++argi) { - const char *sec_str = argv[argi]->arg; - struct in_addr sec_addr; - result = inet_pton(AF_INET, sec_str, &sec_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s", - sec_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); } - vty_out(vty, - "FIXME WRITEME consider neighbor secondary address %s%s", - sec_str, VTY_NEWLINE); + json_row = json_object_new_object(); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + json_object_string_add(json_row, "rp", rp_str); + json_object_string_add(json_row, "local", local_str); + json_object_string_add(json_row, "sptSetup", spt_str); + json_object_string_add(json_row, "upTime", timebuf); + json_object_string_add(json_row, "stateTimer", statetimer); + json_object_object_add(json_group, src_str, json_row); + } else { + vty_out(vty, "SA : %s%s", sa->sg_str, VTY_NEWLINE); + vty_out(vty, " RP : %s%s", rp_str, VTY_NEWLINE); + vty_out(vty, " Peer : %s%s", peer_str, VTY_NEWLINE); + vty_out(vty, " Local : %s%s", local_str, VTY_NEWLINE); + vty_out(vty, " SPT Setup : %s%s", spt_str, VTY_NEWLINE); + vty_out(vty, " Uptime : %s%s", timebuf, VTY_NEWLINE); + vty_out(vty, " State Timer : %s%s", statetimer, VTY_NEWLINE); + vty_out(vty, "%s", VTY_NEWLINE); + } +} + +static void +ip_msdp_show_sa_detail(struct vty *vty, u_char uj) +{ + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; + + if (uj) { + json = json_object_new_object(); } - pim_tlv_size = pim_hello_build_tlv(ifp->name, - pim_msg + PIM_PIM_MIN_LEN, - sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN, - neigh_holdtime, - neigh_dr_priority, - neigh_generation_id, - neigh_propagation_delay, - neigh_override_interval, - neigh_can_disable_join_suppression, - 0 /* FIXME secondary address list */); - if (pim_tlv_size < 0) { - vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s", - pim_tlv_size, VTY_NEWLINE); - return CMD_WARNING; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json); } - pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; - - pim_msg_build_header(pim_msg, pim_msg_size, - PIM_MSG_TYPE_HELLO); - - /* "receive" message */ - - ip_msg_len = ip_hlen + pim_msg_size; - result = pim_pim_packet(ifp, buf, ip_msg_len); - if (result) { - vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", - ip_msg_len, result, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } +} + +DEFUN (show_ip_msdp_sa_detail, + show_ip_msdp_sa_detail_cmd, + "show ip msdp sa detail [json]", + SHOW_STR + IP_STR + MSDP_STR + "MSDP active-source information\n" + "Detailed output\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + ip_msdp_show_sa_detail(vty, uj); return CMD_SUCCESS; } -DEFUN (test_pim_receive_assert, - test_pim_receive_assert_cmd, - "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D (0-65535) (0-65535) (0-1)", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test reception of PIM assert\n" - "Interface\n" - "Neighbor address\n" - "Assert multicast group address\n" - "Assert unicast source address\n" - "Assert metric preference\n" - "Assert route metric\n" - "Assert RPT bit flag\n") +static void +ip_msdp_show_sa_addr(struct vty *vty, const char *addr, u_char uj) { - int idx_interface = 4; - int idx_ipv4 = 5; - int idx_ipv4_2 = 6; - int idx_ipv4_3 = 7; - int idx_number = 8; - int idx_number_2 = 9; - int idx_number_3 = 10; - uint8_t buf[1000]; - uint8_t *buf_pastend = buf + sizeof(buf); - uint8_t *pim_msg; - struct ip *ip_hdr; - size_t ip_hlen; /* ip header length in bytes */ - int ip_msg_len; - int pim_msg_size; - const char *neigh_str; - struct in_addr neigh_addr; - const char *group_str; - struct in_addr group_addr; - const char *source_str; - struct in_addr source_addr; - const char *ifname; - struct interface *ifp; - uint32_t assert_metric_preference; - uint32_t assert_route_metric; - uint32_t assert_rpt_bit_flag; - int remain; - int result; + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; - /* Find interface */ - ifname = argv[idx_interface]->arg; - ifp = if_lookup_by_name(ifname); - if (!ifp) { - vty_out(vty, "No such interface name %s%s", - ifname, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + json = json_object_new_object(); } - /* Neighbor address */ - neigh_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, neigh_str, &neigh_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", - neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + if (!strcmp(addr, src_str) || !strcmp(addr, grp_str)) { + ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json); + } } - /* Group address */ - group_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, group_str, &group_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } - - /* Source address */ - source_str = argv[idx_ipv4_3]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - assert_metric_preference = atoi(argv[idx_number]->arg); - assert_route_metric = atoi(argv[idx_number_2]->arg); - assert_rpt_bit_flag = atoi(argv[idx_number_3]->arg); - - remain = buf_pastend - buf; - if (remain < (int) sizeof(struct ip)) { - vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s", - remain, sizeof(struct ip), VTY_NEWLINE); - return CMD_WARNING; - } - - /* - Tweak IP header - */ - ip_hdr = (struct ip *) buf; - ip_hdr->ip_p = PIM_IP_PROTO_PIM; - ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ - ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ - ip_hdr->ip_src = neigh_addr; - ip_hdr->ip_dst = qpim_all_pim_routers_addr; - - /* - Build PIM assert message - */ - pim_msg = buf + ip_hlen; /* skip ip header */ - - pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp, - group_addr, source_addr, - assert_metric_preference, - assert_route_metric, - assert_rpt_bit_flag); - if (pim_msg_size < 0) { - vty_out(vty, "Failure building PIM assert message: size=%d%s", - pim_msg_size, VTY_NEWLINE); - return CMD_WARNING; - } - - /* "receive" message */ - - ip_msg_len = ip_hlen + pim_msg_size; - result = pim_pim_packet(ifp, buf, ip_msg_len); - if (result) { - vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", - ip_msg_len, result, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; } -static int recv_joinprune(struct vty *vty, - struct cmd_token **argv, - int src_is_join) +static void +ip_msdp_show_sa_sg(struct vty *vty, const char *src, const char *grp, u_char uj) { - uint8_t buf[1000]; - const uint8_t *buf_pastend = buf + sizeof(buf); - uint8_t *pim_msg; - uint8_t *pim_msg_curr; - int pim_msg_size; - struct ip *ip_hdr; - size_t ip_hlen; /* ip header length in bytes */ - int ip_msg_len; - uint16_t neigh_holdtime; - const char *neigh_dst_str; - struct in_addr neigh_dst_addr; - const char *neigh_src_str; - struct in_addr neigh_src_addr; - const char *group_str; - struct in_addr group_addr; - const char *source_str; - struct in_addr source_addr; - const char *ifname; - struct interface *ifp; - int result; - int remain; - uint16_t num_joined; - uint16_t num_pruned; + struct listnode *sanode; + struct pim_msdp_sa *sa; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json = NULL; - /* Find interface */ - ifname = argv[0]->arg; - ifp = if_lookup_by_name(ifname); - if (!ifp) { - vty_out(vty, "No such interface name %s%s", - ifname, VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + json = json_object_new_object(); } - neigh_holdtime = atoi(argv[1]->arg); - - /* Neighbor destination address */ - neigh_dst_str = argv[2]->arg; - result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s", - neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + pim_inet4_dump("", sa->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", sa->sg.grp, grp_str, sizeof(grp_str)); + if (!strcmp(src, src_str) && !strcmp(grp, grp_str)) { + ip_msdp_show_sa_entry_detail(sa, src_str, grp_str, vty, uj, json); + } } - /* Neighbor source address */ - neigh_src_str = argv[3]->arg; - result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr); - if (result <= 0) { - vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s", - neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; + if (uj) { + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); } - - /* Multicast group address */ - group_str = argv[4]->arg; - result = inet_pton(AF_INET, group_str, &group_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Multicast source address */ - source_str = argv[5]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - /* - Tweak IP header - */ - ip_hdr = (struct ip *) buf; - ip_hdr->ip_p = PIM_IP_PROTO_PIM; - ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ - ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ - ip_hdr->ip_src = neigh_src_addr; - ip_hdr->ip_dst = qpim_all_pim_routers_addr; - - /* - Build PIM message - */ - pim_msg = buf + ip_hlen; - - /* skip room for pim header */ - pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; - - remain = buf_pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, - remain, - neigh_dst_addr); - if (!pim_msg_curr) { - vty_out(vty, "Failure encoding destination address %s: space left=%d%s", - neigh_dst_str, remain, VTY_NEWLINE); - return CMD_WARNING; - } - - remain = buf_pastend - pim_msg_curr; - if (remain < 4) { - vty_out(vty, "Group will not fit: space left=%d%s", - remain, VTY_NEWLINE); - return CMD_WARNING; - } - - *pim_msg_curr = 0; /* reserved */ - ++pim_msg_curr; - *pim_msg_curr = 1; /* number of groups */ - ++pim_msg_curr; - *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime); - ++pim_msg_curr; - ++pim_msg_curr; - - remain = buf_pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, - remain, - group_addr); - if (!pim_msg_curr) { - vty_out(vty, "Failure encoding group address %s: space left=%d%s", - group_str, remain, VTY_NEWLINE); - return CMD_WARNING; - } - - remain = buf_pastend - pim_msg_curr; - if (remain < 4) { - vty_out(vty, "Sources will not fit: space left=%d%s", - remain, VTY_NEWLINE); - return CMD_WARNING; - } - - if (src_is_join) { - num_joined = 1; - num_pruned = 0; - } - else { - num_joined = 0; - num_pruned = 1; - } - - /* number of joined sources */ - *((uint16_t *) pim_msg_curr) = htons(num_joined); - ++pim_msg_curr; - ++pim_msg_curr; - - /* number of pruned sources */ - *((uint16_t *) pim_msg_curr) = htons(num_pruned); - ++pim_msg_curr; - ++pim_msg_curr; - - remain = buf_pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, - remain, - source_addr); - if (!pim_msg_curr) { - vty_out(vty, "Failure encoding source address %s: space left=%d%s", - source_str, remain, VTY_NEWLINE); - return CMD_WARNING; - } - - /* Add PIM header */ - - pim_msg_size = pim_msg_curr - pim_msg; - - pim_msg_build_header(pim_msg, pim_msg_size, - PIM_MSG_TYPE_JOIN_PRUNE); - - /* - "Receive" message - */ - - ip_msg_len = ip_hlen + pim_msg_size; - result = pim_pim_packet(ifp, buf, ip_msg_len); - if (result) { - vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", - ip_msg_len, result, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; } -DEFUN (test_pim_receive_join, - test_pim_receive_join_cmd, - "test pim receive join INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test PIM join reception from neighbor\n" - "Interface\n" - "Neighbor holdtime\n" - "Upstream neighbor unicast destination address\n" - "Downstream neighbor unicast source address\n" - "Multicast group address\n" - "Unicast source address\n") +DEFUN (show_ip_msdp_sa_sg, + show_ip_msdp_sa_sg_cmd, + "show ip msdp sa [A.B.C.D] [A.B.C.D] [json]", + SHOW_STR + IP_STR + MSDP_STR + "MSDP active-source information\n" + "source or group ip\n" + "group ip\n" + "JavaScript Object Notation\n") { - return recv_joinprune(vty, argv, 1 /* src_is_join=true */); -} - -DEFUN (test_pim_receive_prune, - test_pim_receive_prune_cmd, - "test pim receive prune INTERFACE (0-65535) A.B.C.D A.B.C.D A.B.C.D A.B.C.D", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test PIM prune reception from neighbor\n" - "Interface\n" - "Neighbor holdtime\n" - "Upstream neighbor unicast destination address\n" - "Downstream neighbor unicast source address\n" - "Multicast group address\n" - "Unicast source address\n") -{ - return recv_joinprune(vty, argv, 0 /* src_is_join=false */); -} - -DEFUN (test_pim_receive_upcall, - test_pim_receive_upcall_cmd, - "test pim receive upcall (0-65535) A.B.C.D A.B.C.D", - "Test\n" - "Test PIM protocol\n" - "Test PIM message reception\n" - "Test reception of kernel upcall\n" - "NOCACHE kernel upcall\n" - "WRONGVIF kernel upcall\n" - "WHOLEPKT kernel upcall\n" - "Input interface vif index\n" - "Multicast group address\n" - "Multicast source address\n") -{ - int idx_type = 4; - int idx_number = 5; - int idx_ipv4 = 6; - int idx_ipv4_2 = 7; - struct igmpmsg msg; - const char *upcall_type; - const char *group_str; - const char *source_str; - int result; - - upcall_type = argv[idx_type]->arg; - - if (upcall_type[0] == 'n') - msg.im_msgtype = IGMPMSG_NOCACHE; - else if (upcall_type[1] == 'r') - msg.im_msgtype = IGMPMSG_WRONGVIF; - else if (upcall_type[1] == 'h') - msg.im_msgtype = IGMPMSG_WHOLEPKT; - else { - vty_out(vty, "Unknown kernel upcall type: %s%s", - upcall_type, VTY_NEWLINE); - return CMD_WARNING; - } - - msg.im_vif = atoi(argv[idx_number]->arg); - - /* Group address */ - group_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, group_str, &msg.im_dst); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Source address */ - source_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, source_str, &msg.im_src); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, safe_strerror(errno), VTY_NEWLINE); - return CMD_WARNING; - } - - msg.im_mbz = 0; /* Must be zero */ - - result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg)); - if (result) { - vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s", - sizeof(msg), result, VTY_NEWLINE); - return CMD_WARNING; - } + u_char uj = use_json(argc, argv); + if (argv[5]->arg) + ip_msdp_show_sa_sg(vty, argv[4]->arg, argv[5]->arg, uj); + else if (argv[4]->arg) + ip_msdp_show_sa_addr(vty, argv[4]->arg, uj); + else + ip_msdp_show_sa(vty, uj); return CMD_SUCCESS; } @@ -4759,17 +5964,33 @@ void pim_cmd_init() install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */ if_cmd_init (); + install_node (&debug_node, pim_debug_config_write); + install_element (CONFIG_NODE, &ip_multicast_routing_cmd); install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd); install_element (CONFIG_NODE, &ip_pim_rp_cmd); install_element (CONFIG_NODE, &no_ip_pim_rp_cmd); + install_element (CONFIG_NODE, &ip_pim_rp_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ip_pim_rp_prefix_list_cmd); + install_element (CONFIG_NODE, &ip_pim_register_suppress_cmd); + install_element (CONFIG_NODE, &no_ip_pim_register_suppress_cmd); + install_element (CONFIG_NODE, &ip_pim_joinprune_time_cmd); + install_element (CONFIG_NODE, &no_ip_pim_joinprune_time_cmd); + install_element (CONFIG_NODE, &ip_pim_keep_alive_cmd); + install_element (CONFIG_NODE, &no_ip_pim_keep_alive_cmd); + install_element (CONFIG_NODE, &ip_pim_packets_cmd); + install_element (CONFIG_NODE, &no_ip_pim_packets_cmd); install_element (CONFIG_NODE, &ip_ssmpingd_cmd); install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd); + install_element (CONFIG_NODE, &ip_msdp_peer_cmd); + install_element (CONFIG_NODE, &no_ip_msdp_peer_cmd); install_element (INTERFACE_NODE, &interface_ip_igmp_cmd); install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd); install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd); install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_version_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_version_cmd); install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd); @@ -4793,29 +6014,25 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_igmp_interface_cmd); install_element (VIEW_NODE, &show_ip_igmp_join_cmd); - install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd); install_element (VIEW_NODE, &show_ip_igmp_groups_cmd); install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); install_element (VIEW_NODE, &show_ip_igmp_sources_cmd); install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); - install_element (VIEW_NODE, &show_ip_igmp_querier_cmd); install_element (VIEW_NODE, &show_ip_pim_assert_cmd); install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd); install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd); install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd); - install_element (VIEW_NODE, &show_ip_pim_dr_cmd); - install_element (VIEW_NODE, &show_ip_pim_hello_cmd); install_element (VIEW_NODE, &show_ip_pim_interface_cmd); install_element (VIEW_NODE, &show_ip_pim_join_cmd); - install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd); - install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd); install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd); install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd); install_element (VIEW_NODE, &show_ip_pim_rpf_cmd); install_element (VIEW_NODE, &show_ip_pim_secondary_cmd); + install_element (VIEW_NODE, &show_ip_pim_state_cmd); install_element (VIEW_NODE, &show_ip_pim_upstream_cmd); install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element (VIEW_NODE, &show_ip_pim_rp_cmd); install_element (VIEW_NODE, &show_ip_multicast_cmd); install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (VIEW_NODE, &show_ip_mroute_count_cmd); @@ -4823,22 +6040,12 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_ssmpingd_cmd); install_element (VIEW_NODE, &show_debugging_pim_cmd); - install_element (ENABLE_NODE, &show_ip_pim_address_cmd); - install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_mroute_cmd); install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); - install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); - install_element (ENABLE_NODE, &test_pim_receive_assert_cmd); - install_element (ENABLE_NODE, &test_pim_receive_dump_cmd); - install_element (ENABLE_NODE, &test_pim_receive_hello_cmd); - install_element (ENABLE_NODE, &test_pim_receive_join_cmd); - install_element (ENABLE_NODE, &test_pim_receive_prune_cmd); - install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd); - install_element (ENABLE_NODE, &debug_igmp_cmd); install_element (ENABLE_NODE, &no_debug_igmp_cmd); install_element (ENABLE_NODE, &debug_igmp_events_cmd); @@ -4848,7 +6055,9 @@ void pim_cmd_init() install_element (ENABLE_NODE, &debug_igmp_trace_cmd); install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd); install_element (ENABLE_NODE, &debug_mroute_cmd); + install_element (ENABLE_NODE, &debug_mroute_detail_cmd); install_element (ENABLE_NODE, &no_debug_mroute_cmd); + install_element (ENABLE_NODE, &no_debug_mroute_detail_cmd); install_element (ENABLE_NODE, &debug_static_cmd); install_element (ENABLE_NODE, &no_debug_static_cmd); install_element (ENABLE_NODE, &debug_pim_cmd); @@ -4856,9 +6065,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &debug_pim_events_cmd); install_element (ENABLE_NODE, &no_debug_pim_events_cmd); install_element (ENABLE_NODE, &debug_pim_packets_cmd); - install_element (ENABLE_NODE, &debug_pim_packets_filter_cmd); install_element (ENABLE_NODE, &no_debug_pim_packets_cmd); - install_element (ENABLE_NODE, &no_debug_pim_packets_filter_cmd); install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd); install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd); install_element (ENABLE_NODE, &debug_pim_packetdump_recv_cmd); @@ -4869,6 +6076,15 @@ void pim_cmd_init() install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd); install_element (ENABLE_NODE, &debug_pim_zebra_cmd); install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element (ENABLE_NODE, &debug_msdp_cmd); + install_element (ENABLE_NODE, &no_debug_msdp_cmd); + install_element (ENABLE_NODE, &undebug_msdp_cmd); + install_element (ENABLE_NODE, &debug_msdp_events_cmd); + install_element (ENABLE_NODE, &no_debug_msdp_events_cmd); + install_element (ENABLE_NODE, &undebug_msdp_events_cmd); + install_element (ENABLE_NODE, &debug_msdp_packets_cmd); + install_element (ENABLE_NODE, &no_debug_msdp_packets_cmd); + install_element (ENABLE_NODE, &undebug_msdp_packets_cmd); install_element (CONFIG_NODE, &debug_igmp_cmd); install_element (CONFIG_NODE, &no_debug_igmp_cmd); @@ -4879,7 +6095,9 @@ void pim_cmd_init() install_element (CONFIG_NODE, &debug_igmp_trace_cmd); install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd); install_element (CONFIG_NODE, &debug_mroute_cmd); + install_element (CONFIG_NODE, &debug_mroute_detail_cmd); install_element (CONFIG_NODE, &no_debug_mroute_cmd); + install_element (CONFIG_NODE, &no_debug_mroute_detail_cmd); install_element (CONFIG_NODE, &debug_static_cmd); install_element (CONFIG_NODE, &no_debug_static_cmd); install_element (CONFIG_NODE, &debug_pim_cmd); @@ -4887,13 +6105,30 @@ void pim_cmd_init() install_element (CONFIG_NODE, &debug_pim_events_cmd); install_element (CONFIG_NODE, &no_debug_pim_events_cmd); install_element (CONFIG_NODE, &debug_pim_packets_cmd); - install_element (CONFIG_NODE, &debug_pim_packets_filter_cmd); install_element (CONFIG_NODE, &no_debug_pim_packets_cmd); - install_element (CONFIG_NODE, &no_debug_pim_packets_filter_cmd); install_element (CONFIG_NODE, &debug_pim_trace_cmd); install_element (CONFIG_NODE, &no_debug_pim_trace_cmd); install_element (CONFIG_NODE, &debug_ssmpingd_cmd); install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd); install_element (CONFIG_NODE, &debug_pim_zebra_cmd); install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element (CONFIG_NODE, &debug_msdp_cmd); + install_element (CONFIG_NODE, &no_debug_msdp_cmd); + install_element (CONFIG_NODE, &undebug_msdp_cmd); + install_element (CONFIG_NODE, &debug_msdp_events_cmd); + install_element (CONFIG_NODE, &no_debug_msdp_events_cmd); + install_element (CONFIG_NODE, &undebug_msdp_events_cmd); + install_element (CONFIG_NODE, &debug_msdp_packets_cmd); + install_element (CONFIG_NODE, &no_debug_msdp_packets_cmd); + install_element (CONFIG_NODE, &undebug_msdp_packets_cmd); + install_element (CONFIG_NODE, &ip_msdp_mesh_group_member_cmd); + install_element (CONFIG_NODE, &no_ip_msdp_mesh_group_member_cmd); + install_element (CONFIG_NODE, &ip_msdp_mesh_group_source_cmd); + install_element (CONFIG_NODE, &no_ip_msdp_mesh_group_source_cmd); + install_element (VIEW_NODE, &show_ip_msdp_peer_detail_cmd); + install_element (VIEW_NODE, &show_ip_msdp_sa_detail_cmd); + install_element (VIEW_NODE, &show_ip_msdp_sa_sg_cmd); + install_element (VIEW_NODE, &show_ip_msdp_mesh_group_cmd); + install_element (INTERFACE_NODE, &interface_pim_use_source_cmd); + install_element (INTERFACE_NODE, &interface_no_pim_use_source_cmd); } diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 34f350e36b..e08cefb29b 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -47,6 +47,7 @@ #define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" #define DEBUG_PIM_HELLO_PACKETS_STR "PIM Hello protocol packets\n" #define DEBUG_PIM_J_P_PACKETS_STR "PIM Join/Prune protocol packets\n" +#define DEBUG_PIM_PIM_REG_PACKETS_STR "PIM Register/Reg-Stop protocol packets\n" #define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n" #define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n" #define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" @@ -57,6 +58,12 @@ #define CLEAR_IP_PIM_STR "PIM clear commands\n" #define MROUTE_STR "IP multicast routing table\n" #define RIB_STR "IP unicast routing table\n" +#define CFG_MSDP_STR "Configure multicast source discovery protocol\n" +#define MSDP_STR "MSDP information\n" +#define DEBUG_MSDP_STR "MSDP protocol activity\n" +#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n" +#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n" +#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n" void pim_cmd_init(void); diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 1cd44f2539..3d7ae4ad22 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -37,7 +37,7 @@ static void on_trace(const char *label, struct interface *ifp, struct in_addr src) { if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src, src_str, sizeof(src_str)); zlog_debug("%s: from %s on %s", label, src_str, ifp->name); @@ -49,7 +49,7 @@ static void tlv_trace_bool(const char *label, const char *tlv_name, int isset, int value) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d", label, @@ -63,7 +63,7 @@ static void tlv_trace_uint16(const char *label, const char *tlv_name, int isset, uint16_t value) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", label, @@ -77,7 +77,7 @@ static void tlv_trace_uint32(const char *label, const char *tlv_name, int isset, uint32_t value) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", label, @@ -91,7 +91,7 @@ static void tlv_trace_uint32_hex(const char *label, const char *tlv_name, int isset, uint32_t value) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x", label, @@ -106,7 +106,7 @@ static void tlv_trace(const char *label, const char *tlv_name, int isset) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s", label, @@ -121,7 +121,7 @@ static void tlv_trace_list(const char *label, const char *tlv_name, int isset, struct list *addr_list) { if (isset) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p", label, @@ -181,7 +181,7 @@ int pim_hello_recv(struct interface *ifp, if (remain < PIM_TLV_MIN_SIZE) { if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s", __PRETTY_FUNCTION__, @@ -198,7 +198,7 @@ int pim_hello_recv(struct interface *ifp, if ((tlv_curr + option_len) > tlv_pastend) { if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s", __PRETTY_FUNCTION__, @@ -209,7 +209,7 @@ int pim_hello_recv(struct interface *ifp, } if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s", __PRETTY_FUNCTION__, @@ -267,7 +267,7 @@ int pim_hello_recv(struct interface *ifp, break; case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH: if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s", __PRETTY_FUNCTION__, @@ -277,7 +277,7 @@ int pim_hello_recv(struct interface *ifp, break; default: if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s", __PRETTY_FUNCTION__, @@ -326,7 +326,7 @@ int pim_hello_recv(struct interface *ifp, if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello missing holdtime from %s on interface %s", __PRETTY_FUNCTION__, @@ -349,10 +349,11 @@ int pim_hello_recv(struct interface *ifp, hello_option_override_interval, hello_option_dr_priority, hello_option_generation_id, - hello_option_addr_list); + hello_option_addr_list, + PIM_NEIGHBOR_SEND_DELAY); if (!neigh) { if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: failure creating PIM neighbor %s on interface %s", __PRETTY_FUNCTION__, @@ -373,15 +374,10 @@ int pim_hello_recv(struct interface *ifp, /* GenID mismatch ? */ if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) || (hello_option_generation_id != neigh->generation_id)) { - - /* GenID changed */ - - pim_upstream_rpf_genid_changed(neigh->source_addr); - /* GenID mismatch, then replace neighbor */ if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s", __PRETTY_FUNCTION__, @@ -400,10 +396,11 @@ int pim_hello_recv(struct interface *ifp, hello_option_override_interval, hello_option_dr_priority, hello_option_generation_id, - hello_option_addr_list); + hello_option_addr_list, + PIM_NEIGHBOR_SEND_NOW); if (!neigh) { if (PIM_DEBUG_PIM_HELLO) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: failure re-creating PIM neighbor %s on interface %s", __PRETTY_FUNCTION__, diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index bac9692caa..cc4f4f3dce 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -25,6 +25,8 @@ #include "memory.h" #include "prefix.h" #include "vrf.h" +#include "linklist.h" +#include "plist.h" #include "pimd.h" #include "pim_iface.h" @@ -38,14 +40,26 @@ #include "pim_sock.h" #include "pim_time.h" #include "pim_ssmpingd.h" +#include "pim_rp.h" struct interface *pim_regiface = NULL; +struct list *pim_ifchannel_list = NULL; static void pim_if_igmp_join_del_all(struct interface *ifp); -void pim_if_init() +void +pim_if_init (void) { vrf_iflist_create(VRF_DEFAULT); + pim_ifchannel_list = list_new(); + pim_ifchannel_list->cmp = (int (*)(void *, void *))pim_ifchannel_compare; +} + +void +pim_if_terminate (void) +{ + if (pim_ifchannel_list) + list_free (pim_ifchannel_list); } static void *if_list_clean(struct pim_interface *pim_ifp) @@ -78,15 +92,16 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) zassert(ifp); zassert(!ifp->info); - pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); + pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); if (!pim_ifp) { - zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp)); + zlog_err("PIM XCALLOC(%zu) failure", sizeof(*pim_ifp)); return 0; } pim_ifp->options = 0; pim_ifp->mroute_vif_index = -1; + pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; @@ -104,15 +119,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) if (igmp) PIM_IF_DO_IGMP(pim_ifp->options); -#if 0 - /* FIXME: Should join? */ PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); -#endif - pim_ifp->igmp_join_list = 0; - pim_ifp->igmp_socket_list = 0; - pim_ifp->pim_neighbor_list = 0; - pim_ifp->pim_ifchannel_list = 0; + pim_ifp->igmp_join_list = NULL; + pim_ifp->igmp_socket_list = NULL; + pim_ifp->pim_neighbor_list = NULL; + pim_ifp->pim_ifchannel_list = NULL; pim_ifp->pim_generation_id = 0; /* list of struct igmp_sock */ @@ -141,6 +153,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) return if_list_clean(pim_ifp); } pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free; + pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare; ifp->info = pim_ifp; @@ -164,16 +177,11 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->igmp_join_list) { pim_if_igmp_join_del_all(ifp); } - zassert(!pim_ifp->igmp_join_list); - zassert(pim_ifp->igmp_socket_list); - zassert(!listcount(pim_ifp->igmp_socket_list)); + pim_ifchannel_delete_all (ifp); + igmp_sock_delete_all (ifp); - zassert(pim_ifp->pim_neighbor_list); - zassert(!listcount(pim_ifp->pim_neighbor_list)); - - zassert(pim_ifp->pim_ifchannel_list); - zassert(!listcount(pim_ifp->pim_ifchannel_list)); + pim_neighbor_delete_all (ifp, "Interface removed from configuration"); if (PIM_MROUTE_IS_ENABLED) { pim_if_del_vif(ifp); @@ -185,7 +193,7 @@ void pim_if_delete(struct interface *ifp) XFREE(MTYPE_PIM_INTERFACE, pim_ifp); - ifp->info = 0; + ifp->info = NULL; } void pim_if_update_could_assert(struct interface *ifp) @@ -258,14 +266,10 @@ static int detect_primary_address_change(struct interface *ifp, int force_prim_as_any, const char *caller) { - struct pim_interface *pim_ifp; + struct pim_interface *pim_ifp = ifp->info; struct in_addr new_prim_addr; int changed; - pim_ifp = ifp->info; - if (!pim_ifp) - return 0; - if (force_prim_as_any) new_prim_addr = qpim_inaddr_any; else @@ -274,8 +278,8 @@ static int detect_primary_address_change(struct interface *ifp, changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; if (PIM_DEBUG_ZEBRA) { - char new_prim_str[100]; - char old_prim_str[100]; + char new_prim_str[INET_ADDRSTRLEN]; + char old_prim_str[INET_ADDRSTRLEN]; pim_inet4_dump("", new_prim_addr, new_prim_str, sizeof(new_prim_str)); pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); zlog_debug("%s: old=%s new=%s on interface %s: %s", @@ -286,57 +290,230 @@ static int detect_primary_address_change(struct interface *ifp, if (changed) { pim_ifp->primary_address = new_prim_addr; - - if (!PIM_IF_TEST_PIM(pim_ifp->options)) { - return changed; - } - - pim_addr_change(ifp); } return changed; } -static void detect_secondary_address_change(struct interface *ifp, +static int pim_sec_addr_comp(const void *p1, const void *p2) +{ + const struct pim_secondary_addr *sec1 = p1; + const struct pim_secondary_addr *sec2 = p2; + + if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr)) + return -1; + + if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr)) + return 1; + + return 0; +} + +static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr) +{ + XFREE(MTYPE_PIM_SEC_ADDR, sec_addr); +} + +static struct pim_secondary_addr * +pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr) +{ + struct pim_secondary_addr *sec_addr; + struct listnode *node; + + if (!pim_ifp->sec_addr_list) { + return NULL; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + if (sec_addr->addr.s_addr == addr.s_addr) { + return sec_addr; + } + } + + return NULL; +} + +static void pim_sec_addr_del(struct pim_interface *pim_ifp, + struct pim_secondary_addr *sec_addr) +{ + listnode_delete(pim_ifp->sec_addr_list, sec_addr); + pim_sec_addr_free(sec_addr); +} + +static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr) +{ + int changed = 0; + struct pim_secondary_addr *sec_addr; + + sec_addr = pim_sec_addr_find(pim_ifp, addr); + if (sec_addr) { + sec_addr->flags &= ~PIM_SEC_ADDRF_STALE; + return changed; + } + + if (!pim_ifp->sec_addr_list) { + pim_ifp->sec_addr_list = list_new(); + pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free; + pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp; + } + + sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr)); + if (!sec_addr) { + if (list_isempty(pim_ifp->sec_addr_list)) { + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } + return changed; + } + + changed = 1; + sec_addr->addr = addr; + listnode_add_sort(pim_ifp->sec_addr_list, sec_addr); + + return changed; +} + +static int pim_sec_addr_del_all(struct pim_interface *pim_ifp) +{ + int changed = 0; + + if (!pim_ifp->sec_addr_list) { + return changed; + } + if (!list_isempty(pim_ifp->sec_addr_list)) { + changed = 1; + /* remove all nodes and free up the list itself */ + list_delete_all_node(pim_ifp->sec_addr_list); + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } + + return changed; +} + +static int pim_sec_addr_update(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + struct pim_secondary_addr *sec_addr; + int changed = 0; + + if (pim_ifp->sec_addr_list) { + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + sec_addr->flags |= PIM_SEC_ADDRF_STALE; + } + } + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) { + continue; + } + + if (PIM_INADDR_IS_ANY(p->u.prefix4)) { + continue; + } + + if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) { + /* don't add the primary address into the secondary address list */ + continue; + } + + if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) { + changed = 1; + } + } + + if (pim_ifp->sec_addr_list) { + /* Drop stale entries */ + for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) { + if (sec_addr->flags & PIM_SEC_ADDRF_STALE) { + pim_sec_addr_del(pim_ifp, sec_addr); + changed = 1; + } + } + + /* If the list went empty free it up */ + if (list_isempty(pim_ifp->sec_addr_list)) { + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } + } + + return changed; +} + +static int detect_secondary_address_change(struct interface *ifp, + int force_prim_as_any, const char *caller) { - struct pim_interface *pim_ifp; - int changed; + struct pim_interface *pim_ifp = ifp->info; + int changed = 0; - pim_ifp = ifp->info; - if (!pim_ifp) - return; - - changed = 1; /* true */ - if (PIM_DEBUG_ZEBRA) - zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change", - __PRETTY_FUNCTION__, ifp->name); - - if (!changed) { - return; + if (force_prim_as_any) { + /* if primary address is being forced to zero just flush the + * secondary address list */ + changed = pim_sec_addr_del_all(pim_ifp); + } else { + /* re-evaluate the secondary address list */ + changed = pim_sec_addr_update(ifp); } - if (!PIM_IF_TEST_PIM(pim_ifp->options)) { - return; - } - - pim_addr_change(ifp); + return changed; } static void detect_address_change(struct interface *ifp, int force_prim_as_any, const char *caller) { - int prim_changed; + int changed = 0; + struct pim_interface *pim_ifp; - prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller); - if (prim_changed) { - /* no need to detect secondary change because - the reaction would be the same */ + pim_ifp = ifp->info; + if (!pim_ifp) return; + + if (detect_primary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; } - detect_secondary_address_change(ifp, caller); + if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; + } + + + if (changed) { + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + pim_addr_change(ifp); + } + + /* XXX: if we have unnumbered interfaces we need to run detect address + * address change on all of them when the lo address changes */ +} + +int pim_update_source_set(struct interface *ifp, struct in_addr source) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) { + return PIM_IFACE_NOT_FOUND; + } + + if (pim_ifp->update_source.s_addr == source.s_addr) { + return PIM_UPDATE_SOURCE_DUP; + } + + pim_ifp->update_source = source; + detect_address_change(ifp, 0 /* force_prim_as_any */, + __PRETTY_FUNCTION__); + + return PIM_SUCCESS; } void pim_if_addr_add(struct connected *ifc) @@ -406,6 +583,7 @@ void pim_if_addr_add(struct connected *ifc) if (pim_ifp->mroute_vif_index < 0) { pim_if_add_vif(ifp); } + pim_ifchannel_scan_forward_start (ifp); } } @@ -496,19 +674,59 @@ void pim_if_addr_add_all(struct interface *ifp) struct connected *ifc; struct listnode *node; struct listnode *nextnode; + int v4_addrs = 0; + int v6_addrs = 0; + struct pim_interface *pim_ifp = ifp->info; + /* PIM/IGMP enabled ? */ - if (!ifp->info) + if (!pim_ifp) return; for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { struct prefix *p = ifc->address; if (p->family != AF_INET) - continue; + { + v6_addrs++; + continue; + } + v4_addrs++; pim_if_addr_add(ifc); } + + if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) + { + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + + /* Interface has a valid primary address ? */ + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + + /* Interface has a valid socket ? */ + if (pim_ifp->pim_sock_fd < 0) { + if (pim_sock_add(ifp)) { + zlog_warn("Failure creating PIM socket for interface %s", + ifp->name); + } + } + + } + } /* pim */ + } + if (PIM_MROUTE_IS_ENABLED) { + /* + * PIM or IGMP is enabled on interface, and there is at least one + * address assigned, then try to create a vif_index. + */ + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); + } + pim_ifchannel_scan_forward_start (ifp); + } + + pim_rp_setup(); + pim_rp_check_on_if_add(pim_ifp); } void pim_if_addr_del_all(struct interface *ifp) @@ -529,6 +747,9 @@ void pim_if_addr_del_all(struct interface *ifp) pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); } + + pim_rp_setup(); + pim_i_am_rp_re_evaluate(); } void pim_if_addr_del_all_igmp(struct interface *ifp) @@ -571,17 +792,28 @@ void pim_if_addr_del_all_pim(struct interface *ifp) } } -static struct in_addr find_first_nonsec_addr(struct interface *ifp) +struct in_addr +pim_find_primary_addr (struct interface *ifp) { struct connected *ifc; struct listnode *node; struct in_addr addr; + int v4_addrs = 0; + int v6_addrs = 0; + struct pim_interface *pim_ifp = ifp->info; + + if (pim_ifp && PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) { + return pim_ifp->update_source; + } for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { struct prefix *p = ifc->address; - + if (p->family != AF_INET) - continue; + { + v6_addrs++; + continue; + } if (PIM_INADDR_IS_ANY(p->u.prefix4)) { zlog_warn("%s: null IPv4 address connected to interface %s", @@ -589,22 +821,33 @@ static struct in_addr find_first_nonsec_addr(struct interface *ifp) continue; } + v4_addrs++; + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) continue; return p->u.prefix4; } + /* + * If we have no v4_addrs and v6 is configured + * We probably are using unnumbered + * So let's grab the loopbacks v4 address + * and use that as the primary address + */ + if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) + { + struct interface *lo_ifp; + lo_ifp = if_lookup_by_name_vrf ("lo", VRF_DEFAULT); + if (lo_ifp) + return pim_find_primary_addr (lo_ifp); + } + addr.s_addr = PIM_NET_INADDR_ANY; return addr; } -struct in_addr pim_find_primary_addr(struct interface *ifp) -{ - return find_first_nonsec_addr(ifp); -} - static int pim_iface_vif_index = 0; static int @@ -800,9 +1043,9 @@ int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex) struct interface *ifp; ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT); - pim_ifp = ifp->info; - if (!pim_ifp) + if (!ifp || !ifp->info) return -1; + pim_ifp = ifp->info; return pim_ifp->mroute_vif_index; } @@ -899,14 +1142,14 @@ struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, } if (PIM_DEBUG_PIM_TRACE) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s: neighbor not found for address %s on interface %s", __PRETTY_FUNCTION__, addr_str, ifp->name); } - return 0; + return NULL; } long pim_if_t_suppressed_msec(struct interface *ifp) @@ -985,8 +1228,8 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr); if (join_fd < 0) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", @@ -995,13 +1238,13 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, return 0; } - ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); + ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s", + zlog_err("%s: XCALLOC(%zu) failure for IGMP group %s source %s on interface %s", __PRETTY_FUNCTION__, sizeof(*ij), group_str, source_str, ifp->name); close(join_fd); @@ -1045,8 +1288,8 @@ int pim_if_igmp_join_add(struct interface *ifp, ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); if (ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s", @@ -1057,8 +1300,8 @@ int pim_if_igmp_join_add(struct interface *ifp, ij = igmp_join_new(ifp, group_addr, source_addr); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s", @@ -1068,8 +1311,8 @@ int pim_if_igmp_join_add(struct interface *ifp, } if (PIM_DEBUG_IGMP_EVENTS) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", @@ -1106,8 +1349,8 @@ int pim_if_igmp_join_del(struct interface *ifp, ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: could not find IGMP group %s source %s on interface %s", @@ -1117,14 +1360,13 @@ int pim_if_igmp_join_del(struct interface *ifp, } if (close(ij->sock_fd)) { - int e = errno; - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", __PRETTY_FUNCTION__, - ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e)); + ij->sock_fd, group_str, source_str, ifp->name, errno, safe_strerror(errno)); /* warning only */ } listnode_delete(pim_ifp->igmp_join_list, ij); @@ -1249,3 +1491,40 @@ void pim_if_create_pimreg (void) pim_if_new(pim_regiface, 0, 0); } } + +int +pim_if_connected_to_source (struct interface *ifp, struct in_addr src) +{ + struct listnode *cnode; + struct connected *c; + struct prefix p; + + p.family = AF_INET; + p.u.prefix4 = src; + p.prefixlen = IPV4_MAX_BITLEN; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if ((c->address->family == AF_INET) && + prefix_match (CONNECTED_PREFIX (c), &p)) + { + return 1; + } + } + + return 0; +} + +struct interface * +pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id) +{ + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist(vrf_id), ifnode, ifp)) + { + if (pim_if_connected_to_source (ifp, src) && ifp->info) + return ifp; + } + return NULL; +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index e56559ca46..244de598db 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -16,7 +16,7 @@ along with this program; see the file COPYING; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + */ #ifndef PIM_IFACE_H #define PIM_IFACE_H @@ -58,12 +58,26 @@ enum pim_interface_type { PIM_INTERFACE_SM }; +enum pim_secondary_addr_flags { + PIM_SEC_ADDRF_NONE = 0, + PIM_SEC_ADDRF_STALE = (1 << 0) +}; + +struct pim_secondary_addr { + struct in_addr addr; + enum pim_secondary_addr_flags flags; +}; + struct pim_interface { enum pim_interface_type itype; uint32_t options; /* bit vector */ ifindex_t mroute_vif_index; struct in_addr primary_address; /* remember addr to detect change */ + struct list *sec_addr_list; /* list of struct pim_secondary_addr */ + struct in_addr update_source; /* user can statically set the primary + * address of the interface */ + int igmp_version; /* IGMP version */ int igmp_default_robustness_variable; /* IGMPv3 QRV */ int igmp_default_query_interval; /* IGMPv3 secs between general queries */ int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ @@ -106,6 +120,7 @@ struct pim_interface { }; extern struct interface *pim_regiface; +extern struct list *pim_ifchannel_list; /* if default_holdtime is set (>= 0), use it; otherwise default_holdtime is 3.5 * hello_period @@ -116,6 +131,7 @@ extern struct interface *pim_regiface; ((pim_ifp)->pim_default_holdtime)) void pim_if_init(void); +void pim_if_terminate (void); struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim); void pim_if_delete(struct interface *ifp); @@ -126,6 +142,8 @@ void pim_if_addr_del_all(struct interface *ifp); void pim_if_addr_del_all_igmp(struct interface *ifp); void pim_if_addr_del_all_pim(struct interface *ifp); +struct interface *pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id); + int pim_if_add_vif(struct interface *ifp); int pim_if_del_vif(struct interface *ifp); void pim_if_add_vif_all(void); @@ -166,4 +184,8 @@ void pim_if_update_join_desired(struct pim_interface *pim_ifp); void pim_if_update_assert_tracking_desired(struct interface *ifp); void pim_if_create_pimreg(void); + +int pim_if_connected_to_source (struct interface *ifp, struct in_addr src); +int pim_update_source_set(struct interface *ifp, struct in_addr source); + #endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 7afb7a5bdf..07318791e7 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -24,6 +24,7 @@ #include "thread.h" #include "memory.h" #include "if.h" +#include "vrf.h" #include "pimd.h" #include "pim_str.h" @@ -36,13 +37,100 @@ #include "pim_join.h" #include "pim_rpf.h" #include "pim_macro.h" +#include "pim_oil.h" +#include "pim_upstream.h" + +int +pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2) +{ + struct pim_interface *pim_ifp1; + struct pim_interface *pim_ifp2; + + if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr)) + return -1; + + if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr)) + return 1; + + if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr)) + return -1; + + if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr)) + return 1; + + pim_ifp1 = ch1->interface->info; + pim_ifp2 = ch2->interface->info; + if (ntohl(pim_ifp1->primary_address.s_addr) < ntohl(pim_ifp2->primary_address.s_addr)) + return -1; + + if (ntohl(pim_ifp1->primary_address.s_addr) > ntohl(pim_ifp2->primary_address.s_addr)) + return 1; + + if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index) + return -1; + + if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index) + return 1; + + return 0; +} + +/* + * A (*,G) or a (*,*) is going away + * remove the parent pointer from + * those pointing at us + */ +static void +pim_ifchannel_remove_children (struct pim_ifchannel *ch) +{ + struct pim_ifchannel *child; + + if (!ch->sources) + return; + + while (!list_isempty (ch->sources)) + { + child = listnode_head (ch->sources); + child->parent = NULL; + listnode_delete (ch->sources, child); + } +} + +/* + * A (*,G) or a (*,*) is being created + * find all the children that would point + * at us. + */ +static void +pim_ifchannel_find_new_children (struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp = ch->interface->info; + struct pim_ifchannel *child; + struct listnode *ch_node; + + // Basic Sanity that we are not being silly + if ((ch->sg.src.s_addr != INADDR_ANY) && + (ch->sg.grp.s_addr != INADDR_ANY)) + return; + + if ((ch->sg.src.s_addr == INADDR_ANY) && + (ch->sg.grp.s_addr == INADDR_ANY)) + return; + + for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child)) + { + if ((ch->sg.grp.s_addr != INADDR_ANY) && + (child->sg.grp.s_addr == ch->sg.grp.s_addr) && + (child != ch)) + { + child->parent = ch; + listnode_add_sort (ch->sources, child); + } + } +} void pim_ifchannel_free(struct pim_ifchannel *ch) { - zassert(!ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - zassert(!ch->t_ifassert_timer); - XFREE(MTYPE_PIM_IFCHANNEL, ch); } @@ -51,48 +139,87 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) struct pim_interface *pim_ifp; pim_ifp = ch->interface->info; - zassert(pim_ifp); + + if (ch->upstream->channel_oil) + { + pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + /* + * Do we have any S,G's that are inheriting? + * Nuke from on high too. + */ + if (ch->upstream->sources) + { + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (ch->upstream->sources, up_node, child)) + pim_channel_del_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + } + } + + /* + * When this channel is removed + * we need to find all our children + * and make sure our pointers are fixed + */ + pim_ifchannel_remove_children (ch); + + if (ch->sources) + list_delete (ch->sources); if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { pim_upstream_update_join_desired(ch->upstream); } - pim_upstream_del(ch->upstream); + pim_upstream_del(ch->upstream, __PRETTY_FUNCTION__); + ch->upstream = NULL; THREAD_OFF(ch->t_ifjoin_expiry_timer); THREAD_OFF(ch->t_ifjoin_prune_pending_timer); THREAD_OFF(ch->t_ifassert_timer); + if (ch->parent) + { + listnode_delete (ch->parent->sources, ch); + ch->parent = NULL; + } /* notice that listnode_delete() can't be moved into pim_ifchannel_free() because the later is called by list_delete_all_node() */ listnode_delete(pim_ifp->pim_ifchannel_list, ch); + listnode_delete(pim_ifchannel_list, ch); pim_ifchannel_free(ch); } -#define IFCHANNEL_NOINFO(ch) \ - ( \ - ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \ - && \ - ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \ - && \ - ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \ - ) +void +pim_ifchannel_delete_all (struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *ifchannel_node; + struct listnode *ifchannel_nextnode; + struct pim_ifchannel *ifchannel; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, ifchannel_node, + ifchannel_nextnode, ifchannel)) + { + pim_ifchannel_delete (ifchannel); + } +} static void delete_on_noinfo(struct pim_ifchannel *ch) { - if (IFCHANNEL_NOINFO(ch)) { - - /* In NOINFO state, timers should have been cleared */ - zassert(!ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - zassert(!ch->t_ifassert_timer); - + if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO && + ch->ifjoin_state == PIM_IFJOIN_NOINFO && + ch->t_ifjoin_expiry_timer == NULL) pim_ifchannel_delete(ch); - } + } void pim_ifchannel_ifjoin_switch(const char *caller, @@ -101,6 +228,14 @@ void pim_ifchannel_ifjoin_switch(const char *caller, { enum pim_ifjoin_state old_state = ch->ifjoin_state; + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s", + ch->interface->name, + ch->sg_str, + pim_ifchannel_ifjoin_name (ch->ifjoin_state), + pim_ifchannel_ifjoin_name (new_state)); + + if (old_state == new_state) { if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s calledby %s: non-transition on state %d (%s)", @@ -110,25 +245,68 @@ void pim_ifchannel_ifjoin_switch(const char *caller, return; } - zassert(old_state != new_state); - ch->ifjoin_state = new_state; + if (ch->sg.src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = ch->upstream; + struct pim_upstream *child; + struct listnode *up_node; + + if (up) + { + if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) + { + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + struct channel_oil *c_oil = child->channel_oil; + struct pim_interface *pim_ifp = ch->interface->info; + + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s %s: Prune(S,G)=%s from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, up->sg_str); + if (!c_oil) + continue; + + if (!pim_upstream_evaluate_join_desired (child)) + pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + + /* + * If the S,G has no if channel and the c_oil still + * has output here then the *,G was supplying the implied + * if channel. So remove it. + */ + if (!ch && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + } + } + if (ch->ifjoin_state == PIM_IFJOIN_JOIN) + { + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s %s: Join(S,G)=%s from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, up->sg_str); + + if (pim_upstream_evaluate_join_desired (child)) + { + pim_channel_add_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + pim_upstream_switch (child, PIM_UPSTREAM_JOINED); + } + } + } + } + } /* Transition to/from NOINFO ? */ - if ( - (old_state == PIM_IFJOIN_NOINFO) - || - (new_state == PIM_IFJOIN_NOINFO) - ) { + if ((old_state == PIM_IFJOIN_NOINFO) || + (new_state == PIM_IFJOIN_NOINFO)) { if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s", + zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s", ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"), - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); } /* @@ -145,9 +323,12 @@ void pim_ifchannel_ifjoin_switch(const char *caller, const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state) { switch (ifjoin_state) { - case PIM_IFJOIN_NOINFO: return "NOINFO"; - case PIM_IFJOIN_JOIN: return "JOIN"; - case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + case PIM_IFJOIN_NOINFO: return "NOINFO"; + case PIM_IFJOIN_JOIN: return "JOIN"; + case PIM_IFJOIN_PRUNE: return "PRUNE"; + case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + case PIM_IFJOIN_PRUNE_TMP: return "PRUNET"; + case PIM_IFJOIN_PRUNE_PENDING_TMP: return "PRUNEPT"; } return "ifjoin_bad_state"; @@ -180,77 +361,8 @@ void reset_ifassert_state(struct pim_ifchannel *ch) qpim_infinite_assert_metric); } -static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) -{ - struct pim_ifchannel *ch; - struct pim_interface *pim_ifp; - struct pim_upstream *up; - - pim_ifp = ifp->info; - zassert(pim_ifp); - - up = pim_upstream_add(source_addr, group_addr, NULL); - if (!up) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); - zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); - return 0; - } - - ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); - if (!ch) { - zlog_err("%s: PIM XMALLOC(%zu) failure", - __PRETTY_FUNCTION__, sizeof(*ch)); - return 0; - } - - ch->flags = 0; - ch->upstream = up; - ch->interface = ifp; - ch->source_addr = source_addr; - ch->group_addr = group_addr; - ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; - - ch->ifjoin_state = PIM_IFJOIN_NOINFO; - ch->t_ifjoin_expiry_timer = 0; - ch->t_ifjoin_prune_pending_timer = 0; - ch->ifjoin_creation = 0; - - ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); - ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); - - ch->ifassert_winner.s_addr = 0; - - /* Assert state */ - ch->t_ifassert_timer = 0; - reset_ifassert_state(ch); - if (pim_macro_ch_could_assert_eval(ch)) - PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); - else - PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); - - if (pim_macro_assert_tracking_desired_eval(ch)) - PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); - else - PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); - - /* Attach to list */ - listnode_add(pim_ifp->pim_ifchannel_list, ch); - - zassert(IFCHANNEL_NOINFO(ch)); - - return ch; -} - struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) + struct prefix_sg *sg) { struct pim_interface *pim_ifp; struct listnode *ch_node; @@ -261,21 +373,17 @@ struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_ifp = ifp->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, + pim_str_sg_dump (sg), ifp->name); return 0; } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { if ( - (source_addr.s_addr == ch->source_addr.s_addr) && - (group_addr.s_addr == ch->group_addr.s_addr) + (sg->src.s_addr == ch->sg.src.s_addr) && + (sg->grp.s_addr == ch->sg.grp.s_addr) ) { return ch; } @@ -291,13 +399,9 @@ static void ifmembership_set(struct pim_ifchannel *ch, return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s", + zlog_debug("%s: (S,G)=%s membership now is %s on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, + ch->sg_str, membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", ch->interface->name); } @@ -339,29 +443,110 @@ void pim_ifchannel_delete_on_noinfo(struct interface *ifp) } } -struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) +/* + * For a given Interface, if we are given a S,G + * Find the *,G (If we have it). + * If we are passed a *,G, find the *,* ifchannel + * if we have it. + */ +static struct pim_ifchannel * +pim_ifchannel_find_parent (struct pim_ifchannel *ch) { + struct prefix_sg parent_sg = ch->sg; + struct pim_ifchannel *parent = NULL; + + // (S,G) + if ((parent_sg.src.s_addr != INADDR_ANY) && + (parent_sg.grp.s_addr != INADDR_ANY)) + { + parent_sg.src.s_addr = INADDR_ANY; + parent = pim_ifchannel_find (ch->interface, &parent_sg); + + if (parent) + listnode_add (parent->sources, ch); + return parent; + } + + return NULL; +} + +struct pim_ifchannel * +pim_ifchannel_add(struct interface *ifp, + struct prefix_sg *sg, int flags) +{ + struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - char src_str[100]; - char grp_str[100]; + struct pim_upstream *up; - ch = pim_ifchannel_find(ifp, source_addr, group_addr); + ch = pim_ifchannel_find(ifp, sg); if (ch) return ch; - ch = pim_ifchannel_new(ifp, source_addr, group_addr); - if (ch) - return ch; - - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + pim_ifp = ifp->info; - return 0; + up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__); + if (!up) { + zlog_err("%s: could not attach upstream (S,G)=%s on interface %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump (sg), ifp->name); + return NULL; + } + + ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); + if (!ch) { + zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s", + __PRETTY_FUNCTION__, + up->sg_str, ifp->name); + + pim_upstream_del (up, __PRETTY_FUNCTION__); + return NULL; + } + + ch->flags = 0; + ch->upstream = up; + ch->interface = ifp; + ch->sg = *sg; + pim_str_sg_set (sg, ch->sg_str); + ch->parent = pim_ifchannel_find_parent (ch); + if (ch->sg.src.s_addr == INADDR_ANY) + { + ch->sources = list_new (); + ch->sources->cmp = (int (*)(void *, void *))pim_ifchannel_compare; + } + else + ch->sources = NULL; + + pim_ifchannel_find_new_children (ch); + ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; + + ch->ifjoin_state = PIM_IFJOIN_NOINFO; + ch->t_ifjoin_expiry_timer = NULL; + ch->t_ifjoin_prune_pending_timer = NULL; + ch->ifjoin_creation = 0; + + ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); + ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); + + ch->ifassert_winner.s_addr = 0; + + /* Assert state */ + ch->t_ifassert_timer = NULL; + reset_ifassert_state(ch); + if (pim_macro_ch_could_assert_eval(ch)) + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + else + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (pim_macro_assert_tracking_desired_eval(ch)) + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + else + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + /* Attach to list */ + listnode_add_sort(pim_ifp->pim_ifchannel_list, ch); + listnode_add_sort(pim_ifchannel_list, ch); + + return ch; } static void ifjoin_to_noinfo(struct pim_ifchannel *ch) @@ -375,13 +560,9 @@ static int on_ifjoin_expiry_timer(struct thread *t) { struct pim_ifchannel *ch; - zassert(t); ch = THREAD_ARG(t); - zassert(ch); - ch->t_ifjoin_expiry_timer = 0; - - zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN); + ch->t_ifjoin_expiry_timer = NULL; ifjoin_to_noinfo(ch); /* ch may have been deleted */ @@ -389,64 +570,37 @@ static int on_ifjoin_expiry_timer(struct thread *t) return 0; } -static void prune_echo(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) -{ - struct pim_interface *pim_ifp; - struct in_addr neigh_dst_addr; - - pim_ifp = ifp->info; - zassert(pim_ifp); - - neigh_dst_addr = pim_ifp->primary_address; - - if (PIM_DEBUG_PIM_EVENTS) { - char source_str[100]; - char group_str[100]; - char neigh_dst_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str)); - zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s", - __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name); - } - - pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr, - 0 /* boolean: send_join=false (prune) */); -} - static int on_ifjoin_prune_pending_timer(struct thread *t) { struct pim_ifchannel *ch; int send_prune_echo; /* boolean */ struct interface *ifp; struct pim_interface *pim_ifp; - struct in_addr ch_source; - struct in_addr ch_group; - zassert(t); ch = THREAD_ARG(t); - zassert(ch); - ch->t_ifjoin_prune_pending_timer = 0; + ch->t_ifjoin_prune_pending_timer = NULL; - zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); + if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) + { + /* Send PruneEcho(S,G) ? */ + ifp = ch->interface; + pim_ifp = ifp->info; + send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); - /* Send PruneEcho(S,G) ? */ - ifp = ch->interface; - pim_ifp = ifp->info; - send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); + ifjoin_to_noinfo(ch); + /* from here ch may have been deleted */ - /* Save (S,G) */ - ch_source = ch->source_addr; - ch_group = ch->group_addr; - - ifjoin_to_noinfo(ch); - /* from here ch may have been deleted */ - - if (send_prune_echo) - prune_echo(ifp, ch_source, ch_group); + if (send_prune_echo) + pim_joinprune_send (ifp, pim_ifp->primary_address, + ch->upstream, 0); + } + else + { + zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state", + __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg), + pim_ifchannel_ifjoin_name (ch->ifjoin_state)); + } return 0; } @@ -454,15 +608,14 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) static void check_recv_upstream(int is_join, struct interface *recv_ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, int holdtime) { struct pim_upstream *up; /* Upstream (S,G) in Joined state ? */ - up = pim_upstream_find(source_addr, group_addr); + up = pim_upstream_find(sg); if (!up) return; if (up->join_state != PIM_UPSTREAM_JOINED) @@ -470,31 +623,23 @@ static void check_recv_upstream(int is_join, /* Upstream (S,G) in Joined state */ - if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { /* RPF'(S,G) not found */ - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s %s: RPF'(%s,%s) not found", + zlog_warn("%s %s: RPF'%s not found", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str); + up->sg_str); return; } /* upstream directed to RPF'(S,G) ? */ - if (upstream.s_addr != up->rpf.rpf_addr.s_addr) { - char src_str[100]; - char grp_str[100]; - char up_str[100]; - char rpf_str[100]; - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) { + char up_str[INET_ADDRSTRLEN]; + char rpf_str[PREFIX_STRLEN]; pim_inet4_dump("", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s", + pim_addr_dump("", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, + up->sg_str, up_str, rpf_str, recv_ifp->name); return; } @@ -502,7 +647,7 @@ static void check_recv_upstream(int is_join, if (is_join) { /* Join(S,G) to RPF'(S,G) */ - pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime); + pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime); return; } @@ -512,26 +657,25 @@ static void check_recv_upstream(int is_join, if (source_flags & PIM_WILDCARD_BIT_MASK) { /* Prune(*,G) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", - up, up->rpf.rpf_addr); + up, up->rpf.rpf_addr.u.prefix4); return; } /* Prune(S,G,rpt) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", - up, up->rpf.rpf_addr); + up, up->rpf.rpf_addr.u.prefix4); return; } /* Prune(S,G) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up, - up->rpf.rpf_addr); + up->rpf.rpf_addr.u.prefix4); } static int nonlocal_upstream(int is_join, struct interface *recv_ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { @@ -543,17 +687,13 @@ static int nonlocal_upstream(int is_join, is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr); - if (PIM_DEBUG_PIM_TRACE) { - char up_str[100]; - char src_str[100]; - char grp_str[100]; + if (PIM_DEBUG_PIM_TRACE_DETAIL) { + char up_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s", + zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s", __PRETTY_FUNCTION__, is_join ? "join" : "prune", - src_str, grp_str, + pim_str_sg_dump (sg), is_local ? "local" : "non-local", up_str, recv_ifp->name); } @@ -565,7 +705,7 @@ static int nonlocal_upstream(int is_join, Since recv upstream addr was not directed to our primary address, check if we should react to it in any way. */ - check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr, + check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags, holdtime); return 1; /* non-local */ @@ -574,8 +714,7 @@ static int nonlocal_upstream(int is_join, void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { @@ -583,11 +722,11 @@ void pim_ifchannel_join_add(struct interface *ifp, struct pim_ifchannel *ch; if (nonlocal_upstream(1 /* join */, ifp, upstream, - source_addr, group_addr, source_flags, holdtime)) { + sg, source_flags, holdtime)) { return; } - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); if (!ch) return; @@ -608,15 +747,11 @@ void pim_ifchannel_join_add(struct interface *ifp, address of the join message is our primary address. */ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { - char src_str[100]; - char grp_str[100]; - char neigh_str[100]; - pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + char neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); - zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s", + zlog_warn("%s: Assert Loser recv Join%s from %s on %s", __PRETTY_FUNCTION__, - src_str, grp_str, neigh_str, ifp->name); + ch->sg_str, neigh_str, ifp->name); assert_action_a5(ch); } @@ -628,6 +763,7 @@ void pim_ifchannel_join_add(struct interface *ifp, case PIM_IFJOIN_NOINFO: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); if (pim_macro_chisin_oiflist(ch)) { + pim_upstream_inherited_olist (ch->upstream); pim_forward_start(ch); } break; @@ -660,16 +796,26 @@ void pim_ifchannel_join_add(struct interface *ifp, } THREAD_OFF(ch->t_ifjoin_expiry_timer); break; + case PIM_IFJOIN_PRUNE: + if (source_flags & PIM_ENCODE_RPT_BIT) + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + break; case PIM_IFJOIN_PRUNE_PENDING: - zassert(!ch->t_ifjoin_expiry_timer); - zassert(ch->t_ifjoin_prune_pending_timer); THREAD_OFF(ch->t_ifjoin_prune_pending_timer); - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + if (source_flags & PIM_ENCODE_RPT_BIT) + { + THREAD_OFF(ch->t_ifjoin_expiry_timer); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + } + else + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + break; + case PIM_IFJOIN_PRUNE_TMP: + break; + case PIM_IFJOIN_PRUNE_PENDING_TMP: break; } - zassert(!IFCHANNEL_NOINFO(ch)); - if (holdtime != 0xFFFF) { THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, on_ifjoin_expiry_timer, @@ -679,66 +825,112 @@ void pim_ifchannel_join_add(struct interface *ifp, void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; int jp_override_interval_msec; if (nonlocal_upstream(0 /* prune */, ifp, upstream, - source_addr, group_addr, source_flags, holdtime)) { + sg, source_flags, holdtime)) { return; } - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_find (ifp, sg); + if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d", + __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags); + return; + } + + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); if (!ch) return; + pim_ifp = ifp->info; + switch (ch->ifjoin_state) { case PIM_IFJOIN_NOINFO: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + PIM_IF_FLAG_SET_S_G_RPT(ch->flags); + ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING; + if (listcount(pim_ifp->pim_neighbor_list) > 1) + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + else + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } + break; case PIM_IFJOIN_PRUNE_PENDING: /* nothing to do */ break; case PIM_IFJOIN_JOIN: - { - struct pim_interface *pim_ifp; + THREAD_OFF(ch->t_ifjoin_expiry_timer); - pim_ifp = ifp->info; + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); - zassert(ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - - THREAD_OFF(ch->t_ifjoin_expiry_timer); - - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); - - if (listcount(pim_ifp->pim_neighbor_list) > 1) { - jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + if (listcount(pim_ifp->pim_neighbor_list) > 1) + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + else + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + break; + case PIM_IFJOIN_PRUNE: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); } - else { - jp_override_interval_msec = 0; /* schedule to expire immediately */ - /* If we called ifjoin_prune() directly instead, care should - be taken not to use "ch" afterwards since it would be - deleted. */ + break; + case PIM_IFJOIN_PRUNE_TMP: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + ch->ifjoin_state = PIM_IFJOIN_PRUNE; + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } + break; + case PIM_IFJOIN_PRUNE_PENDING_TMP: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING; + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); } - - THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, - on_ifjoin_prune_pending_timer, - ch, jp_override_interval_msec); - - zassert(!ch->t_ifjoin_expiry_timer); - zassert(ch->t_ifjoin_prune_pending_timer); - } break; } - } void pim_ifchannel_local_membership_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) + struct prefix_sg *sg) { struct pim_ifchannel *ch; struct pim_interface *pim_ifp; @@ -750,19 +942,37 @@ void pim_ifchannel_local_membership_add(struct interface *ifp, if (!PIM_IF_TEST_PIM(pim_ifp->options)) return; - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP); if (!ch) { return; } ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); - zassert(!IFCHANNEL_NOINFO(ch)); + if (sg->src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = pim_upstream_find (sg); + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + if (PIM_DEBUG_EVENTS) + zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, ifp->name, up->sg_str); + + if (pim_upstream_evaluate_join_desired (child)) + { + pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM); + pim_upstream_switch (child, PIM_UPSTREAM_JOINED); + } + } + } } void pim_ifchannel_local_membership_del(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) + struct prefix_sg *sg) { struct pim_ifchannel *ch; struct pim_interface *pim_ifp; @@ -774,12 +984,41 @@ void pim_ifchannel_local_membership_del(struct interface *ifp, if (!PIM_IF_TEST_PIM(pim_ifp->options)) return; - ch = pim_ifchannel_find(ifp, source_addr, group_addr); + ch = pim_ifchannel_find(ifp, sg); if (!ch) return; ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + if (sg->src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = pim_upstream_find (sg); + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + struct channel_oil *c_oil = child->channel_oil; + struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg); + struct pim_interface *pim_ifp = ifp->info; + + if (PIM_DEBUG_EVENTS) + zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s", + __FILE__, __PRETTY_FUNCTION__, + up->sg_str, ifp->name, child->sg_str); + + if (c_oil && !pim_upstream_evaluate_join_desired (child)) + pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM); + + /* + * If the S,G has no if channel and the c_oil still + * has output here then the *,G was supplying the implied + * if channel. So remove it. + */ + if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM); + } + } delete_on_noinfo(ch); } @@ -792,10 +1031,10 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", __PRETTY_FUNCTION__, src_str, grp_str, ch->interface->name, @@ -834,12 +1073,12 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - char old_addr_str[100]; - char new_addr_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char old_addr_str[INET_ADDRSTRLEN]; + char new_addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); pim_inet4_dump("", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", @@ -872,10 +1111,10 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", __PRETTY_FUNCTION__, src_str, grp_str, ch->interface->name, @@ -895,3 +1134,88 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) } } } + +/* + * If we have a new pim interface, check to + * see if any of the pre-existing channels have + * their upstream out that way and turn on forwarding + * for that ifchannel then. + */ +void +pim_ifchannel_scan_forward_start (struct interface *new_ifp) +{ + struct listnode *ifnode; + struct interface *ifp; + struct pim_interface *new_pim_ifp = new_ifp->info; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) + { + struct pim_interface *loop_pim_ifp = ifp->info; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + if (!loop_pim_ifp) + continue; + + if (new_pim_ifp == loop_pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch)) + { + if (ch->ifjoin_state == PIM_IFJOIN_JOIN) + { + struct pim_upstream *up = ch->upstream; + if ((!up->channel_oil) && + (up->rpf.source_nexthop.interface == new_ifp)) + pim_forward_start (ch); + } + } + } +} + +/* + * Downstream per-interface (S,G,rpt) state machine + * states that we need to move (S,G,rpt) items + * into different states at the start of the + * reception of a *,G join as well, when + * we get End of Message + */ +void +pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom) +{ + struct pim_ifchannel *child; + struct listnode *ch_node; + + if (PIM_DEBUG_PIM_TRACE) + zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__, + pim_ifchannel_ifjoin_name(ch->ifjoin_state), + ch->sg_str, eom); + if (!ch->sources) + return; + + for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child)) + { + if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) + continue; + + switch (child->ifjoin_state) + { + case PIM_IFJOIN_NOINFO: + case PIM_IFJOIN_JOIN: + break; + case PIM_IFJOIN_PRUNE: + if (!eom) + child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP; + break; + case PIM_IFJOIN_PRUNE_PENDING: + if (!eom) + child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP; + break; + case PIM_IFJOIN_PRUNE_TMP: + case PIM_IFJOIN_PRUNE_PENDING_TMP: + if (eom) + child->ifjoin_state = PIM_IFJOIN_NOINFO; + break; + } + } +} diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index ce753222ee..bfe632135c 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -24,6 +24,7 @@ #include #include "if.h" +#include "prefix.h" #include "pim_upstream.h" @@ -35,7 +36,10 @@ enum pim_ifmembership { enum pim_ifjoin_state { PIM_IFJOIN_NOINFO, PIM_IFJOIN_JOIN, - PIM_IFJOIN_PRUNE_PENDING + PIM_IFJOIN_PRUNE, + PIM_IFJOIN_PRUNE_PENDING, + PIM_IFJOIN_PRUNE_TMP, + PIM_IFJOIN_PRUNE_PENDING_TMP, }; enum pim_ifassert_state { @@ -66,12 +70,22 @@ struct pim_assert_metric { #define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) #define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +/* + * Flat to tell us if the ifchannel is (S,G,rpt) + */ +#define PIM_IF_FLAG_MASK_S_G_RPT (1 << 2) +#define PIM_IF_FLAG_TEST_S_G_RPT(flags) ((flags) & PIM_IF_FLAG_MASK_S_G_RPT) +#define PIM_IF_FLAG_SET_S_G_RPT(flags) ((flags) |= PIM_IF_FLAG_MASK_S_G_RPT) +#define PIM_IF_FLAG_UNSET_S_G_RPT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_S_G_RPT) + /* Per-interface (S,G) state */ struct pim_ifchannel { - struct in_addr source_addr; /* (S,G) source key */ - struct in_addr group_addr; /* (S,G) group key */ + struct pim_ifchannel *parent; + struct list *sources; + struct prefix_sg sg; + char sg_str[PIM_SG_LEN]; struct interface *interface; /* backpointer to interface */ uint32_t flags; @@ -98,33 +112,28 @@ struct pim_ifchannel { void pim_ifchannel_free(struct pim_ifchannel *ch); void pim_ifchannel_delete(struct pim_ifchannel *ch); +void pim_ifchannel_delete_all (struct interface *ifp); void pim_ifchannel_membership_clear(struct interface *ifp); void pim_ifchannel_delete_on_noinfo(struct interface *ifp); struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr); + struct prefix_sg *sg); struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr); + struct prefix_sg *sg, int flags); void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime); void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime); void pim_ifchannel_local_membership_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr); + struct prefix_sg *sg); void pim_ifchannel_local_membership_del(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr); + struct prefix_sg *sg); void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, @@ -140,4 +149,8 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch); void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch); void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch); +void pim_ifchannel_scan_forward_start (struct interface *new_ifp); +void pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom); + +int pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2); #endif /* PIM_IFCHANNEL_H */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index ef1b3cbac0..4a23e4f668 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -26,6 +26,7 @@ #include "pimd.h" #include "pim_igmp.h" +#include "pim_igmpv2.h" #include "pim_igmpv3.h" #include "pim_iface.h" #include "pim_sock.h" @@ -35,34 +36,31 @@ #include "pim_time.h" #include "pim_zebra.h" -#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1) -#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2) -#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3) -#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4) -#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5) -#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6) - static void group_timer_off(struct igmp_group *group); +/* This socket is used for TXing IGMP packets only, IGMP RX happens + * in pim_mroute_msg() + */ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim_options) { int fd; int join = 0; struct in_addr group; - fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */); + fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1); + if (fd < 0) return -1; if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) { if (inet_aton(PIM_ALL_ROUTERS, &group)) { if (!pim_socket_join(fd, group, ifaddr, ifindex)) - ++join; + ++join; } else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_ROUTERS, errno, safe_strerror(errno)); + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_ROUTERS, errno, safe_strerror(errno)); } } @@ -76,8 +74,8 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim } else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); } if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { @@ -87,13 +85,13 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim } else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); } if (!join) { zlog_err("IGMP socket fd=%d could not join any group on interface address %s", - fd, inet_ntoa(ifaddr)); + fd, inet_ntoa(ifaddr)); close(fd); fd = -1; } @@ -154,22 +152,20 @@ static int pim_igmp_other_querier_expire(struct thread *t) { struct igmp_sock *igmp; - zassert(t); igmp = THREAD_ARG(t); - zassert(igmp); zassert(igmp->t_other_querier_timer); zassert(!igmp->t_igmp_query_timer); if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: Querier %s resuming", __PRETTY_FUNCTION__, ifaddr_str); } - igmp->t_other_querier_timer = 0; + igmp->t_other_querier_timer = NULL; /* We are the current querier, then @@ -198,7 +194,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp) */ if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present", ifaddr_str); @@ -210,7 +206,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp) else { /* We are the current querier, then stop sending general queries: - igmp->t_igmp_query_timer = 0; + igmp->t_igmp_query_timer = NULL; */ pim_igmp_general_query_off(igmp); } @@ -241,7 +237,7 @@ void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp) pim_ifp->igmp_query_max_response_time_dsec); if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present", ifaddr_str, @@ -260,7 +256,7 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp) if (PIM_DEBUG_IGMP_TRACE) { if (igmp->t_other_querier_timer) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s", ifaddr_str, igmp->fd, igmp->interface->name); @@ -270,31 +266,27 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp) zassert(!igmp->t_other_querier_timer); } -static int recv_igmp_query(struct igmp_sock *igmp, int query_version, - int max_resp_code, - struct in_addr from, const char *from_str, - char *igmp_msg, int igmp_msg_len) +static int +igmp_recv_query(struct igmp_sock *igmp, int query_version, + int max_resp_code, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) { struct interface *ifp; struct pim_interface *pim_ifp; - uint8_t resv_s_qrv = 0; - uint8_t s_flag = 0; - uint8_t qrv = 0; struct in_addr group_addr; uint16_t recv_checksum; uint16_t checksum; - int i; - //group_addr = *(struct in_addr *)(igmp_msg + 4); memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); ifp = igmp->interface; pim_ifp = ifp->info; - recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET); /* for computing checksum */ - *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; + *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0; checksum = in_cksum(igmp_msg, igmp_msg_len); if (checksum != recv_checksum) { @@ -303,12 +295,33 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, return -1; } + /* RFC 3376 defines some guidelines on operating in backwards compatibility + * with older versions of IGMP but there are some gaps in the logic: + * + * - once we drop from say version 3 to version 2 we will never go back to + * version 3 even if the node that TXed an IGMP v2 query upgrades to v3 + * + * - The node with the lowest IP is the querier so we will only know to drop + * from v3 to v2 if the node that is the querier is also the one that is + * running igmp v2. If a non-querier only supports igmp v2 we will have + * no way of knowing. + * + * For now we will simplify things and inform the user that they need to + * configure all PIM routers to use the same version of IGMP. + */ + if (query_version != pim_ifp->igmp_version) { + zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please " + "configure all PIM routers on this subnet to use the same " + "IGMP version", + query_version, from_str, ifp->name, pim_ifp->igmp_version); + return 0; + } + if (PIM_DEBUG_IGMP_PACKETS) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s", - query_version, from_str, ifp->name, - igmp_msg_len, checksum, group_str); + zlog_debug("Recv IGMP query v%d from %s on %s for group %s", + query_version, from_str, ifp->name, group_str); } /* @@ -320,9 +333,9 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, elected querier. */ if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) { - + if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)", ifp->name, @@ -333,272 +346,11 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, pim_igmp_other_querier_timer_on(igmp); } + /* IGMP version 3 is the only one where we process the RXed query */ if (query_version == 3) { - /* - RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) - - Routers adopt the QRV value from the most recently received Query - as their own [Robustness Variable] value, unless that most - recently received QRV was zero, in which case the receivers use - the default [Robustness Variable] value specified in section 8.1 - or a statically configured value. - */ - resv_s_qrv = igmp_msg[8]; - qrv = 7 & resv_s_qrv; - igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + igmp_v3_recv_query(igmp, from_str, igmp_msg); } - /* - RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) - - Multicast routers that are not the current querier adopt the QQI - value from the most recently received Query as their own [Query - Interval] value, unless that most recently received QQI was zero, - in which case the receiving routers use the default. - */ - if (igmp->t_other_querier_timer && query_version == 3) { - /* other querier present */ - uint8_t qqic; - uint16_t qqi; - qqic = igmp_msg[9]; - qqi = igmp_msg_decode8to16(qqic); - igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval; - - if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; - pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); - zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)", - ifaddr_str, - qqi ? "recv-non-default" : "default", - igmp->querier_query_interval, - qqic, - from_str); - } - } - - /* - RFC 3376: 6.6.1. Timer Updates - - When a router sends or receives a query with a clear Suppress - Router-Side Processing flag, it must update its timers to reflect - the correct timeout values for the group or sources being queried. - - General queries don't trigger timer update. - */ - if (query_version == 3) { - s_flag = (1 << 3) & resv_s_qrv; - } - else { - /* Neither V1 nor V2 have this field. Pimd should really go into - * a compatibility mode here and run as V2 (or V1) but it doesn't - * so for now, lets just set the flag to suppress these timer updates. - */ - s_flag = 1; - } - - if (!s_flag) { - /* s_flag is clear */ - - if (PIM_INADDR_IS_ANY(group_addr)) { - /* this is a general query */ - - /* log that general query should have the s_flag set */ - zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear", - query_version, from_str, ifp->name); - } - else { - struct igmp_group *group; - - /* this is a non-general query: perform timer updates */ - - group = find_group_by_addr(igmp, group_addr); - if (group) { - int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET)); - - /* - RFC 3376: 6.6.1. Timer Updates - Query Q(G,A): Source Timer for sources in A are lowered to LMQT - Query Q(G): Group Timer is lowered to LMQT - */ - if (recv_num_sources < 1) { - /* Query Q(G): Group Timer is lowered to LMQT */ - - igmp_group_timer_lower_to_lmqt(group); - } - else { - /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */ - - /* Scan sources in query and lower their timers to LMQT */ - struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); - for (i = 0; i < recv_num_sources; ++i) { - //struct in_addr src_addr = sources[i]; - //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); - struct in_addr src_addr; - struct igmp_source *src; - memcpy(&src_addr, sources + i, sizeof(struct in_addr)); - src = igmp_find_source_by_addr(group, src_addr); - if (src) { - igmp_source_timer_lower_to_lmqt(src); - } - } - } - - } - else { - char group_str[100]; - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update", - query_version, from_str, ifp->name, group_str); - } - } - } /* s_flag is clear: timer updates */ - - return 0; -} - -static int igmp_v3_report(struct igmp_sock *igmp, - struct in_addr from, const char *from_str, - char *igmp_msg, int igmp_msg_len) -{ - uint16_t recv_checksum; - uint16_t checksum; - int num_groups; - uint8_t *group_record; - uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len; - struct interface *ifp = igmp->interface; - int i; - int local_ncb = 0; - - if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { - zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", - from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE); - return -1; - } - - recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); - - /* for computing checksum */ - *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; - - checksum = in_cksum(igmp_msg, igmp_msg_len); - if (checksum != recv_checksum) { - zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", - from_str, ifp->name, recv_checksum, checksum); - return -1; - } - - num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); - if (num_groups < 1) { - zlog_warn("Recv IGMP report v3 from %s on %s: missing group records", - from_str, ifp->name); - return -1; - } - - if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d", - from_str, ifp->name, igmp_msg_len, checksum, num_groups); - } - - group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; - - /* Scan groups */ - for (i = 0; i < num_groups; ++i) { - struct in_addr rec_group; - uint8_t *sources; - uint8_t *src; - int rec_type; - int rec_auxdatalen; - int rec_num_sources; - int j; - struct prefix lncb; - struct prefix g; - - if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) { - zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end", - from_str, ifp->name); - return -1; - } - - rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET]; - rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; - rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); - - //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET); - memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr)); - - if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", - from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group)); - } - - /* Scan sources */ - - sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET; - - for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { - - if ((src + 4) > report_pastend) { - zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end", - from_str, ifp->name); - return -1; - } - - if (PIM_DEBUG_IGMP_PACKETS) { - char src_str[200]; - - if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str))) - sprintf(src_str, ""); - - zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", - from_str, ifp->name, i, inet_ntoa(rec_group), src_str); - } - } /* for (sources) */ - - - lncb.family = AF_INET; - lncb.u.prefix4.s_addr = 0x000000E0; - lncb.prefixlen = 24; - - g.family = AF_INET; - g.u.prefix4 = rec_group; - g.prefixlen = 32; - /* - * If we receive a igmp report with the group in 224.0.0.0/24 - * then we should ignore it - */ - if (prefix_match(&lncb, &g)) - local_ncb = 1; - - if (!local_ncb) - switch (rec_type) { - case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE: - igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: - igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE: - igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: - igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES: - igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES: - igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); - break; - default: - zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d", - from_str, ifp->name, rec_type); - } - - group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2); - local_ncb = 0; - - } /* for (group records) */ - return 0; } @@ -606,73 +358,17 @@ static void on_trace(const char *label, struct interface *ifp, struct in_addr from) { if (PIM_DEBUG_IGMP_TRACE) { - char from_str[100]; + char from_str[INET_ADDRSTRLEN]; pim_inet4_dump("", from, from_str, sizeof(from_str)); zlog_debug("%s: from %s on %s", label, from_str, ifp->name); } } -static int igmp_v2_report(struct igmp_sock *igmp, - struct in_addr from, const char *from_str, - char *igmp_msg, int igmp_msg_len) -{ - struct interface *ifp = igmp->interface; - struct igmp_group *group; - struct in_addr group_addr; - - on_trace(__PRETTY_FUNCTION__, igmp->interface, from); - - if (igmp_msg_len != IGMP_V12_MSG_SIZE) { - zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d", - from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); - return -1; - } - - if (PIM_DEBUG_IGMP_TRACE) { - zlog_warn("%s %s: FIXME WRITEME", - __FILE__, __PRETTY_FUNCTION__); - } - - //group_addr = *(struct in_addr *)(igmp_msg + 4); - memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); - - /* non-existant group is created as INCLUDE {empty} */ - group = igmp_add_group_by_addr(igmp, group_addr); - if (!group) { - return -1; - } - - group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec(); - - return 0; -} - -static int igmp_v2_leave(struct igmp_sock *igmp, - struct in_addr from, const char *from_str, - char *igmp_msg, int igmp_msg_len) -{ - struct interface *ifp = igmp->interface; - - on_trace(__PRETTY_FUNCTION__, igmp->interface, from); - - if (igmp_msg_len != IGMP_V12_MSG_SIZE) { - zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d", - from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); - return -1; - } - - if (PIM_DEBUG_IGMP_TRACE) { - zlog_warn("%s %s: FIXME WRITEME", - __FILE__, __PRETTY_FUNCTION__); - } - - return 0; -} - -static int igmp_v1_report(struct igmp_sock *igmp, - struct in_addr from, const char *from_str, - char *igmp_msg, int igmp_msg_len) +static int +igmp_v1_recv_report (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) { struct interface *ifp = igmp->interface; struct igmp_group *group; @@ -691,7 +387,6 @@ static int igmp_v1_report(struct igmp_sock *igmp, __FILE__, __PRETTY_FUNCTION__); } - //group_addr = *(struct in_addr *)(igmp_msg + 4); memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); /* non-existant group is created as INCLUDE {empty} */ @@ -712,8 +407,8 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) char *igmp_msg; int igmp_msg_len; int msg_type; - char from_str[100]; - char to_str[100]; + char from_str[INET_ADDRSTRLEN]; + char to_str[INET_ADDRSTRLEN]; if (len < sizeof(*ip_hdr)) { zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", @@ -790,26 +485,26 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) return -1; } - return recv_igmp_query(igmp, query_version, max_resp_code, + return igmp_recv_query(igmp, query_version, max_resp_code, ip_hdr->ip_src, from_str, igmp_msg, igmp_msg_len); } case PIM_IGMP_V3_MEMBERSHIP_REPORT: - return igmp_v3_report(igmp, ip_hdr->ip_src, from_str, - igmp_msg, igmp_msg_len); + return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); case PIM_IGMP_V2_MEMBERSHIP_REPORT: - return igmp_v2_report(igmp, ip_hdr->ip_src, from_str, - igmp_msg, igmp_msg_len); + return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); case PIM_IGMP_V1_MEMBERSHIP_REPORT: - return igmp_v1_report(igmp, ip_hdr->ip_src, from_str, - igmp_msg, igmp_msg_len); + return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); case PIM_IGMP_V2_LEAVE_GROUP: - return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str, - igmp_msg, igmp_msg_len); + return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); } zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); @@ -825,9 +520,6 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp) int startup_mode; int query_interval; - zassert(igmp); - zassert(igmp->interface); - /* Since this socket is starting as querier, there should not exist a timer for other-querier-present. @@ -841,20 +533,31 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp) The Startup Query Interval is the interval between General Queries sent by a Querier on startup. Default: 1/4 the Query Interval. + The first one should be sent out immediately instead of 125/4 + seconds from now. */ startup_mode = igmp->startup_query_count > 0; if (startup_mode) { - --igmp->startup_query_count; + /* + * If this is the first time we are sending a query on a + * newly configured igmp interface send it out in 1 second + * just to give the entire world a tiny bit of time to settle + * else the query interval is: + * query_interval = pim_ifp->igmp_default_query_interval >> 2; + */ + if (igmp->startup_query_count == igmp->querier_robustness_variable) + query_interval = 1; + else + query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); - /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */ - query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + --igmp->startup_query_count; } else { query_interval = igmp->querier_query_interval; } if (PIM_DEBUG_IGMP_TRACE) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d", ifaddr_str, @@ -862,8 +565,7 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp) startup_mode ? "startup" : "non-startup", igmp->fd); } - igmp->t_igmp_query_timer = 0; - zassert(!igmp->t_igmp_query_timer); + igmp->t_igmp_query_timer = NULL; THREAD_TIMER_ON(master, igmp->t_igmp_query_timer, pim_igmp_general_query, igmp, query_interval); @@ -875,35 +577,39 @@ void pim_igmp_general_query_off(struct igmp_sock *igmp) if (PIM_DEBUG_IGMP_TRACE) { if (igmp->t_igmp_query_timer) { - char ifaddr_str[100]; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s", ifaddr_str, igmp->fd, igmp->interface->name); } } THREAD_OFF(igmp->t_igmp_query_timer); - zassert(!igmp->t_igmp_query_timer); } /* Issue IGMP general query */ static int pim_igmp_general_query(struct thread *t) { - char query_buf[PIM_IGMP_BUFSIZE_WRITE]; struct igmp_sock *igmp; struct in_addr dst_addr; struct in_addr group_addr; struct pim_interface *pim_ifp; - - zassert(t); + int query_buf_size; igmp = THREAD_ARG(t); - zassert(igmp); zassert(igmp->interface); zassert(igmp->interface->info); pim_ifp = igmp->interface->info; + if (pim_ifp->igmp_version == 3) { + query_buf_size = PIM_IGMP_BUFSIZE_WRITE; + } else { + query_buf_size = IGMP_V12_MSG_SIZE; + } + + char query_buf[query_buf_size]; + /* RFC3376: 4.1.12. IP Destination Addresses for Queries @@ -917,8 +623,8 @@ static int pim_igmp_general_query(struct thread *t) group_addr.s_addr = PIM_NET_INADDR_ANY; if (PIM_DEBUG_IGMP_TRACE) { - char querier_str[100]; - char dst_str[100]; + char querier_str[INET_ADDRSTRLEN]; + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", igmp->ifaddr, querier_str, sizeof(querier_str)); pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); @@ -926,124 +632,25 @@ static int pim_igmp_general_query(struct thread *t) querier_str, dst_str, igmp->interface->name); } - pim_igmp_send_membership_query(0 /* igmp_group */, - igmp->fd, - igmp->interface->name, - query_buf, - sizeof(query_buf), - 0 /* num_sources */, - dst_addr, - group_addr, - pim_ifp->igmp_query_max_response_time_dsec, - 1 /* s_flag: always set for general queries */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query (pim_ifp->igmp_version, + 0 /* igmp_group */, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources */, + dst_addr, + group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 1 /* s_flag: always set for general queries */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); pim_igmp_general_query_on(igmp); return 0; } -static int pim_igmp_read(struct thread *t); - -static void igmp_read_on(struct igmp_sock *igmp) -{ - zassert(igmp); - - if (PIM_DEBUG_IGMP_TRACE_DETAIL) { - zlog_debug("Scheduling READ event on IGMP socket fd=%d", - igmp->fd); - } - igmp->t_igmp_read = 0; - zassert(!igmp->t_igmp_read); - THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd); -} - -static int pim_igmp_read(struct thread *t) -{ - struct igmp_sock *igmp; - int fd; - struct sockaddr_in from; - struct sockaddr_in to; - socklen_t fromlen = sizeof(from); - socklen_t tolen = sizeof(to); - uint8_t buf[PIM_IGMP_BUFSIZE_READ]; - int len; - ifindex_t ifindex = -1; - int result = -1; /* defaults to bad */ - - zassert(t); - - igmp = THREAD_ARG(t); - - zassert(igmp); - - fd = THREAD_FD(t); - - zassert(fd == igmp->fd); - - len = pim_socket_recvfromto(fd, buf, sizeof(buf), - &from, &fromlen, - &to, &tolen, - &ifindex); - if (len < 0) { - zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - goto done; - } - - if (PIM_DEBUG_IGMP_PACKETS) { - char from_str[100]; - char to_str[100]; - - if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) - sprintf(from_str, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) - sprintf(to_str, ""); - - zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", - len, from_str, to_str, fd, ifindex, igmp->interface->ifindex); - } - -#ifdef PIM_CHECK_RECV_IFINDEX_SANITY - /* ifindex sanity check */ - if (ifindex != igmp->interface->ifindex) { - char from_str[100]; - char to_str[100]; - struct interface *ifp; - - if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) - sprintf(from_str, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) - sprintf(to_str, ""); - - ifp = if_lookup_by_index(ifindex); - if (ifp) { - zassert(ifindex == ifp->ifindex); - } - -#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH - zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", - from_str, to_str, fd, - ifindex, ifp ? ifp->name : "", - igmp->interface->ifindex, igmp->interface->name); -#endif - goto done; - } -#endif - - if (pim_igmp_packet(igmp, (char *)buf, len)) { - goto done; - } - - result = 0; /* good */ - - done: - igmp_read_on(igmp); - - return result; -} - static void sock_close(struct igmp_sock *igmp) { pim_igmp_other_querier_timer_off(igmp); @@ -1057,7 +664,6 @@ static void sock_close(struct igmp_sock *igmp) } } THREAD_OFF(igmp->t_igmp_read); - zassert(!igmp->t_igmp_read); if (close(igmp->fd)) { zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", @@ -1106,7 +712,7 @@ static void igmp_group_delete(struct igmp_group *group) struct igmp_source *src; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Deleting IGMP group %s from socket %d interface %s", group_str, @@ -1168,6 +774,22 @@ void igmp_sock_delete(struct igmp_sock *igmp) igmp_sock_free(igmp); } +void +igmp_sock_delete_all (struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *igmp_node, *igmp_nextnode; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node, + igmp_nextnode, igmp)) + { + igmp_sock_delete(igmp); + } +} + static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, struct interface *ifp) @@ -1182,9 +804,9 @@ static struct igmp_sock *igmp_sock_new(int fd, fd, inet_ntoa(ifaddr), ifp->name); } - igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); + igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); if (!igmp) { - zlog_warn("%s %s: XMALLOC() failure", + zlog_warn("%s %s: XCALLOC() failure", __FILE__, __PRETTY_FUNCTION__); return 0; } @@ -1200,9 +822,9 @@ static struct igmp_sock *igmp_sock_new(int fd, igmp->fd = fd; igmp->interface = ifp; igmp->ifaddr = ifaddr; - igmp->t_igmp_read = 0; - igmp->t_igmp_query_timer = 0; - igmp->t_other_querier_timer = 0; /* no other querier present */ + igmp->t_igmp_read = NULL; + igmp->t_igmp_query_timer = NULL; + igmp->t_other_querier_timer = NULL; /* no other querier present */ igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable; igmp->sock_creation = pim_time_monotonic_sec(); @@ -1212,13 +834,63 @@ static struct igmp_sock *igmp_sock_new(int fd, igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; */ igmp_startup_mode_on(igmp); - - igmp_read_on(igmp); pim_igmp_general_query_on(igmp); return igmp; } +static void igmp_read_on (struct igmp_sock *igmp); + +static int +pim_igmp_read (struct thread *t) +{ + uint8_t buf[10000]; + struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t); + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + ifindex_t ifindex = -1; + int cont = 1; + int len; + + while (cont) + { + len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) + { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) + { + cont = 0; + break; + } + goto done; + } + } + + done: + igmp_read_on(igmp); + return 0; +} + +static void +igmp_read_on (struct igmp_sock *igmp) +{ + + if (PIM_DEBUG_IGMP_TRACE_DETAIL) { + zlog_debug("Scheduling READ event on IGMP socket fd=%d", + igmp->fd); + } + igmp->t_igmp_read = NULL; + THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd); + +} + struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, struct in_addr ifaddr, struct interface *ifp) @@ -1244,6 +916,8 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, return 0; } + igmp_read_on (igmp); + listnode_add(igmp_sock_list, igmp); #ifdef IGMP_SOCK_DUMP @@ -1271,12 +945,10 @@ static int igmp_group_timer(struct thread *t) { struct igmp_group *group; - zassert(t); group = THREAD_ARG(t); - zassert(group); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("%s: Timer for group %s on interface %s", __PRETTY_FUNCTION__, @@ -1315,7 +987,7 @@ static void group_timer_off(struct igmp_group *group) return; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Cancelling TIMER event for group %s on %s", group_str, group->group_igmp_sock->interface->name); @@ -1331,7 +1003,7 @@ void igmp_group_timer_on(struct igmp_group *group, group_timer_off(group); if (PIM_DEBUG_IGMP_EVENTS) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s", interval_msec / 1000, @@ -1377,6 +1049,19 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, return group; } + if (!pim_is_group_224_4 (group_addr)) + { + zlog_warn("%s: Group Specified is not part of 224.0.0.0/4", + __PRETTY_FUNCTION__); + return NULL; + } + + if (pim_is_group_224_0_0_0_24 (group_addr)) + { + zlog_warn("%s: Group specified is part of 224.0.0.0/24", + __PRETTY_FUNCTION__); + return NULL; + } /* Non-existant group is created as INCLUDE {empty}: @@ -1390,11 +1075,11 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, of INCLUDE and an empty source list. */ - group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); + group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); if (!group) { - zlog_warn("%s %s: XMALLOC() failure", + zlog_warn("%s %s: XCALLOC() failure", __FILE__, __PRETTY_FUNCTION__); - return 0; /* error, not found, could not create */ + return NULL; /* error, not found, could not create */ } group->group_source_list = list_new(); @@ -1402,7 +1087,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, zlog_warn("%s %s: list_new() failure", __FILE__, __PRETTY_FUNCTION__); XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */ - return 0; /* error, not found, could not initialize */ + return NULL; /* error, not found, could not initialize */ } group->group_source_list->del = (void (*)(void *)) igmp_source_free; @@ -1414,6 +1099,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, group->last_igmp_v1_report_dsec = -1; group->last_igmp_v2_report_dsec = -1; group->group_creation = pim_time_monotonic_sec(); + group->igmp_version = IGMP_DEFAULT_VERSION; /* initialize new group as INCLUDE {empty} */ group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ @@ -1421,7 +1107,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, listnode_add(igmp->igmp_group_list, group); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Creating new IGMP group %s on socket %d interface %s", group_str, igmp->fd, igmp->interface->name); @@ -1442,3 +1128,32 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, return group; } + +void +igmp_send_query (int igmp_version, + struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval) +{ + if (igmp_version == 3) { + igmp_v3_send_query (group, fd, ifname, query_buf, + query_buf_size, num_sources, + dst_addr, group_addr, + query_max_response_time_dsec, s_flag, + querier_robustness_variable, + querier_query_interval); + } else if (igmp_version == 2) { + igmp_v2_send_query (group, fd, ifname, query_buf, + dst_addr, group_addr, + query_max_response_time_dsec); + } +} diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index eb0377ce8c..802f1ba471 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -51,6 +51,7 @@ #define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2) #define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4) #define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8) +#define IGMP_CHECKSUM_OFFSET (2) /* RFC 3376: 8.1. Robustness Variable - Default: 2 */ #define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) @@ -64,6 +65,8 @@ /* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */ #define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10) +#define IGMP_DEFAULT_VERSION (3) + struct igmp_join { struct in_addr group_addr; struct in_addr source_addr; @@ -97,7 +100,7 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, struct interface *ifp); void igmp_sock_delete(struct igmp_sock *igmp); void igmp_sock_free(struct igmp_sock *igmp); - +void igmp_sock_delete_all (struct interface *ifp); int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len); void pim_igmp_general_query_on(struct igmp_sock *igmp); @@ -151,6 +154,9 @@ struct igmp_group { since sources have their counters) */ int group_specific_query_retransmit_count; + /* compatibility mode - igmp v1, v2 or v3 */ + int igmp_version; + struct in_addr group_addr; int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */ struct list *group_source_list; /* list of struct igmp_source */ @@ -175,4 +181,18 @@ void igmp_group_timer_on(struct igmp_group *group, struct igmp_source * source_new (struct igmp_group *group, struct in_addr src_addr); + +void igmp_send_query(int igmp_version, + struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval); #endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c new file mode 100644 index 0000000000..ee4aa7bd9d --- /dev/null +++ b/pimd/pim_igmpv2.c @@ -0,0 +1,190 @@ +/* + * PIM for Quagga + * Copyright (C) 2016 Cumulus Networks, Inc. + * Daniel Walton + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "zebra.h" + +#include "pimd.h" +#include "pim_igmp.h" +#include "pim_igmpv2.h" +#include "pim_igmpv3.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_util.h" + + +static void +on_trace (const char *label, + struct interface *ifp, struct in_addr from) +{ + if (PIM_DEBUG_IGMP_TRACE) { + char from_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", from, from_str, sizeof(from_str)); + zlog_debug("%s: from %s on %s", + label, from_str, ifp->name); + } +} + +void +igmp_v2_send_query (struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec) +{ + ssize_t msg_size = 8; + uint8_t max_resp_code; + ssize_t sent; + struct sockaddr_in to; + socklen_t tolen; + uint16_t checksum; + + /* max_resp_code must be non-zero else this will look like an IGMP v1 query */ + max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); + zassert(max_resp_code > 0); + + query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; + query_buf[1] = max_resp_code; + *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + memcpy(query_buf+4, &group_addr, sizeof(struct in_addr)); + + checksum = in_cksum(query_buf, msg_size); + *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum; + + if (PIM_DEBUG_IGMP_PACKETS) { + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s", + dst_str, ifname, group_str); + } + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = dst_addr; + tolen = sizeof(to); + + sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, + (struct sockaddr *)&to, tolen); + if (sent != (ssize_t) msg_size) { + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (sent < 0) { + zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s", + dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno)); + } + else { + zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd", + dst_str, ifname, group_str, msg_size, sent); + } + return; + } +} + +int +igmp_v2_recv_report (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct in_addr group_addr; + char group_str[INET_ADDRSTRLEN]; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + + if (PIM_DEBUG_IGMP_PACKETS) { + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s", + from_str, ifp->name, group_str); + } + + /* + * RFC 3376 + * 7.3.2. In the Presence of Older Version Group Members + * + * When Group Compatibility Mode is IGMPv2, a router internally + * translates the following IGMPv2 messages for that group to their + * IGMPv3 equivalents: + * + * IGMPv2 Message IGMPv3 Equivalent + * -------------- ----------------- + * Report IS_EX( {} ) + * Leave TO_IN( {} ) + */ + igmpv3_report_isex (igmp, from, group_addr, 0, NULL, 1); + + return 0; +} + +int +igmp_v2_recv_leave (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct in_addr group_addr; + char group_str[INET_ADDRSTRLEN]; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + + if (PIM_DEBUG_IGMP_PACKETS) { + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", + from_str, ifp->name, group_str); + } + + /* + * RFC 3376 + * 7.3.2. In the Presence of Older Version Group Members + * + * When Group Compatibility Mode is IGMPv2, a router internally + * translates the following IGMPv2 messages for that group to their + * IGMPv3 equivalents: + * + * IGMPv2 Message IGMPv3 Equivalent + * -------------- ----------------- + * Report IS_EX( {} ) + * Leave TO_IN( {} ) + */ + igmpv3_report_toin (igmp, from, group_addr, 0, NULL); + + return 0; +} diff --git a/pimd/pim_igmpv2.h b/pimd/pim_igmpv2.h new file mode 100644 index 0000000000..10a2477724 --- /dev/null +++ b/pimd/pim_igmpv2.h @@ -0,0 +1,41 @@ +/* + * PIM for Quagga + * Copyright (C) 2016 Cumulus Networks, Inc. + * Daniel Walton + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef PIM_IGMPV2_H +#define PIM_IGMPV2_H + +void igmp_v2_send_query (struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec); + +int igmp_v2_recv_report (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len); + +int igmp_v2_recv_leave (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len); + +#endif /* PIM_IGMPV2_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index bdaf2bb270..f7a6cbd0ce 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -46,8 +46,8 @@ static void on_trace(const char *label, int num_sources, struct in_addr *sources) { if (PIM_DEBUG_IGMP_TRACE) { - char from_str[100]; - char group_str[100]; + char from_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", from, from_str, sizeof(from_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); @@ -57,50 +57,6 @@ static void on_trace(const char *label, } } -int igmp_group_compat_mode(const struct igmp_sock *igmp, - const struct igmp_group *group) -{ - struct pim_interface *pim_ifp; - int64_t now_dsec; - long older_host_present_interval_dsec; - - zassert(igmp); - zassert(igmp->interface); - zassert(igmp->interface->info); - - pim_ifp = igmp->interface->info; - - /* - RFC 3376: 8.13. Older Host Present Interval - - This value MUST be ((the Robustness Variable) times (the Query - Interval)) plus (one Query Response Interval). - - older_host_present_interval_dsec = \ - igmp->querier_robustness_variable * \ - 10 * igmp->querier_query_interval + \ - pim_ifp->query_max_response_time_dsec; - */ - older_host_present_interval_dsec = - PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, - igmp->querier_query_interval, - pim_ifp->igmp_query_max_response_time_dsec); - - now_dsec = pim_time_monotonic_dsec(); - if (now_dsec < 1) { - /* broken timer logged by pim_time_monotonic_dsec() */ - return 3; - } - - if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec) - return 1; /* IGMPv1 */ - - if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec) - return 2; /* IGMPv2 */ - - return 3; /* IGMPv3 */ -} - void igmp_group_reset_gmi(struct igmp_group *group) { long group_membership_interval_msec; @@ -132,7 +88,7 @@ void igmp_group_reset_gmi(struct igmp_group *group) pim_ifp->igmp_query_max_response_time_dsec); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s", group_str, @@ -158,15 +114,13 @@ static int igmp_source_timer(struct thread *t) struct igmp_source *source; struct igmp_group *group; - zassert(t); source = THREAD_ARG(t); - zassert(source); group = source->source_group; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("%s: Source timer expired for group %s source %s on %s", @@ -176,7 +130,7 @@ static int igmp_source_timer(struct thread *t) } zassert(source->t_source_timer); - source->t_source_timer = 0; + source->t_source_timer = NULL; /* RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules @@ -230,8 +184,8 @@ static void source_timer_off(struct igmp_group *group, return; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("Cancelling TIMER event for group %s source %s on %s", @@ -250,8 +204,8 @@ static void igmp_source_timer_on(struct igmp_group *group, source_timer_off(group, source); if (PIM_DEBUG_IGMP_EVENTS) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", @@ -291,8 +245,8 @@ void igmp_source_reset_gmi(struct igmp_sock *igmp, pim_ifp->igmp_query_max_response_time_dsec); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); @@ -383,7 +337,7 @@ static void source_channel_oil_detach(struct igmp_source *source) { if (source->source_channel_oil) { pim_channel_oil_del(source->source_channel_oil); - source->source_channel_oil = 0; + source->source_channel_oil = NULL; } } @@ -398,8 +352,8 @@ void igmp_source_delete(struct igmp_source *source) group = source->source_group; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s", @@ -413,8 +367,8 @@ void igmp_source_delete(struct igmp_source *source) /* sanity check that forwarding has been disabled */ if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", @@ -483,8 +437,8 @@ source_new (struct igmp_group *group, struct igmp_source *src; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", src_addr, source_str, sizeof(source_str)); zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s", @@ -493,9 +447,9 @@ source_new (struct igmp_group *group, group->group_igmp_sock->interface->name); } - src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); + src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); if (!src) { - zlog_warn("%s %s: XMALLOC() failure", + zlog_warn("%s %s: XCALLOC() failure", __FILE__, __PRETTY_FUNCTION__); return 0; /* error, not found, could not create */ } @@ -646,8 +600,10 @@ static void isex_excl(struct igmp_group *group, struct in_addr star = { .s_addr = INADDR_ANY }; source = igmp_find_source_by_addr (group, star); if (source) - IGMP_SOURCE_DONT_DELETE(source->source_flags); - igmp_source_reset_gmi (group->group_igmp_sock, group, source); + { + IGMP_SOURCE_DONT_DELETE(source->source_flags); + igmp_source_reset_gmi (group->group_igmp_sock, group, source); + } } /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ @@ -702,7 +658,8 @@ static void isex_incl(struct igmp_group *group, void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, - int num_sources, struct in_addr *sources) + int num_sources, struct in_addr *sources, + int from_igmp_v2_report) { struct interface *ifp = igmp->interface; struct igmp_group *group; @@ -716,6 +673,10 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, return; } + /* So we can display how we learned the group in our show command output */ + if (from_igmp_v2_report) + group->igmp_version = 2; + if (group->group_filtermode_isexcl) { /* EXCLUDE mode */ isex_excl(group, num_sources, sources); @@ -932,6 +893,16 @@ static void toex_excl(struct igmp_group *group, /* clear off SEND flag from all known sources (X,Y) */ source_clear_send_flag(group->group_source_list); + if (num_sources == 0) + { + struct igmp_source *source; + struct in_addr any = { .s_addr = INADDR_ANY }; + + source = igmp_find_source_by_addr (group, any); + if (source) + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + /* scan received sources (A) */ for (i = 0; i < num_sources; ++i) { struct igmp_source *source; @@ -1037,17 +1008,25 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, */ static void group_retransmit_group(struct igmp_group *group) { - char query_buf[PIM_IGMP_BUFSIZE_WRITE]; struct igmp_sock *igmp; struct pim_interface *pim_ifp; long lmqc; /* Last Member Query Count */ long lmqi_msec; /* Last Member Query Interval */ long lmqt_msec; /* Last Member Query Time */ int s_flag; + int query_buf_size; igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; + if (pim_ifp->igmp_version == 3) { + query_buf_size = PIM_IGMP_BUFSIZE_WRITE; + } else { + query_buf_size = IGMP_V12_MSG_SIZE; + } + + char query_buf[query_buf_size]; + lmqc = igmp->querier_robustness_variable; lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; @@ -1062,7 +1041,7 @@ static void group_retransmit_group(struct igmp_group *group) s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", group_str, igmp->interface->name, s_flag, @@ -1077,18 +1056,19 @@ static void group_retransmit_group(struct igmp_group *group) interest. */ - pim_igmp_send_membership_query(group, - igmp->fd, - igmp->interface->name, - query_buf, - sizeof(query_buf), - 0 /* num_sources_tosend */, - group->group_addr /* dst_addr */, - group->group_addr /* group_addr */, - pim_ifp->igmp_specific_query_max_response_time_dsec, - s_flag, - igmp->querier_robustness_variable, - igmp->querier_query_interval); + igmp_send_query(pim_ifp->igmp_version, + group, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources_tosend */, + group->group_addr /* dst_addr */, + group->group_addr /* group_addr */, + pim_ifp->igmp_specific_query_max_response_time_dsec, + s_flag, + igmp->querier_robustness_variable, + igmp->querier_query_interval); } /* @@ -1123,9 +1103,6 @@ static int group_retransmit_sources(struct igmp_group *group, struct igmp_source *src; int num_retransmit_sources_left = 0; - query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; - query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; - source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); @@ -1163,7 +1140,7 @@ static int group_retransmit_sources(struct igmp_group *group, num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", group_str, igmp->interface->name, @@ -1183,7 +1160,7 @@ static int group_retransmit_sources(struct igmp_group *group, query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; if (num_sources_tosend1 > query_buf1_max_sources) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __PRETTY_FUNCTION__, group_str, igmp->interface->name, @@ -1198,19 +1175,19 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - pim_igmp_send_membership_query(group, - igmp->fd, - igmp->interface->name, - query_buf1, - sizeof(query_buf1), - num_sources_tosend1, - group->group_addr, - group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 1 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); - + igmp_send_query(pim_ifp->igmp_version, + group, + igmp->fd, + igmp->interface->name, + query_buf1, + sizeof(query_buf1), + num_sources_tosend1, + group->group_addr, + group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + 1 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); } } /* send_with_sflag_set */ @@ -1225,7 +1202,7 @@ static int group_retransmit_sources(struct igmp_group *group, query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; if (num_sources_tosend2 > query_buf2_max_sources) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __PRETTY_FUNCTION__, group_str, igmp->interface->name, @@ -1240,19 +1217,19 @@ static int group_retransmit_sources(struct igmp_group *group, interest. */ - pim_igmp_send_membership_query(group, - igmp->fd, - igmp->interface->name, - query_buf2, - sizeof(query_buf2), - num_sources_tosend2, - group->group_addr, - group->group_addr, - pim_ifp->igmp_specific_query_max_response_time_dsec, - 0 /* s_flag */, - igmp->querier_robustness_variable, - igmp->querier_query_interval); - + igmp_send_query(pim_ifp->igmp_version, + group, + igmp->fd, + igmp->interface->name, + query_buf2, + sizeof(query_buf2), + num_sources_tosend2, + group->group_addr, + group->group_addr, + pim_ifp->igmp_specific_query_max_response_time_dsec, + 0 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); } } @@ -1265,12 +1242,10 @@ static int igmp_group_retransmit(struct thread *t) int num_retransmit_sources_left; int send_with_sflag_set; /* boolean */ - zassert(t); group = THREAD_ARG(t); - zassert(group); if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("group_retransmit_timer: group %s on %s", group_str, group->group_igmp_sock->interface->name); @@ -1300,7 +1275,7 @@ static int igmp_group_retransmit(struct thread *t) num_retransmit_sources_left = group_retransmit_sources(group, send_with_sflag_set); - group->t_group_query_retransmit_timer = 0; + group->t_group_query_retransmit_timer = NULL; /* Keep group retransmit timer running if there is any retransmit @@ -1336,7 +1311,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s", lmqi_msec / 1000, @@ -1565,7 +1540,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", __PRETTY_FUNCTION__, @@ -1600,8 +1575,8 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ if (PIM_DEBUG_IGMP_TRACE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", @@ -1613,32 +1588,19 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) igmp_source_timer_on(group, source, lmqt_msec); } -/* - Copy sources to message: - - struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET); - if (num_sources > 0) { - struct listnode *node; - struct igmp_source *src; - int i = 0; - - for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) { - sources[i++] = src->source_addr; - } - } -*/ -void pim_igmp_send_membership_query(struct igmp_group *group, - int fd, - const char *ifname, - char *query_buf, - int query_buf_size, - int num_sources, - struct in_addr dst_addr, - struct in_addr group_addr, - int query_max_response_time_dsec, - uint8_t s_flag, - uint8_t querier_robustness_variable, - uint16_t querier_query_interval) +void +igmp_v3_send_query (struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval) { ssize_t msg_size; uint8_t max_resp_code; @@ -1678,7 +1640,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group, query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; query_buf[1] = max_resp_code; - *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */ memcpy(query_buf+4, &group_addr, sizeof(struct in_addr)); query_buf[8] = (s_flag << 3) | querier_robustness_variable; @@ -1686,18 +1648,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group, *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); checksum = in_cksum(query_buf, msg_size); - *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum; + *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum; if (PIM_DEBUG_IGMP_PACKETS) { - char dst_str[100]; - char group_str[100]; + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x", - __PRETTY_FUNCTION__, - dst_str, ifname, group_str, num_sources, - msg_size, s_flag, querier_robustness_variable, - querier_query_interval, qqic, checksum); + zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x", + dst_str, ifname, group_str, + num_sources, msg_size, s_flag, querier_robustness_variable, + querier_query_interval, qqic); } memset(&to, 0, sizeof(to)); @@ -1708,22 +1669,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group, sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, (struct sockaddr *)&to, tolen); if (sent != (ssize_t) msg_size) { - int e = errno; - char dst_str[100]; - char group_str[100]; + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); if (sent < 0) { - zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s", - __PRETTY_FUNCTION__, - dst_str, ifname, group_str, msg_size, - e, safe_strerror(e)); + zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s", + dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno)); } else { - zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd", - __PRETTY_FUNCTION__, - dst_str, ifname, group_str, - msg_size, sent); + zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd", + dst_str, ifname, group_str, msg_size, sent); } return; } @@ -1742,8 +1698,8 @@ void pim_igmp_send_membership_query(struct igmp_group *group, if (!s_flag) { /* general query? */ if (PIM_INADDR_IS_ANY(group_addr)) { - char dst_str[100]; - char group_str[100]; + char dst_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!", @@ -1751,5 +1707,268 @@ void pim_igmp_send_membership_query(struct igmp_group *group, dst_str, ifname, group_str, num_sources); } } - +} + +void +igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr group_addr; + uint8_t resv_s_qrv = 0; + uint8_t s_flag = 0; + uint8_t qrv = 0; + int i; + + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + ifp = igmp->interface; + pim_ifp = ifp->info; + + /* + * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + * + * Routers adopt the QRV value from the most recently received Query + * as their own [Robustness Variable] value, unless that most + * recently received QRV was zero, in which case the receivers use + * the default [Robustness Variable] value specified in section 8.1 + * or a statically configured value. + */ + resv_s_qrv = igmp_msg[8]; + qrv = 7 & resv_s_qrv; + igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + + /* + * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + * + * Multicast routers that are not the current querier adopt the QQI + * value from the most recently received Query as their own [Query + * Interval] value, unless that most recently received QQI was zero, + * in which case the receiving routers use the default. + */ + if (igmp->t_other_querier_timer) { + /* other querier present */ + uint8_t qqic; + uint16_t qqi; + qqic = igmp_msg[9]; + qqi = igmp_msg_decode8to16(qqic); + igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval; + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)", + ifaddr_str, + qqi ? "recv-non-default" : "default", + igmp->querier_query_interval, + qqic, + from_str); + } + } + + /* + * RFC 3376: 6.6.1. Timer Updates + * + * When a router sends or receives a query with a clear Suppress + * Router-Side Processing flag, it must update its timers to reflect + * the correct timeout values for the group or sources being queried. + * + * General queries don't trigger timer update. + */ + s_flag = (1 << 3) & resv_s_qrv; + + if (!s_flag) { + /* s_flag is clear */ + + if (PIM_INADDR_IS_ANY(group_addr)) { + /* this is a general query */ + /* log that general query should have the s_flag set */ + zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear", + from_str, ifp->name); + } else { + struct igmp_group *group; + + /* this is a non-general query: perform timer updates */ + + group = find_group_by_addr(igmp, group_addr); + if (group) { + int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET)); + + /* + * RFC 3376: 6.6.1. Timer Updates + * Query Q(G,A): Source Timer for sources in A are lowered to LMQT + * Query Q(G): Group Timer is lowered to LMQT + */ + if (recv_num_sources < 1) { + /* Query Q(G): Group Timer is lowered to LMQT */ + + igmp_group_timer_lower_to_lmqt(group); + } else { + /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */ + + /* Scan sources in query and lower their timers to LMQT */ + struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); + for (i = 0; i < recv_num_sources; ++i) { + struct in_addr src_addr; + struct igmp_source *src; + memcpy(&src_addr, sources + i, sizeof(struct in_addr)); + src = igmp_find_source_by_addr(group, src_addr); + if (src) { + igmp_source_timer_lower_to_lmqt(src); + } + } + } + } else { + char group_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update", + from_str, ifp->name, group_str); + } + } + } /* s_flag is clear: timer updates */ +} + +int +igmp_v3_recv_report (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + uint16_t recv_checksum; + uint16_t checksum; + int num_groups; + uint8_t *group_record; + uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len; + struct interface *ifp = igmp->interface; + int i; + int local_ncb = 0; + + if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { + zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE); + return -1; + } + + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET); + + /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0; + + checksum = in_cksum(igmp_msg, igmp_msg_len); + if (checksum != recv_checksum) { + zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); + if (num_groups < 1) { + zlog_warn("Recv IGMP report v3 from %s on %s: missing group records", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d", + from_str, ifp->name, igmp_msg_len, checksum, num_groups); + } + + group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; + + /* Scan groups */ + for (i = 0; i < num_groups; ++i) { + struct in_addr rec_group; + uint8_t *sources; + uint8_t *src; + int rec_type; + int rec_auxdatalen; + int rec_num_sources; + int j; + struct prefix lncb; + struct prefix g; + + if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end", + from_str, ifp->name); + return -1; + } + + rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET]; + rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; + rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); + + memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr)); + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", + from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group)); + } + + /* Scan sources */ + + sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET; + + for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { + + if ((src + 4) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char src_str[200]; + + if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str))) + sprintf(src_str, ""); + + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", + from_str, ifp->name, i, inet_ntoa(rec_group), src_str); + } + } /* for (sources) */ + + + lncb.family = AF_INET; + lncb.u.prefix4.s_addr = 0x000000E0; + lncb.prefixlen = 24; + + g.family = AF_INET; + g.u.prefix4 = rec_group; + g.prefixlen = 32; + /* + * If we receive a igmp report with the group in 224.0.0.0/24 + * then we should ignore it + */ + if (prefix_match(&lncb, &g)) + local_ncb = 1; + + if (!local_ncb) + switch (rec_type) { + case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE: + igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: + igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE: + igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: + igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES: + igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES: + igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + default: + zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d", + from_str, ifp->name, rec_type); + } + + group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2); + local_ncb = 0; + + } /* for (group records) */ + + return 0; } diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h index db7895f9be..3a4a81d97e 100644 --- a/pimd/pim_igmpv3.h +++ b/pimd/pim_igmpv3.h @@ -30,6 +30,13 @@ #define IGMP_V3_NUMSOURCES_OFFSET (10) #define IGMP_V3_SOURCES_OFFSET (12) +#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1) +#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4) +#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5) +#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6) + /* GMI: Group Membership Interval */ #define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec)) @@ -54,15 +61,13 @@ void igmp_source_free(struct igmp_source *source); void igmp_source_delete(struct igmp_source *source); void igmp_source_delete_expired(struct list *source_list); -int igmp_group_compat_mode(const struct igmp_sock *igmp, - const struct igmp_group *group); - void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, int num_sources, struct in_addr *sources); void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, - int num_sources, struct in_addr *sources); + int num_sources, struct in_addr *sources, + int from_igmp_v2_report); void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, struct in_addr group_addr, int num_sources, struct in_addr *sources); @@ -82,17 +87,24 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source); struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, struct in_addr src_addr); -void pim_igmp_send_membership_query(struct igmp_group *group, - int fd, - const char *ifname, - char *query_buf, - int query_buf_size, - int num_sources, - struct in_addr dst_addr, - struct in_addr group_addr, - int query_max_response_time_dsec, - uint8_t s_flag, - uint8_t querier_robustness_variable, - uint16_t querier_query_interval); +void igmp_v3_send_query (struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval); + +void igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, + char *igmp_msg); + +int igmp_v3_recv_report (struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len); #endif /* PIM_IGMPV3_H */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 6a5fb851d6..028f77f532 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -23,6 +23,8 @@ #include "log.h" #include "prefix.h" #include "if.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_str.h" @@ -30,15 +32,19 @@ #include "pim_msg.h" #include "pim_pim.h" #include "pim_join.h" +#include "pim_oil.h" #include "pim_iface.h" #include "pim_hello.h" #include "pim_ifchannel.h" +#include "pim_rpf.h" +#include "pim_rp.h" -static void on_trace(const char *label, - struct interface *ifp, struct in_addr src) +static void +on_trace (const char *label, + struct interface *ifp, struct in_addr src) { if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src, src_str, sizeof(src_str)); zlog_debug("%s: from %s on %s", label, src_str, ifp->name); @@ -49,58 +55,81 @@ static void recv_join(struct interface *ifp, struct pim_neighbor *neigh, uint16_t holdtime, struct in_addr upstream, - struct in_addr group, - struct in_addr source, + struct prefix_sg *sg, uint8_t source_flags) { if (PIM_DEBUG_PIM_TRACE) { - char up_str[100]; - char src_str[100]; - char grp_str[100]; - char neigh_str[100]; + char up_str[INET_ADDRSTRLEN]; + char neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("", source, src_str, sizeof(src_str)); - pim_inet4_dump("", group, grp_str, sizeof(grp_str)); pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); - zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", __PRETTY_FUNCTION__, - src_str, grp_str, + pim_str_sg_dump (sg), source_flags & PIM_RPT_BIT_MASK, source_flags & PIM_WILDCARD_BIT_MASK, up_str, holdtime, neigh_str, ifp->name); } - + + /* + * If the RPT and WC are set it's a (*,G) + * and the source is the RP + */ + if ((source_flags & PIM_RPT_BIT_MASK) && + (source_flags & PIM_WILDCARD_BIT_MASK)) + { + struct pim_rpf *rp = RP (sg->grp); + + /* + * If the RP sent in the message is not + * our RP for the group, drop the message + */ + if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr) + return; + + sg->src.s_addr = INADDR_ANY; + } + /* Restart join expiry timer */ pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, - source, group, source_flags, holdtime); + sg, source_flags, holdtime); + } static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh, uint16_t holdtime, struct in_addr upstream, - struct in_addr group, - struct in_addr source, + struct prefix_sg *sg, uint8_t source_flags) { if (PIM_DEBUG_PIM_TRACE) { - char up_str[100]; - char src_str[100]; - char grp_str[100]; - char neigh_str[100]; + char up_str[INET_ADDRSTRLEN]; + char neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("", source, src_str, sizeof(src_str)); - pim_inet4_dump("", group, grp_str, sizeof(grp_str)); pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); - zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", __PRETTY_FUNCTION__, - src_str, grp_str, + pim_str_sg_dump (sg), source_flags & PIM_RPT_BIT_MASK, source_flags & PIM_WILDCARD_BIT_MASK, up_str, holdtime, neigh_str, ifp->name); } - - pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime); + + if ((source_flags & PIM_RPT_BIT_MASK) && + (source_flags & PIM_WILDCARD_BIT_MASK)) + { + struct pim_rpf *rp = RP (sg->grp); + + // Ignoring Prune *,G's at the moment. + if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr) + return; + + sg->src.s_addr = INADDR_ANY; + } + + pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime); + } int pim_joinprune_recv(struct interface *ifp, @@ -117,8 +146,6 @@ int pim_joinprune_recv(struct interface *ifp, int remain; int group; - on_trace(__PRETTY_FUNCTION__, ifp, src_addr); - buf = tlv_buf; pastend = tlv_buf + tlv_buf_size; @@ -128,7 +155,7 @@ int pim_joinprune_recv(struct interface *ifp, addr_offset = pim_parse_addr_ucast (&msg_upstream_addr, buf, pastend - buf); if (addr_offset < 1) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -141,8 +168,8 @@ int pim_joinprune_recv(struct interface *ifp, Check upstream address family */ if (msg_upstream_addr.family != AF_INET) { - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; + if (PIM_DEBUG_PIM_J_P) { + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s", __PRETTY_FUNCTION__, @@ -153,7 +180,7 @@ int pim_joinprune_recv(struct interface *ifp, remain = pastend - buf; if (remain < 4) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s", __PRETTY_FUNCTION__, @@ -168,28 +195,29 @@ int pim_joinprune_recv(struct interface *ifp, ++buf; ++buf; - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char upstream_str[100]; + if (PIM_DEBUG_PIM_J_P) { + char src_str[INET_ADDRSTRLEN]; + char upstream_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); pim_inet4_dump("", msg_upstream_addr.u.prefix4, upstream_str, sizeof(upstream_str)); - zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s", - __PRETTY_FUNCTION__, - upstream_str, msg_num_groups, msg_holdtime, - src_str, ifp->name); + zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + upstream_str, msg_num_groups, msg_holdtime, + src_str, ifp->name); } /* Scan groups */ for (group = 0; group < msg_num_groups; ++group) { - struct prefix msg_group_addr; - struct prefix msg_source_addr; + struct prefix_sg sg; uint8_t msg_source_flags; uint16_t msg_num_joined_sources; uint16_t msg_num_pruned_sources; int source; + struct pim_ifchannel *ch = NULL; - addr_offset = pim_parse_addr_group (&msg_group_addr, + memset (&sg, 0, sizeof (struct prefix_sg)); + addr_offset = pim_parse_addr_group (&sg, buf, pastend - buf); if (addr_offset < 1) { return -5; @@ -198,7 +226,7 @@ int pim_joinprune_recv(struct interface *ifp, remain = pastend - buf; if (remain < 4) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s", __PRETTY_FUNCTION__, @@ -211,25 +239,25 @@ int pim_joinprune_recv(struct interface *ifp, msg_num_pruned_sources = ntohs(*(const uint16_t *) buf); buf += 2; - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char upstream_str[100]; - char group_str[100]; + if (PIM_DEBUG_PIM_J_P) { + char src_str[INET_ADDRSTRLEN]; + char upstream_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); pim_inet4_dump("", msg_upstream_addr.u.prefix4, upstream_str, sizeof(upstream_str)); - pim_inet4_dump("", msg_group_addr.u.prefix4, + pim_inet4_dump("", sg.grp, group_str, sizeof(group_str)); - zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s", + zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s", __PRETTY_FUNCTION__, - upstream_str, group_str, msg_group_addr.prefixlen, + upstream_str, group_str, msg_num_joined_sources, msg_num_pruned_sources, src_str, ifp->name); } /* Scan joined sources */ for (source = 0; source < msg_num_joined_sources; ++source) { - addr_offset = pim_parse_addr_source (&msg_source_addr, + addr_offset = pim_parse_addr_source (&sg, &msg_source_flags, buf, pastend - buf); if (addr_offset < 1) { @@ -240,14 +268,20 @@ int pim_joinprune_recv(struct interface *ifp, recv_join(ifp, neigh, msg_holdtime, msg_upstream_addr.u.prefix4, - msg_group_addr.u.prefix4, - msg_source_addr.u.prefix4, + &sg, msg_source_flags); + + if (sg.src.s_addr == INADDR_ANY) + { + ch = pim_ifchannel_find (ifp, &sg); + if (ch) + pim_ifchannel_set_star_g_join_state (ch, 0); + } } /* Scan pruned sources */ for (source = 0; source < msg_num_pruned_sources; ++source) { - addr_offset = pim_parse_addr_source (&msg_source_addr, + addr_offset = pim_parse_addr_source (&sg, &msg_source_flags, buf, pastend - buf); if (addr_offset < 1) { @@ -258,11 +292,12 @@ int pim_joinprune_recv(struct interface *ifp, recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr.u.prefix4, - msg_group_addr.u.prefix4, - msg_source_addr.u.prefix4, + &sg, msg_source_flags); } - + if (ch) + pim_ifchannel_set_star_g_join_state (ch, 1); + ch = NULL; } /* scan groups */ return 0; @@ -270,16 +305,14 @@ int pim_joinprune_recv(struct interface *ifp, int pim_joinprune_send(struct interface *ifp, struct in_addr upstream_addr, - struct in_addr source_addr, - struct in_addr group_addr, + struct pim_upstream *up, int send_join) { struct pim_interface *pim_ifp; - uint8_t pim_msg[1000]; - const uint8_t *pastend = pim_msg + sizeof(pim_msg); - uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */ + uint8_t pim_msg[9000]; int pim_msg_size; - int remain; + + on_trace (__PRETTY_FUNCTION__, ifp, upstream_addr); zassert(ifp); @@ -292,31 +325,23 @@ int pim_joinprune_send(struct interface *ifp, return -1; } - if (PIM_DEBUG_PIM_TRACE) { - char source_str[100]; - char group_str[100]; - char dst_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (PIM_DEBUG_PIM_J_P) { + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); - zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s", + zlog_debug("%s: sending %s(S,G)=%s to upstream=%s on interface %s", __PRETTY_FUNCTION__, send_join ? "Join" : "Prune", - source_str, group_str, dst_str, ifp->name); + up->sg_str, dst_str, ifp->name); } if (PIM_INADDR_IS_ANY(upstream_addr)) { - if (PIM_DEBUG_PIM_TRACE) { - char source_str[100]; - char group_str[100]; - char dst_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (PIM_DEBUG_PIM_J_P) { + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); - zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s", + zlog_debug("%s: %s(S,G)=%s: upstream=%s is myself on interface %s", __PRETTY_FUNCTION__, send_join ? "Join" : "Prune", - source_str, group_str, dst_str, ifp->name); + up->sg_str, dst_str, ifp->name); } return 0; } @@ -335,83 +360,14 @@ int pim_joinprune_send(struct interface *ifp, /* Build PIM message */ + pim_msg_size = pim_msg_join_prune_encode (pim_msg, 9000, send_join, + up, upstream_addr, PIM_JP_HOLDTIME); - remain = pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, - remain, - upstream_addr); - if (!pim_msg_curr) { - char dst_str[100]; - pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); - zlog_warn("%s: failure encoding destination address %s: space left=%d", - __PRETTY_FUNCTION__, dst_str, remain); - return -3; - } - - remain = pastend - pim_msg_curr; - if (remain < 4) { - zlog_warn("%s: group will not fit: space left=%d", - __PRETTY_FUNCTION__, remain); - return -4; - } - - *pim_msg_curr = 0; /* reserved */ - ++pim_msg_curr; - *pim_msg_curr = 1; /* number of groups */ - ++pim_msg_curr; - *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME); - ++pim_msg_curr; - ++pim_msg_curr; - - remain = pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, - remain, - group_addr); - if (!pim_msg_curr) { - char group_str[100]; - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: failure encoding group address %s: space left=%d", - __PRETTY_FUNCTION__, group_str, remain); - return -5; - } - - remain = pastend - pim_msg_curr; - if (remain < 4) { - zlog_warn("%s: sources will not fit: space left=%d", - __PRETTY_FUNCTION__, remain); - return -6; - } - - /* number of joined sources */ - *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0); - ++pim_msg_curr; - ++pim_msg_curr; - - /* number of pruned sources */ - *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1); - ++pim_msg_curr; - ++pim_msg_curr; - - remain = pastend - pim_msg_curr; - pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, - remain, - source_addr); - if (!pim_msg_curr) { - char source_str[100]; - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_warn("%s: failure encoding source address %s: space left=%d", - __PRETTY_FUNCTION__, source_str, remain); - return -7; - } - - /* Add PIM header */ - - pim_msg_size = pim_msg_curr - pim_msg; - - pim_msg_build_header(pim_msg, pim_msg_size, - PIM_MSG_TYPE_JOIN_PRUNE); + if (pim_msg_size < 0) + return pim_msg_size; if (pim_msg_send(pim_ifp->pim_sock_fd, + pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, diff --git a/pimd/pim_join.h b/pimd/pim_join.h index dcdca00359..1eeeef756f 100644 --- a/pimd/pim_join.h +++ b/pimd/pim_join.h @@ -34,8 +34,7 @@ int pim_joinprune_recv(struct interface *ifp, int pim_joinprune_send(struct interface *ifp, struct in_addr upstream_addr, - struct in_addr source_addr, - struct in_addr group_addr, + struct pim_upstream *up, int send_join); #endif /* PIM_JOIN_H */ diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c index 622bef4439..127e0f6252 100644 --- a/pimd/pim_macro.c +++ b/pimd/pim_macro.c @@ -21,22 +21,39 @@ #include #include "log.h" +#include "prefix.h" +#include "vty.h" +#include "plist.h" -#include "pim_macro.h" #include "pimd.h" -#include "pim_str.h" +#include "pim_macro.h" #include "pim_iface.h" #include "pim_ifchannel.h" +#include "pim_rp.h" /* DownstreamJPState(S,G,I) is the per-interface state machine for receiving (S,G) Join/Prune messages. - DownstreamJPState(S,G,I) is either Join or Prune-Pending ? + DownstreamJPState(S,G,I) is either Join or Prune-Pending + DownstreamJPState(*,G,I) is either Join or Prune-Pending */ static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch) { - return ch->ifjoin_state != PIM_IFJOIN_NOINFO; + switch (ch->ifjoin_state) + { + case PIM_IFJOIN_NOINFO: + case PIM_IFJOIN_PRUNE: + case PIM_IFJOIN_PRUNE_TMP: + case PIM_IFJOIN_PRUNE_PENDING_TMP: + return 0; + break; + case PIM_IFJOIN_JOIN: + case PIM_IFJOIN_PRUNE_PENDING: + return 1; + break; + } + return 0; } /* @@ -100,13 +117,9 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): null interface", + zlog_warn("%s: (S,G)=%s: null interface", __PRETTY_FUNCTION__, - src_str, grp_str); + ch->sg_str); return 0; /* false */ } @@ -116,13 +129,9 @@ int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) pim_ifp = ifp->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); return 0; /* false */ } @@ -157,13 +166,9 @@ int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch) struct pim_interface *pim_ifp = ch->interface->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); return 0; /* false */ } @@ -225,13 +230,8 @@ int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): null interface", - __PRETTY_FUNCTION__, - src_str, grp_str); + zlog_warn("%s: (S,G)=%s: null interface", + __PRETTY_FUNCTION__, ch->sg_str); return 0; /* false */ } @@ -350,7 +350,7 @@ static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch) */ int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch) { - if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) { + if (ch->upstream->join_state == PIM_UPSTREAM_NOTJOINED) { /* oiflist is NULL */ return 0; /* false */ } @@ -386,25 +386,15 @@ int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch) ifp = ch->interface; if (!ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): null interface", - __PRETTY_FUNCTION__, - src_str, grp_str); + zlog_warn("%s: (S,G)=%s: null interface", + __PRETTY_FUNCTION__, ch->sg_str); return 0; /* false */ } pim_ifp = ifp->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name); + zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, ch->sg_str, ch->interface->name); return 0; /* false */ } diff --git a/pimd/pim_main.c b/pimd/pim_main.c index ed8f69b517..0749d60b05 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -43,10 +43,9 @@ #include "pim_version.h" #include "pim_signals.h" #include "pim_zebra.h" - -#ifdef PIM_ZCLIENT_DEBUG -extern int zclient_debug; -#endif +#include "pim_msdp.h" +#include "pim_iface.h" +#include "pim_rp.h" extern struct host host; @@ -70,6 +69,7 @@ zebra_capabilities_t _caps_p [] = ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, ZCAP_NET_RAW, + ZCAP_BIND, }; /* pimd privileges to run with */ @@ -104,18 +104,9 @@ Daemon which manages PIM.\n\n\ -A, --vty_addr Set vty's bind address\n\ -P, --vty_port Set vty's port number\n\ -v, --version Print program version\n\ -" - -#ifdef PIM_ZCLIENT_DEBUG -"\ --Z, --debug_zclient Enable zclient debugging\n\ -" -#endif - -"\ -h, --help Display this help and exit\n\ \n\ -Report bugs to %s\n", progname, PIMD_BUG_ADDRESS); +Report bugs to %s\n", progname, PACKAGE_BUGREPORT); } exit (status); @@ -177,11 +168,6 @@ int main(int argc, char** argv, char** envp) { print_version(progname); exit (0); break; -#ifdef PIM_ZCLIENT_DEBUG - case 'Z': - zclient_debug = 1; - break; -#endif case 'h': usage (0); break; @@ -206,8 +192,12 @@ int main(int argc, char** argv, char** envp) { vrf_init (); access_list_init(); prefix_list_init (); + prefix_list_add_hook (pim_rp_prefix_list_update); + prefix_list_delete_hook (pim_rp_prefix_list_update); + pim_route_map_init (); pim_init(); + pim_msdp_init (master); /* * Initialize zclient "update" and "lookup" sockets @@ -254,11 +244,6 @@ int main(int argc, char** argv, char** envp) { PIM_DO_DEBUG_ZEBRA; #endif -#ifdef PIM_ZCLIENT_DEBUG - zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)", - zclient_debug ? "ON" : "OFF"); -#endif - #ifdef PIM_CHECK_RECV_IFINDEX_SANITY zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex"); #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index 6014725020..ccd0fa81ac 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -39,3 +39,11 @@ DEFINE_MTYPE(PIMD, PIM_UPSTREAM, "PIM upstream (S,G) state") DEFINE_MTYPE(PIMD, PIM_SSMPINGD, "PIM sspimgd socket") DEFINE_MTYPE(PIMD, PIM_STATIC_ROUTE, "PIM Static Route") DEFINE_MTYPE(PIMD, PIM_BR, "PIM Bridge Router info") +DEFINE_MTYPE(PIMD, PIM_RP, "PIM RP info") +DEFINE_MTYPE(PIMD, PIM_FILTER_NAME, "PIM RP filter info") +DEFINE_MTYPE(PIMD, PIM_MSDP_PEER, "PIM MSDP peer") +DEFINE_MTYPE(PIMD, PIM_MSDP_MG_NAME, "PIM MSDP mesh-group name") +DEFINE_MTYPE(PIMD, PIM_MSDP_SA, "PIM MSDP source-active cache") +DEFINE_MTYPE(PIMD, PIM_MSDP_MG, "PIM MSDP mesh group") +DEFINE_MTYPE(PIMD, PIM_MSDP_MG_MBR, "PIM MSDP mesh group mbr") +DEFINE_MTYPE(PIMD, PIM_SEC_ADDR, "PIM secondary address") diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index 81841e58b6..b6b9b23239 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -38,5 +38,13 @@ DECLARE_MTYPE(PIM_UPSTREAM) DECLARE_MTYPE(PIM_SSMPINGD) DECLARE_MTYPE(PIM_STATIC_ROUTE) DECLARE_MTYPE(PIM_BR) +DECLARE_MTYPE(PIM_RP) +DECLARE_MTYPE(PIM_FILTER_NAME) +DECLARE_MTYPE(PIM_MSDP_PEER) +DECLARE_MTYPE(PIM_MSDP_MG_NAME) +DECLARE_MTYPE(PIM_MSDP_SA) +DECLARE_MTYPE(PIM_MSDP_MG) +DECLARE_MTYPE(PIM_MSDP_MG_MBR) +DECLARE_MTYPE(PIM_SEC_ADDR) #endif /* _QUAGGA_PIM_MEMORY_H */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 3fbed88800..dfd22b7022 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -23,8 +23,11 @@ #include "privs.h" #include "if.h" #include "prefix.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" +#include "pim_rpf.h" #include "pim_mroute.h" #include "pim_oil.h" #include "pim_str.h" @@ -34,10 +37,14 @@ #include "pim_rp.h" #include "pim_oil.h" #include "pim_register.h" +#include "pim_ifchannel.h" +#include "pim_zlookup.h" /* GLOBAL VARS */ extern struct zebra_privs_t pimd_privs; +static struct thread *qpim_mroute_socket_reader = NULL; + static void mroute_read_on(void); static int pim_mroute_set(int fd, int enable) @@ -45,62 +52,75 @@ static int pim_mroute_set(int fd, int enable) int err; int opt = enable ? MRT_INIT : MRT_DONE; socklen_t opt_len = sizeof(opt); + int rcvbuf = 1024 * 1024 * 8; + long flags; err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len); if (err) { - int e = errno; zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, - fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e)); - errno = e; + fd, enable ? "MRT_INIT" : "MRT_DONE", opt, errno, safe_strerror(errno)); return -1; } -#if 0 - zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok", - __FILE__, __PRETTY_FUNCTION__, - fd, opt); -#endif + err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + if (err) { + zlog_warn("%s: failure: setsockopt(fd=%d, SOL_SOCKET, %d): errno=%d: %s", + __PRETTY_FUNCTION__, fd, rcvbuf, errno, safe_strerror(errno)); + } - return 0; -} - -static int -pim_mroute_connected_to_source (struct interface *ifp, struct in_addr src) -{ - struct listnode *cnode; - struct connected *c; - struct prefix p; - - p.family = AF_INET; - p.u.prefix4 = src; - p.prefixlen = IPV4_MAX_BITLEN; - - for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { - if ((c->address->family == AF_INET) && - prefix_match (CONNECTED_PREFIX (c), &p)) - { - return 1; - } + zlog_warn("Could not get flags on socket fd:%d %d %s", + fd, errno, safe_strerror(errno)); + close (fd); + return -1; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) + { + zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s", + fd, errno, safe_strerror(errno)); + close(fd); + return -1; } + if (enable) + { +#if defined linux + int upcalls = IGMPMSG_WRVIFWHOLE; + opt = MRT_PIM; + + err = setsockopt (fd, IPPROTO_IP, opt, &upcalls, sizeof (upcalls)); + if (err) + { + zlog_warn ("Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s", + errno, safe_strerror (errno)); + return -1; + } +#else + zlog_warn ("PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall"); +#endif + } + return 0; } -static const char *igmpmsgtype2str[IGMPMSG_WHOLEPKT + 1] = { +static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = { "", "NOCACHE", "WRONGVIF", - "WHOLEPKT", }; + "WHOLEPKT", + "WRVIFWHOLE" }; static int -pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg, - const char *src_str, const char *grp_str) +pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg) { struct pim_interface *pim_ifp = ifp->info; struct pim_upstream *up; struct pim_rpf *rpg; + struct prefix_sg sg; + struct channel_oil *oil; rpg = RP(msg->im_dst); /* @@ -108,108 +128,131 @@ pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg * the Interface type is SSM we don't need to * do anything here */ - if ((rpg->rpf_addr.s_addr == INADDR_NONE) || + if ((pim_rpf_addr_is_inaddr_none (rpg)) || (!pim_ifp) || (!(PIM_I_am_DR(pim_ifp))) || (pim_ifp->itype == PIM_INTERFACE_SSM)) - return 0; + { + if (PIM_DEBUG_MROUTE_DETAIL) + zlog_debug ("%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP", + __PRETTY_FUNCTION__); + return 0; + } /* * If we've received a multicast packet that isn't connected to * us */ - if (!pim_mroute_connected_to_source (ifp, msg->im_src)) + if (!pim_if_connected_to_source (ifp, msg->im_src)) { - if (PIM_DEBUG_PIM_TRACE) - zlog_debug ("%s: Received incoming packet that does originate on our seg", + if (PIM_DEBUG_MROUTE_DETAIL) + zlog_debug ("%s: Received incoming packet that doesn't originate on our seg", __PRETTY_FUNCTION__); return 0; } - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption", - __PRETTY_FUNCTION__, grp_str, src_str); + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = msg->im_src; + sg.grp = msg->im_dst; + + oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index); + if (!oil) { + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: Failure to add channel oil for %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump (&sg)); + } + return 0; } - up = pim_upstream_add(msg->im_src, msg->im_dst, ifp); + up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__); if (!up) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: Failure to add upstream information for (%s,%s)", + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: Failure to add upstream information for %s", __PRETTY_FUNCTION__, - src_str, grp_str); + pim_str_sg_dump (&sg)); } return 0; } - pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD); - - up->channel_oil = pim_channel_oil_add(msg->im_dst, - msg->im_src, - pim_ifp->mroute_vif_index); - if (!up->channel_oil) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: Failure to add channel oil for (%s,%s)", - __PRETTY_FUNCTION__, - src_str, grp_str); - } - return 0; + /* + * I moved this debug till after the actual add because + * I want to take advantage of the up->sg_str being filled in. + */ + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption", + __PRETTY_FUNCTION__, up->sg_str); } + + PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); + pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); + + up->channel_oil = oil; up->channel_oil->cc.pktcnt++; - - pim_channel_add_oif(up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_SOURCE); + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + up->join_state = PIM_UPSTREAM_JOINED; return 0; } static int -pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf, - const char *src_str, const char *grp_str) +pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf) { struct pim_interface *pim_ifp; - struct in_addr group; - struct in_addr src; + struct prefix_sg sg; struct pim_rpf *rpg; const struct ip *ip_hdr; struct pim_upstream *up; ip_hdr = (const struct ip *)buf; - src = ip_hdr->ip_src; - group = ip_hdr->ip_dst; + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = ip_hdr->ip_src; + sg.grp = ip_hdr->ip_dst; - up = pim_upstream_find(src, group); + up = pim_upstream_find(&sg); if (!up) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)", - __PRETTY_FUNCTION__, src_str, grp_str); + if (PIM_DEBUG_MROUTE_DETAIL) { + zlog_debug("%s: Unable to find upstream channel WHOLEPKT%s", + __PRETTY_FUNCTION__, pim_str_sg_dump (&sg)); } return 0; } pim_ifp = up->rpf.source_nexthop.interface->info; - rpg = RP(group); + rpg = RP(sg.grp); - if ((rpg->rpf_addr.s_addr == INADDR_NONE) || + if ((pim_rpf_addr_is_inaddr_none (rpg)) || (!pim_ifp) || (!(PIM_I_am_DR(pim_ifp))) || (pim_ifp->itype == PIM_INTERFACE_SSM)) { - if (PIM_DEBUG_PIM_TRACE) { + if (PIM_DEBUG_MROUTE) { zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__); } return 0; } - pim_register_send((const struct ip *)(buf + sizeof(struct ip)), rpg); + /* + * If we've received a register suppress + */ + if (!up->t_rs_timer) + pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs (ip_hdr->ip_len), + pim_ifp->primary_address, rpg, 0, up); return 0; } static int -pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg, - const char *src_str, const char *grp_str) +pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg) { struct pim_ifchannel *ch; struct pim_interface *pim_ifp; + struct prefix_sg sg; + + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = msg->im_src; + sg.grp = msg->im_dst; /* Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. @@ -223,32 +266,39 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms */ if (!ifp) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d", __PRETTY_FUNCTION__, - src_str, grp_str, msg->im_vif); - } + pim_str_sg_dump (&sg), msg->im_vif); return -1; } pim_ifp = ifp->info; if (!pim_ifp) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s", + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); - } + pim_str_sg_dump (&sg), ifp->name); return -2; } - ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst); + ch = pim_ifchannel_find(ifp, &sg); if (!ch) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s", + struct prefix_sg star_g = sg; + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: WRONGVIF (S,G)=%s could not find channel on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + pim_str_sg_dump(&sg), ifp->name); + + star_g.src.s_addr = INADDR_ANY; + ch = pim_ifchannel_find(ifp, &star_g); + if (!ch) { + if (PIM_DEBUG_MROUTE) + zlog_debug("%s: WRONGVIF (*,G)=%s could not find channel on interface %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump(&star_g), ifp->name); + return -3; } - return -3; } /* @@ -266,28 +316,28 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms */ if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s", + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); } return -4; } if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel", + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); } return -5; } if (assert_action_a1(ch)) { - if (PIM_DEBUG_PIM_TRACE) { - zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + if (PIM_DEBUG_MROUTE) { + zlog_debug("%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + ch->sg_str, ifp->name); } return -6; } @@ -295,107 +345,250 @@ pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *ms return 0; } +static int +pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf) +{ + const struct ip *ip_hdr = (const struct ip *)buf; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + struct pim_upstream *up; + //struct prefix_sg star_g; + struct prefix_sg sg; + struct channel_oil *oil; + + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = ip_hdr->ip_src; + sg.grp = ip_hdr->ip_dst; + + ch = pim_ifchannel_find(ifp, &sg); + if (ch) + { + if (PIM_DEBUG_MROUTE) + zlog_debug ("WRVIFWHOLE (S,G)=%s found ifchannel on interface %s", + ch->sg_str, ifp->name); + return -1; + } +#if 0 + star_g = sg; + star_g.src.s_addr = INADDR_ANY; + ch = pim_ifchannel_find(ifp, &star_g); + if (ch) + { + if (PIM_DEBUG_MROUTE) + zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s", + pim_str_sg_dump (&star_g), ifp->name); + return -1; + } +#endif + + up = pim_upstream_find (&sg); + if (up) + { + struct pim_nexthop source; + struct pim_rpf *rpf = RP (sg.grp); + if (!rpf || !rpf->source_nexthop.interface) + return 0; + + pim_ifp = rpf->source_nexthop.interface->info; + + memset (&source, 0, sizeof (source)); + /* + * If we are the fhr that means we are getting a callback during + * the pimreg period, so I believe we can ignore this packet + */ + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) + { + //No if channel, but upstream we are at the RP. + if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0) + pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register); + if (!up->channel_oil) + up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index); + pim_upstream_inherited_olist (up); + if (!up->channel_oil->installed) + pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_set_sptbit (up, ifp); + } + else + { + if (I_am_RP (up->sg.grp)) + { + if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0) + pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register); + up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; + } + pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); + pim_upstream_inherited_olist (up); + pim_mroute_msg_wholepkt (fd, ifp, buf); + } + return 0; + } + + pim_ifp = ifp->info; + oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index); + if (!oil->installed) + pim_mroute_add (oil, __PRETTY_FUNCTION__); + if (pim_if_connected_to_source (ifp, sg.src)) + { + up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__); + if (!up) + { + if (PIM_DEBUG_MROUTE) + zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface", + pim_str_sg_dump (&sg), ifp->name); + return -2; + } + PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); + pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); + up->channel_oil = oil; + up->channel_oil->cc.pktcnt++; + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + up->join_state = PIM_UPSTREAM_JOINED; + pim_upstream_inherited_olist (up); + + // Send the packet to the RP + pim_mroute_msg_wholepkt (fd, ifp, buf); + } + + return 0; +} + int pim_mroute_msg(int fd, const char *buf, int buf_size) { struct interface *ifp; + struct pim_interface *pim_ifp; const struct ip *ip_hdr; const struct igmpmsg *msg; - char src_str[100] = ""; - char grp_str[100] = ""; + char ip_src_str[INET_ADDRSTRLEN] = ""; + char ip_dst_str[INET_ADDRSTRLEN] = ""; + char src_str[INET_ADDRSTRLEN] = ""; + char grp_str[INET_ADDRSTRLEN] = ""; + struct in_addr ifaddr; + struct igmp_sock *igmp; ip_hdr = (const struct ip *) buf; - /* kernel upcall must have protocol=0 */ - if (ip_hdr->ip_p) { - /* this is not a kernel upcall */ - if (PIM_DEBUG_PIM_TRACE) { + if (ip_hdr->ip_p == IPPROTO_IGMP) { + + /* We have the IP packet but we do not know which interface this packet was + * received on. Find the interface that is on the same subnet as the source + * of the IP packet. + */ + ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT); + + if (!ifp) { + if (PIM_DEBUG_MROUTE_DETAIL) { + pim_inet4_dump("", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str)); + + zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s", + __PRETTY_FUNCTION__, + ip_src_str, + ip_dst_str); + } + return 0; + } + pim_ifp = ifp->info; + ifaddr = pim_find_primary_addr(ifp); + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr); + + if (PIM_DEBUG_MROUTE) { + pim_inet4_dump("", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str)); + + zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s", + __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str); + } + if (igmp) + pim_igmp_packet(igmp, (char *)buf, buf_size); + + } else if (ip_hdr->ip_p) { + if (PIM_DEBUG_MROUTE_DETAIL) { pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); pim_inet4_dump("", ip_hdr->ip_dst, grp_str, sizeof(grp_str)); - zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d", - __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size); + zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d", + __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size); } - return 0; - } - msg = (const struct igmpmsg *) buf; + } else { + msg = (const struct igmpmsg *) buf; - ifp = pim_if_find_by_vif_index(msg->im_vif); + ifp = pim_if_find_by_vif_index(msg->im_vif); - if (PIM_DEBUG_PIM_TRACE) { - pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); - pim_inet4_dump("", msg->im_dst, grp_str, sizeof(grp_str)); - zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", - __PRETTY_FUNCTION__, - igmpmsgtype2str[msg->im_msgtype], - msg->im_msgtype, - ip_hdr->ip_p, - fd, - src_str, - grp_str, - ifp ? ifp->name : "", - msg->im_vif); - } + if (!ifp) + return 0; + if (PIM_DEBUG_MROUTE) { + pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); + pim_inet4_dump("", msg->im_dst, grp_str, sizeof(grp_str)); + zlog_warn("%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d", + __PRETTY_FUNCTION__, + igmpmsgtype2str[msg->im_msgtype], + msg->im_msgtype, + ip_hdr->ip_p, + fd, + src_str, + grp_str, + ifp->name, + msg->im_vif, buf_size); + } - switch (msg->im_msgtype) { - case IGMPMSG_WRONGVIF: - return pim_mroute_msg_wrongvif(fd, ifp, msg, src_str, grp_str); - break; - case IGMPMSG_NOCACHE: - return pim_mroute_msg_nocache(fd, ifp, msg, src_str, grp_str); - break; - case IGMPMSG_WHOLEPKT: - return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg, src_str, grp_str); - break; - default: - break; + switch (msg->im_msgtype) { + case IGMPMSG_WRONGVIF: + return pim_mroute_msg_wrongvif(fd, ifp, msg); + break; + case IGMPMSG_NOCACHE: + return pim_mroute_msg_nocache(fd, ifp, msg); + break; + case IGMPMSG_WHOLEPKT: + return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg); + break; + case IGMPMSG_WRVIFWHOLE: + return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg); + break; + default: + break; + } } return 0; } -static int mroute_read_msg(int fd) -{ - const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); - char buf[1000]; - int rd; - - if (((int) sizeof(buf)) < msg_min_size) { - zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d", - __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); - return -1; - } - - rd = read(fd, buf, sizeof(buf)); - if (rd < 0) { - zlog_warn("%s: failure reading fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); - return -2; - } - - if (rd < msg_min_size) { - zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d", - __PRETTY_FUNCTION__, fd, rd, msg_min_size); - return -3; - } - - return pim_mroute_msg(fd, buf, rd); -} - static int mroute_read(struct thread *t) { + static long long count; + char buf[10000]; + int result = 0; + int cont = 1; int fd; - int result; - - zassert(t); - zassert(!THREAD_ARG(t)); + int rd; fd = THREAD_FD(t); - zassert(fd == qpim_mroute_socket_fd); - result = mroute_read_msg(fd); + while (cont) + { + rd = read(fd, buf, sizeof(buf)); + if (rd < 0) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) + { + cont = 0; + break; + } + if (PIM_DEBUG_MROUTE) + zlog_warn("%s: failure reading fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + goto done; + } + result = pim_mroute_msg(fd, buf, rd); + + count++; + if (count % qpim_packet_process == 0) + cont = 0; + } /* Keep reading */ - qpim_mroute_socket_reader = 0; + done: + qpim_mroute_socket_reader = NULL; mroute_read_on(); return result; @@ -450,8 +643,6 @@ int pim_mroute_socket_enable() qpim_mroute_socket_creation = pim_time_monotonic_sec(); mroute_read_on(); - zassert(PIM_MROUTE_IS_ENABLED); - return 0; } @@ -475,8 +666,6 @@ int pim_mroute_socket_disable() mroute_read_off(); qpim_mroute_socket_fd = -1; - zassert(PIM_MROUTE_IS_DISABLED); - return 0; } @@ -521,16 +710,14 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned ch err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); if (err) { - char ifaddr_str[100]; - int e = errno; + char ifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ifaddr, ifaddr_str, sizeof(ifaddr_str)); zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags, - e, safe_strerror(e)); - errno = e; + errno, safe_strerror(errno)); return -2; } @@ -553,22 +740,21 @@ int pim_mroute_del_vif(int vif_index) err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); if (err) { - int e = errno; zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, vif_index, - e, safe_strerror(e)); - errno = e; + errno, safe_strerror(errno)); return -2; } return 0; } -int pim_mroute_add(struct channel_oil *c_oil) +int pim_mroute_add(struct channel_oil *c_oil, const char *name) { int err; int orig = 0; + int orig_iif_vif = 0; qpim_mroute_add_last = pim_time_monotonic_sec(); ++qpim_mroute_add_events; @@ -589,27 +775,57 @@ int pim_mroute_add(struct channel_oil *c_oil) c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; } + /* + * If we have an unresolved cache entry for the S,G + * it is owned by the pimreg for the incoming IIF + * So set pimreg as the IIF temporarily to cause + * the packets to be forwarded. Then set it + * to the correct IIF afterwords. + */ + if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY && + c_oil->oil.mfcc_parent != 0) + { + orig_iif_vif = c_oil->oil.mfcc_parent; + c_oil->oil.mfcc_parent = 0; + } err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, &c_oil->oil, sizeof(c_oil->oil)); + if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY && + orig_iif_vif != 0) + { + c_oil->oil.mfcc_parent = orig_iif_vif; + err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, + &c_oil->oil, sizeof (c_oil->oil)); + } + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; if (err) { - int e = errno; zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, - e, safe_strerror(e)); - errno = e; + errno, safe_strerror(errno)); return -2; } + if (PIM_DEBUG_MROUTE) + { + struct prefix_sg sg; + + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; + + zlog_debug("%s(%s), Added Route: %s to mroute table", + __PRETTY_FUNCTION__, name, pim_str_sg_dump(&sg)); + } + c_oil->installed = 1; return 0; } -int pim_mroute_del (struct channel_oil *c_oil) +int pim_mroute_del (struct channel_oil *c_oil, const char *name) { int err; @@ -624,15 +840,24 @@ int pim_mroute_del (struct channel_oil *c_oil) err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil)); if (err) { - int e = errno; - zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", - __FILE__, __PRETTY_FUNCTION__, - qpim_mroute_socket_fd, - e, safe_strerror(e)); - errno = e; + if (PIM_DEBUG_MROUTE) + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, + errno, safe_strerror(errno)); return -2; } + if (PIM_DEBUG_MROUTE) + { + struct prefix_sg sg; + + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; + + zlog_debug("%s(%s), Deleted Route: %s from mroute table", + __PRETTY_FUNCTION__, name, pim_str_sg_dump(&sg)); + } c_oil->installed = 0; return 0; @@ -643,28 +868,46 @@ pim_mroute_update_counters (struct channel_oil *c_oil) { struct sioc_sg_req sgreq; - memset (&sgreq, 0, sizeof(sgreq)); - sgreq.src = c_oil->oil.mfcc_origin; - sgreq.grp = c_oil->oil.mfcc_mcastgrp; - c_oil->cc.oldpktcnt = c_oil->cc.pktcnt; c_oil->cc.oldbytecnt = c_oil->cc.bytecnt; c_oil->cc.oldwrong_if = c_oil->cc.wrong_if; + if (!c_oil->installed) + { + c_oil->cc.lastused = 100 * qpim_keep_alive_time; + if (PIM_DEBUG_MROUTE) + { + struct prefix_sg sg; + + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; + if (PIM_DEBUG_MROUTE) + zlog_debug("Channel(%s) is not installed no need to collect data from kernel", + pim_str_sg_dump (&sg)); + } + return; + } + + memset (&sgreq, 0, sizeof(sgreq)); + sgreq.src = c_oil->oil.mfcc_origin; + sgreq.grp = c_oil->oil.mfcc_mcastgrp; + + pim_zlookup_sg_statistics (c_oil); if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) { - char group_str[100]; - char source_str[100]; + if (PIM_DEBUG_MROUTE) + { + struct prefix_sg sg; - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + sg.src = c_oil->oil.mfcc_origin; + sg.grp = c_oil->oil.mfcc_mcastgrp; - zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s", - (unsigned long)SIOCGETSGCNT, - source_str, - group_str, - errno, - safe_strerror(errno)); + zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s", + (unsigned long)SIOCGETSGCNT, + pim_str_sg_dump (&sg), + errno, + safe_strerror(errno)); + } return; } diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index f385ce09f4..3c15c800da 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -157,6 +157,10 @@ struct igmpmsg #endif #endif +#ifndef IGMPMSG_WRVIFWHOLE +#define IGMPMSG_WRVIFWHOLE 4 /* For PIM processing */ +#endif + /* Above: from */ @@ -167,8 +171,8 @@ int pim_mroute_socket_disable(void); int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags); int pim_mroute_del_vif(int vif_index); -int pim_mroute_add(struct channel_oil *c_oil); -int pim_mroute_del(struct channel_oil *c_oil); +int pim_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_mroute_del(struct channel_oil *c_oil, const char *name); int pim_mroute_msg(int fd, const char *buf, int buf_size); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c new file mode 100644 index 0000000000..93141f39de --- /dev/null +++ b/pimd/pim_msdp.c @@ -0,0 +1,1606 @@ +/* + * IP MSDP for Quagga + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pimd.h" +#include "pim_cmd.h" +#include "pim_memory.h" +#include "pim_iface.h" +#include "pim_rp.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_upstream.h" + +#include "pim_msdp.h" +#include "pim_msdp_packet.h" +#include "pim_msdp_socket.h" + +struct pim_msdp pim_msdp, *msdp = &pim_msdp; + +static void pim_msdp_peer_listen(struct pim_msdp_peer *mp); +static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start); +static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start); +static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start); +static void pim_msdp_peer_free(struct pim_msdp_peer *mp); +static void pim_msdp_enable(void); +static void pim_msdp_sa_adv_timer_setup(bool start); +static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags); +static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2); +static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr); +static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr); + +/************************ SA cache management ******************************/ +static void +pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, const char *timer_str) +{ + zlog_debug("MSDP SA %s %s timer expired", sa->sg_str, timer_str); +} + +/* RFC-3618:Sec-5.1 - global active source advertisement timer */ +static int +pim_msdp_sa_adv_timer_cb(struct thread *t) +{ + msdp->sa_adv_timer = NULL; + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA advertisment timer expired"); + } + + pim_msdp_sa_adv_timer_setup(true /* start */); + pim_msdp_pkt_sa_tx(); + return 0; +} +static void +pim_msdp_sa_adv_timer_setup(bool start) +{ + THREAD_OFF(msdp->sa_adv_timer); + if (start) { + THREAD_TIMER_ON(msdp->master, msdp->sa_adv_timer, + pim_msdp_sa_adv_timer_cb, NULL, PIM_MSDP_SA_ADVERTISMENT_TIME); + } +} + +/* RFC-3618:Sec-5.3 - SA cache state timer */ +static int +pim_msdp_sa_state_timer_cb(struct thread *t) +{ + struct pim_msdp_sa *sa; + + sa = THREAD_ARG(t); + sa->sa_state_timer = NULL; + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_sa_timer_expiry_log(sa, "state"); + } + + pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER); + return 0; +} +static void +pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start) +{ + THREAD_OFF(sa->sa_state_timer); + if (start) { + THREAD_TIMER_ON(msdp->master, sa->sa_state_timer, + pim_msdp_sa_state_timer_cb, sa, PIM_MSDP_SA_HOLD_TIME); + } +} + +static void +pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa) +{ + struct pim_upstream *up = sa->up; + if (!up) { + return; + } + + sa->up = NULL; + if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) { + PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags); + sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG; + pim_upstream_del(up, __PRETTY_FUNCTION__); + sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG; + } + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s de-referenced SPT", sa->sg_str); + } +} + +static bool +pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa, struct pim_upstream *xg_up) +{ + if (!(sa->flags & PIM_MSDP_SAF_PEER)) { + /* SA should have been rxed from a peer */ + return false; + } + /* check if we are RP */ + if (!I_am_RP(sa->sg.grp)) { + return false; + } + + /* check if we have a (*, G) with a non-empty immediate OIL */ + if (!xg_up) { + struct prefix_sg sg; + + memset(&sg, 0, sizeof(sg)); + sg.grp = sa->sg.grp; + + xg_up = pim_upstream_find(&sg); + } + if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) { + /* join desired will be true for such (*, G) entries so we will + * just look at join_state and let the PIM state machine do the rest of + * the magic */ + return false; + } + + return true; +} + +/* Upstream add evaluation needs to happen everytime - + * 1. Peer reference is added or removed. + * 2. The RP for a group changes. + * 3. joinDesired for the associated (*, G) changes + * 4. associated (*, G) is removed - this seems like a bit redundant + * (considering #4); but just in case an entry gets nuked without + * upstream state transition + * */ +static void +pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa, + struct pim_upstream *xg_up, const char *ctx) +{ + struct pim_upstream *up; + + if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) { + pim_msdp_sa_upstream_del(sa); + return; + } + + if (sa->up) { + /* nothing to do */ + return; + } + + up = pim_upstream_find(&sa->sg); + if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) { + /* somehow we lost track of the upstream ptr? best log it */ + sa->up = up; + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s SPT reference missing", sa->sg_str); + } + return; + } + + /* RFC3618: "RP triggers a (S, G) join event towards the data source + * as if a JP message was rxed addressed to the RP itself." */ + up = pim_upstream_add(&sa->sg, NULL /* iif */, + PIM_UPSTREAM_FLAG_MASK_SRC_MSDP, + __PRETTY_FUNCTION__); + + sa->up = up; + if (up) { + /* update inherited oil */ + pim_upstream_inherited_olist(up); + /* should we also start the kat in parallel? we will need it when the + * SA ages out */ + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s referenced SPT", sa->sg_str); + } + } else { + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s SPT reference failed", sa->sg_str); + } + } +} + +/* release all mem associated with a sa */ +static void +pim_msdp_sa_free(struct pim_msdp_sa *sa) +{ + XFREE(MTYPE_PIM_MSDP_SA, sa); +} + +static struct pim_msdp_sa * +pim_msdp_sa_new(struct prefix_sg *sg, struct in_addr rp) +{ + struct pim_msdp_sa *sa; + + sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa)); + if (!sa) { + zlog_err("%s: PIM XCALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*sa)); + return NULL; + } + + sa->sg = *sg; + pim_str_sg_set(sg, sa->sg_str); + sa->rp = rp; + sa->uptime = pim_time_monotonic_sec(); + + /* insert into misc tables for easy access */ + sa = hash_get(msdp->sa_hash, sa, hash_alloc_intern); + if (!sa) { + zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__); + pim_msdp_sa_free(sa); + return NULL; + } + listnode_add_sort(msdp->sa_list, sa); + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s created", sa->sg_str); + } + + return sa; +} + +static struct pim_msdp_sa * +pim_msdp_sa_find(struct prefix_sg *sg) +{ + struct pim_msdp_sa lookup; + + lookup.sg = *sg; + return hash_lookup(msdp->sa_hash, &lookup); +} + +static struct pim_msdp_sa * +pim_msdp_sa_add(struct prefix_sg *sg, struct in_addr rp) +{ + struct pim_msdp_sa *sa; + + sa = pim_msdp_sa_find(sg); + if (sa) { + return sa; + } + + return pim_msdp_sa_new(sg, rp); +} + +static void +pim_msdp_sa_del(struct pim_msdp_sa * sa) +{ + /* this is somewhat redundant - still want to be careful not to leave + * stale upstream references */ + pim_msdp_sa_upstream_del(sa); + + /* stop timers */ + pim_msdp_sa_state_timer_setup(sa, false /* start */); + + /* remove the entry from various tables */ + listnode_delete(msdp->sa_list, sa); + hash_release(msdp->sa_hash, sa); + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s deleted", sa->sg_str); + } + + /* free up any associated memory */ + pim_msdp_sa_free(sa); +} + +static void +pim_msdp_sa_peer_ip_set(struct pim_msdp_sa *sa, struct pim_msdp_peer *mp, struct in_addr rp) +{ + struct pim_msdp_peer *old_mp; + + /* optimize the "no change" case as it will happen + * frequently/periodically */ + if (mp && (sa->peer.s_addr == mp->peer.s_addr)) { + return; + } + + /* any time the peer ip changes also update the rp address */ + if (PIM_INADDR_ISNOT_ANY(sa->peer)) { + old_mp = pim_msdp_peer_find(sa->peer); + if (old_mp && old_mp->sa_cnt) { + --old_mp->sa_cnt; + } + } + + if (mp) { + ++mp->sa_cnt; + sa->peer = mp->peer; + } else { + sa->peer.s_addr = PIM_NET_INADDR_ANY; + } + sa->rp = rp; +} + +/* When a local active-source is removed there is no way to withdraw the + * source from peers. We will simply remove it from the SA cache so it will + * not be sent in supsequent SA updates. Peers will consequently timeout the + * SA. + * Similarly a "peer-added" SA is never explicitly deleted. It is simply + * aged out overtime if not seen in the SA updates from the peers. + * XXX: should we provide a knob to drop entries learnt from a peer when the + * peer goes down? */ +static void +pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags) +{ + bool update_up = false; + + if ((sa->flags &PIM_MSDP_SAF_LOCAL)) { + if (flags & PIM_MSDP_SAF_LOCAL) { + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s local reference removed", sa->sg_str); + } + if (msdp->local_cnt) + --msdp->local_cnt; + } + } + + if ((sa->flags &PIM_MSDP_SAF_PEER)) { + if (flags & PIM_MSDP_SAF_PEER) { + struct in_addr rp; + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s peer reference removed", sa->sg_str); + } + pim_msdp_sa_state_timer_setup(sa, false /* start */); + rp.s_addr = INADDR_ANY; + pim_msdp_sa_peer_ip_set(sa, NULL /* mp */, rp); + /* if peer ref was removed we need to remove the msdp reference on the + * msdp entry */ + update_up = true; + } + } + + sa->flags &= ~flags; + if (update_up) { + pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref"); + } + + if (!(sa->flags & PIM_MSDP_SAF_REF)) { + pim_msdp_sa_del(sa); + } +} + +void +pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg, + struct in_addr rp) +{ + struct pim_msdp_sa *sa; + + sa = pim_msdp_sa_add(sg, rp); + if (!sa) { + return; + } + + /* reference it */ + if (mp) { + if (!(sa->flags & PIM_MSDP_SAF_PEER)) { + sa->flags |= PIM_MSDP_SAF_PEER; + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s added by peer", sa->sg_str); + } + } + pim_msdp_sa_peer_ip_set(sa, mp, rp); + /* start/re-start the state timer to prevent cache expiry */ + pim_msdp_sa_state_timer_setup(sa, true /* start */); + /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from a + * peer. XXX: If this becomes too much of a periodic overhead we + * can make it event based */ + pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref"); + } else { + if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) { + sa->flags |= PIM_MSDP_SAF_LOCAL; + ++msdp->local_cnt; + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP SA %s added locally", sa->sg_str); + } + /* send an immediate SA update to peers */ + pim_msdp_pkt_sa_tx_one(sa); + } + sa->flags &= ~PIM_MSDP_SAF_STALE; + } +} + +/* The following criteria must be met to originate an SA from the MSDP + * speaker - + * 1. KAT must be running i.e. source is active. + * 2. We must be RP for the group. + * 3. Source must be registrable to the RP (this is where the RFC is vague + * and especially ambiguous in CLOS networks; with anycast RP all sources + * are potentially registrable to all RPs in the domain). We assume #3 is + * satisfied if - + * a. We are also the FHR-DR for the source (OR) + * b. We rxed a pim register (null or data encapsulated) within the last + * (3 * (1.5 * register_suppression_timer))). + */ +static bool +pim_msdp_sa_local_add_ok(struct pim_upstream *up) +{ + if (!(msdp->flags & PIM_MSDPF_ENABLE)) { + return false; + } + + if (!up->t_ka_timer) { + /* stream is not active */ + return false; + } + + if (!I_am_RP(up->sg.grp)) { + /* we are not RP for the group */ + return false; + } + + /* we are the FHR-DR for this stream or we are RP and have seen registers + * from a FHR for this source */ + if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || up->t_msdp_reg_timer) { + return true; + } + + return false; +} + +static void +pim_msdp_sa_local_add(struct prefix_sg *sg) +{ + struct in_addr rp; + rp.s_addr = 0; + pim_msdp_sa_ref(NULL /* mp */, sg, rp); +} + +void +pim_msdp_sa_local_del(struct prefix_sg *sg) +{ + struct pim_msdp_sa *sa; + + sa = pim_msdp_sa_find(sg); + if (sa) { + pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL); + } +} + +/* we need to be very cautious with this API as SA del too can trigger an + * upstream del and we will get stuck in a simple loop */ +static void +pim_msdp_sa_local_del_on_up_del(struct prefix_sg *sg) +{ + struct pim_msdp_sa *sa; + + sa = pim_msdp_sa_find(sg); + if (sa) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP local sa %s del on up del", sa->sg_str); + } + + /* if there is no local reference escape */ + if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP local sa %s del; no local ref", sa->sg_str); + } + return; + } + + if (sa->flags & PIM_MSDP_SAF_UP_DEL_IN_PROG) { + /* MSDP is the one that triggered the upstream del. if this happens + * we most certainly have a bug in the PIM upstream state machine. We + * will not have a local reference unless the KAT is running. And if the + * KAT is running there MUST be an additional source-stream reference to + * the flow. Accounting for such cases requires lot of changes; perhaps + * address this in the next release? - XXX */ + zlog_err("MSDP sa %s SPT teardown is causing the local entry to be removed", sa->sg_str); + return; + } + + /* we are dropping the sa on upstream del we should not have an + * upstream reference */ + if (sa->up) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP local sa %s del; up non-NULL", sa->sg_str); + } + sa->up = NULL; + } + pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL); + } +} + +/* Local SA qualification needs to be re-evaluated when - + * 1. KAT is started or stopped + * 2. on RP changes + * 3. Whenever FHR status changes for a (S,G) - XXX - currently there + * is no clear path to transition an entry out of "MASK_FHR" need + * to discuss this with Donald. May result in some strangeness if the + * FHR is also the RP. + * 4. When msdp_reg timer is started or stopped + */ +void +pim_msdp_sa_local_update(struct pim_upstream *up) +{ + if (pim_msdp_sa_local_add_ok(up)) { + pim_msdp_sa_local_add(&up->sg); + } else { + pim_msdp_sa_local_del(&up->sg); + } +} + +static void +pim_msdp_sa_local_setup(void) +{ + struct pim_upstream *up; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) { + pim_msdp_sa_local_update(up); + } +} + +/* whenever the RP changes we need to re-evaluate the "local" SA-cache */ +/* XXX: needs to be tested */ +void +pim_msdp_i_am_rp_changed(void) +{ + struct listnode *sanode; + struct listnode *nextnode; + struct pim_msdp_sa *sa; + + if (!(msdp->flags & PIM_MSDPF_ENABLE)) { + /* if the feature is not enabled do nothing */ + return; + } + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP i_am_rp changed"); + } + + /* mark all local entries as stale */ + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + if (sa->flags & PIM_MSDP_SAF_LOCAL) { + sa->flags |= PIM_MSDP_SAF_STALE; + } + } + + /* re-setup local SA entries */ + pim_msdp_sa_local_setup(); + + for (ALL_LIST_ELEMENTS(msdp->sa_list, sanode, nextnode, sa)) { + /* purge stale SA entries */ + if (sa->flags & PIM_MSDP_SAF_STALE) { + /* clear the stale flag; the entry may be kept even after + * "local-deref" */ + sa->flags &= ~PIM_MSDP_SAF_STALE; + /* sa_deref can end up freeing the sa; so don't access contents after */ + pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL); + } else { + /* if the souce is still active check if we can influence SPT */ + pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change"); + } + } +} + +/* We track the join state of (*, G) entries. If G has sources in the SA-cache + * we need to setup or teardown SPT when the JoinDesired status changes for + * (*, G) */ +void +pim_msdp_up_join_state_changed(struct pim_upstream *xg_up) +{ + struct listnode *sanode; + struct pim_msdp_sa *sa; + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP join state changed for %s", xg_up->sg_str); + } + + /* If this is not really an XG entry just move on */ + if ((xg_up->sg.src.s_addr != INADDR_ANY) || + (xg_up->sg.grp.s_addr == INADDR_ANY)) { + return; + } + + /* XXX: Need to maintain SAs per-group to avoid all this unnecessary + * walking */ + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + if (sa->sg.grp.s_addr != xg_up->sg.grp.s_addr) { + continue; + } + pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change"); + } +} + +static void +pim_msdp_up_xg_del(struct prefix_sg *sg) +{ + struct listnode *sanode; + struct pim_msdp_sa *sa; + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP %s del", pim_str_sg_dump(sg)); + } + + /* If this is not really an XG entry just move on */ + if ((sg->src.s_addr != INADDR_ANY) || + (sg->grp.s_addr == INADDR_ANY)) { + return; + } + + /* XXX: Need to maintain SAs per-group to avoid all this unnecessary + * walking */ + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + if (sa->sg.grp.s_addr != sg->grp.s_addr) { + continue; + } + pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change"); + } +} + +void +pim_msdp_up_del(struct prefix_sg *sg) +{ + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP up %s del", pim_str_sg_dump(sg)); + } + if (sg->src.s_addr == INADDR_ANY) { + pim_msdp_up_xg_del(sg); + } else { + pim_msdp_sa_local_del_on_up_del(sg); + } +} + +/* sa hash and peer list helpers */ +static unsigned int +pim_msdp_sa_hash_key_make(void *p) +{ + struct pim_msdp_sa *sa = p; + + return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0)); +} + +static int +pim_msdp_sa_hash_eq(const void *p1, const void *p2) +{ + const struct pim_msdp_sa *sa1 = p1; + const struct pim_msdp_sa *sa2 = p2; + + return ((sa1->sg.src.s_addr == sa2->sg.src.s_addr) && + (sa1->sg.grp.s_addr == sa2->sg.grp.s_addr)); +} + +static int +pim_msdp_sa_comp(const void *p1, const void *p2) +{ + const struct pim_msdp_sa *sa1 = p1; + const struct pim_msdp_sa *sa2 = p2; + + if (ntohl(sa1->sg.grp.s_addr) < ntohl(sa2->sg.grp.s_addr)) + return -1; + + if (ntohl(sa1->sg.grp.s_addr) > ntohl(sa2->sg.grp.s_addr)) + return 1; + + if (ntohl(sa1->sg.src.s_addr) < ntohl(sa2->sg.src.s_addr)) + return -1; + + if (ntohl(sa1->sg.src.s_addr) > ntohl(sa2->sg.src.s_addr)) + return 1; + + return 0; +} + +/* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */ +/* XXX: this can use a bit of refining and extensions */ +bool +pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp) +{ + if (mp->peer.s_addr == rp.s_addr) { + return true; + } + + return false; +} + +/************************ Peer session management **************************/ +char * +pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size) +{ + switch (state) { + case PIM_MSDP_DISABLED: + snprintf(buf, buf_size, "%s", "disabled"); + break; + case PIM_MSDP_INACTIVE: + snprintf(buf, buf_size, "%s", "inactive"); + break; + case PIM_MSDP_LISTEN: + snprintf(buf, buf_size, "%s", "listen"); + break; + case PIM_MSDP_CONNECTING: + snprintf(buf, buf_size, "%s", "connecting"); + break; + case PIM_MSDP_ESTABLISHED: + snprintf(buf, buf_size, "%s", "established"); + break; + default: + snprintf(buf, buf_size, "unk-%d", state); + } + return buf; +} + +char * +pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format) +{ + char peer_str[INET_ADDRSTRLEN]; + char local_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", mp->peer, peer_str, sizeof(peer_str)); + if (long_format) { + pim_inet4_dump("", mp->local, local_str, sizeof(local_str)); + snprintf(buf, buf_size, "MSDP peer %s local %s mg %s", + peer_str, local_str, mp->mesh_group_name); + } else { + snprintf(buf, buf_size, "MSDP peer %s", peer_str); + } + + return buf; +} + +static void +pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp) +{ + char state_str[PIM_MSDP_STATE_STRLEN]; + + pim_msdp_state_dump(mp->state, state_str, sizeof(state_str)); + zlog_debug("MSDP peer %s state chg to %s", mp->key_str, state_str); +} + +/* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */ +/* 11.2.A2: active peer - start connect retry timer; when the timer fires + * a tcp connection will be made */ +static void +pim_msdp_peer_connect(struct pim_msdp_peer *mp) +{ + mp->state = PIM_MSDP_CONNECTING; + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_state_chg_log(mp); + } + + pim_msdp_peer_cr_timer_setup(mp, true /* start */); +} + +/* 11.2.A3: passive peer - just listen for connections */ +static void +pim_msdp_peer_listen(struct pim_msdp_peer *mp) +{ + mp->state = PIM_MSDP_LISTEN; + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_state_chg_log(mp); + } + + /* this is interntionally asymmetric i.e. we set up listen-socket when the + * first listening peer is configured; but don't bother tearing it down when + * all the peers go down */ + pim_msdp_sock_listen(); +} + +/* 11.2.A4 and 11.2.A5: transition active or passive peer to + * established state */ +void +pim_msdp_peer_established(struct pim_msdp_peer *mp) +{ + if (mp->state != PIM_MSDP_ESTABLISHED) { + ++mp->est_flaps; + } + + mp->state = PIM_MSDP_ESTABLISHED; + mp->uptime = pim_time_monotonic_sec(); + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_state_chg_log(mp); + } + + /* stop retry timer on active peers */ + pim_msdp_peer_cr_timer_setup(mp, false /* start */); + + /* send KA; start KA and hold timers */ + pim_msdp_pkt_ka_tx(mp); + pim_msdp_peer_ka_timer_setup(mp, true /* start */); + pim_msdp_peer_hold_timer_setup(mp, true /* start */); + + pim_msdp_pkt_sa_tx_to_one_peer(mp); + + PIM_MSDP_PEER_WRITE_ON(mp); + PIM_MSDP_PEER_READ_ON(mp); +} + +/* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */ +void +pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state) +{ + if (chg_state) { + if (mp->state == PIM_MSDP_ESTABLISHED) { + ++mp->est_flaps; + } + mp->state = PIM_MSDP_INACTIVE; + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_state_chg_log(mp); + } + } + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_peer_stop_tcp_conn", mp->key_str); + } + /* stop read and write threads */ + PIM_MSDP_PEER_READ_OFF(mp); + PIM_MSDP_PEER_WRITE_OFF(mp); + + /* reset buffers */ + mp->packet_size = 0; + if (mp->ibuf) + stream_reset(mp->ibuf); + if (mp->obuf) + stream_fifo_clean(mp->obuf); + + /* stop all peer timers */ + pim_msdp_peer_ka_timer_setup(mp, false /* start */); + pim_msdp_peer_cr_timer_setup(mp, false /* start */); + pim_msdp_peer_hold_timer_setup(mp, false /* start */); + + /* close connection */ + if (mp->fd >= 0) { + close(mp->fd); + mp->fd = -1; + } +} + +/* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */ +void +pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str) +{ + if (PIM_DEBUG_EVENTS) { + zlog_debug("MSDP peer %s tcp reset %s", mp->key_str, rc_str); + snprintf(mp->last_reset, sizeof(mp->last_reset), "%s", rc_str); + } + + /* close the connection and transition to listening or connecting */ + pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */); + if (PIM_MSDP_PEER_IS_LISTENER(mp)) { + pim_msdp_peer_listen(mp); + } else { + pim_msdp_peer_connect(mp); + } +} + +static void +pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, const char *timer_str) +{ + zlog_debug("MSDP peer %s %s timer expired", mp->key_str, timer_str); +} + +/* RFC-3618:Sec-5.4 - peer hold timer */ +static int +pim_msdp_peer_hold_timer_cb(struct thread *t) +{ + struct pim_msdp_peer *mp; + + mp = THREAD_ARG(t); + mp->hold_timer = NULL; + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_timer_expiry_log(mp, "hold"); + } + + if (mp->state != PIM_MSDP_ESTABLISHED) { + return 0; + } + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_state_chg_log(mp); + } + pim_msdp_peer_reset_tcp_conn(mp, "ht-expired"); + return 0; +} +static void +pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start) +{ + THREAD_OFF(mp->hold_timer); + if (start) { + THREAD_TIMER_ON(msdp->master, mp->hold_timer, + pim_msdp_peer_hold_timer_cb, mp, PIM_MSDP_PEER_HOLD_TIME); + } +} + + +/* RFC-3618:Sec-5.5 - peer keepalive timer */ +static int +pim_msdp_peer_ka_timer_cb(struct thread *t) +{ + struct pim_msdp_peer *mp; + + mp = THREAD_ARG(t); + mp->ka_timer = NULL; + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_timer_expiry_log(mp, "ka"); + } + + pim_msdp_pkt_ka_tx(mp); + pim_msdp_peer_ka_timer_setup(mp, true /* start */); + return 0; +} +static void +pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start) +{ + THREAD_OFF(mp->ka_timer); + if (start) { + THREAD_TIMER_ON(msdp->master, mp->ka_timer, + pim_msdp_peer_ka_timer_cb, mp, PIM_MSDP_PEER_KA_TIME); + } +} + +static void +pim_msdp_peer_active_connect(struct pim_msdp_peer *mp) +{ + int rc; + ++mp->conn_attempts; + rc = pim_msdp_sock_connect(mp); + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_peer_active_connect: %d", mp->key_str, rc); + } + + switch (rc) { + case connect_error: + case -1: + /* connect failed restart the connect-retry timer */ + pim_msdp_peer_cr_timer_setup(mp, true /* start */); + break; + + case connect_success: + /* connect was sucessful move to established */ + pim_msdp_peer_established(mp); + break; + + case connect_in_progress: + /* for NB content we need to wait till sock is readable or + * writeable */ + PIM_MSDP_PEER_WRITE_ON(mp); + PIM_MSDP_PEER_READ_ON(mp); + /* also restart connect-retry timer to reset the socket if connect is + * not sucessful */ + pim_msdp_peer_cr_timer_setup(mp, true /* start */); + break; + } +} + +/* RFC-3618:Sec-5.6 - connection retry on active peer */ +static int +pim_msdp_peer_cr_timer_cb(struct thread *t) +{ + struct pim_msdp_peer *mp; + + mp = THREAD_ARG(t); + mp->cr_timer = NULL; + + if (PIM_DEBUG_MSDP_EVENTS) { + pim_msdp_peer_timer_expiry_log(mp, "connect-retry"); + } + + if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) { + return 0; + } + + pim_msdp_peer_active_connect(mp); + return 0; +} +static void +pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start) +{ + THREAD_OFF(mp->cr_timer); + if (start) { + THREAD_TIMER_ON(msdp->master, mp->cr_timer, + pim_msdp_peer_cr_timer_cb, mp, PIM_MSDP_PEER_CONNECT_RETRY_TIME); + } +} + +/* if a valid packet is rxed from the peer we can restart hold timer */ +void +pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp) +{ + if (mp->state == PIM_MSDP_ESTABLISHED) { + pim_msdp_peer_hold_timer_setup(mp, true /* start */); + } +} + +/* if a valid packet is txed to the peer we can restart ka timer and avoid + * unnecessary ka noise in the network */ +void +pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp) +{ + if (mp->state == PIM_MSDP_ESTABLISHED) { + pim_msdp_peer_ka_timer_setup(mp, true /* start */); + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP ka timer restart on pkt tx to %s", mp->key_str); + } + } +} + +static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr) +{ + sockunion_init(su); + su->sin.sin_addr = addr; + su->sin.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + su->sin.sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ +} + +/* 11.2.A1: create a new peer and transition state to listen or connecting */ +static enum pim_msdp_err +pim_msdp_peer_new(struct in_addr peer_addr, struct in_addr local_addr, + const char *mesh_group_name, struct pim_msdp_peer **mp_p) +{ + struct pim_msdp_peer *mp; + + pim_msdp_enable(); + + mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp)); + if (!mp) { + zlog_err("%s: PIM XCALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*mp)); + return PIM_MSDP_ERR_OOM; + } + + mp->peer = peer_addr; + pim_inet4_dump("", mp->peer, mp->key_str, sizeof(mp->key_str)); + pim_msdp_addr2su(&mp->su_peer, mp->peer); + mp->local = local_addr; + /* XXX: originator_id setting needs to move to the mesh group */ + msdp->originator_id = local_addr; + pim_msdp_addr2su(&mp->su_local, mp->local); + mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name); + mp->state = PIM_MSDP_INACTIVE; + mp->fd = -1; + strcpy(mp->last_reset, "-"); + /* higher IP address is listener */ + if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) { + mp->flags |= PIM_MSDP_PEERF_LISTENER; + } + + /* setup packet buffers */ + mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE); + mp->obuf = stream_fifo_new(); + + /* insert into misc tables for easy access */ + mp = hash_get(msdp->peer_hash, mp, hash_alloc_intern); + listnode_add_sort(msdp->peer_list, mp); + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP peer %s created", mp->key_str); + + pim_msdp_peer_state_chg_log(mp); + } + + /* fireup the connect state machine */ + if (PIM_MSDP_PEER_IS_LISTENER(mp)) { + pim_msdp_peer_listen(mp); + } else { + pim_msdp_peer_connect(mp); + } + if (mp_p) { + *mp_p = mp; + } + return PIM_MSDP_ERR_NONE; +} + +struct pim_msdp_peer * +pim_msdp_peer_find(struct in_addr peer_addr) +{ + struct pim_msdp_peer lookup; + + lookup.peer = peer_addr; + return hash_lookup(msdp->peer_hash, &lookup); +} + +/* add peer configuration if it doesn't already exist */ +enum pim_msdp_err +pim_msdp_peer_add(struct in_addr peer_addr, struct in_addr local_addr, + const char *mesh_group_name, struct pim_msdp_peer **mp_p) +{ + struct pim_msdp_peer *mp; + + if (mp_p) { + *mp_p = NULL; + } + + if (peer_addr.s_addr == local_addr.s_addr) { + /* skip session setup if config is invalid */ + if (PIM_DEBUG_MSDP_EVENTS) { + char peer_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("", peer_addr, peer_str, sizeof(peer_str)); + zlog_debug("%s add skipped as DIP=SIP", peer_str); + } + return PIM_MSDP_ERR_SIP_EQ_DIP; + } + + mp = pim_msdp_peer_find(peer_addr); + if (mp) { + if (mp_p) { + *mp_p = mp; + } + return PIM_MSDP_ERR_PEER_EXISTS; + } + + return pim_msdp_peer_new(peer_addr, local_addr, mesh_group_name, mp_p); +} + +/* release all mem associated with a peer */ +static void +pim_msdp_peer_free(struct pim_msdp_peer *mp) +{ + if (mp->ibuf) { + stream_free(mp->ibuf); + } + + if (mp->obuf) { + stream_fifo_free(mp->obuf); + } + + if (mp->mesh_group_name) { + XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); + } + XFREE(MTYPE_PIM_MSDP_PEER, mp); +} + +/* delete the peer config */ +static enum pim_msdp_err +pim_msdp_peer_do_del(struct pim_msdp_peer *mp) +{ + /* stop the tcp connection and shutdown all timers */ + pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */); + + /* remove the session from various tables */ + listnode_delete(msdp->peer_list, mp); + hash_release(msdp->peer_hash, mp); + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP peer %s deleted", mp->key_str); + } + + /* free up any associated memory */ + pim_msdp_peer_free(mp); + + return PIM_MSDP_ERR_NONE; +} + +enum pim_msdp_err +pim_msdp_peer_del(struct in_addr peer_addr) +{ + struct pim_msdp_peer *mp; + + mp = pim_msdp_peer_find(peer_addr); + if (!mp) { + return PIM_MSDP_ERR_NO_PEER; + } + + return pim_msdp_peer_do_del(mp); +} + +/* peer hash and peer list helpers */ +static unsigned int +pim_msdp_peer_hash_key_make(void *p) +{ + struct pim_msdp_peer *mp = p; + return (jhash_1word(mp->peer.s_addr, 0)); +} + +static int +pim_msdp_peer_hash_eq(const void *p1, const void *p2) +{ + const struct pim_msdp_peer *mp1 = p1; + const struct pim_msdp_peer *mp2 = p2; + + return (mp1->peer.s_addr == mp2->peer.s_addr); +} + +static int +pim_msdp_peer_comp(const void *p1, const void *p2) +{ + const struct pim_msdp_peer *mp1 = p1; + const struct pim_msdp_peer *mp2 = p2; + + if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr)) + return -1; + + if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr)) + return 1; + + return 0; +} + +/************************** Mesh group management **************************/ +static void +pim_msdp_mg_free(struct pim_msdp_mg *mg) +{ + /* If the mesh-group has valid member or src_ip don't delete it */ + if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) { + return; + } + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name); + } + if (mg->mesh_group_name) + XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name); + + if (mg->mbr_list) + list_free(mg->mbr_list); + + XFREE(MTYPE_PIM_MSDP_MG, mg); + msdp->mg = NULL; +} + +static struct pim_msdp_mg * +pim_msdp_mg_new(const char *mesh_group_name) +{ + struct pim_msdp_mg *mg; + + mg = XCALLOC(MTYPE_PIM_MSDP_MG, sizeof(*mg)); + if (!mg) { + zlog_err("%s: PIM XCALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*mg)); + return NULL; + } + + mg->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name); + mg->mbr_list = list_new(); + mg->mbr_list->del = (void (*)(void *))pim_msdp_mg_mbr_free; + mg->mbr_list->cmp = (int (*)(void *, void *))pim_msdp_mg_mbr_comp; + + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name); + } + return mg; +} + +enum pim_msdp_err +pim_msdp_mg_del(const char *mesh_group_name) +{ + struct pim_msdp_mg *mg = msdp->mg; + struct pim_msdp_mg_mbr *mbr; + + if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { + return PIM_MSDP_ERR_NO_MG; + } + + /* delete all the mesh-group members */ + while (!list_isempty(mg->mbr_list)) { + mbr = listnode_head(mg->mbr_list); + pim_msdp_mg_mbr_do_del(mg, mbr); + } + + /* clear src ip */ + mg->src_ip.s_addr = INADDR_ANY; + + /* free up the mesh-group */ + pim_msdp_mg_free(mg); + return PIM_MSDP_ERR_NONE; +} + +static enum pim_msdp_err +pim_msdp_mg_add(const char *mesh_group_name) +{ + if (msdp->mg) { + if (!strcmp(msdp->mg->mesh_group_name, mesh_group_name)) { + return PIM_MSDP_ERR_NONE; + } + /* currently only one mesh-group can exist at a time */ + return PIM_MSDP_ERR_MAX_MESH_GROUPS; + } + + msdp->mg = pim_msdp_mg_new(mesh_group_name); + if (!msdp->mg) { + return PIM_MSDP_ERR_OOM; + } + + return PIM_MSDP_ERR_NONE; +} + +static int +pim_msdp_mg_mbr_comp(const void *p1, const void *p2) +{ + const struct pim_msdp_mg_mbr *mbr1 = p1; + const struct pim_msdp_mg_mbr *mbr2 = p2; + + if (ntohl(mbr1->mbr_ip.s_addr) < ntohl(mbr2->mbr_ip.s_addr)) + return -1; + + if (ntohl(mbr1->mbr_ip.s_addr) > ntohl(mbr2->mbr_ip.s_addr)) + return 1; + + return 0; +} + +static void +pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr) +{ + XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr); +} + +static struct pim_msdp_mg_mbr * +pim_msdp_mg_mbr_find(struct in_addr mbr_ip) +{ + struct pim_msdp_mg_mbr *mbr; + struct listnode *mbr_node; + + if (!msdp->mg) { + return NULL; + } + /* we can move this to a hash but considering that number of peers in + * a mesh-group that seems like bit of an overkill */ + for (ALL_LIST_ELEMENTS_RO(msdp->mg->mbr_list, mbr_node, mbr)) { + if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) { + return mbr; + } + } + return mbr; +} + +enum pim_msdp_err +pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip) +{ + int rc; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; + + rc = pim_msdp_mg_add(mesh_group_name); + if (rc != PIM_MSDP_ERR_NONE) { + return rc; + } + + mg = msdp->mg; + mbr = pim_msdp_mg_mbr_find(mbr_ip); + if (mbr) { + return PIM_MSDP_ERR_MG_MBR_EXISTS; + } + + mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr)); + if (!mbr) { + zlog_err("%s: PIM XCALLOC(%zu) failure", + __PRETTY_FUNCTION__, sizeof(*mbr)); + /* if there are no references to the mg free it */ + pim_msdp_mg_free(mg); + return PIM_MSDP_ERR_OOM; + } + mbr->mbr_ip = mbr_ip; + listnode_add_sort(mg->mbr_list, mbr); + + /* if valid SIP has been configured add peer session */ + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_msdp_peer_add(mbr_ip, mg->src_ip, mesh_group_name, + &mbr->mp); + } + + if (PIM_DEBUG_MSDP_EVENTS) { + char ip_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", mbr->mbr_ip, ip_str, sizeof(ip_str)); + zlog_debug("MSDP mesh-group %s mbr %s created", mg->mesh_group_name, ip_str); + } + ++mg->mbr_cnt; + return PIM_MSDP_ERR_NONE; +} + +static void +pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr) +{ + /* Delete active peer session if any */ + if (mbr->mp) { + pim_msdp_peer_do_del(mbr->mp); + } + + listnode_delete(mg->mbr_list, mbr); + if (PIM_DEBUG_MSDP_EVENTS) { + char ip_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", mbr->mbr_ip, ip_str, sizeof(ip_str)); + zlog_debug("MSDP mesh-group %s mbr %s deleted", mg->mesh_group_name, ip_str); + } + pim_msdp_mg_mbr_free(mbr); + if (mg->mbr_cnt) { + --mg->mbr_cnt; + } +} + +enum pim_msdp_err +pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip) +{ + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg = msdp->mg; + + if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { + return PIM_MSDP_ERR_NO_MG; + } + + mbr = pim_msdp_mg_mbr_find(mbr_ip); + if (!mbr) { + return PIM_MSDP_ERR_NO_MG_MBR; + } + + pim_msdp_mg_mbr_do_del(mg, mbr); + /* if there are no references to the mg free it */ + pim_msdp_mg_free(mg); + + return PIM_MSDP_ERR_NONE; +} + +static void +pim_msdp_mg_src_do_del(void) +{ + struct pim_msdp_mg_mbr *mbr; + struct listnode *mbr_node; + struct pim_msdp_mg *mg = msdp->mg; + + /* SIP is being removed - tear down all active peer sessions */ + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { + if (mbr->mp) { + pim_msdp_peer_do_del(mbr->mp); + mbr->mp = NULL; + } + } + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_debug("MSDP mesh-group %s src cleared", mg->mesh_group_name); + } +} + +enum pim_msdp_err +pim_msdp_mg_src_del(const char *mesh_group_name) +{ + struct pim_msdp_mg *mg = msdp->mg; + + if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { + return PIM_MSDP_ERR_NO_MG; + } + + if (mg->src_ip.s_addr != INADDR_ANY) { + mg->src_ip.s_addr = INADDR_ANY; + pim_msdp_mg_src_do_del(); + /* if there are no references to the mg free it */ + pim_msdp_mg_free(mg); + } + return PIM_MSDP_ERR_NONE; +} + +enum pim_msdp_err +pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip) +{ + int rc; + struct pim_msdp_mg_mbr *mbr; + struct listnode *mbr_node; + struct pim_msdp_mg *mg; + + if (src_ip.s_addr == INADDR_ANY) { + pim_msdp_mg_src_del(mesh_group_name); + return PIM_MSDP_ERR_NONE; + } + + rc = pim_msdp_mg_add(mesh_group_name); + if (rc != PIM_MSDP_ERR_NONE) { + return rc; + } + + mg = msdp->mg; + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_msdp_mg_src_do_del(); + } + mg->src_ip = src_ip; + + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { + pim_msdp_peer_add(mbr->mbr_ip, mg->src_ip, mesh_group_name, + &mbr->mp); + } + + if (PIM_DEBUG_MSDP_EVENTS) { + char ip_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", mg->src_ip, ip_str, sizeof(ip_str)); + zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, ip_str); + } + return PIM_MSDP_ERR_NONE; +} + +/*********************** MSDP feature APIs *********************************/ +int +pim_msdp_config_write(struct vty *vty) +{ + struct listnode *mbrnode; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg = msdp->mg; + char mbr_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; + int count = 0; + + if (!mg) { + return count; + } + + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_inet4_dump("", mg->src_ip, src_str, sizeof(src_str)); + vty_out(vty, "ip msdp mesh-group %s source %s%s", + mg->mesh_group_name, src_str, VTY_NEWLINE); + ++count; + } + + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { + pim_inet4_dump("", mbr->mbr_ip, mbr_str, sizeof(mbr_str)); + vty_out(vty, "ip msdp mesh-group %s member %s%s", + mg->mesh_group_name, mbr_str, VTY_NEWLINE); + ++count; + } + return count; +} + +/* Enable feature including active/periodic timers etc. on the first peer + * config. Till then MSDP should just stay quiet. */ +static void +pim_msdp_enable(void) +{ + if (msdp->flags & PIM_MSDPF_ENABLE) { + /* feature is already enabled */ + return; + } + msdp->flags |= PIM_MSDPF_ENABLE; + msdp->work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE); + pim_msdp_sa_adv_timer_setup(true /* start */); + /* setup sa cache based on local sources */ + pim_msdp_sa_local_setup(); +} + +/* MSDP init */ +void +pim_msdp_init(struct thread_master *master) +{ + msdp->master = master; + + msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make, + pim_msdp_peer_hash_eq); + msdp->peer_list = list_new(); + msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free; + msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp; + + msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make, + pim_msdp_sa_hash_eq); + msdp->sa_list = list_new(); + msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free; + msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp; +} + +/* counterpart to MSDP init; XXX: unused currently */ +void +pim_msdp_exit(void) +{ + /* XXX: stop listener and delete all peer sessions */ + + if (msdp->peer_hash) { + hash_free(msdp->peer_hash); + msdp->peer_hash = NULL; + } + + if (msdp->peer_list) { + list_free(msdp->peer_list); + msdp->peer_list = NULL; + } +} diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h new file mode 100644 index 0000000000..33c1d88a45 --- /dev/null +++ b/pimd/pim_msdp.h @@ -0,0 +1,233 @@ +/* + * IP MSDP for Quagga + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef PIM_MSDP_H +#define PIM_MSDP_H + +enum pim_msdp_peer_state { + PIM_MSDP_DISABLED, + PIM_MSDP_INACTIVE, + PIM_MSDP_LISTEN, + PIM_MSDP_CONNECTING, + PIM_MSDP_ESTABLISHED +}; + +/* SA and KA TLVs are processed; rest ignored */ +enum pim_msdp_tlv { + PIM_MSDP_V4_SOURCE_ACTIVE = 1, + PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST, + PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE, + PIM_MSDP_KEEPALIVE, + PIM_MSDP_RESERVED, + PIM_MSDP_TRACEROUTE_PROGRESS, + PIM_MSDP_TRACEROUTE_REPLY, +}; + +/* MSDP error codes */ +enum pim_msdp_err { + PIM_MSDP_ERR_NONE = 0, + PIM_MSDP_ERR_OOM = -1, + PIM_MSDP_ERR_PEER_EXISTS = -2, + PIM_MSDP_ERR_MAX_MESH_GROUPS = -3, + PIM_MSDP_ERR_NO_PEER = -4, + PIM_MSDP_ERR_MG_MBR_EXISTS = -5, + PIM_MSDP_ERR_NO_MG = -6, + PIM_MSDP_ERR_NO_MG_MBR = -7, + PIM_MSDP_ERR_SIP_EQ_DIP = -8, +}; + +#define PIM_MSDP_STATE_STRLEN 16 +#define PIM_MSDP_UPTIME_STRLEN 80 +#define PIM_MSDP_TIMER_STRLEN 12 +#define PIM_MSDP_TCP_PORT 639 +#define PIM_MSDP_SOCKET_SNDBUF_SIZE 65536 + +enum pim_msdp_sa_flags { + PIM_MSDP_SAF_NONE = 0, + /* There are two cases where we can pickup an active source locally - + * 1. We are RP and got a source-register from the FHR + * 2. We are RP and FHR and learnt a new directly connected source on a + * DR interface */ + PIM_MSDP_SAF_LOCAL = (1 << 0), + /* We got this in the MSDP SA TLV from a peer (and this passed peer-RPF + * checks) */ + PIM_MSDP_SAF_PEER = (1 << 1), + PIM_MSDP_SAF_REF = (PIM_MSDP_SAF_LOCAL | PIM_MSDP_SAF_PEER), + PIM_MSDP_SAF_STALE = (1 << 2), /* local entries can get kicked out on + * misc pim events such as RP change */ + PIM_MSDP_SAF_UP_DEL_IN_PROG = (1 << 3) +}; + +struct pim_msdp_sa { + struct prefix_sg sg; + char sg_str[PIM_SG_LEN]; + struct in_addr rp; /* Last RP address associated with this SA */ + struct in_addr peer; /* last peer from who we heard this SA */ + enum pim_msdp_sa_flags flags; + + /* rfc-3618 is missing default value for SA-hold-down-Period. pulled + * this number from industry-standards */ +#define PIM_MSDP_SA_HOLD_TIME ((3*60)+30) + struct thread *sa_state_timer; // 5.6 + int64_t uptime; + + struct pim_upstream *up; +}; + +enum pim_msdp_peer_flags { + PIM_MSDP_PEERF_NONE = 0, + PIM_MSDP_PEERF_LISTENER = (1 << 0), +#define PIM_MSDP_PEER_IS_LISTENER(mp) (mp->flags & PIM_MSDP_PEERF_LISTENER) + PIM_MSDP_PEERF_SA_JUST_SENT = (1 << 1) +}; + +struct pim_msdp_peer { + /* configuration */ + struct in_addr local; + struct in_addr peer; + char *mesh_group_name; + char key_str[INET_ADDRSTRLEN]; + + /* state */ + enum pim_msdp_peer_state state; + enum pim_msdp_peer_flags flags; + + /* TCP socket info */ + union sockunion su_local; + union sockunion su_peer; + int fd; + + /* protocol timers */ +#define PIM_MSDP_PEER_HOLD_TIME 75 + struct thread *hold_timer; // 5.4 +#define PIM_MSDP_PEER_KA_TIME 60 + struct thread *ka_timer; // 5.5 +#define PIM_MSDP_PEER_CONNECT_RETRY_TIME 30 + struct thread *cr_timer; // 5.6 + + /* packet thread and buffers */ + uint32_t packet_size; + struct stream *ibuf; + struct stream_fifo *obuf; + struct thread *t_read; + struct thread *t_write; + + /* stats */ + uint32_t conn_attempts; + uint32_t est_flaps; + uint32_t sa_cnt; /* number of SAs attributed to this peer */ +#define PIM_MSDP_PEER_LAST_RESET_STR 20 + char last_reset[PIM_MSDP_PEER_LAST_RESET_STR]; + + /* packet stats */ + uint32_t ka_tx_cnt; + uint32_t sa_tx_cnt; + uint32_t ka_rx_cnt; + uint32_t sa_rx_cnt; + uint32_t unk_rx_cnt; + + /* timestamps */ + int64_t uptime; +}; + +struct pim_msdp_mg_mbr { + struct in_addr mbr_ip; + struct pim_msdp_peer *mp; +}; + +/* PIM MSDP mesh-group */ +struct pim_msdp_mg { + char *mesh_group_name; + struct in_addr src_ip; + uint32_t mbr_cnt; + struct list *mbr_list; +}; + +enum pim_msdp_flags { + PIM_MSDPF_NONE = 0, + PIM_MSDPF_ENABLE = (1 << 0), + PIM_MSDPF_LISTENER = (1 << 1) +}; + +struct pim_msdp_listener { + int fd; + union sockunion su; + struct thread *thread; +}; + +struct pim_msdp { + enum pim_msdp_flags flags; + struct thread_master *master; + struct pim_msdp_listener listener; + uint32_t rejected_accepts; + + /* MSDP peer info */ + struct hash *peer_hash; + struct list *peer_list; + + /* MSDP active-source info */ +#define PIM_MSDP_SA_ADVERTISMENT_TIME 60 + struct thread *sa_adv_timer; // 5.6 + struct hash *sa_hash; + struct list *sa_list; + uint32_t local_cnt; + + /* keep a scratch pad for building SA TLVs */ + struct stream *work_obuf; + + struct in_addr originator_id; + + /* currently only one mesh-group is supported - so just stash it here */ + struct pim_msdp_mg *mg; +}; + +#define PIM_MSDP_PEER_READ_ON(mp) THREAD_READ_ON(msdp->master, mp->t_read, pim_msdp_read, mp, mp->fd); +#define PIM_MSDP_PEER_WRITE_ON(mp) THREAD_WRITE_ON(msdp->master, mp->t_write, pim_msdp_write, mp, mp->fd); + +#define PIM_MSDP_PEER_READ_OFF(mp) THREAD_READ_OFF(mp->t_read) +#define PIM_MSDP_PEER_WRITE_OFF(mp) THREAD_WRITE_OFF(mp->t_write) + +extern struct pim_msdp *msdp; +void pim_msdp_init(struct thread_master *master); +void pim_msdp_exit(void); +enum pim_msdp_err pim_msdp_peer_add(struct in_addr peer, struct in_addr local, const char *mesh_group_name, struct pim_msdp_peer **mp_p); +enum pim_msdp_err pim_msdp_peer_del(struct in_addr peer_addr); +char *pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size); +struct pim_msdp_peer *pim_msdp_peer_find(struct in_addr peer_addr); +void pim_msdp_peer_established(struct pim_msdp_peer *mp); +void pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp); +void pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state); +void pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str); +int pim_msdp_write(struct thread *thread); +char *pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format); +int pim_msdp_config_write(struct vty *vty); +void pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp); +void pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg, struct in_addr rp); +void pim_msdp_sa_local_update(struct pim_upstream *up); +void pim_msdp_sa_local_del(struct prefix_sg *sg); +void pim_msdp_i_am_rp_changed(void); +bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp); +void pim_msdp_up_join_state_changed(struct pim_upstream *xg_up); +void pim_msdp_up_del(struct prefix_sg *sg); +enum pim_msdp_err pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip); +enum pim_msdp_err pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip); +enum pim_msdp_err pim_msdp_mg_src_del(const char *mesh_group_name); +enum pim_msdp_err pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip); +enum pim_msdp_err pim_msdp_mg_del(const char *mesh_group_name); +#endif diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c new file mode 100644 index 0000000000..fbf34cd91c --- /dev/null +++ b/pimd/pim_msdp_packet.c @@ -0,0 +1,696 @@ +/* + * IP MSDP packet helper + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include +#include + +#include "pimd.h" +#include "pim_str.h" + +#include "pim_msdp.h" +#include "pim_msdp_packet.h" +#include "pim_msdp_socket.h" + +static char * +pim_msdp_pkt_type_dump(enum pim_msdp_tlv type, char *buf, int buf_size) +{ + switch (type) { + case PIM_MSDP_V4_SOURCE_ACTIVE: + snprintf(buf, buf_size, "%s", "SA"); + break; + case PIM_MSDP_V4_SOURCE_ACTIVE_REQUEST: + snprintf(buf, buf_size, "%s", "SA_REQ"); + break; + case PIM_MSDP_V4_SOURCE_ACTIVE_RESPONSE: + snprintf(buf, buf_size, "%s", "SA_RESP"); + break; + case PIM_MSDP_KEEPALIVE: + snprintf(buf, buf_size, "%s", "KA"); + break; + case PIM_MSDP_RESERVED: + snprintf(buf, buf_size, "%s", "RSVD"); + break; + case PIM_MSDP_TRACEROUTE_PROGRESS: + snprintf(buf, buf_size, "%s", "TRACE_PROG"); + break; + case PIM_MSDP_TRACEROUTE_REPLY: + snprintf(buf, buf_size, "%s", "TRACE_REPLY"); + break; + default: + snprintf(buf, buf_size, "UNK-%d", type); + } + return buf; +} + +static void +pim_msdp_pkt_sa_dump_one(struct stream *s) +{ + struct prefix_sg sg; + + /* just throw away the three reserved bytes */ + stream_get3(s); + /* throw away the prefix length also */ + stream_getc(s); + + memset(&sg, 0, sizeof (struct prefix_sg)); + sg.grp.s_addr = stream_get_ipv4(s); + sg.src.s_addr = stream_get_ipv4(s); + + zlog_debug(" sg %s", pim_str_sg_dump(&sg)); +} + +static void +pim_msdp_pkt_sa_dump(struct stream *s) +{ + int entry_cnt; + int i; + struct in_addr rp; /* Last RP address associated with this SA */ + + entry_cnt = stream_getc(s); + rp.s_addr = stream_get_ipv4(s); + + if (PIM_DEBUG_MSDP_PACKETS) { + char rp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", rp, rp_str, sizeof(rp_str)); + zlog_debug(" entry_cnt %d rp %s", entry_cnt, rp_str); + } + + /* dump SAs */ + for (i = 0; i < entry_cnt; ++i) { + pim_msdp_pkt_sa_dump_one(s); + } +} + +static void +pim_msdp_pkt_dump(struct pim_msdp_peer *mp, int type, int len, bool rx, + struct stream *s) +{ + char type_str[PIM_MSDP_PKT_TYPE_STRLEN]; + + pim_msdp_pkt_type_dump(type, type_str, sizeof(type_str)); + + zlog_debug("MSDP peer %s pkt %s type %s len %d", + mp->key_str, rx?"rx":"tx", type_str, len); + + if (!s) { + return; + } + + switch(type) { + case PIM_MSDP_V4_SOURCE_ACTIVE: + pim_msdp_pkt_sa_dump(s); + break; + default:; + } +} + +/* Check file descriptor whether connect is established. */ +static void +pim_msdp_connect_check(struct pim_msdp_peer *mp) +{ + int status; + socklen_t slen; + int ret; + + if (mp->state != PIM_MSDP_CONNECTING) { + /* if we are here it means we are not in a connecting or established state + * for now treat this as a fatal error */ + pim_msdp_peer_reset_tcp_conn(mp, "invalid-state"); + return; + } + + PIM_MSDP_PEER_READ_OFF(mp); + PIM_MSDP_PEER_WRITE_OFF(mp); + + /* Check file descriptor. */ + slen = sizeof(status); + ret = getsockopt(mp->fd, SOL_SOCKET, SO_ERROR, (void *)&status, &slen); + + /* If getsockopt is fail, this is fatal error. */ + if (ret < 0) { + zlog_err("can't get sockopt for nonblocking connect"); + pim_msdp_peer_reset_tcp_conn(mp, "connect-failed"); + return; + } + + /* When status is 0 then TCP connection is established. */ + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_connect_check %s", mp->key_str, status?"fail":"success"); + } + if (status == 0) { + pim_msdp_peer_established(mp); + } else { + pim_msdp_peer_reset_tcp_conn(mp, "connect-failed"); + } +} + +static void +pim_msdp_pkt_delete(struct pim_msdp_peer *mp) +{ + stream_free(stream_fifo_pop(mp->obuf)); +} + +static void +pim_msdp_pkt_add(struct pim_msdp_peer *mp, struct stream *s) +{ + stream_fifo_push(mp->obuf, s); +} + +static void +pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp) +{ + if (stream_fifo_head(mp->obuf)) { + PIM_MSDP_PEER_WRITE_ON(mp); + } +} + +int +pim_msdp_write(struct thread *thread) +{ + struct pim_msdp_peer *mp; + struct stream *s; + int num; + enum pim_msdp_tlv type; + int len; + int work_cnt = 0; + int work_max_cnt = 100; + + mp = THREAD_ARG(thread); + mp->t_write = NULL; + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_write", mp->key_str); + } + if (mp->fd < 0) { + return -1; + } + + /* check if TCP connection is established */ + if (mp->state != PIM_MSDP_ESTABLISHED) { + pim_msdp_connect_check(mp); + return 0; + } + + s = stream_fifo_head(mp->obuf); + if (!s) { + pim_msdp_write_proceed_actions(mp); + return 0; + } + + sockopt_cork(mp->fd, 1); + + /* Nonblocking write until TCP output buffer is full */ + do + { + int writenum; + + /* Number of bytes to be sent */ + writenum = stream_get_endp(s) - stream_get_getp(s); + + /* Call write() system call */ + num = write(mp->fd, STREAM_PNT(s), writenum); + if (num < 0) { + /* write failed either retry needed or error */ + if (ERRNO_IO_RETRY(errno)) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_write io retry", mp->key_str); + } + break; + } + + pim_msdp_peer_reset_tcp_conn(mp, "pkt-tx-failed"); + return 0; + } + + if (num != writenum) { + /* Partial write */ + stream_forward_getp(s, num); + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_partial_write", mp->key_str); + } + break; + } + + /* Retrieve msdp packet type. */ + stream_set_getp(s,0); + type = stream_getc(s); + len = stream_getw(s); + switch (type) + { + case PIM_MSDP_KEEPALIVE: + mp->ka_tx_cnt++; + break; + case PIM_MSDP_V4_SOURCE_ACTIVE: + mp->sa_tx_cnt++; + break; + default:; + } + if (PIM_DEBUG_MSDP_PACKETS) { + pim_msdp_pkt_dump(mp, type, len, false /*rx*/, s); + } + + /* packet sent delete it. */ + pim_msdp_pkt_delete(mp); + + ++work_cnt; + /* may need to pause if we have done too much work in this + * loop */ + if (work_cnt >= work_max_cnt) { + break; + } + } while ((s = stream_fifo_head(mp->obuf)) != NULL); + pim_msdp_write_proceed_actions(mp); + + sockopt_cork(mp->fd, 0); + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp->key_str, work_cnt); + } + + return 0; +} + +static void +pim_msdp_pkt_send(struct pim_msdp_peer *mp, struct stream *s) +{ + /* Add packet to the end of list. */ + pim_msdp_pkt_add(mp, s); + + PIM_MSDP_PEER_WRITE_ON(mp); +} + +void +pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp) +{ + struct stream *s; + + if (mp->state != PIM_MSDP_ESTABLISHED) { + /* don't tx anything unless a session is established */ + return; + } + s = stream_new(PIM_MSDP_KA_TLV_MAX_SIZE); + stream_putc(s, PIM_MSDP_KEEPALIVE); + stream_putw(s, PIM_MSDP_KA_TLV_MAX_SIZE); + + pim_msdp_pkt_send(mp, s); +} + +static void +pim_msdp_pkt_sa_push_to_one_peer(struct pim_msdp_peer *mp) +{ + struct stream *s; + + if (mp->state != PIM_MSDP_ESTABLISHED) { + /* don't tx anything unless a session is established */ + return; + } + s = stream_dup(msdp->work_obuf); + if (s) { + pim_msdp_pkt_send(mp, s); + mp->flags |= PIM_MSDP_PEERF_SA_JUST_SENT; + } +} + +/* push the stream into the obuf fifo of all the peers */ +static void +pim_msdp_pkt_sa_push(struct pim_msdp_peer *mp) +{ + struct listnode *mpnode; + + if (mp) { + pim_msdp_pkt_sa_push_to_one_peer(mp); + } else { + for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_pkt_sa_push", mp->key_str); + } + pim_msdp_pkt_sa_push_to_one_peer(mp); + } + } +} + +static int +pim_msdp_pkt_sa_fill_hdr(int local_cnt) +{ + int curr_tlv_ecnt; + + stream_reset(msdp->work_obuf); + curr_tlv_ecnt = local_cnt>PIM_MSDP_SA_MAX_ENTRY_CNT?PIM_MSDP_SA_MAX_ENTRY_CNT:local_cnt; + local_cnt -= curr_tlv_ecnt; + stream_putc(msdp->work_obuf, PIM_MSDP_V4_SOURCE_ACTIVE); + stream_putw(msdp->work_obuf, PIM_MSDP_SA_ENTRY_CNT2SIZE(curr_tlv_ecnt)); + stream_putc(msdp->work_obuf, curr_tlv_ecnt); + stream_put_ipv4(msdp->work_obuf, msdp->originator_id.s_addr); + + return local_cnt; +} + +static void +pim_msdp_pkt_sa_fill_one(struct pim_msdp_sa *sa) +{ + stream_put3(msdp->work_obuf, 0 /* reserved */); + stream_putc(msdp->work_obuf, 32 /* sprefix len */); + stream_put_ipv4(msdp->work_obuf, sa->sg.grp.s_addr); + stream_put_ipv4(msdp->work_obuf, sa->sg.src.s_addr); +} + +static void +pim_msdp_pkt_sa_gen(struct pim_msdp_peer *mp) +{ + struct listnode *sanode; + struct pim_msdp_sa *sa; + int sa_count; + int local_cnt = msdp->local_cnt; + + sa_count = 0; + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug(" sa gen %d", local_cnt); + } + + local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt); + + for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) { + if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) { + /* current implementation of MSDP is for anycast i.e. full mesh. so + * no re-forwarding of SAs that we learnt from other peers */ + continue; + } + /* add sa into scratch pad */ + pim_msdp_pkt_sa_fill_one(sa); + ++sa_count; + if (sa_count >= PIM_MSDP_SA_MAX_ENTRY_CNT) { + pim_msdp_pkt_sa_push(mp); + /* reset headers */ + sa_count = 0; + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug(" sa gen for remainder %d", local_cnt); + } + local_cnt = pim_msdp_pkt_sa_fill_hdr(local_cnt); + } + } + + if (sa_count) { + pim_msdp_pkt_sa_push(mp); + } + return; +} + +static void +pim_msdp_pkt_sa_tx_done(void) +{ + struct listnode *mpnode; + struct pim_msdp_peer *mp; + + /* if SA were sent to the peers we restart ka timer and avoid + * unnecessary ka noise */ + for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) { + if (mp->flags & PIM_MSDP_PEERF_SA_JUST_SENT) { + mp->flags &= ~PIM_MSDP_PEERF_SA_JUST_SENT; + pim_msdp_peer_pkt_txed(mp); + } + } +} + +void +pim_msdp_pkt_sa_tx(void) +{ + pim_msdp_pkt_sa_gen(NULL /* mp */); + pim_msdp_pkt_sa_tx_done(); +} + +void +pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa) +{ + pim_msdp_pkt_sa_fill_hdr(1 /* cnt */); + pim_msdp_pkt_sa_fill_one(sa); + pim_msdp_pkt_sa_push(NULL); + pim_msdp_pkt_sa_tx_done(); +} + +/* when a connection is first established we push all SAs immediately */ +void +pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp) +{ + pim_msdp_pkt_sa_gen(mp); + pim_msdp_pkt_sa_tx_done(); +} + +static void +pim_msdp_pkt_rxed_with_fatal_error(struct pim_msdp_peer *mp) +{ + pim_msdp_peer_reset_tcp_conn(mp, "invalid-pkt-rx"); +} + +static void +pim_msdp_pkt_ka_rx(struct pim_msdp_peer *mp, int len) +{ + mp->ka_rx_cnt++; + if (len != PIM_MSDP_KA_TLV_MAX_SIZE) { + pim_msdp_pkt_rxed_with_fatal_error(mp); + return; + } + pim_msdp_peer_pkt_rxed(mp); +} + +static void +pim_msdp_pkt_sa_rx_one(struct pim_msdp_peer *mp, struct in_addr rp) +{ + int prefix_len; + struct prefix_sg sg; + + /* just throw away the three reserved bytes */ + stream_get3(mp->ibuf); + prefix_len = stream_getc(mp->ibuf); + + memset(&sg, 0, sizeof (struct prefix_sg)); + sg.grp.s_addr = stream_get_ipv4(mp->ibuf); + sg.src.s_addr = stream_get_ipv4(mp->ibuf); + + if (prefix_len != 32) { + /* ignore SA update if the prefix length is not 32 */ + zlog_err("rxed sa update with invalid prefix length %d", prefix_len); + return; + } + if (PIM_DEBUG_MSDP_PACKETS) { + zlog_debug(" sg %s", pim_str_sg_dump(&sg)); + } + pim_msdp_sa_ref(mp, &sg, rp); +} + +static void +pim_msdp_pkt_sa_rx(struct pim_msdp_peer *mp, int len) +{ + int entry_cnt; + int i; + struct in_addr rp; /* Last RP address associated with this SA */ + + mp->sa_rx_cnt++; + + if (len < PIM_MSDP_SA_TLV_MIN_SIZE) { + pim_msdp_pkt_rxed_with_fatal_error(mp); + return; + } + + entry_cnt = stream_getc(mp->ibuf); + /* some vendors include the actual multicast data in the tlv (at the end). + * we will ignore such data. in the future we may consider pushing it down + * the RPT */ + if (len < PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt)) { + pim_msdp_pkt_rxed_with_fatal_error(mp); + return; + } + rp.s_addr = stream_get_ipv4(mp->ibuf); + + if (PIM_DEBUG_MSDP_PACKETS) { + char rp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", rp, rp_str, sizeof(rp_str)); + zlog_debug(" entry_cnt %d rp %s", entry_cnt, rp_str); + } + + if (!pim_msdp_peer_rpf_check(mp, rp)) { + /* if peer-RPF check fails don't process the packet any further */ + if (PIM_DEBUG_MSDP_PACKETS) { + zlog_debug(" peer RPF check failed"); + } + return; + } + + pim_msdp_peer_pkt_rxed(mp); + + /* update SA cache */ + for (i = 0; i < entry_cnt; ++i) { + pim_msdp_pkt_sa_rx_one(mp, rp); + } +} + +static void +pim_msdp_pkt_rx(struct pim_msdp_peer *mp) +{ + enum pim_msdp_tlv type; + int len; + + /* re-read type and len */ + type = stream_getc_from(mp->ibuf, 0); + len = stream_getw_from(mp->ibuf, 1); + if (len < PIM_MSDP_HEADER_SIZE) { + pim_msdp_pkt_rxed_with_fatal_error(mp); + return; + } + + if (len > PIM_MSDP_SA_TLV_MAX_SIZE) { + /* if tlv size if greater than max just ignore the tlv */ + return; + } + + if (PIM_DEBUG_MSDP_PACKETS) { + pim_msdp_pkt_dump(mp, type, len, true /*rx*/, NULL /*s*/); + } + + switch(type) { + case PIM_MSDP_KEEPALIVE: + pim_msdp_pkt_ka_rx(mp, len); + break; + case PIM_MSDP_V4_SOURCE_ACTIVE: + mp->sa_rx_cnt++; + pim_msdp_pkt_sa_rx(mp, len); + break; + default: + mp->unk_rx_cnt++; + } +} + +/* pim msdp read utility function. */ +static int +pim_msdp_read_packet(struct pim_msdp_peer *mp) +{ + int nbytes; + int readsize; + int old_endp; + int new_endp; + + old_endp = stream_get_endp(mp->ibuf); + readsize = mp->packet_size - old_endp; + if (!readsize) { + return 0; + } + + /* Read packet from fd */ + nbytes = stream_read_try(mp->ibuf, mp->fd, readsize); + new_endp = stream_get_endp(mp->ibuf); + if (nbytes < 0) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes); + } + if (nbytes == -2) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_read io retry old_end: %d new_end: %d", mp->key_str, old_endp, new_endp); + } + /* transient error retry */ + return -1; + } + pim_msdp_pkt_rxed_with_fatal_error(mp); + return -1; + } + + if (!nbytes) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s read failed %d", mp->key_str, nbytes); + } + pim_msdp_peer_reset_tcp_conn(mp, "peer-down"); + return -1; + } + + /* We read partial packet. */ + if (stream_get_endp(mp->ibuf) != mp->packet_size) { + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s read partial len %d old_endp %d new_endp %d", mp->key_str, mp->packet_size, old_endp, new_endp); + } + return -1; + } + + return 0; +} + +int +pim_msdp_read(struct thread *thread) +{ + struct pim_msdp_peer *mp; + int rc; + uint32_t len; + + mp = THREAD_ARG(thread); + mp->t_read = NULL; + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s pim_msdp_read", mp->key_str); + } + + if (mp->fd < 0) { + return -1; + } + + /* check if TCP connection is established */ + if (mp->state != PIM_MSDP_ESTABLISHED) { + pim_msdp_connect_check(mp); + return 0; + } + + PIM_MSDP_PEER_READ_ON(mp); + + if (!mp->packet_size) { + mp->packet_size = PIM_MSDP_HEADER_SIZE; + } + + if (stream_get_endp(mp->ibuf) < PIM_MSDP_HEADER_SIZE) { + /* start by reading the TLV header */ + rc = pim_msdp_read_packet(mp); + if (rc < 0) { + goto pim_msdp_read_end; + } + + /* Find TLV type and len */ + stream_getc(mp->ibuf); + len = stream_getw(mp->ibuf); + if (len < PIM_MSDP_HEADER_SIZE) { + pim_msdp_pkt_rxed_with_fatal_error(mp); + goto pim_msdp_read_end; + } + /* read complete TLV */ + mp->packet_size = len; + } + + rc = pim_msdp_read_packet(mp); + if (rc < 0) { + goto pim_msdp_read_end; + } + + pim_msdp_pkt_rx(mp); + + /* reset input buffers and get ready for the next packet */ + mp->packet_size = 0; + stream_reset(mp->ibuf); + +pim_msdp_read_end: + return 0; +} diff --git a/pimd/pim_msdp_packet.h b/pimd/pim_msdp_packet.h new file mode 100644 index 0000000000..f6fcfee6bb --- /dev/null +++ b/pimd/pim_msdp_packet.h @@ -0,0 +1,72 @@ +/* + * IP MSDP packet helpers + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef PIM_MSDP_PACKET_H +#define PIM_MSDP_PACKET_H + +/* type and length of a single tlv can be consider packet header */ +#define PIM_MSDP_HEADER_SIZE 3 + +/* Keepalive TLV + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 4 | 3 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +#define PIM_MSDP_KA_TLV_MAX_SIZE PIM_MSDP_HEADER_SIZE + +/* Source-Active TLV (x=8, y=12xEntryCount) + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| 1 | x + y | Entry Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| RP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Reserved | Sprefix Len | \ ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \ +| Group Address | ) z ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ / +| Source Address | / ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +#define PIM_MSDP_SA_TLV_MAX_SIZE 9192 +#define PIM_MSDP_SA_X_SIZE 8 +#define PIM_MSDP_SA_ONE_ENTRY_SIZE 12 +#define PIM_MSDP_SA_Y_SIZE(entry_cnt) (PIM_MSDP_SA_ONE_ENTRY_SIZE * entry_cnt) +#define PIM_MSDP_SA_ENTRY_CNT2SIZE(entry_cnt) (PIM_MSDP_SA_X_SIZE +\ + PIM_MSDP_SA_Y_SIZE(entry_cnt)) +/* SA TLV has to have atleast only one entry in it so x=8 + y=12 */ +#define PIM_MSDP_SA_TLV_MIN_SIZE PIM_MSDP_SA_ENTRY_CNT2SIZE(1) +/* XXX: theoretically we can fix a max of 255 but that may result in packet + * fragmentation */ +#define PIM_MSDP_SA_MAX_ENTRY_CNT 120 + +#define PIM_MSDP_MAX_PACKET_SIZE max(PIM_MSDP_SA_TLV_MAX_SIZE, PIM_MSDP_KA_TLV_MAX_SIZE) + +#define PIM_MSDP_PKT_TYPE_STRLEN 16 + +void pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp); +int pim_msdp_read(struct thread *thread); +void pim_msdp_pkt_sa_tx(void); +void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa); +void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp); + +#endif diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c new file mode 100644 index 0000000000..bc9720f1f3 --- /dev/null +++ b/pimd/pim_msdp_socket.c @@ -0,0 +1,229 @@ +/* + * IP MSDP socket management + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include + +#include "pimd.h" + +#include "pim_msdp.h" +#include "pim_msdp_socket.h" + +extern struct zebra_privs_t pimd_privs; + +/* increase socket send buffer size */ +static void +pim_msdp_update_sock_send_buffer_size (int fd) +{ + int size = PIM_MSDP_SOCKET_SNDBUF_SIZE; + int optval; + socklen_t optlen = sizeof(optval); + + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) { + zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno)); + return; + } + + if (optval < size) { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { + zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno)); + } + } +} + +/* passive peer socket accept */ +static int +pim_msdp_sock_accept(struct thread *thread) +{ + union sockunion su; + struct pim_msdp_listener *listener = THREAD_ARG(thread); + int accept_sock; + int msdp_sock; + struct pim_msdp_peer *mp; + char buf[SU_ADDRSTRLEN]; + + sockunion_init(&su); + + /* re-register accept thread */ + accept_sock = THREAD_FD(thread); + if (accept_sock < 0) { + zlog_err ("accept_sock is negative value %d", accept_sock); + return -1; + } + listener->thread = thread_add_read(master, pim_msdp_sock_accept, + listener, accept_sock); + + /* accept client connection. */ + msdp_sock = sockunion_accept(accept_sock, &su); + if (msdp_sock < 0) { + zlog_err ("pim_msdp_sock_accept failed (%s)", safe_strerror (errno)); + return -1; + } + + /* see if have peer config for this */ + mp = pim_msdp_peer_find(su.sin.sin_addr); + if (!mp || !PIM_MSDP_PEER_IS_LISTENER(mp)) { + ++msdp->rejected_accepts; + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_err("msdp peer connection refused from %s", + sockunion2str(&su, buf, SU_ADDRSTRLEN)); + } + close(msdp_sock); + return -1; + } + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s accept success%s", mp->key_str, mp->fd>=0?"(dup)":""); + } + + /* if we have an existing connection we need to kill that one + * with this one */ + if (mp->fd >= 0) { + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_err("msdp peer new connection from %s stop old connection", + sockunion2str(&su, buf, SU_ADDRSTRLEN)); + } + pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */); + } + mp->fd = msdp_sock; + set_nonblocking(mp->fd); + pim_msdp_update_sock_send_buffer_size(mp->fd); + pim_msdp_peer_established(mp); + return 0; +} + +/* global listener for the MSDP well know TCP port */ +int +pim_msdp_sock_listen(void) +{ + int sock; + int socklen; + struct sockaddr_in sin; + int rc; + struct pim_msdp_listener *listener = &msdp->listener; + + if (msdp->flags & PIM_MSDPF_LISTENER) { + /* listener already setup */ + return 0; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + zlog_err ("socket: %s", safe_strerror (errno)); + return sock; + } + + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(PIM_MSDP_TCP_PORT); + socklen = sizeof(struct sockaddr_in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin.sin_len = socklen; +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + sockopt_reuseaddr(sock); + sockopt_reuseport(sock); + + if (pimd_privs.change(ZPRIVS_RAISE)) { + zlog_err ("pim_msdp_socket: could not raise privs, %s", + safe_strerror (errno)); + } + + /* bind to well known TCP port */ + rc = bind(sock, (struct sockaddr *)&sin, socklen); + + if (pimd_privs.change(ZPRIVS_LOWER)) { + zlog_err ("pim_msdp_socket: could not lower privs, %s", + safe_strerror (errno)); + } + + if (rc < 0) { + zlog_err ("pim_msdp_socket bind to port %d: %s", ntohs(sin.sin_port), safe_strerror (errno)); + close(sock); + return rc; + } + + rc = listen(sock, 3 /* backlog */); + if (rc < 0) { + zlog_err ("pim_msdp_socket listen: %s", safe_strerror (errno)); + close(sock); + return rc; + } + + /* add accept thread */ + listener->fd = sock; + memcpy(&listener->su, &sin, socklen); + listener->thread = thread_add_read(msdp->master, pim_msdp_sock_accept, listener, sock); + + msdp->flags |= PIM_MSDPF_LISTENER; + return 0; +} + +/* active peer socket setup */ +int +pim_msdp_sock_connect(struct pim_msdp_peer *mp) +{ + int rc; + + if (PIM_DEBUG_MSDP_INTERNAL) { + zlog_debug("MSDP peer %s attempt connect%s", mp->key_str, mp->fd<0?"":"(dup)"); + } + + /* if we have an existing connection we need to kill that one + * with this one */ + if (mp->fd >= 0) { + if (PIM_DEBUG_MSDP_EVENTS) { + zlog_err("msdp duplicate connect to %s nuke old connection", mp->key_str); + } + pim_msdp_peer_stop_tcp_conn(mp, false /* chg_state */); + } + + /* Make socket for the peer. */ + mp->fd = sockunion_socket(&mp->su_peer); + if (mp->fd < 0) { + zlog_err ("pim_msdp_socket socket failure: %s", safe_strerror (errno)); + return -1; + } + + set_nonblocking(mp->fd); + + /* Set socket send buffer size */ + pim_msdp_update_sock_send_buffer_size(mp->fd); + sockopt_reuseaddr(mp->fd); + sockopt_reuseport(mp->fd); + + /* source bind */ + rc = sockunion_bind(mp->fd, &mp->su_local, 0, &mp->su_local); + if (rc < 0) { + zlog_err ("pim_msdp_socket connect bind failure: %s", safe_strerror (errno)); + close(mp->fd); + mp->fd = -1; + return rc; + } + + /* Connect to the remote mp. */ + return (sockunion_connect(mp->fd, &mp->su_peer, htons(PIM_MSDP_TCP_PORT), 0)); +} + diff --git a/pimd/pim_msdp_socket.h b/pimd/pim_msdp_socket.h new file mode 100644 index 0000000000..bf3d12ad2c --- /dev/null +++ b/pimd/pim_msdp_socket.h @@ -0,0 +1,25 @@ +/* + * IP MSDP socket management for Quagga + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef PIM_MSDP_SOCKET_H +#define PIM_MSDP_SOCKET_H + +int pim_msdp_sock_listen(void); +int pim_msdp_sock_connect(struct pim_msdp_peer *mp); +#endif diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 9d0fc0ad8f..7ea7b1ad82 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -21,11 +21,20 @@ #include #include "if.h" +#include "log.h" +#include "prefix.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" +#include "pim_vty.h" #include "pim_pim.h" #include "pim_msg.h" #include "pim_util.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_rp.h" +#include "pim_rpf.h" void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, uint8_t pim_msg_type) @@ -86,9 +95,9 @@ uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, return buf + ENCODED_IPV4_GROUP_SIZE; } -uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, - int buf_size, - struct in_addr addr) +uint8_t * +pim_msg_addr_encode_ipv4_source(uint8_t *buf, int buf_size, + struct in_addr addr, uint8_t bits) { const int ENCODED_IPV4_SOURCE_SIZE = 8; @@ -98,9 +107,172 @@ uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ buf[1] = '\0'; /* native encoding */ - buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */ + buf[2] = bits; buf[3] = 32; /* mask len */ memcpy(buf+4, &addr, sizeof(struct in_addr)); return buf + ENCODED_IPV4_SOURCE_SIZE; } + +int +pim_msg_join_prune_encode (uint8_t *buf, int buf_size, int is_join, + struct pim_upstream *up, + struct in_addr upstream, int holdtime) +{ + uint8_t *pim_msg = buf; + uint8_t *pim_msg_curr = buf + PIM_MSG_HEADER_LEN; + uint8_t *end = buf + buf_size; + uint16_t *prunes = NULL; + uint16_t *joins = NULL; + struct in_addr stosend; + uint8_t bits; + int remain; + + remain = end - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast (pim_msg_curr, buf_size - PIM_MSG_HEADER_LEN, upstream); + if (!pim_msg_curr) { + char dst_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", upstream, dst_str, sizeof(dst_str)); + zlog_warn("%s: failure encoding destination address %s: space left=%d", + __PRETTY_FUNCTION__, dst_str, remain); + return -3; + } + + remain = end - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: group will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -4; + } + + *pim_msg_curr = 0; /* reserved */ + ++pim_msg_curr; + *pim_msg_curr = 1; /* number of groups */ + ++pim_msg_curr; + + *((uint16_t *) pim_msg_curr) = htons(holdtime); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = end - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group (pim_msg_curr, remain, + up->sg.grp); + if (!pim_msg_curr) { + char group_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", up->sg.grp, group_str, sizeof(group_str)); + zlog_warn("%s: failure encoding group address %s: space left=%d", + __PRETTY_FUNCTION__, group_str, remain); + return -5; + } + + remain = end - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: sources will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -6; + } + + /* number of joined sources */ + joins = (uint16_t *)pim_msg_curr; + *joins = htons(is_join ? 1 : 0); + ++pim_msg_curr; + ++pim_msg_curr; + + /* number of pruned sources */ + prunes = (uint16_t *)pim_msg_curr; + *prunes = htons(is_join ? 0 : 1); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = end - pim_msg_curr; + if (up->sg.src.s_addr == INADDR_ANY) + { + struct pim_rpf *rpf = pim_rp_g (up->sg.grp); + bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT | PIM_ENCODE_RPT_BIT; + stosend = rpf->rpf_addr.u.prefix4; + } + else + { + bits = PIM_ENCODE_SPARSE_BIT; + stosend = up->sg.src; + } + pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain, stosend, bits); + if (!pim_msg_curr) { + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", up->sg.src, source_str, sizeof(source_str)); + zlog_warn("%s: failure encoding source address %s: space left=%d", + __PRETTY_FUNCTION__, source_str, remain); + return -7; + } + remain = pim_msg_curr - pim_msg; + + /* + * This is not implemented correctly at this point in time + * Make it stop. + */ +#if 0 + if (up->sg.src.s_addr == INADDR_ANY) + { + struct pim_upstream *child; + struct listnode *up_node; + int send_prune = 0; + + zlog_debug ("%s: Considering (%s) children for (S,G,rpt) prune", + __PRETTY_FUNCTION__, up->sg_str); + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) + { + if (!pim_rpf_is_same(&up->rpf, &child->rpf)) + { + send_prune = 1; + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: SPT Bit and RPF'(%s) != RPF'(S,G): Add Prune (%s,rpt) to compound message", + __PRETTY_FUNCTION__, up->sg_str, child->sg_str); + } + else + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", + __PRETTY_FUNCTION__, up->sg_str, child->sg_str); + } + else if (pim_upstream_is_sg_rpt (child)) + { + if (pim_upstream_empty_inherited_olist (child)) + { + send_prune = 1; + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", + __PRETTY_FUNCTION__, child->sg_str); + } + else if (!pim_rpf_is_same (&up->rpf, &child->rpf)) + { + send_prune = 1; + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message", + __PRETTY_FUNCTION__, up->sg_str, child->sg_str); + } + else + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message", + __PRETTY_FUNCTION__, up->sg_str, child->sg_str); + } + else + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("%s: SPT bit is not set for (%s)", + __PRETTY_FUNCTION__, child->sg_str); + if (send_prune) + { + pim_msg_curr = pim_msg_addr_encode_ipv4_source (pim_msg_curr, remain, + child->sg.src, + PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_RPT_BIT); + remain = pim_msg_curr - pim_msg; + *prunes = htons(ntohs(*prunes) + 1); + send_prune = 0; + } + } + } +#endif + pim_msg_build_header (pim_msg, remain, PIM_MSG_TYPE_JOIN_PRUNE); + + return remain; +} diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index 96a89659e6..5229f85c72 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -43,8 +43,17 @@ uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, int buf_size, struct in_addr addr); + +#define PIM_ENCODE_SPARSE_BIT 0x04 +#define PIM_ENCODE_WC_BIT 0x02 +#define PIM_ENCODE_RPT_BIT 0x01 uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, int buf_size, - struct in_addr addr); + struct in_addr addr, + uint8_t bits); + +int pim_msg_join_prune_encode (uint8_t *buf, int buf_size, int is_join, + struct pim_upstream *up, + struct in_addr upstream, int holdtime); #endif /* PIM_MSG_H */ diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 04792eb35e..346b911157 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -24,6 +24,8 @@ #include "prefix.h" #include "memory.h" #include "if.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_neighbor.h" @@ -33,6 +35,8 @@ #include "pim_pim.h" #include "pim_upstream.h" #include "pim_ifchannel.h" +#include "pim_rp.h" +#include "pim_zebra.h" static void dr_election_by_addr(struct interface *ifp) { @@ -125,8 +129,8 @@ int pim_if_dr_election(struct interface *ifp) if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { if (PIM_DEBUG_PIM_EVENTS) { - char dr_old_str[100]; - char dr_new_str[100]; + char dr_old_str[INET_ADDRSTRLEN]; + char dr_new_str[INET_ADDRSTRLEN]; pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); zlog_debug("%s: DR was %s now is %s on interface %s", @@ -207,20 +211,18 @@ static int on_neighbor_timer(struct thread *t) struct interface *ifp; char msg[100]; - zassert(t); neigh = THREAD_ARG(t); - zassert(neigh); ifp = neigh->interface; if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s", neigh->holdtime, src_str, ifp->name); } - neigh->t_expire_timer = 0; + neigh->t_expire_timer = NULL; snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime); pim_neighbor_delete(ifp, neigh, msg); @@ -237,26 +239,11 @@ static int on_neighbor_timer(struct thread *t) return 0; } -static void neighbor_timer_off(struct pim_neighbor *neigh) -{ - if (PIM_DEBUG_PIM_TRACE_DETAIL) { - if (neigh->t_expire_timer) { - char src_str[100]; - pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); - zlog_debug("%s: cancelling timer for neighbor %s on %s", - __PRETTY_FUNCTION__, - src_str, neigh->interface->name); - } - } - THREAD_OFF(neigh->t_expire_timer); - zassert(!neigh->t_expire_timer); -} - void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) { neigh->holdtime = holdtime; - neighbor_timer_off(neigh); + THREAD_OFF(neigh->t_expire_timer); /* 0xFFFF is request for no holdtime @@ -266,7 +253,7 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) } if (PIM_DEBUG_PIM_TRACE_DETAIL) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); zlog_debug("%s: starting %u sec timer for neighbor %s on %s", __PRETTY_FUNCTION__, @@ -290,15 +277,15 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, { struct pim_interface *pim_ifp; struct pim_neighbor *neigh; - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; zassert(ifp); pim_ifp = ifp->info; zassert(pim_ifp); - neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); + neigh = XCALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); if (!neigh) { - zlog_err("%s: PIM XMALLOC(%zu) failure", + zlog_err("%s: PIM XCALLOC(%zu) failure", __PRETTY_FUNCTION__, sizeof(*neigh)); return 0; } @@ -311,10 +298,18 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, neigh->dr_priority = dr_priority; neigh->generation_id = generation_id; neigh->prefix_list = addr_list; - neigh->t_expire_timer = 0; + neigh->t_expire_timer = NULL; neigh->interface = ifp; pim_neighbor_timer_reset(neigh, holdtime); + /* + * The pim_ifstat_hello_sent variable is used to decide if + * we should expedite a hello out the interface. If we + * establish a new neighbor, we unfortunately need to + * reset the value so that we can know to hurry up and + * hello + */ + pim_ifp->pim_ifstat_hello_sent = 0; pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); @@ -391,7 +386,8 @@ struct pim_neighbor *pim_neighbor_find(struct interface *ifp, struct pim_neighbor *neigh; pim_ifp = ifp->info; - zassert(pim_ifp); + if (!pim_ifp) + return NULL; for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { if (source_addr.s_addr == neigh->source_addr.s_addr) { @@ -399,7 +395,35 @@ struct pim_neighbor *pim_neighbor_find(struct interface *ifp, } } - return 0; + return NULL; +} + +/* + * Find the *one* interface out + * this interface. If more than + * one return NULL + */ +struct pim_neighbor * +pim_neighbor_find_if (struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp || pim_ifp->pim_neighbor_list->count != 1) + return NULL; + + return listnode_head (pim_ifp->pim_neighbor_list); +} + +/* rpf info associated with an upstream entry needs to be re-evaluated + * when an RPF neighbor comes or goes */ +static void +pim_neighbor_rpf_update(void) +{ + /* XXX: for the time being piggyback on the timer used on rib changes + * to scan and update the rpf nexthop. This is expensive processing + * and we should be able to optimize neighbor changes differently than + * nexthop changes. */ + sched_rpf_cache_refresh(); } struct pim_neighbor *pim_neighbor_add(struct interface *ifp, @@ -410,7 +434,8 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp, uint16_t override_interval, uint32_t dr_priority, uint32_t generation_id, - struct list *addr_list) + struct list *addr_list, + int send_hello_now) { struct pim_interface *pim_ifp; struct pim_neighbor *neigh; @@ -449,9 +474,22 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp, message with a new GenID is received from an existing neighbor, a new Hello message should be sent on this interface after a randomized delay between 0 and Triggered_Hello_Delay. - */ - pim_hello_restart_triggered(neigh->interface); + This is a bit silly to do it that way. If I get a new + genid we need to send the hello *now* because we've + lined up a bunch of join/prune messages to go out the + interface. + */ + if (send_hello_now) + pim_hello_restart_now (ifp); + else + pim_hello_restart_triggered(neigh->interface); + + pim_upstream_find_new_rpf(); + + pim_rp_setup (); + + pim_neighbor_rpf_update(); return neigh; } @@ -508,7 +546,7 @@ void pim_neighbor_delete(struct interface *ifp, const char *delete_message) { struct pim_interface *pim_ifp; - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_ifp = ifp->info; zassert(pim_ifp); @@ -517,7 +555,7 @@ void pim_neighbor_delete(struct interface *ifp, zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s", src_str, ifp->name, delete_message); - neighbor_timer_off(neigh); + THREAD_OFF(neigh->t_expire_timer); pim_if_assert_on_neighbor_down(ifp, neigh->source_addr); @@ -564,6 +602,8 @@ void pim_neighbor_delete(struct interface *ifp, listnode_delete(pim_ifp->pim_neighbor_list, neigh); pim_neighbor_free(neigh); + + pim_neighbor_rpf_update(); } void pim_neighbor_delete_all(struct interface *ifp, @@ -646,9 +686,9 @@ static void delete_from_neigh_addr(struct interface *ifp, { struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4); if (p) { - char addr_str[100]; - char this_neigh_str[100]; - char other_neigh_str[100]; + char addr_str[INET_ADDRSTRLEN]; + char this_neigh_str[INET_ADDRSTRLEN]; + char other_neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr->u.prefix4, addr_str, sizeof(addr_str)); pim_inet4_dump("", neigh_addr, this_neigh_str, sizeof(this_neigh_str)); diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index e023a7f1ef..211eda25c7 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -25,6 +25,7 @@ #include "if.h" #include "linklist.h" +#include "prefix.h" #include "pim_tlv.h" @@ -46,6 +47,12 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime); void pim_neighbor_free(struct pim_neighbor *neigh); struct pim_neighbor *pim_neighbor_find(struct interface *ifp, struct in_addr source_addr); + +struct pim_neighbor *pim_neighbor_find_if (struct interface *ifp); + + +#define PIM_NEIGHBOR_SEND_DELAY 0 +#define PIM_NEIGHBOR_SEND_NOW 1 struct pim_neighbor *pim_neighbor_add(struct interface *ifp, struct in_addr source_addr, pim_hello_options hello_options, @@ -54,7 +61,8 @@ struct pim_neighbor *pim_neighbor_add(struct interface *ifp, uint16_t override_interval, uint32_t dr_priority, uint32_t generation_id, - struct list *addr_list); + struct list *addr_list, + int send_hello_now); void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, const char *delete_message); diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index ebbc6e19f9..0cebe47355 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -24,6 +24,8 @@ #include "memory.h" #include "linklist.h" #include "if.h" +#include "hash.h" +#include "jhash.h" #include "pimd.h" #include "pim_oil.h" @@ -31,104 +33,162 @@ #include "pim_iface.h" #include "pim_time.h" +struct list *pim_channel_oil_list = NULL; +struct hash *pim_channel_oil_hash = NULL; + +static int +pim_channel_oil_compare (struct channel_oil *c1, struct channel_oil *c2) +{ + if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr)) + return -1; + + if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) > ntohl(c2->oil.mfcc_mcastgrp.s_addr)) + return 1; + + if (ntohl(c1->oil.mfcc_origin.s_addr) < ntohl(c2->oil.mfcc_origin.s_addr)) + return -1; + + if (ntohl(c1->oil.mfcc_origin.s_addr) > ntohl(c2->oil.mfcc_origin.s_addr)) + return 1; + + return 0; +} + +static int +pim_oil_equal (const void *arg1, const void *arg2) +{ + const struct channel_oil *c1 = (const struct channel_oil *)arg1; + const struct channel_oil *c2 = (const struct channel_oil *)arg2; + + if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) && + (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr)) + return 1; + + return 0; +} + +static unsigned int +pim_oil_hash_key (void *arg) +{ + struct channel_oil *oil = (struct channel_oil *)arg; + + return jhash_2words (oil->oil.mfcc_mcastgrp.s_addr, oil->oil.mfcc_origin.s_addr, 0); +} + +void +pim_oil_init (void) +{ + pim_channel_oil_hash = hash_create_size (8192, pim_oil_hash_key, + pim_oil_equal); + + pim_channel_oil_list = list_new(); + if (!pim_channel_oil_list) { + zlog_err("%s %s: failure: channel_oil_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return; + } + pim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free; + pim_channel_oil_list->cmp = (int (*)(void *, void *)) pim_channel_oil_compare; +} + +void +pim_oil_terminate (void) +{ + if (pim_channel_oil_list) + list_free(pim_channel_oil_list); + pim_channel_oil_list = NULL; + + if (pim_channel_oil_hash) + hash_free (pim_channel_oil_hash); + pim_channel_oil_hash = NULL; +} + void pim_channel_oil_free(struct channel_oil *c_oil) { XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil); } -static void pim_channel_oil_delete(struct channel_oil *c_oil) +static void +pim_del_channel_oil (struct channel_oil *c_oil) { /* notice that listnode_delete() can't be moved into pim_channel_oil_free() because the later is called by list_delete_all_node() */ - listnode_delete(qpim_channel_oil_list, c_oil); + listnode_delete(pim_channel_oil_list, c_oil); + hash_release (pim_channel_oil_hash, c_oil); pim_channel_oil_free(c_oil); } -static struct channel_oil *channel_oil_new(struct in_addr group_addr, - struct in_addr source_addr, - int input_vif_index) +static struct channel_oil * +pim_add_channel_oil (struct prefix_sg *sg, + int input_vif_index) { struct channel_oil *c_oil; - struct interface *ifp_in; + struct interface *ifp; - ifp_in = pim_if_find_by_vif_index(input_vif_index); - if (!ifp_in) { + ifp = pim_if_find_by_vif_index(input_vif_index); + if (!ifp) { /* warning only */ - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + zlog_warn("%s: (S,G)=%s could not find input interface for input_vif_index=%d", __PRETTY_FUNCTION__, - source_str, group_str, input_vif_index); + pim_str_sg_dump (sg), input_vif_index); } c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); if (!c_oil) { zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); - return 0; + return NULL; } - c_oil->oil.mfcc_mcastgrp = group_addr; - c_oil->oil.mfcc_origin = source_addr; + c_oil->oil.mfcc_mcastgrp = sg->grp; + c_oil->oil.mfcc_origin = sg->src; + c_oil = hash_get (pim_channel_oil_hash, c_oil, hash_alloc_intern); + c_oil->oil.mfcc_parent = input_vif_index; c_oil->oil_ref_count = 1; c_oil->installed = 0; - zassert(c_oil->oil_size == 0); + listnode_add_sort(pim_channel_oil_list, c_oil); return c_oil; } -static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr, - struct in_addr source_addr, - int input_vif_index) +static struct channel_oil *pim_find_channel_oil(struct prefix_sg *sg) { - struct channel_oil *c_oil; + struct channel_oil *c_oil = NULL; + struct channel_oil lookup; - c_oil = channel_oil_new(group_addr, source_addr, input_vif_index); - if (!c_oil) { - zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); - return 0; - } + lookup.oil.mfcc_mcastgrp = sg->grp; + lookup.oil.mfcc_origin = sg->src; - listnode_add(qpim_channel_oil_list, c_oil); + c_oil = hash_lookup (pim_channel_oil_hash, &lookup); return c_oil; } -static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr, - struct in_addr source_addr) -{ - struct listnode *node; - struct channel_oil *c_oil; - - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) && - (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr)) - return c_oil; - } - - return 0; -} - -struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, - struct in_addr source_addr, +struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg, int input_vif_index) { struct channel_oil *c_oil; - c_oil = pim_find_channel_oil(group_addr, source_addr); + c_oil = pim_find_channel_oil(sg); if (c_oil) { + if (c_oil->oil.mfcc_parent != input_vif_index) + { + c_oil->oil_inherited_rescan = 1; + if (PIM_DEBUG_MROUTE) + zlog_debug ("%s: Existing channel oil %s points to %d, modifying to point at %d", + __PRETTY_FUNCTION__, pim_str_sg_dump(sg), c_oil->oil.mfcc_parent, input_vif_index); + } + c_oil->oil.mfcc_parent = input_vif_index; ++c_oil->oil_ref_count; return c_oil; } - return pim_add_channel_oil(group_addr, source_addr, input_vif_index); + return pim_add_channel_oil(sg, input_vif_index); } void pim_channel_oil_del(struct channel_oil *c_oil) @@ -136,10 +196,96 @@ void pim_channel_oil_del(struct channel_oil *c_oil) --c_oil->oil_ref_count; if (c_oil->oil_ref_count < 1) { - pim_channel_oil_delete(c_oil); + pim_del_channel_oil(c_oil); } } +int +pim_channel_del_oif (struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask) +{ + struct pim_interface *pim_ifp; + + zassert (channel_oil); + zassert (oif); + + pim_ifp = oif->info; + + /* + * Don't do anything if we've been asked to remove a source + * that is not actually on it. + */ + if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) + { + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, channel_oil->oif_flags[pim_ifp->mroute_vif_index], + oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + return 0; + } + + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; + + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) + { + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + return 0; + } + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + + if (pim_mroute_add (channel_oil, __PRETTY_FUNCTION__)) { + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + } + return -1; + } + + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + + return 0; +} + + int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, uint32_t proto_mask) @@ -147,21 +293,18 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct pim_interface *pim_ifp; int old_ttl; - zassert(channel_oil); + /* + * If we've gotten here we've gone bad, but let's + * not take down pim + */ + if (!channel_oil) + { + zlog_warn ("Attempt to Add OIF for non-existent channel oil"); + return -1; + } pim_ifp = oif->info; - if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str, - proto_mask, oif->name, pim_ifp->mroute_vif_index); - } - #ifdef PIM_ENFORCE_LOOPFREE_MFC /* Prevent creating MFC entry with OIF=IIF. @@ -175,10 +318,11 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, TODO T22. */ if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { + channel_oil->oil_inherited_rescan = 1; if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", @@ -195,8 +339,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", @@ -218,8 +362,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", @@ -238,8 +382,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, if (old_ttl > 0) { if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)", @@ -252,11 +396,11 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; - if (pim_mroute_add(channel_oil)) { + if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", @@ -274,8 +418,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", @@ -286,3 +430,24 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, return 0; } + +int +pim_channel_oil_empty (struct channel_oil *c_oil) +{ + static uint32_t zero[MAXVIFS]; + static int inited = 0; + + if (!c_oil) + return 1; + /* + * Not sure that this is necessary, but I would rather ensure + * that this works. + */ + if (!inited) + { + memset(&zero, 0, sizeof(uint32_t) * MAXVIFS); + inited = 1; + } + + return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t)); +} diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 540acce3a3..143cfb7942 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -51,6 +51,7 @@ struct channel_counts { + unsigned long long lastused; unsigned long pktcnt; unsigned long oldpktcnt; unsigned long bytecnt; @@ -69,6 +70,7 @@ struct channel_counts struct channel_oil { struct mfcctl oil; int installed; + int oil_inherited_rescan; int oil_size; int oil_ref_count; time_t oif_creation[MAXVIFS]; @@ -76,14 +78,22 @@ struct channel_oil { struct channel_counts cc; }; +extern struct list *pim_channel_oil_list; + +void pim_oil_init (void); +void pim_oil_terminate (void); + void pim_channel_oil_free(struct channel_oil *c_oil); -struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, - struct in_addr source_addr, +struct channel_oil *pim_channel_oil_add(struct prefix_sg *sg, int input_vif_index); void pim_channel_oil_del(struct channel_oil *c_oil); int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif, uint32_t proto_mask); +int pim_channel_del_oif (struct channel_oil *c_oil, + struct interface *oif, + uint32_t proto_mask); +int pim_channel_oil_empty (struct channel_oil *c_oil); #endif /* PIM_OIL_H */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 0f41a43315..1dbbd7c655 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -44,6 +44,25 @@ static int on_pim_hello_send(struct thread *t); static int pim_hello_send(struct interface *ifp, uint16_t holdtime); +static +const char *pim_pim_msgtype2str (enum pim_msg_type type) +{ + switch (type) + { + case PIM_MSG_TYPE_HELLO: return "HELLO"; + case PIM_MSG_TYPE_REGISTER: return "REGISTER"; + case PIM_MSG_TYPE_REG_STOP: return "REGSTOP"; + case PIM_MSG_TYPE_JOIN_PRUNE: return "JOINPRUNE"; + case PIM_MSG_TYPE_BOOTSTRAP: return "BOOT"; + case PIM_MSG_TYPE_ASSERT: return "ASSERT"; + case PIM_MSG_TYPE_GRAFT: return "GRAFT"; + case PIM_MSG_TYPE_GRAFT_ACK: return "GACK"; + case PIM_MSG_TYPE_CANDIDATE: return "CANDIDATE"; + } + + return "UNKNOWN"; +} + static void sock_close(struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; @@ -70,7 +89,10 @@ static void sock_close(struct interface *ifp) pim_ifp->pim_sock_fd, ifp->name); } - if (close(pim_ifp->pim_sock_fd)) { + /* + * If the fd is already deleted no need to do anything here + */ + if (pim_ifp->pim_sock_fd > 0 && close(pim_ifp->pim_sock_fd)) { zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s", pim_ifp->pim_sock_fd, ifp->name, errno, safe_strerror(errno)); @@ -78,11 +100,6 @@ static void sock_close(struct interface *ifp) pim_ifp->pim_sock_fd = -1; pim_ifp->pim_sock_creation = 0; - - zassert(pim_ifp->pim_sock_fd < 0); - zassert(!pim_ifp->t_pim_sock_read); - zassert(!pim_ifp->t_pim_hello_timer); - zassert(!pim_ifp->pim_sock_creation); } void pim_sock_delete(struct interface *ifp, const char *delete_message) @@ -114,67 +131,53 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) { struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ - char src_str[100]; - char dst_str[100]; + char src_str[INET_ADDRSTRLEN]; + char dst_str[INET_ADDRSTRLEN]; uint8_t *pim_msg; int pim_msg_len; uint8_t pim_version; - uint8_t pim_type; + enum pim_msg_type pim_type; uint16_t pim_checksum; /* received checksum */ uint16_t checksum; /* computed checksum */ struct pim_neighbor *neigh; - if (!ifp->info) { - zlog_warn("%s: PIM not enabled on interface %s", - __PRETTY_FUNCTION__, ifp->name); - return -1; - } - if (len < sizeof(*ip_hdr)) { - zlog_warn("PIM packet size=%zu shorter than minimum=%zu", - len, sizeof(*ip_hdr)); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("PIM packet size=%zu shorter than minimum=%zu", + len, sizeof(*ip_hdr)); return -1; } ip_hdr = (struct ip *) buf; - - pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); - pim_inet4_dump("", ip_hdr->ip_dst, dst_str, sizeof(dst_str)); - ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ - if (PIM_DEBUG_PIM_PACKETS) { - zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", - src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p); - } - if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) { - zlog_warn("IP packet protocol=%d is not PIM=%d", - ip_hdr->ip_p, PIM_IP_PROTO_PIM); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("IP packet protocol=%d is not PIM=%d", + ip_hdr->ip_p, PIM_IP_PROTO_PIM); return -1; } if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { - zlog_warn("IP packet header size=%zu shorter than minimum=%d", - ip_hlen, PIM_IP_HEADER_MIN_LEN); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("IP packet header size=%zu shorter than minimum=%d", + ip_hlen, PIM_IP_HEADER_MIN_LEN); return -1; } if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { - zlog_warn("IP packet header size=%zu greater than maximum=%d", - ip_hlen, PIM_IP_HEADER_MAX_LEN); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("IP packet header size=%zu greater than maximum=%d", + ip_hlen, PIM_IP_HEADER_MAX_LEN); return -1; } pim_msg = buf + ip_hlen; pim_msg_len = len - ip_hlen; - if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { - pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len); - } - if (pim_msg_len < PIM_PIM_MIN_LEN) { - zlog_warn("PIM message size=%d shorter than minimum=%d", - pim_msg_len, PIM_PIM_MIN_LEN); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("PIM message size=%d shorter than minimum=%d", + pim_msg_len, PIM_PIM_MIN_LEN); return -1; } @@ -182,8 +185,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) pim_type = PIM_MSG_HDR_GET_TYPE(pim_msg); if (pim_version != PIM_PROTO_VERSION) { - zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d", - ifp->name, pim_version); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("Ignoring PIM pkt from %s with unsupported version: %d", + ifp->name, pim_version); return -1; } @@ -195,71 +199,79 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) checksum = in_cksum(pim_msg, pim_msg_len); if (checksum != pim_checksum) { - zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", - ifp->name, pim_checksum, checksum); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", + ifp->name, pim_checksum, checksum); return -1; } if (PIM_DEBUG_PIM_PACKETS) { - zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x", - src_str, dst_str, ifp->name, ip_hdr->ip_ttl, - pim_version, pim_type, pim_msg_len, checksum); + pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, dst_str, sizeof(dst_str)); + zlog_debug("Recv PIM %s packet from %s to %s on %s: ttl=%d pim_version=%d pim_msg_size=%d checksum=%x", + pim_pim_msgtype2str (pim_type), src_str, dst_str, ifp->name, + ip_hdr->ip_ttl, pim_version, pim_msg_len, checksum); + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len); + } } - if (pim_type == PIM_MSG_TYPE_REG_STOP || - pim_type == PIM_MSG_TYPE_BOOTSTRAP || - pim_type == PIM_MSG_TYPE_GRAFT || - pim_type == PIM_MSG_TYPE_GRAFT_ACK || - pim_type == PIM_MSG_TYPE_CANDIDATE) + switch (pim_type) { + case PIM_MSG_TYPE_HELLO: + return pim_hello_recv (ifp, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + break; + case PIM_MSG_TYPE_REGISTER: + return pim_register_recv (ifp, + ip_hdr->ip_dst, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + break; + case PIM_MSG_TYPE_REG_STOP: + return pim_register_stop_recv (pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + break; + case PIM_MSG_TYPE_JOIN_PRUNE: + neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); + if (!neigh) { + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + return -1; + } + pim_neighbor_timer_reset(neigh, neigh->holdtime); + return pim_joinprune_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + break; + case PIM_MSG_TYPE_ASSERT: + neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); + if (!neigh) { + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + return -1; + } + pim_neighbor_timer_reset(neigh, neigh->holdtime); + return pim_assert_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + break; + default: if (PIM_DEBUG_PIM_PACKETS) { zlog_debug("Recv PIM packet type %d which is not currently understood", pim_type); } return -1; } - - if (pim_type == PIM_MSG_TYPE_HELLO) { - return pim_hello_recv(ifp, - ip_hdr->ip_src, - pim_msg + PIM_MSG_HEADER_LEN, - pim_msg_len - PIM_MSG_HEADER_LEN); - } else if (pim_type == PIM_MSG_TYPE_REGISTER) { - return pim_register_recv(ifp, - ip_hdr->ip_dst, - ip_hdr->ip_src, - pim_msg + PIM_MSG_HEADER_LEN, - pim_msg_len - PIM_MSG_HEADER_LEN); - } - - neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); - if (!neigh) { - zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", - __FILE__, __PRETTY_FUNCTION__, - pim_type, src_str, ifp->name); - return -1; - } - - switch (pim_type) { - case PIM_MSG_TYPE_JOIN_PRUNE: - return pim_joinprune_recv(ifp, neigh, - ip_hdr->ip_src, - pim_msg + PIM_MSG_HEADER_LEN, - pim_msg_len - PIM_MSG_HEADER_LEN); - break; - case PIM_MSG_TYPE_ASSERT: - return pim_assert_recv(ifp, neigh, - ip_hdr->ip_src, - pim_msg + PIM_MSG_HEADER_LEN, - pim_msg_len - PIM_MSG_HEADER_LEN); - break; - default: - zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s", - __FILE__, __PRETTY_FUNCTION__, - pim_type, src_str, ifp->name); - break; - } - return -1; } @@ -278,80 +290,73 @@ static int pim_sock_read(struct thread *t) int len; ifindex_t ifindex = -1; int result = -1; /* defaults to bad */ - - zassert(t); + static long long count = 0; + int cont = 1; ifp = THREAD_ARG(t); - zassert(ifp); - fd = THREAD_FD(t); pim_ifp = ifp->info; - zassert(pim_ifp); - zassert(fd == pim_ifp->pim_sock_fd); - - len = pim_socket_recvfromto(fd, buf, sizeof(buf), - &from, &fromlen, - &to, &tolen, - &ifindex); - if (len < 0) { - zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - goto done; - } - - if (PIM_DEBUG_PIM_PACKETS) { - char from_str[100]; - char to_str[100]; - - if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) - sprintf(from_str, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) - sprintf(to_str, ""); - - zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", - len, from_str, to_str, fd, ifindex, ifp->ifindex); - } - - if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { - pim_pkt_dump(__PRETTY_FUNCTION__, buf, len); - } + while (cont) + { + len = pim_socket_recvfromto(fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) + { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) + { + cont = 0; + break; + } + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug ("Received errno: %d %s", errno, safe_strerror (errno)); + goto done; + } #ifdef PIM_CHECK_RECV_IFINDEX_SANITY - /* ifindex sanity check */ - if (ifindex != (int) ifp->ifindex) { - char from_str[100]; - char to_str[100]; - struct interface *recv_ifp; + /* ifindex sanity check */ + if (ifindex != (int) ifp->ifindex) { + char from_str[INET_ADDRSTRLEN]; + char to_str[INET_ADDRSTRLEN]; + struct interface *recv_ifp; - if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) - sprintf(from_str, ""); - if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) - sprintf(to_str, ""); + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); - recv_ifp = if_lookup_by_index(ifindex); - if (recv_ifp) { - zassert(ifindex == (int) recv_ifp->ifindex); - } + recv_ifp = if_lookup_by_index(ifindex); + if (recv_ifp) { + zassert(ifindex == (int) recv_ifp->ifindex); + } #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH - zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", - from_str, to_str, fd, - ifindex, recv_ifp ? recv_ifp->name : "", - ifp->ifindex, ifp->name); + zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", + from_str, to_str, fd, + ifindex, recv_ifp ? recv_ifp->name : "", + ifp->ifindex, ifp->name); #endif - goto done; - } + goto done; + } #endif - int fail = pim_pim_packet(ifp, buf, len); - if (fail) { - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug("%s: pim_pim_packet() return=%d", - __PRETTY_FUNCTION__, fail); - goto done; - } + int fail = pim_pim_packet(ifp, buf, len); + if (fail) { + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("%s: pim_pim_packet() return=%d", + __PRETTY_FUNCTION__, fail); + goto done; + } + + count++; + if (count % qpim_packet_process == 0) + cont = 0; + } result = 0; /* good */ @@ -378,7 +383,7 @@ static void pim_sock_read_on(struct interface *ifp) zlog_debug("Scheduling READ event on PIM socket fd=%d", pim_ifp->pim_sock_fd); } - pim_ifp->t_pim_sock_read = 0; + pim_ifp->t_pim_sock_read = NULL; zassert(!pim_ifp->t_pim_sock_read); THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp, pim_ifp->pim_sock_fd); @@ -431,9 +436,9 @@ void pim_sock_reset(struct interface *ifp) pim_ifp->pim_sock_fd = -1; pim_ifp->pim_sock_creation = 0; - pim_ifp->t_pim_sock_read = 0; + pim_ifp->t_pim_sock_read = NULL; - pim_ifp->t_pim_hello_timer = 0; + pim_ifp->t_pim_hello_timer = NULL; pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD; pim_ifp->pim_default_holdtime = -1; /* unset: means 3.5 * pim_hello_period */ pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY; @@ -462,18 +467,91 @@ void pim_sock_reset(struct interface *ifp) pim_ifstat_reset(ifp); } -int pim_msg_send(int fd, - struct in_addr dst, - uint8_t *pim_msg, - int pim_msg_size, - const char *ifname) +static uint16_t ip_id = 0; + + +static int +pim_msg_send_frame (int fd, char *buf, size_t len, + struct sockaddr *dst, size_t salen) +{ + struct ip *ip = (struct ip *)buf; + + while (sendto (fd, buf, len, MSG_DONTWAIT, dst, salen) < 0) + { + char dst_str[INET_ADDRSTRLEN]; + + switch (errno) + { + case EMSGSIZE: + { + size_t hdrsize = sizeof (struct ip); + size_t newlen1 = ((len - hdrsize) / 2 ) & 0xFFF8; + size_t sendlen = newlen1 + hdrsize; + size_t offset = ntohs (ip->ip_off); + + ip->ip_len = htons (sendlen); + ip->ip_off = htons (offset | IP_MF); + if (pim_msg_send_frame (fd, buf, sendlen, dst, salen) == 0) + { + struct ip *ip2 = (struct ip *)(buf + newlen1); + size_t newlen2 = len - sendlen; + sendlen = newlen2 + hdrsize; + + memcpy (ip2, ip, hdrsize); + ip2->ip_len = htons (sendlen); + ip2->ip_off = htons (offset + (newlen1 >> 3)); + return pim_msg_send_frame (fd, (char *)ip2, sendlen, dst, salen); + } + } + + return -1; + break; + default: + if (PIM_DEBUG_PIM_PACKETS) + { + pim_inet4_dump ("", ip->ip_dst, dst_str, sizeof (dst_str)); + zlog_warn ("%s: sendto() failure to %s: fd=%d msg_size=%zd: errno=%d: %s", + __PRETTY_FUNCTION__, + dst_str, fd, len, + errno, safe_strerror(errno)); + } + return -1; + break; + } + } + + return 0; +} + +int +pim_msg_send(int fd, struct in_addr src, + struct in_addr dst, uint8_t *pim_msg, + int pim_msg_size, const char *ifname) { - ssize_t sent; struct sockaddr_in to; socklen_t tolen; + unsigned char buffer[10000]; + unsigned char *msg_start; + struct ip *ip; + + memset (buffer, 0, 10000); + int sendlen = sizeof (struct ip) + pim_msg_size; + + msg_start = buffer + sizeof (struct ip); + memcpy (msg_start, pim_msg, pim_msg_size); + + ip = (struct ip *)buffer; + ip->ip_id = htons (++ip_id); + ip->ip_hl = 5; + ip->ip_v = 4; + ip->ip_p = PIM_IP_PROTO_PIM; + ip->ip_src = src; + ip->ip_dst = dst; + ip->ip_ttl = MAXTTL; + ip->ip_len = htons (sendlen); if (PIM_DEBUG_PIM_PACKETS) { - char dst_str[100]; + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x", __PRETTY_FUNCTION__, @@ -490,27 +568,8 @@ int pim_msg_send(int fd, pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size); } - sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, - (struct sockaddr *)&to, tolen); - if (sent != (ssize_t) pim_msg_size) { - int e = errno; - char dst_str[100]; - pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); - if (sent < 0) { - zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s", - __PRETTY_FUNCTION__, - dst_str, ifname, fd, pim_msg_size, - e, safe_strerror(e)); - } - else { - zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd", - __PRETTY_FUNCTION__, - dst_str, ifname, fd, - pim_msg_size, sent); - } - return -1; - } - + pim_msg_send_frame (fd, (char *)buffer, sendlen, + (struct sockaddr *)&to, tolen); return 0; } @@ -525,7 +584,7 @@ static int hello_send(struct interface *ifp, pim_ifp = ifp->info; if (PIM_DEBUG_PIM_HELLO) { - char dst_str[100]; + char dst_str[INET_ADDRSTRLEN]; pim_inet4_dump("", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str)); zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", __PRETTY_FUNCTION__, @@ -560,6 +619,7 @@ static int hello_send(struct interface *ifp, PIM_MSG_TYPE_HELLO); if (pim_msg_send(pim_ifp->pim_sock_fd, + pim_ifp->primary_address, qpim_all_pim_routers_addr, pim_msg, pim_msg_size, @@ -627,16 +687,14 @@ static int on_pim_hello_send(struct thread *t) struct pim_interface *pim_ifp; struct interface *ifp; - zassert(t); ifp = THREAD_ARG(t); - zassert(ifp); pim_ifp = ifp->info; /* * Schedule next hello */ - pim_ifp->t_pim_hello_timer = 0; + pim_ifp->t_pim_hello_timer = NULL; hello_resched(ifp); /* @@ -692,7 +750,20 @@ void pim_hello_restart_triggered(struct interface *ifp) pim_ifp = ifp->info; zassert(pim_ifp); - triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay; + /* + * There exists situations where we have the a RPF out this + * interface, but we haven't formed a neighbor yet. This + * happens especially during interface flaps. While + * we would like to handle this more gracefully in other + * parts of the code. In order to get us up and running + * let's just send the hello immediate'ish + * This should be revisited when we get nexthop tracking + * in and when we have a better handle on safely + * handling the rpf information for upstreams that + * we cannot legally reach yet. + */ + triggered_hello_delay_msec = 1; + //triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay; if (pim_ifp->t_pim_hello_timer) { long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer); @@ -703,11 +774,11 @@ void pim_hello_restart_triggered(struct interface *ifp) } THREAD_OFF(pim_ifp->t_pim_hello_timer); - pim_ifp->t_pim_hello_timer = 0; + pim_ifp->t_pim_hello_timer = NULL; } - zassert(!pim_ifp->t_pim_hello_timer); - random_msec = random() % (triggered_hello_delay_msec + 1); + random_msec = triggered_hello_delay_msec; + //random_msec = random() % (triggered_hello_delay_msec + 1); if (PIM_DEBUG_PIM_HELLO) { zlog_debug("Scheduling %d msec triggered hello on interface %s", @@ -729,8 +800,9 @@ int pim_sock_add(struct interface *ifp) zassert(pim_ifp); if (pim_ifp->pim_sock_fd >= 0) { - zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s", - pim_ifp->pim_sock_fd, ifp->name); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("Can't recreate existing PIM socket fd=%d for interface %s", + pim_ifp->pim_sock_fd, ifp->name); return -1; } @@ -738,12 +810,15 @@ int pim_sock_add(struct interface *ifp) pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex); if (pim_ifp->pim_sock_fd < 0) { - zlog_warn("Could not open PIM socket on interface %s", - ifp->name); + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("Could not open PIM socket on interface %s", + ifp->name); return -2; } - pim_ifp->t_pim_sock_read = 0; + pim_socket_ip_hdr (pim_ifp->pim_sock_fd); + + pim_ifp->t_pim_sock_read = NULL; pim_ifp->pim_sock_creation = pim_time_monotonic_sec(); /* diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h index 5692a37938..00c5f9012e 100644 --- a/pimd/pim_pim.h +++ b/pimd/pim_pim.h @@ -28,8 +28,6 @@ #define PIM_PIM_BUFSIZE_READ (20000) #define PIM_PIM_BUFSIZE_WRITE (20000) -#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20) - #define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */ #define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */ #define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */ @@ -38,15 +36,17 @@ #define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */ #define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */ -#define PIM_MSG_TYPE_HELLO (0) -#define PIM_MSG_TYPE_REGISTER (1) -#define PIM_MSG_TYPE_REG_STOP (2) -#define PIM_MSG_TYPE_JOIN_PRUNE (3) -#define PIM_MSG_TYPE_BOOTSTRAP (4) -#define PIM_MSG_TYPE_ASSERT (5) -#define PIM_MSG_TYPE_GRAFT (6) -#define PIM_MSG_TYPE_GRAFT_ACK (7) -#define PIM_MSG_TYPE_CANDIDATE (8) +enum pim_msg_type { + PIM_MSG_TYPE_HELLO = 0, + PIM_MSG_TYPE_REGISTER, + PIM_MSG_TYPE_REG_STOP, + PIM_MSG_TYPE_JOIN_PRUNE, + PIM_MSG_TYPE_BOOTSTRAP, + PIM_MSG_TYPE_ASSERT, + PIM_MSG_TYPE_GRAFT, + PIM_MSG_TYPE_GRAFT_ACK, + PIM_MSG_TYPE_CANDIDATE +}; #define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg) #define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg) @@ -67,6 +67,7 @@ void pim_hello_restart_triggered(struct interface *ifp); int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len); int pim_msg_send(int fd, + struct in_addr src, struct in_addr dst, uint8_t *pim_msg, int pim_msg_size, diff --git a/pimd/pim_register.c b/pimd/pim_register.c index ce3ac1a433..490a05be37 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -24,6 +24,9 @@ #include "log.h" #include "if.h" #include "thread.h" +#include "prefix.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_mroute.h" @@ -39,65 +42,150 @@ #include "pim_oil.h" #include "pim_zebra.h" #include "pim_join.h" +#include "pim_util.h" struct thread *send_test_packet_timer = NULL; -/* - * This seems stupidly expensive. A list lookup. Why is this - * not a hash? - */ -static int -pim_check_is_my_ip_address (struct in_addr dest_addr) +void +pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg, + struct in_addr src, struct in_addr originator) { - /* - * See if we can short-cut some? - * This might not make sense if we ever leave a static RP - * type of configuration. - * Note - Premature optimization might bite our patooeys' here. - */ - if (I_am_RP(dest_addr) && (dest_addr.s_addr == qpim_rp.rpf_addr.s_addr)) - return 1; + struct pim_interface *pinfo; + unsigned char buffer[10000]; + unsigned int b1length = 0; + unsigned int length; + uint8_t *b1; + struct prefix p; - if (if_lookup_exact_address (&dest_addr, AF_INET)) - return 1; + if (PIM_DEBUG_PIM_REG) + { + zlog_debug ("Sending Register stop for %s to %s on %s", + pim_str_sg_dump (sg), inet_ntoa(originator), ifp->name); + } + + memset (buffer, 0, 10000); + b1 = (uint8_t *)buffer + PIM_MSG_REGISTER_STOP_LEN; + + length = pim_encode_addr_group (b1, AFI_IP, 0, 0, sg->grp); + b1length += length; + b1 += length; + + p.family = AF_INET; + p.u.prefix4 = sg->src; + p.prefixlen = 32; + length = pim_encode_addr_ucast (b1, &p); + b1length += length; + + pim_msg_build_header (buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, PIM_MSG_TYPE_REG_STOP); + + pinfo = (struct pim_interface *)ifp->info; + if (!pinfo) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug ("%s: No pinfo!\n", __PRETTY_FUNCTION__); + return; + } + if (pim_msg_send (pinfo->pim_sock_fd, src, originator, + buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, + ifp->name)) + { + if (PIM_DEBUG_PIM_TRACE) + { + zlog_debug ("%s: could not send PIM register stop message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + } + } +} + +int +pim_register_stop_recv (uint8_t *buf, int buf_size) +{ + struct pim_upstream *upstream = NULL; + struct prefix source; + struct prefix_sg sg; + int l; + + memset (&sg, 0, sizeof (struct prefix_sg)); + l = pim_parse_addr_group (&sg, buf, buf_size); + buf += l; + buf_size -= l; + pim_parse_addr_ucast (&source, buf, buf_size); + sg.src = source.u.prefix4; + + upstream = pim_upstream_find (&sg); + if (!upstream) + { + return 0; + } + + if (PIM_DEBUG_PIM_REG) + zlog_debug ("Received Register stop for %s", + upstream->sg_str); + + switch (upstream->join_state) + { + case PIM_UPSTREAM_NOTJOINED: + case PIM_UPSTREAM_PRUNE: + return 0; + break; + case PIM_UPSTREAM_JOINED: + upstream->join_state = PIM_UPSTREAM_PRUNE; + pim_channel_del_oif (upstream->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + pim_upstream_start_register_stop_timer (upstream, 0); + case PIM_UPSTREAM_JOIN_PENDING: + upstream->join_state = PIM_UPSTREAM_PRUNE; + pim_upstream_start_register_stop_timer (upstream, 0); + return 0; + break; + } return 0; } -static void -pim_register_stop_send (struct in_addr src) -{ - return; -} - void -pim_register_send (const struct ip *ip_hdr, struct pim_rpf *rpg) +pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up) { - unsigned char buffer[3000]; + unsigned char buffer[10000]; unsigned char *b1; struct pim_interface *pinfo; struct interface *ifp; - uint32_t plen; + + if (PIM_DEBUG_PIM_REG) + { + char rp_str[INET_ADDRSTRLEN]; + strcpy (rp_str, inet_ntoa (rpg->rpf_addr.u.prefix4)); + zlog_debug ("Sending %s %sRegister Packet to %s", + up->sg_str, null_register ? "NULL " : "", rp_str); + } ifp = rpg->source_nexthop.interface; + if (!ifp) + { + if (PIM_DEBUG_PIM_REG) + zlog_debug ("%s: No interface to transmit register on", __PRETTY_FUNCTION__); + return; + } pinfo = (struct pim_interface *)ifp->info; if (!pinfo) { - zlog_debug("%s: No pinfo!\n", __PRETTY_FUNCTION__); + if (PIM_DEBUG_PIM_REG) + zlog_debug("%s: Interface: %s not configured for pim to trasmit on!\n", __PRETTY_FUNCTION__, ifp->name); return; } - memset(buffer, 0, 3000); + memset(buffer, 0, 10000); + b1 = buffer + PIM_MSG_HEADER_LEN; + *b1 |= null_register << 6; b1 = buffer + PIM_MSG_REGISTER_LEN; - plen = ntohs(ip_hdr->ip_len); - memcpy(b1, (const unsigned char *)ip_hdr, plen); + memcpy(b1, (const unsigned char *)buf, buf_size); - pim_msg_build_header(buffer, plen + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER); + pim_msg_build_header(buffer, buf_size + PIM_MSG_REGISTER_LEN, PIM_MSG_TYPE_REGISTER); if (pim_msg_send(pinfo->pim_sock_fd, - rpg->rpf_addr, + src, + rpg->rpf_addr.u.prefix4, buffer, - plen + PIM_MSG_REGISTER_LEN, + buf_size + PIM_MSG_REGISTER_LEN, ifp->name)) { if (PIM_DEBUG_PIM_TRACE) { zlog_debug("%s: could not send PIM register message on interface %s", @@ -159,15 +247,16 @@ pim_register_recv (struct interface *ifp, { int sentRegisterStop = 0; struct ip *ip_hdr; - //size_t hlen; - struct in_addr group = { .s_addr = 0 }; - struct in_addr source = { .s_addr = 0 }; - //uint8_t *msg; + struct prefix_sg sg; uint32_t *bits; + int i_am_rp = 0; - if (!pim_check_is_my_ip_address (dest_addr)) { - if (PIM_DEBUG_PIM_PACKETS) { - char dest[100]; +#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 + ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); + + if (!pim_rp_check_is_my_ip_address (ip_hdr->ip_dst, dest_addr)) { + if (PIM_DEBUG_PIM_REG) { + char dest[INET_ADDRSTRLEN]; pim_inet4_dump ("", dest_addr, dest, sizeof(dest)); zlog_debug ("%s: Received Register message for %s that I do not own", __func__, @@ -176,7 +265,6 @@ pim_register_recv (struct interface *ifp, return 0; } -#define inherited_olist(S,G) NULL /* * Please note this is not drawn to get the correct bit/data size * @@ -203,25 +291,33 @@ pim_register_recv (struct interface *ifp, * Line above. So we need to add 4 bytes to get to the * start of the actual Encapsulated data. */ -#define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 - ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); - //hlen = (ip_hdr->ip_hl << 2) | PIM_MSG_REGISTER_LEN; - //msg = (uint8_t *)tlv_buf + hlen; - source = ip_hdr->ip_src; - group = ip_hdr->ip_dst; + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = ip_hdr->ip_src; + sg.grp = ip_hdr->ip_dst; - if (I_am_RP (group) && (dest_addr.s_addr == ((RP (group))->rpf_addr.s_addr))) { + i_am_rp = I_am_RP (sg.grp); + + if (PIM_DEBUG_PIM_REG) + { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump ("", src_addr, src_str, sizeof (src_str)); + zlog_debug ("Received Register message(%s) from %s on %s, rp: %d", + pim_str_sg_dump (&sg), src_str, ifp->name, i_am_rp); + } + + if (i_am_rp && (dest_addr.s_addr == ((RP (sg.grp))->rpf_addr.u.prefix4.s_addr))) { sentRegisterStop = 0; if (*bits & PIM_REGISTER_BORDER_BIT) { - struct in_addr pimbr = pim_br_get_pmbr (source, group); + struct in_addr pimbr = pim_br_get_pmbr (&sg); if (PIM_DEBUG_PIM_PACKETS) zlog_debug("%s: Received Register message with Border bit set", __func__); if (pimbr.s_addr == pim_br_unknown.s_addr) - pim_br_set_pmbr(source, group, src_addr); + pim_br_set_pmbr(&sg, src_addr); else if (src_addr.s_addr != pimbr.s_addr) { - pim_register_stop_send(src_addr); + pim_register_stop_send (ifp, &sg, dest_addr, src_addr); if (PIM_DEBUG_PIM_PACKETS) zlog_debug("%s: Sending register-Stop to %s and dropping mr. packet", __func__, "Sender"); @@ -230,83 +326,81 @@ pim_register_recv (struct interface *ifp, } } - struct pim_upstream *upstream = pim_upstream_find (source, group); + struct pim_upstream *upstream = pim_upstream_find (&sg); /* * If we don't have a place to send ignore the packet */ if (!upstream) - return 1; + { + upstream = pim_upstream_add (&sg, ifp, + PIM_UPSTREAM_FLAG_MASK_SRC_STREAM, + __PRETTY_FUNCTION__); + if (!upstream) + { + zlog_warn ("Failure to create upstream state"); + return 1; + } + PIM_UPSTREAM_FLAG_SET_SRC_STREAM(upstream->flags); + + upstream->upstream_register = src_addr; + pim_rp_set_upstream_addr (&upstream->upstream_addr, sg.src, sg.grp); + if (pim_nexthop_lookup (&upstream->rpf.source_nexthop, + upstream->upstream_addr, 1) != 0) + { + if (PIM_DEBUG_PIM_REG) + { + zlog_debug ("Received Register(%s), for which I have no path back", upstream->sg_str); + } + PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(upstream->flags); + pim_upstream_del (upstream, __PRETTY_FUNCTION__); + return 1; + } + upstream->sg.src = sg.src; + upstream->rpf.rpf_addr = upstream->rpf.source_nexthop.mrib_nexthop_addr; + + upstream->join_state = PIM_UPSTREAM_PRUNE; + + } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) || - ((SwitchToSptDesired(source, group)) && - (inherited_olist(source, group) == NULL))) { - pim_register_stop_send(src_addr); + ((SwitchToSptDesired(&sg)) && + pim_upstream_inherited_olist (upstream) == 0)) { + //pim_scan_individual_oil (upstream->channel_oil); + pim_register_stop_send (ifp, &sg, dest_addr, src_addr); sentRegisterStop = 1; + } else { + if (PIM_DEBUG_PIM_REG) + zlog_debug ("(%s) sptbit: %d", upstream->sg_str, upstream->sptbit); } - if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) || - (SwitchToSptDesired(source, group))) { + (SwitchToSptDesired(&sg))) { if (sentRegisterStop) { - pim_upstream_keep_alive_timer_start (upstream, PIM_RP_KEEPALIVE_PERIOD); + pim_upstream_keep_alive_timer_start (upstream, qpim_rp_keep_alive_time); } else { - pim_upstream_keep_alive_timer_start (upstream, PIM_KEEPALIVE_PERIOD); + pim_upstream_keep_alive_timer_start (upstream, qpim_keep_alive_time); } } if (!(upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) && !(*bits & PIM_REGISTER_NR_BIT)) { - pim_rp_set_upstream_addr (&upstream->upstream_addr, source); - pim_nexthop_lookup (&upstream->rpf.source_nexthop, - upstream->upstream_addr, NULL); - upstream->rpf.source_nexthop.interface = ifp; - upstream->source_addr.s_addr = source.s_addr; - upstream->rpf.rpf_addr.s_addr = source.s_addr; - upstream->channel_oil->oil.mfcc_origin = source; - pim_scan_individual_oil (upstream->channel_oil); - pim_joinprune_send(upstream->rpf.source_nexthop.interface, - upstream->rpf.source_nexthop.mrib_nexthop_addr, - upstream->source_addr, - upstream->group_addr, - 1); - //decapsulate and forward the iner packet to //inherited_olist(S,G,rpt) + // This is taken care of by the kernel for us } + pim_upstream_msdp_reg_timer_start(upstream); } else { - pim_register_stop_send(src_addr); + if (PIM_DEBUG_PIM_REG) + { + if (!i_am_rp) + zlog_debug ("Received Register packet for %s, Rejecting packet because I am not the RP configured for group", + pim_str_sg_dump (&sg)); + else + zlog_debug ("Received Register packet for %s, Rejecting packet because the dst ip address is not the actual RP", + pim_str_sg_dump (&sg)); + } + pim_register_stop_send (ifp, &sg, dest_addr, src_addr); } return 1; } - - -static int -pim_register_send_test_packet (struct thread *t) -{ - uint8_t *packet; - - packet = THREAD_ARG(t); - - *packet = 4; - - return 1; -} - -/* - * pim_register_send_test_packet - * - * Send a test packet to the RP from source, in group and pps packets per second - */ -void -pim_register_send_test_packet_start (struct in_addr source, - struct in_addr group, - uint32_t pps) -{ - uint8_t *packet = NULL; - - THREAD_TIMER_MSEC_ON(master, send_test_packet_timer, - pim_register_send_test_packet, packet, 1000/pps); - - return; -} diff --git a/pimd/pim_register.h b/pimd/pim_register.h index 039c0006e0..42a908b225 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -31,15 +31,14 @@ #define PIM_MSG_REGISTER_LEN (8) #define PIM_MSG_REGISTER_STOP_LEN (4) -void pim_register_send_test_packet_start (struct in_addr source, - struct in_addr group, - uint32_t pps); +int pim_register_stop_recv (uint8_t *buf, int buf_size); int pim_register_recv (struct interface *ifp, struct in_addr dest_addr, struct in_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size); -void pim_register_send (const struct ip *msg, struct pim_rpf *rpg); +void pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up); +void pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator); #endif diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 26d108bcaa..ba464e98c6 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -20,48 +20,597 @@ */ #include +#include "lib/json.h" #include "log.h" #include "network.h" #include "if.h" +#include "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "vty.h" +#include "vrf.h" +#include "plist.h" #include "pimd.h" +#include "pim_vty.h" #include "pim_str.h" +#include "pim_iface.h" #include "pim_rp.h" #include "pim_str.h" #include "pim_rpf.h" +#include "pim_sock.h" +#include "pim_memory.h" +#include "pim_iface.h" +#include "pim_msdp.h" -static int i_am_rp = 0; - -/* - * Checks to see if we should elect ourself the actual RP - */ -void -pim_rp_check_rp (struct in_addr old, struct in_addr new) +struct rp_info { - if (PIM_DEBUG_ZEBRA) { - char sold[100]; - char snew[100]; - char rp[100]; - pim_inet4_dump("", qpim_rp.rpf_addr, rp, sizeof(rp)); - pim_inet4_dump("", old, sold, sizeof(sold)); - pim_inet4_dump("", new, snew, sizeof(snew)); - zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew ); - } + struct prefix group; + struct pim_rpf rp; + int i_am_rp; + char *plist; +}; - if (qpim_rp.rpf_addr.s_addr == INADDR_NONE) +static struct list *qpim_rp_list = NULL; +static struct rp_info *tail = NULL; + +static void +pim_rp_info_free (struct rp_info *rp_info) +{ + XFREE (MTYPE_PIM_RP, rp_info); +} + +static int +pim_rp_list_cmp (void *v1, void *v2) +{ + struct rp_info *rp1 = (struct rp_info *)v1; + struct rp_info *rp2 = (struct rp_info *)v2; + + if (rp1 == rp2) + return 0; + + if (!rp1 && rp2) + return -1; + + if (rp1 && !rp2) + return 1; + + /* + * Sort by RP IP address + */ + if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr) + return -1; + + if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr) + return 1; + + /* + * Sort by group IP address + */ + if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr) + return -1; + + if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr) + return 1; + + if (rp1 == tail) + return 1; + + return -1; +} + +void +pim_rp_init (void) +{ + struct rp_info *rp_info; + + qpim_rp_list = list_new (); + qpim_rp_list->del = (void (*)(void *))pim_rp_info_free; + qpim_rp_list->cmp = pim_rp_list_cmp; + + rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); + + if (!rp_info) return; - if (new.s_addr == qpim_rp.rpf_addr.s_addr) + str2prefix ("224.0.0.0/4", &rp_info->group); + rp_info->group.family = AF_INET; + rp_info->rp.rpf_addr.family = AF_INET; + rp_info->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; + tail = rp_info; + + listnode_add (qpim_rp_list, rp_info); +} + +void +pim_rp_free (void) +{ + if (qpim_rp_list) + list_free (qpim_rp_list); +} + +/* + * Given an RP's prefix-list, return the RP's rp_info for that prefix-list + */ +static struct rp_info * +pim_rp_find_prefix_list (struct in_addr rp, const char *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) { - i_am_rp = 1; - return; + if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && + rp_info->plist && strcmp(rp_info->plist, plist) == 0) + { + return rp_info; + } } - if (old.s_addr == qpim_rp.rpf_addr.s_addr) + return NULL; +} + +/* + * Return true if plist is used by any rp_info + */ +static int +pim_rp_prefix_list_used (const char *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) { - i_am_rp = 0; - return; + if (rp_info->plist && strcmp(rp_info->plist, plist) == 0) + { + return 1; + } } + + return 0; +} + +/* + * Given an RP's address, return the RP's rp_info that is an exact match for 'group' + */ +static struct rp_info * +pim_rp_find_exact (struct in_addr rp, struct prefix *group) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && + prefix_same (&rp_info->group, group)) + return rp_info; + } + + return NULL; +} + +/* + * Given a group, return the rp_info for that group + */ +static struct rp_info * +pim_rp_find_match_group (struct prefix *group) +{ + struct listnode *node; + struct rp_info *rp_info; + struct prefix_list *plist; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->plist) + { + plist = prefix_list_lookup (AFI_IP, rp_info->plist); + + if (plist && prefix_list_apply (plist, group) == PREFIX_PERMIT) + return rp_info; + } + else + { + if (prefix_match (&rp_info->group, group)) + return rp_info; + } + } + + return NULL; +} + +/* + * When the user makes "ip pim rp" configuration changes or if they change the + * prefix-list(s) used by these statements we must tickle the upstream state + * for each group to make them re-lookup who their RP should be. + * + * This is a placeholder function for now. + */ +static void +pim_rp_refresh_group_to_rp_mapping() +{ + pim_msdp_i_am_rp_changed(); +} + +void +pim_rp_prefix_list_update (struct prefix_list *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + int refresh_needed = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->plist && strcmp(rp_info->plist, prefix_list_name (plist)) == 0) + { + refresh_needed = 1; + break; + } + } + + if (refresh_needed) + pim_rp_refresh_group_to_rp_mapping(); +} + +static int +pim_rp_check_interface_addrs(struct rp_info *rp_info, + struct pim_interface *pim_ifp) +{ + struct listnode *node; + struct pim_secondary_addr *sec_addr; + + if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) + return 1; + + if (!pim_ifp->sec_addr_list) { + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) { + return 1; + } + } + + return 0; +} + +static void +pim_rp_check_interfaces (struct rp_info *rp_info) +{ + struct listnode *node; + struct interface *ifp; + + rp_info->i_am_rp = 0; + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) + { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { + rp_info->i_am_rp = 1; + } + } +} + +int +pim_rp_new (const char *rp, const char *group_range, const char *plist) +{ + int result; + struct rp_info *rp_info; + struct rp_info *rp_all; + struct prefix group_all; + struct listnode *node, *nnode; + struct rp_info *tmp_rp_info; + char buffer[BUFSIZ]; + + rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); + if (!rp_info) + return PIM_MALLOC_FAIL; + + if (group_range == NULL) + result = str2prefix ("224.0.0.0/4", &rp_info->group); + else + result = str2prefix (group_range, &rp_info->group); + + if (!result) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_BAD_ADDRESS; + } + + rp_info->rp.rpf_addr.family = AF_INET; + result = inet_pton (rp_info->rp.rpf_addr.family, rp, &rp_info->rp.rpf_addr.u.prefix4); + + if (result <= 0) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_RP_BAD_ADDRESS; + } + + if (plist) + { + /* + * Return if the prefix-list is already configured for this RP + */ + if (pim_rp_find_prefix_list (rp_info->rp.rpf_addr.u.prefix4, plist)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_SUCCESS; + } + + /* + * Barf if the prefix-list is already configured for an RP + */ + if (pim_rp_prefix_list_used (plist)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_RP_PFXLIST_IN_USE; + } + + /* + * Free any existing rp_info entries for this RP + */ + for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) + { + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + if (tmp_rp_info->plist) + pim_rp_del (rp, NULL, tmp_rp_info->plist); + else + pim_rp_del (rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL); + } + } + + rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist); + } + else + { + str2prefix ("224.0.0.0/4", &group_all); + rp_all = pim_rp_find_match_group(&group_all); + + /* + * Barf if group is a non-multicast subnet + */ + if (! prefix_match (&rp_all->group, &rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_BAD_ADDRESS; + } + + /* + * Remove any prefix-list rp_info entries for this RP + */ + for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) + { + if (tmp_rp_info->plist && + rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + pim_rp_del (rp, NULL, tmp_rp_info->plist); + } + } + + /* + * Take over the 224.0.0.0/4 group if the rp is INADDR_NONE + */ + if (prefix_same (&rp_all->group, &rp_info->group) && + pim_rpf_addr_is_inaddr_none (&rp_all->rp)) + { + rp_all->rp.rpf_addr = rp_info->rp.rpf_addr; + XFREE (MTYPE_PIM_RP, rp_info); + + if (pim_nexthop_lookup (&rp_all->rp.source_nexthop, rp_all->rp.rpf_addr.u.prefix4, 1) != 0) + return PIM_RP_NO_PATH; + + pim_rp_check_interfaces (rp_all); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; + } + + /* + * Return if the group is already configured for this RP + */ + if (pim_rp_find_exact (rp_info->rp.rpf_addr.u.prefix4, &rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_SUCCESS; + } + + /* + * Barf if this group is already covered by some other RP + */ + tmp_rp_info = pim_rp_find_match_group (&rp_info->group); + + if (tmp_rp_info) + { + if (tmp_rp_info->plist) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_PFXLIST_OVERLAP; + } + else + { + /* + * If the only RP that covers this group is an RP configured for + * 224.0.0.0/4 that is fine, ignore that one. For all others + * though we must return PIM_GROUP_OVERLAP + */ + if (! prefix_same (&group_all, &tmp_rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_OVERLAP; + } + } + } + } + + listnode_add_sort (qpim_rp_list, rp_info); + + if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) + return PIM_RP_NO_PATH; + + pim_rp_check_interfaces (rp_info); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; +} + +int +pim_rp_del (const char *rp, const char *group_range, const char *plist) +{ + struct prefix group; + struct in_addr rp_addr; + struct prefix g_all; + struct rp_info *rp_info; + struct rp_info *rp_all; + int result; + + if (group_range == NULL) + result = str2prefix ("224.0.0.0/4", &group); + else + result = str2prefix (group_range, &group); + + if (!result) + return PIM_GROUP_BAD_ADDRESS; + + result = inet_pton (AF_INET, rp, &rp_addr); + if (result <= 0) + return PIM_RP_BAD_ADDRESS; + + if (plist) + rp_info = pim_rp_find_prefix_list (rp_addr, plist); + else + rp_info = pim_rp_find_exact (rp_addr, &group); + + if (!rp_info) + return PIM_RP_NOT_FOUND; + + if (rp_info->plist) + { + XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist); + rp_info->plist = NULL; + } + + str2prefix ("224.0.0.0/4", &g_all); + rp_all = pim_rp_find_match_group (&g_all); + + if (rp_all == rp_info) + { + rp_all->rp.rpf_addr.family = AF_INET; + rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; + rp_all->i_am_rp = 0; + return PIM_SUCCESS; + } + + listnode_delete (qpim_rp_list, rp_info); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; +} + +int +pim_rp_setup (void) +{ + struct listnode *node; + struct rp_info *rp_info; + int ret = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE) + continue; + + if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug ("Unable to lookup nexthop for rp specified"); + ret++; + } + } + + if (ret) + return 0; + + return 1; +} + +/* + * Checks to see if we should elect ourself the actual RP when new if + * addresses are added against an interface. + */ +void +pim_rp_check_on_if_add(struct pim_interface *pim_ifp) +{ + struct listnode *node; + struct rp_info *rp_info; + bool i_am_rp_changed = false; + + if (qpim_rp_list == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) { + if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + continue; + + /* if i_am_rp is already set nothing to be done (adding new addresses + * is not going to make a difference). */ + if (rp_info->i_am_rp) { + continue; + } + + if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { + i_am_rp_changed = true; + rp_info->i_am_rp = 1; + if (PIM_DEBUG_ZEBRA) { + char rp[PREFIX_STRLEN]; + pim_addr_dump("", &rp_info->rp.rpf_addr, rp, sizeof(rp)); + zlog_debug("%s: %s: i am rp", __func__, rp); + } + } + } + + if (i_am_rp_changed) { + pim_msdp_i_am_rp_changed(); + } +} + +/* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses + * are removed. Removing numbers is an uncommon event in an active network + * so I have made no attempt to optimize it. */ +void +pim_i_am_rp_re_evaluate(void) +{ + struct listnode *node; + struct rp_info *rp_info; + bool i_am_rp_changed = false; + int old_i_am_rp; + + if (qpim_rp_list == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) { + if (pim_rpf_addr_is_inaddr_none(&rp_info->rp)) + continue; + + old_i_am_rp = rp_info->i_am_rp; + pim_rp_check_interfaces(rp_info); + + if (old_i_am_rp != rp_info->i_am_rp) { + i_am_rp_changed = true; + if (PIM_DEBUG_ZEBRA) { + char rp[PREFIX_STRLEN]; + pim_addr_dump("", &rp_info->rp.rpf_addr, rp, sizeof(rp)); + if (rp_info->i_am_rp) { + zlog_debug("%s: %s: i am rp", __func__, rp); + } else { + zlog_debug("%s: %s: i am no longer rp", __func__, rp); + } + } + } + } + + if (i_am_rp_changed) { + pim_msdp_i_am_rp_changed(); + } } /* @@ -73,7 +622,20 @@ pim_rp_check_rp (struct in_addr old, struct in_addr new) int pim_rp_i_am_rp (struct in_addr group) { - return i_am_rp; + struct prefix g; + struct rp_info *rp_info; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if (rp_info) + return rp_info->i_am_rp; + + return 0; } /* @@ -84,11 +646,24 @@ pim_rp_i_am_rp (struct in_addr group) struct pim_rpf * pim_rp_g (struct in_addr group) { - /* - * For staticly configured RP, it is always the qpim_rp - */ - pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL); - return(&qpim_rp); + struct prefix g; + struct rp_info *rp_info; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if (rp_info) + { + pim_nexthop_lookup(&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1); + return (&rp_info->rp); + } + + // About to Go Down + return NULL; } /* @@ -100,16 +675,168 @@ pim_rp_g (struct in_addr group) * */ int -pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source) +pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group) { - if ((qpim_rp.rpf_addr.s_addr == INADDR_NONE) && (source.s_addr == INADDR_ANY)) + struct rp_info *rp_info; + struct prefix g; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if ((pim_rpf_addr_is_inaddr_none (&rp_info->rp)) && (source.s_addr == INADDR_ANY)) { if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__); return 0; } - *up = (source.s_addr == INADDR_ANY) ? qpim_rp.rpf_addr : source; + *up = (source.s_addr == INADDR_ANY) ? rp_info->rp.rpf_addr.u.prefix4 : source; return 1; } + +int +pim_rp_config_write (struct vty *vty) +{ + struct listnode *node; + struct rp_info *rp_info; + char rp_buffer[32]; + char group_buffer[32]; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + continue; + + if (rp_info->plist) + vty_out(vty, "ip pim rp %s prefix-list %s%s", + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), + rp_info->plist, VTY_NEWLINE); + else + vty_out(vty, "ip pim rp %s %s%s", + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), + prefix2str(&rp_info->group, group_buffer, 32), VTY_NEWLINE); + count++; + } + + return count; +} + +int +pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr) +{ + struct rp_info *rp_info; + struct prefix g; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + /* + * See if we can short-cut some? + * This might not make sense if we ever leave a static RP + * type of configuration. + * Note - Premature optimization might bite our patooeys' here. + */ + if (I_am_RP(group)) + { + if (dest_addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) + return 1; + } + + if (if_lookup_exact_address (&dest_addr, AF_INET)) + return 1; + + return 0; +} + +void +pim_rp_show_information (struct vty *vty, u_char uj) +{ + struct rp_info *rp_info; + struct rp_info *prev_rp_info = NULL; + struct listnode *node; + + json_object *json = NULL; + json_object *json_rp_rows = NULL; + json_object *json_row = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out (vty, "RP address group/prefix-list OIF I am RP%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (!pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + { + char buf[48]; + + if (uj) + { + /* + * If we have moved on to a new RP then add the entry for the previous RP + */ + if (prev_rp_info && + prev_rp_info->rp.rpf_addr.u.prefix4.s_addr != rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); + json_rp_rows = NULL; + } + + if (!json_rp_rows) + json_rp_rows = json_object_new_array(); + + json_row = json_object_new_object(); + if (rp_info->rp.source_nexthop.interface) + json_object_string_add(json_row, "outboundInterface", rp_info->rp.source_nexthop.interface->name); + + if (rp_info->i_am_rp) + json_object_boolean_true_add(json_row, "iAmRP"); + + if (rp_info->plist) + json_object_string_add(json_row, "prefixList", rp_info->plist); + else + json_object_string_add(json_row, "group", prefix2str(&rp_info->group, buf, 48)); + + json_object_array_add(json_rp_rows, json_row); + } + else + { + vty_out (vty, "%-15s ", inet_ntoa (rp_info->rp.rpf_addr.u.prefix4)); + + if (rp_info->plist) + vty_out (vty, "%-18s ", rp_info->plist); + else + vty_out (vty, "%-18s ", prefix2str(&rp_info->group, buf, 48)); + + if (rp_info->rp.source_nexthop.interface) + vty_out (vty, "%-10s ", rp_info->rp.source_nexthop.interface->name); + else + vty_out (vty, "%-10s ", "(Unknown)"); + + if (rp_info->i_am_rp) + vty_out (vty, "yes%s", VTY_NEWLINE); + else + vty_out (vty, "no%s", VTY_NEWLINE); + } + + prev_rp_info = rp_info; + } + } + + if (uj) { + if (prev_rp_info && json_rp_rows) + json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); + + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index bb785e7efc..b32228ed49 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -21,11 +21,29 @@ #ifndef PIM_RP_H #define PIM_RP_H -void pim_rp_check_rp (struct in_addr old, struct in_addr new); +void pim_rp_init (void); +void pim_rp_free (void); + +int pim_rp_new (const char *rp, const char *group, const char *plist); +int pim_rp_del (const char *rp, const char *group, const char *plist); +void pim_rp_prefix_list_update (struct prefix_list *plist); + +int pim_rp_config_write (struct vty *vty); + +int pim_rp_setup (void); + int pim_rp_i_am_rp (struct in_addr group); -int pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source); +void pim_rp_check_on_if_add(struct pim_interface *pim_ifp); +void pim_i_am_rp_re_evaluate(void); + +int pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr); + +int pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group); + struct pim_rpf *pim_rp_g (struct in_addr group); #define I_am_RP(G) pim_rp_i_am_rp ((G)) #define RP(G) pim_rp_g ((G)) + +void pim_rp_show_information (struct vty *vty, u_char uj); #endif diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 8d6e7b70c1..ae00e347b5 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -33,108 +33,160 @@ #include "pim_iface.h" #include "pim_zlookup.h" #include "pim_ifchannel.h" +#include "pim_time.h" + +static long long last_route_change_time = -1; +long long nexthop_lookups_avoided = 0; static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); -int pim_nexthop_lookup(struct pim_nexthop *nexthop, - struct in_addr addr, struct interface *incoming) +void +pim_rpf_set_refresh_time (void) { - struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + last_route_change_time = pim_time_monotonic_usec(); + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: New last route change time: %lld", + __PRETTY_FUNCTION__, last_route_change_time); +} + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed) +{ + struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM]; int num_ifindex; - struct interface *ifp; - int first_ifindex; + struct interface *ifp = NULL; + ifindex_t first_ifindex = 0; + int found = 0; + int i = 0; - memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * PIM_NEXTHOP_IFINDEX_TAB_SIZE); - - if (!incoming) + if ((nexthop->last_lookup.s_addr == addr.s_addr) && + (nexthop->last_lookup_time > last_route_change_time)) { - num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, - PIM_NEXTHOP_IFINDEX_TAB_SIZE, - addr, PIM_NEXTHOP_LOOKUP_MAX); - if (num_ifindex < 1) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: could not find nexthop ifindex for address %s", - __FILE__, __PRETTY_FUNCTION__, - addr_str); - return -1; - } - - first_ifindex = nexthop_tab[0].ifindex; - - if (num_ifindex > 1) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", - __FILE__, __PRETTY_FUNCTION__, - num_ifindex, addr_str, first_ifindex); - /* debug warning only, do not return */ - } - - ifp = if_lookup_by_index(first_ifindex); - if (!ifp) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: could not find interface for ifindex %d (address %s)", - __FILE__, __PRETTY_FUNCTION__, - first_ifindex, addr_str); - return -2; - } + if (PIM_DEBUG_TRACE) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug ("%s: Using last lookup for %s at %lld, %lld", + __PRETTY_FUNCTION__, + addr_str, + nexthop->last_lookup_time, + last_route_change_time); + } + nexthop_lookups_avoided++; + return 0; } else { - ifp = incoming; - first_ifindex = ifp->ifindex; + if (PIM_DEBUG_TRACE) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug ("%s: Looking up: %s, last lookup time: %lld, %lld", + __PRETTY_FUNCTION__, + addr_str, + nexthop->last_lookup_time, + last_route_change_time); + } } - if (!ifp->info) { - char addr_str[100]; + memset (nexthop_tab, 0, sizeof (struct pim_zlookup_nexthop) * MULTIPATH_NUM); + num_ifindex = zclient_lookup_nexthop(nexthop_tab, + MULTIPATH_NUM, + addr, PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", - __PRETTY_FUNCTION__, - ifp->name, first_ifindex, addr_str); - /* debug warning only, do not return */ + zlog_warn("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + return -1; } - if (PIM_DEBUG_PIM_TRACE) { - char nexthop_str[100]; - char addr_str[100]; - pim_inet4_dump("", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", - __FILE__, __PRETTY_FUNCTION__, - nexthop_str, addr_str, - ifp->name, first_ifindex, - nexthop_tab[0].route_metric, - nexthop_tab[0].protocol_distance); - } + while (!found && (i < num_ifindex)) + { + first_ifindex = nexthop_tab[i].ifindex; - /* update nextop data */ - nexthop->interface = ifp; - nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr; - nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance; - nexthop->mrib_route_metric = nexthop_tab[0].route_metric; + ifp = if_lookup_by_index(first_ifindex); + if (!ifp) + { + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: could not find interface for ifindex %d (address %s)", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, addr_str); + } + i++; + continue; + } - return 0; + if (!ifp->info) + { + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", + __PRETTY_FUNCTION__, + ifp->name, first_ifindex, addr_str); + } + i++; + } + else if (neighbor_needed && !pim_if_connected_to_source (ifp, addr)) + { + struct pim_neighbor *nbr; + + nbr = pim_neighbor_find (ifp, nexthop_tab[i].nexthop_addr.u.prefix4); + if (PIM_DEBUG_PIM_TRACE_DETAIL) + zlog_debug ("ifp name: %s, pim nbr: %p", ifp->name, nbr); + if (!nbr && !if_is_loopback (ifp)) + i++; + else + found = 1; + } + else + found = 1; + } + + if (found) + { + if (PIM_DEBUG_ZEBRA) { + char nexthop_str[PREFIX_STRLEN]; + char addr_str[INET_ADDRSTRLEN]; + pim_addr_dump("", &nexthop_tab[i].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", + __FILE__, __PRETTY_FUNCTION__, + nexthop_str, addr_str, + ifp->name, first_ifindex, + nexthop_tab[i].route_metric, + nexthop_tab[i].protocol_distance); + } + /* update nextop data */ + nexthop->interface = ifp; + nexthop->mrib_nexthop_addr = nexthop_tab[i].nexthop_addr; + nexthop->mrib_metric_preference = nexthop_tab[i].protocol_distance; + nexthop->mrib_route_metric = nexthop_tab[i].route_metric; + nexthop->last_lookup = addr; + nexthop->last_lookup_time = pim_time_monotonic_usec(); + return 0; + } + else + return -1; } static int nexthop_mismatch(const struct pim_nexthop *nh1, const struct pim_nexthop *nh2) { - return (nh1->interface != nh2->interface) - || - (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr) - || - (nh1->mrib_metric_preference != nh2->mrib_metric_preference) - || + return (nh1->interface != nh2->interface) || + (nh1->mrib_nexthop_addr.u.prefix4.s_addr != nh2->mrib_nexthop_addr.u.prefix4.s_addr) || + (nh1->mrib_metric_preference != nh2->mrib_metric_preference) || (nh1->mrib_route_metric != nh2->mrib_route_metric); } -enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, - struct in_addr *old_rpf_addr, - struct interface *incoming) +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, struct in_addr *old_rpf_addr) { - struct in_addr save_rpf_addr; + struct prefix save_rpf_addr; struct pim_nexthop save_nexthop; struct pim_rpf *rpf = &up->rpf; @@ -142,36 +194,31 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */ if (pim_nexthop_lookup(&rpf->source_nexthop, - up->upstream_addr, incoming)) { + up->upstream_addr, + !PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) && + !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up->flags))) { return PIM_RPF_FAILURE; } - rpf->rpf_addr = pim_rpf_find_rpf_addr(up); - if (PIM_INADDR_IS_ANY(rpf->rpf_addr) && PIM_DEBUG_PIM_EVENTS) { + rpf->rpf_addr.family = AF_INET; + rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up); + if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) { /* RPF'(S,G) not found */ - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s %s: RPF'(%s,%s) not found: won't send join upstream", + zlog_debug("%s %s: RPF'%s not found: won't send join upstream", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str); + up->sg_str); /* warning only */ } /* detect change in pim_nexthop */ if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) { - if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - char nhaddr_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); - zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", + if (PIM_DEBUG_ZEBRA) { + char nhaddr_str[PREFIX_STRLEN]; + pim_addr_dump("", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); + zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, + up->sg_str, rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", nhaddr_str, rpf->source_nexthop.mrib_metric_preference, @@ -186,14 +233,10 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, /* detect change in RPF_interface(S) */ if (save_nexthop.interface != rpf->source_nexthop.interface) { - if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, + up->sg_str, save_nexthop.interface ? save_nexthop.interface->name : "", rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); /* warning only */ @@ -203,11 +246,11 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, } /* detect change in RPF'(S,G) */ - if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) { + if (save_rpf_addr.u.prefix4.s_addr != rpf->rpf_addr.u.prefix4.s_addr) { /* return old rpf to caller ? */ if (old_rpf_addr) - *old_rpf_addr = save_rpf_addr; + *old_rpf_addr = save_rpf_addr.u.prefix4; return PIM_RPF_CHANGED; } @@ -237,20 +280,16 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) struct in_addr rpf_addr; if (!up->rpf.source_nexthop.interface) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)", + zlog_warn("%s: missing RPF interface for upstream (S,G)=%s", __PRETTY_FUNCTION__, - src_str, grp_str); + up->sg_str); rpf_addr.s_addr = PIM_NET_INADDR_ANY; return rpf_addr; } rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface, - up->source_addr, up->group_addr); + &up->sg); if (rpf_ch) { if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { return rpf_ch->ifassert_winner; @@ -260,7 +299,7 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */ neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface, - up->rpf.source_nexthop.mrib_nexthop_addr); + up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4); if (neigh) rpf_addr = neigh->source_addr; else @@ -268,3 +307,52 @@ static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) return rpf_addr; } + +int +pim_rpf_addr_is_inaddr_none (struct pim_rpf *rpf) +{ + switch (rpf->rpf_addr.family) + { + case AF_INET: + return rpf->rpf_addr.u.prefix4.s_addr == INADDR_NONE; + break; + case AF_INET6: + zlog_warn ("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__); + return 1; + break; + default: + return 0; + break; + } + + return 0; +} + +int +pim_rpf_addr_is_inaddr_any (struct pim_rpf *rpf) +{ + switch (rpf->rpf_addr.family) + { + case AF_INET: + return rpf->rpf_addr.u.prefix4.s_addr == INADDR_ANY; + break; + case AF_INET6: + zlog_warn ("%s: v6 Unimplmented", __PRETTY_FUNCTION__); + return 1; + break; + default: + return 0; + break; + } + + return 0; +} + +int +pim_rpf_is_same (struct pim_rpf *rpf1, struct pim_rpf *rpf2) +{ + if (rpf1->source_nexthop.interface == rpf2->source_nexthop.interface) + return 1; + + return 0; +} diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index 4d55bd6881..bb77775324 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -26,10 +26,48 @@ #include "pim_upstream.h" #include "pim_neighbor.h" -int pim_nexthop_lookup(struct pim_nexthop *nexthop, - struct in_addr addr, struct interface *incoming); -enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, - struct in_addr *old_rpf_addr, - struct interface *incoming); +/* + RFC 4601: + Metric Preference + Preference value assigned to the unicast routing protocol that + provided the route to the multicast source or Rendezvous-Point. + + Metric + The unicast routing table metric associated with the route used to + reach the multicast source or Rendezvous-Point. The metric is in + units applicable to the unicast routing protocol used. +*/ +struct pim_nexthop { + struct in_addr last_lookup; + long long last_lookup_time; + struct interface *interface; /* RPF_interface(S) */ + struct prefix mrib_nexthop_addr; /* MRIB.next_hop(S) */ + uint32_t mrib_metric_preference; /* MRIB.pref(S) */ + uint32_t mrib_route_metric; /* MRIB.metric(S) */ +}; + +struct pim_rpf { + struct pim_nexthop source_nexthop; + struct prefix rpf_addr; /* RPF'(S,G) */ +}; + +enum pim_rpf_result { + PIM_RPF_OK = 0, + PIM_RPF_CHANGED, + PIM_RPF_FAILURE +}; + +struct pim_upstream; + +extern long long nexthop_lookups_avoided; + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed); +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, struct in_addr *old_rpf_addr); + +int pim_rpf_addr_is_inaddr_none (struct pim_rpf *rpf); +int pim_rpf_addr_is_inaddr_any (struct pim_rpf *rpf); + +int pim_rpf_is_same (struct pim_rpf *rpf1, struct pim_rpf *rpf2); +void pim_rpf_set_refresh_time (void); #endif /* PIM_RPF_H */ diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 54816d126b..eda81c6571 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -44,7 +44,8 @@ /* GLOBAL VARS */ extern struct zebra_privs_t pimd_privs; -int pim_socket_raw(int protocol) +int +pim_socket_raw (int protocol) { int fd; @@ -67,8 +68,58 @@ int pim_socket_raw(int protocol) return fd; } +int +pim_socket_ip_hdr (int fd) +{ + const int on = 1; + int ret; + + if (pimd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", + __PRETTY_FUNCTION__, safe_strerror (errno)); + + ret = setsockopt (fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)); + + if (pimd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", + __PRETTY_FUNCTION__, safe_strerror (errno)); + + return ret; +} + +/* + * Given a socket and a interface, + * Bind that socket to that interface + */ +int +pim_socket_bind (int fd, struct interface *ifp) +{ + int ret = 0; +#ifdef SO_BINDTODEVICE + + if (pimd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", + __PRETTY_FUNCTION__, safe_strerror (errno)); + + ret = setsockopt (fd, SOL_SOCKET, + SO_BINDTODEVICE, ifp->name, strlen (ifp->name)); + + if (pimd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", + __PRETTY_FUNCTION__, safe_strerror (errno)); + +#endif + return ret; +} + int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop) { + int rcvbuf = 1024 * 1024 * 8; +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn mreq; +#else + struct ip_mreq mreq; +#endif int fd; fd = pim_socket_raw(protocol); @@ -86,17 +137,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char lo ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT); - if (pimd_privs.change (ZPRIVS_RAISE)) - zlog_err ("%s: could not raise privs, %s", - __PRETTY_FUNCTION__, safe_strerror (errno)); - - ret = setsockopt (fd, SOL_SOCKET, - SO_BINDTODEVICE, ifp->name, strlen (ifp->name)); - - if (pimd_privs.change (ZPRIVS_LOWER)) - zlog_err ("%s: could not lower privs, %s", - __PRETTY_FUNCTION__, safe_strerror (errno)); - + ret = pim_socket_bind (fd, ifp); if (ret) { zlog_warn("Could not set fd: %d for interface: %s to device", @@ -180,14 +221,28 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char lo return PIM_SOCK_ERR_LOOP; } + memset (&mreq, 0, sizeof (mreq)); +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + mreq.imr_ifindex = ifindex; +#else + /* + * I am not sure what to do here yet for *BSD + */ + //mreq.imr_interface = ifindex; +#endif + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, - (void *) &ifaddr, sizeof(ifaddr))) { + (void *) &mreq, sizeof(mreq))) { zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_IFACE; } + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) + zlog_warn("%s: Failure to set buffer size to %d", + __PRETTY_FUNCTION__, rcvbuf); + { long flags; @@ -232,8 +287,8 @@ int pim_socket_join(int fd, struct in_addr group, ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); if (ret) { - char group_str[100]; - char ifaddr_str[100]; + char group_str[INET_ADDRSTRLEN]; + char ifaddr_str[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) sprintf(group_str, ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) @@ -245,8 +300,8 @@ int pim_socket_join(int fd, struct in_addr group, } if (PIM_DEBUG_TRACE) { - char group_str[100]; - char ifaddr_str[100]; + char group_str[INET_ADDRSTRLEN]; + char ifaddr_str[INET_ADDRSTRLEN]; if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) sprintf(group_str, ""); if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) @@ -265,15 +320,14 @@ int pim_socket_join_source(int fd, ifindex_t ifindex, const char *ifname) { if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) { - int e = errno; - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", __PRETTY_FUNCTION__, fd, group_str, source_str, ifindex, ifname, - e, safe_strerror(e)); + errno, safe_strerror(errno)); return -1; } @@ -298,17 +352,14 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, if (to) { struct sockaddr_in si; socklen_t si_len = sizeof(si); - - ((struct sockaddr_in *) to)->sin_family = AF_INET; - if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) { - ((struct sockaddr_in *) to)->sin_port = ntohs(0); - ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0); - } - else { - ((struct sockaddr_in *) to)->sin_port = si.sin_port; - ((struct sockaddr_in *) to)->sin_addr = si.sin_addr; - } + memset (&si, 0, sizeof (si)); + to->sin_family = AF_INET; + + pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len); + + to->sin_port = si.sin_port; + to->sin_addr = si.sin_addr; if (tolen) *tolen = sizeof(si); @@ -346,14 +397,6 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, if (ifindex) *ifindex = i->ipi_ifindex; - if (to && PIM_DEBUG_PACKETS) { - char to_str[100]; - pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); - zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d", - __PRETTY_FUNCTION__, - to_str, ntohs(to->sin_port)); - } - break; } #endif @@ -366,14 +409,6 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, if (tolen) *tolen = sizeof(struct sockaddr_in); - if (to && PIM_DEBUG_PACKETS) { - char to_str[100]; - pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); - zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d", - __PRETTY_FUNCTION__, - to_str, ntohs(to->sin_port)); - } - break; } #endif diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index f905c661dd..aaf82e862a 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -36,6 +36,8 @@ #define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */ #define PIM_SOCK_ERR_BIND (-11) /* Can't bind to interface */ +int pim_socket_bind (int fd, struct interface *ifp); +int pim_socket_ip_hdr (int fd); int pim_socket_raw(int protocol); int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop); int pim_socket_join(int fd, struct in_addr group, diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index ece644a861..0354083332 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -25,11 +25,10 @@ #include "memory.h" #include "sockopt.h" +#include "pimd.h" #include "pim_ssmpingd.h" #include "pim_time.h" #include "pim_sock.h" -#include "pim_str.h" -#include "pimd.h" static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234"; @@ -96,7 +95,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) sockaddr.sin_port = htons(port); if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", __PRETTY_FUNCTION__, @@ -195,12 +194,11 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss) THREAD_OFF(ss->t_sock_read); if (close(ss->sock_fd)) { - int e = errno; - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s", __PRETTY_FUNCTION__, - ss->sock_fd, source_str, e, safe_strerror(e)); + ss->sock_fd, source_str, errno, safe_strerror(errno)); /* warning only */ } @@ -219,14 +217,13 @@ static void ssmpingd_sendto(struct ssmpingd_sock *ss, sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, (struct sockaddr *)&to, tolen); if (sent != len) { - int e = errno; - char to_str[100]; + char to_str[INET_ADDRSTRLEN]; pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); if (sent < 0) { zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s", __PRETTY_FUNCTION__, to_str, ntohs(to.sin_port), ss->sock_fd, len, - e, safe_strerror(e)); + errno, safe_strerror(errno)); } else { zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d", @@ -255,7 +252,7 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) &to, &tolen, &ifindex); if (len < 0) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno)); @@ -265,9 +262,9 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) ifp = if_lookup_by_index(ifindex); if (buf[0] != PIM_SSMPINGD_REQUEST) { - char source_str[100]; - char from_str[100]; - char to_str[100]; + char source_str[INET_ADDRSTRLEN]; + char from_str[INET_ADDRSTRLEN]; + char to_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); @@ -283,9 +280,9 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) } if (PIM_DEBUG_SSMPINGD) { - char source_str[100]; - char from_str[100]; - char to_str[100]; + char source_str[INET_ADDRSTRLEN]; + char from_str[INET_ADDRSTRLEN]; + char to_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); @@ -316,10 +313,7 @@ static int ssmpingd_sock_read(struct thread *t) int sock_fd; int result; - zassert(t); - ss = THREAD_ARG(t); - zassert(ss); sock_fd = THREAD_FD(t); zassert(sock_fd == ss->sock_fd); @@ -357,18 +351,18 @@ static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64); if (sock_fd < 0) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: ssmpingd_socket() failure for source %s", __PRETTY_FUNCTION__, source_str); return 0; } - ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss)); + ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss)); if (!ss) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s", + zlog_err("%s: XCALLOC(%zu) failure for ssmpingd source %s", __PRETTY_FUNCTION__, sizeof(*ss), source_str); close(sock_fd); @@ -399,7 +393,7 @@ int pim_ssmpingd_start(struct in_addr source_addr) } { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_info("%s: starting ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); @@ -407,7 +401,7 @@ int pim_ssmpingd_start(struct in_addr source_addr) ss = ssmpingd_new(source_addr); if (!ss) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: ssmpingd_new() failure for source %s", __PRETTY_FUNCTION__, source_str); @@ -423,7 +417,7 @@ int pim_ssmpingd_stop(struct in_addr source_addr) ss = ssmpingd_find(source_addr); if (!ss) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: could not find ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); @@ -431,7 +425,7 @@ int pim_ssmpingd_stop(struct in_addr source_addr) } { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_info("%s: stopping ssmpingd for source %s", __PRETTY_FUNCTION__, source_str); diff --git a/pimd/pim_static.c b/pimd/pim_static.c index 565d6fe728..e1ccec387a 100644 --- a/pimd/pim_static.c +++ b/pimd/pim_static.c @@ -78,11 +78,11 @@ static struct static_route *static_route_new(unsigned int iif, int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source) { - struct listnode *node = 0; - struct static_route *s_route = 0; - struct static_route *original_s_route = 0; - struct pim_interface *pim_iif = iif ? iif->info : 0; - struct pim_interface *pim_oif = oif ? oif->info : 0; + struct listnode *node = NULL; + struct static_route *s_route = NULL; + struct static_route *original_s_route = NULL; + struct pim_interface *pim_iif = iif ? iif->info : NULL; + struct pim_interface *pim_oif = oif ? oif->info : NULL; ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; ifindex_t oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; @@ -110,8 +110,8 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr s_route->source.s_addr == source.s_addr) { if (s_route->iif == iif_index && s_route->oif_ttls[oif_index]) { - char gifaddr_str[100]; - char sifaddr_str[100]; + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)", @@ -174,10 +174,10 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr listnode_add(qpim_static_route_list, s_route); } - if (pim_mroute_add(&s_route->c_oil)) + if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { - char gifaddr_str[100]; - char sifaddr_str[100]; + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)", @@ -209,8 +209,8 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr } if (PIM_DEBUG_STATIC) { - char gifaddr_str[100]; - char sifaddr_str[100]; + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)", @@ -226,9 +226,9 @@ int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source) { - struct listnode *node = 0; - struct listnode *nextnode = 0; - struct static_route *s_route = 0; + struct listnode *node = NULL; + struct listnode *nextnode = NULL; + struct static_route *s_route = NULL; struct pim_interface *pim_iif = iif ? iif->info : 0; struct pim_interface *pim_oif = oif ? oif->info : 0; ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; @@ -253,23 +253,23 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */ if (s_route->c_oil.oil_ref_count <= 0 ? - pim_mroute_del(&s_route->c_oil) : pim_mroute_add(&s_route->c_oil)) { - char gifaddr_str[100]; - char sifaddr_str[100]; - pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); - pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); - zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)", + pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) : pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)", __FILE__, __PRETTY_FUNCTION__, iif_index, oif_index, gifaddr_str, sifaddr_str); - s_route->oif_ttls[oif_index] = 1; - s_route->c_oil.oil.mfcc_ttls[oif_index] = 1; - ++s_route->c_oil.oil_ref_count; + s_route->oif_ttls[oif_index] = 1; + s_route->c_oil.oil.mfcc_ttls[oif_index] = 1; + ++s_route->c_oil.oil_ref_count; - return -1; + return -1; } s_route->c_oil.oif_creation[oif_index] = 0; @@ -280,8 +280,8 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr } if (PIM_DEBUG_STATIC) { - char gifaddr_str[100]; - char sifaddr_str[100]; + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)", @@ -297,8 +297,8 @@ int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr } if (!node) { - char gifaddr_str[100]; - char sifaddr_str[100]; + char gifaddr_str[INET_ADDRSTRLEN]; + char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)", @@ -319,8 +319,8 @@ pim_static_write_mroute (struct vty *vty, struct interface *ifp) struct listnode *node; struct static_route *sroute; int count = 0; - char sbuf[100]; - char gbuf[100]; + char sbuf[INET_ADDRSTRLEN]; + char gbuf[INET_ADDRSTRLEN]; for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute)) { diff --git a/pimd/pim_str.c b/pimd/pim_str.c index c817045697..83f2a635b3 100644 --- a/pimd/pim_str.c +++ b/pimd/pim_str.c @@ -28,17 +28,62 @@ #include "pim_str.h" -void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) +void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size) { int save_errno = errno; - if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { - int e = errno; - zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", - buf_size, e, safe_strerror(e)); + if (!inet_ntop(p->family, &p->u.prefix, buf, buf_size)) { + zlog_warn("pim_addr_dump: inet_ntop(buf_size=%d): errno=%d: %s", + buf_size, errno, safe_strerror(errno)); if (onfail) snprintf(buf, buf_size, "%s", onfail); } errno = save_errno; } + +void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) +{ + int save_errno = errno; + + if (addr.s_addr == INADDR_ANY) + strcpy(buf, "*"); + else + { + if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { + zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", + buf_size, errno, safe_strerror(errno)); + if (onfail) + snprintf(buf, buf_size, "%s", onfail); + } + } + + errno = save_errno; +} + +char * +pim_str_sg_dump (const struct prefix_sg *sg) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + static char sg_str[PIM_SG_LEN]; + + pim_inet4_dump ("", sg->src, src_str, sizeof(src_str)); + pim_inet4_dump ("", sg->grp, grp_str, sizeof(grp_str)); + snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str); + + return sg_str; +} + +char * +pim_str_sg_set (const struct prefix_sg *sg, char *sg_str) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + + pim_inet4_dump ("", sg->src, src_str, sizeof(src_str)); + pim_inet4_dump ("", sg->grp, grp_str, sizeof(grp_str)); + snprintf (sg_str, PIM_SG_LEN, "(%s,%s)", src_str, grp_str); + + return sg_str; +} diff --git a/pimd/pim_str.h b/pimd/pim_str.h index d2af0110a4..97263e6a37 100644 --- a/pimd/pim_str.h +++ b/pimd/pim_str.h @@ -25,6 +25,20 @@ #include #include +#include + +/* + * Longest possible length of a (S,G) string is 36 bytes + * 123.123.123.123 = 16 * 2 + * (,) = 3 + * NULL Character at end = 1 + * (123.123.123.123,123,123,123,123) + */ +#define PIM_SG_LEN 36 + +void pim_addr_dump (const char *onfail, struct prefix *p, char *buf, int buf_size); void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); +char *pim_str_sg_dump (const struct prefix_sg *sg); +char *pim_str_sg_set (const struct prefix_sg *sg, char *sg_str); #endif diff --git a/pimd/pim_time.c b/pimd/pim_time.c index 75f767fef1..348793cd0b 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -82,6 +82,25 @@ int64_t pim_time_monotonic_dsec() return now_dsec; } +int64_t +pim_time_monotonic_usec (void) +{ + struct timeval now_tv; + int64_t now_dsec; + + if (gettime_monotonic(&now_tv)) { + zlog_err("%s: gettime_monotonic() failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, safe_strerror(errno)); + return -1; + } + + now_dsec = ((int64_t) now_tv.tv_sec) * 1000000 + ((int64_t) now_tv.tv_usec); + + return now_dsec; + +} + int pim_time_mmss(char *buf, int buf_size, long sec) { long mm; diff --git a/pimd/pim_time.h b/pimd/pim_time.h index 8ccc6a9207..de304a9f71 100644 --- a/pimd/pim_time.h +++ b/pimd/pim_time.h @@ -28,6 +28,7 @@ int64_t pim_time_monotonic_sec(void); int64_t pim_time_monotonic_dsec(void); +int64_t pim_time_monotonic_usec(void); int pim_time_mmss(char *buf, int buf_size, long sec); void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t); void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t); diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 546ceb86e1..5223f60e1b 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -95,8 +95,35 @@ uint8_t *pim_tlv_append_uint32(uint8_t *buf, #define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) -static int -pim_encode_unicast_address (uint8_t *buf, struct prefix *p) +/* + * An Encoded-Unicast address takes the following format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Addr Family | Encoding Type | Unicast Address + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+... + * + * Addr Family + * The PIM address family of the 'Unicast Address' field of this + * address. + * + * Values 0-127 are as assigned by the IANA for Internet Address * Families in [7]. Values 128-250 are reserved to be assigned by + * the IANA for PIM-specific Address Families. Values 251 though + * 255 are designated for private use. As there is no assignment + * authority for this space, collisions should be expected. + * + * Encoding Type + * The type of encoding used within a specific Address Family. The + * value '0' is reserved for this field and represents the native + * encoding of the Address Family. + * + * Unicast Address + * The unicast address as represented by the given Address Family + * and Encoding Type. + */ +int +pim_encode_addr_ucast (uint8_t *buf, struct prefix *p) { switch (p->family) { @@ -114,6 +141,79 @@ pim_encode_unicast_address (uint8_t *buf, struct prefix *p) } } +#define group_ipv4_encoding_len (4 + sizeof (struct in_addr)) + +/* + * Encoded-Group addresses take the following format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Addr Family | Encoding Type |B| Reserved |Z| Mask Len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Group multicast Address + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+... + * + * Addr Family + * Described above. + * + * Encoding Type + * Described above. + * + * [B]idirectional PIM + * Indicates the group range should use Bidirectional PIM [13]. + * For PIM-SM defined in this specification, this bit MUST be zero. + * + * Reserved + * Transmitted as zero. Ignored upon receipt. + * + * Admin Scope [Z]one + * indicates the group range is an admin scope zone. This is used + * in the Bootstrap Router Mechanism [11] only. For all other + * purposes, this bit is set to zero and ignored on receipt. + * + * Mask Len + * The Mask length field is 8 bits. The value is the number of + * contiguous one bits that are left justified and used as a mask; + * when combined with the group address, it describes a range of + * groups. It is less than or equal to the address length in bits + * for the given Address Family and Encoding Type. If the message + * is sent for a single group, then the Mask length must equal the + * address length in bits for the given Address Family and Encoding + * Type (e.g., 32 for IPv4 native encoding, 128 for IPv6 native + * encoding). + * + * Group multicast Address + * Contains the group address. + */ +int +pim_encode_addr_group (uint8_t *buf, afi_t afi, int bidir, int scope, struct in_addr group) +{ + uint8_t flags = 0; + + flags |= bidir << 8; + flags |= scope; + + switch (afi) + { + case AFI_IP: + *(uint8_t *)buf = PIM_MSG_ADDRESS_FAMILY_IPV4; + ++buf; + *(uint8_t *)buf = 0; + ++buf; + *(uint8_t *)buf = flags; + ++buf; + *(uint8_t *)buf = 32; + ++buf; + memcpy (buf, &group, sizeof (struct in_addr)); + return group_ipv4_encoding_len; + break; + default: + return 0; + break; + } +} + uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, const uint8_t *buf_pastend, struct list *ifconnected) @@ -143,7 +243,7 @@ uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, if ((curr + ucast_ipv4_encoding_len) > buf_pastend) return 0; - l_encode = pim_encode_unicast_address (curr, p); + l_encode = pim_encode_addr_ucast (curr, p); curr += l_encode; option_len += l_encode; } @@ -173,7 +273,7 @@ static int check_tlv_length(const char *label, const char *tlv_name, int correct_len, int option_len) { if (option_len != correct_len) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s", label, tlv_name, @@ -192,7 +292,7 @@ static void check_tlv_redefinition_uint16(const char *label, const char *tlv_nam uint16_t new, uint16_t old) { if (PIM_OPTION_IS_SET(options, opt_mask)) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", label, tlv_name, @@ -208,7 +308,7 @@ static void check_tlv_redefinition_uint32(const char *label, const char *tlv_nam uint32_t new, uint32_t old) { if (PIM_OPTION_IS_SET(options, opt_mask)) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", label, tlv_name, @@ -224,7 +324,7 @@ static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv uint32_t new, uint32_t old) { if (PIM_OPTION_IS_SET(options, opt_mask)) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s", label, tlv_name, @@ -408,7 +508,7 @@ pim_parse_addr_ucast (struct prefix *p, } int -pim_parse_addr_group (struct prefix *p, +pim_parse_addr_group (struct prefix_sg *sg, const uint8_t *buf, int buf_size) { @@ -450,17 +550,15 @@ pim_parse_addr_group (struct prefix *p, return -3; } - p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ - memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); - p->prefixlen = mask_len; + memcpy(&sg->grp.s_addr, addr, sizeof(struct in_addr)); addr += sizeof(struct in_addr); break; default: { - zlog_warn("%s: unknown group address encoding family=%d from", - __PRETTY_FUNCTION__, family); + zlog_warn("%s: unknown group address encoding family=%d mask_len=%d from", + __PRETTY_FUNCTION__, family, mask_len); return -4; } } @@ -469,7 +567,7 @@ pim_parse_addr_group (struct prefix *p, } int -pim_parse_addr_source(struct prefix *p, +pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, const uint8_t *buf, int buf_size) @@ -513,9 +611,7 @@ pim_parse_addr_source(struct prefix *p, return -3; } - p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ - memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); - p->prefixlen = mask_len; + memcpy(&sg->src, addr, sizeof(struct in_addr)); /* RFC 4601: 4.9.1 Encoded Source and Group Address Formats @@ -527,9 +623,9 @@ pim_parse_addr_source(struct prefix *p, and 128 for IPv6 native). A router SHOULD ignore any messages received with any other mask length. */ - if (p->prefixlen != 32) { + if (mask_len != 32) { zlog_warn("%s: IPv4 bad source address mask: %d", - __PRETTY_FUNCTION__, p->prefixlen); + __PRETTY_FUNCTION__, mask_len); return -4; } @@ -582,7 +678,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, */ addr_offset = pim_parse_addr_ucast(&tmp, addr, pastend - addr); if (addr_offset < 1) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", __PRETTY_FUNCTION__, @@ -599,8 +695,8 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, switch (tmp.family) { case AF_INET: { - char addr_str[100]; - char src_str[100]; + char addr_str[INET_ADDRSTRLEN]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", tmp.u.prefix4, addr_str, sizeof(addr_str)); pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s", @@ -612,7 +708,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, break; default: { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s", __PRETTY_FUNCTION__, @@ -629,7 +725,7 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, */ if (tmp.family == AF_INET) { if (tmp.u.prefix4.s_addr == src_addr.s_addr) { - char src_str[100]; + char src_str[INET_ADDRSTRLEN]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_warn("%s: ignoring primary address in secondary list from %s on %s", __PRETTY_FUNCTION__, diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index 16c5aa4b97..9c4ebc9f0f 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -109,13 +109,16 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, uint16_t option_len, const uint8_t *tlv_curr); +int pim_encode_addr_ucast (uint8_t *buf, struct prefix *p); +int pim_encode_addr_group (uint8_t *buf, afi_t afi, int bidir, int scope, struct in_addr group); + int pim_parse_addr_ucast (struct prefix *p, const uint8_t *buf, int buf_size); -int pim_parse_addr_group (struct prefix *p, +int pim_parse_addr_group (struct prefix_sg *sg, const uint8_t *buf, int buf_size); -int pim_parse_addr_source(struct prefix *p, +int pim_parse_addr_source(struct prefix_sg *sg, uint8_t *flags, const uint8_t *buf, int buf_size); diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 059de3b62e..fbb7d84a25 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -27,6 +27,11 @@ #include "memory.h" #include "thread.h" #include "linklist.h" +#include "vty.h" +#include "plist.h" +#include "hash.h" +#include "jhash.h" +#include "wheel.h" #include "pimd.h" #include "pim_pim.h" @@ -44,10 +49,95 @@ #include "pim_macro.h" #include "pim_rp.h" #include "pim_br.h" +#include "pim_register.h" +#include "pim_msdp.h" + +struct hash *pim_upstream_hash = NULL; +struct list *pim_upstream_list = NULL; +struct timer_wheel *pim_upstream_sg_wheel = NULL; static void join_timer_start(struct pim_upstream *up); static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up); +/* + * A (*,G) or a (*,*) is going away + * remove the parent pointer from + * those pointing at us + */ +static void +pim_upstream_remove_children (struct pim_upstream *up) +{ + struct pim_upstream *child; + + if (!up->sources) + return; + + while (!list_isempty (up->sources)) + { + child = listnode_head (up->sources); + child->parent = NULL; + listnode_delete (up->sources, child); + } +} + +/* + * A (*,G) or a (*,*) is being created + * Find the children that would point + * at us. + */ +static void +pim_upstream_find_new_children (struct pim_upstream *up) +{ + struct pim_upstream *child; + struct listnode *ch_node; + + if ((up->sg.src.s_addr != INADDR_ANY) && + (up->sg.grp.s_addr != INADDR_ANY)) + return; + + if ((up->sg.src.s_addr == INADDR_ANY) && + (up->sg.grp.s_addr == INADDR_ANY)) + return; + + for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child)) + { + if ((up->sg.grp.s_addr != INADDR_ANY) && + (child->sg.grp.s_addr == up->sg.grp.s_addr) && + (child != up)) + { + child->parent = up; + listnode_add_sort (up->sources, child); + } + } +} + +/* + * If we have a (*,*) || (S,*) there is no parent + * If we have a (S,G), find the (*,G) + * If we have a (*,G), find the (*,*) + */ +static struct pim_upstream * +pim_upstream_find_parent (struct pim_upstream *child) +{ + struct prefix_sg any = child->sg; + struct pim_upstream *up = NULL; + + // (S,G) + if ((child->sg.src.s_addr != INADDR_ANY) && + (child->sg.grp.s_addr != INADDR_ANY)) + { + any.src.s_addr = INADDR_ANY; + up = pim_upstream_find (&any); + + if (up) + listnode_add (up->sources, child); + + return up; + } + + return NULL; +} + void pim_upstream_free(struct pim_upstream *up) { XFREE(MTYPE_PIM_UPSTREAM, up); @@ -61,48 +151,89 @@ static void upstream_channel_oil_detach(struct pim_upstream *up) } } -void pim_upstream_delete(struct pim_upstream *up) +void +pim_upstream_del(struct pim_upstream *up, const char *name) { + bool notify_msdp = false; + + if (PIM_DEBUG_TRACE) + zlog_debug ("%s(%s): Delete %s ref count: %d", + __PRETTY_FUNCTION__, name, up->sg_str, up->ref_count); + + --up->ref_count; + + if (up->ref_count >= 1) + return; + THREAD_OFF(up->t_join_timer); THREAD_OFF(up->t_ka_timer); + THREAD_OFF(up->t_rs_timer); + THREAD_OFF(up->t_msdp_reg_timer); + if (up->join_state == PIM_UPSTREAM_JOINED) { + pim_joinprune_send (up->rpf.source_nexthop.interface, + up->rpf.rpf_addr.u.prefix4, + up, 0); + if (up->sg.src.s_addr == INADDR_ANY) { + /* if a (*, G) entry in the joined state is being deleted we + * need to notify MSDP */ + notify_msdp = true; + } + } + + if (up->sg.src.s_addr != INADDR_ANY) { + wheel_remove_item (pim_upstream_sg_wheel, up); + notify_msdp = true; + } + + pim_upstream_remove_children (up); + pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__); upstream_channel_oil_detach(up); + if (up->sources) + list_delete (up->sources); + up->sources = NULL; + /* notice that listnode_delete() can't be moved into pim_upstream_free() because the later is called by list_delete_all_node() */ - listnode_delete(qpim_upstream_list, up); + if (up->parent) + { + listnode_delete (up->parent->sources, up); + up->parent = NULL; + } + listnode_delete (pim_upstream_list, up); + hash_release (pim_upstream_hash, up); + if (notify_msdp) { + pim_msdp_up_del(&up->sg); + } pim_upstream_free(up); } -static void send_join(struct pim_upstream *up) +void +pim_upstream_send_join (struct pim_upstream *up) { - zassert(up->join_state == PIM_UPSTREAM_JOINED); - - - if (PIM_DEBUG_PIM_TRACE) { - if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { - char src_str[100]; - char grp_str[100]; - char rpf_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s", - __PRETTY_FUNCTION__, - src_str, grp_str, rpf_str); + if (PIM_DEBUG_TRACE) { + char rpf_str[PREFIX_STRLEN]; + pim_addr_dump("", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__, + up->sg_str, rpf_str, pim_upstream_state2str (up->join_state), + up->rpf.source_nexthop.interface->name); + if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { + zlog_debug("%s: can't send join upstream: RPF'%s=%s", + __PRETTY_FUNCTION__, + up->sg_str, rpf_str); /* warning only */ } } - + /* send Join(S,G) to the current upstream neighbor */ pim_joinprune_send(up->rpf.source_nexthop.interface, - up->rpf.rpf_addr, - up->source_addr, - up->group_addr, + up->rpf.rpf_addr.u.prefix4, + up, 1 /* join */); } @@ -110,13 +241,23 @@ static int on_join_timer(struct thread *t) { struct pim_upstream *up; - zassert(t); up = THREAD_ARG(t); - zassert(up); - - send_join(up); up->t_join_timer = NULL; + + /* + * In the case of a HFR we will not ahve anyone to send this to. + */ + if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) + return 0; + + /* + * Don't send the join if the outgoing interface is a loopback + * But since this might change leave the join timer running + */ + if (!if_is_loopback (up->rpf.source_nexthop.interface)) + pim_upstream_send_join (up); + join_timer_start(up); return 0; @@ -125,18 +266,13 @@ static int on_join_timer(struct thread *t) static void join_timer_start(struct pim_upstream *up) { if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)", + zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s", __PRETTY_FUNCTION__, qpim_t_periodic, - src_str, grp_str); + up->sg_str); } - zassert(!up->t_join_timer); - + THREAD_OFF (up->t_join_timer); THREAD_TIMER_ON(master, up->t_join_timer, on_join_timer, up, qpim_t_periodic); @@ -152,14 +288,10 @@ static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, int interval_msec) { if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)", + zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s", __PRETTY_FUNCTION__, interval_msec, - src_str, grp_str); + up->sg_str); } THREAD_OFF(up->t_join_timer); @@ -180,29 +312,21 @@ void pim_upstream_join_suppress(struct pim_upstream *up, join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - char rpf_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + if (PIM_DEBUG_TRACE) { + char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", + zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, + up->sg_str, rpf_str, join_timer_remain_msec, t_joinsuppress_msec); } if (join_timer_remain_msec < t_joinsuppress_msec) { - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec", + if (PIM_DEBUG_TRACE) { + zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, t_joinsuppress_msec); + up->sg_str, t_joinsuppress_msec); } pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec); @@ -219,28 +343,20 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface); - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - char rpf_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + if (PIM_DEBUG_TRACE) { + char rpf_str[INET_ADDRSTRLEN]; pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec", + zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec", debug_label, - src_str, grp_str, rpf_str, + up->sg_str, rpf_str, join_timer_remain_msec, t_override_msec); } if (join_timer_remain_msec > t_override_msec) { - if (PIM_DEBUG_PIM_TRACE) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec", + if (PIM_DEBUG_TRACE) { + zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec", debug_label, - src_str, grp_str, + up->sg_str, t_override_msec); } @@ -250,196 +366,327 @@ void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, static void forward_on(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; + /* scan (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; - if (ch->upstream != up) - continue; + if (pim_macro_chisin_oiflist(ch)) + pim_forward_start(ch); - if (pim_macro_chisin_oiflist(ch)) - pim_forward_start(ch); - - } /* scan iface channel list */ - } /* scan iflist */ + } /* scan iface channel list */ } static void forward_off(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; - if (ch->upstream != up) - continue; + pim_forward_stop(ch); - pim_forward_stop(ch); - - } /* scan iface channel list */ - } /* scan iflist */ + } /* scan iface channel list */ } -static void pim_upstream_switch(struct pim_upstream *up, - enum pim_upstream_state new_state) +static int +pim_upstream_could_register (struct pim_upstream *up) +{ + struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info; + + if (pim_ifp && PIM_I_am_DR (pim_ifp) && + pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src)) + return 1; + + return 0; +} + +void +pim_upstream_switch(struct pim_upstream *up, + enum pim_upstream_state new_state) { enum pim_upstream_state old_state = up->join_state; - zassert(old_state != new_state); - - up->join_state = new_state; - up->state_transition = pim_time_monotonic_sec(); - if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)", + zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s", __PRETTY_FUNCTION__, - ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"), - src_str, grp_str); + up->sg_str, + pim_upstream_state2str (up->join_state), + pim_upstream_state2str (new_state)); } + /* + * This code still needs work. + */ + switch (up->join_state) + { + case PIM_UPSTREAM_PRUNE: + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) + { + up->join_state = new_state; + up->state_transition = pim_time_monotonic_sec (); + } + break; + case PIM_UPSTREAM_JOIN_PENDING: + break; + case PIM_UPSTREAM_NOTJOINED: + case PIM_UPSTREAM_JOINED: + up->join_state = new_state; + if (old_state != new_state) + up->state_transition = pim_time_monotonic_sec(); + + break; + } + pim_upstream_update_assert_tracking_desired(up); if (new_state == PIM_UPSTREAM_JOINED) { - forward_on(up); - send_join(up); - join_timer_start(up); + if (old_state != PIM_UPSTREAM_JOINED) + { + int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags); + forward_on(up); + pim_msdp_up_join_state_changed(up); + if (pim_upstream_could_register (up)) + { + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) + { + pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + } + } + else + { + pim_upstream_send_join (up); + join_timer_start (up); + } + } + else + { + forward_on (up); + } } else { forward_off(up); + if (old_state == PIM_UPSTREAM_JOINED) + pim_msdp_up_join_state_changed(up); pim_joinprune_send(up->rpf.source_nexthop.interface, - up->rpf.rpf_addr, - up->source_addr, - up->group_addr, + up->rpf.rpf_addr.u.prefix4, + up, 0 /* prune */); - zassert(up->t_join_timer); - THREAD_OFF(up->t_join_timer); + if (up->t_join_timer) + THREAD_OFF(up->t_join_timer); } - } - -static struct pim_upstream *pim_upstream_new(struct in_addr source_addr, - struct in_addr group_addr, - struct interface *incoming) +static int +pim_upstream_compare (void *arg1, void *arg2) { - struct pim_upstream *up; - enum pim_rpf_result rpf_result; + const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; + const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; - up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr)) + return -1; + + if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr)) + return 1; + + if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr)) + return -1; + + if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr)) + return 1; + + return 0; +} + +static struct pim_upstream * +pim_upstream_new (struct prefix_sg *sg, + struct interface *incoming, + int flags) +{ + enum pim_rpf_result rpf_result; + struct pim_interface *pim_ifp; + struct pim_upstream *up; + + up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); if (!up) { - zlog_err("%s: PIM XMALLOC(%zu) failure", + zlog_err("%s: PIM XCALLOC(%zu) failure", __PRETTY_FUNCTION__, sizeof(*up)); return NULL; } - up->source_addr = source_addr; - if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr)) + up->sg = *sg; + pim_str_sg_set (sg, up->sg_str); + up = hash_get (pim_upstream_hash, up, hash_alloc_intern); + if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp)) { - if (PIM_DEBUG_PIM_TRACE) + if (PIM_DEBUG_TRACE) zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__); + hash_release (pim_upstream_hash, up); XFREE (MTYPE_PIM_UPSTREAM, up); return NULL; } - up->group_addr = group_addr; - up->flags = 0; + up->parent = pim_upstream_find_parent (up); + if (up->sg.src.s_addr == INADDR_ANY) + { + up->sources = list_new (); + up->sources->cmp = pim_upstream_compare; + } + else + up->sources = NULL; + + pim_upstream_find_new_children (up); + up->flags = flags; up->ref_count = 1; up->t_join_timer = NULL; up->t_ka_timer = NULL; + up->t_rs_timer = NULL; + up->t_msdp_reg_timer = NULL; up->join_state = 0; up->state_transition = pim_time_monotonic_sec(); up->channel_oil = NULL; up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; - up->rpf.source_nexthop.interface = 0; - up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + up->rpf.source_nexthop.interface = NULL; + up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET; + up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference; up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric; - up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY; + up->rpf.rpf_addr.family = AF_INET; + up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; - rpf_result = pim_rpf_update(up, 0, incoming); + if (up->sg.src.s_addr != INADDR_ANY) + wheel_add_item (pim_upstream_sg_wheel, up); + + rpf_result = pim_rpf_update(up, NULL); if (rpf_result == PIM_RPF_FAILURE) { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__, + up->sg_str); + + if (up->parent) + { + listnode_delete (up->parent->sources, up); + up->parent = NULL; + } + + if (up->sg.src.s_addr != INADDR_ANY) + wheel_remove_item (pim_upstream_sg_wheel, up); + + pim_upstream_remove_children (up); + if (up->sources) + list_delete (up->sources); + + hash_release (pim_upstream_hash, up); XFREE(MTYPE_PIM_UPSTREAM, up); return NULL; } - listnode_add(qpim_upstream_list, up); + pim_ifp = up->rpf.source_nexthop.interface->info; + if (pim_ifp) + up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index); + + listnode_add_sort(pim_upstream_list, up); + + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Created Upstream %s", __PRETTY_FUNCTION__, up->sg_str); return up; } -struct pim_upstream *pim_upstream_find(struct in_addr source_addr, - struct in_addr group_addr) +struct pim_upstream *pim_upstream_find(struct prefix_sg *sg) { - struct listnode *up_node; - struct pim_upstream *up; + struct pim_upstream lookup; + struct pim_upstream *up = NULL; - for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { - if (group_addr.s_addr == up->group_addr.s_addr) { - if ((up->source_addr.s_addr == INADDR_ANY) || - (source_addr.s_addr == up->source_addr.s_addr)) { - return up; - } - } - } - - return 0; + lookup.sg = *sg; + up = hash_lookup (pim_upstream_hash, &lookup); + return up; } -struct pim_upstream *pim_upstream_add(struct in_addr source_addr, - struct in_addr group_addr, - struct interface *incoming) +static void pim_upstream_ref(struct pim_upstream *up, int flags) { - struct pim_upstream *up; + up->flags |= flags; + ++up->ref_count; +} - up = pim_upstream_find(source_addr, group_addr); +struct pim_upstream *pim_upstream_add(struct prefix_sg *sg, + struct interface *incoming, + int flags, const char *name) +{ + struct pim_upstream *up = NULL; + int found = 0; + up = pim_upstream_find(sg); if (up) { - ++up->ref_count; + pim_upstream_ref(up, flags); + found = 1; } else { - up = pim_upstream_new(source_addr, group_addr, incoming); + up = pim_upstream_new(sg, incoming, flags); } + if (PIM_DEBUG_TRACE) + { + if (up) + zlog_debug("%s(%s): %s, found: %d: ref_count: %d", + __PRETTY_FUNCTION__, name, + up->sg_str, found, + up->ref_count); + else + zlog_debug("%s(%s): (%s) failure to create", + __PRETTY_FUNCTION__, name, + pim_str_sg_dump (sg)); + } + return up; } -void pim_upstream_del(struct pim_upstream *up) +static int +pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up, + struct pim_ifchannel *ch) { - --up->ref_count; + struct pim_upstream *parent = up->parent; - if (up->ref_count < 1) { - pim_upstream_delete(up); - } + if (ch->upstream == up) + { + if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch)) + return 1; + + if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) + return 0; + } + + /* + * joins (*,G) + */ + if (parent && ch->upstream == parent) + { + if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch)) + return 1; + } + + return 0; } /* @@ -467,34 +714,23 @@ void pim_upstream_del(struct pim_upstream *up) */ int pim_upstream_evaluate_join_desired(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; + int ret = 0; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; - if (!pim_ifp) - continue; - - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { - if (ch->upstream != up) + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) + { + pim_ifp = ch->interface->info; + if (!pim_ifp) continue; - if (pim_macro_ch_lost_assert(ch)) - continue; /* keep searching */ - - if (pim_macro_chisin_joins_or_include(ch)) - return 1; /* true */ + ret += pim_upstream_evaluate_join_desired_interface (up, ch); } /* scan iface channel list */ - } /* scan iflist */ - return 0; /* false */ + return ret; /* false */ } /* @@ -515,14 +751,12 @@ void pim_upstream_update_join_desired(struct pim_upstream *up) /* switched from false to true */ if (is_join_desired && !was_join_desired) { - zassert(up->join_state == PIM_UPSTREAM_NOTJOINED); pim_upstream_switch(up, PIM_UPSTREAM_JOINED); return; } /* switched from true to false */ if (!is_join_desired && was_join_desired) { - zassert(up->join_state == PIM_UPSTREAM_JOINED); pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED); return; } @@ -544,22 +778,18 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr) struct pim_upstream *up; /* - Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr - */ - for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr + */ + for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) { - if (PIM_DEBUG_PIM_TRACE) { - char neigh_str[100]; - char src_str[100]; - char grp_str[100]; - char rpf_addr_str[100]; + if (PIM_DEBUG_TRACE) { + char neigh_str[INET_ADDRSTRLEN]; + char rpf_addr_str[PREFIX_STRLEN]; pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); - zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s", + pim_addr_dump("", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s", __PRETTY_FUNCTION__, - neigh_str, src_str, grp_str, + neigh_str, up->sg_str, up->join_state == PIM_UPSTREAM_JOINED, rpf_addr_str); } @@ -569,7 +799,7 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr) continue; /* match RPF'(S,G)=neigh_addr */ - if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr) + if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr) continue; pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change", @@ -581,130 +811,141 @@ void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr) void pim_upstream_rpf_interface_changed(struct pim_upstream *up, struct interface *old_rpf_ifp) { - struct listnode *ifnode; - struct listnode *ifnextnode; - struct interface *ifp; + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - struct listnode *chnode; - struct listnode *chnextnode; - struct pim_ifchannel *ch; - struct pim_interface *pim_ifp; + /* search all ifchannels */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { - pim_ifp = ifp->info; + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* search all ifchannels */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { - if (ch->upstream != up) - continue; + if (ch->upstream != up) + continue; - if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { - if ( - /* RPF_interface(S) was NOT I */ - (old_rpf_ifp == ch->interface) - && - /* RPF_interface(S) stopped being I */ - (ch->upstream->rpf.source_nexthop.interface != ch->interface) - ) { - assert_action_a5(ch); - } - } /* PIM_IFASSERT_I_AM_LOSER */ + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + if ( + /* RPF_interface(S) was NOT I */ + (old_rpf_ifp == ch->interface) + && + /* RPF_interface(S) stopped being I */ + (ch->upstream->rpf.source_nexthop.interface != ch->interface) + ) { + assert_action_a5(ch); + } + } /* PIM_IFASSERT_I_AM_LOSER */ - pim_ifchannel_update_assert_tracking_desired(ch); - } + pim_ifchannel_update_assert_tracking_desired(ch); } } void pim_upstream_update_could_assert(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; - if (ch->upstream != up) - continue; - - pim_ifchannel_update_could_assert(ch); - - } /* scan iface channel list */ - } /* scan iflist */ + pim_ifchannel_update_could_assert(ch); + } /* scan iface channel list */ } void pim_upstream_update_my_assert_metric(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; - if (ch->upstream != up) - continue; + pim_ifchannel_update_my_assert_metric(ch); - pim_ifchannel_update_my_assert_metric(ch); - - } /* scan iface channel list */ - } /* scan iflist */ + } /* scan iface channel list */ } static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up) { - struct listnode *ifnode; - struct listnode *ifnextnode; struct listnode *chnode; struct listnode *chnextnode; - struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - /* scan all interfaces */ - for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { - pim_ifp = ifp->info; + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) { + pim_ifp = ch->interface->info; if (!pim_ifp) continue; - /* scan per-interface (S,G) state */ - for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; - if (ch->upstream != up) - continue; + pim_ifchannel_update_assert_tracking_desired(ch); - pim_ifchannel_update_assert_tracking_desired(ch); + } /* scan iface channel list */ +} - } /* scan iface channel list */ - } /* scan iflist */ +/* When kat is stopped CouldRegister goes to false so we need to + * transition the (S, G) on FHR to NI state and remove reg tunnel + * from the OIL */ +static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up) +{ + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) + return; + + if (PIM_DEBUG_TRACE) + zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str); + + /* stop reg-stop timer */ + THREAD_OFF(up->t_rs_timer); + /* remove regiface from the OIL if it is there*/ + pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + /* move to "not-joined" */ + up->join_state = PIM_UPSTREAM_NOTJOINED; + PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); +} + +/* When kat is started CouldRegister can go to true. And if it does we + * need to transition the (S, G) on FHR to JOINED state and add reg tunnel + * to the OIL */ +static void pim_upstream_fhr_kat_start(struct pim_upstream *up) +{ + if (pim_upstream_could_register(up)) { + if (PIM_DEBUG_TRACE) + zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str); + + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + if (up->join_state == PIM_UPSTREAM_NOTJOINED) { + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + up->join_state = PIM_UPSTREAM_JOINED; + } + } } /* * On an RP, the PMBR value must be cleared when the * Keepalive Timer expires + * KAT expiry indicates that flow is inactive. If the flow was created or + * maintained by activity now is the time to deref it. */ static int pim_upstream_keep_alive_timer (struct thread *t) @@ -712,41 +953,73 @@ pim_upstream_keep_alive_timer (struct thread *t) struct pim_upstream *up; up = THREAD_ARG(t); + up->t_ka_timer = NULL; - if (I_am_RP (up->group_addr)) - { - pim_br_clear_pmbr (up->source_addr, up->group_addr); - /* - * We need to do more here :) - * But this is the start. - */ - } - else - { - pim_mroute_update_counters (up->channel_oil); + if (I_am_RP (up->sg.grp)) + { + pim_br_clear_pmbr (&up->sg); + /* + * We need to do more here :) + * But this is the start. + */ + } - if (up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) - { - pim_mroute_del (up->channel_oil); - pim_upstream_delete (up); - } - else - { - up->t_ka_timer = NULL; - pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD); - } - } - return 1; + /* source is no longer active - pull the SA from MSDP's cache */ + pim_msdp_sa_local_del(&up->sg); + + /* if entry was created because of activity we need to deref it */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) + { + pim_upstream_fhr_kat_expiry(up); + if (PIM_DEBUG_TRACE) + zlog_debug ("kat expired on %s; remove stream reference", up->sg_str); + PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags); + pim_upstream_del(up, __PRETTY_FUNCTION__); + } + + return 0; } void pim_upstream_keep_alive_timer_start (struct pim_upstream *up, uint32_t time) { + if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { + if (PIM_DEBUG_TRACE) + zlog_debug ("kat start on %s with no stream reference", up->sg_str); + } + THREAD_OFF (up->t_ka_timer); THREAD_TIMER_ON (master, up->t_ka_timer, pim_upstream_keep_alive_timer, up, time); + + /* any time keepalive is started against a SG we will have to + * re-evaluate our active source database */ + pim_msdp_sa_local_update(up); +} + +/* MSDP on RP needs to know if a source is registerable to this RP */ +static int +pim_upstream_msdp_reg_timer(struct thread *t) +{ + struct pim_upstream *up; + + up = THREAD_ARG(t); + up->t_msdp_reg_timer = NULL; + + /* source is no longer active - pull the SA from MSDP's cache */ + pim_msdp_sa_local_del(&up->sg); + return 1; +} +void +pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) +{ + THREAD_OFF(up->t_msdp_reg_timer); + THREAD_TIMER_ON(master, up->t_msdp_reg_timer, + pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD); + + pim_msdp_sa_local_update(up); } /* @@ -778,7 +1051,472 @@ pim_upstream_keep_alive_timer_start (struct pim_upstream *up, * received for the source and group. */ int -pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group) +pim_upstream_switch_to_spt_desired (struct prefix_sg *sg) { + if (I_am_RP (sg->grp)) + return 1; + return 0; } + +int +pim_upstream_is_sg_rpt (struct pim_upstream *up) +{ + struct listnode *chnode; + struct pim_ifchannel *ch; + + for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch)) + { + if ((ch->upstream == up) && + (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))) + return 1; + } + + return 0; +} +/* + * After receiving a packet set SPTbit: + * void + * Update_SPTbit(S,G,iif) { + * if ( iif == RPF_interface(S) + * AND JoinDesired(S,G) == TRUE + * AND ( DirectlyConnected(S) == TRUE + * OR RPF_interface(S) != RPF_interface(RP(G)) + * OR inherited_olist(S,G,rpt) == NULL + * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND + * ( RPF'(S,G) != NULL ) ) + * OR ( I_Am_Assert_Loser(S,G,iif) ) { + * Set SPTbit(S,G) to TRUE + * } + * } + */ +void +pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming) +{ + struct pim_rpf *grpf = NULL; + + // iif == RPF_interfvace(S) + if (up->rpf.source_nexthop.interface != incoming) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Incoming Interface: %s is different than RPF_interface(S) %s", + __PRETTY_FUNCTION__, incoming->name, up->rpf.source_nexthop.interface->name); + return; + } + + // AND JoinDesired(S,G) == TRUE + // FIXME + + // DirectlyConnected(S) == TRUE + if (pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: %s is directly connected to the source", __PRETTY_FUNCTION__, + up->sg_str); + up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; + return; + } + + // OR RPF_interface(S) != RPF_interface(RP(G)) + grpf = RP(up->sg.grp); + if (!grpf || up->rpf.source_nexthop.interface != grpf->source_nexthop.interface) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: %s RPF_interface(S) != RPF_interface(RP(G))", + __PRETTY_FUNCTION__, up->sg_str); + up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; + return; + } + + // OR inherited_olist(S,G,rpt) == NULL + if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: %s OR inherited_olist(S,G,rpt) == NULL", __PRETTY_FUNCTION__, + up->sg_str); + up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; + return; + } + + // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND + // ( RPF'(S,G) != NULL ) ) + if (up->parent && pim_rpf_is_same (&up->rpf, &up->parent->rpf)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: %s RPF'(S,G) is the same as RPF'(*,G)", __PRETTY_FUNCTION__, + up->sg_str); + up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE; + return; + } + + return; +} + +const char * +pim_upstream_state2str (enum pim_upstream_state join_state) +{ + switch (join_state) + { + case PIM_UPSTREAM_NOTJOINED: + return "NotJoined"; + break; + case PIM_UPSTREAM_JOINED: + return "Joined"; + break; + case PIM_UPSTREAM_JOIN_PENDING: + return "JoinPending"; + break; + case PIM_UPSTREAM_PRUNE: + return "Prune"; + break; + } + return "Unknown"; +} + +static int +pim_upstream_register_stop_timer (struct thread *t) +{ + struct pim_interface *pim_ifp; + struct pim_upstream *up; + struct pim_rpf *rpg; + struct ip ip_hdr; + up = THREAD_ARG (t); + + up->t_rs_timer = NULL; + + if (PIM_DEBUG_TRACE) + { + zlog_debug ("%s: (S,G)=%s upstream register stop timer %s", + __PRETTY_FUNCTION__, up->sg_str, + pim_upstream_state2str(up->join_state)); + } + + switch (up->join_state) + { + case PIM_UPSTREAM_JOIN_PENDING: + up->join_state = PIM_UPSTREAM_JOINED; + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + break; + case PIM_UPSTREAM_JOINED: + break; + case PIM_UPSTREAM_PRUNE: + pim_ifp = up->rpf.source_nexthop.interface->info; + if (!pim_ifp) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Interface: %s is not configured for pim", + __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name); + return 0; + } + up->join_state = PIM_UPSTREAM_JOIN_PENDING; + pim_upstream_start_register_stop_timer (up, 1); + + if (((up->channel_oil->cc.lastused/100) > PIM_KEEPALIVE_PERIOD) && + (I_am_RP (up->sg.grp))) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", __PRETTY_FUNCTION__); + return 0; + } + rpg = RP (up->sg.grp); + memset (&ip_hdr, 0, sizeof (struct ip)); + ip_hdr.ip_p = PIM_IP_PROTO_PIM; + ip_hdr.ip_hl = 5; + ip_hdr.ip_v = 4; + ip_hdr.ip_src = up->sg.src; + ip_hdr.ip_dst = up->sg.grp; + ip_hdr.ip_len = htons (20); + // checksum is broken + pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip), + pim_ifp->primary_address, rpg, 1, up); + break; + default: + break; + } + + return 0; +} + +void +pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register) +{ + uint32_t time; + + if (up->t_rs_timer) + { + THREAD_TIMER_OFF (up->t_rs_timer); + up->t_rs_timer = NULL; + } + + if (!null_register) + { + uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD); + uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD); + time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD; + } + else + time = PIM_REGISTER_PROBE_PERIOD; + + if (PIM_DEBUG_TRACE) + { + zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d", + __PRETTY_FUNCTION__, up->sg_str, time); + } + THREAD_TIMER_ON (master, up->t_rs_timer, + pim_upstream_register_stop_timer, + up, time); +} + +int +pim_upstream_inherited_olist_decide (struct pim_upstream *up) +{ + struct pim_interface *pim_ifp; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct listnode *chnode; + int output_intf = 0; + + pim_ifp = up->rpf.source_nexthop.interface->info; + if (pim_ifp && !up->channel_oil) + up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index); + + for (ALL_LIST_ELEMENTS (pim_ifchannel_list, chnode, chnextnode, ch)) + { + pim_ifp = ch->interface->info; + if (!pim_ifp) + continue; + + if (pim_upstream_evaluate_join_desired_interface (up, ch)) + { + pim_channel_add_oif (up->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM); + output_intf++; + } + } + + return output_intf; +} + +/* + * For a given upstream, determine the inherited_olist + * and apply it. + * + * inherited_olist(S,G,rpt) = + * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) + * (+) ( pim_include(*,G) (-) pim_exclude(S,G)) + * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) ) + * + * inherited_olist(S,G) = + * inherited_olist(S,G,rpt) (+) + * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + * + * return 1 if there are any output interfaces + * return 0 if there are not any output interfaces + */ +int +pim_upstream_inherited_olist (struct pim_upstream *up) +{ + int output_intf = pim_upstream_inherited_olist_decide (up); + + /* + * If we have output_intf switch state to Join and work like normal + * If we don't have an output_intf that means we are probably a + * switch on a stick so turn on forwarding to just accept the + * incoming packets so we don't bother the other stuff! + */ + if (output_intf) + pim_upstream_switch (up, PIM_UPSTREAM_JOINED); + else + forward_on (up); + + return output_intf; +} + +int +pim_upstream_empty_inherited_olist (struct pim_upstream *up) +{ + return pim_channel_oil_empty (up->channel_oil); +} + +/* + * When we have a new neighbor, + * find upstreams that don't have their rpf_addr + * set and see if the new neighbor allows + * the join to be sent + */ +void +pim_upstream_find_new_rpf (void) +{ + struct listnode *up_node; + struct listnode *up_nextnode; + struct pim_upstream *up; + + /* + * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr + */ + for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) + { + if (pim_rpf_addr_is_inaddr_any(&up->rpf)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("Upstream %s without a path to send join, checking", + up->sg_str); + pim_rpf_update (up, NULL); + } + } +} + +static unsigned int +pim_upstream_hash_key (void *arg) +{ + struct pim_upstream *up = (struct pim_upstream *)arg; + + return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0); +} + +void pim_upstream_terminate (void) +{ + if (pim_upstream_list) + list_delete (pim_upstream_list); + pim_upstream_list = NULL; + + if (pim_upstream_hash) + hash_free (pim_upstream_hash); + pim_upstream_hash = NULL; +} + +static int +pim_upstream_equal (const void *arg1, const void *arg2) +{ + const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; + const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; + + if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) && + (up1->sg.src.s_addr == up2->sg.src.s_addr)) + return 1; + + return 0; +} + +/* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines + * the cases where kat has to be restarted on rxing traffic - + * + * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) { + * set KeepaliveTimer(S,G) to Keepalive_Period + * # Note: a register state transition or UpstreamJPState(S,G) + * # transition may happen as a result of restarting + * # KeepaliveTimer, and must be dealt with here. + * } + * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND + * inherited_olist(S,G) != NULL ) { + * set KeepaliveTimer(S,G) to Keepalive_Period + * } + */ +static bool pim_upstream_kat_start_ok(struct pim_upstream *up) +{ + /* "iif == RPF_interface(S)" check has to be done by the kernel or hw + * so we will skip that here */ + if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, + up->sg.src)) { + return true; + } + + if ((up->join_state == PIM_UPSTREAM_JOINED) && + !pim_upstream_empty_inherited_olist(up)) { + /* XXX: I have added this RP check just for 3.2 and it's a digression from + * what rfc-4601 says. Till now we were only running KAT on FHR and RP and + * there is some angst around making the change to run it all routers that + * maintain the (S, G) state. This is tracked via CM-13601 and MUST be + * removed to handle spt turn-arounds correctly in a 3-tier clos */ + if (I_am_RP (up->sg.grp)) + return true; + } + + return false; +} + +/* + * Code to check and see if we've received packets on a S,G mroute + * and if so to set the SPT bit appropriately + */ +static void +pim_upstream_sg_running (void *arg) +{ + struct pim_upstream *up = (struct pim_upstream *)arg; + + // No packet can have arrived here if this is the case + if (!up->channel_oil || !up->channel_oil->installed) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: %s is not installed in mroute", + __PRETTY_FUNCTION__, up->sg_str); + return; + } + + /* + * This is a bit of a hack + * We've noted that we should rescan but + * we've missed the window for doing so in + * pim_zebra.c for some reason. I am + * only doing this at this point in time + * to get us up and working for the moment + */ + if (up->channel_oil->oil_inherited_rescan) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Handling unscanned inherited_olist for %s", __PRETTY_FUNCTION__, up->sg_str); + pim_upstream_inherited_olist_decide (up); + up->channel_oil->oil_inherited_rescan = 0; + } + pim_mroute_update_counters (up->channel_oil); + + // Have we seen packets? + if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) && + (up->channel_oil->cc.lastused/100 > 30)) + { + if (PIM_DEBUG_TRACE) + { + zlog_debug ("%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)", + __PRETTY_FUNCTION__, up->sg_str, + up->channel_oil->cc.oldpktcnt, + up->channel_oil->cc.pktcnt, + up->channel_oil->cc.lastused/100); + } + return; + } + + if (pim_upstream_kat_start_ok(up)) { + /* Add a source reference to the stream if + * one doesn't already exist */ + if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("source reference created on kat restart %s", up->sg_str); + + pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM); + PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags); + pim_upstream_fhr_kat_start(up); + } + pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time); + } + + if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) + { + pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface); + } + return; +} + +void +pim_upstream_init (void) +{ + pim_upstream_sg_wheel = wheel_init (master, 31000, 100, + pim_upstream_hash_key, + pim_upstream_sg_running); + pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key, + pim_upstream_equal); + + pim_upstream_list = list_new (); + pim_upstream_list->del = (void (*)(void *)) pim_upstream_free; + pim_upstream_list->cmp = pim_upstream_compare; + +} diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index f10c8feb3b..1a50c0c6e3 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -22,52 +22,47 @@ #define PIM_UPSTREAM_H #include +#include + +#include #define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0) -#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0) +#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (1 << 1) +#define PIM_UPSTREAM_FLAG_MASK_FHR (1 << 2) +#define PIM_UPSTREAM_FLAG_MASK_SRC_IGMP (1 << 3) +#define PIM_UPSTREAM_FLAG_MASK_SRC_PIM (1 << 4) +#define PIM_UPSTREAM_FLAG_MASK_SRC_STREAM (1 << 5) +#define PIM_UPSTREAM_FLAG_MASK_SRC_MSDP (1 << 6) #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) +#define PIM_UPSTREAM_FLAG_TEST_FHR(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_FHR) +#define PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) +#define PIM_UPSTREAM_FLAG_TEST_SRC_PIM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_PIM) +#define PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) +#define PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) +#define PIM_UPSTREAM_FLAG_SET_FHR(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_FHR) +#define PIM_UPSTREAM_FLAG_SET_SRC_IGMP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) +#define PIM_UPSTREAM_FLAG_SET_SRC_PIM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_PIM) +#define PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) +#define PIM_UPSTREAM_FLAG_SET_SRC_MSDP(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) - -/* - RFC 4601: - - Metric Preference - Preference value assigned to the unicast routing protocol that - provided the route to the multicast source or Rendezvous-Point. - - Metric - The unicast routing table metric associated with the route used to - reach the multicast source or Rendezvous-Point. The metric is in - units applicable to the unicast routing protocol used. -*/ -struct pim_nexthop { - struct interface *interface; /* RPF_interface(S) */ - struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */ - uint32_t mrib_metric_preference; /* MRIB.pref(S) */ - uint32_t mrib_route_metric; /* MRIB.metric(S) */ -}; - -struct pim_rpf { - struct pim_nexthop source_nexthop; - struct in_addr rpf_addr; /* RPF'(S,G) */ -}; - -enum pim_rpf_result { - PIM_RPF_OK = 0, - PIM_RPF_CHANGED, - PIM_RPF_FAILURE -}; +#define PIM_UPSTREAM_FLAG_UNSET_FHR(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_FHR) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_PIM) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_STREAM) +#define PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_MSDP) enum pim_upstream_state { PIM_UPSTREAM_NOTJOINED, - PIM_UPSTREAM_JOINED + PIM_UPSTREAM_JOINED, + PIM_UPSTREAM_JOIN_PENDING, + PIM_UPSTREAM_PRUNE, }; enum pim_upstream_sptbit { @@ -83,11 +78,14 @@ enum pim_upstream_sptbit { See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message */ struct pim_upstream { + struct pim_upstream *parent; struct in_addr upstream_addr;/* Who we are talking to */ - struct in_addr source_addr; /* (S,G) source key */ - struct in_addr group_addr; /* (S,G) group key */ + struct in_addr upstream_register; /*Who we received a register from*/ + struct prefix_sg sg; /* (S,G) group key */ + char sg_str[PIM_SG_LEN]; uint32_t flags; struct channel_oil *channel_oil; + struct list *sources; enum pim_upstream_state join_state; enum pim_upstream_sptbit sptbit; @@ -98,6 +96,13 @@ struct pim_upstream { struct thread *t_join_timer; + /* + * RST(S,G) + */ + struct thread *t_rs_timer; +#define PIM_REGISTER_SUPPRESSION_PERIOD (60) +#define PIM_REGISTER_PROBE_PERIOD (15) + /* * KAT(S,G) */ @@ -105,17 +110,23 @@ struct pim_upstream { #define PIM_KEEPALIVE_PERIOD (210) #define PIM_RP_KEEPALIVE_PERIOD ( 3 * qpim_register_suppress_time + qpim_register_probe_time ) + /* on the RP we restart a timer to indicate if registers are being rxed for + * SG. This is needed by MSDP to determine its local SA cache */ + struct thread *t_msdp_reg_timer; +#define PIM_MSDP_REG_RXED_PERIOD (3 * (1.5 * qpim_register_suppress_time)) + int64_t state_transition; /* Record current state uptime */ }; +struct list *pim_upstream_list; +struct hash *pim_upstream_hash; + void pim_upstream_free(struct pim_upstream *up); -void pim_upstream_delete(struct pim_upstream *up); -struct pim_upstream *pim_upstream_find(struct in_addr source_addr, - struct in_addr group_addr); -struct pim_upstream *pim_upstream_add(struct in_addr source_addr, - struct in_addr group_addr, - struct interface *ifp); -void pim_upstream_del(struct pim_upstream *up); +struct pim_upstream *pim_upstream_find (struct prefix_sg *sg); +struct pim_upstream *pim_upstream_add (struct prefix_sg *sg, + struct interface *ifp, int flags, + const char *name); +void pim_upstream_del(struct pim_upstream *up, const char *name); int pim_upstream_evaluate_join_desired(struct pim_upstream *up); void pim_upstream_update_join_desired(struct pim_upstream *up); @@ -136,7 +147,27 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up); void pim_upstream_keep_alive_timer_start (struct pim_upstream *up, uint32_t time); -int pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group); -#define SwitchToSptDesired(S,G) pim_upstream_switch_to_spt_desired ((S), (G)) +int pim_upstream_switch_to_spt_desired (struct prefix_sg *sg); +#define SwitchToSptDesired(sg) pim_upstream_switch_to_spt_desired (sg) +int pim_upstream_is_sg_rpt (struct pim_upstream *up); +void pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming); + +void pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register); + +void pim_upstream_send_join (struct pim_upstream *up); + +void pim_upstream_switch (struct pim_upstream *up, enum pim_upstream_state new_state); + +const char *pim_upstream_state2str (enum pim_upstream_state join_state); + +int pim_upstream_inherited_olist_decide (struct pim_upstream *up); +int pim_upstream_inherited_olist (struct pim_upstream *up); +int pim_upstream_empty_inherited_olist (struct pim_upstream *up); + +void pim_upstream_find_new_rpf (void); +void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up); + +void pim_upstream_init (void); +void pim_upstream_terminate (void); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c index f5b6a8210a..1125db00a9 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -21,6 +21,7 @@ #include #include "log.h" +#include "prefix.h" #include "pim_util.h" @@ -103,3 +104,43 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size) size); zlog_hexdump(buf, size); } + +int +pim_is_group_224_0_0_0_24 (struct in_addr group_addr) +{ + static int first = 1; + static struct prefix group_224; + struct prefix group; + + if (first) + { + str2prefix ("224.0.0.0/24", &group_224); + first = 0; + } + + group.family = AF_INET; + group.u.prefix4 = group_addr; + group.prefixlen = 32; + + return prefix_match (&group_224, &group); +} + +int +pim_is_group_224_4 (struct in_addr group_addr) +{ + static int first = 1; + static struct prefix group_all; + struct prefix group; + + if (first) + { + str2prefix ("224.0.0.0/4", &group_all); + first = 0; + } + + group.family = AF_INET; + group.u.prefix4 = group_addr; + group.prefixlen = 32; + + return prefix_match (&group_all, &group); +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h index d780bfbc27..94635466d9 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -32,4 +32,6 @@ uint16_t igmp_msg_decode8to16(uint8_t code); void pim_pkt_dump(const char *label, const uint8_t *buf, int size); +int pim_is_group_224_0_0_0_24 (struct in_addr group_addr); +int pim_is_group_224_4 (struct in_addr group_addr); #endif /* PIM_UTIL_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index f9bc2319e7..03ee7e92ec 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -22,7 +22,10 @@ #include "if.h" #include "linklist.h" +#include "prefix.h" +#include "vty.h" #include "vrf.h" +#include "plist.h" #include "pimd.h" #include "pim_vty.h" @@ -33,11 +36,26 @@ #include "pim_pim.h" #include "pim_oil.h" #include "pim_static.h" +#include "pim_rp.h" +#include "pim_msdp.h" -int pim_debug_config_write(struct vty *vty) +int +pim_debug_config_write (struct vty *vty) { int writes = 0; + if (PIM_DEBUG_MSDP_EVENTS) { + vty_out(vty, "debug msdp events%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_MSDP_PACKETS) { + vty_out(vty, "debug msdp packets%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_MSDP_INTERNAL) { + vty_out(vty, "debug msdp internal%s", VTY_NEWLINE); + ++writes; + } if (PIM_DEBUG_IGMP_EVENTS) { vty_out(vty, "debug igmp events%s", VTY_NEWLINE); ++writes; @@ -50,12 +68,21 @@ int pim_debug_config_write(struct vty *vty) vty_out(vty, "debug igmp trace%s", VTY_NEWLINE); ++writes; } + if (PIM_DEBUG_IGMP_TRACE_DETAIL) { + vty_out(vty, "debug igmp trace detail%s", VTY_NEWLINE); + ++writes; + } if (PIM_DEBUG_MROUTE) { vty_out(vty, "debug mroute%s", VTY_NEWLINE); ++writes; } + if (PIM_DEBUG_MROUTE_DETAIL) { + vty_out (vty, "debug mroute detail%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_EVENTS) { vty_out(vty, "debug pim events%s", VTY_NEWLINE); ++writes; @@ -72,10 +99,15 @@ int pim_debug_config_write(struct vty *vty) vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE); ++writes; } + if (PIM_DEBUG_PIM_TRACE) { vty_out(vty, "debug pim trace%s", VTY_NEWLINE); ++writes; } + if (PIM_DEBUG_PIM_TRACE_DETAIL) { + vty_out(vty, "debug pim trace detail%s", VTY_NEWLINE); + ++writes; + } if (PIM_DEBUG_ZEBRA) { vty_out(vty, "debug pim zebra%s", VTY_NEWLINE); @@ -87,22 +119,66 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_PIM_HELLO) { + vty_out (vty, "debug pim packets hello%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_PIM_J_P) { + vty_out (vty, "debug pim packets joins%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_PIM_REG) { + vty_out (vty, "debug pim packets register%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_STATIC) { + vty_out (vty, "debug pim static%s", VTY_NEWLINE); + ++writes; + } + return writes; } int pim_global_config_write(struct vty *vty) { int writes = 0; - char buffer[32]; + + writes += pim_msdp_config_write (vty); if (PIM_MROUTE_IS_ENABLED) { vty_out(vty, "ip multicast-routing%s", VTY_NEWLINE); ++writes; } - if (qpim_rp.rpf_addr.s_addr != INADDR_NONE) { - vty_out(vty, "ip pim rp %s%s", inet_ntop(AF_INET, &qpim_rp.rpf_addr, buffer, 32), VTY_NEWLINE); - ++writes; - } + + writes += pim_rp_config_write (vty); + + if (qpim_register_suppress_time != PIM_REGISTER_SUPPRESSION_TIME_DEFAULT) + { + vty_out (vty, "ip pim register-suppress-time %d%s", + qpim_register_suppress_time, VTY_NEWLINE); + ++writes; + } + if (qpim_t_periodic != PIM_DEFAULT_T_PERIODIC) + { + vty_out (vty, "ip pim join-prune-interval %d%s", + qpim_t_periodic, VTY_NEWLINE); + ++writes; + } + if (qpim_keep_alive_time != PIM_KEEPALIVE_PERIOD) + { + vty_out (vty, "ip pim keep-alive-timer %d%s", + qpim_keep_alive_time, VTY_NEWLINE); + ++writes; + } + if (qpim_packet_process != PIM_DEFAULT_PACKET_PROCESS) + { + vty_out (vty, "ip pim packets %d%s", + qpim_packet_process, VTY_NEWLINE); + ++writes; + } if (qpim_ssmpingd_list) { struct listnode *node; @@ -110,7 +186,7 @@ int pim_global_config_write(struct vty *vty) vty_out(vty, "!%s", VTY_NEWLINE); ++writes; for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { - char source_str[100]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE); ++writes; @@ -159,12 +235,30 @@ int pim_interface_config_write(struct vty *vty) vty_out(vty, "%s", VTY_NEWLINE); } + /* update source */ + if (PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) { + char src_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", pim_ifp->update_source, src_str, + sizeof(src_str)); + vty_out(vty, " ip pim use-source %s%s", src_str, VTY_NEWLINE); + ++writes; + } + /* IF ip igmp */ if (PIM_IF_TEST_IGMP(pim_ifp->options)) { vty_out(vty, " ip igmp%s", VTY_NEWLINE); ++writes; } + /* ip igmp version */ + if (pim_ifp->igmp_version != IGMP_DEFAULT_VERSION) + { + vty_out(vty, " ip igmp version %d%s", + pim_ifp->igmp_version, + VTY_NEWLINE); + ++writes; + } + /* IF ip igmp query-interval */ if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL) { @@ -177,7 +271,7 @@ int pim_interface_config_write(struct vty *vty) /* IF ip igmp query-max-response-time */ if (pim_ifp->igmp_query_max_response_time_dsec != IGMP_QUERY_MAX_RESPONSE_TIME_DSEC) { - vty_out(vty, " ip igmp query-max-response-time-dsec %d%s", + vty_out(vty, " ip igpm query-max-response-time %d%s", pim_ifp->igmp_query_max_response_time_dsec, VTY_NEWLINE); ++writes; @@ -188,10 +282,10 @@ int pim_interface_config_write(struct vty *vty) struct listnode *node; struct igmp_join *ij; for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", ij->group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", ij->source_addr, source_str, sizeof(source_str)); + inet_ntop(AF_INET, &ij->source_addr, source_str, sizeof(source_str)); vty_out(vty, " ip igmp join %s %s%s", group_str, source_str, VTY_NEWLINE); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index f2195960c4..b21da624d6 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -28,6 +28,8 @@ #include "zclient.h" #include "stream.h" #include "network.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_pim.h" @@ -171,6 +173,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, } if (!if_is_operative(ifp)) { + pim_ifchannel_delete_all(ifp); /* pim_if_addr_del_all() suffices for shutting down IGMP, but not for shutting down PIM @@ -186,6 +189,9 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, } } + if (ifp->info) + pim_if_del_vif(ifp); + return 0; } @@ -220,7 +226,7 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, { struct connected *c; struct prefix *p; - struct in_addr old = { .s_addr = 0 }; + struct pim_interface *pim_ifp; /* zebra api notifies address adds/dels events by using the same call @@ -234,10 +240,9 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, if (!c) return 0; + pim_ifp = c->ifp->info; p = c->address; - if (p->family != AF_INET) - return 0; - + if (PIM_DEBUG_ZEBRA) { char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); @@ -251,7 +256,25 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, #endif } - pim_rp_check_rp (old, p->u.prefix4); + if (p->family != AF_INET) + { + struct listnode *cnode; + struct connected *conn; + int v4addrs = 0; + + for (ALL_LIST_ELEMENTS_RO (c->ifp->connected, cnode, conn)) + { + if (conn->address->family == AF_INET) + v4addrs++; + } + if (!v4addrs && pim_ifp) + { + pim_ifp->primary_address = pim_find_primary_addr (c->ifp); + pim_if_addr_add_all (c->ifp); + pim_if_add_vif (c->ifp); + } + return 0; + } if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) { /* trying to add primary address */ @@ -262,20 +285,32 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, /* but we had a primary address already */ char buf[BUFSIZ]; - char old[100]; prefix2str(p, buf, BUFSIZ); - pim_inet4_dump("", primary_addr, old, sizeof(old)); - zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s", + zlog_warn("%s: %s : forcing secondary flag on %s", __PRETTY_FUNCTION__, - c->ifp->name, old, buf); + c->ifp->name, buf); } SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); } } pim_if_addr_add(c); + if (pim_ifp) + pim_rp_check_on_if_add(pim_ifp); + + if (if_is_loopback (c->ifp)) + { + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) + { + if (!if_is_loopback (ifp) && if_is_operative (ifp)) + pim_if_addr_add_all (ifp); + } + } return 0; } @@ -285,7 +320,6 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, { struct connected *c; struct prefix *p; - struct in_addr new = { .s_addr = 0 }; /* zebra api notifies address adds/dels events by using the same call @@ -316,8 +350,9 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, #endif } - pim_rp_check_rp (p->u.prefix4, new); pim_if_addr_del(c, 0); + pim_rp_setup(); + pim_i_am_rp_re_evaluate(); return 0; } @@ -328,18 +363,37 @@ static void scan_upstream_rpf_cache() struct listnode *up_nextnode; struct pim_upstream *up; - for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) { struct in_addr old_rpf_addr; + struct interface *old_interface; enum pim_rpf_result rpf_result; - rpf_result = pim_rpf_update(up, &old_rpf_addr, NULL); + old_interface = up->rpf.source_nexthop.interface; + rpf_result = pim_rpf_update(up, &old_rpf_addr); if (rpf_result == PIM_RPF_FAILURE) continue; if (rpf_result == PIM_RPF_CHANGED) { - + + /* + * We have detected a case where we might need to rescan + * the inherited o_list so do it. + */ + if (up->channel_oil->oil_inherited_rescan) + { + pim_upstream_inherited_olist_decide (up); + up->channel_oil->oil_inherited_rescan = 0; + } + if (up->join_state == PIM_UPSTREAM_JOINED) { - + /* + * If we come up real fast we can be here + * where the mroute has not been installed + * so install it. + */ + if (!up->channel_oil->installed) + pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__); + /* RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages @@ -356,17 +410,13 @@ static void scan_upstream_rpf_cache() /* send Prune(S,G) to the old upstream neighbor */ - pim_joinprune_send(up->rpf.source_nexthop.interface, - old_rpf_addr, - up->source_addr, - up->group_addr, - 0 /* prune */); + pim_joinprune_send(old_interface, old_rpf_addr, + up, 0 /* prune */); /* send Join(S,G) to the current upstream neighbor */ pim_joinprune_send(up->rpf.source_nexthop.interface, - up->rpf.rpf_addr, - up->source_addr, - up->group_addr, + up->rpf.rpf_addr.u.prefix4, + up, 1 /* join */); pim_upstream_join_timer_restart(up); @@ -389,7 +439,7 @@ pim_scan_individual_oil (struct channel_oil *c_oil) int input_iface_vif_index; int old_vif_index; - if (!pim_rp_set_upstream_addr (&vif_source, c_oil->oil.mfcc_origin)) + if (!pim_rp_set_upstream_addr (&vif_source, c_oil->oil.mfcc_origin, c_oil->oil.mfcc_mcastgrp)) return; input_iface_vif_index = fib_lookup_if_vif_index (vif_source); @@ -397,20 +447,23 @@ pim_scan_individual_oil (struct channel_oil *c_oil) { if (PIM_DEBUG_ZEBRA) { - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_debug("%s %s: could not find input interface(%d) for (S,G)=(%s,%s)", __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent, source_str, group_str); } - pim_mroute_del (c_oil); + pim_mroute_del (c_oil, __PRETTY_FUNCTION__); return; } if (input_iface_vif_index == c_oil->oil.mfcc_parent) { + if (!c_oil->installed) + pim_mroute_add (c_oil, __PRETTY_FUNCTION__); + /* RPF unchanged */ return; } @@ -419,15 +472,15 @@ pim_scan_individual_oil (struct channel_oil *c_oil) { struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, - new_iif ? new_iif->name : "", input_iface_vif_index); + old_iif->name, c_oil->oil.mfcc_parent, + new_iif->name, input_iface_vif_index); } /* new iif loops to existing oif ? */ @@ -436,39 +489,41 @@ pim_scan_individual_oil (struct channel_oil *c_oil) struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); if (PIM_DEBUG_ZEBRA) { - char source_str[100]; - char group_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - new_iif ? new_iif->name : "", input_iface_vif_index); + new_iif->name, input_iface_vif_index); } - del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY); + //del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY); } /* update iif vif_index */ old_vif_index = c_oil->oil.mfcc_parent; c_oil->oil.mfcc_parent = input_iface_vif_index; - zlog_debug ("FF"); /* update kernel multicast forwarding cache (MFC) */ - if (pim_mroute_add(c_oil)) + if (pim_mroute_add(c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index); - struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str, - old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, - new_iif ? new_iif->name : "", input_iface_vif_index); + if (PIM_DEBUG_MROUTE) + { + /* just log warning */ + struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index); + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", input_iface_vif_index); + } } } @@ -481,13 +536,12 @@ void pim_scan_oil() qpim_scan_oil_last = pim_time_monotonic_sec(); ++qpim_scan_oil_events; - for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) + for (ALL_LIST_ELEMENTS(pim_channel_oil_list, node, nextnode, c_oil)) pim_scan_individual_oil (c_oil); } static int on_rpf_cache_refresh(struct thread *t) { - zassert(t); zassert(qpim_rpf_cache_refresher); qpim_rpf_cache_refresher = 0; @@ -501,13 +555,16 @@ static int on_rpf_cache_refresh(struct thread *t) qpim_rpf_cache_refresh_last = pim_time_monotonic_sec(); ++qpim_rpf_cache_refresh_events; + pim_rp_setup (); return 0; } -static void sched_rpf_cache_refresh() +void sched_rpf_cache_refresh(void) { ++qpim_rpf_cache_refresh_requests; + pim_rpf_set_refresh_time (); + if (qpim_rpf_cache_refresher) { /* Refresh timer is already running */ return; @@ -575,17 +632,6 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); } - if (length < min_len) { - zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s", - __FILE__, __PRETTY_FUNCTION__, - length, min_len, - CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "", - CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "", - CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "", - CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); - return -1; - } - /* IPv4 prefix. */ stream_get(&p.prefix, s, PSIZE(p.prefixlen)); @@ -654,6 +700,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, sched_rpf_cache_refresh(); + pim_rp_setup (); return 0; } @@ -662,6 +709,7 @@ pim_zebra_connected (struct zclient *zclient) { zclient_send_reg_requests (zclient, VRF_DEFAULT); } + void pim_zebra_init(char *zebra_sock_path) { int i; @@ -719,9 +767,7 @@ void pim_zebra_init(char *zebra_sock_path) __PRETTY_FUNCTION__); } - zassert(!qpim_zclient_lookup); - qpim_zclient_lookup = zclient_lookup_new(); - zassert(qpim_zclient_lookup); + zclient_lookup_new(); } void igmp_anysource_forward_start(struct igmp_group *group) @@ -754,36 +800,42 @@ void igmp_anysource_forward_stop(struct igmp_group *group) static int fib_lookup_if_vif_index(struct in_addr addr) { - struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM]; int num_ifindex; int vif_index; ifindex_t first_ifindex; - num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, - PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr, + num_ifindex = zclient_lookup_nexthop(nexthop_tab, + MULTIPATH_NUM, addr, PIM_NEXTHOP_LOOKUP_MAX); if (num_ifindex < 1) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: could not find nexthop ifindex for address %s", - __FILE__, __PRETTY_FUNCTION__, - addr_str); + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + } return -1; } first_ifindex = nexthop_tab[0].ifindex; if (num_ifindex > 1) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", - __FILE__, __PRETTY_FUNCTION__, - num_ifindex, addr_str, first_ifindex); + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", + __FILE__, __PRETTY_FUNCTION__, + num_ifindex, addr_str, first_ifindex); + } /* debug warning only, do not return */ } if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s", __FILE__, __PRETTY_FUNCTION__, @@ -793,31 +845,17 @@ static int fib_lookup_if_vif_index(struct in_addr addr) vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex); if (vif_index < 0) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s", - __FILE__, __PRETTY_FUNCTION__, - vif_index, addr_str); + if (PIM_DEBUG_ZEBRA) + { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: low vif_index=%d < 1 nexthop for address %s", + __FILE__, __PRETTY_FUNCTION__, + vif_index, addr_str); + } return -2; } - zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); - - if (vif_index > qpim_mroute_oif_highest_vif_index) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s", - __FILE__, __PRETTY_FUNCTION__, - vif_index, qpim_mroute_oif_highest_vif_index, addr_str); - - zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?", - __FILE__, __PRETTY_FUNCTION__, - ifindex2ifname(vif_index), - vif_index); - - return -3; - } - return vif_index; } @@ -828,17 +866,11 @@ static int del_oif(struct channel_oil *channel_oil, struct pim_interface *pim_ifp; int old_ttl; - zassert(channel_oil); - pim_ifp = oif->info; - zassert(pim_ifp->mroute_vif_index >= 1); - zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); - zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); - if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", @@ -850,15 +882,18 @@ static int del_oif(struct channel_oil *channel_oil, /* Prevent single protocol from unsubscribing same interface from channel (S,G) multiple times */ if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - proto_mask, oif->name, pim_ifp->mroute_vif_index, - channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], - source_str, group_str); + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } return -2; } @@ -873,15 +908,18 @@ static int del_oif(struct channel_oil *channel_oil, /* Check the OIF keeps existing before returning, and only log warning otherwise */ if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - proto_mask, oif->name, pim_ifp->mroute_vif_index, - channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], - source_str, group_str); + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } } return 0; @@ -890,22 +928,25 @@ static int del_oif(struct channel_oil *channel_oil, old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]; if (old_ttl < 1) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - oif->name, pim_ifp->mroute_vif_index, - source_str, group_str); + if (PIM_DEBUG_MROUTE) + { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + } return -3; } channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; - if (pim_mroute_add(channel_oil)) { - char group_str[100]; - char source_str[100]; + if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)", @@ -920,21 +961,24 @@ static int del_oif(struct channel_oil *channel_oil, --channel_oil->oil_size; if (channel_oil->oil_size < 1) { - if (pim_mroute_del(channel_oil)) { - /* just log a warning in case of failure */ - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); + if (pim_mroute_del(channel_oil, __PRETTY_FUNCTION__)) { + if (PIM_DEBUG_MROUTE) + { + /* just log a warning in case of failure */ + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: failure removing OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } } } if (PIM_DEBUG_MROUTE) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", @@ -949,16 +993,17 @@ static int del_oif(struct channel_oil *channel_oil, void igmp_source_forward_start(struct igmp_source *source) { struct igmp_group *group; + struct prefix_sg sg; int result; + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = source->source_addr; + sg.grp = source->source_group->group_addr; + if (PIM_DEBUG_IGMP_TRACE) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __PRETTY_FUNCTION__, - source_str, group_str, + pim_str_sg_dump (&sg), source->source_group->group_igmp_sock->fd, source->source_group->group_igmp_sock->interface->name, IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); @@ -976,16 +1021,19 @@ void igmp_source_forward_start(struct igmp_source *source) struct in_addr vif_source; struct pim_interface *pim_oif; - if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr)) + if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr, sg.grp)) return; int input_iface_vif_index = fib_lookup_if_vif_index(vif_source); if (input_iface_vif_index < 1) { - char source_str[100]; - pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); - zlog_warn("%s %s: could not find input interface for source %s", - __FILE__, __PRETTY_FUNCTION__, - source_str); + if (PIM_DEBUG_IGMP_TRACE) + { + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + } return; } @@ -996,28 +1044,21 @@ void igmp_source_forward_start(struct igmp_source *source) */ pim_oif = source->source_group->group_igmp_sock->interface->info; if (!pim_oif) { - zlog_warn("%s: multicast not enabled on oif=%s ?", - __PRETTY_FUNCTION__, - source->source_group->group_igmp_sock->interface->name); - return; - } - if (pim_oif->mroute_vif_index < 1) { - zlog_warn("%s %s: oif=%s vif_index=%d < 1", - __FILE__, __PRETTY_FUNCTION__, - source->source_group->group_igmp_sock->interface->name, - pim_oif->mroute_vif_index); + if (PIM_DEBUG_IGMP_TRACE) + { + zlog_debug("%s: multicast not enabled on oif=%s ?", + __PRETTY_FUNCTION__, + source->source_group->group_igmp_sock->interface->name); + } return; } + if (input_iface_vif_index == pim_oif->mroute_vif_index) { /* ignore request for looped MFC entry */ if (PIM_DEBUG_IGMP_TRACE) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d", + zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d", __PRETTY_FUNCTION__, - source_str, group_str, + pim_str_sg_dump (&sg), source->source_group->group_igmp_sock->fd, source->source_group->group_igmp_sock->interface->name, input_iface_vif_index); @@ -1025,17 +1066,15 @@ void igmp_source_forward_start(struct igmp_source *source) return; } - source->source_channel_oil = pim_channel_oil_add(group->group_addr, - source->source_addr, + source->source_channel_oil = pim_channel_oil_add(&sg, input_iface_vif_index); if (!source->source_channel_oil) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); - zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); + if (PIM_DEBUG_IGMP_TRACE) + { + zlog_debug("%s %s: could not create OIL for channel (S,G)=%s", + __FILE__, __PRETTY_FUNCTION__, + pim_str_sg_dump (&sg)); + } return; } } @@ -1044,8 +1083,11 @@ void igmp_source_forward_start(struct igmp_source *source) group->group_igmp_sock->interface, PIM_OIF_FLAG_PROTO_IGMP); if (result) { - zlog_warn("%s: add_oif() failed with return=%d", - __func__, result); + if (PIM_DEBUG_MROUTE) + { + zlog_warn("%s: add_oif() failed with return=%d", + __func__, result); + } return; } @@ -1053,8 +1095,7 @@ void igmp_source_forward_start(struct igmp_source *source) Feed IGMPv3-gathered local membership information into PIM per-interface (S,G) state. */ - pim_ifchannel_local_membership_add(group->group_igmp_sock->interface, - source->source_addr, group->group_addr); + pim_ifchannel_local_membership_add(group->group_igmp_sock->interface, &sg); IGMP_SOURCE_DO_FORWARDING(source->source_flags); } @@ -1066,16 +1107,17 @@ void igmp_source_forward_start(struct igmp_source *source) void igmp_source_forward_stop(struct igmp_source *source) { struct igmp_group *group; + struct prefix_sg sg; int result; + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = source->source_addr; + sg.grp = source->source_group->group_addr; + if (PIM_DEBUG_IGMP_TRACE) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + zlog_debug("%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __PRETTY_FUNCTION__, - source_str, group_str, + pim_str_sg_dump (&sg), source->source_group->group_igmp_sock->fd, source->source_group->group_igmp_sock->interface->name, IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); @@ -1104,8 +1146,9 @@ void igmp_source_forward_stop(struct igmp_source *source) group->group_igmp_sock->interface, PIM_OIF_FLAG_PROTO_IGMP); if (result) { - zlog_warn("%s: del_oif() failed with return=%d", - __func__, result); + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug("%s: del_oif() failed with return=%d", + __func__, result); return; } @@ -1114,7 +1157,7 @@ void igmp_source_forward_stop(struct igmp_source *source) per-interface (S,G) state. */ pim_ifchannel_local_membership_del(group->group_igmp_sock->interface, - source->source_addr, group->group_addr); + &sg); IGMP_SOURCE_DONT_FORWARDING(source->source_flags); } @@ -1124,12 +1167,12 @@ void pim_forward_start(struct pim_ifchannel *ch) struct pim_upstream *up = ch->upstream; if (PIM_DEBUG_PIM_TRACE) { - char source_str[100]; - char group_str[100]; - char upstream_str[100]; + char source_str[INET_ADDRSTRLEN]; + char group_str[INET_ADDRSTRLEN]; + char upstream_str[INET_ADDRSTRLEN]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ch->sg.src, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->sg.grp, group_str, sizeof(group_str)); pim_inet4_dump("", up->upstream_addr, upstream_str, sizeof(upstream_str)); zlog_debug("%s: (S,G)=(%s,%s) oif=%s(%s)", __PRETTY_FUNCTION__, @@ -1139,24 +1182,24 @@ void pim_forward_start(struct pim_ifchannel *ch) if (!up->channel_oil) { int input_iface_vif_index = fib_lookup_if_vif_index(up->upstream_addr); if (input_iface_vif_index < 1) { - char source_str[100]; - pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); - zlog_warn("%s %s: could not find input interface for source %s", - __FILE__, __PRETTY_FUNCTION__, - source_str); + if (PIM_DEBUG_PIM_TRACE) + { + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", up->sg.src, source_str, sizeof(source_str)); + zlog_debug("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + } return; } - up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr, + up->channel_oil = pim_channel_oil_add(&up->sg, input_iface_vif_index); if (!up->channel_oil) { - char group_str[100]; - char source_str[100]; - pim_inet4_dump("", up->group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); - zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s %s: could not create OIL for channel (S,G)=%s", + __FILE__, __PRETTY_FUNCTION__, + up->sg_str); return; } } @@ -1171,23 +1214,16 @@ void pim_forward_stop(struct pim_ifchannel *ch) struct pim_upstream *up = ch->upstream; if (PIM_DEBUG_PIM_TRACE) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: (S,G)=(%s,%s) oif=%s", + zlog_debug("%s: (S,G)=%s oif=%s", __PRETTY_FUNCTION__, - source_str, group_str, ch->interface->name); + ch->sg_str, ch->interface->name); } if (!up->channel_oil) { - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL", - __PRETTY_FUNCTION__, - source_str, group_str, ch->interface->name); + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s: (S,G)=%s oif=%s missing channel OIL", + __PRETTY_FUNCTION__, + ch->sg_str, ch->interface->name); return; } diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index 257c9b896f..0c302efbd5 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -38,4 +38,5 @@ void igmp_source_forward_stop(struct igmp_source *source); void pim_forward_start(struct pim_ifchannel *ch); void pim_forward_stop(struct pim_ifchannel *ch); +void sched_rpf_cache_refresh(void); #endif /* PIM_ZEBRA_H */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index af561a0b62..61e3e27261 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -27,13 +27,17 @@ #include "stream.h" #include "network.h" #include "thread.h" +#include "prefix.h" +#include "vty.h" #include "pimd.h" +#include "pim_iface.h" #include "pim_pim.h" #include "pim_str.h" +#include "pim_oil.h" #include "pim_zlookup.h" -extern int zclient_debug; +static struct zclient *zlookup = NULL; static void zclient_lookup_sched(struct zclient *zlookup, int delay); @@ -116,15 +120,14 @@ static void zclient_lookup_failed(struct zclient *zlookup) zclient_lookup_reconnect(zlookup); } -struct zclient *zclient_lookup_new() +void +zclient_lookup_new (void) { - struct zclient *zlookup; - zlookup = zclient_new (master); if (!zlookup) { zlog_err("%s: zclient_new() failure", __PRETTY_FUNCTION__); - return 0; + return; } zlookup->sock = -1; @@ -137,7 +140,6 @@ struct zclient *zclient_lookup_new() zlog_notice("%s: zclient lookup socket initialized", __PRETTY_FUNCTION__); - return zlookup; } static int zclient_read_nexthop(struct zclient *zlookup, @@ -159,8 +161,8 @@ static int zclient_read_nexthop(struct zclient *zlookup, int nexthop_num; int i, err; - if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + if (PIM_DEBUG_PIM_TRACE_DETAIL) { + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s: addr=%s", __PRETTY_FUNCTION__, @@ -192,8 +194,8 @@ static int zclient_read_nexthop(struct zclient *zlookup, raddr.s_addr = stream_get_ipv4(s); if (raddr.s_addr != addr.s_addr) { - char addr_str[100]; - char raddr_str[100]; + char addr_str[INET_ADDRSTRLEN]; + char raddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); pim_inet4_dump("", raddr, raddr_str, sizeof(raddr_str)); zlog_warn("%s: address mismatch: addr=%s raddr=%s", @@ -212,77 +214,52 @@ static int zclient_read_nexthop(struct zclient *zlookup, return -6; } - length -= MIN_LEN; - for (i = 0; i < nexthop_num; ++i) { enum nexthop_types_t nexthop_type; + struct pim_neighbor *nbr; - if (length < 1) { - zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d", - __func__, zlookup->sock, length); - return -7; - } - nexthop_type = stream_getc(s); - --length; - + if (num_ifindex >= tab_size) { + char addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", + __FILE__, __PRETTY_FUNCTION__, + (num_ifindex + 1), tab_size, addr_str); + return num_ifindex; + } switch (nexthop_type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IPV4_IFINDEX: - if (num_ifindex >= tab_size) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", - __FILE__, __PRETTY_FUNCTION__, - (num_ifindex + 1), tab_size, addr_str); - return num_ifindex; - } - if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX) { - if (length < 4) { - zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d", - __func__, zlookup->sock, length); - return -8; - } - nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); - length -= 4; + case NEXTHOP_TYPE_IPV4: + nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET; + if (nexthop_type == NEXTHOP_TYPE_IPV4_IFINDEX || + nexthop_type == NEXTHOP_TYPE_IPV4) { + nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = stream_get_ipv4(s); } else { - nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + nexthop_tab[num_ifindex].nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; } nexthop_tab[num_ifindex].ifindex = stream_getl(s); nexthop_tab[num_ifindex].protocol_distance = distance; nexthop_tab[num_ifindex].route_metric = metric; ++num_ifindex; break; - case NEXTHOP_TYPE_IPV4: - if (num_ifindex >= tab_size) { - char addr_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", - __FILE__, __PRETTY_FUNCTION__, - (num_ifindex + 1), tab_size, addr_str); - return num_ifindex; - } - nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); - length -= 4; - nexthop_tab[num_ifindex].ifindex = 0; - nexthop_tab[num_ifindex].protocol_distance = distance; - nexthop_tab[num_ifindex].route_metric = metric; - if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; - char nexthop_str[100]; - pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - pim_inet4_dump("", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str)); - zlog_debug("%s %s: zebra returned recursive nexthop %s for address %s", - __FILE__, __PRETTY_FUNCTION__, - nexthop_str, addr_str); - } - ++num_ifindex; + case NEXTHOP_TYPE_IPV6_IFINDEX: + nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET6; + stream_get (&nexthop_tab[num_ifindex].nexthop_addr.u.prefix6, s, 16); + nexthop_tab[num_ifindex].ifindex = stream_getl (s); + nbr = pim_neighbor_find_if (if_lookup_by_index_vrf (nexthop_tab[num_ifindex].ifindex, VRF_DEFAULT)); + if (nbr) + { + nexthop_tab[num_ifindex].nexthop_addr.family = AF_INET; + nexthop_tab[num_ifindex].nexthop_addr.u.prefix4 = nbr->source_addr; + } + ++num_ifindex; break; default: /* do nothing */ { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s", __FILE__, __PRETTY_FUNCTION__, @@ -295,16 +272,16 @@ static int zclient_read_nexthop(struct zclient *zlookup, return num_ifindex; } -static int zclient_lookup_nexthop_once(struct zclient *zlookup, - struct pim_zlookup_nexthop nexthop_tab[], - const int tab_size, - struct in_addr addr) +static int +zclient_lookup_nexthop_once (struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr) { struct stream *s; int ret; - if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + if (PIM_DEBUG_PIM_TRACE_DETAIL) { + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s: addr=%s", __PRETTY_FUNCTION__, @@ -327,8 +304,8 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup, ret = writen(zlookup->sock, s->data, stream_get_endp(s)); if (ret < 0) { - zlog_err("%s %s: writen() failure writing to zclient lookup socket", - __FILE__, __PRETTY_FUNCTION__); + zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__, errno); zclient_lookup_failed(zlookup); return -2; } @@ -343,26 +320,28 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup, tab_size, addr); } -int zclient_lookup_nexthop(struct zclient *zlookup, - struct pim_zlookup_nexthop nexthop_tab[], - const int tab_size, - struct in_addr addr, - int max_lookup) +int +zclient_lookup_nexthop (struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr, + int max_lookup) { int lookup; uint32_t route_metric = 0xFFFFFFFF; uint8_t protocol_distance = 0xFF; + qpim_nexthop_lookups++; + for (lookup = 0; lookup < max_lookup; ++lookup) { int num_ifindex; int first_ifindex; - struct in_addr nexthop_addr; + struct prefix nexthop_addr; - num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab, - PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr); + num_ifindex = zclient_lookup_nexthop_once(nexthop_tab, + tab_size, addr); if (num_ifindex < 1) { if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s", __FILE__, __PRETTY_FUNCTION__, @@ -378,9 +357,13 @@ int zclient_lookup_nexthop(struct zclient *zlookup, } /* - FIXME: Non-recursive nexthop ensured only for first ifindex. - However, recursive route lookup should really be fixed in zebra daemon. - See also TODO T24. + * FIXME: Non-recursive nexthop ensured only for first ifindex. + * However, recursive route lookup should really be fixed in zebra daemon. + * See also TODO T24. + * + * So Zebra for NEXTHOP_TYPE_IPV4 returns the ifindex now since + * it was being stored. This Doesn't solve all cases of + * recursive lookup but for the most common types it does. */ first_ifindex = nexthop_tab[0].ifindex; nexthop_addr = nexthop_tab[0].nexthop_addr; @@ -390,7 +373,7 @@ int zclient_lookup_nexthop(struct zclient *zlookup, if (lookup > 0) { /* Report non-recursive success after first lookup */ if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_debug("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d", __FILE__, __PRETTY_FUNCTION__, @@ -400,7 +383,7 @@ int zclient_lookup_nexthop(struct zclient *zlookup, } /* use last address as nexthop address */ - nexthop_tab[0].nexthop_addr = addr; + nexthop_tab[0].nexthop_addr.u.prefix4 = addr; /* report original route metric/distance */ nexthop_tab[0].route_metric = route_metric; @@ -411,10 +394,10 @@ int zclient_lookup_nexthop(struct zclient *zlookup, } if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; - char nexthop_str[100]; + char addr_str[INET_ADDRSTRLEN]; + char nexthop_str[PREFIX_STRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - pim_inet4_dump("", nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_addr_dump("", &nexthop_addr, nexthop_str, sizeof(nexthop_str)); zlog_debug("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d", __FILE__, __PRETTY_FUNCTION__, lookup, max_lookup, nexthop_str, addr_str, @@ -422,12 +405,12 @@ int zclient_lookup_nexthop(struct zclient *zlookup, nexthop_tab[0].route_metric); } - addr = nexthop_addr; /* use nexthop addr for recursive lookup */ + addr = nexthop_addr.u.prefix4; /* use nexthop addr for recursive lookup */ } /* for (max_lookup) */ if (PIM_DEBUG_ZEBRA) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s", __FILE__, __PRETTY_FUNCTION__, @@ -436,3 +419,100 @@ int zclient_lookup_nexthop(struct zclient *zlookup, return -2; } + +void +pim_zlookup_show_ip_multicast (struct vty *vty) +{ + vty_out(vty, "Zclient lookup socket: "); + if (zlookup) { + vty_out(vty, "%d failures=%d%s", zlookup->sock, + zlookup->fail, VTY_NEWLINE); + } + else { + vty_out(vty, "%s", VTY_NEWLINE); + } +} + +int +pim_zlookup_sg_statistics (struct channel_oil *c_oil) +{ + struct stream *s = zlookup->obuf; + uint16_t command = 0; + unsigned long long lastused; + struct prefix_sg sg; + int count = 0; + int ret; + struct interface *ifp = pim_if_find_by_vif_index (c_oil->oil.mfcc_parent); + + if (PIM_DEBUG_ZEBRA) + { + struct prefix_sg more; + + more.src = c_oil->oil.mfcc_origin; + more.grp = c_oil->oil.mfcc_mcastgrp; + zlog_debug ("Sending Request for New Channel Oil Information(%s)", pim_str_sg_dump (&more)); + } + + if (!ifp) + return -1; + + stream_reset (s); + zclient_create_header (s, ZEBRA_IPMR_ROUTE_STATS, VRF_DEFAULT); + stream_put_in_addr (s, &c_oil->oil.mfcc_origin); + stream_put_in_addr (s, &c_oil->oil.mfcc_mcastgrp); + stream_putl (s, ifp->ifindex); + stream_putw_at(s, 0, stream_get_endp(s)); + + count = stream_get_endp (s); + ret = writen (zlookup->sock, s->data, count); + if (ret <= 0) + { + zlog_err("%s %s: writen() failure: %d writing to zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__, errno); + return -1; + } + + s = zlookup->ibuf; + + while (command != ZEBRA_IPMR_ROUTE_STATS) + { + int err; + uint16_t length = 0; + vrf_id_t vrf_id; + u_char marker; + u_char version; + + stream_reset (s); + err = zclient_read_header (s, zlookup->sock, &length, &marker, &version, + &vrf_id, &command); + if (err < 0) + { + zlog_err ("%s %s: zclient_read_header() failed", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_failed(zlookup); + return -1; + } + } + + sg.src.s_addr = stream_get_ipv4 (s); + sg.grp.s_addr = stream_get_ipv4 (s); + if (sg.src.s_addr != c_oil->oil.mfcc_origin.s_addr || + sg.grp.s_addr != c_oil->oil.mfcc_mcastgrp.s_addr) + { + zlog_err ("%s: Received wrong %s information", + __PRETTY_FUNCTION__, pim_str_sg_dump (&sg)); + zclient_lookup_failed (zlookup); + return -3; + } + + stream_get (&lastused, s, sizeof (lastused)); + ret = stream_getl (s); + + if (PIM_DEBUG_ZEBRA) + zlog_debug ("Received %lld for %s success: %d", lastused, pim_str_sg_dump (&sg), ret); + + c_oil->cc.lastused = lastused; + + return 0; + +} diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h index dbce92647b..d8e7ff9e0d 100644 --- a/pimd/pim_zlookup.h +++ b/pimd/pim_zlookup.h @@ -28,18 +28,20 @@ #define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */ struct pim_zlookup_nexthop { - struct in_addr nexthop_addr; + struct prefix nexthop_addr; ifindex_t ifindex; uint32_t route_metric; uint8_t protocol_distance; }; -struct zclient *zclient_lookup_new(void); +void zclient_lookup_new (void); -int zclient_lookup_nexthop(struct zclient *zlookup, - struct pim_zlookup_nexthop nexthop_tab[], +int zclient_lookup_nexthop(struct pim_zlookup_nexthop nexthop_tab[], const int tab_size, struct in_addr addr, int max_lookup); +void pim_zlookup_show_ip_multicast (struct vty *vty); + +int pim_zlookup_sg_statistics (struct channel_oil *c_oil); #endif /* PIM_ZLOOKUP_H */ diff --git a/pimd/pimd.c b/pimd/pimd.c index 15e52afc1d..1627c4048d 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -23,6 +23,9 @@ #include "log.h" #include "memory.h" #include "if.h" +#include "prefix.h" +#include "vty.h" +#include "plist.h" #include "pimd.h" #include "pim_cmd.h" @@ -35,6 +38,7 @@ #include "pim_rpf.h" #include "pim_ssmpingd.h" #include "pim_static.h" +#include "pim_rp.h" const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; @@ -45,21 +49,17 @@ struct thread_master *master = NULL; uint32_t qpim_debugs = 0; int qpim_mroute_socket_fd = -1; int64_t qpim_mroute_socket_creation = 0; /* timestamp of creation */ -struct thread *qpim_mroute_socket_reader = 0; int qpim_mroute_oif_highest_vif_index = -1; -struct list *qpim_channel_oil_list = 0; int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */ -struct list *qpim_upstream_list = 0; -struct zclient *qpim_zclient_update = 0; -struct zclient *qpim_zclient_lookup = 0; +struct zclient *qpim_zclient_update = NULL; struct pim_assert_metric qpim_infinite_assert_metric; -long qpim_rpf_cache_refresh_delay_msec = 10000; -struct thread *qpim_rpf_cache_refresher = 0; +long qpim_rpf_cache_refresh_delay_msec = 50; +struct thread *qpim_rpf_cache_refresher = NULL; int64_t qpim_rpf_cache_refresh_requests = 0; int64_t qpim_rpf_cache_refresh_events = 0; int64_t qpim_rpf_cache_refresh_last = 0; struct in_addr qpim_inaddr_any; -struct list *qpim_ssmpingd_list = 0; +struct list *qpim_ssmpingd_list = NULL; struct in_addr qpim_ssmpingd_group_addr; int64_t qpim_scan_oil_events = 0; int64_t qpim_scan_oil_last = 0; @@ -67,8 +67,11 @@ int64_t qpim_mroute_add_events = 0; int64_t qpim_mroute_add_last = 0; int64_t qpim_mroute_del_events = 0; int64_t qpim_mroute_del_last = 0; -struct list *qpim_static_route_list = 0; -struct pim_rpf qpim_rp = { .rpf_addr.s_addr = INADDR_NONE }; +struct list *qpim_static_route_list = NULL; +unsigned int qpim_keep_alive_time = PIM_KEEPALIVE_PERIOD; +signed int qpim_rp_keep_alive_time = 0; +int64_t qpim_nexthop_lookups = 0; +int qpim_packet_process = PIM_DEFAULT_PACKET_PROCESS; int32_t qpim_register_suppress_time = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT; int32_t qpim_register_probe_time = PIM_REGISTER_PROBE_TIME_DEFAULT; @@ -77,22 +80,28 @@ static void pim_free() { pim_ssmpingd_destroy(); - if (qpim_channel_oil_list) - list_free(qpim_channel_oil_list); + pim_oil_terminate (); - if (qpim_upstream_list) - list_free(qpim_upstream_list); + pim_upstream_terminate (); if (qpim_static_route_list) list_free(qpim_static_route_list); pim_route_map_terminate(); + + pim_if_terminate (); + pim_rp_free (); + pim_route_map_terminate(); } void pim_init() { srandom(time(NULL)); + qpim_rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD; + + pim_rp_init (); + if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) { zlog_err("%s %s: could not solve %s to group address: errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, @@ -101,22 +110,9 @@ void pim_init() return; } - qpim_channel_oil_list = list_new(); - if (!qpim_channel_oil_list) { - zlog_err("%s %s: failure: channel_oil_list=list_new()", - __FILE__, __PRETTY_FUNCTION__); - return; - } - qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free; + pim_oil_init (); - qpim_upstream_list = list_new(); - if (!qpim_upstream_list) { - zlog_err("%s %s: failure: upstream_list=list_new()", - __FILE__, __PRETTY_FUNCTION__); - pim_free(); - return; - } - qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free; + pim_upstream_init (); qpim_static_route_list = list_new(); if (!qpim_static_route_list) { @@ -129,9 +125,6 @@ void pim_init() qpim_mroute_socket_fd = -1; /* mark mroute as disabled */ qpim_mroute_oif_highest_vif_index = -1; - zassert(!qpim_debugs); - zassert(!PIM_MROUTE_IS_ENABLED); - qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY; /* diff --git a/pimd/pimd.h b/pimd/pimd.h index 2230a6ce91..20cf3c2dd2 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -23,13 +23,13 @@ #include +#include "pim_str.h" #include "pim_memory.h" #include "pim_assert.h" #define PIMD_PROGNAME "pimd" #define PIMD_DEFAULT_CONFIG "pimd.conf" #define PIMD_VTY_PORT 2611 -#define PIMD_BUG_ADDRESS "https://github.com/udhos/qpimd" #define PIM_IP_HEADER_MIN_LEN (20) #define PIM_IP_HEADER_MAX_LEN (60) @@ -51,6 +51,8 @@ #define PIM_INADDR_IS_ANY(addr) (addr).s_addr == PIM_NET_INADDR_ANY #define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */ +#define max(x,y) ((x) > (y) ? (x) : (y)) + #define PIM_MASK_PIM_EVENTS (1 << 0) #define PIM_MASK_PIM_EVENTS_DETAIL (1 << 1) #define PIM_MASK_PIM_PACKETS (1 << 2) @@ -65,9 +67,28 @@ #define PIM_MASK_ZEBRA (1 << 11) #define PIM_MASK_SSMPINGD (1 << 12) #define PIM_MASK_MROUTE (1 << 13) -#define PIM_MASK_PIM_HELLO (1 << 14) -#define PIM_MASK_PIM_J_P (1 << 15) -#define PIM_MASK_STATIC (1 << 16) +#define PIM_MASK_MROUTE_DETAIL (1 << 14) +#define PIM_MASK_PIM_HELLO (1 << 15) +#define PIM_MASK_PIM_J_P (1 << 16) +#define PIM_MASK_STATIC (1 << 17) +#define PIM_MASK_PIM_REG (1 << 18) +#define PIM_MASK_MSDP_EVENTS (1 << 19) +#define PIM_MASK_MSDP_PACKETS (1 << 20) +#define PIM_MASK_MSDP_INTERNAL (1 << 21) + + +/* PIM error codes */ +#define PIM_SUCCESS 0 +#define PIM_MALLOC_FAIL -1 +#define PIM_GROUP_BAD_ADDRESS -2 +#define PIM_GROUP_OVERLAP -3 +#define PIM_GROUP_PFXLIST_OVERLAP -4 +#define PIM_RP_BAD_ADDRESS -5 +#define PIM_RP_NO_PATH -6 +#define PIM_RP_NOT_FOUND -7 +#define PIM_RP_PFXLIST_IN_USE -8 +#define PIM_IFACE_NOT_FOUND -9 +#define PIM_UPDATE_SOURCE_DUP -10 const char *const PIM_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS; @@ -78,14 +99,10 @@ extern struct thread_master *master; uint32_t qpim_debugs; int qpim_mroute_socket_fd; int64_t qpim_mroute_socket_creation; /* timestamp of creation */ -struct thread *qpim_mroute_socket_reader; int qpim_mroute_oif_highest_vif_index; -struct list *qpim_channel_oil_list; /* list of struct channel_oil */ struct in_addr qpim_all_pim_routers_addr; int qpim_t_periodic; /* Period between Join/Prune Messages */ -struct list *qpim_upstream_list; /* list of struct pim_upstream */ struct zclient *qpim_zclient_update; -struct zclient *qpim_zclient_lookup; struct pim_assert_metric qpim_infinite_assert_metric; long qpim_rpf_cache_refresh_delay_msec; struct thread *qpim_rpf_cache_refresher; @@ -101,8 +118,12 @@ int64_t qpim_mroute_add_events; int64_t qpim_mroute_add_last; int64_t qpim_mroute_del_events; int64_t qpim_mroute_del_last; +int64_t qpim_nexthop_lookups; struct list *qpim_static_route_list; /* list of routes added statically */ -struct pim_rpf qpim_rp; +extern unsigned int qpim_keep_alive_time; +extern signed int qpim_rp_keep_alive_time; +extern int qpim_packet_process; +#define PIM_DEFAULT_PACKET_PROCESS 3 #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) @@ -132,12 +153,17 @@ extern int32_t qpim_register_probe_time; #define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) #define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_MROUTE (qpim_debugs & PIM_MASK_MROUTE) +#define PIM_DEBUG_MROUTE_DETAIL (qpim_debugs & PIM_MASK_MROUTE_DETAIL) #define PIM_DEBUG_PIM_HELLO (qpim_debugs & PIM_MASK_PIM_HELLO) #define PIM_DEBUG_PIM_J_P (qpim_debugs & PIM_MASK_PIM_J_P) +#define PIM_DEBUG_PIM_REG (qpim_debugs & PIM_MASK_PIM_REG) #define PIM_DEBUG_STATIC (qpim_debugs & PIM_MASK_STATIC) +#define PIM_DEBUG_MSDP_EVENTS (qpim_debugs & PIM_MASK_MSDP_EVENTS) +#define PIM_DEBUG_MSDP_PACKETS (qpim_debugs & PIM_MASK_MSDP_PACKETS) +#define PIM_DEBUG_MSDP_INTERNAL (qpim_debugs & PIM_MASK_MSDP_INTERNAL) -#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) -#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) +#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS)) +#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS)) #define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE)) #define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS) @@ -152,9 +178,14 @@ extern int32_t qpim_register_probe_time; #define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) #define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) #define PIM_DO_DEBUG_MROUTE (qpim_debugs |= PIM_MASK_MROUTE) +#define PIM_DO_DEBUG_MROUTE_DETAIL (qpim_debugs |= PIM_MASK_MROUTE_DETAIL) #define PIM_DO_DEBUG_PIM_HELLO (qpim_debugs |= PIM_MASK_PIM_HELLO) #define PIM_DO_DEBUG_PIM_J_P (qpim_debugs |= PIM_MASK_PIM_J_P) +#define PIM_DO_DEBUG_PIM_REG (qpim_debugs |= PIM_MASK_PIM_REG) #define PIM_DO_DEBUG_STATIC (qpim_debugs |= PIM_MASK_STATIC) +#define PIM_DO_DEBUG_MSDP_EVENTS (qpim_debugs |= PIM_MASK_MSDP_EVENTS) +#define PIM_DO_DEBUG_MSDP_PACKETS (qpim_debugs |= PIM_MASK_MSDP_PACKETS) +#define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL) #define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) @@ -168,9 +199,14 @@ extern int32_t qpim_register_probe_time; #define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) #define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) #define PIM_DONT_DEBUG_MROUTE (qpim_debugs &= ~PIM_MASK_MROUTE) +#define PIM_DONT_DEBUG_MROUTE_DETAIL (qpim_debugs &= ~PIM_MASK_MROUTE_DETAIL) #define PIM_DONT_DEBUG_PIM_HELLO (qpim_debugs &= ~PIM_MASK_PIM_HELLO) #define PIM_DONT_DEBUG_PIM_J_P (qpim_debugs &= ~PIM_MASK_PIM_J_P) +#define PIM_DONT_DEBUG_PIM_REG (qpim_debugs &= ~PIM_MASK_PIM_REG) #define PIM_DONT_DEBUG_STATIC (qpim_debugs &= ~PIM_MASK_STATIC) +#define PIM_DONT_DEBUG_MSDP_EVENTS (qpim_debugs &= ~PIM_MASK_MSDP_EVENTS) +#define PIM_DONT_DEBUG_MSDP_PACKETS (qpim_debugs &= ~PIM_MASK_MSDP_PACKETS) +#define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL) void pim_init(void); void pim_terminate(void); diff --git a/tests/test-timer-performance.c b/tests/test-timer-performance.c index ee45ede6ac..a7d09beecc 100644 --- a/tests/test-timer-performance.c +++ b/tests/test-timer-performance.c @@ -61,7 +61,7 @@ int main(int argc, char **argv) for (i = 0; i < SCHEDULE_TIMERS; i++) thread_cancel(timers[i]); - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_start); + monotime(&tv_start); for (i = 0; i < SCHEDULE_TIMERS; i++) { @@ -72,7 +72,7 @@ int main(int argc, char **argv) NULL, interval_msec); } - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_lap); + monotime(&tv_lap); for (i = 0; i < REMOVE_TIMERS; i++) { @@ -84,7 +84,7 @@ int main(int argc, char **argv) timers[index] = NULL; } - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_stop); + monotime(&tv_stop); t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec); t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000; diff --git a/tests/testcli.refout b/tests/testcli.refout index 088cbdfec4..8b438baee2 100644 --- a/tests/testcli.refout +++ b/tests/testcli.refout @@ -188,15 +188,11 @@ test# pat c c x % [NONE] Unknown command: pat c c x test# test# pat d -cmd8 with 2 args. -[00]: pat -[01]: d +% Command incomplete. test# pat d bar baz foo test# pat d -cmd8 with 2 args. -[00]: pat -[01]: d +% Command incomplete. test# pat d foo 1.2.3.4 cmd8 with 4 args. [00]: pat diff --git a/tools/permutations.c b/tools/permutations.c index 8db51ee037..0ca980b259 100644 --- a/tools/permutations.c +++ b/tools/permutations.c @@ -70,7 +70,7 @@ permute (struct graph_node *start) for (ALL_LIST_ELEMENTS_RO (position,ln,gnn)) { struct cmd_token *tt = gnn->data; - if (tt->type < SELECTOR_TKN) + if (tt->type < SPECIAL_TKN) fprintf (stdout, "%s ", tt->text); } fprintf (stdout, "\n"); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 50677b5685..51b5091c57 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -312,6 +312,10 @@ vtysh_execute_func (const char *line, int pager) { vtysh_execute("exit-address-family"); } + else if (saved_node == BGP_VRF_POLICY_NODE && (tried == 1)) + { + vtysh_execute("exit-vrf-policy"); + } else if ((saved_node == BGP_VNC_DEFAULTS_NODE || saved_node == BGP_VNC_NVE_GROUP_NODE || saved_node == BGP_VNC_L2_GROUP_NODE) && (tried == 1)) @@ -963,6 +967,11 @@ static struct cmd_node bgp_vnc_nve_group_node = "%s(config-router-vnc-nve-group)# " }; +static struct cmd_node bgp_vrf_policy_node = { + BGP_VRF_POLICY_NODE, + "%s(config-router-vrf-policy)# " +}; + static struct cmd_node bgp_vnc_l2_group_node = { BGP_VNC_L2_GROUP_NODE, @@ -1209,6 +1218,17 @@ DEFUNSH (VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH (VTYSH_BGPD, + vnc_vrf_policy, + vnc_vrf_policy_cmd, + "vrf-policy NAME", + "Configure a VRF policy group\n" + "Group name\n") +{ + vty->node = BGP_VRF_POLICY_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_BGPD, vnc_l2_group, vnc_l2_group_cmd, @@ -1481,6 +1501,7 @@ vtysh_exit (struct vty *vty) case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1560,6 +1581,17 @@ DEFUNSH (VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH (VTYSH_BGPD, + exit_vrf_policy, + exit_vrf_policy_cmd, + "exit-vrf-policy", + "Exit from VRF configuration mode\n") +{ + if (vty->node == BGP_VRF_POLICY_NODE) + vty->node = BGP_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_RIPD, vtysh_exit_ripd, vtysh_exit_ripd_cmd, @@ -3042,6 +3074,7 @@ vtysh_init_vty (void) install_node (&bgp_ipv4m_node, NULL); install_node (&bgp_ipv6_node, NULL); install_node (&bgp_ipv6m_node, NULL); + install_node (&bgp_vrf_policy_node, NULL); install_node (&bgp_vnc_defaults_node, NULL); install_node (&bgp_vnc_nve_group_node, NULL); install_node (&bgp_vnc_l2_group_node, NULL); @@ -3079,6 +3112,7 @@ vtysh_init_vty (void) vtysh_install_default (BGP_IPV6_NODE); vtysh_install_default (BGP_IPV6M_NODE); #if ENABLE_BGP_VNC + vtysh_install_default (BGP_VRF_POLICY_NODE); vtysh_install_default (BGP_VNC_DEFAULTS_NODE); vtysh_install_default (BGP_VNC_NVE_GROUP_NODE); vtysh_install_default (BGP_VNC_L2_GROUP_NODE); @@ -3150,6 +3184,8 @@ vtysh_init_vty (void) install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); #if defined (ENABLE_BGP_VNC) + install_element (BGP_VRF_POLICY_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_VRF_POLICY_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_exit_bgpd_cmd); @@ -3191,6 +3227,7 @@ vtysh_init_vty (void) install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd); + install_element (BGP_VRF_POLICY_NODE, &vtysh_end_all_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); @@ -3239,6 +3276,7 @@ vtysh_init_vty (void) install_element (BGP_NODE, &address_family_encapv4_cmd); install_element (BGP_NODE, &address_family_encapv6_cmd); #if defined(ENABLE_BGP_VNC) + install_element (BGP_NODE, &vnc_vrf_policy_cmd); install_element (BGP_NODE, &vnc_defaults_cmd); install_element (BGP_NODE, &vnc_nve_group_cmd); install_element (BGP_NODE, &vnc_l2_group_cmd); @@ -3256,6 +3294,7 @@ vtysh_init_vty (void) install_element (BGP_IPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); + install_element (BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd); install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd); install_element (BGP_VNC_L2_GROUP_NODE, &exit_vnc_config_cmd); diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 65927262f2..428090d488 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -44,7 +44,7 @@ zebra_SOURCES = \ irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \ zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \ - $(protobuf_srcs) \ + $(protobuf_srcs) zebra_mroute.c \ $(dev_srcs) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ @@ -60,7 +60,7 @@ noinst_HEADERS = \ rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h \ zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \ zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \ - kernel_netlink.h if_netlink.h + kernel_netlink.h if_netlink.h zebra_mroute.h zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index d553850546..660fad6530 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -173,6 +173,48 @@ netlink_to_zebra_link_type (unsigned int hwt) } } + +//Temporary Assignments to compile on older platforms. +#ifndef IFLA_BR_MAX +#define IFLA_BR_MAX 39 +#endif + +#ifndef IFLA_VXLAN_ID +#define IFLA_VXLAN_ID 1 +#endif + +#ifndef IFLA_VXLAN_LOCAL +#define IFLA_VXLAN_LOCAL 4 +#endif + +#ifndef IFLA_VXLAN_MAX +#define IFLA_VXLAN_MAX 26 +#endif + +#ifndef IFLA_BRIDGE_MAX +#define IFLA_BRIDGE_MAX 2 +#endif + +#ifndef IFLA_BRIDGE_VLAN_INFO +#define IFLA_BRIDGE_VLAN_INFO 2 +#endif + +#ifndef BRIDGE_VLAN_INFO_PVID +#define BRIDGE_VLAN_INFO_PVID (1<<1) +#endif + +#ifndef RTEXT_FILTER_BRVLAN +#define RTEXT_FILTER_BRVLAN (1<<1) +#endif + +#ifndef NTF_SELF +#define NTF_SELF 0x02 +#endif + +#ifndef IFLA_BR_VLAN_FILTERING +#define IFLA_BR_VLAN_FILTERING 7 +#endif + #define parse_rtattr_nested(tb, max, rta) \ netlink_parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)) diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index a20597882c..896ca96b48 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -60,3 +60,5 @@ int kernel_neigh_update (int a, int b, uint32_t c, char *d, int e) void kernel_init (struct zebra_ns *zns) { return; } void kernel_terminate (struct zebra_ns *zns) { return; } void route_read (struct zebra_ns *zns) { return; } + +int kernel_get_ipmr_sg_stats (void *m) { return 0; } diff --git a/zebra/rt.h b/zebra/rt.h index e56e3b8fe3..40beb6a45f 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -40,4 +40,5 @@ extern int kernel_upd_lsp (zebra_lsp_t *); extern int kernel_del_lsp (zebra_lsp_t *); extern int mpls_kernel_init (void); +extern int kernel_get_ipmr_sg_stats (void *mroute); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 913d1d5770..d2781f4c4e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -55,6 +55,8 @@ #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" +#include "zebra/zebra_mroute.h" + /* TODO - Temporary definitions, need to refine. */ #ifndef AF_MPLS @@ -88,6 +90,10 @@ #ifndef MPLS_IPTUNNEL_DST #define MPLS_IPTUNNEL_DST 1 #endif + +#ifndef NDA_MASTER +#define NDA_MASTER 9 +#endif /* End of temporary definitions */ struct gw_family_t @@ -520,15 +526,17 @@ netlink_route_change_read_unicast (struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; } +static struct mcast_route_data *mroute = NULL; + static int netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id) { int len; - unsigned long long lastused = 0; struct rtmsg *rtm; struct rtattr *tb[RTA_MAX + 1]; - struct prefix_sg sg; + struct mcast_route_data *m; + struct mcast_route_data mr; int iif = 0; int count; int oif[256]; @@ -536,10 +544,16 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h char sbuf[40]; char gbuf[40]; char oif_list[256] = "\0"; - memset (&sg, 0, sizeof (sg)); - sg.family = IANA_AFI_IPMR; vrf_id_t vrf = ns_id; + if (mroute) + m = mroute; + else + { + memset (&mr, 0, sizeof (mr)); + m = &mr; + } + rtm = NLMSG_DATA (h); len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg)); @@ -551,13 +565,13 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h iif = *(int *)RTA_DATA (tb[RTA_IIF]); if (tb[RTA_SRC]) - sg.src = *(struct in_addr *)RTA_DATA (tb[RTA_SRC]); + m->sg.src = *(struct in_addr *)RTA_DATA (tb[RTA_SRC]); if (tb[RTA_DST]) - sg.grp = *(struct in_addr *)RTA_DATA (tb[RTA_DST]); + m->sg.grp = *(struct in_addr *)RTA_DATA (tb[RTA_DST]); if ((RTA_EXPIRES <= RTA_MAX) && tb[RTA_EXPIRES]) - lastused = *(unsigned long long *)RTA_DATA (tb[RTA_EXPIRES]); + m->lastused = *(unsigned long long *)RTA_DATA (tb[RTA_EXPIRES]); if (tb[RTA_MULTIPATH]) { @@ -580,18 +594,20 @@ netlink_route_change_read_multicast (struct sockaddr_nl *snl, struct nlmsghdr *h if (IS_ZEBRA_DEBUG_KERNEL) { - strcpy (sbuf, inet_ntoa (sg.src)); - strcpy (gbuf, inet_ntoa (sg.grp)); + struct interface *ifp; + strcpy (sbuf, inet_ntoa (m->sg.src)); + strcpy (gbuf, inet_ntoa (m->sg.grp)); for (count = 0; count < oif_count; count++) { - struct interface *ifp = if_lookup_by_index_vrf (oif[count], vrf); + ifp = if_lookup_by_index_vrf (oif[count], vrf); char temp[256]; sprintf (temp, "%s ", ifp->name); strcat (oif_list, temp); } - zlog_debug ("MCAST %s (%s,%s) IIF: %d OIF: %s jiffies: %lld", - nl_msg_type_to_str (h->nlmsg_type), sbuf, gbuf, iif, oif_list, lastused); + ifp = if_lookup_by_index_vrf (iif, vrf); + zlog_debug ("MCAST %s (%s,%s) IIF: %s OIF: %s jiffies: %lld", + nl_msg_type_to_str(h->nlmsg_type), sbuf, gbuf, ifp->name, oif_list, m->lastused); } return 0; } @@ -1509,6 +1525,39 @@ skip: return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns); } +int +kernel_get_ipmr_sg_stats (void *in) +{ + int suc = 0; + struct mcast_route_data *mr = (struct mcast_route_data *)in; + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + + mroute = mr; + struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT); + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.ndm.ndm_family = AF_INET; + req.n.nlmsg_type = RTM_GETROUTE; + + addattr_l (&req.n, sizeof (req), RTA_IIF, &mroute->ifindex, 4); + addattr_l (&req.n, sizeof (req), RTA_OIF, &mroute->ifindex, 4); + addattr_l (&req.n, sizeof (req), RTA_SRC, &mroute->sg.src.s_addr, 4); + addattr_l (&req.n, sizeof (req), RTA_DST, &mroute->sg.grp.s_addr, 4); + + suc = netlink_talk (netlink_route_change_read_multicast, &req.n, &zns->netlink_cmd, zns); + + mroute = NULL; + return suc; +} + int kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new) { diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index b2c99d9813..f65ec887dd 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -416,3 +416,9 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen) /* TODO */ return 0; } + +extern int +kernel_get_ipmr_sg_stats (void *mroute) +{ + return 0; +} diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index d91cda9fb0..afa557096c 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -296,20 +296,6 @@ zfpm_state_to_str (zfpm_state_t state) } } -/* - * zfpm_get_time - */ -static time_t -zfpm_get_time (void) -{ - struct timeval tv; - - if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) - zlog_warn ("FPM: quagga_gettime failed!!"); - - return tv.tv_sec; -} - /* * zfpm_get_elapsed_time * @@ -320,7 +306,7 @@ zfpm_get_elapsed_time (time_t reference) { time_t now; - now = zfpm_get_time (); + now = monotime(NULL); if (now < reference) { @@ -1179,7 +1165,7 @@ zfpm_connect_cb (struct thread *t) */ zfpm_g->connect_calls++; zfpm_g->stats.connect_calls++; - zfpm_g->last_connect_call_time = zfpm_get_time (); + zfpm_g->last_connect_call_time = monotime(NULL); ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); if (ret >= 0) @@ -1533,7 +1519,7 @@ zfpm_clear_stats (struct vty *vty) zfpm_stop_stats_timer (); zfpm_start_stats_timer (); - zfpm_g->last_stats_clear_time = zfpm_get_time(); + zfpm_g->last_stats_clear_time = monotime(NULL); vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE); } diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c index bd171c89b2..715e250a66 100644 --- a/zebra/zebra_fpm_dt.c +++ b/zebra/zebra_fpm_dt.c @@ -42,6 +42,8 @@ #include "vrf.h" #include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" #include "zebra_fpm_private.h" diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 2c781899f4..9fffc9e611 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -28,6 +28,7 @@ #include "log.h" #include "rib.h" #include "vty.h" +#include "prefix.h" #include "zebra/zserv.h" #include "zebra/zebra_ns.h" diff --git a/zebra/zebra_mroute.c b/zebra/zebra_mroute.c new file mode 100644 index 0000000000..86356104bd --- /dev/null +++ b/zebra/zebra_mroute.c @@ -0,0 +1,68 @@ +/* zebra_mroute code + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of Quagga + * + * Quagga 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. + * + * Quagga 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "stream.h" +#include "prefix.h" +#include "vrf.h" +#include "rib.h" + +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_mroute.h" +#include "zebra/rt.h" + +int +zebra_ipmr_route_stats (struct zserv *client, int fd, u_short length, struct zebra_vrf *zvrf) +{ + struct mcast_route_data mroute; + struct stream *s; + int suc; + + char sbuf[40]; + char gbuf[40]; + + memset (&mroute, 0, sizeof (mroute)); + stream_get (&mroute.sg.src, client->ibuf, 4); + stream_get (&mroute.sg.grp, client->ibuf, 4); + mroute.ifindex = stream_getl (client->ibuf); + + strcpy (sbuf, inet_ntoa (mroute.sg.src)); + strcpy (gbuf, inet_ntoa (mroute.sg.grp)); + + suc = kernel_get_ipmr_sg_stats (&mroute); + + s = client->obuf; + + stream_reset (s); + + zserv_create_header (s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id (zvrf)); + stream_put_in_addr (s, &mroute.sg.src); + stream_put_in_addr (s, &mroute.sg.grp); + stream_put (s, &mroute.lastused, sizeof (mroute.lastused)); + stream_putl (s, suc); + + stream_putw_at (s, 0, stream_get_endp (s)); + zebra_server_send_message (client); + return 0; +} diff --git a/zebra/zebra_mroute.h b/zebra/zebra_mroute.h new file mode 100644 index 0000000000..c0bac43a81 --- /dev/null +++ b/zebra/zebra_mroute.h @@ -0,0 +1,35 @@ +/* zebra_mroute.h + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of Quagga. + * + * Quagga 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. + * + * Quagga 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 GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __ZEBRA_MROUTE_H__ +#define __ZEBRA_MROUTE_H__ + +struct mcast_route_data { + struct prefix_sg sg; + unsigned int ifindex; + unsigned long long lastused; +}; + +int zebra_ipmr_route_stats (struct zserv *client, int sock, u_short length, struct zebra_vrf *zvf); + +#endif + diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 315d4832dd..182cfe552d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -922,7 +922,7 @@ send_client (struct rnh *rnh, struct zserv *client, rnh_type_t type, vrf_id_t vr } stream_putw_at (s, 0, stream_get_endp (s)); - client->nh_last_upd_time = quagga_monotime(); + client->nh_last_upd_time = monotime(NULL); client->last_write_cmd = cmd; return zebra_server_send_message(client); } diff --git a/zebra/zserv.c b/zebra/zserv.c index f53b2270f1..6f72ad1758 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -54,6 +54,7 @@ #include "zebra/rtadv.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_fpm.h" +#include "zebra/zebra_mroute.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -100,7 +101,7 @@ zserv_flush_data(struct thread *thread) break; } - client->last_write_time = quagga_monotime(); + client->last_write_time = monotime(NULL); return 0; } @@ -134,7 +135,7 @@ zebra_server_send_message(struct zserv *client) break; } - client->last_write_time = quagga_monotime(); + client->last_write_time = monotime(NULL); return 0; } @@ -785,8 +786,6 @@ zsend_write_nexthop (struct stream *s, struct nexthop *nexthop) switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: - stream_put_in_addr (s, &nexthop->gate.ipv4); - break; case NEXTHOP_TYPE_IPV4_IFINDEX: stream_put_in_addr (s, &nexthop->gate.ipv4); stream_putl (s, nexthop->ifindex); @@ -826,7 +825,7 @@ zserv_rnh_register (struct zserv *client, int sock, u_short length, s = client->ibuf; - client->nh_reg_time = quagga_monotime(); + client->nh_reg_time = monotime(NULL); while (l < length) { @@ -914,7 +913,7 @@ zserv_rnh_unregister (struct zserv *client, int sock, u_short length, rnh = zebra_lookup_rnh(&p, zvrf_id (zvrf), type); if (rnh) { - client->nh_dereg_time = quagga_monotime(); + client->nh_dereg_time = monotime(NULL); zebra_remove_rnh_client(rnh, client, type); } } @@ -1826,7 +1825,7 @@ zebra_client_create (int sock) /* Set table number. */ client->rtm_table = zebrad.rtm_table_default; - client->connect_time = quagga_monotime(); + client->connect_time = monotime(NULL); /* Initialize flags */ for (afi = AFI_IP; afi < AFI_MAX; afi++) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) @@ -1952,7 +1951,7 @@ zebra_client_read (struct thread *thread) zlog_debug ("zebra message received [%s] %d in VRF %u", zserv_command_string (command), length, vrf_id); - client->last_read_time = quagga_monotime(); + client->last_read_time = monotime(NULL); client->last_read_cmd = command; zvrf = zebra_vrf_lookup_by_id (vrf_id); @@ -2051,6 +2050,9 @@ zebra_client_read (struct thread *thread) case ZEBRA_MPLS_LABELS_DELETE: zread_mpls_labels (command, client, length, vrf_id); break; + case ZEBRA_IPMR_ROUTE_STATS: + zebra_ipmr_route_stats (client, sock, length, zvrf); + break; default: zlog_info ("Zebra received unknown command %d", command); break; @@ -2263,7 +2265,7 @@ zserv_time_buf(time_t *time1, char *buf, int buflen) return (buf); } - now = quagga_monotime(); + now = monotime(NULL); now -= *time1; tm = gmtime(&now);