mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 05:27:16 +02:00
pimd,yang: Extend multicast boundary functionality
Add new interface command ip multicast boundary ACCESSLIST4_NAME. This allows filtering on both source and group using the extended access-list syntax vs. group-only as with the existing "ip multicast boundary oil" command, which uses prefix-lists. If both are configured, the prefix- list is evaluated first. The default behavior for both prefix-lists and access-lists remains "deny", so the prefix-list must have a terminating "permit" statement in order to also evaluate against the access-list. The following example denies groups in range 229.1.1.0/24 and groups in range 232.1.1.0/24 with source 10.0.20.2: ! ip prefix-list pim-oil-plist seq 10 deny 229.1.1.0/24 ip prefix-list pim-oil-plist seq 20 permit any ! access-list pim-acl seq 10 deny ip host 10.0.20.2 232.1.1.0 0.0.0.255 access-list pim-acl seq 20 permit ip any any ! interface r1-eth0 ip address 10.0.20.1/24 ip igmp ip pim ip multicast boundary oil pim-oil-plist ip multicast boundary pim-acl ! Signed-off-by: Corey Siltala <csiltala@atcorp.com>
This commit is contained in:
parent
a9bee74ea2
commit
4de4017d64
|
@ -5871,6 +5871,21 @@ DEFUN(interface_no_ip_pim_boundary_oil,
|
|||
return pim_process_no_ip_pim_boundary_oil_cmd(vty);
|
||||
}
|
||||
|
||||
DEFPY_YANG(interface_ip_pim_boundary_acl,
|
||||
interface_ip_pim_boundary_acl_cmd,
|
||||
"[no] ip multicast boundary ACCESSLIST4_NAME$name",
|
||||
NO_STR
|
||||
IP_STR
|
||||
"Generic multicast configuration options\n"
|
||||
"Define multicast boundary\n"
|
||||
"Access-list to filter OIL with by source and group\n")
|
||||
{
|
||||
nb_cli_enqueue_change(vty, "./multicast-boundary-acl",
|
||||
(!!no ? NB_OP_DESTROY : NB_OP_MODIFY), name);
|
||||
|
||||
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
|
||||
}
|
||||
|
||||
DEFUN (interface_ip_mroute,
|
||||
interface_ip_mroute_cmd,
|
||||
"ip mroute INTERFACE A.B.C.D [A.B.C.D]",
|
||||
|
@ -9018,6 +9033,7 @@ void pim_cmd_init(void)
|
|||
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);
|
||||
install_element(INTERFACE_NODE, &interface_ip_pim_boundary_acl_cmd);
|
||||
install_element(INTERFACE_NODE, &interface_ip_igmp_query_generate_cmd);
|
||||
|
||||
// Static mroutes NEB
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "pim_igmp_join.h"
|
||||
#include "pim_vxlan.h"
|
||||
#include "pim_tib.h"
|
||||
#include "pim_util.h"
|
||||
|
||||
#include "pim6_mld.h"
|
||||
|
||||
|
@ -1258,6 +1259,14 @@ static int gm_join_sock(const char *ifname, ifindex_t ifindex,
|
|||
{
|
||||
int join_fd;
|
||||
|
||||
if (pim_is_group_filtered(pim_ifp, &group_addr, &source_addr)) {
|
||||
if (PIM_DEBUG_GM_EVENTS) {
|
||||
zlog_debug("%s: join failed for (S,G)=(%pPAs,%pPAs) due to multicast boundary filtering",
|
||||
__func__, &source_addr, &group_addr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
pim_ifp->igmp_ifstat_joins_sent++;
|
||||
|
||||
join_fd = pim_socket_raw(IPPROTO_GM);
|
||||
|
|
|
@ -133,8 +133,10 @@ struct pim_interface {
|
|||
uint32_t pim_dr_priority; /* config */
|
||||
int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */
|
||||
|
||||
/* boundary prefix-list */
|
||||
/* boundary prefix-list (group) */
|
||||
char *boundary_oil_plist;
|
||||
/* boundary access-list (source and group) */
|
||||
struct access_list *boundary_acl;
|
||||
|
||||
/* Turn on Active-Active for this interface */
|
||||
bool activeactive;
|
||||
|
|
|
@ -666,7 +666,7 @@ static int igmp_v1_recv_report(struct gm_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))
|
||||
if (pim_is_group_filtered(ifp->info, &group_addr, NULL))
|
||||
return -1;
|
||||
|
||||
/* non-existent group is created as INCLUDE {empty} */
|
||||
|
|
|
@ -134,6 +134,9 @@ int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
|
|||
ifp->name, group_str);
|
||||
}
|
||||
|
||||
if (pim_is_group_filtered(pim_ifp, &group_addr, NULL))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* RFC 4604
|
||||
* section 2.2.1
|
||||
|
|
|
@ -507,6 +507,8 @@ static void allow(struct gm_sock *igmp, struct in_addr from,
|
|||
struct in_addr *src_addr;
|
||||
|
||||
src_addr = sources + i;
|
||||
if (pim_is_group_filtered(igmp->interface->info, &group_addr, src_addr))
|
||||
continue;
|
||||
|
||||
source = igmp_get_source_by_addr(group, *src_addr, NULL);
|
||||
if (!source)
|
||||
|
@ -646,7 +648,7 @@ void igmpv3_report_isex(struct gm_sock *igmp, struct in_addr from,
|
|||
|
||||
on_trace(__func__, ifp, from, group_addr, num_sources, sources);
|
||||
|
||||
if (pim_is_group_filtered(ifp->info, &group_addr))
|
||||
if (pim_is_group_filtered(ifp->info, &group_addr, NULL))
|
||||
return;
|
||||
|
||||
/* non-existent group is created as INCLUDE {empty} */
|
||||
|
@ -1809,12 +1811,13 @@ static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str,
|
|||
pim_ifp = ifp->info;
|
||||
|
||||
/* determine filtering status for group */
|
||||
if (pim_is_group_filtered(pim_ifp, &grp)) {
|
||||
if (pim_is_group_filtered(pim_ifp, &grp, NULL)) {
|
||||
if (PIM_DEBUG_GM_PACKETS) {
|
||||
zlog_debug(
|
||||
"Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
|
||||
&grp.s_addr, from_str, ifp->name,
|
||||
pim_ifp->boundary_oil_plist);
|
||||
zlog_debug("Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s or access-list %s",
|
||||
&grp.s_addr, from_str, ifp->name,
|
||||
(pim_ifp->boundary_oil_plist ? pim_ifp->boundary_oil_plist
|
||||
: "(not found)"),
|
||||
(pim_ifp->boundary_acl ? pim_ifp->boundary_acl->name : "(not found)"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1943,11 +1946,9 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
|
|||
sizeof(struct in_addr));
|
||||
|
||||
if (PIM_DEBUG_GM_PACKETS) {
|
||||
zlog_debug(
|
||||
" Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
|
||||
from_str, ifp->name, i, rec_type,
|
||||
rec_auxdatalen, rec_num_sources,
|
||||
&rec_group);
|
||||
zlog_debug(" Recv IGMP report v3 (type %d) from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
|
||||
rec_type, from_str, ifp->name, i, rec_type, rec_auxdatalen,
|
||||
rec_num_sources, &rec_group);
|
||||
}
|
||||
|
||||
/* Scan sources */
|
||||
|
|
|
@ -245,7 +245,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;
|
||||
bool group_filtered = false;
|
||||
|
||||
memset(&sg, 0, sizeof(sg));
|
||||
addr_offset = pim_parse_addr_group(&sg, buf, pastend - buf);
|
||||
|
@ -275,7 +275,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
|
|||
&src_addr, ifp->name);
|
||||
|
||||
/* boundary check */
|
||||
filtered = pim_is_group_filtered(pim_ifp, &sg.grp);
|
||||
group_filtered = pim_is_group_filtered(pim_ifp, &sg.grp, NULL);
|
||||
|
||||
/* Scan joined sources */
|
||||
for (source = 0; source < msg_num_joined_sources; ++source) {
|
||||
|
@ -287,8 +287,8 @@ 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)
|
||||
/* if we are filtering this group or (S,G), skip the join */
|
||||
if (group_filtered || pim_is_group_filtered(pim_ifp, &sg.grp, &sg.src))
|
||||
continue;
|
||||
|
||||
recv_join(ifp, neigh, msg_holdtime, msg_upstream_addr,
|
||||
|
@ -312,10 +312,6 @@ 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,
|
||||
&sg, msg_source_flags);
|
||||
/*
|
||||
|
@ -361,7 +357,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (starg_ch && !filtered)
|
||||
if (starg_ch && !group_filtered)
|
||||
pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0);
|
||||
starg_ch = NULL;
|
||||
} /* scan groups */
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "pim_sock.h"
|
||||
#include "pim_vxlan.h"
|
||||
#include "pim_msg.h"
|
||||
#include "pim_util.h"
|
||||
|
||||
static void mroute_read_on(struct pim_instance *pim);
|
||||
static int pim_upstream_mroute_update(struct channel_oil *c_oil,
|
||||
|
@ -271,7 +272,9 @@ int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
|
|||
*oil_incoming_vif(up->channel_oil) >= MAXVIFS) {
|
||||
pim_upstream_mroute_iif_update(up->channel_oil, __func__);
|
||||
}
|
||||
pim_register_join(up);
|
||||
|
||||
if (!pim_is_group_filtered(pim_ifp, &sg.grp, &sg.src))
|
||||
pim_register_join(up);
|
||||
/* if we have receiver, inherit from parent */
|
||||
pim_upstream_inherited_olist_decide(pim_ifp->pim, up);
|
||||
|
||||
|
@ -632,7 +635,8 @@ int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
|
|||
pim_upstream_keep_alive_timer_start(
|
||||
up, pim_ifp->pim->keep_alive_time);
|
||||
up->channel_oil->cc.pktcnt++;
|
||||
pim_register_join(up);
|
||||
if (!pim_is_group_filtered(pim_ifp, &sg.grp, &sg.src))
|
||||
pim_register_join(up);
|
||||
pim_upstream_inherited_olist(pim_ifp->pim, up);
|
||||
if (!up->channel_oil->installed)
|
||||
pim_upstream_mroute_add(up->channel_oil, __func__);
|
||||
|
|
|
@ -352,6 +352,13 @@ const struct frr_yang_module_info frr_pim_info = {
|
|||
.destroy = lib_interface_pim_address_family_multicast_boundary_oil_destroy,
|
||||
}
|
||||
},
|
||||
{
|
||||
.xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/multicast-boundary-acl",
|
||||
.cbs = {
|
||||
.modify = lib_interface_pim_address_family_multicast_boundary_acl_modify,
|
||||
.destroy = lib_interface_pim_address_family_multicast_boundary_acl_destroy,
|
||||
}
|
||||
},
|
||||
{
|
||||
.xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/mroute",
|
||||
.cbs = {
|
||||
|
|
|
@ -140,6 +140,8 @@ int lib_interface_pim_address_family_multicast_boundary_oil_modify(
|
|||
struct nb_cb_modify_args *args);
|
||||
int lib_interface_pim_address_family_multicast_boundary_oil_destroy(
|
||||
struct nb_cb_destroy_args *args);
|
||||
int lib_interface_pim_address_family_multicast_boundary_acl_modify(struct nb_cb_modify_args *args);
|
||||
int lib_interface_pim_address_family_multicast_boundary_acl_destroy(struct nb_cb_destroy_args *args);
|
||||
int lib_interface_pim_address_family_mroute_create(
|
||||
struct nb_cb_create_args *args);
|
||||
int lib_interface_pim_address_family_mroute_destroy(
|
||||
|
|
|
@ -2452,6 +2452,71 @@ int lib_interface_pim_address_family_multicast_boundary_oil_destroy(
|
|||
return NB_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/multicast-boundary-acl
|
||||
*/
|
||||
int lib_interface_pim_address_family_multicast_boundary_acl_modify(struct nb_cb_modify_args *args)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct pim_interface *pim_ifp;
|
||||
const struct lyd_node *if_dnode;
|
||||
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
|
||||
if (!is_pim_interface(if_dnode)) {
|
||||
snprintf(args->errmsg, args->errmsg_len,
|
||||
"%% Enable PIM and/or IGMP on this interface first");
|
||||
return NB_ERR_VALIDATION;
|
||||
}
|
||||
if (!access_list_lookup(AFI_IP, yang_dnode_get_string(args->dnode, NULL))) {
|
||||
snprintf(args->errmsg, args->errmsg_len,
|
||||
"%% Specified access-list not found");
|
||||
return NB_ERR_VALIDATION;
|
||||
}
|
||||
break;
|
||||
case NB_EV_ABORT:
|
||||
case NB_EV_PREPARE:
|
||||
break;
|
||||
case NB_EV_APPLY:
|
||||
ifp = nb_running_get_entry(args->dnode, NULL, true);
|
||||
pim_ifp = ifp->info;
|
||||
pim_ifp->boundary_acl =
|
||||
access_list_lookup(AFI_IP, yang_dnode_get_string(args->dnode, NULL));
|
||||
break;
|
||||
}
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
int lib_interface_pim_address_family_multicast_boundary_acl_destroy(struct nb_cb_destroy_args *args)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct pim_interface *pim_ifp;
|
||||
const struct lyd_node *if_dnode;
|
||||
|
||||
switch (args->event) {
|
||||
case NB_EV_VALIDATE:
|
||||
if_dnode = yang_dnode_get_parent(args->dnode, "interface");
|
||||
if (!is_pim_interface(if_dnode)) {
|
||||
snprintf(args->errmsg, args->errmsg_len,
|
||||
"%% Enable PIM and/or IGMP on this interface first");
|
||||
return NB_ERR_VALIDATION;
|
||||
}
|
||||
break;
|
||||
case NB_EV_ABORT:
|
||||
case NB_EV_PREPARE:
|
||||
break;
|
||||
case NB_EV_APPLY:
|
||||
ifp = nb_running_get_entry(args->dnode, NULL, true);
|
||||
pim_ifp = ifp->info;
|
||||
pim_ifp->boundary_acl = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return NB_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/mroute
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "prefix.h"
|
||||
#include "plist.h"
|
||||
|
||||
#include "pimd.h"
|
||||
#include "pim_instance.h"
|
||||
#include "pim_util.h"
|
||||
|
||||
/*
|
||||
|
@ -167,20 +169,47 @@ enum filter_type pim_access_list_apply(struct access_list *access, const struct
|
|||
return access_list_apply(access, &group_prefix);
|
||||
}
|
||||
|
||||
bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp)
|
||||
bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp, pim_addr *src)
|
||||
{
|
||||
struct prefix grp_pfx;
|
||||
struct prefix_list *pl;
|
||||
bool is_filtered = false;
|
||||
#if PIM_IPV == 4
|
||||
struct prefix grp_pfx = {};
|
||||
struct prefix_list *pl = NULL;
|
||||
pim_addr any_src = PIMADDR_ANY;
|
||||
|
||||
if (!pim_ifp->boundary_oil_plist)
|
||||
if (!pim_ifp->boundary_oil_plist && !pim_ifp->boundary_acl)
|
||||
return false;
|
||||
|
||||
pim_addr_to_prefix(&grp_pfx, *grp);
|
||||
|
||||
pl = prefix_list_lookup(PIM_AFI, pim_ifp->boundary_oil_plist);
|
||||
return pl ? prefix_list_apply_ext(pl, NULL, &grp_pfx, true) ==
|
||||
PREFIX_DENY
|
||||
: false;
|
||||
|
||||
/* Filter if either group or (S,G) are denied */
|
||||
if (pl) {
|
||||
is_filtered = prefix_list_apply_ext(pl, NULL, &grp_pfx, true) == PREFIX_DENY;
|
||||
if (is_filtered && PIM_DEBUG_EVENTS) {
|
||||
zlog_debug("Filtering group %pI4 per prefix-list %s", grp,
|
||||
pim_ifp->boundary_oil_plist);
|
||||
}
|
||||
}
|
||||
if (!is_filtered && pim_ifp->boundary_acl) {
|
||||
/* If src not provided, set to "any" (*)? */
|
||||
if (!src)
|
||||
src = &any_src;
|
||||
/* S,G filtering using extended access-list syntax */
|
||||
is_filtered = pim_access_list_apply(pim_ifp->boundary_acl, src, grp) == FILTER_DENY;
|
||||
if (is_filtered && PIM_DEBUG_EVENTS) {
|
||||
if (pim_addr_is_any(*src)) {
|
||||
zlog_debug("Filtering (S,G)=(*, %pI4) per access-list %s", grp,
|
||||
pim_ifp->boundary_acl->name);
|
||||
} else {
|
||||
zlog_debug("Filtering (S,G)=(%pI4, %pI4) per access-list %s", src,
|
||||
grp, pim_ifp->boundary_acl->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return is_filtered;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ 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);
|
||||
enum filter_type pim_access_list_apply(struct access_list *access, const struct in_addr *source,
|
||||
const struct in_addr *group);
|
||||
bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp);
|
||||
bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp, pim_addr *src);
|
||||
int pim_get_all_mcast_group(struct prefix *prefix);
|
||||
bool pim_addr_is_multicast(pim_addr addr);
|
||||
#endif /* PIM_UTIL_H */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "vty.h"
|
||||
#include "vrf.h"
|
||||
#include "plist.h"
|
||||
#include "filter.h"
|
||||
|
||||
#include "pimd.h"
|
||||
#include "pim_vty.h"
|
||||
|
@ -496,6 +497,12 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
|
|||
++writes;
|
||||
}
|
||||
|
||||
if (pim_ifp->boundary_acl) {
|
||||
vty_out(vty, " " PIM_AF_NAME " multicast boundary %s\n",
|
||||
pim_ifp->boundary_acl->name);
|
||||
++writes;
|
||||
}
|
||||
|
||||
if (pim_ifp->pim_passive_enable) {
|
||||
vty_out(vty, " " PIM_AF_NAME " pim passive\n");
|
||||
++writes;
|
||||
|
|
|
@ -78,6 +78,10 @@ module frr-pim {
|
|||
type string;
|
||||
}
|
||||
|
||||
typedef access-list-ref {
|
||||
type string;
|
||||
}
|
||||
|
||||
/*
|
||||
* Groupings
|
||||
*/
|
||||
|
@ -507,7 +511,13 @@ module frr-pim {
|
|||
leaf multicast-boundary-oil {
|
||||
type plist-ref;
|
||||
description
|
||||
"Prefix-List to define multicast boundary";
|
||||
"Prefix-List to define multicast boundary by group";
|
||||
}
|
||||
|
||||
leaf multicast-boundary-acl {
|
||||
type access-list-ref;
|
||||
description
|
||||
"Access-list to define multicast boundary by source and group";
|
||||
}
|
||||
|
||||
list mroute {
|
||||
|
|
Loading…
Reference in a new issue