Adds the ability to filter PIM Joins & IGMP reports on an interface.
Enabling a multicast boundary on an interface for a particular group
will prevent the interface from appearing in the group's OIL.
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
return CMD_SUCCESS;
}
+/* boundaries */
+DEFUN(interface_ip_pim_boundary_oil,
+ interface_ip_pim_boundary_oil_cmd,
+ "ip multicast boundary oil WORD",
+ IP_STR
+ "Generic multicast configuration options\n"
+ "Define multicast boundary\n"
+ "Filter OIL by group using prefix list\n"
+ "Prefix list to filter OIL with")
+{
+ VTY_DECLVAR_CONTEXT(interface, iif);
+ struct pim_interface *pim_ifp;
+ int idx = 0;
+
+ argv_find(argv, argc, "WORD", &idx);
+
+ PIM_GET_PIM_INTERFACE(pim_ifp, iif);
+
+ if (pim_ifp->boundary_oil_plist)
+ XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
+
+ pim_ifp->boundary_oil_plist =
+ XSTRDUP(MTYPE_PIM_INTERFACE, argv[idx]->arg);
+
+ /* Interface will be pruned from OIL on next Join */
+ return CMD_SUCCESS;
+}
+
+DEFUN(interface_no_ip_pim_boundary_oil,
+ interface_no_ip_pim_boundary_oil_cmd,
+ "no ip multicast boundary oil [WORD]",
+ NO_STR
+ IP_STR
+ "Generic multicast configuration options\n"
+ "Define multicast boundary\n"
+ "Filter OIL by group using prefix list\n"
+ "Prefix list to filter OIL with")
+{
+ VTY_DECLVAR_CONTEXT(interface, iif);
+ struct pim_interface *pim_ifp;
+ int idx;
+
+ argv_find(argv, argc, "WORD", &idx);
+
+ PIM_GET_PIM_INTERFACE(pim_ifp, iif);
+
+ if (pim_ifp->boundary_oil_plist)
+ XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (interface_ip_mroute,
interface_ip_mroute_cmd,
"ip mroute INTERFACE A.B.C.D",
install_element(INTERFACE_NODE, &interface_no_ip_pim_drprio_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_hello_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_pim_hello_cmd);
+ install_element(INTERFACE_NODE, &interface_ip_pim_boundary_oil_cmd);
+ install_element(INTERFACE_NODE, &interface_no_ip_pim_boundary_oil_cmd);
// Static mroutes NEB
install_element(INTERFACE_NODE, &interface_ip_mroute_cmd);
list_delete(pim_ifp->upstream_switch_list);
list_delete(pim_ifp->sec_addr_list);
+ if (pim_ifp->boundary_oil_plist)
+ XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
+
while ((ch = RB_ROOT(pim_ifchannel_rb,
&pim_ifp->ifchannel_rb)) != NULL)
pim_ifchannel_delete(ch);
uint32_t pim_dr_priority; /* config */
int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */
+ /* boundary prefix-list */
+ char *boundary_oil_plist;
+
int64_t pim_ifstat_start; /* start timestamp for stats */
uint32_t pim_ifstat_hello_sent;
uint32_t pim_ifstat_hello_sendfail;
return -1;
}
- /* RFC 3376 defines some guidelines on operating in backwards
- * compatibility
- * with older versions of IGMP but there are some gaps in the logic:
+ /*
+ * RFC 3376 defines some guidelines on operating in backwards
+ * compatibility with older versions of IGMP but there are some gaps in
+ * the logic:
*
* - once we drop from say version 3 to version 2 we will never go back
- * to
- * version 3 even if the node that TXed an IGMP v2 query upgrades to
- * v3
+ * to version 3 even if the node that TXed an IGMP v2 query upgrades
+ * to v3
*
* - The node with the lowest IP is the querier so we will only know to
- * drop
- * from v3 to v2 if the node that is the querier is also the one that
- * is
- * running igmp v2. If a non-querier only supports igmp v2 we will
- * have
- * no way of knowing.
+ * drop from v3 to v2 if the node that is the querier is also the one
+ * that is running igmp v2. If a non-querier only supports igmp v2
+ * we will have no way of knowing.
*
* For now we will simplify things and inform the user that they need to
* configure all PIM routers to use the same version of IGMP.
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+ if (pim_is_group_filtered(ifp->info, &group_addr))
+ return -1;
+
/* non-existant group is created as INCLUDE {empty} */
group = igmp_add_group_by_addr(igmp, group_addr);
if (!group) {
on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources,
sources);
+ if (pim_is_group_filtered(ifp->info, &group_addr))
+ return;
+
/* non-existant group is created as INCLUDE {empty} */
group = igmp_add_group_by_addr(igmp, group_addr);
if (!group) {
struct interface *ifp = igmp->interface;
int i;
int local_ncb = 0;
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = igmp->interface->info;
if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
zlog_warn(
int j;
struct prefix lncb;
struct prefix g;
+ bool filtered = false;
if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
> report_pastend) {
g.family = AF_INET;
g.u.prefix4 = rec_group;
g.prefixlen = 32;
+
+ /* determine filtering status for group */
+ filtered = pim_is_group_filtered(ifp->info, &rec_group);
+
+ if (PIM_DEBUG_IGMP_PACKETS && filtered)
+ zlog_debug(
+ "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
+ inet_ntoa(rec_group), from_str, ifp->name,
+ pim_ifp->boundary_oil_plist);
+
/*
* If we receive a igmp report with the group in 224.0.0.0/24
* then we should ignore it
if (prefix_match(&lncb, &g))
local_ncb = 1;
- if (!local_ncb)
+ if (!local_ncb && !filtered)
switch (rec_type) {
case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
igmpv3_report_isin(igmp, from, rec_group,
#include "pim_rpf.h"
#include "pim_rp.h"
#include "pim_jp_agg.h"
+#include "pim_util.h"
static void on_trace(const char *label, struct interface *ifp,
struct in_addr src)
int tlv_buf_size)
{
struct prefix msg_upstream_addr;
+ struct pim_interface *pim_ifp;
uint8_t msg_num_groups;
uint16_t msg_holdtime;
int addr_offset;
buf = tlv_buf;
pastend = tlv_buf + tlv_buf_size;
+ pim_ifp = ifp->info;
/*
Parse ucast addr
uint16_t msg_num_pruned_sources;
int source;
struct pim_ifchannel *starg_ch = NULL, *sg_ch = NULL;
+ bool filtered = false;
memset(&sg, 0, sizeof(struct prefix_sg));
addr_offset = pim_parse_addr_group(&sg, buf, pastend - buf);
src_str, ifp->name);
}
+ /* boundary check */
+ filtered = pim_is_group_filtered(pim_ifp, &sg.grp);
+
/* Scan joined sources */
for (source = 0; source < msg_num_joined_sources; ++source) {
addr_offset = pim_parse_addr_source(
buf += addr_offset;
+ /* if we are filtering this group, skip the join */
+ if (filtered)
+ continue;
+
recv_join(ifp, neigh, msg_holdtime,
msg_upstream_addr.u.prefix4, &sg,
msg_source_flags);
}
buf += addr_offset;
+
+ /* if we are filtering this group, skip the prune */
+ if (filtered)
+ continue;
+
recv_prune(ifp, neigh, msg_holdtime,
msg_upstream_addr.u.prefix4, &sg,
msg_source_flags);
}
}
}
- if (starg_ch)
+ if (starg_ch && !filtered)
pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0);
starg_ch = NULL;
} /* scan groups */
#include "log.h"
#include "prefix.h"
+#include "plist.h"
#include "pim_util.h"
group.family = AF_INET;
group.u.prefix4 = group_addr;
- group.prefixlen = 32;
+ group.prefixlen = IPV4_MAX_PREFIXLEN;
return prefix_match(&group_224, &group);
}
return prefix_match(&group_all, &group);
}
+
+bool pim_is_group_filtered(struct pim_interface *pim_ifp, struct in_addr *grp)
+{
+ struct prefix grp_pfx;
+ struct prefix_list *pl;
+
+ if (!pim_ifp->boundary_oil_plist)
+ return false;
+
+ grp_pfx.family = AF_INET;
+ grp_pfx.prefixlen = 32;
+ grp_pfx.u.prefix4 = *grp;
+
+ pl = prefix_list_lookup(AFI_IP, pim_ifp->boundary_oil_plist);
+ return pl ? prefix_list_apply(pl, &grp_pfx) == PREFIX_DENY : false;
+}
#include <zebra.h>
#include "checksum.h"
+#include "pimd.h"
+#include "pim_iface.h"
uint8_t igmp_msg_encode16to8(uint16_t value);
uint16_t igmp_msg_decode8to16(uint8_t code);
int pim_is_group_224_0_0_0_24(struct in_addr group_addr);
int pim_is_group_224_4(struct in_addr group_addr);
+bool pim_is_group_filtered(struct pim_interface *pim_ifp, struct in_addr *grp);
#endif /* PIM_UTIL_H */
vty_out(vty, " %d",
pim_ifp->pim_default_holdtime);
vty_out(vty, "\n");
+ ++writes;
}
/* update source */
}
}
+ /* boundary */
+ if (pim_ifp->boundary_oil_plist) {
+ vty_out(vty,
+ " ip pim boundary oil %s\n",
+ pim_ifp->boundary_oil_plist);
+ ++writes;
+ }
+
writes +=
pim_static_write_mroute(pim, vty, ifp);
pim_bfd_write_config(vty, ifp);