From b0f525a84c16807ccb3897d037d5052ba342df0f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Sep 2017 14:33:00 -0400 Subject: [PATCH] pimd: add support for boundaries 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 --- pimd/pim_cmd.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_iface.c | 3 +++ pimd/pim_iface.h | 3 +++ pimd/pim_igmp.c | 24 ++++++++++----------- pimd/pim_igmpv3.c | 19 ++++++++++++++++- pimd/pim_join.c | 18 +++++++++++++++- pimd/pim_util.c | 19 ++++++++++++++++- pimd/pim_util.h | 3 +++ pimd/pim_vty.c | 9 ++++++++ 9 files changed, 137 insertions(+), 15 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 396b949e4..1ebe9c9ab 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -6462,6 +6462,58 @@ DEFUN (interface_no_ip_pim_sm, 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", @@ -8564,6 +8616,8 @@ void pim_cmd_init(void) 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); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index d98e5f1f6..b8cbed7f9 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -246,6 +246,9 @@ void pim_if_delete(struct interface *ifp) 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); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 2f27a1401..09bd2b06e 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -122,6 +122,9 @@ struct pim_interface { 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; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 3a870374c..f6c8db7ac 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -299,22 +299,19 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, return -1; } - /* RFC 3376 defines some guidelines on operating in backwards - * compatibility - * with older versions of IGMP but there are some gaps in the logic: + /* + * 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. @@ -403,6 +400,9 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, 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) { diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 1fc7517e0..ecde546c0 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -671,6 +671,9 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, 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) { @@ -1869,6 +1872,9 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, 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( @@ -1920,6 +1926,7 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, int j; struct prefix lncb; struct prefix g; + bool filtered = false; if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) { @@ -1983,6 +1990,16 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, 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 @@ -1990,7 +2007,7 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, 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, diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 4f5e53401..ae5032be7 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -38,6 +38,7 @@ #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) @@ -153,6 +154,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, 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; @@ -163,6 +165,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, buf = tlv_buf; pastend = tlv_buf + tlv_buf_size; + pim_ifp = ifp->info; /* Parse ucast addr @@ -231,6 +234,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, 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); @@ -273,6 +277,9 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, 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( @@ -283,6 +290,10 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, 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); @@ -304,6 +315,11 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, } 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); @@ -335,7 +351,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, } } } - if (starg_ch) + if (starg_ch && !filtered) pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0); starg_ch = NULL; } /* scan groups */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c index 820117a03..15bde256d 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -21,6 +21,7 @@ #include "log.h" #include "prefix.h" +#include "plist.h" #include "pim_util.h" @@ -114,7 +115,7 @@ int pim_is_group_224_0_0_0_24(struct in_addr group_addr) group.family = AF_INET; group.u.prefix4 = group_addr; - group.prefixlen = 32; + group.prefixlen = IPV4_MAX_PREFIXLEN; return prefix_match(&group_224, &group); } @@ -137,3 +138,19 @@ int pim_is_group_224_4(struct in_addr group_addr) 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; +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h index 1b319cfe4..c66dd7b66 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -25,6 +25,8 @@ #include #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); @@ -33,4 +35,5 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size); int pim_is_group_224_0_0_0_24(struct in_addr group_addr); int pim_is_group_224_4(struct in_addr group_addr); +bool pim_is_group_filtered(struct pim_interface *pim_ifp, struct in_addr *grp); #endif /* PIM_UTIL_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 3da092541..c1adbcc91 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -285,6 +285,7 @@ int pim_interface_config_write(struct vty *vty) vty_out(vty, " %d", pim_ifp->pim_default_holdtime); vty_out(vty, "\n"); + ++writes; } /* update source */ @@ -358,6 +359,14 @@ int pim_interface_config_write(struct vty *vty) } } + /* 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); -- 2.39.5