From df9517c95a09e2860cd9c049ee8fe2a5b1a7a48f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 28 Sep 2021 14:40:23 +0200 Subject: [PATCH] pimd: add IGMPv2/MLDv1 immediate-leave (Somewhat) useful when dealing with an interface that has only one host attached. Only works for IGMPv2 and MLDv1, other protocol versions have no leave message. Co-authored-by: David Lamparter Signed-off-by: Rafael Zalamena --- pimd/pim6_cmd.c | 14 ++++++++++++++ pimd/pim6_mld.c | 35 +++++++++++++++++++++++++++-------- pimd/pim_cmd.c | 14 ++++++++++++++ pimd/pim_iface.h | 3 +++ pimd/pim_igmpv3.c | 24 ++++++++++++++++++++++-- pimd/pim_nb.c | 8 +++++++- pimd/pim_nb.h | 1 + pimd/pim_nb_config.c | 23 +++++++++++++++++++++++ pimd/pim_vty.c | 6 ++++++ yang/frr-gmp.yang | 8 ++++++++ 10 files changed, 125 insertions(+), 11 deletions(-) diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 40bd7caf7d..50357ca1d0 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -1649,6 +1649,19 @@ ALIAS_YANG(interface_ipv6_mld_limits, "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", @@ -2944,6 +2957,7 @@ void pim_cmd_init(void) 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); diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index d7e0314d3b..37c5309ba3 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -448,18 +448,23 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) 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); } @@ -1102,10 +1107,24 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp, 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" */ } } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index fa9c6f9537..4ad75a21e7 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -5693,6 +5693,19 @@ ALIAS_YANG(interface_ip_igmp_limits, "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)", @@ -9140,6 +9153,7 @@ void pim_cmd_init(void) 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); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 0a7993fd27..606fda6721 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -107,6 +107,9 @@ struct pim_interface { 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 */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 7cb168dc5d..2de2b32688 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -728,9 +728,25 @@ static void toin_incl(struct gm_group *group, int num_sources, 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); @@ -1496,7 +1512,9 @@ void igmp_group_timer_lower_to_lmqt(struct gm_group *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 */ @@ -1531,7 +1549,9 @@ void igmp_source_timer_lower_to_lmqt(struct gm_source *source) 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 */ diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 62c5d531d9..3cd5c0f4e5 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -743,7 +743,13 @@ const struct frr_yang_module_info frr_gmp_info = { .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, diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 1656313fc2..ce7b5e43af 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -289,6 +289,7 @@ int lib_interface_gmp_address_family_static_group_destroy( 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 diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index c926696610..d671ea3b65 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -4492,6 +4492,29 @@ int lib_interface_gmp_address_family_robustness_variable_modify( 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 */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 64750a22f6..b2525998a6 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -471,6 +471,12 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp, ++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", diff --git a/yang/frr-gmp.yang b/yang/frr-gmp.yang index 26b19501f9..3843c88cff 100644 --- a/yang/frr-gmp.yang +++ b/yang/frr-gmp.yang @@ -186,6 +186,14 @@ module frr-gmp { } } + 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 -- 2.39.5