Skip to content

Commit

Permalink
Merge pull request #17461 from csiltala/multicast-boundary-acl
Browse files Browse the repository at this point in the history
pimd: Extend multicast boundary/ACL functionality
  • Loading branch information
donaldsharp authored Dec 9, 2024
2 parents 17a0d92 + 8465ba1 commit c05c2b1
Show file tree
Hide file tree
Showing 23 changed files with 925 additions and 104 deletions.
27 changes: 24 additions & 3 deletions doc/user/filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ defined, it can be applied in any direction.
IP Access List
==============

.. clicmd:: access-list NAME [seq (1-4294967295)] permit IPV4-NETWORK

.. clicmd:: access-list NAME [seq (1-4294967295)] deny IPV4-NETWORK
.. clicmd:: access-list NAME [seq (1-4294967295)] <permit|deny> <A.B.C.D/M [exact-match]|any>

seq
seq `number` can be set either automatically or manually. In the
Expand All @@ -35,6 +33,29 @@ IP Access List
access-list filter permit 10.0.0.0/8
access-list filter seq 13 permit 10.0.0.0/7
.. clicmd:: access-list NAME [seq (1-4294967295)] <deny|permit> ip <A.B.C.D A.B.C.D|host A.B.C.D|any> <A.B.C.D A.B.C.D|host A.B.C.D|any>

The extended access-list syntax enables filtering on both source and destination
IP addresses (or source and group, if used for multicast boundaries). The
source address is first in order in the command.

If providing a mask, note that the access-lists use wildcard masks (inverse
matching logic of subnet masks). If specifying ``host``, only the single address
given will be matched.

A basic example is as follows:

.. code-block:: frr
access-list filter seq 5 permit ip host 10.0.20.2 232.1.1.0 0.0.0.128
access-list filter seq 10 deny ip 10.0.20.0 0.0.0.255 232.1.1.0 0.0.0.255
access-list filter seq 15 permit ip any any
.. note ::
If an access-list is specified but no match is found, the default verdict
is deny.
.. clicmd:: show <ip|ipv6> access-list [json]

Display all IPv4 or IPv6 access lists.
Expand Down
46 changes: 41 additions & 5 deletions doc/user/pim.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ PIM

PIM -- Protocol Independent Multicast

*pimd* supports pim-sm as well as igmp v2 and v3. pim is
vrf aware and can work within the context of vrf's in order to
do S,G mrouting. Additionally PIM can be used in the EVPN underlay
*pimd* supports PIM-SM as well as IGMP v2 and v3. PIM is
VRF aware and can work within the context of VRFs in order to
do S,G mrouting. Additionally, PIM can be used in the EVPN underlay
network for optimizing forwarding of overlay BUM traffic.

.. note::
Expand Down Expand Up @@ -348,10 +348,46 @@ is in a vrf, enter the interface command with the vrf keyword at the end.

.. clicmd:: ip multicast boundary oil WORD

Set a pim multicast boundary, based upon the WORD prefix-list. If a pim join
or IGMP report is received on this interface and the Group is denied by the
Set a PIM multicast boundary, based upon the WORD prefix-list. If a PIM join
or IGMP report is received on this interface and the group is denied by the
prefix-list, PIM will ignore the join or report.

.. code-block:: frr
prefix-list multicast-acl seq 5 permit 232.1.1.1/32
prefix-list multicast-acl seq 10 deny 232.1.1.0/24
prefix-list multicast-acl seq 15 permit any
!
interface r1-eth0
ip pim
ip igmp
ip multicast boundary oil multicast-acl
exit
.. clicmd:: ip multicast boundary ACCESS-LIST

Set a PIM multicast boundary, based upon the ACCESS-LIST. If a PIM join
or IGMP report is received on this interface and the (S,G) tuple is denied by the
access-list, PIM will ignore the join or report.

To filter on both source and group, the extended access-list syntax must be used.

If both a prefix-list and access-list are configured for multicast boundaries,
the prefix-list will be evaluated first (and must have a terminating "permit any"
in order to also evaluate against the access-list).

.. code-block:: frr
access-list multicast-acl seq 5 permit ip host 10.0.20.2 host 232.1.1.1
access-list multicast-acl seq 10 deny ip 10.0.20.0 0.0.0.255 232.1.1.0 0.0.0.255
access-list multicast-acl seq 15 permit ip any any
!
interface r1-eth0
ip pim
ip igmp
ip multicast boundary pim-acl
exit
.. clicmd:: ip igmp last-member-query-count (1-255)

Set the IGMP last member query count. The default value is 2. 'no' form of
Expand Down
16 changes: 16 additions & 0 deletions pimd/pim_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]",
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion pimd/pim_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -215,7 +216,6 @@ void pim_if_delete(struct interface *ifp)
if (pim_ifp->bfd_config.profile)
XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile);

XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);

ifp->info = NULL;
Expand Down Expand Up @@ -1258,6 +1258,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);
Expand Down
6 changes: 4 additions & 2 deletions pimd/pim_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
char *boundary_oil_plist;
/* boundary prefix-list (group) */
struct prefix_list *boundary_oil_plist;
/* boundary access-list (source and group) */
struct access_list *boundary_acl;

/* Turn on Active-Active for this interface */
bool activeactive;
Expand Down
2 changes: 1 addition & 1 deletion pimd/pim_igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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} */
Expand Down
3 changes: 3 additions & 0 deletions pimd/pim_igmpv2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 15 additions & 11 deletions pimd/pim_igmpv3.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "memory.h"
#include "if.h"
#include "lib_errors.h"
#include "plist.h"
#include "plist_int.h"

#include "pimd.h"
#include "pim_instance.h"
Expand Down Expand Up @@ -507,6 +509,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)
Expand Down Expand Up @@ -646,7 +650,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} */
Expand Down Expand Up @@ -1809,12 +1813,14 @@ 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->name
: "(not found)"),
(pim_ifp->boundary_acl ? pim_ifp->boundary_acl->name
: "(not found)"));
}
return false;
}
Expand Down Expand Up @@ -1943,11 +1949,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 */
Expand Down
14 changes: 5 additions & 9 deletions pimd/pim_join.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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);
/*
Expand Down Expand Up @@ -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 */
Expand Down
8 changes: 6 additions & 2 deletions pimd/pim_mroute.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand 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__);
Expand Down
Loading

0 comments on commit c05c2b1

Please sign in to comment.