diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index d36b792e394d..3b46e7fb2bde 100644 --- a/pimd/pim_autorp.c +++ b/pimd/pim_autorp.c @@ -19,6 +19,7 @@ #include "lib/json.h" #include "pimd.h" +#include "pim_util.h" #include "pim_iface.h" #include "pim_rp.h" #include "pim_sock.h" @@ -26,61 +27,90 @@ #include "pim_autorp.h" DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP, "PIM AutoRP info"); -DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP_RP, "PIM AutoRP advertised RP info"); -DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP_CRP, "PIM AutoRP candidate RP info"); +DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP_RP, "PIM AutoRP discovered RP info"); DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP_ANNOUNCE, "PIM AutoRP announcement packet"); +DEFINE_MTYPE_STATIC(PIMD, PIM_AUTORP_GRPPFIX, "PIM AutoRP group prefix list"); static const char *PIM_AUTORP_ANNOUNCEMENT_GRP = "224.0.1.39"; static const char *PIM_AUTORP_DISCOVERY_GRP = "224.0.1.40"; static const in_port_t PIM_AUTORP_PORT = 496; -static int pim_autorp_rp_cmp(const struct pim_autorp_rp *l, - const struct pim_autorp_rp *r) +static int pim_autorp_rp_cmp(const struct pim_autorp_rp *l, const struct pim_autorp_rp *r) { return pim_addr_cmp(l->addr, r->addr); } -DECLARE_SORTLIST_UNIQ(pim_autorp_rp, struct pim_autorp_rp, list, - pim_autorp_rp_cmp); +DECLARE_SORTLIST_UNIQ(pim_autorp_rp, struct pim_autorp_rp, item, pim_autorp_rp_cmp); -static void pim_autorp_rp_free(struct pim_autorp_rp *rp) +static int pim_autorp_grppfix_cmp(const struct pim_autorp_grppfix *l, + const struct pim_autorp_grppfix *r) { - event_cancel(&rp->hold_timer); + return prefix_cmp(&l->grp, &r->grp); +} - /* Clean up installed RP info */ - if (pim_rp_del(rp->autorp->pim, rp->addr, rp->grp, - (strlen(rp->grplist) ? rp->grplist : NULL), - RP_SRC_AUTORP)) - if (PIM_DEBUG_AUTORP) - zlog_err("%s: Failed to delete RP %pI4", __func__, - &rp->addr); +DECLARE_SORTLIST_UNIQ(pim_autorp_grppfix, struct pim_autorp_grppfix, item, pim_autorp_grppfix_cmp); - XFREE(MTYPE_PIM_AUTORP_RP, rp); +static void pim_autorp_grppfix_free(struct pim_autorp_grppfix_head *head) +{ + struct pim_autorp_grppfix *grp; + + while ((grp = pim_autorp_grppfix_pop(head))) + XFREE(MTYPE_PIM_AUTORP_GRPPFIX, grp); } -static void pim_autorp_rplist_free(struct pim_autorp_rp_head *head) +static void pim_autorp_rp_free(struct pim_autorp_rp *rp, bool installed) { - struct pim_autorp_rp *rp; + event_cancel(&rp->hold_timer); - while ((rp = pim_autorp_rp_pop(head))) - pim_autorp_rp_free(rp); + /* Clean up installed RP info */ + if (installed) { + if (pim_rp_del(rp->autorp->pim, rp->addr, rp->grp, + (strlen(rp->grplist) ? rp->grplist : NULL), RP_SRC_AUTORP)) { + zlog_warn("%s: Failed to delete RP %pI4", __func__, &rp->addr); + } + + if (strlen(rp->grplist)) { + struct prefix_list *pl; + + pl = prefix_list_lookup(AFI_IP, rp->grplist); + if (pl) + prefix_list_delete(pl); + } + } + + pim_autorp_grppfix_free(&rp->grp_pfix_list); + pim_autorp_grppfix_fini(&rp->grp_pfix_list); + + XFREE(MTYPE_PIM_AUTORP_RP, rp); } -static void pim_autorp_rplist_cfree(struct pim_autorp_rp_head *head) +static void pim_autorp_rplist_free(struct pim_autorp_rp_head *head, bool installed) { struct pim_autorp_rp *rp; while ((rp = pim_autorp_rp_pop(head))) - XFREE(MTYPE_PIM_AUTORP_CRP, rp); + pim_autorp_rp_free(rp, installed); } static void pim_autorp_free(struct pim_autorp *autorp) { - pim_autorp_rplist_free(&(autorp->discovery_rp_list)); + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Freeing PIM AutoRP", __func__); + + pim_autorp_rplist_free(&(autorp->discovery_rp_list), true); pim_autorp_rp_fini(&(autorp->discovery_rp_list)); - pim_autorp_rplist_cfree(&(autorp->candidate_rp_list)); + pim_autorp_rplist_free(&(autorp->candidate_rp_list), false); pim_autorp_rp_fini(&(autorp->candidate_rp_list)); + + pim_autorp_rplist_free(&(autorp->mapping_rp_list), false); + pim_autorp_rp_fini(&(autorp->mapping_rp_list)); + + pim_autorp_rplist_free(&(autorp->advertised_rp_list), false); + pim_autorp_rp_fini(&(autorp->advertised_rp_list)); + + if (autorp->announce_pkt) + XFREE(MTYPE_PIM_AUTORP_ANNOUNCE, autorp->announce_pkt); } static bool pim_autorp_join_groups(struct interface *ifp) @@ -97,26 +127,22 @@ static bool pim_autorp_join_groups(struct interface *ifp) inet_pton(PIM_AF, PIM_AUTORP_DISCOVERY_GRP, &grp); if (pim_socket_join(autorp->sock, grp, pim_ifp->primary_address, ifp->ifindex, pim_ifp)) { - zlog_err("Failed to join group %pI4 on interface %s", &grp, - ifp->name); + zlog_warn("Failed to join group %pI4 on interface %s", &grp, ifp->name); return false; } - /* TODO: Future Mapping agent implementation - * Join announcement group for AutoRP mapping agent - * inet_pton(PIM_AF, PIM_AUTORP_ANNOUNCEMENT_GRP, &grp); - * if (pim_socket_join(pim->autorp->sock, grp, - * pim_ifp->primary_address, - * ifp->ifindex, pim_ifp)) { - * zlog_err("Failed to join group %pI4 on interface %s", - * &grp, ifp->name); - * return errno; - * } - */ + zlog_info("%s: Joined AutoRP discovery group %pPA on interface %s", __func__, &grp, + ifp->name); - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: Joined AutoRP groups on interface %s", __func__, - ifp->name); + inet_pton(PIM_AF, PIM_AUTORP_ANNOUNCEMENT_GRP, &grp); + if (pim_socket_join(pim->autorp->sock, grp, pim_ifp->primary_address, ifp->ifindex, + pim_ifp)) { + zlog_warn("Failed to join group %pI4 on interface %s", &grp, ifp->name); + return errno; + } + + zlog_info("%s: Joined AutoRP announcement group %pPA on interface %s", __func__, &grp, + ifp->name); return true; } @@ -135,31 +161,26 @@ static bool pim_autorp_leave_groups(struct interface *ifp) inet_pton(PIM_AF, PIM_AUTORP_DISCOVERY_GRP, &grp); if (pim_socket_leave(autorp->sock, grp, pim_ifp->primary_address, ifp->ifindex, pim_ifp)) { - zlog_err("Failed to leave group %pI4 on interface %s", &grp, - ifp->name); + zlog_warn("Failed to leave group %pI4 on interface %s", &grp, ifp->name); return false; } - /* TODO: Future Mapping agent implementation - * Leave announcement group for AutoRP mapping agent - * inet_pton(PIM_AF, PIM_AUTORP_ANNOUNCEMENT_GRP, &grp); - * if (pim_socket_leave(pim->autorp->sock, grp, - * pim_ifp->primary_address, - * ifp->ifindex, pim_ifp)) { - * zlog_err("Failed to leave group %pI4 on interface %s", - * &grp, ifp->name); - * return errno; - * } - */ + zlog_info("%s: Left AutoRP discovery group %pPA on interface %s", __func__, &grp, ifp->name); - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: Left AutoRP groups on interface %s", __func__, - ifp->name); + inet_pton(PIM_AF, PIM_AUTORP_ANNOUNCEMENT_GRP, &grp); + if (pim_socket_leave(pim->autorp->sock, grp, pim_ifp->primary_address, ifp->ifindex, + pim_ifp)) { + zlog_warn("Failed to leave group %pI4 on interface %s", &grp, ifp->name); + return errno; + } + + zlog_info("%s: Left AutoRP announcement group %pPA on interface %s", __func__, &grp, + ifp->name); return true; } -static bool pim_autorp_setup(struct pim_autorp *autorp) +static bool pim_autorp_setup(int fd) { #if defined(HAVE_IP_PKTINFO) int data; @@ -170,35 +191,39 @@ static bool pim_autorp_setup(struct pim_autorp *autorp) .sin_addr = { .s_addr = INADDR_ANY }, .sin_port = htons(PIM_AUTORP_PORT) }; - setsockopt_so_recvbuf(autorp->sock, 1024 * 1024 * 8); + setsockopt_so_recvbuf(fd, 1024 * 1024 * 8); #if defined(HAVE_IP_PKTINFO) /* Linux and Solaris IP_PKTINFO */ data = 1; - if (setsockopt(autorp->sock, PIM_IPPROTO, IP_PKTINFO, &data, data_len)) { - zlog_err("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - autorp->sock, errno, safe_strerror(errno)); + if (setsockopt(fd, PIM_IPPROTO, IP_PKTINFO, &data, data_len)) { + zlog_warn("%s: Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", __func__, + fd, errno, safe_strerror(errno)); return false; } #endif - if (set_nonblocking(autorp->sock) < 0) { - zlog_err("Could not set non blocking on socket fd=%d: errno=%d: %s", - autorp->sock, errno, safe_strerror(errno)); + if (set_nonblocking(fd) < 0) { + zlog_warn("%s: Could not set non blocking on socket fd=%d: errno=%d: %s", __func__, + fd, errno, safe_strerror(errno)); + return false; + } + + if (sockopt_reuseaddr(fd)) { + zlog_warn("%s: Could not set reuse addr on socket fd=%d: errno=%d: %s", __func__, + fd, errno, safe_strerror(errno)); return false; } - if (sockopt_reuseaddr(autorp->sock)) { - zlog_err("Could not set reuse addr on socket fd=%d: errno=%d: %s", - autorp->sock, errno, safe_strerror(errno)); + if (setsockopt_ipv4_multicast_loop(fd, 1) < 0) { + zlog_warn("%s: Could not enable multicast loopback on socket fd=%d: errno=%d: %s", + __func__, fd, errno, safe_strerror(errno)); return false; } - if (bind(autorp->sock, (const struct sockaddr *)&autorp_addr, - sizeof(autorp_addr)) < 0) { - zlog_err("Could not bind socket: %pSUp, fd=%d, errno=%d, %s", - (union sockunion *)&autorp_addr, autorp->sock, errno, - safe_strerror(errno)); + if (bind(fd, (const struct sockaddr *)&autorp_addr, sizeof(autorp_addr)) < 0) { + zlog_warn("%s: Could not bind socket: %pSUp, fd=%d, errno=%d, %s", __func__, + (union sockunion *)&autorp_addr, fd, errno, safe_strerror(errno)); return false; } @@ -208,20 +233,148 @@ static bool pim_autorp_setup(struct pim_autorp *autorp) return true; } -static bool pim_autorp_announcement(struct pim_autorp *autorp, uint8_t rpcnt, - uint16_t holdtime, char *buf, - size_t buf_size) +static void autorp_ma_rp_holdtime(struct event *evt) { - /* TODO: Future Mapping agent implementation - * Implement AutoRP mapping agent logic using received announcement messages - */ + /* Mapping agent RP hold time expired, remove the RP */ + struct pim_autorp_rp *rp = EVENT_ARG(evt); + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP hold time expired, RP removed from mapping agent: addr=%pI4, grp=%pFX, grplist=%s", + __func__, &rp->addr, &rp->grp, + (strlen(rp->grplist) ? rp->grplist : "NONE")); + + pim_autorp_rp_del(&(rp->autorp->mapping_rp_list), rp); + pim_autorp_rp_free(rp, false); +} + +static bool autorp_recv_announcement(struct pim_autorp *autorp, uint8_t rpcnt, uint16_t holdtime, + char *buf, size_t buf_size) +{ + int i, j; + struct autorp_pkt_rp *rp; + struct autorp_pkt_grp *grp; + size_t offset = 0; + pim_addr rp_addr; + struct pim_autorp_rp *ma_rp; + struct pim_autorp_rp *trp; + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Processing AutoRP Announcement (rpcnt=%u, holdtime=%u)", __func__, + rpcnt, holdtime); + + for (i = 0; i < rpcnt; ++i) { + if ((buf_size - offset) < AUTORP_RPLEN) { + zlog_warn("%s: Failed to parse AutoRP Announcement RP, invalid buffer size (%u < %u)", + __func__, (uint32_t)(buf_size - offset), AUTORP_RPLEN); + return false; + } + + rp = (struct autorp_pkt_rp *)(buf + offset); + offset += AUTORP_RPLEN; + + rp_addr.s_addr = rp->addr; + + /* Ignore RP's limited to PIM version 1 or with an unknown version */ + if (rp->pimver == AUTORP_PIM_V1 || rp->pimver == AUTORP_PIM_VUNKNOWN) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Ignoring unsupported PIM version (%u) in AutoRP Announcement for RP %pI4", + __func__, rp->pimver, (in_addr_t *)&(rp->addr)); + /* Update the offset to skip past the groups advertised for this RP */ + offset += (AUTORP_GRPLEN * rp->grpcnt); + continue; + } + + if (rp->grpcnt == 0) { + /* No groups?? */ + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Announcement message has no groups for RP %pI4", + __func__, (in_addr_t *)&(rp->addr)); + continue; + } + + if ((buf_size - offset) < AUTORP_GRPLEN) { + zlog_warn("%s: Buffer underrun parsing groups for RP %pI4", __func__, + (in_addr_t *)&(rp->addr)); + return false; + } + + /* Store all announced RP's, calculate what to send in discovery when discovery is sent. */ + ma_rp = XCALLOC(MTYPE_PIM_AUTORP_RP, sizeof(struct pim_autorp_rp)); + memcpy(&(ma_rp->addr), &rp_addr, sizeof(pim_addr)); + trp = pim_autorp_rp_add(&(autorp->mapping_rp_list), ma_rp); + if (trp == NULL) { + /* RP was brand new, finish initializing */ + ma_rp->autorp = autorp; + ma_rp->holdtime = holdtime; + ma_rp->hold_timer = NULL; + ma_rp->grplist[0] = '\0'; + memset(&(ma_rp->grp), 0, sizeof(ma_rp->grp)); + pim_autorp_grppfix_init(&ma_rp->grp_pfix_list); + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: New candidate RP learned (%pPA)", __func__, + &rp_addr); + } else { + /* Returned an existing entry, free allocated RP */ + XFREE(MTYPE_PIM_AUTORP_RP, ma_rp); + ma_rp = trp; + /* Free the existing group prefix list, in case the advertised groups changed */ + pim_autorp_grppfix_free(&ma_rp->grp_pfix_list); + } + + /* Cancel any existing timer and restart it */ + event_cancel(&ma_rp->hold_timer); + if (holdtime > 0) + event_add_timer(router->master, autorp_ma_rp_holdtime, ma_rp, + ma_rp->holdtime, &(ma_rp->hold_timer)); + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Parsing %u group(s) for candidate RP %pPA", __func__, + rp->grpcnt, &rp_addr); + + for (j = 0; j < rp->grpcnt; ++j) { + /* grp is already pointing at the first group in the buffer */ + struct pim_autorp_grppfix *lgrp; + struct pim_autorp_grppfix *tgrp; + + if ((buf_size - offset) < AUTORP_GRPLEN) { + zlog_warn("%s: Failed parsing AutoRP announcement, RP(%pI4), invalid buffer size (%u < %u)", + __func__, &rp_addr, (uint32_t)(buf_size - offset), + AUTORP_GRPLEN); + return false; + } + + grp = (struct autorp_pkt_grp *)(buf + offset); + offset += AUTORP_GRPLEN; + + lgrp = XCALLOC(MTYPE_PIM_AUTORP_GRPPFIX, sizeof(struct pim_autorp_grppfix)); + lgrp->grp.family = AF_INET; + lgrp->grp.prefixlen = grp->masklen; + lgrp->grp.u.prefix4.s_addr = grp->addr; + lgrp->negative = grp->negprefix; + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: %s%pFX added to candidate RP %pPA", __func__, + (lgrp->negative ? "!" : ""), &lgrp->grp, &rp_addr); + + tgrp = pim_autorp_grppfix_add(&ma_rp->grp_pfix_list, lgrp); + if (tgrp != NULL) { + /* This should never happen but if there was an existing entry just free the + * allocated group prefix + */ + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: %pFX was duplicated in AutoRP announcement", + __func__, &lgrp->grp); + XFREE(MTYPE_PIM_AUTORP_GRPPFIX, lgrp); + } + } + } + if (PIM_DEBUG_AUTORP) - zlog_debug("%s: AutoRP processed announcement message", - __func__); + zlog_debug("%s: AutoRP processed announcement message", __func__); return true; } -static void autorp_rp_holdtime(struct event *evt) +static void autorp_cand_rp_holdtime(struct event *evt) { /* RP hold time expired, remove the RP */ struct pim_autorp_rp *rp = EVENT_ARG(evt); @@ -232,79 +385,320 @@ static void autorp_rp_holdtime(struct event *evt) (strlen(rp->grplist) ? rp->grplist : "NONE")); pim_autorp_rp_del(&(rp->autorp->discovery_rp_list), rp); - pim_autorp_rp_free(rp); + pim_autorp_rp_free(rp, true); } -static bool pim_autorp_add_rp(struct pim_autorp *autorp, pim_addr rpaddr, - struct prefix grp, char *listname, - uint16_t holdtime) +static bool pim_autorp_add_rp(struct pim_autorp *autorp, pim_addr rpaddr, struct prefix grp, + char *listname, uint16_t holdtime) { struct pim_autorp_rp *rp; struct pim_autorp_rp *trp = NULL; int ret; ret = pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP); + /* There may not be a path to the RP right now, but that doesn't mean it failed to add the RP */ if (ret != PIM_SUCCESS && ret != PIM_RP_NO_PATH) { - zlog_err("%s: Failed to add new RP addr=%pI4, grp=%pFX, grplist=%s", - __func__, &rpaddr, &grp, - (listname ? listname : "NONE")); + zlog_warn("%s: Failed to add active RP addr=%pI4, grp=%pFX, grplist=%s", __func__, + &rpaddr, &grp, (listname ? listname : "NONE")); return false; } - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: Added new AutoRP learned RP addr=%pI4, grp=%pFX, grplist=%s", - __func__, &rpaddr, &grp, - (listname ? listname : "NONE")); - rp = XCALLOC(MTYPE_PIM_AUTORP_RP, sizeof(*rp)); rp->autorp = autorp; memcpy(&(rp->addr), &rpaddr, sizeof(pim_addr)); - prefix_copy(&(rp->grp), &grp); - if (listname) - snprintf(rp->grplist, sizeof(rp->grplist), "%s", listname); - else - rp->grplist[0] = '\0'; - - rp->holdtime = holdtime; - rp->hold_timer = NULL; trp = pim_autorp_rp_add(&(autorp->discovery_rp_list), rp); if (trp == NULL) { /* RP was brand new */ trp = pim_autorp_rp_find(&(autorp->discovery_rp_list), (const struct pim_autorp_rp *)rp); + /* Make sure the timer is NULL so the cancel below doesn't mess up */ + trp->hold_timer = NULL; + zlog_info("%s: Added new AutoRP learned RP addr=%pI4, grp=%pFX, grplist=%s", + __func__, &rpaddr, &grp, (listname ? listname : "NONE")); } else { - /* RP already existed */ + /* RP already existed, free the temp one */ XFREE(MTYPE_PIM_AUTORP_RP, rp); - event_cancel(&trp->hold_timer); - - /* We know the address matches, but these values may have changed */ - trp->holdtime = holdtime; - prefix_copy(&(trp->grp), &grp); - if (listname) { - snprintf(trp->grplist, sizeof(trp->grplist), "%s", - listname); - } else { - trp->grplist[0] = '\0'; - } } + /* Cancel any existing timer before restarting it */ + event_cancel(&trp->hold_timer); + trp->holdtime = holdtime; + prefix_copy(&(trp->grp), &grp); + if (listname) + snprintf(trp->grplist, sizeof(trp->grplist), "%s", listname); + else + trp->grplist[0] = '\0'; + if (holdtime > 0) { - event_add_timer(router->master, autorp_rp_holdtime, trp, - holdtime, &(trp->hold_timer)); + event_add_timer(router->master, autorp_cand_rp_holdtime, trp, holdtime, + &(trp->hold_timer)); if (PIM_DEBUG_AUTORP) zlog_debug("%s: Started %u second hold timer for RP %pI4", __func__, holdtime, &trp->addr); - } else { - /* If hold time is zero, make sure there doesn't exist a hold timer for it already */ - event_cancel(&trp->hold_timer); } return true; } -static bool pim_autorp_discovery(struct pim_autorp *autorp, uint8_t rpcnt, - uint16_t holdtime, char *buf, size_t buf_size) +static size_t autorp_build_disc_rps(struct pim_autorp *autorp, uint8_t *buf, size_t buf_sz, + size_t *sz) +{ + /* Header has already been added, fill in starting with the address of RP1 + * buf_sz is the max size of the buf + * sz is the current size of the packet, update as buf is filled + * return the total number of RP's added + * + * + * We need to resolve the announced RP's following these rules: + * 1) Co-existence of longer and shorter group prefixes, from different RPs. E.g. when RP1 + * announces 224.2.*.*, and RP2 announces 224.2.2.*, both are accepted; + * 2) For announcements for identical group prefixes from two different RPs, the one from the + * RP with the higher IP address is accepted; + * 3) No duplicates are sent to the AUTORP-DISCOVERY address. E.g. if an RP announces both + * 224.2.2.* and 224.2.*.*, the former group-prefix is not sent and only 224.2.*.* is sent + * to the AUTORP-DISCOVERY address. + * + * + * The approach to resolution, first loop the stored RP's and extract the group prefixes, stored + * in a sorted list, sorted from least specific to most 0.0.0.0/0 -> 239.255.255.255/32. Each + * group prefix will then store the RP advertising that group prefix, this will resolve 2. + * The next step is to then loop the group prefix list and store them back into a list sorted by + * RP address, where the least specific group address will be stored, resolving 3. 1 is more + * about what is allowed, and in the example above the different prefixes will be unique in the + * list of group prefixes, and when they go back into RP's, they are also from different RP's + * and will therefore be sent. + */ + + struct pim_autorp_rp *rp; + struct pim_autorp_rp *trp; + struct pim_autorp_grppfix *grp; + struct pim_autorp_grppfix *grp2; + struct pim_autorp_grppfix *tgrp; + struct pim_autorp_grppfix_head grplist; + bool skip = false; + size_t rpcnt = 0; + size_t bsz = 0; + + /* Initialize the lists, grplist is temporary, disc rp list is stored long term for + * show output, so make sure it's empty + */ + pim_autorp_grppfix_init(&grplist); + pim_autorp_rplist_free(&autorp->advertised_rp_list, false); + + /* Loop the advertised RP's and their group prefixes and make a unique list of group prefixes, + * keeping just the highest IP RP for each group prefix + */ + frr_each (pim_autorp_rp, &autorp->mapping_rp_list, rp) { + frr_each (pim_autorp_grppfix, &rp->grp_pfix_list, grp) { + grp2 = XCALLOC(MTYPE_PIM_AUTORP_GRPPFIX, sizeof(struct pim_autorp_grppfix)); + prefix_copy(&grp2->grp, &grp->grp); + grp2->negative = grp->negative; + grp2->rp = rp->addr; + tgrp = pim_autorp_grppfix_add(&grplist, grp2); + if (tgrp != NULL) { + /* Returned an existing entry. Use the highest RP addr and free allocated object */ + if (IPV4_ADDR_CMP(&tgrp->rp, &grp2->rp)) + tgrp->rp = grp2->rp; + XFREE(MTYPE_PIM_AUTORP_GRPPFIX, grp2); + } + } + } + + /* Now loop the unique group prefixes and put it back into an RP list */ + frr_each (pim_autorp_grppfix, &grplist, grp) { + rp = XCALLOC(MTYPE_PIM_AUTORP_RP, sizeof(struct pim_autorp_rp)); + rp->addr = grp->rp; + trp = pim_autorp_rp_add(&autorp->advertised_rp_list, rp); + if (trp == NULL) { + /* RP was brand new, finish initializing */ + rp->autorp = NULL; + rp->holdtime = 0; + rp->hold_timer = NULL; + rp->grplist[0] = '\0'; + memset(&(rp->grp), 0, sizeof(rp->grp)); + pim_autorp_grppfix_init(&rp->grp_pfix_list); + } else { + /* Returned an existing entry, free allocated RP */ + XFREE(MTYPE_PIM_AUTORP_RP, rp); + rp = trp; + } + + /* Groups are in order from least specific to most, so go through the existing + * groups for this RP and see if the current group is within the prefix of one that + * is already in the list, if so, skip it, if not, add it + * If one is a positive match and the other is negative, then still include it. + */ + skip = false; + frr_each (pim_autorp_grppfix, &rp->grp_pfix_list, grp2) { + if (prefix_match(&grp2->grp, &grp->grp) && grp->negative == grp2->negative) { + skip = true; + break; + } + } + + if (skip) + continue; + + /* add the group to the RP's group list */ + grp2 = XCALLOC(MTYPE_PIM_AUTORP_GRPPFIX, sizeof(struct pim_autorp_grppfix)); + prefix_copy(&grp2->grp, &grp->grp); + grp2->negative = grp->negative; + tgrp = pim_autorp_grppfix_add(&rp->grp_pfix_list, grp2); + assert(tgrp == NULL); + } + + /* Done with temporary group prefix list, so free and finish */ + pim_autorp_grppfix_free(&grplist); + pim_autorp_grppfix_fini(&grplist); + + /* Now finally we can loop the disc rp list and build the packet */ + frr_each (pim_autorp_rp, &autorp->advertised_rp_list, rp) { + struct autorp_pkt_rp *brp; + struct autorp_pkt_grp *bgrp; + size_t rp_sz; + size_t grpcnt; + + grpcnt = pim_autorp_grppfix_count(&rp->grp_pfix_list); + rp_sz = sizeof(struct autorp_pkt_rp) + (grpcnt * sizeof(struct autorp_pkt_grp)); + if (buf_sz < *sz + rp_sz) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Failed to pack AutoRP discovery packet, buffer overrun, (%u < %u)", + __func__, (uint32_t)buf_sz, (uint32_t)(*sz + rp_sz)); + break; + } + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Add RP %pI4 (grpcnt=%u) to discovery message", __func__, + &rp->addr, (uint32_t)grpcnt); + + rpcnt++; + + brp = (struct autorp_pkt_rp *)(buf + bsz); + bsz += sizeof(struct autorp_pkt_rp); + + /* Since this is an in_addr, assume it's already the right byte order */ + brp->addr = rp->addr.s_addr; + brp->pimver = AUTORP_PIM_V2; + brp->reserved = 0; + brp->grpcnt = grpcnt; + + frr_each (pim_autorp_grppfix, &rp->grp_pfix_list, grp) { + bgrp = (struct autorp_pkt_grp *)(buf + bsz); + bsz += sizeof(struct autorp_pkt_grp); + + bgrp->addr = grp->grp.u.prefix4.s_addr; + bgrp->masklen = grp->grp.prefixlen; + bgrp->negprefix = grp->negative; + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Add group %s%pFX for RP %pI4 to discovery message", + __func__, (grp->negative ? "!" : ""), &grp->grp, + &rp->addr); + } + + /* Update the size with this RP now that it is packed */ + *sz += bsz; + } + + return rpcnt; +} + +static size_t autorp_build_disc_packet(struct pim_autorp *autorp, uint8_t *buf, size_t buf_sz) +{ + size_t sz = 0; + struct autorp_pkt_hdr *hdr; + + if (buf_sz >= AUTORP_HDRLEN) { + hdr = (struct autorp_pkt_hdr *)buf; + hdr->version = AUTORP_VERSION; + hdr->type = AUTORP_DISCOVERY_TYPE; + hdr->holdtime = htons(autorp->discovery_holdtime); + hdr->reserved = 0; + sz += AUTORP_HDRLEN; + hdr->rpcnt = autorp_build_disc_rps(autorp, buf + sizeof(struct autorp_pkt_hdr), + (buf_sz - AUTORP_HDRLEN), &sz); + if (hdr->rpcnt == 0) + sz = 0; + } + return sz; +} + +static void autorp_send_discovery(struct event *evt) +{ + struct pim_autorp *autorp = EVENT_ARG(evt); + struct sockaddr_in discGrp; + size_t disc_sz; + size_t buf_sz = 65535; + uint8_t buf[65535] = { 0 }; + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP sending discovery info", __func__); + + /* Mark true, even if nothing is sent */ + autorp->mapping_agent_active = true; + disc_sz = autorp_build_disc_packet(autorp, buf, buf_sz); + + if (disc_sz > 0) { + discGrp.sin_family = AF_INET; + discGrp.sin_port = htons(PIM_AUTORP_PORT); + inet_pton(PIM_AF, PIM_AUTORP_DISCOVERY_GRP, &discGrp.sin_addr); + + if (setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_TTL, + &(autorp->discovery_scope), sizeof(autorp->discovery_scope)) == 0) { + if (setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_IF, + &(autorp->mapping_agent_addrsel.run_addr), + sizeof(autorp->mapping_agent_addrsel.run_addr)) == 0) { + if (sendto(autorp->sock, buf, disc_sz, 0, + (struct sockaddr *)&discGrp, sizeof(discGrp)) > 0) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP discovery message sent", + __func__); + } else if (PIM_DEBUG_AUTORP) + zlog_warn("%s: Failed to send AutoRP discovery message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); + } else if (PIM_DEBUG_AUTORP) + zlog_warn("%s: Failed to set Multicast Interface for sending AutoRP discovery message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); + } else if (PIM_DEBUG_AUTORP) + zlog_warn("%s: Failed to set Multicast TTL for sending AutoRP discovery message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); + } + + /* Start the new timer for the entire send discovery interval */ + event_add_timer(router->master, autorp_send_discovery, autorp, autorp->discovery_interval, + &(autorp->send_discovery_timer)); +} + +static void autorp_send_discovery_on(struct pim_autorp *autorp) +{ + int interval = 5; + + /* Send the first discovery shortly after being enabled. + * If the configured interval is less than 5 seconds, then just use that. + */ + if (interval > autorp->discovery_interval) + interval = autorp->discovery_interval; + + if (autorp->send_discovery_timer) + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP discovery sending enabled in %u seconds", __func__, + interval); + + event_add_timer(router->master, autorp_send_discovery, autorp, interval, + &(autorp->send_discovery_timer)); +} + +static void autorp_send_discovery_off(struct pim_autorp *autorp) +{ + if (autorp->send_discovery_timer) + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP discovery sending disabled", __func__); + event_cancel(&(autorp->send_discovery_timer)); +} + +static bool autorp_recv_discovery(struct pim_autorp *autorp, uint8_t rpcnt, uint16_t holdtime, + char *buf, size_t buf_size, pim_addr src) { int i, j; struct autorp_pkt_rp *rp; @@ -318,65 +712,122 @@ static bool pim_autorp_discovery(struct pim_autorp *autorp, uint8_t rpcnt, int64_t seq = 1; bool success = true; + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Received AutoRP discovery message (src=%pI4, rpcnt=%u, holdtime=%u)", + __func__, &src, rpcnt, holdtime); + + if (autorp->send_rp_discovery && + (pim_addr_cmp(autorp->mapping_agent_addrsel.run_addr, src) < 0)) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP send discovery suppressed -- Discovery received with higher IP address", + __func__); + + /* Cancel the existing send timer and restart for 3X the send discovery interval */ + event_cancel(&(autorp->send_discovery_timer)); + event_add_timer(router->master, autorp_send_discovery, autorp, + (autorp->discovery_interval * 3), &(autorp->send_discovery_timer)); + + /* Clear the last sent discovery RP's, since it is no longer valid */ + pim_autorp_rplist_free(&autorp->advertised_rp_list, false); + /* Unset flag indicating we are active */ + autorp->mapping_agent_active = false; + } + for (i = 0; i < rpcnt; ++i) { - if ((buf_size - offset) < AUTORP_RPLEN) + if ((buf_size - offset) < AUTORP_RPLEN) { + zlog_warn("%s: Failed to parse AutoRP discovery message, invalid buffer size (%u < %u)", + __func__, (uint32_t)(buf_size - offset), AUTORP_RPLEN); return false; + } rp = (struct autorp_pkt_rp *)(buf + offset); offset += AUTORP_RPLEN; rp_addr.s_addr = rp->addr; + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Parsing RP %pI4 (grpcnt=%u)", __func__, + (in_addr_t *)&rp->addr, rp->grpcnt); + /* Ignore RP's limited to PIM version 1 or with an unknown version */ - if (rp->pimver == PIM_V1 || rp->pimver == PIM_VUNKNOWN) { - zlog_warn("%s: Ignoring unsupported PIM version in AutoRP Discovery for RP %pI4", - __func__, (in_addr_t *)&(rp->addr)); + if (rp->pimver == AUTORP_PIM_V1 || rp->pimver == AUTORP_PIM_VUNKNOWN) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Ignoring unsupported PIM version in AutoRP Discovery for RP %pI4", + __func__, (in_addr_t *)&(rp->addr)); /* Update the offset to skip past the groups advertised for this RP */ offset += (AUTORP_GRPLEN * rp->grpcnt); continue; } - if (rp->grpcnt == 0) { /* No groups?? */ - zlog_warn("%s: Discovery message has no groups for RP %pI4", - __func__, (in_addr_t *)&(rp->addr)); + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Discovery message has no groups for RP %pI4", + __func__, (in_addr_t *)&(rp->addr)); continue; } - if ((buf_size - offset) < AUTORP_GRPLEN) { - zlog_warn("%s: Buffer underrun parsing groups for RP %pI4", - __func__, (in_addr_t *)&(rp->addr)); + /* Make sure there is enough buffer to parse all the groups */ + if ((buf_size - offset) < (AUTORP_GRPLEN * rp->grpcnt)) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Buffer underrun parsing groups for RP %pI4 (%u < %u)", + __func__, (in_addr_t *)&(rp->addr), + (uint32_t)(buf_size - offset), + (uint32_t)(AUTORP_GRPLEN * rp->grpcnt)); return false; } + /* Get the first group so we can check for a negative prefix */ + /* Don't add to offset yet to make the multiple group loop easier */ grp = (struct autorp_pkt_grp *)(buf + offset); - offset += AUTORP_GRPLEN; if (rp->grpcnt == 1 && grp->negprefix == 0) { /* Only one group with positive prefix, we can use the standard RP API */ + offset += AUTORP_GRPLEN; grppfix.family = AF_INET; grppfix.prefixlen = grp->masklen; grppfix.u.prefix4.s_addr = grp->addr; - if (!pim_autorp_add_rp(autorp, rp_addr, grppfix, NULL, - holdtime)) + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Parsing group %s%pFX for RP %pI4", __func__, + (grp->negprefix ? "!" : ""), &grppfix, + (in_addr_t *)&rp->addr); + + if (!pim_autorp_add_rp(autorp, rp_addr, grppfix, NULL, holdtime)) success = false; } else { - /* More than one grp, or the only group is a negative prefix, need to make a prefix list for this RP */ - snprintfrr(plname, sizeof(plname), "__AUTORP_%pI4__", - &rp_addr); + /* More than one grp, or the only group is a negative prefix. + * Need to make a prefix list for this RP + */ + snprintfrr(plname, sizeof(plname), "__AUTORP_%pI4__", &rp_addr); + pl = prefix_list_lookup(AFI_IP, plname); + + if (pl) { + /* Existing prefix list found, delete it first */ + /* TODO: Instead of deleting completely, maybe we can just clear it and re-add entries */ + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Found existing prefix list %s, replacing it", + __func__, plname); + prefix_list_delete(pl); + } + + /* Now get a new prefix list */ pl = prefix_list_get(AFI_IP, 0, plname); for (j = 0; j < rp->grpcnt; ++j) { - /* grp is already pointing at the first group in the buffer */ + /* This will just set grp to the same pointer on the first loop, but offset will + * be updated correctly while parsing + */ + grp = (struct autorp_pkt_grp *)(buf + offset); + offset += AUTORP_GRPLEN; + ple = prefix_list_entry_new(); ple->pl = pl; ple->seq = seq; seq += 5; memset(&ple->prefix, 0, sizeof(ple->prefix)); prefix_list_entry_update_start(ple); - ple->type = (grp->negprefix ? PREFIX_DENY - : PREFIX_PERMIT); + ple->type = (grp->negprefix ? PREFIX_DENY : PREFIX_PERMIT); ple->prefix.family = AF_INET; ple->prefix.prefixlen = grp->masklen; ple->prefix.u.prefix4.s_addr = grp->addr; @@ -385,60 +836,59 @@ static bool pim_autorp_discovery(struct pim_autorp *autorp, uint8_t rpcnt, ple->le = 32; prefix_list_entry_update_finish(ple); - if ((buf_size - offset) < AUTORP_GRPLEN) - return false; - - grp = (struct autorp_pkt_grp *)(buf + offset); - offset += AUTORP_GRPLEN; + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Parsing group %s%pFX for RP %pI4", __func__, + (grp->negprefix ? "!" : ""), &ple->prefix, + (in_addr_t *)&rp->addr); } - if (!pim_autorp_add_rp(autorp, rp_addr, grppfix, plname, - holdtime)) + if (!pim_autorp_add_rp(autorp, rp_addr, grppfix, plname, holdtime)) success = false; } } - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: Processed AutoRP Discovery message", __func__); - return success; } -static bool pim_autorp_msg(struct pim_autorp *autorp, char *buf, size_t buf_size) +static bool autorp_recv_msg(struct pim_autorp *autorp, char *buf, size_t buf_size, pim_addr src) { struct autorp_pkt_hdr *h; - if (buf_size < AUTORP_HDRLEN) + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Received AutoRP message", __func__); + + if (buf_size < AUTORP_HDRLEN) { + zlog_warn("%s: Invalid AutoRP Header size (%u < %u)", __func__, (uint32_t)buf_size, + AUTORP_HDRLEN); return false; + } h = (struct autorp_pkt_hdr *)buf; - if (h->version != AUTORP_VERSION) + if (h->version != AUTORP_VERSION) { + zlog_warn("%s: Unsupported AutoRP version (%u != %u)", __func__, h->version, + AUTORP_VERSION); return false; + } - if (h->type == AUTORP_ANNOUNCEMENT_TYPE && - !pim_autorp_announcement(autorp, h->rpcnt, htons(h->holdtime), - buf + AUTORP_HDRLEN, - buf_size - AUTORP_HDRLEN)) - return false; + if (h->type == AUTORP_ANNOUNCEMENT_TYPE) + return autorp_recv_announcement(autorp, h->rpcnt, htons(h->holdtime), + buf + AUTORP_HDRLEN, buf_size - AUTORP_HDRLEN); - if (h->type == AUTORP_DISCOVERY_TYPE && - !pim_autorp_discovery(autorp, h->rpcnt, htons(h->holdtime), - buf + AUTORP_HDRLEN, buf_size - AUTORP_HDRLEN)) - return false; + if (h->type == AUTORP_DISCOVERY_TYPE) + return autorp_recv_discovery(autorp, h->rpcnt, htons(h->holdtime), + buf + AUTORP_HDRLEN, buf_size - AUTORP_HDRLEN, src); - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: Processed AutoRP packet", __func__); + zlog_warn("%s: Unknown AutoRP message type (%u)", __func__, h->type); - return true; + return false; } static void autorp_read(struct event *t); static void autorp_read_on(struct pim_autorp *autorp) { - event_add_read(router->master, autorp_read, autorp, autorp->sock, - &(autorp->read_event)); + event_add_read(router->master, autorp_read, autorp, autorp->sock, &(autorp->read_event)); if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP socket read enabled", __func__); } @@ -456,26 +906,35 @@ static void autorp_read(struct event *evt) int fd = evt->u.fd; char buf[10000]; int rd; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + pim_addr src; if (PIM_DEBUG_AUTORP) zlog_debug("%s: Reading from AutoRP socket", __func__); while (1) { - rd = pim_socket_recvfromto(fd, (uint8_t *)buf, sizeof(buf), - NULL, NULL, NULL, NULL, NULL); + rd = pim_socket_recvfromto(fd, (uint8_t *)buf, sizeof(buf), &from, &fromlen, NULL, + NULL, NULL); if (rd <= 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) break; + zlog_warn("%s: Failure reading rd=%d: fd=%d: errno=%d: %s", __func__, rd, + fd, errno, safe_strerror(errno)); + goto err; + } - zlog_warn("%s: Failure reading rd=%d: fd=%d: errno=%d: %s", - __func__, rd, fd, errno, safe_strerror(errno)); + if (from.ss_family == AF_INET) + src.s_addr = ((struct sockaddr_in *)&from)->sin_addr.s_addr; + else { + zlog_warn("%s: AutoRP message is not IPV4", __func__); goto err; } - if (!pim_autorp_msg(autorp, buf, rd)) - zlog_err("%s: Failure parsing AutoRP message", __func__); + if (!autorp_recv_msg(autorp, buf, rd, src)) + zlog_warn("%s: Failure parsing AutoRP message", __func__); /* Keep reading until would block */ } @@ -493,23 +952,23 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) frr_with_privs (&pimd_privs) { fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd < 0) { - zlog_warn("Could not create autorp socket: errno=%d: %s", - errno, safe_strerror(errno)); + zlog_warn("Could not create autorp socket: errno=%d: %s", errno, + safe_strerror(errno)); return false; } - autorp->sock = fd; - if (!pim_autorp_setup(autorp)) { - zlog_warn("Could not setup autorp socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + if (!pim_autorp_setup(fd)) { + zlog_warn("Could not setup autorp socket fd=%d: errno=%d: %s", fd, errno, + safe_strerror(errno)); close(fd); - autorp->sock = -1; return false; } } + autorp->sock = fd; + if (PIM_DEBUG_AUTORP) - zlog_debug("%s: AutoRP socket enabled", __func__); + zlog_debug("%s: AutoRP socket enabled (fd=%u)", __func__, fd); return true; } @@ -517,8 +976,8 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) static bool pim_autorp_socket_disable(struct pim_autorp *autorp) { if (close(autorp->sock)) { - zlog_warn("Failure closing autorp socket: fd=%d errno=%d: %s", - autorp->sock, errno, safe_strerror(errno)); + zlog_warn("Failure closing autorp socket: fd=%d errno=%d: %s", autorp->sock, errno, + safe_strerror(errno)); return false; } @@ -542,13 +1001,15 @@ static void autorp_send_announcement(struct event *evt) announceGrp.sin_port = htons(PIM_AUTORP_PORT); inet_pton(PIM_AF, PIM_AUTORP_ANNOUNCEMENT_GRP, &announceGrp.sin_addr); - if (autorp->annouce_pkt_sz >= MIN_AUTORP_PKT_SZ) { + if (autorp->announce_pkt_sz >= MIN_AUTORP_PKT_SZ) { + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Sending AutoRP announcement", __func__); + if (setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_TTL, - &(autorp->announce_scope), - sizeof(autorp->announce_scope)) < 0) { - if (PIM_DEBUG_AUTORP) - zlog_err("%s: Failed to set Multicast TTL for sending AutoRP announcement message, errno=%d, %s", - __func__, errno, safe_strerror(errno)); + &(autorp->announce_scope), sizeof(autorp->announce_scope)) < 0) { + zlog_warn("%s: Failed to set Multicast TTL for sending AutoRP announcement message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); + return; } FOR_ALL_INTERFACES (autorp->pim->vrf, ifp) { @@ -556,57 +1017,56 @@ static void autorp_send_announcement(struct event *evt) /* Only send on active interfaces with full pim enabled, non-passive * and have a primary address set. */ - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && - pim_ifp && pim_ifp->pim_enable && - !pim_ifp->pim_passive_enable && + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && pim_ifp && + pim_ifp->pim_enable && !pim_ifp->pim_passive_enable && !pim_addr_is_any(pim_ifp->primary_address)) { - if (setsockopt(autorp->sock, IPPROTO_IP, - IP_MULTICAST_IF, + if (setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_IF, &(pim_ifp->primary_address), - sizeof(pim_ifp->primary_address)) < - 0) { - if (PIM_DEBUG_AUTORP) - zlog_err("%s: Failed to set Multicast Interface for sending AutoRP announcement message, errno=%d, %s", - __func__, errno, - safe_strerror(errno)); + sizeof(pim_ifp->primary_address)) < 0) { + zlog_warn("%s: Failed to set Multicast Interface for sending AutoRP announcement message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); + continue; } - if (sendto(autorp->sock, autorp->annouce_pkt, - autorp->annouce_pkt_sz, 0, + + if (sendto(autorp->sock, autorp->announce_pkt, + autorp->announce_pkt_sz, 0, (struct sockaddr *)&announceGrp, - sizeof(announceGrp)) <= 0) { - if (PIM_DEBUG_AUTORP) - zlog_err("%s: Failed to send AutoRP announcement message, errno=%d, %s", - __func__, errno, - safe_strerror(errno)); - } + sizeof(announceGrp)) <= 0) + zlog_warn("%s: Failed to send AutoRP announcement message, errno=%d, %s", + __func__, errno, safe_strerror(errno)); } } } /* Start the new timer for the entire announce interval */ - event_add_timer(router->master, autorp_send_announcement, autorp, - autorp->announce_interval, &(autorp->announce_timer)); + event_add_timer(router->master, autorp_send_announcement, autorp, autorp->announce_interval, + &(autorp->announce_timer)); } static void autorp_announcement_on(struct pim_autorp *autorp) { int interval = 5; - if (interval > autorp->announce_interval) { - /* If the configured interval is less than 5 seconds, then just use that */ + /* Send the first announcement shortly after being enabled. + * If the configured interval is less than 5 seconds, then just use that. + */ + if (interval > autorp->announce_interval) interval = autorp->announce_interval; - } - event_add_timer(router->master, autorp_send_announcement, autorp, - interval, &(autorp->announce_timer)); - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: AutoRP announcement sending enabled", __func__); + + if (autorp->announce_timer == NULL) + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP announcement sending enabled", __func__); + + event_add_timer(router->master, autorp_send_announcement, autorp, interval, + &(autorp->announce_timer)); } static void autorp_announcement_off(struct pim_autorp *autorp) { + if (autorp->announce_timer != NULL) + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP announcement sending disabled", __func__); event_cancel(&(autorp->announce_timer)); - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: AutoRP announcement sending disabled", __func__); } /* Pack the groups of the RP @@ -614,34 +1074,31 @@ static void autorp_announcement_off(struct pim_autorp *autorp) * buf - Pointer to the buffer where to start packing groups * returns - Total group count packed */ -static uint8_t pim_autorp_new_announcement_rp_grps(struct pim_autorp_rp *rp, - uint8_t *buf) +static uint8_t pim_autorp_new_announcement_rp_grps(struct pim_autorp_rp *rp, uint8_t *buf) { - struct prefix_list *plist; - struct prefix_list_entry *ple; struct autorp_pkt_grp *grpp = (struct autorp_pkt_grp *)buf; uint8_t cnt = 0; - in_addr_t taddr; if (is_default_prefix(&(rp->grp))) { /* No group so pack from the prefix list * The grplist should be set and the prefix list exist with at least one group address */ + struct prefix_list *plist; + struct prefix_list_entry *ple; + plist = prefix_list_lookup(AFI_IP, rp->grplist); for (ple = plist->head; ple; ple = ple->next) { - taddr = ntohl(ple->prefix.u.prefix4.s_addr); - if ((taddr & 0xF0000000) == 0xE0000000) { + if (pim_addr_is_multicast(ple->prefix.u.prefix4) && + ple->prefix.prefixlen >= 4) { grpp->addr = ple->prefix.u.prefix4.s_addr; grpp->masklen = ple->prefix.prefixlen; - grpp->negprefix = - (ple->type == PREFIX_PERMIT ? 0 : 1); + grpp->negprefix = (ple->type == PREFIX_PERMIT ? 0 : 1); grpp->reserved = 0; ++cnt; - grpp = (struct autorp_pkt_grp - *)(buf + - (sizeof(struct autorp_pkt_grp) * - cnt)); + grpp = (struct autorp_pkt_grp *)(buf + + (sizeof(struct autorp_pkt_grp) * + cnt)); } } @@ -661,20 +1118,16 @@ static uint8_t pim_autorp_new_announcement_rp_grps(struct pim_autorp_rp *rp, * buf - Pointer to the buffer where to start packing the RP * returns - Buffer pointer pointing to the start of the next RP */ -static uint8_t *pim_autorp_new_announcement_rp(struct pim_autorp_rp *rp, - uint8_t *buf) +static uint8_t *pim_autorp_new_announcement_rp(struct pim_autorp_rp *rp, uint8_t *buf) { struct autorp_pkt_rp *brp = (struct autorp_pkt_rp *)buf; /* Since this is an in_addr, assume it's already the right byte order */ brp->addr = rp->addr.s_addr; - brp->pimver = PIM_V2; + brp->pimver = AUTORP_PIM_V2; brp->reserved = 0; - brp->grpcnt = - pim_autorp_new_announcement_rp_grps(rp, - buf + sizeof(struct autorp_pkt_rp)); - return buf + sizeof(struct autorp_pkt_rp) + - (brp->grpcnt * sizeof(struct autorp_pkt_grp)); + brp->grpcnt = pim_autorp_new_announcement_rp_grps(rp, buf + sizeof(struct autorp_pkt_rp)); + return buf + sizeof(struct autorp_pkt_rp) + (brp->grpcnt * sizeof(struct autorp_pkt_grp)); } /* Pack the candidate RP's on the announcement packet @@ -683,36 +1136,45 @@ static uint8_t *pim_autorp_new_announcement_rp(struct pim_autorp_rp *rp, * bufsz - Output parameter to track size of packed bytes * returns - Total count of RP's packed */ -static int pim_autorp_new_announcement_rps(struct pim_autorp *autorp, - uint8_t *buf, uint16_t *bufsz) +static int pim_autorp_new_announcement_rps(struct pim_autorp *autorp, uint8_t *buf, uint16_t *bufsz) { int cnt = 0; struct pim_autorp_rp *rp; /* Keep the original buffer pointer to calculate final size after packing */ uint8_t *obuf = buf; - struct prefix_list *plist; - struct prefix_list_entry *ple; - in_addr_t taddr; frr_each_safe (pim_autorp_rp, &(autorp->candidate_rp_list), rp) { - /* We must have an rp address and either group or list in order to pack this RP, so skip this one */ + /* We must have an rp address and either group or list in order to pack this RP, + * so skip this one + */ + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: Evaluating AutoRP candidate %pI4, group range %pFX, group list %s", + __func__, &rp->addr, &rp->grp, rp->grplist); + if (pim_addr_is_any(rp->addr) || - (is_default_prefix(&(rp->grp)) && strlen(rp->grplist) == 0)) + (is_default_prefix(&rp->grp) && strlen(rp->grplist) == 0)) continue; - /* Group is net set, so list must be set, make sure the prefix list exists and has valid multicast groups */ - if (is_default_prefix(&(rp->grp))) { + /* Make sure that either group prefix is set, or that the prefix list exists and has at + * least one valid multicast prefix in it. Only multicast prefixes will be used. + */ + if (is_default_prefix(&rp->grp)) { + struct prefix_list *plist; + struct prefix_list_entry *ple; + plist = prefix_list_lookup(AFI_IP, rp->grplist); if (plist == NULL) continue; plist = prefix_list_lookup(AFI_IP, rp->grplist); for (ple = plist->head; ple; ple = ple->next) { - taddr = ntohl(ple->prefix.u.prefix4.s_addr); - if ((taddr & 0xF0000000) == 0xE0000000) + if (pim_addr_is_multicast(ple->prefix.u.prefix4) && + ple->prefix.prefixlen >= 4) break; } - /* If we went through the entire list without finding a multicast prefix, then skip this RP */ + /* If we went through the entire list without finding a multicast prefix, + * then skip this RP + */ if (ple == NULL) continue; } @@ -721,6 +1183,10 @@ static int pim_autorp_new_announcement_rps(struct pim_autorp *autorp, ++cnt; /* This will return the buffer pointer at the location to start packing the next RP */ buf = pim_autorp_new_announcement_rp(rp, buf); + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP candidate %pI4 added to announcement", __func__, + &rp->addr); } if (cnt > 0) @@ -729,7 +1195,9 @@ static int pim_autorp_new_announcement_rps(struct pim_autorp *autorp, return cnt; } -/* Build the new announcement packet. If there is a packet to send, restart the send timer with a short wait */ +/* Build the new announcement packet. If there is a packet to send, restart the send timer + * with a short wait + */ static void pim_autorp_new_announcement(struct pim_instance *pim) { struct pim_autorp *autorp = pim->autorp; @@ -739,70 +1207,87 @@ static void pim_autorp_new_announcement(struct pim_instance *pim) /* First disable any existing send timer */ autorp_announcement_off(autorp); - if (!autorp->annouce_pkt) { - /* - * First time building, allocate the space - * Allocate the max packet size of 65536 so we don't need to resize later. - * This should be ok since we are only allocating the memory once for a single packet (potentially per vrf) - */ - autorp->annouce_pkt = XCALLOC(MTYPE_PIM_AUTORP_ANNOUNCE, 65536); - } + /* + * First time building, allocate the space + * Allocate the max packet size of 65536 so we don't need to resize later. + * This should be ok since we are only allocating the memory once for a single packet + * (potentially per vrf) + */ + if (!autorp->announce_pkt) + autorp->announce_pkt = XCALLOC(MTYPE_PIM_AUTORP_ANNOUNCE, 65536); - autorp->annouce_pkt_sz = 0; + autorp->announce_pkt_sz = 0; holdtime = autorp->announce_holdtime; - if (holdtime == DEFAULT_ANNOUNCE_HOLDTIME) + if (holdtime == DEFAULT_AUTORP_ANNOUNCE_HOLDTIME) holdtime = autorp->announce_interval * 3; if (holdtime > UINT16_MAX) holdtime = UINT16_MAX; - hdr = (struct autorp_pkt_hdr *)autorp->annouce_pkt; + hdr = (struct autorp_pkt_hdr *)autorp->announce_pkt; hdr->version = AUTORP_VERSION; hdr->type = AUTORP_ANNOUNCEMENT_TYPE; hdr->holdtime = htons((uint16_t)holdtime); hdr->reserved = 0; - hdr->rpcnt = - pim_autorp_new_announcement_rps(autorp, - autorp->annouce_pkt + - sizeof(struct autorp_pkt_hdr), - &(autorp->annouce_pkt_sz)); + hdr->rpcnt = pim_autorp_new_announcement_rps(autorp, + autorp->announce_pkt + + sizeof(struct autorp_pkt_hdr), + &(autorp->announce_pkt_sz)); /* Still need to add on the size of the header */ - autorp->annouce_pkt_sz += sizeof(struct autorp_pkt_hdr); + autorp->announce_pkt_sz += sizeof(struct autorp_pkt_hdr); /* Only turn on the announcement timer if we have a packet to send */ - if (autorp->annouce_pkt_sz >= MIN_AUTORP_PKT_SZ) + if (autorp->announce_pkt_sz >= MIN_AUTORP_PKT_SZ) autorp_announcement_on(autorp); } +void pim_autorp_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist) +{ + struct pim_autorp_rp *rp = NULL; + struct pim_autorp *autorp = NULL; + + autorp = pim->autorp; + if (autorp == NULL) + return; + + /* Search for a candidate RP using this prefix list */ + frr_each_safe (pim_autorp_rp, &(autorp->candidate_rp_list), rp) { + if (strmatch(rp->grplist, plist->name)) + break; + } + + /* If we broke out of the loop early because we found a match, then rebuild the announcement */ + if (rp != NULL) + pim_autorp_new_announcement(pim); +} + bool pim_autorp_rm_candidate_rp(struct pim_instance *pim, pim_addr rpaddr) { struct pim_autorp *autorp = pim->autorp; struct pim_autorp_rp *rp; struct pim_autorp_rp find = { .addr = rpaddr }; - rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), - (const struct pim_autorp_rp *)&find); + rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), (const struct pim_autorp_rp *)&find); if (!rp) return false; pim_autorp_rp_del(&(autorp->candidate_rp_list), rp); - pim_autorp_rp_free(rp); + pim_autorp_rp_free(rp, false); pim_autorp_new_announcement(pim); return true; } -void pim_autorp_add_candidate_rp_group(struct pim_instance *pim, - pim_addr rpaddr, struct prefix group) +void pim_autorp_add_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, + struct prefix group) { struct pim_autorp *autorp = pim->autorp; struct pim_autorp_rp *rp; struct pim_autorp_rp find = { .addr = rpaddr }; - rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), - (const struct pim_autorp_rp *)&find); + rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), (const struct pim_autorp_rp *)&find); if (!rp) { - rp = XCALLOC(MTYPE_PIM_AUTORP_CRP, sizeof(*rp)); + rp = XCALLOC(MTYPE_PIM_AUTORP_RP, sizeof(*rp)); memset(rp, 0, sizeof(struct pim_autorp_rp)); rp->autorp = autorp; memcpy(&(rp->addr), &rpaddr, sizeof(pim_addr)); @@ -817,15 +1302,13 @@ void pim_autorp_add_candidate_rp_group(struct pim_instance *pim, pim_autorp_new_announcement(pim); } -bool pim_autorp_rm_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, - struct prefix group) +bool pim_autorp_rm_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, struct prefix group) { struct pim_autorp *autorp = pim->autorp; struct pim_autorp_rp *rp; struct pim_autorp_rp find = { .addr = rpaddr }; - rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), - (const struct pim_autorp_rp *)&find); + rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), (const struct pim_autorp_rp *)&find); if (!rp) return false; @@ -834,17 +1317,15 @@ bool pim_autorp_rm_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, return true; } -void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim, - pim_addr rpaddr, const char *plist) +void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, const char *plist) { struct pim_autorp *autorp = pim->autorp; struct pim_autorp_rp *rp; struct pim_autorp_rp find = { .addr = rpaddr }; - rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), - (const struct pim_autorp_rp *)&find); + rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), (const struct pim_autorp_rp *)&find); if (!rp) { - rp = XCALLOC(MTYPE_PIM_AUTORP_CRP, sizeof(*rp)); + rp = XCALLOC(MTYPE_PIM_AUTORP_RP, sizeof(*rp)); memset(rp, 0, sizeof(struct pim_autorp_rp)); rp->autorp = autorp; memcpy(&(rp->addr), &rpaddr, sizeof(pim_addr)); @@ -859,15 +1340,13 @@ void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim, pim_autorp_new_announcement(pim); } -bool pim_autorp_rm_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, - const char *plist) +bool pim_autorp_rm_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, const char *plist) { struct pim_autorp *autorp = pim->autorp; struct pim_autorp_rp *rp; struct pim_autorp_rp find = { .addr = rpaddr }; - rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), - (const struct pim_autorp_rp *)&find); + rp = pim_autorp_rp_find(&(autorp->candidate_rp_list), (const struct pim_autorp_rp *)&find); if (!rp) return false; @@ -880,7 +1359,7 @@ void pim_autorp_announce_scope(struct pim_instance *pim, uint8_t scope) { struct pim_autorp *autorp = pim->autorp; - scope = (scope == 0 ? DEFAULT_ANNOUNCE_SCOPE : scope); + scope = (scope == 0 ? DEFAULT_AUTORP_ANNOUNCE_SCOPE : scope); if (autorp->announce_scope != scope) { autorp->announce_scope = scope; pim_autorp_new_announcement(pim); @@ -891,7 +1370,7 @@ void pim_autorp_announce_interval(struct pim_instance *pim, uint16_t interval) { struct pim_autorp *autorp = pim->autorp; - interval = (interval == 0 ? DEFAULT_ANNOUNCE_INTERVAL : interval); + interval = (interval == 0 ? DEFAULT_AUTORP_ANNOUNCE_INTERVAL : interval); if (autorp->announce_interval != interval) { autorp->announce_interval = interval; pim_autorp_new_announcement(pim); @@ -908,6 +1387,16 @@ void pim_autorp_announce_holdtime(struct pim_instance *pim, int32_t holdtime) } } +void pim_autorp_send_discovery_apply(struct pim_autorp *autorp) +{ + if (!autorp->mapping_agent_addrsel.run || !autorp->send_rp_discovery) { + autorp_send_discovery_off(autorp); + return; + } + + autorp_send_discovery_on(autorp); +} + void pim_autorp_add_ifp(struct interface *ifp) { /* Add a new interface for autorp @@ -923,17 +1412,15 @@ void pim_autorp_add_ifp(struct interface *ifp) struct pim_interface *pim_ifp; pim_ifp = ifp->info; - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && pim_ifp && - pim_ifp->pim_enable) { + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && pim_ifp && pim_ifp->pim_enable) { pim = pim_ifp->pim; if (pim && pim->autorp && pim->autorp->do_discovery) { if (PIM_DEBUG_AUTORP) zlog_debug("%s: Adding interface %s to AutoRP, joining AutoRP groups", __func__, ifp->name); - if (!pim_autorp_join_groups(ifp)) { - zlog_err("Could not join AutoRP groups, errno=%d, %s", - errno, safe_strerror(errno)); - } + if (!pim_autorp_join_groups(ifp)) + zlog_warn("Could not join AutoRP groups, errno=%d, %s", errno, + safe_strerror(errno)); } } } @@ -954,10 +1441,9 @@ void pim_autorp_rm_ifp(struct interface *ifp) if (PIM_DEBUG_AUTORP) zlog_debug("%s: Removing interface %s from AutoRP, leaving AutoRP groups", __func__, ifp->name); - if (!pim_autorp_leave_groups(ifp)) { - zlog_err("Could not leave AutoRP groups, errno=%d, %s", - errno, safe_strerror(errno)); - } + if (!pim_autorp_leave_groups(ifp)) + zlog_warn("Could not leave AutoRP groups, errno=%d, %s", errno, + safe_strerror(errno)); } } } @@ -1008,14 +1494,22 @@ void pim_autorp_init(struct pim_instance *pim) autorp->read_event = NULL; autorp->announce_timer = NULL; autorp->do_discovery = false; + autorp->send_discovery_timer = NULL; + autorp->send_rp_discovery = false; pim_autorp_rp_init(&(autorp->discovery_rp_list)); pim_autorp_rp_init(&(autorp->candidate_rp_list)); - autorp->announce_scope = DEFAULT_ANNOUNCE_SCOPE; - autorp->announce_interval = DEFAULT_ANNOUNCE_INTERVAL; - autorp->announce_holdtime = DEFAULT_ANNOUNCE_HOLDTIME; + pim_autorp_rp_init(&(autorp->mapping_rp_list)); + pim_autorp_rp_init(&autorp->advertised_rp_list); + autorp->announce_scope = DEFAULT_AUTORP_ANNOUNCE_SCOPE; + autorp->announce_interval = DEFAULT_AUTORP_ANNOUNCE_INTERVAL; + autorp->announce_holdtime = DEFAULT_AUTORP_ANNOUNCE_HOLDTIME; + autorp->discovery_scope = DEFAULT_AUTORP_DISCOVERY_SCOPE; + autorp->discovery_interval = DEFAULT_AUTORP_DISCOVERY_INTERVAL; + autorp->discovery_holdtime = DEFAULT_AUTORP_DISCOVERY_HOLDTIME; + cand_addrsel_clear(&(autorp->mapping_agent_addrsel)); if (!pim_autorp_socket_enable(autorp)) { - zlog_err("%s: AutoRP failed to initialize", __func__); + zlog_warn("%s: AutoRP failed to initialize", __func__); return; } @@ -1032,24 +1526,20 @@ void pim_autorp_finish(struct pim_instance *pim) struct pim_autorp *autorp = pim->autorp; autorp_read_off(autorp); + autorp_announcement_off(autorp); + autorp_send_discovery_off(autorp); pim_autorp_free(autorp); - if (pim_autorp_socket_disable(autorp)) { - if (PIM_DEBUG_AUTORP) - zlog_debug("%s: AutoRP Finished", __func__); - } else - zlog_err("%s: AutoRP failed to finish", __func__); - + pim_autorp_socket_disable(autorp); XFREE(MTYPE_PIM_AUTORP, pim->autorp); + + if (PIM_DEBUG_AUTORP) + zlog_debug("%s: AutoRP Finished", __func__); } int pim_autorp_config_write(struct pim_instance *pim, struct vty *vty) { struct pim_autorp_rp *rp; struct pim_autorp *autorp = pim->autorp; - char interval_str[16] = { 0 }; - char scope_str[16] = { 0 }; - char holdtime_str[32] = { 0 }; - char grp_str[64] = { 0 }; int writes = 0; if (!autorp->do_discovery) { @@ -1057,24 +1547,17 @@ int pim_autorp_config_write(struct pim_instance *pim, struct vty *vty) ++writes; } - if (autorp->announce_interval != DEFAULT_ANNOUNCE_INTERVAL) { - snprintf(interval_str, sizeof(interval_str), " interval %u", - autorp->announce_interval); - } - - if (autorp->announce_scope != DEFAULT_ANNOUNCE_SCOPE) { - snprintf(scope_str, sizeof(scope_str), " scope %u", - autorp->announce_scope); - } - - if (autorp->announce_holdtime != DEFAULT_ANNOUNCE_HOLDTIME) { - snprintf(holdtime_str, sizeof(holdtime_str), " holdtime %u", - autorp->announce_holdtime); - } - - if (strlen(interval_str) || strlen(scope_str) || strlen(holdtime_str)) { - vty_out(vty, " autorp announce%s%s%s\n", interval_str, - scope_str, holdtime_str); + if (autorp->announce_interval != DEFAULT_AUTORP_ANNOUNCE_INTERVAL || + autorp->announce_scope != DEFAULT_AUTORP_ANNOUNCE_SCOPE || + autorp->announce_holdtime != DEFAULT_AUTORP_ANNOUNCE_HOLDTIME) { + vty_out(vty, " autorp announce"); + if (autorp->announce_interval != DEFAULT_AUTORP_ANNOUNCE_INTERVAL) + vty_out(vty, " interval %u", autorp->announce_interval); + if (autorp->announce_scope != DEFAULT_AUTORP_ANNOUNCE_SCOPE) + vty_out(vty, " scope %u", autorp->announce_scope); + if (autorp->announce_holdtime != DEFAULT_AUTORP_ANNOUNCE_HOLDTIME) + vty_out(vty, " holdtime %u", autorp->announce_holdtime); + vty_out(vty, "\n"); ++writes; } @@ -1084,83 +1567,371 @@ int pim_autorp_config_write(struct pim_instance *pim, struct vty *vty) (is_default_prefix(&(rp->grp)) && strlen(rp->grplist) == 0)) continue; - /* Don't make sure the prefix list has multicast groups, user may not have created it yet */ - + vty_out(vty, " autorp announce %pI4", &(rp->addr)); if (!is_default_prefix(&(rp->grp))) - snprintfrr(grp_str, sizeof(grp_str), "%pFX", &(rp->grp)); + vty_out(vty, " %pFX", &(rp->grp)); else - snprintfrr(grp_str, sizeof(grp_str), "group-list %s", - rp->grplist); - - vty_out(vty, " autorp announce %pI4 %s\n", &(rp->addr), grp_str); + vty_out(vty, " group-list %s", rp->grplist); + vty_out(vty, "\n"); ++writes; } + if (autorp->send_rp_discovery) { + if (autorp->mapping_agent_addrsel.cfg_enable) { + vty_out(vty, " autorp send-rp-discovery"); + switch (autorp->mapping_agent_addrsel.cfg_mode) { + case CAND_ADDR_LO: + break; + case CAND_ADDR_ANY: + vty_out(vty, " source any"); + break; + case CAND_ADDR_IFACE: + vty_out(vty, " source interface %s", + autorp->mapping_agent_addrsel.cfg_ifname); + break; + case CAND_ADDR_EXPLICIT: + vty_out(vty, " source address %pPA", + &autorp->mapping_agent_addrsel.cfg_addr); + break; + } + vty_out(vty, "\n"); + ++writes; + } + + if (autorp->discovery_interval != DEFAULT_AUTORP_DISCOVERY_INTERVAL || + autorp->discovery_scope != DEFAULT_AUTORP_DISCOVERY_SCOPE || + autorp->discovery_holdtime != DEFAULT_AUTORP_DISCOVERY_HOLDTIME) { + vty_out(vty, " autorp send-rp-discovery"); + if (autorp->discovery_interval != DEFAULT_AUTORP_DISCOVERY_INTERVAL) + vty_out(vty, " interval %u", autorp->discovery_interval); + if (autorp->discovery_scope != DEFAULT_AUTORP_DISCOVERY_SCOPE) + vty_out(vty, " scope %u", autorp->discovery_scope); + if (autorp->discovery_holdtime != DEFAULT_AUTORP_DISCOVERY_HOLDTIME) + vty_out(vty, " holdtime %u", autorp->discovery_holdtime); + vty_out(vty, "\n"); + ++writes; + } + } + return writes; } -void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim, - json_object *json) +static void pim_autorp_show_autorp_json(struct pim_autorp *autorp, const char *component, + json_object *json, struct ttable *cand_table) { struct pim_autorp_rp *rp; + + if (!component || strmatch(component, "discovery")) { + json_object *disc_obj; + + disc_obj = json_object_new_object(); + json_object_boolean_add(disc_obj, "enabled", autorp->do_discovery); + if (autorp->do_discovery) { + json_object *rplist_obj; + + rplist_obj = json_object_new_object(); + frr_each (pim_autorp_rp, &(autorp->discovery_rp_list), rp) { + json_object *rp_obj; + json_object *grp_arr; + + rp_obj = json_object_new_object(); + json_object_string_addf(rp_obj, "rpAddress", "%pI4", &rp->addr); + json_object_int_add(rp_obj, "holdtime", rp->holdtime); + grp_arr = json_object_new_array(); + + if (strlen(rp->grplist)) { + struct prefix_list *pl; + struct prefix_list_entry *ple; + + pl = prefix_list_lookup(AFI_IP, rp->grplist); + if (pl == NULL) + continue; + + for (ple = pl->head; ple != NULL; ple = ple->next) { + json_object *grp_obj; + + grp_obj = json_object_new_object(); + json_object_boolean_add(grp_obj, "negative", + ple->type == PREFIX_DENY); + json_object_string_addf(grp_obj, "prefix", "%pFX", + &ple->prefix); + json_object_array_add(grp_arr, grp_obj); + } + } else { + json_object *grp_obj; + + grp_obj = json_object_new_object(); + json_object_boolean_add(grp_obj, "negative", false); + json_object_string_addf(grp_obj, "prefix", "%pFX", &rp->grp); + json_object_array_add(grp_arr, grp_obj); + } + + json_object_object_add(rp_obj, "groupRanges", grp_arr); + json_object_object_addf(rplist_obj, rp_obj, "%pI4", &rp->addr); + } + json_object_object_add(disc_obj, "rpList", rplist_obj); + } + json_object_object_add(json, "discovery", disc_obj); + } + + if (!component || strmatch(component, "candidate")) { + json_object *announce_obj; + + announce_obj = json_object_new_object(); + json_object_boolean_add(announce_obj, "enabled", + pim_autorp_rp_count(&autorp->candidate_rp_list) > 0); + if (pim_autorp_rp_count(&autorp->candidate_rp_list) > 0) { + json_object_int_add(announce_obj, "scope", autorp->announce_scope); + json_object_int_add(announce_obj, "interval", autorp->announce_interval); + json_object_int_add(announce_obj, "holdtime", + (autorp->announce_holdtime == + DEFAULT_AUTORP_ANNOUNCE_HOLDTIME + ? (autorp->announce_interval * 3) + : autorp->announce_holdtime)); + json_object_object_add(announce_obj, "rpList", + ttable_json_with_json_text(cand_table, "sss", + "rpAddress|groupRange|prefixList")); + } + json_object_object_add(json, "announce", announce_obj); + } + + if (!component || strmatch(component, "mapping-agent")) { + json_object *adv_obj; + + adv_obj = json_object_new_object(); + json_object_boolean_add(adv_obj, "enabled", autorp->send_rp_discovery); + if (autorp->send_rp_discovery) { + json_object *rplist_obj; + + json_object_boolean_add(adv_obj, "active", autorp->mapping_agent_active); + json_object_int_add(adv_obj, "scope", autorp->discovery_scope); + json_object_int_add(adv_obj, "interval", autorp->discovery_interval); + json_object_int_add(adv_obj, "holdtime", autorp->discovery_holdtime); + switch (autorp->mapping_agent_addrsel.cfg_mode) { + case CAND_ADDR_LO: + json_object_string_add(adv_obj, "source", "loopback"); + break; + case CAND_ADDR_ANY: + json_object_string_add(adv_obj, "source", "any"); + break; + case CAND_ADDR_IFACE: + json_object_string_add(adv_obj, "source", "interface"); + json_object_string_add(adv_obj, "interface", + autorp->mapping_agent_addrsel.cfg_ifname); + break; + case CAND_ADDR_EXPLICIT: + json_object_string_add(adv_obj, "source", "address"); + break; + } + json_object_string_addf(adv_obj, "address", "%pPA", + &autorp->mapping_agent_addrsel.run_addr); + + rplist_obj = json_object_new_object(); + frr_each (pim_autorp_rp, &(autorp->advertised_rp_list), rp) { + json_object *rp_obj; + json_object *grp_arr; + struct pim_autorp_grppfix *grppfix; + + rp_obj = json_object_new_object(); + json_object_string_addf(rp_obj, "rpAddress", "%pI4", &rp->addr); + grp_arr = json_object_new_array(); + frr_each (pim_autorp_grppfix, &rp->grp_pfix_list, grppfix) { + json_object *grp_obj; + + grp_obj = json_object_new_object(); + json_object_boolean_add(grp_obj, "negative", + grppfix->negative); + json_object_string_addf(grp_obj, "prefix", "%pFX", + &grppfix->grp); + json_object_array_add(grp_arr, grp_obj); + } + json_object_object_add(rp_obj, "groupRanges", grp_arr); + json_object_object_addf(rplist_obj, rp_obj, "%pI4", &rp->addr); + } + json_object_object_add(adv_obj, "rpList", rplist_obj); + } + json_object_object_add(json, "mapping-agent", adv_obj); + } +} + +void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim, const char *component, + json_object *json) +{ struct pim_autorp *autorp = pim->autorp; - struct ttable *tt = NULL; - char *table = NULL; - char grp_str[64] = { 0 }; - char plist_str[64] = { 0 }; - json_object *annouce_jobj; - - /* Prepare table. */ - tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); - ttable_add_row(tt, "RP address|group|prefix-list"); - tt->style.cell.rpad = 2; - tt->style.corner = '+'; - ttable_restyle(tt); + struct pim_autorp_rp *rp; + struct ttable *cand_table = NULL; + struct ttable *adv_table = NULL; + struct ttable *disc_table = NULL; + char *tmp; - frr_each_safe (pim_autorp_rp, &(autorp->candidate_rp_list), rp) { - if (!is_default_prefix(&(rp->grp))) - snprintfrr(grp_str, sizeof(grp_str), "%pFX", &(rp->grp)); - else - snprintfrr(plist_str, sizeof(plist_str), "%s", - rp->grplist); + if (autorp == NULL) + return; - ttable_add_row(tt, "%pI4|%s|%s", &(rp->addr), grp_str, - plist_str); + /* We may use the candidate table in the json output, so prepare it first. */ + if (!component || strmatch(component, "candidate")) { + cand_table = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(cand_table, "RP address|Group Range|Prefix-List"); + cand_table->style.cell.rpad = 2; + cand_table->style.corner = '+'; + ttable_restyle(cand_table); + + frr_each (pim_autorp_rp, &(autorp->candidate_rp_list), rp) { + if (strlen(rp->grplist)) + ttable_add_row(cand_table, "%pI4|%s|%s", &(rp->addr), "-", + rp->grplist); + else + ttable_add_row(cand_table, "%pI4|%pFX|%s", &(rp->addr), &(rp->grp), + "-"); + } } if (json) { - json_object_boolean_add(json, "discoveryEnabled", - autorp->do_discovery); - - annouce_jobj = json_object_new_object(); - json_object_int_add(annouce_jobj, "scope", - autorp->announce_scope); - json_object_int_add(annouce_jobj, "interval", - autorp->announce_interval); - json_object_int_add(annouce_jobj, "holdtime", - autorp->announce_holdtime); - json_object_object_add(annouce_jobj, "rpList", - ttable_json_with_json_text( - tt, "sss", - "rpAddress|group|prefixList")); - - json_object_object_add(json, "announce", annouce_jobj); - } else { - vty_out(vty, "AutoRP Discovery is %sabled\n", - (autorp->do_discovery ? "en" : "dis")); - vty_out(vty, "AutoRP Candidate RPs\n"); - vty_out(vty, " interval %us, scope %u, holdtime %us\n", - autorp->announce_interval, autorp->announce_scope, - (autorp->announce_holdtime == DEFAULT_ANNOUNCE_HOLDTIME - ? (autorp->announce_interval * 3) - : autorp->announce_holdtime)); + pim_autorp_show_autorp_json(autorp, component, json, cand_table); + if (cand_table) + ttable_del(cand_table); + return; + } - vty_out(vty, "\n"); + /* Prepare discovered RP's table. */ + if (!component || strmatch(component, "discovery")) { + disc_table = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(disc_table, "RP address|Group Range"); + disc_table->style.cell.rpad = 2; + disc_table->style.corner = '+'; + ttable_restyle(disc_table); + + frr_each (pim_autorp_rp, &(autorp->discovery_rp_list), rp) { + if (strlen(rp->grplist)) { + struct prefix_list *pl; + struct prefix_list_entry *ple; + bool first = true; + + pl = prefix_list_lookup(AFI_IP, rp->grplist); + + if (pl == NULL) { + ttable_add_row(disc_table, + "%pI4|failed to find prefix list %s", + &(rp->addr), rp->grplist); + continue; + } - table = ttable_dump(tt, "\n"); - vty_out(vty, "%s\n", table); - XFREE(MTYPE_TMP_TTABLE, table); + for (ple = pl->head; ple != NULL; ple = ple->next) { + if (first) + ttable_add_row(disc_table, "%pI4|%s%pFX", + &(rp->addr), + (ple->type == PREFIX_DENY ? "!" + : " "), + &ple->prefix); + else + ttable_add_row(disc_table, "%s|%s%pFX", " ", + (ple->type == PREFIX_DENY ? "!" + : " "), + &ple->prefix); + first = false; + } + } else + ttable_add_row(disc_table, "%pI4| %pFX", &(rp->addr), &(rp->grp)); + } + } + + /* Prepare discovery RP's table (mapping-agent). */ + if (!component || strmatch(component, "mapping-agent")) { + adv_table = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(adv_table, "RP address|Group Range"); + adv_table->style.cell.rpad = 2; + adv_table->style.corner = '+'; + ttable_restyle(adv_table); + + frr_each (pim_autorp_rp, &(autorp->advertised_rp_list), rp) { + struct pim_autorp_grppfix *grppfix; + bool first = true; + + frr_each (pim_autorp_grppfix, &rp->grp_pfix_list, grppfix) { + if (first) + ttable_add_row(adv_table, "%pI4|%s%pFX", &rp->addr, + grppfix->negative ? "!" : " ", &grppfix->grp); + else + ttable_add_row(adv_table, "%s|%s%pFX", " ", + grppfix->negative ? "!" : " ", &grppfix->grp); + first = false; + } + } + } + + if (!component || strmatch(component, "discovery")) { + vty_out(vty, "AutoRP Discovery is %sabled\n", (autorp->do_discovery ? "en" : "dis")); + if (autorp->do_discovery) { + tmp = ttable_dump(disc_table, "\n"); + vty_out(vty, "\n"); + vty_out(vty, "Discovered RP's (count=%u)\n", + (uint32_t)pim_autorp_rp_count(&autorp->discovery_rp_list)); + vty_out(vty, "%s\n", tmp); + XFREE(MTYPE_TMP_TTABLE, tmp); + } else + vty_out(vty, "\n"); + } + + if (!component || strmatch(component, "candidate")) { + vty_out(vty, "AutoRP Announcement is %sabled\n", + (pim_autorp_rp_count(&autorp->candidate_rp_list) > 0 ? "en" : "dis")); + if (pim_autorp_rp_count(&autorp->candidate_rp_list) > 0) { + tmp = ttable_dump(cand_table, "\n"); + vty_out(vty, " interval %us scope %u holdtime %us\n", + autorp->announce_interval, autorp->announce_scope, + (autorp->announce_holdtime == DEFAULT_AUTORP_ANNOUNCE_HOLDTIME + ? (autorp->announce_interval * 3) + : autorp->announce_holdtime)); + vty_out(vty, "\n"); + vty_out(vty, "Candidate RP's (count=%u)\n", + (uint32_t)pim_autorp_rp_count(&autorp->candidate_rp_list)); + vty_out(vty, "%s\n", tmp); + XFREE(MTYPE_TMP_TTABLE, tmp); + } else + vty_out(vty, "\n"); + } + + if (!component || strmatch(component, "mapping-agent")) { + vty_out(vty, "AutoRP Mapping-Agent is %sabled\n", + (autorp->send_rp_discovery ? "en" : "dis")); + if (autorp->send_rp_discovery) { + vty_out(vty, " interval %us scope %u holdtime %us\n", + autorp->discovery_interval, autorp->discovery_scope, + autorp->discovery_holdtime); + vty_out(vty, " source %pPA", &autorp->mapping_agent_addrsel.run_addr); + switch (autorp->mapping_agent_addrsel.cfg_mode) { + case CAND_ADDR_LO: + vty_out(vty, " (loopback)"); + break; + case CAND_ADDR_ANY: + vty_out(vty, " (any)"); + break; + case CAND_ADDR_IFACE: + vty_out(vty, " (interface %s)", + autorp->mapping_agent_addrsel.cfg_ifname); + break; + case CAND_ADDR_EXPLICIT: + vty_out(vty, " (explicit address)"); + break; + } + vty_out(vty, "\n"); + + if (autorp->mapping_agent_active) { + tmp = ttable_dump(adv_table, "\n"); + vty_out(vty, "\n"); + vty_out(vty, "Advertised RP's (count=%u)\n", + (uint32_t)pim_autorp_rp_count(&autorp->advertised_rp_list)); + vty_out(vty, "%s\n", tmp); + XFREE(MTYPE_TMP_TTABLE, tmp); + } else + vty_out(vty, " Mapping agent is inactive\n"); + } else + vty_out(vty, "\n"); } - ttable_del(tt); + if (cand_table) + ttable_del(cand_table); + if (adv_table) + ttable_del(adv_table); + if (disc_table) + ttable_del(disc_table); } diff --git a/pimd/pim_autorp.h b/pimd/pim_autorp.h index a0b029d00ae6..e4c653010924 100644 --- a/pimd/pim_autorp.h +++ b/pimd/pim_autorp.h @@ -14,16 +14,21 @@ #define AUTORP_VERSION 1 #define AUTORP_ANNOUNCEMENT_TYPE 1 #define AUTORP_DISCOVERY_TYPE 2 -#define PIM_VUNKNOWN 0 -#define PIM_V1 1 -#define PIM_V2 2 -#define PIM_V1_2 3 +#define AUTORP_PIM_VUNKNOWN 0 +#define AUTORP_PIM_V1 1 +#define AUTORP_PIM_V2 2 +#define AUTORP_PIM_V1_2 3 -#define DEFAULT_ANNOUNCE_INTERVAL 60 -#define DEFAULT_ANNOUNCE_SCOPE 31 -#define DEFAULT_ANNOUNCE_HOLDTIME -1 +#define DEFAULT_AUTORP_ANNOUNCE_INTERVAL 60 +#define DEFAULT_AUTORP_ANNOUNCE_SCOPE 31 +#define DEFAULT_AUTORP_ANNOUNCE_HOLDTIME -1 + +#define DEFAULT_AUTORP_DISCOVERY_INTERVAL 60 +#define DEFAULT_AUTORP_DISCOVERY_SCOPE 31 +#define DEFAULT_AUTORP_DISCOVERY_HOLDTIME 180 PREDECL_SORTLIST_UNIQ(pim_autorp_rp); +PREDECL_SORTLIST_UNIQ(pim_autorp_grppfix); struct autorp_pkt_grp { #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -79,7 +84,15 @@ struct pim_autorp_rp { struct event *hold_timer; struct prefix grp; char grplist[32]; - struct pim_autorp_rp_item list; + struct pim_autorp_grppfix_head grp_pfix_list; + struct pim_autorp_rp_item item; +}; + +struct pim_autorp_grppfix { + struct prefix grp; + struct in_addr rp; + bool negative; + struct pim_autorp_grppfix_item item; }; struct pim_autorp { @@ -96,13 +109,18 @@ struct pim_autorp { struct event *announce_timer; /* Event for sending discovery packets*/ - /* struct event *discovery_timer; */ + struct event *send_discovery_timer; /* Flag enabling reading discovery packets */ bool do_discovery; /* Flag enabling mapping agent (reading announcements and sending discovery)*/ - /* bool do_mapping; */ + bool send_rp_discovery; + + /* Flag indicating if we are sending discovery messages (true) or if a higher IP mapping + * agent preemptied our sending (false) + */ + bool mapping_agent_active; /* List of RP's in received discovery packets */ struct pim_autorp_rp_head discovery_rp_list; @@ -111,7 +129,12 @@ struct pim_autorp { struct pim_autorp_rp_head candidate_rp_list; /* List of announced RP's to send in discovery packets */ - /* struct pim_autorp_rp_head mapping_rp_list; */ + struct pim_autorp_rp_head mapping_rp_list; + + /* List of the last advertised RP's, via mapping agent discovery + * This is only filled if a discovery message was sent + */ + struct pim_autorp_rp_head advertised_rp_list; /* Packet parameters for sending announcement packets */ uint8_t announce_scope; @@ -119,32 +142,32 @@ struct pim_autorp { int32_t announce_holdtime; /* Pre-built announcement packet, only changes when configured RP's or packet parameters change */ - uint8_t *annouce_pkt; - uint16_t annouce_pkt_sz; - - /* TODO: Packet parameters for sending discovery packets - * int discovery_scope; - * int discovery_interval; - * int discovery_holdtime; - */ + uint8_t *announce_pkt; + uint16_t announce_pkt_sz; + + /* Packet parameters for sending discovery packets */ + uint8_t discovery_scope; + uint16_t discovery_interval; + uint16_t discovery_holdtime; + struct cand_addrsel mapping_agent_addrsel; }; #define AUTORP_GRPLEN 6 #define AUTORP_RPLEN 6 #define AUTORP_HDRLEN 8 +void pim_autorp_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist); bool pim_autorp_rm_candidate_rp(struct pim_instance *pim, pim_addr rpaddr); -void pim_autorp_add_candidate_rp_group(struct pim_instance *pim, - pim_addr rpaddr, struct prefix group); +void pim_autorp_add_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, + struct prefix group); bool pim_autorp_rm_candidate_rp_group(struct pim_instance *pim, pim_addr rpaddr, struct prefix group); -void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim, - pim_addr rpaddr, const char *plist); -bool pim_autorp_rm_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, - const char *plist); +void pim_autorp_add_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, const char *plist); +bool pim_autorp_rm_candidate_rp_plist(struct pim_instance *pim, pim_addr rpaddr, const char *plist); void pim_autorp_announce_scope(struct pim_instance *pim, uint8_t scope); void pim_autorp_announce_interval(struct pim_instance *pim, uint16_t interval); void pim_autorp_announce_holdtime(struct pim_instance *pim, int32_t holdtime); +void pim_autorp_send_discovery_apply(struct pim_autorp *autorp); void pim_autorp_add_ifp(struct interface *ifp); void pim_autorp_rm_ifp(struct interface *ifp); void pim_autorp_start_discovery(struct pim_instance *pim); @@ -152,7 +175,7 @@ void pim_autorp_stop_discovery(struct pim_instance *pim); void pim_autorp_init(struct pim_instance *pim); void pim_autorp_finish(struct pim_instance *pim); int pim_autorp_config_write(struct pim_instance *pim, struct vty *vty); -void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim, +void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim, const char *component, json_object *json); #endif diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index a44e4e08f3d5..75104141ae8e 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -1769,14 +1769,14 @@ static inline pim_addr if_highest_addr(pim_addr cur, struct interface *ifp) return cur; } -static void cand_addrsel_clear(struct cand_addrsel *asel) +void cand_addrsel_clear(struct cand_addrsel *asel) { asel->run = false; asel->run_addr = PIMADDR_ANY; } /* returns whether address or active changed */ -static bool cand_addrsel_update(struct cand_addrsel *asel, struct vrf *vrf) +bool cand_addrsel_update(struct cand_addrsel *asel, struct vrf *vrf) { bool is_any = false, prev_run = asel->run; struct interface *ifp = NULL; diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h index 1eacc1be5770..b1582d0dfa87 100644 --- a/pimd/pim_bsm.h +++ b/pimd/pim_bsm.h @@ -64,7 +64,7 @@ enum cand_addr { CAND_ADDR_EXPLICIT, }; -/* used separately for Cand-RP and Cand-BSR */ +/* used separately for Cand-RP, Cand-BSR, and AutoRP mapping agent */ struct cand_addrsel { bool cfg_enable; enum cand_addr cfg_mode : 8; @@ -369,6 +369,9 @@ void pim_cand_rp_trigger(struct bsm_scope *scope); void pim_cand_rp_grp_add(struct bsm_scope *scope, const prefix_pim *p); void pim_cand_rp_grp_del(struct bsm_scope *scope, const prefix_pim *p); +void cand_addrsel_clear(struct cand_addrsel *asel); +bool cand_addrsel_update(struct cand_addrsel *asel, struct vrf *vrf); + void pim_cand_addrs_changed(void); int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf, diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 934da2d53e67..732dd2971c8d 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2822,65 +2822,49 @@ DEFPY (show_ip_pim_rp_vrf_all, DEFPY (show_ip_pim_autorp, show_ip_pim_autorp_cmd, - "show ip pim [vrf NAME] autorp [json$json]", + "show ip pim [vrf ] autorp [discovery|candidate|mapping-agent]$component [json$json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR + "All VRF's\n" "PIM AutoRP information\n" + "RP Discovery details\n" + "Candidate RP details\n" + "Mapping Agent details\n" JSON_STR) { - struct vrf *v; json_object *json_parent = NULL; - - v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); - if (!v || !v->info) { - if (!json) - vty_out(vty, "%% Unable to find pim instance\n"); - return CMD_WARNING; - } + struct vrf *v; if (json) json_parent = json_object_new_object(); - pim_autorp_show_autorp(vty, v->info, json_parent); - - if (json) - vty_json(vty, json_parent); - - return CMD_SUCCESS; -} - -DEFPY (show_ip_pim_autorp_vrf_all, - show_ip_pim_autorp_vrf_all_cmd, - "show ip pim vrf all autorp [json$json]", - SHOW_STR - IP_STR - PIM_STR - VRF_CMD_HELP_STR - "PIM AutoRP information\n" - JSON_STR) -{ - struct vrf *vrf; - json_object *json_parent = NULL; - json_object *json_vrf = NULL; + if (vrf && strmatch(vrf, "all")) { + json_object *json_vrf = NULL; - if (json) - json_parent = json_object_new_object(); + RB_FOREACH (v, vrf_name_head, &vrfs_by_name) { + if (!v || !v->info) + continue; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { - if (vrf->info) { - if (!json) - vty_out(vty, "VRF: %s\n", vrf->name); - else + if (json) json_vrf = json_object_new_object(); + else + vty_out(vty, "VRF: %s\n", v->name); - pim_autorp_show_autorp(vty, vrf->info, json_vrf); + pim_autorp_show_autorp(vty, v->info, component, json_vrf); if (json) - json_object_object_add(json_parent, vrf->name, - json_vrf); + json_object_object_add(json_parent, v->name, json_vrf); + } + } else { + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + if (!v || !v->info) { + if (!json) + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; } + pim_autorp_show_autorp(vty, v->info, component, json_parent); } if (json) @@ -4609,13 +4593,17 @@ DEFPY (pim_autorp_announce_rp, "Prefix list\n" "List name\n") { - return pim_process_autorp_candidate_rp_cmd(vty, no, rpaddr_str, (grp_str ? grp : NULL), - plist); + if (grp_str && (!pim_addr_is_multicast(grp->prefix) || grp->prefixlen < 4)) { + vty_out(vty, "%% group prefix %pFX is not a valid multicast range\n", grp); + return CMD_WARNING_CONFIG_FAILED; + } + + return pim_process_autorp_candidate_rp_cmd(vty, no, rpaddr_str, grp_str, plist); } DEFPY (pim_autorp_announce_scope_int, pim_autorp_announce_scope_int_cmd, - "[no] autorp announce ![{scope (1-255) | interval (1-65535) | holdtime (0-65535)}]", + "[no] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)}", NO_STR "AutoRP\n" "AutoRP Candidate RP announcement\n" @@ -4626,11 +4614,44 @@ DEFPY (pim_autorp_announce_scope_int, "Announcement holdtime\n" "Time in seconds\n") { - return pim_process_autorp_announce_scope_int_cmd(vty, no, scope_str, - interval_str, + return pim_process_autorp_announce_scope_int_cmd(vty, no, scope_str, interval_str, holdtime_str); } +DEFPY (pim_autorp_send_rp_discovery, + pim_autorp_send_rp_discovery_cmd, + "[no] autorp send-rp-discovery [source
]", + NO_STR + "AutoRP\n" + "Enable AutoRP mapping agent\n" + "Specify AutoRP discovery source\n" + "Local address\n" + IP_ADDR_STR + "Local Interface (uses highest address)\n" + IFNAME_STR + "Highest loopback address (default)\n" + "Highest address of any interface\n") +{ + return pim_process_autorp_send_rp_discovery_cmd(vty, no, any, loopback, ifname, address_str); +} + +DEFPY (pim_autorp_send_rp_discovery_scope_int, + pim_autorp_send_rp_discovery_scope_int_cmd, + "[no] autorp send-rp-discovery {scope (0-255) | interval (1-65535) | holdtime (0-65535)}", + NO_STR + "AutoRP\n" + "Enable AutoRP mapping agent\n" + "Packet scope (TTL)\n" + "TTL value\n" + "Discovery TX interval\n" + "Time in seconds\n" + "Announcement holdtime\n" + "Time in seconds\n") +{ + return pim_process_autorp_send_rp_discovery_scope_int_cmd(vty, no, scope_str, interval_str, + holdtime_str); +} + DEFPY (pim_bsr_candidate_bsr, pim_bsr_candidate_bsr_cmd, "[no] bsr candidate-bsr [{priority (0-255)|source
}]", @@ -8855,6 +8876,8 @@ void pim_cmd_init(void) install_element(PIM_NODE, &pim_autorp_discovery_cmd); install_element(PIM_NODE, &pim_autorp_announce_rp_cmd); install_element(PIM_NODE, &pim_autorp_announce_scope_int_cmd); + install_element(PIM_NODE, &pim_autorp_send_rp_discovery_cmd); + install_element(PIM_NODE, &pim_autorp_send_rp_discovery_scope_int_cmd); install_element(PIM_NODE, &no_pim_ssm_prefix_list_cmd); install_element(PIM_NODE, &no_pim_ssm_prefix_list_name_cmd); install_element(PIM_NODE, &pim_ssm_prefix_list_cmd); @@ -9010,7 +9033,6 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_rp_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_autorp_cmd); - install_element(VIEW_NODE, &show_ip_pim_autorp_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_bsr_cmd); install_element(VIEW_NODE, &show_ip_multicast_cmd); install_element(VIEW_NODE, &show_ip_multicast_vrf_all_cmd); diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index 02ddea8252d6..1476845a5d59 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -630,139 +630,88 @@ int pim_process_no_autorp_cmd(struct vty *vty) return nb_cli_apply_changes(vty, NULL); } -int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, - const char *rpaddr_str, - const struct prefix_ipv4 *grp, - const char *plist) +int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, const char *rpaddr_str, + const char *grp, const char *plist) { - char xpath[XPATH_MAXLEN]; - char grpstr[64]; - if (no) { - if ((grp && !is_default_prefix((const struct prefix *)grp)) || plist) { + if (grp || plist) { /* If any single values are set, only destroy those */ - if (grp && !is_default_prefix((const struct prefix *)grp)) { - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']/group", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, - NULL); - } - if (plist) { - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']/prefix-list", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, - NULL); - } - } else { + if (grp) + nb_cli_enqueue_change(vty, "./group", NB_OP_DESTROY, NULL); + if (plist) + nb_cli_enqueue_change(vty, "./prefix-list", NB_OP_DESTROY, NULL); + } else /* No values set, remove the entire RP */ - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - } + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); } else { - if ((grp && !is_default_prefix((const struct prefix *)grp)) || plist) { - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - if (grp && !is_default_prefix((const struct prefix *)grp)) { - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']/group", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - prefix2str(grp, grpstr, - sizeof(grpstr))); - } - if (plist) { - snprintfrr(xpath, sizeof(xpath), - "%s/candidate-rp-list[rp-address='%s']/prefix-list", - FRR_PIM_AUTORP_XPATH, rpaddr_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - plist); - } - } else { - return CMD_WARNING_CONFIG_FAILED; - } + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + if (grp) + nb_cli_enqueue_change(vty, "./group", NB_OP_MODIFY, grp); + if (plist) + nb_cli_enqueue_change(vty, "./prefix-list", NB_OP_MODIFY, plist); } - return nb_cli_apply_changes(vty, NULL); + return nb_cli_apply_changes(vty, "%s/candidate-rp-list[rp-address='%s']", + FRR_PIM_AUTORP_XPATH, rpaddr_str); } -int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no, - const char *scope, - const char *interval, - const char *holdtime) +int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no, const char *scope, + const char *interval, const char *holdtime) { - char xpath[XPATH_MAXLEN]; + /* At least one value is required, so set/delete anything defined */ + enum nb_operation op = (no ? NB_OP_DESTROY : NB_OP_MODIFY); + + if (scope) + nb_cli_enqueue_change(vty, "./announce-scope", op, scope); + if (interval) + nb_cli_enqueue_change(vty, "./announce-interval", op, interval); + if (holdtime) + nb_cli_enqueue_change(vty, "./announce-holdtime", op, holdtime); + return nb_cli_apply_changes(vty, "%s", FRR_PIM_AUTORP_XPATH); +} + +int pim_process_autorp_send_rp_discovery_cmd(struct vty *vty, bool no, bool any, bool loopback, + const char *ifname, const char *addr) +{ + /* Just take any "no" version of this command as disable the mapping agent */ + nb_cli_enqueue_change(vty, "./send-rp-discovery", NB_OP_MODIFY, (no ? "false" : "true")); if (no) { - if (scope || interval || holdtime) { - /* If any single values are set, only destroy those */ - if (scope) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-scope"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, - NULL); - } - if (interval) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-interval"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, - NULL); - } - if (holdtime) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-holdtime"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, - NULL); - } - } else { - /* No values set, remove all */ - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, "announce-scope"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, "announce-interval"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, "announce-holdtime"); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - } + nb_cli_enqueue_change(vty, "./if-any", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./interface", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./address", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./if-loopback", NB_OP_DESTROY, NULL); } else { - if (scope || interval || holdtime) { - if (scope) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-scope"); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - scope); - } - if (interval) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-interval"); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - interval); - } - if (holdtime) { - snprintfrr(xpath, sizeof(xpath), "%s/%s", - FRR_PIM_AUTORP_XPATH, - "announce-holdtime"); - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, - holdtime); - } - } else { - return CMD_WARNING_CONFIG_FAILED; - } + /* Enabling mapping agent. Loopback is default, so any non-no for of the command will + * enable the mapping agent. + */ + if (any) + nb_cli_enqueue_change(vty, "./if-any", NB_OP_CREATE, NULL); + else if (ifname) + nb_cli_enqueue_change(vty, "./interface", NB_OP_MODIFY, ifname); + else if (addr) + nb_cli_enqueue_change(vty, "./address", NB_OP_MODIFY, addr); + else + nb_cli_enqueue_change(vty, "./if-loopback", NB_OP_CREATE, NULL); } - return nb_cli_apply_changes(vty, NULL); + return nb_cli_apply_changes(vty, "%s/%s", FRR_PIM_AUTORP_XPATH, "mapping-agent"); +} + +int pim_process_autorp_send_rp_discovery_scope_int_cmd(struct vty *vty, bool no, const char *scope, + const char *interval, const char *holdtime) +{ + /* At least one value is required, so only set/delete the values specified */ + enum nb_operation op = (no ? NB_OP_DESTROY : NB_OP_MODIFY); + + if (scope) + nb_cli_enqueue_change(vty, "./discovery-scope", op, scope); + if (interval) + nb_cli_enqueue_change(vty, "./discovery-interval", op, interval); + if (holdtime) + nb_cli_enqueue_change(vty, "./discovery-holdtime", op, holdtime); + + return nb_cli_apply_changes(vty, "%s/%s", FRR_PIM_AUTORP_XPATH, "mapping-agent"); } bool pim_sgaddr_match(pim_sgaddr item, pim_sgaddr match) diff --git a/pimd/pim_cmd_common.h b/pimd/pim_cmd_common.h index d7c97e31d45d..a067647113b1 100644 --- a/pimd/pim_cmd_common.h +++ b/pimd/pim_cmd_common.h @@ -37,14 +37,14 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str, const char *prefix_list); int pim_process_autorp_cmd(struct vty *vty); int pim_process_no_autorp_cmd(struct vty *vty); -int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, - const char *rpaddr_str, - const struct prefix_ipv4 *grp, - const char *plist); -int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no, - const char *scope, - const char *interval, - const char *holdtime); +int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, const char *rpaddr_str, + const char *grp, const char *plist); +int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no, const char *scope, + const char *interval, const char *holdtime); +int pim_process_autorp_send_rp_discovery_cmd(struct vty *vty, bool no, bool any, bool loopback, + const char *ifname, const char *addr); +int pim_process_autorp_send_rp_discovery_scope_int_cmd(struct vty *vty, bool no, const char *scope, + const char *interval, const char *holdtime); int pim_process_ip_pim_cmd(struct vty *vty); int pim_process_no_ip_pim_cmd(struct vty *vty); int pim_process_ip_pim_passive_cmd(struct vty *vty, bool enable); diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 1dc66be82d7d..cb0f5e8255c8 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -447,6 +447,58 @@ const struct frr_yang_module_info frr_pim_rp_info = { .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy, } }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/send-rp-discovery", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_send_rp_discovery_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-scope", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_scope_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-interval", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_interval_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-holdtime", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_holdtime_modify, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/address", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/interface", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/if-loopback", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/if-any", + .cbs = { + .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_create, + .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy, + } + }, { .xpath = NULL, }, diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index b45af3d589da..49a94f363e34 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -191,6 +191,20 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_send_rp_discovery_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_scope_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_interval_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_holdtime_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_create( + struct nb_cb_create_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy( + struct nb_cb_destroy_args *args); /* frr-cand-bsr */ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_create( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 87338f37c0ea..636669985790 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -41,12 +41,24 @@ int funcname(struct argtype *args) \ } \ MACRO_REQUIRE_SEMICOLON() +#define pim6_autorp_err(funcname, argtype) \ + int funcname(struct argtype *args) \ + { \ + snprintf(args->errmsg, args->errmsg_len, \ + "Trying to configure AutoRP in pim6d. " \ + "AutoRP does not exist for IPv6."); \ + return NB_ERR_VALIDATION; \ + } \ + MACRO_REQUIRE_SEMICOLON() + #define yang_dnode_get_pimaddr yang_dnode_get_ipv6 #else /* PIM_IPV != 6 */ #define pim6_msdp_err(funcname, argtype) \ MACRO_REQUIRE_SEMICOLON() +#define pim6_autorp_err(funcname, argtype) MACRO_REQUIRE_SEMICOLON() + #define yang_dnode_get_pimaddr yang_dnode_get_ipv4 #endif /* PIM_IPV != 6 */ @@ -490,6 +502,26 @@ static void change_query_max_response_time(struct interface *ifp, #endif /* PIM_IPV == 4 */ } +static void yang_addrsel(struct cand_addrsel *addrsel, const struct lyd_node *node) +{ + memset(addrsel->cfg_ifname, 0, sizeof(addrsel->cfg_ifname)); + addrsel->cfg_addr = PIMADDR_ANY; + + if (yang_dnode_exists(node, "if-any")) { + addrsel->cfg_mode = CAND_ADDR_ANY; + } else if (yang_dnode_exists(node, "address")) { + addrsel->cfg_mode = CAND_ADDR_EXPLICIT; + yang_dnode_get_pimaddr(&addrsel->cfg_addr, node, "address"); + } else if (yang_dnode_exists(node, "interface")) { + addrsel->cfg_mode = CAND_ADDR_IFACE; + strlcpy(addrsel->cfg_ifname, yang_dnode_get_string(node, "interface"), + sizeof(addrsel->cfg_ifname)); + } else if (yang_dnode_exists(node, "if-loopback")) { + addrsel->cfg_mode = CAND_ADDR_LO; + } + addrsel->cfg_enable = true; +} + int routing_control_plane_protocols_name_validate( struct nb_cb_create_args *args) { @@ -2794,13 +2826,77 @@ int pim_embedded_rp_maximum_rps_modify(struct nb_cb_modify_args *args) } } +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create, + nb_cb_create_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy, + nb_cb_destroy_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_send_rp_discovery_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_scope_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_interval_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_holdtime_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_create, + nb_cb_create_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_modify, + nb_cb_modify_args); +pim6_autorp_err( + routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy, + nb_cb_destroy_args); + +#if PIM_IPV == 4 /* * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled */ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; bool enabled; @@ -2820,14 +2916,12 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim_autorp_stop_discovery(pim); break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; bool enabled; @@ -2846,7 +2940,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim_autorp_start_discovery(pim); break; } -#endif return NB_OK; } @@ -2857,7 +2950,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; uint8_t scope; @@ -2872,15 +2964,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim = vrf->info; scope = yang_dnode_get_uint8(args->dnode, NULL); pim_autorp_announce_scope(pim, scope); + break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; @@ -2893,8 +2984,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; pim_autorp_announce_scope(pim, 0); + break; } -#endif return NB_OK; } @@ -2905,7 +2996,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; uint16_t interval; @@ -2920,15 +3010,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim = vrf->info; interval = yang_dnode_get_uint16(args->dnode, NULL); pim_autorp_announce_interval(pim, interval); + break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; @@ -2941,8 +3030,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; pim_autorp_announce_interval(pim, 0); + break; } -#endif return NB_OK; } @@ -2953,7 +3042,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; uint16_t holdtime; @@ -2968,15 +3056,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim = vrf->info; holdtime = yang_dnode_get_uint16(args->dnode, NULL); pim_autorp_announce_holdtime(pim, holdtime); + break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; @@ -2990,8 +3077,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp pim = vrf->info; /* 0 is a valid value, so -1 indicates deleting (go back to default) */ pim_autorp_announce_holdtime(pim, -1); + break; } -#endif return NB_OK; } @@ -3002,7 +3089,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create( struct nb_cb_create_args *args) { -#if PIM_IPV == 4 switch (args->event) { case NB_EV_VALIDATE: case NB_EV_PREPARE: @@ -3010,14 +3096,12 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; pim_addr rp_addr; @@ -3035,7 +3119,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp return NB_ERR_INCONSISTENCY; break; } -#endif return NB_OK; } @@ -3046,7 +3129,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; struct prefix group; @@ -3064,15 +3146,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp yang_dnode_get_prefix(&group, args->dnode, NULL); apply_mask(&group); pim_autorp_add_candidate_rp_group(pim, rp_addr, group); + break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; struct prefix group; @@ -3091,8 +3172,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp apply_mask(&group); if (!pim_autorp_rm_candidate_rp_group(pim, rp_addr, group)) return NB_ERR_INCONSISTENCY; + break; } -#endif return NB_OK; } @@ -3103,7 +3184,6 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify( struct nb_cb_modify_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; pim_addr rp_addr; @@ -3120,15 +3200,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp plist = yang_dnode_get_string(args->dnode, NULL); yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address"); pim_autorp_add_candidate_rp_plist(pim, rp_addr, plist); + break; } -#endif return NB_OK; } int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy( struct nb_cb_destroy_args *args) { -#if PIM_IPV == 4 struct vrf *vrf; struct pim_instance *pim; pim_addr rp_addr; @@ -3148,31 +3227,211 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp return NB_ERR_INCONSISTENCY; break; } -#endif return NB_OK; } -static void yang_addrsel(struct cand_addrsel *addrsel, - const struct lyd_node *node) +/* + * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/send-rp-discovery + */ +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_send_rp_discovery_modify( + struct nb_cb_modify_args *args) { - memset(addrsel->cfg_ifname, 0, sizeof(addrsel->cfg_ifname)); - addrsel->cfg_addr = PIMADDR_ANY; + struct vrf *vrf; + struct pim_instance *pim; - if (yang_dnode_exists(node, "if-any")) { - addrsel->cfg_mode = CAND_ADDR_ANY; - } else if (yang_dnode_exists(node, "address")) { - addrsel->cfg_mode = CAND_ADDR_EXPLICIT; - yang_dnode_get_pimaddr(&addrsel->cfg_addr, node, "address"); - } else if (yang_dnode_exists(node, "interface")) { - addrsel->cfg_mode = CAND_ADDR_IFACE; - strlcpy(addrsel->cfg_ifname, - yang_dnode_get_string(node, "interface"), - sizeof(addrsel->cfg_ifname)); - } else if (yang_dnode_exists(node, "if-loopback")) { - addrsel->cfg_mode = CAND_ADDR_LO; + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) { + pim->autorp->send_rp_discovery = yang_dnode_get_bool(args->dnode, NULL); + pim_autorp_send_discovery_apply(pim->autorp); + } else + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-scope + */ +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_scope_modify( + struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + pim->autorp->discovery_scope = yang_dnode_get_uint8(args->dnode, NULL); + else + return NB_ERR_INCONSISTENCY; + break; } + + return NB_OK; +} + +/* + * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-interval + */ +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_interval_modify( + struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + pim->autorp->discovery_interval = yang_dnode_get_uint16(args->dnode, NULL); + else + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/discovery-holdtime + */ +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_discovery_holdtime_modify( + struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + pim->autorp->discovery_holdtime = yang_dnode_get_uint16(args->dnode, NULL); + else + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; +} + +static int pim_autorp_mapping_agent_addrsel(struct pim_autorp *autorp, + const struct lyd_node *mapping_agent_node, + struct vrf *vrf) +{ + yang_addrsel(&autorp->mapping_agent_addrsel, mapping_agent_node); + if (cand_addrsel_update(&autorp->mapping_agent_addrsel, vrf)) + pim_autorp_send_discovery_apply(autorp); + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/address + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/interface + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/if-loopback + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/mapping-agent/if-any + */ +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_create( + struct nb_cb_create_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + const struct lyd_node *mapping_agent_node; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + mapping_agent_node = yang_dnode_get_parent(args->dnode, "mapping-agent"); + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + return pim_autorp_mapping_agent_addrsel(pim->autorp, mapping_agent_node, + vrf); + else + return NB_ERR_INCONSISTENCY; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_modify( + struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + const struct lyd_node *mapping_agent_node; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + mapping_agent_node = yang_dnode_get_parent(args->dnode, "mapping-agent"); + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + return pim_autorp_mapping_agent_addrsel(pim->autorp, mapping_agent_node, + vrf); + else + return NB_ERR_INCONSISTENCY; + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_mapping_agent_addrsel_destroy( + struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct pim_instance *pim; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim = vrf->info; + if (pim && pim->autorp) + pim->autorp->mapping_agent_addrsel.cfg_enable = false; + else + return NB_ERR_INCONSISTENCY; + break; + } + + return NB_OK; } +#endif /* PIM_IPV == 4 (for AutoRP)*/ static int candidate_bsr_addrsel(struct bsm_scope *scope, const struct lyd_node *cand_bsr_node) diff --git a/pimd/pimd.c b/pimd/pimd.c index db619748000e..a390378a5abf 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -35,6 +35,7 @@ #include "pim_zlookup.h" #include "pim_zebra.h" #include "pim_mlag.h" +#include "pim_autorp.h" #if MAXVIFS > 256 CPP_NOTICE("Work needs to be done to make this work properly via the pim mroute socket\n"); @@ -70,6 +71,9 @@ void pim_prefix_list_update(struct prefix_list *plist) pim_rp_prefix_list_update(pim, plist); pim_ssm_prefix_list_update(pim, plist); pim_upstream_spt_prefix_list_update(pim, plist); +#if PIM_IPV == 4 + pim_autorp_prefix_list_update(pim, plist); +#endif } } diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index 369a794ebc7c..349b82aab436 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -1720,26 +1720,26 @@ def verify_pim_rp_info( ) return errormsg - if not iamRP: - if rp_json["iAmRP"] == False: - logger.info( - "[DUT %s]: Verifying group " - "and iAmNotRP [PASSED]!!" - " Found Expected: (%s, %s:%s)", - dut, - grp_addr, - "iAmRP", - rp_json["iAmRP"], - ) - else: - errormsg = ( - "[DUT %s]: Verifying group" - "%s and iAmRP [FAILED]!! " - "Expected: (iAmRP: %s)," - " Found: (iAmRP: %s)" - % (dut, grp_addr, "false", rp_json["iAmRP"]) - ) - return errormsg + if not iamRP: + if rp_json["iAmRP"] == False: + logger.info( + "[DUT %s]: Verifying group " + "and iAmNotRP [PASSED]!!" + " Found Expected: (%s, %s:%s)", + dut, + grp_addr, + "iAmRP", + rp_json["iAmRP"], + ) + else: + errormsg = ( + "[DUT %s]: Verifying group" + "%s and iAmRP [FAILED]!! " + "Expected: (iAmRP: %s)," + " Found: (iAmRP: %s)" + % (dut, grp_addr, "false", rp_json["iAmRP"]) + ) + return errormsg logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True diff --git a/tests/topotests/pim_autorp/r1/frr.conf b/tests/topotests/pim_autorp/r1/frr.conf index 2fddbc3ae29e..92b9b3b41b22 100644 --- a/tests/topotests/pim_autorp/r1/frr.conf +++ b/tests/topotests/pim_autorp/r1/frr.conf @@ -2,15 +2,25 @@ hostname r1 password zebra log file /tmp/r1-frr.log +! debug pim autorp ! interface r1-eth0 - ip address 10.10.76.1/24 + ip address 10.0.0.1/24 + ip igmp + ip pim +! +interface r1-eth1 + ip address 10.0.1.1/24 ip igmp ip pim ! ip forwarding ! +ip route 10.0.2.0/24 10.0.0.2 50 +ip route 10.0.3.0/24 10.0.0.2 50 +! router pim autorp discovery + rp 10.0.3.4 224.0.1.0/24 ! \ No newline at end of file diff --git a/tests/topotests/pim_autorp/r2/frr.conf b/tests/topotests/pim_autorp/r2/frr.conf index fd3c0cad3990..d67dade6f95f 100644 --- a/tests/topotests/pim_autorp/r2/frr.conf +++ b/tests/topotests/pim_autorp/r2/frr.conf @@ -2,15 +2,25 @@ hostname r2 password zebra log file /tmp/r2-frr.log +! debug pim autorp ! interface r2-eth0 - ip address 10.10.76.2/24 + ip address 10.0.0.2/24 + ip igmp + ip pim +! +interface r2-eth1 + ip address 10.0.2.2/24 ip igmp ip pim ! ip forwarding ! +ip route 10.0.1.0/24 10.0.0.1 50 +ip route 10.0.3.0/24 10.0.2.4 50 +! router pim autorp discovery + rp 10.0.3.4 224.0.1.0/24 ! \ No newline at end of file diff --git a/tests/topotests/pim_autorp/r3/frr.conf b/tests/topotests/pim_autorp/r3/frr.conf new file mode 100644 index 000000000000..4e93d4ba211d --- /dev/null +++ b/tests/topotests/pim_autorp/r3/frr.conf @@ -0,0 +1,26 @@ +! +hostname r3 +password zebra +log file /tmp/r3-frr.log +! +debug pim autorp +! +interface r3-eth0 + ip address 10.0.1.3/24 + ip igmp + ip pim +! +interface r3-eth1 + ip address 10.0.3.3/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.0.0/24 10.0.1.1 50 +ip route 10.0.2.0/24 10.0.3.4 50 +! +router pim + autorp discovery + rp 10.0.3.4 224.0.1.0/24 +! \ No newline at end of file diff --git a/tests/topotests/pim_autorp/r4/frr.conf b/tests/topotests/pim_autorp/r4/frr.conf new file mode 100644 index 000000000000..382999b11943 --- /dev/null +++ b/tests/topotests/pim_autorp/r4/frr.conf @@ -0,0 +1,26 @@ +! +hostname r4 +password zebra +log file /tmp/r4-frr.log +! +debug pim autorp +! +interface r4-eth0 + ip address 10.0.2.4/24 + ip igmp + ip pim +! +interface r4-eth1 + ip address 10.0.3.4/24 + ip igmp + ip pim +! +ip forwarding +! +ip route 10.0.0.0/24 10.0.2.2 50 +ip route 10.0.1.0/24 10.0.2.2 50 +! +router pim + autorp discovery + rp 10.0.3.4 224.0.1.0/24 +! \ No newline at end of file diff --git a/tests/topotests/pim_autorp/test_pim_autorp.py b/tests/topotests/pim_autorp/test_pim_autorp.py index ad618af29e3d..61cf8ebbc5ed 100644 --- a/tests/topotests/pim_autorp/test_pim_autorp.py +++ b/tests/topotests/pim_autorp/test_pim_autorp.py @@ -11,18 +11,14 @@ import os import sys import pytest +import json from functools import partial # pylint: disable=C0413 # Import topogen and topotest helpers -from lib import topotest -from lib.topogen import Topogen, get_topogen +from lib.topogen import Topogen, topotest, get_topogen from lib.topolog import logger -from lib.pim import ( - scapy_send_autorp_raw_packet, - verify_pim_rp_info, - verify_pim_rp_info_is_empty, -) +from lib.pim import verify_pim_rp_info from lib.common_config import step, write_test_header from time import sleep @@ -32,13 +28,26 @@ """ TOPOLOGY = """ - Basic AutoRP functionality - - +---+---+ +---+---+ - | | 10.10.76.0/24 | | - + R1 + <------------------> + R2 | - | | .1 .2 | | - +---+---+ +---+---+ + Test PIM AutoRP functionality: + AutoRP candidate RP announcements + Mapping agent announcement receive and send discovery + AutoRP discovery to active RP info + + +---+---+ +---+---+ + | | 10.0.0.0/24 | | + + R1 +----------------------+ R2 | + | | .1 .2 | | + +---+---+ r1-eth0 r2-eth0 +---+---+ + .1 | r1-eth1 r2-eth1 | .2 + | | + 10.0.1.0/24 | | 10.0.2.0/24 + | | + .3 | r3-eth0 r4-eth0 | .4 + +---+---+ r3-eth1 r4-eth1 +---+---+ + | | .3 .4 | | + + R3 +----------------------+ R4 | + | | 10.0.3.0/24 | | + +---+---+ +---+---+ """ # Save the Current Working Directory to find configuration files. @@ -55,11 +64,14 @@ def build_topo(tgen): # Create routers tgen.add_router("r1") tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") - # Create link between router 1 and 2 - switch = tgen.add_switch("s1-2") - switch.add_link(tgen.gears["r1"]) - switch.add_link(tgen.gears["r2"]) + # Create topology links + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth0", "r2-eth0") + tgen.add_link(tgen.gears["r1"], tgen.gears["r3"], "r1-eth1", "r3-eth0") + tgen.add_link(tgen.gears["r2"], tgen.gears["r4"], "r2-eth1", "r4-eth0") + tgen.add_link(tgen.gears["r3"], tgen.gears["r4"], "r3-eth1", "r4-eth1") def setup_module(mod): @@ -68,15 +80,6 @@ def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) tgen.start_topology() - # Router 1 will be the router configured with "fake" autorp configuration, so give it a default route - # to router 2 so that routing to the RP address is not an issue - # r1_defrt_setup_cmds = [ - # "ip route add default via 10.10.76.1 dev r1-eth0", - # ] - # for cmd in r1_defrt_setup_cmds: - # tgen.net["r1"].cmd(cmd) - - logger.info("Testing PIM AutoRP support") router_list = tgen.routers() for rname, router in router_list.items(): logger.info("Loading router %s" % rname) @@ -95,8 +98,8 @@ def teardown_module(mod): tgen.stop_topology() -def test_pim_autorp_discovery_single_rp(request): - "Test PIM AutoRP Discovery with single RP" +def test_pim_autorp_init(request): + "Test PIM AutoRP startup with only the static RP" tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) @@ -104,84 +107,253 @@ def test_pim_autorp_discovery_single_rp(request): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - step("Start with no RP configuration") - result = verify_pim_rp_info_is_empty(tgen, "r1") - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step("Send AutoRP packet from r1 to r2") - # 1 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/4 - data = "01005e00012800127f55cfb1080045c00030700c000008110abe0a0a4c01e000012801f001f0001c798b12010005000000000a0a4c0103010004e0000000" - scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data) - - step("Verify rp-info from AutoRP packet") - result = verify_pim_rp_info( - tgen, - None, - "r2", - "224.0.0.0/4", - "r2-eth0", - "10.10.76.1", - "AutoRP", - False, - "ipv4", - True, + step("Verify start-up with no extra RP configuration") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ] + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify start-up with AutoRP only discovery enabled") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true + }, + "announce": { + "enabled":false + }, + "mapping-agent": { + "enabled":false + } + }""" ) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim autorp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format( + rtr + ) + + +def test_pim_autorp_no_mapping_agent_rp(request): + "Test PIM AutoRP candidate with no mapping agent" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) - step("Verify AutoRP configuration times out") - result = verify_pim_rp_info_is_empty(tgen, "r2") - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + # Start a candidate RP on r2 + tgen.routers()["r2"].vtysh_cmd( + """ + conf + router pim + autorp announce 10.0.0.2 224.0.0.0/4 + autorp announce scope 31 interval 1 holdtime 5 + """ + ) -def test_pim_autorp_discovery_multiple_rp(request): - "Test PIM AutoRP Discovery with multiple RP's" + # Without a mapping agent, we should still have no RP + step("Verify no RP without mapping agent") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ] + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify candidate RP in AutoRP on R2") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true + }, + "announce": { + "enabled":true, + "scope":31, + "interval":1, + "holdtime":5, + "rpList":[ + { + "rpAddress":"10.0.0.2", + "groupRange":"224.0.0.0/4", + "prefixList":"-" + } + ] + }, + "mapping-agent": { + "enabled":false + } + }""" + ) + test_func = partial( + topotest.router_json_cmp, tgen.gears["r2"], "show ip pim autorp json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format("r2") + + +def test_pim_autorp_discovery_rp(request): + "Test PIM AutoRP candidate advertised by mapping agent" tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) if tgen.routers_have_failure(): - pytest.skip("skipped because of router(s) failure") + pytest.skip(tgen.errors) - step("Start with no RP configuration") - result = verify_pim_rp_info_is_empty(tgen, "r2") - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step("Send AutoRP packet from r1 to r2") - # 2 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/8, 10.10.76.3, group(s) 225.0.0.0/8 - data = "01005e00012800127f55cfb1080045c0003c700c000008110ab20a0a4c01e000012801f001f000283f5712020005000000000a0a4c0103010008e00000000a0a4c0303010008e1000000" - scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data) - - step("Verify rp-info from AutoRP packet") - result = verify_pim_rp_info( - tgen, - None, - "r2", - "224.0.0.0/8", - "r2-eth0", - "10.10.76.1", - "AutoRP", - False, - "ipv4", - True, - ) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info( - tgen, - None, - "r2", - "225.0.0.0/8", - "r2-eth0", - "10.10.76.3", - "AutoRP", - False, - "ipv4", - True, + # Start the mapping agent on R1 + tgen.routers()["r1"].vtysh_cmd( + """ + conf + router pim + autorp send-rp-discovery source interface r1-eth0 + autorp send-rp-discovery scope 31 interval 1 holdtime 5 + """ ) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + step("Verify rp-info of the only candidate RP") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ], + "10.0.0.2":[ + { + "rpAddress":"10.0.0.2", + "group":"224.0.0.0/4", + "source":"AutoRP" + } + ] + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify mapping-agent in AutoRP on R1") + expected = json.loads( + """ + { + "announce": { + "enabled":false + }, + "mapping-agent": { + "enabled":true, + "active":true, + "scope":31, + "interval":1, + "holdtime":5, + "source":"interface", + "interface":"r1-eth0", + "address":"10.0.0.1", + "rpList":{ + "10.0.0.2":{ + "rpAddress":"10.0.0.2", + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + } + } + } + }""" + ) + test_func = partial( + topotest.router_json_cmp, tgen.gears["r1"], "show ip pim autorp json", expected + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format("r1") -def test_pim_autorp_discovery_static(request): - "Test PIM AutoRP Discovery with Static RP" + step("Verify AutoRP discovery RP's") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true, + "rpList":{ + "10.0.0.2":{ + "rpAddress":"10.0.0.2", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + } + } + } + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim autorp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format( + rtr + ) + + +def test_pim_autorp_discovery_multiple_rp_same(request): + "Test PIM AutoRP Discovery with multiple RP's for same group prefix" tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) @@ -189,52 +361,94 @@ def test_pim_autorp_discovery_static(request): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - step("Start with no RP configuration") - result = verify_pim_rp_info_is_empty(tgen, "r2") - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step("Add static RP configuration to r2") - rnode = tgen.routers()["r2"] - rnode.cmd("vtysh -c 'conf t' -c 'router pim' -c 'rp 10.10.76.3 224.0.0.0/4'") - - step("Verify static rp-info from r2") - result = verify_pim_rp_info( - tgen, - None, - "r2", - "224.0.0.0/4", - "r2-eth0", - "10.10.76.3", - "Static", - False, - "ipv4", - True, - ) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step("Send AutoRP packet from r1 to r2") - # 1 RP(s), hold time 5 secs, 10.10.76.1, group(s) 224.0.0.0/4 - data = "01005e00012800127f55cfb1080045c00030700c000008110abe0a0a4c01e000012801f001f0001c798b12010005000000000a0a4c0103010004e0000000" - scapy_send_autorp_raw_packet(tgen, "r1", "r1-eth0", data) - - step("Verify rp-info from AutoRP packet") - result = verify_pim_rp_info( - tgen, - None, - "r2", - "224.0.0.0/4", - "r2-eth0", - "10.10.76.1", - "AutoRP", - False, - "ipv4", - True, + # Start a candidate RP on r3 + tgen.routers()["r3"].vtysh_cmd( + """ + conf + router pim + autorp announce 10.0.1.3 224.0.0.0/4 + autorp announce scope 31 interval 1 holdtime 5 + """ ) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - -def test_pim_autorp_announce_cli(request): - "Test PIM AutoRP Announcement CLI commands" + # The new candidate RP has the same group range but a higher IP, they should all + # switch to this RP + step("Verify rp-info of the candidate RP with the higher IP") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ], + "10.0.1.3":[ + { + "rpAddress":"10.0.1.3", + "group":"224.0.0.0/4", + "source":"AutoRP" + } + ] + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify AutoRP discovery RP's") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true, + "rpList":{ + "10.0.0.2":{ + "rpAddress":"10.0.0.2", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + }, + "10.0.1.3":{ + "rpAddress":"10.0.1.3", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + } + } + } + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim autorp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format( + rtr + ) + + +def test_pim_autorp_discovery_multiple_rp_different(request): + "Test PIM AutoRP Discovery with multiple RP's for different group prefixes" tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) @@ -242,65 +456,218 @@ def test_pim_autorp_announce_cli(request): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - step("Add AutoRP announcement configuration to r1") - r1 = tgen.routers()["r1"] - r1.vtysh_cmd( + # Switch R3 candidate to prefix list with different groups + step("Change R3 candidate to a prefix list") + tgen.routers()["r3"].vtysh_cmd( """ conf + ip prefix-list MYLIST permit 225.0.0.0/8 + ip prefix-list MYLIST permit 226.0.0.0/8 router pim - autorp announce holdtime 90 - autorp announce interval 120 - autorp announce scope 5 - autorp announce 10.2.3.4 225.0.0.0/24 -""" + autorp announce 10.0.1.3 group-list MYLIST + """ ) - expected = { - "discoveryEnabled": True, - "announce": { - "scope": 5, - "interval": 120, - "holdtime": 90, - "rpList": [ - {"rpAddress": "10.2.3.4", "group": "225.0.0.0/24", "prefixList": ""} - ], - }, - } - - test_func = partial( - topotest.router_json_cmp, r1, "show ip pim autorp json", expected + # Now that R3 doesn't conflict, we should see both RP's + step("Verify rp-info of both candidate RP's") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ], + "10.0.0.2":[ + { + "rpAddress":"10.0.0.2", + "group":"224.0.0.0/4", + "source":"AutoRP" + } + ], + "10.0.1.3":[ + { + "rpAddress":"10.0.1.3", + "prefixList":"__AUTORP_10.0.1.3__", + "source":"AutoRP" + } + ] + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify AutoRP discovery RP's") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true, + "rpList":{ + "10.0.0.2":{ + "rpAddress":"10.0.0.2", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + }, + "10.0.1.3":{ + "rpAddress":"10.0.1.3", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"225.0.0.0/8" + }, + { + "negative":false, + "prefix":"226.0.0.0/8" + } + ] + } + } + } + }""" ) - _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assertmsg = '"{}" JSON output mismatches'.format(r1.name) - assert result is None, assertmsg + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim autorp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format( + rtr + ) + + +def test_pim_autorp_discovery_neg_prefixes(request): + "Test PIM AutoRP Discovery with negative prefixes" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) - r1.vtysh_cmd( + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # Add negative prefixes to the R3 candidate prefix list + step("Change R3 candidate prefix list to include overlapping negative prefixes") + tgen.routers()["r3"].vtysh_cmd( """ conf - router pim - autorp announce 10.2.3.4 group-list ListA -""" + ip prefix-list MYLIST deny 225.1.0.0/16 + ip prefix-list MYLIST deny 226.1.0.0/16 + """ ) - expected = { - "discoveryEnabled": True, - "announce": { - "scope": 5, - "interval": 120, - "holdtime": 90, - "rpList": [{"rpAddress": "10.2.3.4", "group": "", "prefixList": "ListA"}], - }, - } - test_func = partial( - topotest.router_json_cmp, r1, "show ip pim autorp json", expected + step("Verify rp-info stays the same") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ], + "10.0.0.2":[ + { + "rpAddress":"10.0.0.2", + "group":"224.0.0.0/4", + "source":"AutoRP" + } + ], + "10.0.1.3":[ + { + "rpAddress":"10.0.1.3", + "prefixList":"__AUTORP_10.0.1.3__", + "source":"AutoRP" + } + ] + }""" ) - _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assertmsg = '"{}" JSON output mismatches'.format(r1.name) - assert result is None, assertmsg + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) + + step("Verify AutoRP discovery RP's") + expected = json.loads( + """ + { + "discovery":{ + "enabled": true, + "rpList":{ + "10.0.0.2":{ + "rpAddress":"10.0.0.2", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"224.0.0.0/4" + } + ] + }, + "10.0.1.3":{ + "rpAddress":"10.0.1.3", + "holdtime":5, + "groupRanges":[ + { + "negative":false, + "prefix":"225.0.0.0/8" + }, + { + "negative":false, + "prefix":"226.0.0.0/8" + }, + { + "negative":true, + "prefix":"225.1.0.0/16" + }, + { + "negative":true, + "prefix":"226.1.0.0/16" + } + ] + } + } + } + }""" + ) + for rtr in ["r1", "r2", "r3", "r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim autorp json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct autorp configuration".format( + rtr + ) -def test_pim_autorp_announce_group(request): - "Test PIM AutoRP Announcement with a single group" +def test_pim_autorp_discovery_static(request): + "Test PIM AutoRP Discovery with Static RP" tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) @@ -308,23 +675,60 @@ def test_pim_autorp_announce_group(request): if tgen.routers_have_failure(): pytest.skip("skipped because of router(s) failure") - step("Add candidate RP configuration to r1") - rnode = tgen.routers()["r1"] - rnode.cmd( - "vtysh -c 'conf t' -c 'router pim' -c 'send-rp-announce 10.10.76.1 224.0.0.0/4'" + # Add in a static RP with a specific range and make sure both are used + step("Add static RP configuration to r4") + tgen.routers()["r4"].vtysh_cmd( + """ + conf t + router pim + rp 10.0.2.2 239.0.0.0/24 + """ ) - step("Verify Announcement sent data") - # TODO: Verify AutoRP mapping agent receives candidate RP announcement - # Mapping agent is not yet implemented - # sleep(10) - step("Change AutoRP Announcement packet parameters") - rnode.cmd( - "vtysh -c 'conf t' -c 'router pim' -c 'send-rp-announce scope 8 interval 10 holdtime 60'" + + step("Verify static rp-info from r4") + expected = json.loads( + """ + { + "10.0.3.4":[ + { + "rpAddress":"10.0.3.4", + "group":"224.0.1.0/24", + "source":"Static" + } + ], + "10.0.0.2":[ + { + "rpAddress":"10.0.0.2", + "group":"224.0.0.0/4", + "source":"AutoRP" + } + ], + "10.0.1.3":[ + { + "rpAddress":"10.0.1.3", + "prefixList":"__AUTORP_10.0.1.3__", + "source":"AutoRP" + } + ], + "10.0.2.2":[ + { + "rpAddress":"10.0.2.2", + "group":"239.0.0.0/24", + "source":"Static" + } + ] + }""" ) - step("Verify Announcement sent data") - # TODO: Verify AutoRP mapping agent receives updated candidate RP announcement - # Mapping agent is not yet implemented - # sleep(10) + + for rtr in ["r4"]: + test_func = partial( + topotest.router_json_cmp, + tgen.gears[rtr], + "show ip pim rp-info json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None) + assert result is None, "{} does not have correct rp-info".format(rtr) def test_memory_leak(): diff --git a/yang/frr-pim-rp.yang b/yang/frr-pim-rp.yang index cbc6e87b805b..5558b0888d73 100644 --- a/yang/frr-pim-rp.yang +++ b/yang/frr-pim-rp.yang @@ -21,6 +21,10 @@ module frr-pim-rp { prefix frr-route-types; } + import frr-interface { + prefix "frr-interface"; + } + organization "FRRouting"; @@ -178,7 +182,7 @@ module frr-pim-rp { leaf announce-interval { type uint16; description - "The time between sending C-RP announcement packets."; + "The time between sending C-RP announcement packets (seconds)."; } leaf announce-holdtime { @@ -216,6 +220,57 @@ module frr-pim-rp { } } } // candidate-rp-list + + container mapping-agent { + leaf send-rp-discovery { + type boolean; + default false; + description + "Make this router an AutoRP mapping agent"; + } + + leaf discovery-scope { + type uint8; + default 31; + description + "The TTL of the discovery packet"; + } + + leaf discovery-interval { + type uint16 { + range "1 .. 65535"; + } + default 60; + description + "The time between sending discovery advertisements (seconds)"; + } + + leaf discovery-holdtime { + type uint16 { + range "0 .. 65535"; + } + default 180; + description + "The hold time in seconds advertised in the discovery packet."; + } + + choice source-address-or-interface { + description "Source address to use for mapping agent operation"; + default if-loopback; + leaf address { + type inet:ip-address; + } + leaf interface { + type frr-interface:interface-ref; + } + leaf if-loopback { + type empty; + } + leaf if-any { + type empty; + } + } + } // mapping-agent } // auto-rp } // auto-rp-container