Skip to content

pimd, pim6d: route-map filtering for source/group #18955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/user/pim.rst
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,21 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
be forwarded on the given interface if the traffic matches the group address
and optionally the source address.

.. clicmd:: ip igmp route-map ROUTE-MAP

Apply the indicated route map to filter incoming IGMP joins.

The following match statements can be used:

* match ip multicast-group A.B.C.D

* match ip multicast-group prefix-list IPV4-PREFIX-LIST

* match ip multicast-source A.B.C.D

* match ip multicast-source prefix-list IPV4-PREFIX-LIST

* match multicast-interface INTERFACE-NAME

.. seealso::

Expand Down
16 changes: 16 additions & 0 deletions doc/user/pimv6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,22 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
10 deciseconds. 'no' form of this command is used to to configure back to the
default value.

.. clicmd:: ipv6 mld route-map ROUTE-MAP

Apply the indicated route map to filter incoming IGMP joins.

The following match statements can be used:

* match ipv6 multicast-group X:X::X:X

* match ipv6 multicast-group prefix-list IPV6-PREFIX-LIST

* match ipv6 multicast-source X:X::X:X

* match ipv6 multicast-source prefix-list IPV6-PREFIX-LIST

* match multicast-interface INTERFACE-NAME

.. clicmd:: ipv6 mroute INTERFACE X:X::X:X [Y:Y::Y:Y]

Set a static multicast route for a traffic coming on the current interface to
Expand Down
12 changes: 6 additions & 6 deletions lib/prefix.c
Original file line number Diff line number Diff line change
Expand Up @@ -1175,11 +1175,11 @@ void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,

const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str)
{
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
char src_str[INET6_ADDRSTRLEN];
char grp_str[INET6_ADDRSTRLEN];

prefix_mcast_ip_dump("<src?>", &sg->src, src_str, sizeof(src_str));
prefix_mcast_inet4_dump("<grp?>", sg->grp, grp_str, sizeof(grp_str));
prefix_mcast_ip_dump("<grp?>", &sg->grp, grp_str, sizeof(grp_str));
snprintf(sg_str, PREFIX_SG_STR_LEN, "(%s,%s)", src_str, grp_str);

return sg_str;
Expand Down Expand Up @@ -1642,7 +1642,7 @@ static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
}
}

printfrr_ext_autoreg_p("PSG4", printfrr_psg);
printfrr_ext_autoreg_p("PSG", printfrr_psg);
static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
Expand All @@ -1657,10 +1657,10 @@ static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea,
else
ret += bprintfrr(buf, "(%pIA,", &sg->src);

if (sg->grp.s_addr == INADDR_ANY)
if (ipaddr_is_zero(&sg->grp))
ret += bputs(buf, "*)");
else
ret += bprintfrr(buf, "%pI4)", &sg->grp);
ret += bprintfrr(buf, "%pIA)", &sg->grp);

return ret;
}
14 changes: 7 additions & 7 deletions lib/prefix.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ struct prefix_fs {
struct prefix_sg {
uint8_t family;
uint16_t prefixlen;
struct ipaddr src __attribute__((aligned(8)));
struct in_addr grp;
struct ipaddr src;
struct ipaddr grp;
};

/* clang-format off */
Expand Down Expand Up @@ -323,13 +323,13 @@ union prefixconstptr {
#define PREFIX_STRLEN 80

/*
* Longest possible length of a (S,G) string is 34 bytes
* 123.123.123.123 = 15 * 2
* Longest possible length of a (S,G) string is 82 bytes
* 1111:2222:3333:4444:5555:6666:7777:8888 = INET6_ADDRSTRLEN * 2 = 92
* (,) = 3
* NULL Character at end = 1
* (123.123.123.123,123.123.123.123)
* (1111:2222:3333:4444:5555:6666:7777:8888,1111:2222:3333:4444:5555:6666:7777:8888)
*/
#define PREFIX_SG_STR_LEN 34
#define PREFIX_SG_STR_LEN (INET6_ADDRSTRLEN * 2 + 3 + 1)

/* Max bit/byte length of IPv4 address. */
#define IPV4_MAX_BYTELEN 4
Expand Down Expand Up @@ -662,7 +662,7 @@ static inline bool ipv4_mcast_ssm(const struct in_addr *addr)
#pragma FRR printfrr_ext "%pRDD" (struct prefix_rd *)
#pragma FRR printfrr_ext "%pRDE" (struct prefix_rd *)

#pragma FRR printfrr_ext "%pPSG4" (struct prefix_sg *)
#pragma FRR printfrr_ext "%pPSG" (struct prefix_sg *)
#endif

#ifdef __cplusplus
Expand Down
18 changes: 18 additions & 0 deletions lib/routemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,24 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-bgp-route-map:ipv4-nexthop"))
#define IS_MATCH_IPV6_NH(C) \
(strmatch(C, "frr-bgp-route-map:ipv6-nexthop"))
#define IS_MATCH_IPV4_MULTICAST_SOURCE(C) \
(strmatch(C, "frr-pim-route-map:ipv4-multicast-source"))
#define IS_MATCH_IPV6_MULTICAST_SOURCE(C) \
(strmatch(C, "frr-pim-route-map:ipv6-multicast-source"))
#define IS_MATCH_IPV4_MULTICAST_SOURCE_PREFIX_LIST(C) \
(strmatch(C, "frr-pim-route-map:ipv4-multicast-source-prefix-list"))
#define IS_MATCH_IPV6_MULTICAST_SOURCE_PREFIX_LIST(C) \
(strmatch(C, "frr-pim-route-map:ipv6-multicast-source-prefix-list"))
#define IS_MATCH_IPV4_MULTICAST_GROUP(C) \
(strmatch(C, "frr-pim-route-map:ipv4-multicast-group"))
#define IS_MATCH_IPV6_MULTICAST_GROUP(C) \
(strmatch(C, "frr-pim-route-map:ipv6-multicast-group"))
#define IS_MATCH_IPV4_MULTICAST_GROUP_PREFIX_LIST(C) \
(strmatch(C, "frr-pim-route-map:ipv4-multicast-group-prefix-list"))
#define IS_MATCH_IPV6_MULTICAST_GROUP_PREFIX_LIST(C) \
(strmatch(C, "frr-pim-route-map:ipv6-multicast-group-prefix-list"))
#define IS_MATCH_MULTICAST_INTERFACE(C) \
(strmatch(C, "frr-pim-route-map:multicast-interface"))

/* Route-map set actions */
#define IS_SET_IPv4_NH(A) \
Expand Down
41 changes: 41 additions & 0 deletions lib/routemap_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,47 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-bgp-route-map:ipv6-address"));
} else if (IS_MATCH_IPV4_MULTICAST_SOURCE(condition)) {
vty_out(vty, " match ip multicast-source %s\n",
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-pim-route-map:ipv4-multicast-source-address"));
} else if (IS_MATCH_IPV6_MULTICAST_SOURCE(condition)) {
vty_out(vty, " match ipv6 multicast-source %s\n",
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-pim-route-map:ipv6-multicast-source-address"));
} else if (IS_MATCH_IPV4_MULTICAST_SOURCE_PREFIX_LIST(condition)) {
vty_out(vty, " match ip multicast-source prefix-list %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-pim-route-map:list-name"));
} else if (IS_MATCH_IPV6_MULTICAST_SOURCE_PREFIX_LIST(condition)) {
vty_out(vty, " match ipv6 multicast-source prefix-list %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-pim-route-map:list-name"));
} else if (IS_MATCH_IPV4_MULTICAST_GROUP(condition)) {
vty_out(vty, " match ip multicast-group %s\n",
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-pim-route-map:ipv4-multicast-group-address"));
} else if (IS_MATCH_IPV6_MULTICAST_GROUP(condition)) {
vty_out(vty, " match ipv6 multicast-group %s\n",
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-pim-route-map:ipv6-multicast-group-address"));
} else if (IS_MATCH_IPV4_MULTICAST_GROUP_PREFIX_LIST(condition)) {
vty_out(vty, " match ip multicast-group prefix-list %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-pim-route-map:list-name"));
} else if (IS_MATCH_IPV6_MULTICAST_GROUP_PREFIX_LIST(condition)) {
vty_out(vty, " match ipv6 multicast-group prefix-list %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-pim-route-map:list-name"));
} else if (IS_MATCH_MULTICAST_INTERFACE(condition)) {
vty_out(vty, " match ipv6 multicast-interface %s\n",
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-pim-route-map:multicast-interface"));
}
}

Expand Down
17 changes: 17 additions & 0 deletions pimd/pim6_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,22 @@ DEFPY_YANG(interface_ipv6_mld_immediate_leave,
return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
}

DEFPY_YANG(interface_ipv6_mld_rmap, interface_ipv6_mld_rmap_cmd,
"[no] ipv6 mld route-map ![RMAP_NAME]",
NO_STR
IPV6_STR
IFACE_MLD_STR
"Filter joins through route-map\n"
"Route-map name\n")
{
if (no)
nb_cli_enqueue_change(vty, "./route-map", NB_OP_DESTROY, NULL);
else
nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap_name);

return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
}

DEFPY (interface_ipv6_mld_query_interval,
interface_ipv6_mld_query_interval_cmd,
"ipv6 mld query-interval (1-65535)$q_interval",
Expand Down Expand Up @@ -3044,6 +3060,7 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE, &interface_ipv6_mld_version_cmd);
install_element(INTERFACE_NODE, &interface_no_ipv6_mld_version_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_immediate_leave_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_rmap_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mld_query_interval_cmd);
install_element(INTERFACE_NODE,
&interface_no_ipv6_mld_query_interval_cmd);
Expand Down
4 changes: 1 addition & 3 deletions pimd/pim6_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static const struct frr_yang_module_info *const pim6d_yang_modules[] = {
&frr_routing_info,
&frr_pim_info,
&frr_pim_rp_info,
&frr_pim_route_map_info,
&frr_pim_candidate_info,
&frr_gmp_info,
};
Expand Down Expand Up @@ -157,12 +158,9 @@ int main(int argc, char **argv, char **envp)
*/
pim_error_init();
pim_vrf_init();
#if 0
prefix_list_add_hook(pim_prefix_list_update);
prefix_list_delete_hook(pim_prefix_list_update);

pim_route_map_init();
#endif
pim_init();
/*
* Initialize zclient "update" and "lookup" sockets
Expand Down
40 changes: 37 additions & 3 deletions pimd/pim6_mld.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,28 @@ static bool gm_sg_limit_reached(struct gm_if *gm_if, const pim_addr source, cons
return false;
}

static bool gm_sg_filter_match(const struct gm_if *gm_if, const pim_addr source,
const pim_addr group)
{
const struct pim_interface *pim_interface = gm_if->ifp->info;
const struct prefix_sg sg = {
.family = PIM_AF,
.src.ipa_type = IPADDR_V6,
.src.ipaddr_v6 = source,
.grp.ipa_type = IPADDR_V6,
.grp.ipaddr_v6 = group,
};

if (!pim_filter_match(&pim_interface->gmp_filter, &sg, gm_if->ifp)) {
if (PIM_DEBUG_GM_TRACE)
zlog_debug("%s: SG%pPSG on interface %s filtered due to route-map",
__func__, &sg, gm_if->ifp->name);
return true;
}

return false;
}

/*
* interface -> packets, sorted by expiry (because add_tail insert order)
*/
Expand Down Expand Up @@ -410,6 +432,7 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired)
struct pim_interface *pim_ifp = gm_ifp->ifp->info;
enum gm_sg_state prev, desired;
bool new_join;
bool entry_filtered = false;
struct gm_sg *grp = NULL;

if (!pim_addr_is_any(sg->sgaddr.src))
Expand Down Expand Up @@ -439,13 +462,17 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired)
else
desired = GM_SG_NOINFO;

/* Check if entry is filtered by route maps */
if (gm_sg_filter_match(gm_ifp, sg->sgaddr.src, sg->sgaddr.grp))
entry_filtered = true;

if (desired != sg->state && !gm_ifp->stopping) {
if (PIM_DEBUG_GM_EVENTS)
zlog_debug(log_sg(sg, "%s => %s"), gm_states[sg->state],
gm_states[desired]);

if (desired == GM_SG_JOIN_EXPIRING ||
desired == GM_SG_NOPRUNE_EXPIRING) {
if (!entry_filtered &&
(desired == GM_SG_JOIN_EXPIRING || desired == GM_SG_NOPRUNE_EXPIRING)) {
struct gm_query_timers timers;

if (!pim_ifp->gmp_immediate_leave) {
Expand Down Expand Up @@ -478,7 +505,7 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired)
else
new_join = gm_sg_state_want_join(desired);

if (new_join && !sg->tib_joined) {
if (new_join && !sg->tib_joined && !entry_filtered) {
pim_addr embedded_rp;

if (sg->iface->pim->embedded_rp.enable &&
Expand Down Expand Up @@ -701,6 +728,8 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
/* this always replaces or creates state */
is_excl = true;
if (!grp) {
if (gm_sg_filter_match(pkt->iface, PIMADDR_ANY, rechdr->grp))
return;
if (gm_sg_limit_reached(pkt->iface, PIMADDR_ANY, rechdr->grp))
return;

Expand Down Expand Up @@ -771,6 +800,8 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,

sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]);
if (!sg) {
if (gm_sg_filter_match(pkt->iface, rechdr->srcs[j], rechdr->grp))
return;
if (gm_sg_limit_reached(pkt->iface, rechdr->srcs[j], rechdr->grp))
return;

Expand Down Expand Up @@ -1026,6 +1057,9 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp,

hdr = (struct mld_v1_pkt *)data;

if (gm_sg_filter_match(gm_ifp, PIMADDR_ANY, hdr->grp))
return;

if (!gm_sg_has_group(gm_ifp->sgs, hdr->grp) &&
gm_sg_limit_reached(gm_ifp, PIMADDR_ANY, hdr->grp))
return;
Expand Down
16 changes: 16 additions & 0 deletions pimd/pim_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -5726,6 +5726,21 @@ DEFPY_YANG(interface_ip_igmp_require_ra, interface_ip_igmp_require_ra_cmd,
"Require IP Router Alert option for IGMP packets\n")
{
nb_cli_enqueue_change(vty, "./require-router-alert", NB_OP_MODIFY, no ? "false" : "true");
return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
}

DEFPY_YANG(interface_ip_igmp_rmap, interface_ip_igmp_rmap_cmd,
"[no] ip igmp route-map ![RMAP_NAME]",
NO_STR
IP_STR
IFACE_IGMP_STR
"Filter joins through route-map\n"
"Route-map name\n")
{
if (no)
nb_cli_enqueue_change(vty, "./route-map", NB_OP_DESTROY, NULL);
else
nb_cli_enqueue_change(vty, "./route-map", NB_OP_MODIFY, rmap_name);

return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
}
Expand Down Expand Up @@ -9339,6 +9354,7 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE, &no_interface_ip_igmp_limits_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_immediate_leave_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_require_ra_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_rmap_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_passive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_cmd);
Expand Down
4 changes: 4 additions & 0 deletions pimd/pim_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim,
pim_if_add_vif(ifp, ispimreg, is_vxlan_term);
pim_ifp->pim->mcast_if_count++;

pim_filter_ref_init(&pim_ifp->gmp_filter);

return pim_ifp;
}

Expand Down Expand Up @@ -221,6 +223,8 @@ void pim_if_delete(struct interface *ifp)

pim_igmp_if_fini(pim_ifp);

pim_filter_ref_fini(&pim_ifp->gmp_filter);

list_delete(&pim_ifp->pim_neighbor_list);
list_delete(&pim_ifp->upstream_switch_list);
list_delete(&pim_ifp->sec_addr_list);
Expand Down
Loading
Loading