summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/pim.rst34
-rw-r--r--pimd/pim_autorp.c1147
-rw-r--r--pimd/pim_autorp.h158
-rw-r--r--pimd/pim_cmd.c147
-rw-r--r--pimd/pim_cmd.h1
-rw-r--r--pimd/pim_cmd_common.c159
-rw-r--r--pimd/pim_cmd_common.h11
-rw-r--r--pimd/pim_iface.c15
-rw-r--r--pimd/pim_instance.c8
-rw-r--r--pimd/pim_instance.h3
-rw-r--r--pimd/pim_nb.c49
-rw-r--r--pimd/pim_nb.h29
-rw-r--r--pimd/pim_nb_config.c365
-rw-r--r--pimd/pim_rp.c5
-rw-r--r--pimd/pim_rp.h6
-rw-r--r--pimd/pim_sock.c30
-rw-r--r--pimd/pim_sock.h2
-rw-r--r--pimd/pim_vty.c8
-rw-r--r--pimd/pimd.h4
-rw-r--r--pimd/subdir.am2
-rw-r--r--tests/topotests/lib/pim.py94
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json2
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json2
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json2
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json1
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json2
-rwxr-xr-xtests/topotests/pim_autorp/__init__.py0
-rw-r--r--tests/topotests/pim_autorp/r1/frr.conf16
-rw-r--r--tests/topotests/pim_autorp/r2/frr.conf16
-rw-r--r--tests/topotests/pim_autorp/test_pim_autorp.py207
-rw-r--r--yang/frr-pim-rp.yang71
31 files changed, 2579 insertions, 17 deletions
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index d6d9ae04d2..0fe53247b0 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -71,6 +71,31 @@ PIM Routers
prefix of group ranges covered. This command is vrf aware, to configure for
a vrf, specify the vrf in the router pim block.
+.. clicmd:: no autorp discovery
+
+ In order to use pim, it is necessary to configure a RP for join messages to
+ be sent to. FRR supports learning RP information dynamically via the AutoRP
+ protocol and performs discovery by default. This command will disable the
+ AutoRP discovery protocol.
+ All routers in the pim network must agree on the network RP information, so
+ all routers in the network should have AutoRP either enabled or disabled.
+ This command is vrf aware, to configure for a vrf, specify the vrf in the
+ router pim block.
+
+.. clicmd:: autorp announce A.B.C.D [A.B.C.D/M | group-list PREFIX_LIST]
+
+ Configure the router to advertise itself as a candidate PIM-SM RP via AutoRP.
+ The supported groups can be defined as a single group range, or multiple
+ group ranges can be defined via a prefix list.
+
+.. clicmd:: autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)}
+
+ Configure the AutoRP advertise messages. The scope defines the TTL value in the
+ messages to limit the scope, defaults to 31. Interval defines the number of
+ seconds elapsed between advertise messages sent, defaults to 60. Hold time defines
+ how long the AutoRP mapping agent will consider the information valid, setting to
+ 0 will disable expiration of the candidate RP information, defaults to 3 * interval.
+
.. clicmd:: rp keep-alive-timer (1-65535)
Modify the time out value for a S,G flow from 1-65535 seconds at RP.
@@ -616,6 +641,11 @@ cause great confusion.
192.168.10.123 239.0.0.0/8 eth2 yes Static ASM
192.168.10.123 239.4.0.0/24 eth2 yes Static SSM
+.. clicmd:: show ip pim [vrf NAME] autorp [json]
+
+ Display information about AutoRP. Including state of AutoRP Discovery parsing
+ and configured AutoRP candidate RP information.
+
.. clicmd:: show ip pim rpf
Display information about currently being used S,G's and their RPF lookup
@@ -761,6 +791,10 @@ the config was written out.
This gathers data about events from zebra that come up through the ZAPI.
+.. clicmd:: debug pim autorp
+
+ This turns on debugging for PIM AutoRP protocol events.
+
PIM Clear Commands
==================
Clear commands reset various variables.
diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c
new file mode 100644
index 0000000000..cf0b9d5550
--- /dev/null
+++ b/pimd/pim_autorp.c
@@ -0,0 +1,1147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pim_autorp.c: PIM AutoRP handling routines
+ *
+ * Copyright (C) 2024 ATCorp
+ * Nathan Bahr
+ */
+
+#include <zebra.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "lib/plist.h"
+#include "lib/plist_int.h"
+#include "lib/sockopt.h"
+#include "lib/network.h"
+#include "lib/termtable.h"
+#include "lib/json.h"
+
+#include "pimd.h"
+#include "pim_iface.h"
+#include "pim_rp.h"
+#include "pim_sock.h"
+#include "pim_instance.h"
+#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_ANNOUNCE, "PIM AutoRP announcement packet");
+
+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)
+{
+ return pim_addr_cmp(l->addr, r->addr);
+}
+
+DECLARE_SORTLIST_UNIQ(pim_autorp_rp, struct pim_autorp_rp, list,
+ pim_autorp_rp_cmp);
+
+static void pim_autorp_rp_free(struct pim_autorp_rp *rp)
+{
+ event_cancel(&rp->hold_timer);
+
+ /* 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);
+
+ XFREE(MTYPE_PIM_AUTORP_RP, rp);
+}
+
+static void pim_autorp_rplist_free(struct pim_autorp_rp_head *head)
+{
+ struct pim_autorp_rp *rp;
+
+ while ((rp = pim_autorp_rp_pop(head)))
+ pim_autorp_rp_free(rp);
+}
+
+static void pim_autorp_rplist_cfree(struct pim_autorp_rp_head *head)
+{
+ struct pim_autorp_rp *rp;
+
+ while ((rp = pim_autorp_rp_pop(head)))
+ XFREE(MTYPE_PIM_AUTORP_CRP, rp);
+}
+
+static void pim_autorp_free(struct pim_autorp *autorp)
+{
+ pim_autorp_rplist_free(&(autorp->discovery_rp_list));
+ pim_autorp_rp_fini(&(autorp->discovery_rp_list));
+
+ pim_autorp_rplist_cfree(&(autorp->candidate_rp_list));
+ pim_autorp_rp_fini(&(autorp->candidate_rp_list));
+}
+
+static bool pim_autorp_join_groups(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
+ struct pim_autorp *autorp;
+ pim_addr grp;
+
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+ autorp = pim->autorp;
+
+ 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);
+ 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;
+ * }
+ */
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: Joined AutoRP groups on interface %s", __func__,
+ ifp->name);
+
+ return true;
+}
+
+static bool pim_autorp_leave_groups(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
+ struct pim_autorp *autorp;
+ pim_addr grp;
+
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+ autorp = pim->autorp;
+
+ 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);
+ 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;
+ * }
+ */
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: Left AutoRP groups on interface %s", __func__,
+ ifp->name);
+
+ return true;
+}
+
+static bool pim_autorp_setup(struct pim_autorp *autorp)
+{
+#if defined(HAVE_IP_PKTINFO)
+ int data;
+ socklen_t data_len = sizeof(data);
+#endif
+
+ struct sockaddr_in autorp_addr = { .sin_family = AF_INET,
+ .sin_addr = { .s_addr = INADDR_ANY },
+ .sin_port = htons(PIM_AUTORP_PORT) };
+
+ setsockopt_so_recvbuf(autorp->sock, 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));
+ 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));
+ 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));
+ 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));
+ return false;
+ }
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP finished setup", __func__);
+
+ return true;
+}
+
+static bool pim_autorp_announcement(struct pim_autorp *autorp, uint8_t rpcnt,
+ uint16_t holdtime, char *buf,
+ size_t buf_size)
+{
+ /* TODO: Future Mapping agent implementation
+ * Implement AutoRP mapping agent logic using received announcement messages
+ */
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP processed announcement message",
+ __func__);
+ return true;
+}
+
+static void autorp_rp_holdtime(struct event *evt)
+{
+ /* 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: addr=%pI4, grp=%pFX, grplist=%s",
+ __func__, &rp->addr, &rp->grp,
+ (strlen(rp->grplist) ? rp->grplist : "NONE"));
+
+ pim_autorp_rp_del(&(rp->autorp->discovery_rp_list), rp);
+ pim_autorp_rp_free(rp);
+}
+
+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;
+
+ if (pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP)) {
+ zlog_err("%s: Failed to add new 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);
+ } else {
+ /* RP already existed */
+ 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';
+ }
+ }
+
+ if (holdtime > 0) {
+ event_add_timer(router->master, autorp_rp_holdtime, trp,
+ holdtime, &(trp->hold_timer));
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: Started %u second hold timer for RP %pI4",
+ __func__, holdtime, &rp->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)
+{
+ int i, j;
+ struct autorp_pkt_rp *rp;
+ struct autorp_pkt_grp *grp;
+ size_t offset = 0;
+ pim_addr rp_addr;
+ struct prefix grppfix;
+ char plname[32];
+ struct prefix_list *pl;
+ struct prefix_list_entry *ple;
+ int64_t seq = 1;
+ bool success = true;
+
+ for (i = 0; i < rpcnt; ++i) {
+ if ((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 == 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));
+ /* 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));
+ 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;
+ }
+
+ 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 */
+ 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))
+ 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);
+ 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 */
+ 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->prefix.family = AF_INET;
+ ple->prefix.prefixlen = grp->masklen;
+ ple->prefix.u.prefix4.s_addr = grp->addr;
+ ple->any = false;
+ ple->ge = 0;
+ 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_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)
+{
+ struct autorp_pkt_hdr *h;
+
+ if (buf_size < AUTORP_HDRLEN)
+ return false;
+
+ h = (struct autorp_pkt_hdr *)buf;
+
+ if (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_DISCOVERY_TYPE &&
+ !pim_autorp_discovery(autorp, h->rpcnt, htons(h->holdtime),
+ buf + AUTORP_HDRLEN, buf_size - AUTORP_HDRLEN))
+ return false;
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: Processed AutoRP packet", __func__);
+
+ return true;
+}
+
+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));
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP socket read enabled", __func__);
+}
+
+static void autorp_read_off(struct pim_autorp *autorp)
+{
+ event_cancel(&(autorp->read_event));
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP socket read disabled", __func__);
+}
+
+static void autorp_read(struct event *evt)
+{
+ struct pim_autorp *autorp = evt->arg;
+ int fd = evt->u.fd;
+ char buf[10000];
+ int rd;
+
+ 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);
+ 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;
+ }
+
+ if (!pim_autorp_msg(autorp, buf, rd))
+ zlog_err("%s: Failure parsing AutoRP message", __func__);
+ /* Keep reading until would block */
+ }
+
+ /* No error, enable read again */
+ autorp_read_on(autorp);
+
+err:
+ return;
+}
+
+static bool pim_autorp_socket_enable(struct pim_autorp *autorp)
+{
+ int fd;
+
+ 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));
+ 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));
+ close(fd);
+ autorp->sock = -1;
+ return false;
+ }
+ }
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP socket enabled", __func__);
+
+ return true;
+}
+
+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));
+ return false;
+ }
+
+ autorp_read_off(autorp);
+ autorp->sock = -1;
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP socket disabled", __func__);
+
+ return true;
+}
+
+static void autorp_send_announcement(struct event *evt)
+{
+ struct pim_autorp *autorp = EVENT_ARG(evt);
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct sockaddr_in announceGrp;
+
+ announceGrp.sin_family = AF_INET;
+ 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) {
+ setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_TTL,
+ &(autorp->announce_scope),
+ sizeof(autorp->announce_scope));
+
+ FOR_ALL_INTERFACES (autorp->pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ /* 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 &&
+ !pim_addr_is_any(pim_ifp->primary_address)) {
+ setsockopt(autorp->sock, IPPROTO_IP,
+ IP_MULTICAST_IF,
+ &(pim_ifp->primary_address),
+ sizeof(pim_ifp->primary_address));
+ sendto(autorp->sock, autorp->annouce_pkt,
+ autorp->annouce_pkt_sz, 0,
+ (struct sockaddr *)&announceGrp,
+ sizeof(announceGrp));
+ }
+ }
+ }
+
+ /* Start the new timer for the entire announce interval */
+ 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 */
+ 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__);
+}
+
+static void autorp_announcement_off(struct pim_autorp *autorp)
+{
+ event_cancel(&(autorp->announce_timer));
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP announcement sending disabled", __func__);
+}
+
+/* Pack the groups of the RP
+ * rp - Pointer to the RP
+ * 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)
+{
+ 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
+ */
+ 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) {
+ grpp->addr = ple->prefix.u.prefix4.s_addr;
+ grpp->masklen = ple->prefix.prefixlen;
+ grpp->negprefix =
+ (ple->type == PREFIX_PERMIT ? 0 : 1);
+ grpp->reserved = 0;
+
+ ++cnt;
+ grpp = (struct autorp_pkt_grp
+ *)(buf +
+ (sizeof(struct autorp_pkt_grp) *
+ cnt));
+ }
+ }
+
+ return cnt;
+ }
+
+ /* Only one of group or prefix list should be defined */
+ grpp->addr = rp->grp.u.prefix4.s_addr;
+ grpp->masklen = rp->grp.prefixlen;
+ grpp->negprefix = 0;
+ grpp->reserved = 0;
+ return 1;
+}
+
+/* Pack a single candidate RP
+ * rp - Pointer to the RP to pack
+ * 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)
+{
+ 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->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));
+}
+
+/* Pack the candidate RP's on the announcement packet
+ * autorp - Pointer to the AutoRP instance
+ * buf - Pointer to the buffer where to start packing the first 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)
+{
+ 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 */
+ if (pim_addr_is_any(rp->addr) ||
+ (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))) {
+ 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)
+ break;
+ }
+
+ /* If we went through the entire list without finding a multicast prefix, then skip this RP */
+ if (ple == NULL)
+ continue;
+ }
+
+ /* Now we know for sure we will pack this RP, so count it */
+ ++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 (cnt > 0)
+ *bufsz = buf - obuf;
+
+ return cnt;
+}
+
+/* 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;
+ struct autorp_pkt_hdr *hdr;
+ int32_t holdtime;
+
+ /* 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);
+ }
+
+ autorp->annouce_pkt_sz = 0;
+
+ holdtime = autorp->announce_holdtime;
+ if (holdtime == DEFAULT_ANNOUNCE_HOLDTIME)
+ holdtime = autorp->announce_interval * 3;
+ if (holdtime > UINT16_MAX)
+ holdtime = UINT16_MAX;
+
+ hdr = (struct autorp_pkt_hdr *)autorp->annouce_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));
+
+ /* Still need to add on the size of the header */
+ autorp->annouce_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)
+ autorp_announcement_on(autorp);
+}
+
+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);
+ if (!rp)
+ return false;
+
+ pim_autorp_rp_del(&(autorp->candidate_rp_list), rp);
+ pim_autorp_rp_free(rp);
+ pim_autorp_new_announcement(pim);
+ return true;
+}
+
+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);
+ if (!rp) {
+ rp = XCALLOC(MTYPE_PIM_AUTORP_CRP, sizeof(*rp));
+ memset(rp, 0, sizeof(struct pim_autorp_rp));
+ rp->autorp = autorp;
+ memcpy(&(rp->addr), &rpaddr, sizeof(pim_addr));
+ pim_autorp_rp_add(&(autorp->candidate_rp_list), rp);
+ }
+
+ apply_mask(&group);
+ prefix_copy(&(rp->grp), &group);
+ /* A new group prefix implies that any previous prefix list is now invalid */
+ rp->grplist[0] = '\0';
+
+ pim_autorp_new_announcement(pim);
+}
+
+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);
+ if (!rp)
+ return false;
+
+ memset(&(rp->grp), 0, sizeof(rp->grp));
+ pim_autorp_new_announcement(pim);
+ return true;
+}
+
+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);
+ if (!rp) {
+ rp = XCALLOC(MTYPE_PIM_AUTORP_CRP, sizeof(*rp));
+ memset(rp, 0, sizeof(struct pim_autorp_rp));
+ rp->autorp = autorp;
+ memcpy(&(rp->addr), &rpaddr, sizeof(pim_addr));
+ pim_autorp_rp_add(&(autorp->candidate_rp_list), rp);
+ }
+
+ snprintf(rp->grplist, sizeof(rp->grplist), "%s", plist);
+ /* A new group prefix list implies that any previous group prefix is now invalid */
+ memset(&(rp->grp), 0, sizeof(rp->grp));
+
+ pim_autorp_new_announcement(pim);
+}
+
+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);
+ if (!rp)
+ return false;
+
+ rp->grplist[0] = '\0';
+ pim_autorp_new_announcement(pim);
+ return true;
+}
+
+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);
+ if (autorp->announce_scope != scope) {
+ autorp->announce_scope = scope;
+ pim_autorp_new_announcement(pim);
+ }
+}
+
+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);
+ if (autorp->announce_interval != interval) {
+ autorp->announce_interval = interval;
+ pim_autorp_new_announcement(pim);
+ }
+}
+
+void pim_autorp_announce_holdtime(struct pim_instance *pim, int32_t holdtime)
+{
+ struct pim_autorp *autorp = pim->autorp;
+
+ if (autorp->announce_holdtime != holdtime) {
+ autorp->announce_holdtime = holdtime;
+ pim_autorp_new_announcement(pim);
+ }
+}
+
+void pim_autorp_add_ifp(struct interface *ifp)
+{
+ /* Add a new interface for autorp
+ * When autorp is enabled, we must join the autorp groups on all
+ * pim/multicast interfaces. When autorp first starts, if finds all
+ * current multicast interfaces and joins on them. If a new interface
+ * comes up or is configured for multicast after autorp is running, then
+ * this method will add it for autorp->
+ * This is called even when adding a new pim interface that is not yet
+ * active, so make sure the check, it'll call in again once the interface is up.
+ */
+ struct pim_instance *pim;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ 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));
+ }
+ }
+ }
+}
+
+void pim_autorp_rm_ifp(struct interface *ifp)
+{
+ /* Remove interface for autorp
+ * When an interface is no longer enabled for multicast, or at all, then
+ * we should leave the AutoRP groups on this interface.
+ */
+ struct pim_instance *pim;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE) && pim_ifp) {
+ pim = pim_ifp->pim;
+ if (pim && pim->autorp) {
+ 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));
+ }
+ }
+ }
+}
+
+void pim_autorp_start_discovery(struct pim_instance *pim)
+{
+ struct interface *ifp;
+ struct pim_autorp *autorp = pim->autorp;
+
+ if (!autorp->do_discovery) {
+ autorp->do_discovery = true;
+ autorp_read_on(autorp);
+
+ FOR_ALL_INTERFACES (autorp->pim->vrf, ifp) {
+ pim_autorp_add_ifp(ifp);
+ }
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP Discovery started", __func__);
+ }
+}
+
+void pim_autorp_stop_discovery(struct pim_instance *pim)
+{
+ struct interface *ifp;
+ struct pim_autorp *autorp = pim->autorp;
+
+ if (autorp->do_discovery) {
+ FOR_ALL_INTERFACES (autorp->pim->vrf, ifp) {
+ pim_autorp_rm_ifp(ifp);
+ }
+
+ autorp->do_discovery = false;
+ autorp_read_off(autorp);
+
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP Discovery stopped", __func__);
+ }
+}
+
+void pim_autorp_init(struct pim_instance *pim)
+{
+ struct pim_autorp *autorp;
+
+ autorp = XCALLOC(MTYPE_PIM_AUTORP, sizeof(*autorp));
+ autorp->pim = pim;
+ autorp->sock = -1;
+ autorp->read_event = NULL;
+ autorp->announce_timer = NULL;
+ autorp->do_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;
+
+ if (!pim_autorp_socket_enable(autorp)) {
+ zlog_err("%s: AutoRP failed to initialize", __func__);
+ return;
+ }
+
+ pim->autorp = autorp;
+ if (PIM_DEBUG_AUTORP)
+ zlog_debug("%s: AutoRP Initialized", __func__);
+
+ /* Start AutoRP discovery by default on startup */
+ pim_autorp_start_discovery(pim);
+}
+
+void pim_autorp_finish(struct pim_instance *pim)
+{
+ struct pim_autorp *autorp = pim->autorp;
+
+ autorp_read_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__);
+
+ XFREE(MTYPE_PIM_AUTORP, pim->autorp);
+}
+
+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) {
+ vty_out(vty, " no autorp discovery\n");
+ ++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);
+ ++writes;
+ }
+
+ frr_each_safe (pim_autorp_rp, &(autorp->candidate_rp_list), rp) {
+ /* Only print candidate RP's that have all the information needed to be announced */
+ if (pim_addr_is_any(rp->addr) ||
+ (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 */
+
+ if (!is_default_prefix(&(rp->grp)))
+ snprintfrr(grp_str, sizeof(grp_str), "%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);
+ ++writes;
+ }
+
+ return writes;
+}
+
+void pim_autorp_show_autorp(struct vty *vty, struct pim_instance *pim,
+ json_object *json)
+{
+ struct pim_autorp_rp *rp;
+ 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);
+
+ 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);
+
+ ttable_add_row(tt, "%pI4|%s|%s", &(rp->addr), grp_str,
+ plist_str);
+ }
+
+ 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));
+
+ vty_out(vty, "\n");
+
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+ }
+
+ ttable_del(tt);
+}
diff --git a/pimd/pim_autorp.h b/pimd/pim_autorp.h
new file mode 100644
index 0000000000..a0b029d00a
--- /dev/null
+++ b/pimd/pim_autorp.h
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pim_autorp.h: PIM Auto RP handling related
+ *
+ * Copyright (C) 20224 ATCorp.
+ * Nathan Bahr
+ */
+
+#ifndef __PIM_AUTORP_H__
+#define __PIM_AUTORP_H__
+
+#include <typesafe.h>
+
+#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 DEFAULT_ANNOUNCE_INTERVAL 60
+#define DEFAULT_ANNOUNCE_SCOPE 31
+#define DEFAULT_ANNOUNCE_HOLDTIME -1
+
+PREDECL_SORTLIST_UNIQ(pim_autorp_rp);
+
+struct autorp_pkt_grp {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t negprefix : 1;
+ uint8_t reserved : 7;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t reserved : 7;
+ uint8_t negprefix : 1;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+ uint8_t masklen;
+ uint32_t addr;
+} __attribute__((__packed__));
+
+struct autorp_pkt_rp {
+ uint32_t addr;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t pimver : 2;
+ uint8_t reserved : 6;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t reserved : 6;
+ uint8_t pimver : 2;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+ uint8_t grpcnt;
+} __attribute__((__packed__));
+
+struct autorp_pkt_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t type : 4;
+ uint8_t version : 4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t version : 4;
+ uint8_t type : 4;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+ uint8_t rpcnt;
+ uint16_t holdtime;
+ uint32_t reserved;
+} __attribute__((__packed__));
+
+#define MIN_AUTORP_PKT_SZ \
+ (sizeof(struct autorp_pkt_hdr) + sizeof(struct autorp_pkt_rp) + \
+ sizeof(struct autorp_pkt_grp))
+
+struct pim_autorp_rp {
+ struct pim_autorp *autorp;
+ struct in_addr addr;
+ uint16_t holdtime;
+ struct event *hold_timer;
+ struct prefix grp;
+ char grplist[32];
+ struct pim_autorp_rp_item list;
+};
+
+struct pim_autorp {
+ /* backpointer to pim instance */
+ struct pim_instance *pim;
+
+ /* UDP socket bound to AutoRP port, used for sending and receiving all AutoRP packets */
+ int sock;
+
+ /* Event for reading AutoRP packets */
+ struct event *read_event;
+
+ /* Event for sending announcement packets */
+ struct event *announce_timer;
+
+ /* Event for sending discovery packets*/
+ /* struct event *discovery_timer; */
+
+ /* Flag enabling reading discovery packets */
+ bool do_discovery;
+
+ /* Flag enabling mapping agent (reading announcements and sending discovery)*/
+ /* bool do_mapping; */
+
+ /* List of RP's in received discovery packets */
+ struct pim_autorp_rp_head discovery_rp_list;
+
+ /* List of configured candidate RP's to send in announcement packets */
+ 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; */
+
+ /* Packet parameters for sending announcement packets */
+ uint8_t announce_scope;
+ uint16_t announce_interval;
+ 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;
+ */
+};
+
+#define AUTORP_GRPLEN 6
+#define AUTORP_RPLEN 6
+#define AUTORP_HDRLEN 8
+
+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);
+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_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_add_ifp(struct interface *ifp);
+void pim_autorp_rm_ifp(struct interface *ifp);
+void pim_autorp_start_discovery(struct pim_instance *pim);
+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,
+ json_object *json);
+
+#endif
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 0b503b8293..aa7fc0d81f 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -2820,6 +2820,75 @@ DEFPY (show_ip_pim_rp_vrf_all,
(struct prefix *)group, !!json);
}
+DEFPY (show_ip_pim_autorp,
+ show_ip_pim_autorp_cmd,
+ "show ip pim [vrf NAME] autorp [json$json]",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ VRF_CMD_HELP_STR
+ "PIM AutoRP information\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;
+ }
+
+ 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 (json)
+ json_parent = json_object_new_object();
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ if (vrf->info) {
+ if (!json)
+ vty_out(vty, "VRF: %s\n", vrf->name);
+ else
+ json_vrf = json_object_new_object();
+
+ pim_autorp_show_autorp(vty, vrf->info, json_vrf);
+
+ if (json)
+ json_object_object_add(json_parent, vrf->name,
+ json_vrf);
+ }
+ }
+
+ if (json)
+ vty_json(vty, json_parent);
+
+ return CMD_SUCCESS;
+}
+
DEFPY (show_ip_pim_rpf,
show_ip_pim_rpf_cmd,
"show ip pim [vrf NAME] rpf [json$json]",
@@ -4516,6 +4585,52 @@ DEFPY_ATTR(no_ip_pim_rp_prefix_list,
return ret;
}
+DEFPY (pim_autorp_discovery,
+ pim_autorp_discovery_cmd,
+ "[no] autorp discovery",
+ NO_STR
+ "AutoRP\n"
+ "Enable AutoRP discovery\n")
+{
+ if (no)
+ return pim_process_no_autorp_cmd(vty);
+ else
+ return pim_process_autorp_cmd(vty);
+}
+
+DEFPY (pim_autorp_announce_rp,
+ pim_autorp_announce_rp_cmd,
+ "[no] autorp announce A.B.C.D$rpaddr ![A.B.C.D/M$grp|group-list PREFIX_LIST$plist]",
+ NO_STR
+ "AutoRP\n"
+ "AutoRP Candidate RP announcement\n"
+ "AutoRP Candidate RP address\n"
+ "Group prefix\n"
+ "Prefix list\n"
+ "List name\n")
+{
+ return pim_process_autorp_candidate_rp_cmd(vty, no, rpaddr_str, grp,
+ 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_STR
+ "AutoRP\n"
+ "AutoRP Candidate RP announcement\n"
+ "Packet scope (TTL)\n"
+ "TTL value\n"
+ "Announcement interval\n"
+ "Time in seconds\n"
+ "Announcement holdtime\n"
+ "Time in seconds\n")
+{
+ return pim_process_autorp_announce_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 <address A.B.C.D|interface IFNAME|loopback$loopback|any$any>}]",
@@ -6377,6 +6492,29 @@ DEFUN (no_debug_bsm,
return CMD_SUCCESS;
}
+DEFUN (debug_autorp,
+ debug_autorp_cmd,
+ "debug pim autorp",
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_AUTORP_STR)
+{
+ PIM_DO_DEBUG_AUTORP;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_autorp,
+ no_debug_autorp_cmd,
+ "no debug pim autorp",
+ NO_STR
+ DEBUG_STR
+ DEBUG_PIM_STR
+ DEBUG_PIM_AUTORP_STR)
+{
+ PIM_DONT_DEBUG_AUTORP;
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
@@ -8714,6 +8852,9 @@ void pim_cmd_init(void)
install_element(PIM_NODE, &no_pim_rp_cmd);
install_element(PIM_NODE, &pim_rp_prefix_list_cmd);
install_element(PIM_NODE, &no_pim_rp_prefix_list_cmd);
+ 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, &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);
@@ -8868,6 +9009,8 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
install_element(VIEW_NODE, &show_ip_pim_rp_cmd);
install_element(VIEW_NODE, &show_ip_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);
@@ -8975,6 +9118,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_pim_trace_detail_cmd);
install_element(ENABLE_NODE, &debug_ssmpingd_cmd);
install_element(CONFIG_NODE, &debug_ssmpingd_cmd);
+ install_element(ENABLE_NODE, &debug_autorp_cmd);
+ install_element(ENABLE_NODE, &no_debug_autorp_cmd);
install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd);
install_element(CONFIG_NODE, &no_debug_ssmpingd_cmd);
install_element(ENABLE_NODE, &debug_pim_zebra_cmd);
@@ -9007,6 +9152,8 @@ void pim_cmd_init(void)
install_element(CONFIG_NODE, &debug_bsm_cmd);
install_element(ENABLE_NODE, &no_debug_bsm_cmd);
install_element(CONFIG_NODE, &no_debug_bsm_cmd);
+ install_element(CONFIG_NODE, &debug_autorp_cmd);
+ install_element(CONFIG_NODE, &no_debug_autorp_cmd);
install_element(CONFIG_NODE, &ip_igmp_group_watermark_cmd);
install_element(VRF_NODE, &ip_igmp_group_watermark_cmd);
diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h
index d39d77cd2f..17cf4bb362 100644
--- a/pimd/pim_cmd.h
+++ b/pimd/pim_cmd.h
@@ -56,6 +56,7 @@
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
#define DEBUG_PIM_BSM_STR "BSR message processing activity\n"
+#define DEBUG_PIM_AUTORP_STR "AutoRP message processing activity\n"
void pim_cmd_init(void);
diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c
index 7337bacc84..be7460d0fb 100644
--- a/pimd/pim_cmd_common.c
+++ b/pimd/pim_cmd_common.c
@@ -606,6 +606,165 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
return nb_cli_apply_changes(vty, NULL);
}
+int pim_process_autorp_cmd(struct vty *vty)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
+ "discovery-enabled");
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+int pim_process_no_autorp_cmd(struct vty *vty)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
+ "discovery-enabled");
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ 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)
+{
+ char xpath[XPATH_MAXLEN];
+ char grpstr[64];
+
+ if (no) {
+ if (!is_default_prefix((const struct prefix *)grp) || plist) {
+ /* If any single values are set, only destroy those */
+ if (!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 {
+ /* 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);
+ }
+ } else {
+ if (!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 (!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;
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+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];
+
+ 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);
+ }
+ } 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;
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
bool pim_sgaddr_match(pim_sgaddr item, pim_sgaddr match)
{
return (pim_addr_is_any(match.grp) ||
diff --git a/pimd/pim_cmd_common.h b/pimd/pim_cmd_common.h
index 7ded9b246a..d7c97e31d4 100644
--- a/pimd/pim_cmd_common.h
+++ b/pimd/pim_cmd_common.h
@@ -35,7 +35,16 @@ int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list);
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_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_iface.c b/pimd/pim_iface.c
index 7f873f45dc..1dc9307e4f 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -1914,6 +1914,12 @@ static int pim_ifp_up(struct interface *ifp)
}
}
+#if PIM_IPV == 4
+ if (pim->autorp && pim->autorp->do_discovery && pim_ifp &&
+ pim_ifp->pim_enable)
+ pim_autorp_add_ifp(ifp);
+#endif
+
pim_cand_addrs_changed();
return 0;
}
@@ -1951,6 +1957,10 @@ static int pim_ifp_down(struct interface *ifp)
pim_ifstat_reset(ifp);
}
+#if PIM_IPV == 4
+ pim_autorp_rm_ifp(ifp);
+#endif
+
pim_cand_addrs_changed();
return 0;
}
@@ -2023,6 +2033,11 @@ void pim_pim_interface_delete(struct interface *ifp)
if (!pim_ifp)
return;
+#if PIM_IPV == 4
+ if (pim_ifp->pim_enable)
+ pim_autorp_rm_ifp(ifp);
+#endif
+
pim_ifp->pim_enable = false;
pim_if_membership_clear(ifp);
diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c
index 9a697c9209..f7c5ea3bcf 100644
--- a/pimd/pim_instance.c
+++ b/pimd/pim_instance.c
@@ -57,6 +57,10 @@ static void pim_instance_terminate(struct pim_instance *pim)
pim_mroute_socket_disable(pim);
+#if PIM_IPV == 4
+ pim_autorp_finish(pim);
+#endif
+
XFREE(MTYPE_PIM_PLIST_NAME, pim->spt.plist);
XFREE(MTYPE_PIM_PLIST_NAME, pim->register_plist);
@@ -125,6 +129,10 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf)
pim->msdp.keep_alive = PIM_MSDP_PEER_KA_TIME;
pim->msdp.connection_retry = PIM_MSDP_PEER_CONNECT_RETRY_TIME;
+#if PIM_IPV == 4
+ pim_autorp_init(pim);
+#endif
+
return pim;
}
diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h
index ec331332cf..f484d847b2 100644
--- a/pimd/pim_instance.h
+++ b/pimd/pim_instance.h
@@ -17,6 +17,7 @@
#include "pim_oil.h"
#include "pim_upstream.h"
#include "pim_mroute.h"
+#include "pim_autorp.h"
enum pim_spt_switchover {
PIM_SPT_IMMEDIATE,
@@ -152,6 +153,8 @@ struct pim_instance {
struct pim_msdp msdp;
struct pim_vxlan_instance vxlan;
+ struct pim_autorp *autorp;
+
struct list *ssmpingd_list;
pim_addr ssmpingd_group_addr;
diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c
index 3c36512a3d..66001d1463 100644
--- a/pimd/pim_nb.c
+++ b/pimd/pim_nb.c
@@ -380,6 +380,55 @@ const struct frr_yang_module_info frr_pim_rp_info = {
}
},
{
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/discovery-enabled",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-scope",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-interval",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-holdtime",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list",
+ .cbs = {
+ .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/group",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_group_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/prefix-list",
+ .cbs = {
+ .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_modify,
+ .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_prefix_list_destroy,
+ }
+ },
+ {
.xpath = NULL,
},
}
diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h
index dfab582968..befad4efe4 100644
--- a/pimd/pim_nb.h
+++ b/pimd/pim_nb.h
@@ -159,6 +159,34 @@ 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_static_rp_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_discovery_enabled_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_discovery_enabled_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_scope_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_interval_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_modify(
+ struct nb_cb_modify_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_announce_holdtime_destroy(
+ struct nb_cb_destroy_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_create(
+ struct nb_cb_create_args *args);
+int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp_auto_rp_candidate_rp_list_destroy(
+ struct nb_cb_destroy_args *args);
+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);
+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);
+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);
+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);
/* frr-cand-bsr */
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_candidate_bsr_create(
@@ -258,6 +286,7 @@ int routing_control_plane_protocols_name_validate(
"mroute[source-addr='%s'][group-addr='%s']"
#define FRR_PIM_STATIC_RP_XPATH \
"frr-pim-rp:rp/static-rp/rp-list[rp-address='%s']"
+#define FRR_PIM_AUTORP_XPATH "./frr-pim-rp:rp/auto-rp"
#define FRR_GMP_INTERFACE_XPATH \
"./frr-gmp:gmp/address-family[address-family='%s']"
#define FRR_GMP_ENABLE_XPATH \
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index 0366d8a857..2b78b8671f 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -26,6 +26,7 @@
#include "lib_errors.h"
#include "pim_util.h"
#include "pim6_mld.h"
+#include "pim_autorp.h"
#include "pim_igmp.h"
#if PIM_IPV == 6
@@ -147,6 +148,11 @@ static int pim_cmd_interface_add(struct interface *ifp)
pim_if_membership_refresh(ifp);
pim_if_create_pimreg(pim_ifp->pim);
+
+#if PIM_IPV == 4
+ pim_autorp_add_ifp(ifp);
+#endif
+
return 1;
}
@@ -2680,6 +2686,365 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp
return NB_OK;
}
+/*
+ * 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;
+
+ 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;
+ enabled = yang_dnode_get_bool(args->dnode, NULL);
+ if (enabled)
+ pim_autorp_start_discovery(pim);
+ else
+ 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;
+
+ 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;
+ enabled = yang_dnode_get_bool(args->dnode, NULL);
+ /* Run AutoRP discovery by default */
+ if (!enabled)
+ pim_autorp_start_discovery(pim);
+ break;
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-scope
+ */
+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;
+
+ 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;
+ scope = yang_dnode_get_uint8(args->dnode, NULL);
+ pim_autorp_announce_scope(pim, scope);
+ }
+#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;
+
+ 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;
+ pim_autorp_announce_scope(pim, 0);
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-interval
+ */
+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;
+
+ 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;
+ interval = yang_dnode_get_uint16(args->dnode, NULL);
+ pim_autorp_announce_interval(pim, interval);
+ }
+#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;
+
+ 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;
+ pim_autorp_announce_interval(pim, 0);
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/announce-holdtime
+ */
+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;
+
+ 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;
+ holdtime = yang_dnode_get_uint16(args->dnode, NULL);
+ pim_autorp_announce_holdtime(pim, holdtime);
+ }
+#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;
+
+ 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;
+ /* 0 is a valid value, so -1 indicates deleting (go back to default) */
+ pim_autorp_announce_holdtime(pim, -1);
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list
+ */
+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:
+ case NB_EV_ABORT:
+ 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;
+
+ 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;
+ yang_dnode_get_pimaddr(&rp_addr, args->dnode, "rp-address");
+ if (!pim_autorp_rm_candidate_rp(pim, rp_addr))
+ return NB_ERR_INCONSISTENCY;
+ break;
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/group
+ */
+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;
+ pim_addr rp_addr;
+
+ 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;
+ yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
+ yang_dnode_get_prefix(&group, args->dnode, NULL);
+ apply_mask(&group);
+ pim_autorp_add_candidate_rp_group(pim, rp_addr, group);
+ }
+#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;
+ pim_addr rp_addr;
+
+ 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;
+ yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
+ yang_dnode_get_prefix(&group, args->dnode, NULL);
+ apply_mask(&group);
+ if (!pim_autorp_rm_candidate_rp_group(pim, rp_addr, group))
+ return NB_ERR_INCONSISTENCY;
+ }
+#endif
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/frr-pim-rp:rp/auto-rp/candidate-rp-list/prefix-list
+ */
+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;
+ const char *plist;
+
+ 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;
+ 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);
+ }
+#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;
+ const char *plist;
+
+ 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;
+ yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address");
+ plist = yang_dnode_get_string(args->dnode, NULL);
+ if (!pim_autorp_rm_candidate_rp_plist(pim, rp_addr, plist))
+ return NB_ERR_INCONSISTENCY;
+ break;
+ }
+#endif
+
+ return NB_OK;
+}
+
static void yang_addrsel(struct cand_addrsel *addrsel,
const struct lyd_node *node)
{
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index a2ddc82164..0c47bc1582 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -1140,7 +1140,8 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty)
if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
continue;
- if (rp_info->rp_src == RP_SRC_BSR)
+ if (rp_info->rp_src != RP_SRC_NONE &&
+ rp_info->rp_src != RP_SRC_STATIC)
continue;
rp_addr = rp_info->rp.rpf_addr;
@@ -1200,6 +1201,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct prefix *range,
strlcpy(source, "Static", sizeof(source));
else if (rp_info->rp_src == RP_SRC_BSR)
strlcpy(source, "BSR", sizeof(source));
+ else if (rp_info->rp_src == RP_SRC_AUTORP)
+ strlcpy(source, "AutoRP", sizeof(source));
else
strlcpy(source, "None", sizeof(source));
if (json) {
diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h
index 32c6306740..24832d0dbd 100644
--- a/pimd/pim_rp.h
+++ b/pimd/pim_rp.h
@@ -16,11 +16,7 @@
struct pim_interface;
-enum rp_source {
- RP_SRC_NONE = 0,
- RP_SRC_STATIC,
- RP_SRC_BSR
-};
+enum rp_source { RP_SRC_NONE = 0, RP_SRC_STATIC, RP_SRC_BSR, RP_SRC_AUTORP };
struct rp_info {
struct prefix group;
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c
index 3476c177b7..8900652df9 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -292,6 +292,36 @@ int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
return ret;
}
+int pim_socket_leave(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
+ struct pim_interface *pim_ifp)
+{
+ int ret;
+
+#if PIM_IPV == 4
+ ret = setsockopt_ipv4_multicast(fd, IP_DROP_MEMBERSHIP, ifaddr,
+ group.s_addr, ifindex);
+#else
+ struct ipv6_mreq opt;
+
+ memcpy(&opt.ipv6mr_multiaddr, &group, 16);
+ opt.ipv6mr_interface = ifindex;
+ ret = setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &opt, sizeof(opt));
+#endif
+
+ if (ret) {
+ flog_err(EC_LIB_SOCKET,
+ "Failure socket leaving fd=%d group %pPAs on interface address %pPAs: %m",
+ fd, &group, &ifaddr);
+ pim_ifp->igmp_ifstat_joins_failed++;
+ return ret;
+ }
+
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("Socket fd=%d left group %pPAs on interface address %pPAs",
+ fd, &group, &ifaddr);
+ return ret;
+}
+
#if PIM_IPV == 4
static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
ifindex_t *ifindex)
diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h
index 1cf01b31d6..0a81c6943a 100644
--- a/pimd/pim_sock.h
+++ b/pimd/pim_sock.h
@@ -32,6 +32,8 @@ int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
uint8_t loop);
int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
struct pim_interface *pim_ifp);
+int pim_socket_leave(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
+ struct pim_interface *pim_ifp);
int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
struct sockaddr_storage *from, socklen_t *fromlen,
struct sockaddr_storage *to, socklen_t *tolen,
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index d5d6fb54d5..b633e81d55 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -165,6 +165,11 @@ int pim_debug_config_write(struct vty *vty)
++writes;
}
+ if (PIM_DEBUG_AUTORP) {
+ vty_out(vty, "debug pim autorp\n");
+ ++writes;
+ }
+
return writes;
}
@@ -182,6 +187,9 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
}
writes += pim_rp_config_write(pim, vty);
+#if PIM_IPV == 4
+ writes += pim_autorp_config_write(pim, vty);
+#endif
writes += pim_cand_config_write(pim, vty);
if (pim->vrf->vrf_id == VRF_DEFAULT) {
diff --git a/pimd/pimd.h b/pimd/pimd.h
index 3d9318953b..461b7d08a3 100644
--- a/pimd/pimd.h
+++ b/pimd/pimd.h
@@ -95,6 +95,7 @@
#define PIM_MASK_VXLAN (1 << 26)
#define PIM_MASK_BSM_PROC (1 << 27)
#define PIM_MASK_MLAG (1 << 28)
+#define PIM_MASK_AUTORP (1 << 29)
/* Remember 32 bits!!! */
/* PIM error codes */
@@ -167,6 +168,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DEBUG_MTRACE (router->debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_VXLAN (router->debugs & PIM_MASK_VXLAN)
#define PIM_DEBUG_BSM (router->debugs & PIM_MASK_BSM_PROC)
+#define PIM_DEBUG_AUTORP (router->debugs & PIM_MASK_AUTORP)
#define PIM_DEBUG_EVENTS \
(router->debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_GM_EVENTS | \
@@ -209,6 +211,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DO_DEBUG_PIM_NHT_RP (router->debugs |= PIM_MASK_PIM_NHT_RP)
#define PIM_DO_DEBUG_MTRACE (router->debugs |= PIM_MASK_MTRACE)
#define PIM_DO_DEBUG_VXLAN (router->debugs |= PIM_MASK_VXLAN)
+#define PIM_DO_DEBUG_AUTORP (router->debugs |= PIM_MASK_AUTORP)
#define PIM_DONT_DEBUG_PIM_EVENTS (router->debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (router->debugs &= ~PIM_MASK_PIM_PACKETS)
@@ -243,6 +246,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;
#define PIM_DONT_DEBUG_MTRACE (router->debugs &= ~PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_VXLAN (router->debugs &= ~PIM_MASK_VXLAN)
#define PIM_DONT_DEBUG_BSM (router->debugs &= ~PIM_MASK_BSM_PROC)
+#define PIM_DONT_DEBUG_AUTORP (router->debugs &= ~PIM_MASK_AUTORP)
/* RFC 3376: 8.1. Robustness Variable - Default: 2 for IGMP */
/* RFC 2710: 7.1. Robustness Variable - Default: 2 for MLD */
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 48f1e3b724..bda594e5c4 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -59,6 +59,7 @@ pim_common = \
pimd_pimd_SOURCES = \
$(pim_common) \
+ pimd/pim_autorp.c \
pimd/pim_cmd.c \
pimd/pim_igmp.c \
pimd/pim_igmp_mtrace.c \
@@ -98,6 +99,7 @@ nodist_pimd_pim6d_SOURCES = \
noinst_HEADERS += \
pimd/pim_addr.h \
pimd/pim_assert.h \
+ pimd/pim_autorp.h \
pimd/pim_bfd.h \
pimd/pim_bsm.h \
pimd/pim_cmd.h \
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
index 2062f65561..369a794ebc 100644
--- a/tests/topotests/lib/pim.py
+++ b/tests/topotests/lib/pim.py
@@ -1746,6 +1746,49 @@ def verify_pim_rp_info(
@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_rp_info_is_empty(tgen, dut, af="ipv4"):
+ """
+ Verify pim rp info by running "show ip pim rp-info" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+
+ Usage
+ -----
+ dut = "r1"
+ result = verify_pim_rp_info_is_empty(tgen, dut)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ ip_cmd = "ip"
+ if af == "ipv6":
+ ip_cmd = "ipv6"
+
+ logger.info("[DUT: %s]: Verifying %s rp info", dut, ip_cmd)
+ cmd = "show {} pim rp-info json".format(ip_cmd)
+ show_ip_rp_info_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if show_ip_rp_info_json:
+ errormsg = "[DUT %s]: Verifying empty rp-info [FAILED]!!" % (dut)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
def verify_pim_state(
tgen,
dut,
@@ -2411,10 +2454,11 @@ def clear_igmp_interfaces(tgen, dut):
# Verify uptime for groups
for group in group_before_clear.keys():
- d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
- d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
- if d2 >= d1:
- errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
+ if group in group_after_clear:
+ d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
+ d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
+ if d2 >= d1:
+ errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]")
@@ -2751,6 +2795,48 @@ def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=N
return True
+def scapy_send_autorp_raw_packet(tgen, senderRouter, senderInterface, packet=None):
+ """
+ Using scapy Raw() method to send AutoRP raw packet from one FRR
+ to other
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `senderRouter` : Sender router
+ * `senderInterface` : SenderInterface
+ * `packet` : AutoRP packet in raw format
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ global CWD
+ result = ""
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ python3_path = tgen.net.get_exec_path(["python3", "python"])
+ # send_bsr_packet.py has no direct ties to bsr, just sends a raw packet out
+ # a given interface, so just reuse it
+ script_path = os.path.join(CWD, "send_bsr_packet.py")
+ node = tgen.net[senderRouter]
+
+ cmd = [
+ python3_path,
+ script_path,
+ packet,
+ senderInterface,
+ "--interval=1",
+ "--count=1",
+ ]
+ logger.info("Scapy cmd: \n %s", cmd)
+ node.cmd_raises(cmd)
+
+ logger.debug("Exiting lib API: scapy_send_autorp_raw_packet")
+ return True
+
+
def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None):
"""
Find which RP is having lowest prioriy and returns rp IP
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
index 715aa1de72..44a7db56f1 100644
--- a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json
@@ -1 +1 @@
-{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
+{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
index 3bbcce1370..982157a624 100644
--- a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json
@@ -1,5 +1,4 @@
{
- "totalGroups":5,
"watermarkLimit":0,
"l1-i1-eth1":{
"name":"l1-i1-eth1",
@@ -48,4 +47,3 @@
]
}
}
-
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
index 876befa1b8..6042ef4dbf 100644
--- a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json
@@ -1 +1 @@
-{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
+{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
index a3fb496d25..0312c3026d 100644
--- a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json
@@ -1,5 +1,4 @@
{
- "totalGroups":5,
"watermarkLimit":0,
"l1-i1-eth1":{
"name":"l1-i1-eth1",
diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
index 11ac5a01e7..537be377be 100644
--- a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
+++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json
@@ -1 +1 @@
-{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
+{"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}}
diff --git a/tests/topotests/pim_autorp/__init__.py b/tests/topotests/pim_autorp/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/pim_autorp/__init__.py
diff --git a/tests/topotests/pim_autorp/r1/frr.conf b/tests/topotests/pim_autorp/r1/frr.conf
new file mode 100644
index 0000000000..2fddbc3ae2
--- /dev/null
+++ b/tests/topotests/pim_autorp/r1/frr.conf
@@ -0,0 +1,16 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+debug pim autorp
+!
+interface r1-eth0
+ ip address 10.10.76.1/24
+ ip igmp
+ ip pim
+!
+ip forwarding
+!
+router pim
+ autorp discovery
+! \ No newline at end of file
diff --git a/tests/topotests/pim_autorp/r2/frr.conf b/tests/topotests/pim_autorp/r2/frr.conf
new file mode 100644
index 0000000000..fd3c0cad39
--- /dev/null
+++ b/tests/topotests/pim_autorp/r2/frr.conf
@@ -0,0 +1,16 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-frr.log
+debug pim autorp
+!
+interface r2-eth0
+ ip address 10.10.76.2/24
+ ip igmp
+ ip pim
+!
+ip forwarding
+!
+router pim
+ autorp discovery
+! \ 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
new file mode 100644
index 0000000000..5aecce942e
--- /dev/null
+++ b/tests/topotests/pim_autorp/test_pim_autorp.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_pim_autorp.py
+#
+# Copyright (c) 2024 ATCorp
+# Nathan Bahr
+#
+
+import os
+import sys
+import pytest
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, 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.common_config import step, write_test_header
+
+from time import sleep
+
+"""
+test_pim_autorp.py: Test general PIM AutoRP functionality
+"""
+
+TOPOLOGY = """
+ Basic AutoRP functionality
+
+ +---+---+ +---+---+
+ | | 10.10.76.0/24 | |
+ + R1 + <------------------> + R2 |
+ | | .1 .2 | |
+ +---+---+ +---+---+
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+pytestmark = [pytest.mark.pimd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ # 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"])
+
+def setup_module(mod):
+ logger.info("PIM AutoRP basic functionality:\n {}".format(TOPOLOGY))
+
+ 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)
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ # Initialize all routers.
+ tgen.start_router()
+ for router in router_list.values():
+ if router.has_version("<", "4.0"):
+ tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_pim_autorp_discovery_single_rp(request):
+ "Test PIM AutoRP Discovery with single RP"
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ 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)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ 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)
+
+def test_pim_autorp_discovery_multiple_rp(request):
+ "Test PIM AutoRP Discovery with multiple RP's"
+ 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")
+
+ 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)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+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)
+
+ 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)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_pim_autorp_announce_group(request):
+ "Test PIM AutoRP Announcement with a single group"
+ 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")
+
+ 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'")
+ 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 Announcement sent data")
+ # TODO: Verify AutoRP mapping agent receives updated candidate RP announcement
+ # Mapping agent is not yet implemented
+ #sleep(10)
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-pim-rp.yang b/yang/frr-pim-rp.yang
index 4cc214135d..dbd5513ee5 100644
--- a/yang/frr-pim-rp.yang
+++ b/yang/frr-pim-rp.yang
@@ -111,6 +111,70 @@ module frr-pim-rp {
} // static-rp
} // static-rp-container
+ grouping auto-rp-container {
+ description
+ "Grouping of AutoRP container.";
+
+ container auto-rp {
+ description
+ "Containing AutoRP attributes.";
+
+ leaf discovery-enabled {
+ type boolean;
+ description
+ "Flag indicating if Auto RP discovery is enabled.";
+ }
+
+ leaf announce-scope {
+ type uint8;
+ description
+ "The TTL of the C-RP Announcement packet.";
+ }
+
+ leaf announce-interval {
+ type uint16;
+ description
+ "The time between sending C-RP announcement packets.";
+ }
+
+ leaf announce-holdtime {
+ type uint16;
+ description
+ "The hold time in seconds advertised in the announcement packet.";
+ }
+
+ list candidate-rp-list {
+ key "rp-address";
+ description
+ "A list of Candidate RP addresses.";
+
+ leaf rp-address {
+ type inet:ip-address;
+ description
+ "Specifies a candidate RP address.";
+ }
+
+ choice group-or-prefix-list {
+ description "Use group or prefix-list";
+ case group {
+ leaf group {
+ type frr-route-types:ip-multicast-group-prefix;
+ description
+ "Multicast group prefix.";
+ }
+ }
+ case prefix-list {
+ leaf prefix-list {
+ type plist-ref;
+ description
+ "Group prefix-list filter";
+ }
+ }
+ }
+ } // candidate-rp-list
+ } // auto-rp
+ } // auto-rp-container
+
/*
* Configuration data nodes
*/
@@ -123,6 +187,13 @@ module frr-pim-rp {
description
"PIM RP configuration data.";
uses static-rp-container;
+
+ uses auto-rp-container {
+ when "../frr-pim:address-family = 'frr-rt:ipv4'" {
+ description
+ "Only applicable to IPv4 address family.";
+ }
+ }
} // rp
} // augment
}