2025-02-05 10:17:20 +01:00
|
|
|
// 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;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP);
|
|
|
|
/* There may not be a path to the RP right now, but that doesn't mean it failed to add the RP */
|
|
|
|
if (ret != PIM_SUCCESS && ret != PIM_RP_NO_PATH) {
|
|
|
|
zlog_err("%s: Failed to add new RP addr=%pI4, grp=%pFX, grplist=%s",
|
|
|
|
__func__, &rpaddr, &grp,
|
|
|
|
(listname ? listname : "NONE"));
|
|
|
|
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, &trp->addr);
|
|
|
|
} else {
|
|
|
|
/* If hold time is zero, make sure there doesn't exist a hold timer for it already */
|
|
|
|
event_cancel(&trp->hold_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool pim_autorp_discovery(struct pim_autorp *autorp, uint8_t rpcnt,
|
|
|
|
uint16_t holdtime, char *buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
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) {
|
|
|
|
if (setsockopt(autorp->sock, IPPROTO_IP, IP_MULTICAST_TTL,
|
|
|
|
&(autorp->announce_scope),
|
|
|
|
sizeof(autorp->announce_scope)) < 0) {
|
|
|
|
if (PIM_DEBUG_AUTORP)
|
|
|
|
zlog_err("%s: Failed to set Multicast TTL for sending AutoRP announcement message, errno=%d, %s",
|
|
|
|
__func__, errno, safe_strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
if (setsockopt(autorp->sock, IPPROTO_IP,
|
|
|
|
IP_MULTICAST_IF,
|
|
|
|
&(pim_ifp->primary_address),
|
|
|
|
sizeof(pim_ifp->primary_address)) <
|
|
|
|
0) {
|
|
|
|
if (PIM_DEBUG_AUTORP)
|
|
|
|
zlog_err("%s: Failed to set Multicast Interface for sending AutoRP announcement message, errno=%d, %s",
|
|
|
|
__func__, errno,
|
|
|
|
safe_strerror(errno));
|
|
|
|
}
|
|
|
|
if (sendto(autorp->sock, autorp->annouce_pkt,
|
|
|
|
autorp->annouce_pkt_sz, 0,
|
|
|
|
(struct sockaddr *)&announceGrp,
|
|
|
|
sizeof(announceGrp)) <= 0) {
|
|
|
|
if (PIM_DEBUG_AUTORP)
|
|
|
|
zlog_err("%s: Failed to send AutoRP announcement message, errno=%d, %s",
|
|
|
|
__func__, errno,
|
|
|
|
safe_strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
rp->grp.family = AF_INET;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2025-02-05 10:19:18 +01:00
|
|
|
pim->autorp = autorp;
|
|
|
|
|
2025-02-05 10:17:20 +01:00
|
|
|
if (!pim_autorp_socket_enable(autorp)) {
|
2025-02-05 10:19:18 +01:00
|
|
|
zlog_err("%s: AutoRP failed to initialize, feature will not work correctly", __func__);
|
2025-02-05 10:17:20 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_TTABLE, table);
|
|
|
|
}
|
|
|
|
|
|
|
|
ttable_del(tt);
|
|
|
|
}
|