diff options
Diffstat (limited to 'pimd')
| -rw-r--r-- | pimd/pim6_cmd.c | 111 | ||||
| -rw-r--r-- | pimd/pim6_mld.c | 114 | ||||
| -rw-r--r-- | pimd/pim6_mld.h | 2 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 83 | ||||
| -rw-r--r-- | pimd/pim_iface.c | 3 | ||||
| -rw-r--r-- | pimd/pim_iface.h | 6 | ||||
| -rw-r--r-- | pimd/pim_ifchannel.c | 12 | ||||
| -rw-r--r-- | pimd/pim_igmp.c | 8 | ||||
| -rw-r--r-- | pimd/pim_igmpv3.c | 33 | ||||
| -rw-r--r-- | pimd/pim_memory.c | 1 | ||||
| -rw-r--r-- | pimd/pim_memory.h | 1 | ||||
| -rw-r--r-- | pimd/pim_msdp.c | 2 | ||||
| -rw-r--r-- | pimd/pim_nb.c | 27 | ||||
| -rw-r--r-- | pimd/pim_nb.h | 5 | ||||
| -rw-r--r-- | pimd/pim_nb_config.c | 159 | ||||
| -rw-r--r-- | pimd/pim_pim.c | 38 | ||||
| -rw-r--r-- | pimd/pim_register.c | 19 | ||||
| -rw-r--r-- | pimd/pim_upstream.c | 2 | ||||
| -rw-r--r-- | pimd/pim_vty.c | 26 |
19 files changed, 622 insertions, 30 deletions
diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 12493b7dbb..8297911828 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -1612,6 +1612,56 @@ DEFPY (interface_no_ipv6_mld_version, "frr-routing:ipv6"); } +DEFPY_YANG(interface_ipv6_mld_limits, + interface_ipv6_mld_limits_cmd, + "[no] ipv6 mld <max-sources$do_src (0-4294967295)$val" + "|max-groups$do_grp (0-4294967295)$val>", + NO_STR + IPV6_STR + IFACE_MLD_STR + "Limit number of MLDv2 sources to track\n" + "Permitted number of sources\n" + "Limit number of MLD group memberships to track\n" + "Permitted number of groups\n") +{ + const char *xpath; + + assert(do_src || do_grp); + if (do_src) + xpath = "./max-sources"; + else + xpath = "./max-groups"; + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, val_str); + + return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS_YANG(interface_ipv6_mld_limits, + no_interface_ipv6_mld_limits_cmd, + "no ipv6 mld <max-sources$do_src|max-groups$do_grp>", + NO_STR + IPV6_STR + IFACE_MLD_STR + "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", @@ -1753,6 +1803,34 @@ DEFPY (interface_no_ipv6_mld_last_member_query_interval, return gm_process_no_last_member_query_interval_cmd(vty); } +DEFPY_YANG(interface_ipv6_pim_neighbor_prefix_list, + interface_ipv6_pim_neighbor_prefix_list_cmd, + "[no] ipv6 pim allowed-neighbors prefix-list PREFIXLIST6_NAME$prefix_list", + NO_STR + IP_STR + PIM_STR + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n" + "Name of a prefix-list\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_MODIFY, + prefix_list); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS(interface_ipv6_pim_neighbor_prefix_list, + interface_no_ipv6_pim_neighbor_prefix_list_cmd, + "no ipv6 pim allowed-neighbors [prefix-list]", + NO_STR + IP_STR + PIM_STR + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n") + DEFPY (show_ipv6_pim_rp, show_ipv6_pim_rp_cmd, "show ipv6 pim [vrf NAME] rp-info [X:X::X:X/M$group] [json$json]", @@ -2341,6 +2419,32 @@ DEFPY (show_ipv6_pim_bsrp, return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json); } +DEFPY(clear_ipv6_mld_interfaces, + clear_ipv6_mld_interfaces_cmd, + "clear ipv6 mld [vrf NAME$vrf_name] interfaces", + CLEAR_STR + IPV6_STR + "MLD clear commands\n" + VRF_CMD_HELP_STR + "Reset MLD interfaces\n") +{ + struct interface *ifp; + struct vrf *vrf; + + vrf = vrf_name ? vrf_lookup_by_name(vrf_name) : vrf_lookup_by_id(VRF_DEFAULT); + if (!vrf) { + vty_out(vty, "Specified VRF: %s does not exist\n", vrf_name); + return CMD_WARNING; + } + + FOR_ALL_INTERFACES (vrf, ifp) + pim_if_addr_del_all(ifp); + FOR_ALL_INTERFACES (vrf, ifp) + pim_if_addr_add_all(ifp); + + return CMD_SUCCESS; +} + DEFPY (clear_ipv6_pim_statistics, clear_ipv6_pim_statistics_cmd, "clear ipv6 pim statistics [vrf NAME]$name", @@ -2865,6 +2969,9 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ipv6_pim_boundary_oil_cmd); install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mroute_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_mld_limits_cmd); + install_element(INTERFACE_NODE, &no_interface_ipv6_mld_limits_cmd); + /* Install BSM command */ install_element(INTERFACE_NODE, &ipv6_pim_bsm_cmd); install_element(INTERFACE_NODE, &no_ipv6_pim_bsm_cmd); @@ -2878,6 +2985,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); @@ -2893,6 +3001,8 @@ void pim_cmd_init(void) &interface_ipv6_mld_last_member_query_interval_cmd); install_element(INTERFACE_NODE, &interface_no_ipv6_mld_last_member_query_interval_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_neighbor_prefix_list_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_neighbor_prefix_list_cmd); install_element(VIEW_NODE, &show_ipv6_pim_rp_cmd); install_element(VIEW_NODE, &show_ipv6_pim_rp_vrf_all_cmd); @@ -2935,6 +3045,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ipv6_pim_bsr_cmd); install_element(VIEW_NODE, &show_ipv6_pim_bsm_db_cmd); install_element(VIEW_NODE, &show_ipv6_pim_bsrp_cmd); + install_element(ENABLE_NODE, &clear_ipv6_mld_interfaces_cmd); install_element(ENABLE_NODE, &clear_ipv6_pim_statistics_cmd); install_element(ENABLE_NODE, &clear_ipv6_mroute_cmd); install_element(ENABLE_NODE, &clear_ipv6_pim_oil_cmd); diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index acfb0c3af3..2546166d0a 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -190,11 +190,26 @@ static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp, return gm_sgs_find(gm_ifp->sgs, &ref); } +static bool gm_sg_has_group(struct gm_sgs_head *sgs, const pim_addr group) +{ + struct gm_sg *sg; + + frr_each (gm_sgs, sgs, sg) + if (pim_addr_cmp(sg->sgaddr.grp, group) == 0) + return true; + + return false; +} + static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp, pim_addr src) { struct gm_sg *ret, *prev; + /* Count all unique group members. */ + if (!gm_sg_has_group(gm_ifp->sgs, grp)) + gm_ifp->groups_count++; + ret = XCALLOC(MTYPE_GM_SG, sizeof(*ret)); ret->sgaddr.grp = grp; ret->sgaddr.src = src; @@ -212,6 +227,47 @@ static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp, return ret; } +static size_t gm_sg_source_count(struct gm_sgs_head *sgs, const pim_addr group) +{ + struct gm_sg *sg; + size_t source_count; + + source_count = 0; + frr_each (gm_sgs, sgs, sg) + if (pim_addr_cmp(sg->sgaddr.grp, group) == 0) + source_count++; + + return source_count; +} + +static bool gm_sg_limit_reached(struct gm_if *gm_if, const pim_addr source, const pim_addr group) +{ + const struct pim_interface *pim_interface = gm_if->ifp->info; + + if (!gm_sg_has_group(gm_if->sgs, group)) { + if (gm_if->groups_count >= pim_interface->gm_group_limit) { + if (PIM_DEBUG_GM_TRACE) + zlog_debug("interface %s has reached group limit (%u), refusing to add group %pPA", + gm_if->ifp->name, pim_interface->gm_group_limit, &group); + + return true; + } + + return false; + } + + if (gm_sg_source_count(gm_if->sgs, group) >= pim_interface->gm_source_limit) { + if (PIM_DEBUG_GM_TRACE) { + zlog_debug("interface %s has reached source limit (%u), refusing to add source %pPA (group %pPA)", + gm_if->ifp->name, pim_interface->gm_source_limit, &source, + &group); + } + return true; + } + + return false; +} + /* * interface -> packets, sorted by expiry (because add_tail insert order) */ @@ -392,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); } @@ -471,6 +532,11 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) zlog_debug(log_sg(sg, "dropping")); gm_sgs_del(gm_ifp->sgs, sg); + + /* Decrement unique group members counter. */ + if (!gm_sg_has_group(gm_ifp->sgs, sg->sgaddr.grp)) + gm_ifp->groups_count--; + gm_sg_free(sg); } } @@ -634,8 +700,12 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, case MLD_RECTYPE_CHANGE_TO_EXCLUDE: /* this always replaces or creates state */ is_excl = true; - if (!grp) + if (!grp) { + if (gm_sg_limit_reached(pkt->iface, PIMADDR_ANY, rechdr->grp)) + return; + grp = gm_sg_make(pkt->iface, rechdr->grp, PIMADDR_ANY); + } item = gm_packet_sg_setup(pkt, grp, is_excl, false); item->n_exclude = n_src; @@ -700,9 +770,13 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, struct gm_sg *sg; sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]); - if (!sg) + if (!sg) { + if (gm_sg_limit_reached(pkt->iface, rechdr->srcs[j], rechdr->grp)) + return; + sg = gm_sg_make(pkt->iface, rechdr->grp, rechdr->srcs[j]); + } gm_packet_sg_setup(pkt, sg, is_excl, true); } @@ -952,6 +1026,10 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp, hdr = (struct mld_v1_pkt *)data; + if (!gm_sg_has_group(gm_ifp->sgs, hdr->grp) && + gm_sg_limit_reached(gm_ifp, PIMADDR_ANY, hdr->grp)) + return; + max_entries = 1; pkt = XCALLOC(MTYPE_GM_STATE, offsetof(struct gm_packet_state, items[max_entries])); @@ -1029,11 +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" */ } } @@ -1255,6 +1346,9 @@ static void gm_handle_q_groupsrc(struct gm_if *gm_ifp, for (i = 0; i < n_src; i++) { sg = gm_sg_find(gm_ifp, grp, srcs[i]); + if (sg == NULL) + continue; + GM_UPDATE_SG_STATE(sg); gm_sg_timer_start(gm_ifp, sg, timers->expire_wait); } diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h index 183ab2fc50..c5a9708961 100644 --- a/pimd/pim6_mld.h +++ b/pimd/pim6_mld.h @@ -350,6 +350,8 @@ struct gm_if { struct gm_subscribers_head subscribers[1]; struct gm_packet_expires_head expires[1]; + size_t groups_count; + struct timeval started; struct gm_if_stats stats; }; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index a1ad261869..f838c401e3 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -5656,6 +5656,56 @@ DEFUN (interface_no_ip_igmp_last_member_query_interval, return gm_process_no_last_member_query_interval_cmd(vty); } +DEFPY_YANG(interface_ip_igmp_limits, + interface_ip_igmp_limits_cmd, + "[no] ip igmp <max-sources$do_src (0-4294967295)$val" + "|max-groups$do_grp (0-4294967295)$val>", + NO_STR + IP_STR + IFACE_IGMP_STR + "Limit number of IGMPv3 sources to track\n" + "Permitted number of sources\n" + "Limit number of IGMP group memberships to track\n" + "Permitted number of groups\n") +{ + const char *xpath; + + assert(do_src || do_grp); + if (do_src) + xpath = "./max-sources"; + else + xpath = "./max-groups"; + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, val_str); + + return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS_YANG(interface_ip_igmp_limits, + no_interface_ip_igmp_limits_cmd, + "no ip igmp <max-sources$do_src|max-groups$do_grp>", + NO_STR + IP_STR + IFACE_IGMP_STR + "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)", @@ -5985,6 +6035,34 @@ DEFPY (interface_ip_igmp_proxy, } +DEFPY_YANG(interface_ip_pim_neighbor_prefix_list, + interface_ip_pim_neighbor_prefix_list_cmd, + "[no] ip pim allowed-neighbors prefix-list WORD", + NO_STR + IP_STR + "pim multicast routing\n" + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n" + "Name of a prefix-list\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./neighbor-filter-prefix-list", NB_OP_MODIFY, + prefix_list); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL); +} + +ALIAS (interface_ip_pim_neighbor_prefix_list, + interface_no_ip_pim_neighbor_prefix_list_cmd, + "no ip pim allowed-neighbors [prefix-list]", + NO_STR + IP_STR + "pim multicast routing\n" + "Restrict allowed PIM neighbors\n" + "Use prefix-list to filter neighbors\n") + DEFUN (debug_igmp, debug_igmp_cmd, "debug igmp", @@ -9101,6 +9179,9 @@ void pim_cmd_init(void) install_element(INTERFACE_NODE, &interface_no_ip_igmp_last_member_query_interval_cmd); 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); @@ -9116,6 +9197,8 @@ void pim_cmd_init(void) 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); + install_element(INTERFACE_NODE, &interface_ip_pim_neighbor_prefix_list_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_pim_neighbor_prefix_list_cmd); // Static mroutes NEB install_element(INTERFACE_NODE, &interface_ip_mroute_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 9316cebc0a..3408574cae 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -128,6 +128,8 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim, pim_ifp->gm_specific_query_max_response_time_dsec = GM_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; pim_ifp->gm_last_member_query_count = GM_DEFAULT_ROBUSTNESS_VARIABLE; + pim_ifp->gm_group_limit = UINT32_MAX; + pim_ifp->gm_source_limit = UINT32_MAX; /* BSM config on interface: true by default */ pim_ifp->bsm_enable = true; @@ -216,6 +218,7 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->bfd_config.profile) XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile); + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); XFREE(MTYPE_PIM_INTERFACE, pim_ifp); ifp->info = NULL; diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 90a81a21d0..b0befcfcba 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -105,6 +105,11 @@ struct pim_interface { struct gm_if *mld; + 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 */ @@ -116,6 +121,7 @@ struct pim_interface { uint32_t pim_generation_id; uint16_t pim_propagation_delay_msec; /* config */ uint16_t pim_override_interval_msec; /* config */ + char *nbr_plist; struct list *pim_neighbor_list; /* list of struct pim_neighbor */ struct list *upstream_switch_list; struct pim_ifchannel_rb ifchannel_rb; diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 1791502b94..d55d2a958a 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -239,10 +239,16 @@ void pim_ifchannel_delete_all(struct interface *ifp) void delete_on_noinfo(struct pim_ifchannel *ch) { - if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO - && ch->ifjoin_state == PIM_IFJOIN_NOINFO - && ch->t_ifjoin_expiry_timer == NULL) + struct pim_upstream *up = ch->upstream; + /* + * (S,G) with no active traffic, KAT expires, PPT expries, + * channel state is NoInfo + */ + if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO && + ch->ifjoin_state == PIM_IFJOIN_NOINFO && + (ch->t_ifjoin_expiry_timer == NULL || (up && !pim_upstream_is_kat_running(up)))) { pim_ifchannel_delete(ch); + } } void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 12f424248f..b1b4566499 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -1416,6 +1416,14 @@ struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp, __func__, &group_addr); return NULL; } + + if (listcount(pim_ifp->gm_group_list) >= pim_ifp->gm_group_limit) { + if (PIM_DEBUG_GM_TRACE) + zlog_debug("interface %s has reached group limit (%u), refusing to add group %pI4", + igmp->interface->name, pim_ifp->gm_group_limit, &group_addr); + return NULL; + } + /* Non-existant group is created as INCLUDE {empty}: diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 7348d8130f..2de2b32688 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -423,6 +423,7 @@ struct gm_source *igmp_find_source_by_addr(struct gm_group *group, struct gm_source *igmp_get_source_by_addr(struct gm_group *group, struct in_addr src_addr, bool *new) { + const struct pim_interface *pim_interface = group->interface->info; struct gm_source *src; if (new) @@ -432,6 +433,14 @@ struct gm_source *igmp_get_source_by_addr(struct gm_group *group, if (src) return src; + if (listcount(group->group_source_list) >= pim_interface->gm_source_limit) { + if (PIM_DEBUG_GM_TRACE) + zlog_debug("interface %s has reached source limit (%u), refusing to add source %pI4 (group %pI4)", + group->interface->name, pim_interface->gm_source_limit, + &src_addr, &group->group_addr); + return NULL; + } + if (PIM_DEBUG_GM_TRACE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -719,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); @@ -1487,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 */ @@ -1522,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_memory.c b/pimd/pim_memory.c index 2c35bc6473..f918cbd146 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -26,6 +26,7 @@ DEFINE_MTYPE(PIMD, PIM_STATIC_ROUTE, "PIM Static Route"); DEFINE_MTYPE(PIMD, PIM_RP, "PIM RP info"); DEFINE_MTYPE(PIMD, PIM_FILTER_NAME, "PIM RP filter info"); DEFINE_MTYPE(PIMD, PIM_MSDP_PEER, "PIM MSDP peer"); +DEFINE_MTYPE(PIMD, PIM_MSDP_FILTER_NAME, "PIM MSDP peer filter name"); DEFINE_MTYPE(PIMD, PIM_MSDP_MG_NAME, "PIM MSDP mesh-group name"); DEFINE_MTYPE(PIMD, PIM_MSDP_AUTH_KEY, "PIM MSDP authentication key"); DEFINE_MTYPE(PIMD, PIM_MSDP_SA, "PIM MSDP source-active cache"); diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index b44d3e191a..5c9bdad50c 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -25,6 +25,7 @@ DECLARE_MTYPE(PIM_STATIC_ROUTE); DECLARE_MTYPE(PIM_RP); DECLARE_MTYPE(PIM_FILTER_NAME); DECLARE_MTYPE(PIM_MSDP_PEER); +DECLARE_MTYPE(PIM_MSDP_FILTER_NAME); DECLARE_MTYPE(PIM_MSDP_MG_NAME); DECLARE_MTYPE(PIM_MSDP_SA); DECLARE_MTYPE(PIM_MSDP_MG); diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 46d5f4881f..1fd27d383e 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1095,6 +1095,8 @@ static void pim_msdp_peer_free(struct pim_msdp_peer *mp) if (mp->auth_listen_sock != -1) close(mp->auth_listen_sock); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name); mp->pim = NULL; diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index ea9ce3cecb..9a2fc5f3cd 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -315,6 +315,13 @@ const struct frr_yang_module_info frr_pim_info = { } }, { + .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/neighbor-filter-prefix-list", + .cbs = { + .modify = lib_interface_pim_address_family_nbr_plist_modify, + .destroy = lib_interface_pim_address_family_nbr_plist_destroy, + } + }, + { .xpath = "/frr-interface:lib/interface/frr-pim:pim/address-family/bfd", .cbs = { .create = lib_interface_pim_address_family_bfd_create, @@ -725,13 +732,31 @@ const struct frr_yang_module_info frr_gmp_info = { .destroy = lib_interface_gmp_address_family_join_group_destroy, } }, + { + .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources", + .cbs = { + .modify = lib_interface_gm_max_sources_modify, + } + }, + { + .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups", + .cbs = { + .modify = lib_interface_gm_max_groups_modify, + } + }, { .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy", .cbs = { .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 a15c6e6d9f..e9faf875b0 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -110,6 +110,8 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_mc struct nb_cb_modify_args *args); int lib_interface_pim_address_family_dr_priority_modify( struct nb_cb_modify_args *args); +int lib_interface_pim_address_family_nbr_plist_modify(struct nb_cb_modify_args *args); +int lib_interface_pim_address_family_nbr_plist_destroy(struct nb_cb_destroy_args *args); int lib_interface_pim_address_family_create(struct nb_cb_create_args *args); int lib_interface_pim_address_family_destroy(struct nb_cb_destroy_args *args); int lib_interface_pim_address_family_pim_enable_modify( @@ -287,6 +289,9 @@ int lib_interface_gmp_address_family_static_group_create( struct nb_cb_create_args *args); 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 51f0615884..1be5e9cb88 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1564,8 +1564,8 @@ int pim_msdp_peer_sa_filter_in_modify(struct nb_cb_modify_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_in); - mp->acl_in = XSTRDUP(MTYPE_TMP, + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); + mp->acl_in = XSTRDUP(MTYPE_PIM_MSDP_FILTER_NAME, yang_dnode_get_string(args->dnode, NULL)); break; } @@ -1585,7 +1585,7 @@ int pim_msdp_peer_sa_filter_in_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_in); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_in); break; } @@ -1608,8 +1608,8 @@ int pim_msdp_peer_sa_filter_out_modify(struct nb_cb_modify_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_out); - mp->acl_out = XSTRDUP(MTYPE_TMP, + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); + mp->acl_out = XSTRDUP(MTYPE_PIM_MSDP_FILTER_NAME, yang_dnode_get_string(args->dnode, NULL)); break; } @@ -1629,7 +1629,7 @@ int pim_msdp_peer_sa_filter_out_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: mp = nb_running_get_entry(args->dnode, NULL, true); - XFREE(MTYPE_TMP, mp->acl_out); + XFREE(MTYPE_PIM_MSDP_FILTER_NAME, mp->acl_out); break; } @@ -2043,6 +2043,11 @@ int lib_interface_pim_address_family_pim_enable_modify(struct nb_cb_modify_args ifp->name); return NB_ERR_INCONSISTENCY; } + + /* Trigger election in case it was never run before */ + pim_ifp = ifp->info; + if (pim_addr_is_any(pim_ifp->pim_dr_addr)) + pim_if_dr_election(ifp); } else { pim_ifp = ifp->info; if (!pim_ifp) @@ -2076,6 +2081,10 @@ int lib_interface_pim_address_family_pim_passive_enable_modify( pim_ifp = ifp->info; pim_ifp->pim_passive_enable = yang_dnode_get_bool(args->dnode, NULL); + + /* Trigger election in case it was never run before */ + if (pim_ifp->pim_passive_enable && pim_addr_is_any(pim_ifp->pim_dr_addr)) + pim_if_dr_election(ifp); break; } @@ -2154,6 +2163,55 @@ int lib_interface_pim_address_family_hello_holdtime_destroy( return NB_OK; } + +/* + * XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/neighbor-filter-prefix-list + */ +int lib_interface_pim_address_family_nbr_plist_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + const char *plist; + + plist = yang_dnode_get_string(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + 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; + + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); + pim_ifp->nbr_plist = XSTRDUP(MTYPE_PIM_PLIST_NAME, plist); + break; + } + + return NB_OK; +} + +int lib_interface_pim_address_family_nbr_plist_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + switch (args->event) { + case NB_EV_VALIDATE: + 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; + XFREE(MTYPE_PIM_PLIST_NAME, pim_ifp->nbr_plist); + break; + } + + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-pim:pim/address-family/bfd */ @@ -4388,6 +4446,72 @@ int lib_interface_gmp_address_family_last_member_query_interval_modify( } /* + * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups + */ +int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + const char *ifp_name; + 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)) { + ifp_name = yang_dnode_get_string(if_dnode, "name"); + snprintf(args->errmsg, args->errmsg_len, + "multicast not enabled on interface %s", ifp_name); + return NB_ERR_VALIDATION; + } + break; + 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->gm_group_limit = yang_dnode_get_uint32(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources + */ +int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + const char *ifp_name; + 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)) { + ifp_name = yang_dnode_get_string(if_dnode, "name"); + snprintf(args->errmsg, args->errmsg_len, + "multicast not enabled on interface %s", ifp_name); + return NB_ERR_VALIDATION; + } + break; + 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->gm_source_limit = yang_dnode_get_uint32(args->dnode, NULL); + break; + } + + return NB_OK; +} + +/* * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/robustness-variable */ int lib_interface_gmp_address_family_robustness_variable_modify( @@ -4418,6 +4542,29 @@ int lib_interface_gmp_address_family_robustness_variable_modify( } /* + * 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 */ int lib_interface_gmp_address_family_proxy_modify(struct nb_cb_modify_args *args) diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index a41bbacea7..fb78e39022 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -149,6 +149,9 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, uint32_t pim_msg_len = 0; uint16_t pim_checksum; /* received checksum */ uint16_t checksum; /* computed checksum */ + struct pim_interface *pim_ifp = ifp->info; + struct prefix src_prefix; + struct prefix_list *nbr_plist = NULL; struct pim_neighbor *neigh; struct pim_msg_header *header; bool no_fwd; @@ -205,6 +208,41 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, return -1; } + switch (header->type) { + case PIM_MSG_TYPE_HELLO: + case PIM_MSG_TYPE_JOIN_PRUNE: + case PIM_MSG_TYPE_ASSERT: + if (pim_ifp == NULL || pim_ifp->nbr_plist == NULL) + break; + + nbr_plist = prefix_list_lookup(PIM_AFI, pim_ifp->nbr_plist); + +#if PIM_IPV == 4 + src_prefix.family = AF_INET; + src_prefix.prefixlen = IPV4_MAX_BITLEN; + src_prefix.u.prefix4 = sg.src; +#else + src_prefix.family = AF_INET6; + src_prefix.prefixlen = IPV6_MAX_BITLEN; + src_prefix.u.prefix6 = sg.src; +#endif + + if (nbr_plist && + prefix_list_apply_ext(nbr_plist, NULL, &src_prefix, true) == PREFIX_PERMIT) + break; + +#if PIM_IPV == 4 + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("neighbor filter rejects packet %pI4 -> %pI4 on %s", + &ip_hdr->ip_src, &ip_hdr->ip_dst, ifp->name); +#else + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug("neighbor filter rejects packet %pI6 -> %pI6 on %s", &sg.src, + &sg.grp, ifp->name); +#endif + return -1; + } + /* save received checksum */ pim_checksum = header->checksum; diff --git a/pimd/pim_register.c b/pimd/pim_register.c index f776a59b7f..29e658ef16 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -186,8 +186,9 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) */ for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { if (PIM_DEBUG_PIM_REG) - zlog_debug("Executing Reg stop for %s", - child->sg_str); + zlog_debug( + "Executing Reg stop for upstream child %s", + child->sg_str); pim_reg_stop_upstream(pim, child); } @@ -208,8 +209,9 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (pim_addr_cmp(up->sg.grp, sg.grp) == 0) { if (PIM_DEBUG_PIM_REG) - zlog_debug("Executing Reg stop for %s", - up->sg_str); + zlog_debug( + "Executing Reg stop for upstream %s", + up->sg_str); pim_reg_stop_upstream(pim, up); } } @@ -682,9 +684,12 @@ int pim_register_recv(struct interface *ifp, pim_addr dest_addr, } } - if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || ((SwitchToSptDesiredOnRp(pim, &sg)) - && pim_upstream_inherited_olist(pim, upstream) == 0)) { + if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) || + (PIM_UPSTREAM_FLAG_TEST_FHR(upstream->flags) && i_am_rp) || + ((SwitchToSptDesiredOnRp(pim, &sg)) && + pim_upstream_inherited_olist(pim, upstream) == 0)) { + zlog_debug("sending pim register stop message : %s ", + upstream->sg_str); pim_register_stop_send(ifp, &sg, dest_addr, src_addr); sentRegisterStop = 1; } else { diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index c94b99d323..e4603ff946 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -312,7 +312,7 @@ static void on_join_timer(struct event *t) } /* - * In the case of a HFR we will not ahve anyone to send this to. + * In the case of a FHR we will not ahve anyone to send this to. */ if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) return; diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index a972a38c72..e37703be2b 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -457,6 +457,32 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp, ++writes; } + /* IF igmp/mld max-sources */ + if (pim_ifp->gm_source_limit != UINT32_MAX) { + vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-sources %u\n", + pim_ifp->gm_source_limit); + ++writes; + } + + /* IF igmp/mld max-groups */ + if (pim_ifp->gm_group_limit != UINT32_MAX) { + vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-groups %u\n", + pim_ifp->gm_group_limit); + ++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 (pim_ifp->nbr_plist) { + vty_out(vty, " " PIM_AF_NAME " pim allowed-neighbors prefix-list %s\n", + pim_ifp->nbr_plist); + ++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", |
