"Limit number of MLDv2 sources to track\n"
"Limit number of MLD group memberships to track\n")
+DEFPY_YANG(interface_ipv6_mld_immediate_leave,
+ interface_ipv6_mld_immediate_leave_cmd,
+ "[no] ipv6 mld immediate-leave",
+ NO_STR
+ IPV6_STR
+ IFACE_MLD_STR
+ "Immediately drop group memberships on receiving Leave (MLDv1 only)\n")
+{
+ nb_cli_enqueue_change(vty, "./immediate-leave", NB_OP_MODIFY, no ? "false" : "true");
+
+ 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",
install_element(INTERFACE_NODE, &interface_ipv6_mld_static_group_cmd);
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_query_interval_cmd);
install_element(INTERFACE_NODE,
&interface_no_ipv6_mld_query_interval_cmd);
desired == GM_SG_NOPRUNE_EXPIRING) {
struct gm_query_timers timers;
- timers.qrv = gm_ifp->cur_qrv;
- timers.max_resp_ms = gm_ifp->cur_max_resp;
- timers.qqic_ms = gm_ifp->cur_query_intv_trig;
- timers.fuzz = gm_ifp->cfg_timing_fuzz;
+ if (!pim_ifp->gmp_immediate_leave) {
+ timers.qrv = gm_ifp->cur_qrv;
+ timers.max_resp_ms = gm_ifp->cur_max_resp;
+ timers.qqic_ms = gm_ifp->cur_query_intv_trig;
+ timers.fuzz = gm_ifp->cfg_timing_fuzz;
+
+ gm_expiry_calc(&timers);
+ } else
+ memset(&timers.expire_wait, 0, sizeof(timers.expire_wait));
- gm_expiry_calc(&timers);
gm_sg_timer_start(gm_ifp, sg, timers.expire_wait);
EVENT_OFF(sg->t_sg_query);
sg->query_sbit = false;
/* Trigger the specific queries only for querier. */
- if (IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest)) {
+ if (!pim_ifp->gmp_immediate_leave &&
+ IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest)) {
sg->n_query = gm_ifp->cur_lmqc;
gm_trigger_specific(sg);
}
if (grp) {
old_grp = gm_packet_sg_find(grp, GM_SUB_POS, subscriber);
if (old_grp) {
+ const struct pim_interface *pim_ifp = gm_ifp->ifp->info;
+ struct gm_packet_sg *item;
+
gm_packet_sg_drop(old_grp);
- gm_sg_update(grp, false);
-/* TODO "need S,G PRUNE => NO_INFO transition here" */
+ /*
+ * If immediate leave drop others subscribers and proceed
+ * to expire the MLD join.
+ */
+ if (pim_ifp->gmp_immediate_leave) {
+ frr_each_safe (gm_packet_sg_subs, grp->subs_positive, item) {
+ gm_packet_sg_drop(item);
+ }
+ gm_sg_update(grp, true);
+ } else
+ gm_sg_update(grp, false);
+
+ /* TODO "need S,G PRUNE => NO_INFO transition here" */
}
}
"Limit number of IGMPv3 sources to track\n"
"Limit number of IGMP group memberships to track\n")
+DEFPY_YANG(interface_ip_igmp_immediate_leave,
+ interface_ip_igmp_immediate_leave_cmd,
+ "[no] ip igmp immediate-leave",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ "Immediately drop group memberships on receiving Leave (IGMPv2 only)\n")
+{
+ nb_cli_enqueue_change(vty, "./immediate-leave", NB_OP_MODIFY, no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
+}
+
DEFUN (interface_ip_pim_drprio,
interface_ip_pim_drprio_cmd,
"ip pim drpriority (0-4294967295)",
install_element(INTERFACE_NODE, &interface_ip_igmp_proxy_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_limits_cmd);
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_pim_activeactive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
uint32_t gm_source_limit, gm_group_limit;
+ /* IGMPv2 only/MLDv1 only immediate leave */
+ bool gmp_immediate_leave;
+
int pim_sock_fd; /* PIM socket file descriptor */
struct event *t_pim_sock_read; /* thread for reading PIM socket */
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
static void toin_excl(struct gm_group *group, int num_sources,
struct in_addr *sources)
{
+ struct listnode *src_node, *src_next;
+ struct pim_interface *pim_ifp = group->interface->info;
int num_sources_tosend;
int i;
+ if (group->igmp_version == 2 && pim_ifp->gmp_immediate_leave) {
+ struct gm_source *src;
+
+ if (PIM_DEBUG_GM_TRACE)
+ zlog_debug("IGMP(v2) Immediate-leave group %pI4 on %s", &group->group_addr,
+ group->interface->name);
+
+ igmp_group_timer_on(group, 0, group->interface->name);
+
+ for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_next, src))
+ igmp_source_delete(src);
+ return;
+ }
+
/* Set SEND flag for X (sources with timer > 0) */
num_sources_tosend = source_mark_send_flag_by_timer(group);
pim_ifp = ifp->info;
ifname = ifp->name;
- lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
+ lmqi_dsec = pim_ifp->gmp_immediate_leave
+ ? 0
+ : pim_ifp->gm_specific_query_max_response_time_dsec;
lmqc = pim_ifp->gm_last_member_query_count;
lmqt_msec = PIM_IGMP_LMQT_MSEC(
lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
pim_ifp = ifp->info;
ifname = ifp->name;
- lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
+ lmqi_dsec = pim_ifp->gmp_immediate_leave
+ ? 0
+ : pim_ifp->gm_specific_query_max_response_time_dsec;
lmqc = pim_ifp->gm_last_member_query_count;
lmqt_msec = PIM_IGMP_LMQT_MSEC(
lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
.modify = lib_interface_gmp_address_family_proxy_modify,
}
},
-{
+ {
+ .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/immediate-leave",
+ .cbs = {
+ .modify = lib_interface_gmp_immediate_leave_modify,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/static-group",
.cbs = {
.create = lib_interface_gmp_address_family_static_group_create,
struct nb_cb_destroy_args *args);
int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args);
int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args);
+int lib_interface_gmp_immediate_leave_modify(struct nb_cb_modify_args *args);
/*
* Callback registered with routing_nb lib to validate only
return NB_OK;
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/immediate-leave
+ */
+int lib_interface_gmp_immediate_leave_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ pim_ifp->gmp_immediate_leave = yang_dnode_get_bool(args->dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy
*/
++writes;
}
+ /* IF ip/ipv6 igmp/mld immediate-leave */
+ if (pim_ifp->gmp_immediate_leave) {
+ vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " immediate-leave\n");
+ ++writes;
+ }
+
/* IF ip pim drpriority */
if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) {
vty_out(vty, " " PIM_AF_NAME " pim drpriority %u\n",
}
}
+ leaf immediate-leave {
+ type boolean;
+ default "false";
+ description
+ "Immediately drop group memberships on receiving IGMPv2/MLDv1 Leave.
+ Has no effect when IGMPv3/MLDv2 is in use.";
+ }
+
list static-group {
key "group-addr source-addr";
description