diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 7e1b451eab..77eb5c7568 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -53,7 +53,7 @@ libpim_a_SOURCES = \ pim_ssmpingd.c pim_int.c pim_rp.c \ pim_static.c pim_br.c pim_register.c pim_routemap.c \ pim_msdp.c pim_msdp_socket.c pim_msdp_packet.c \ - pim_jp_agg.c pim_nht.c + pim_jp_agg.c pim_nht.c pim_ssm.c noinst_HEADERS = \ pim_memory.h \ @@ -66,7 +66,7 @@ noinst_HEADERS = \ pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_rp.h \ pim_static.h pim_br.h pim_register.h \ pim_msdp.h pim_msdp_socket.h pim_msdp_packet.h pim_nht.h \ - pim_jp_agg.h + pim_jp_agg.h pim_ssm.h pimd_SOURCES = \ pim_main.c $(libpim_a_SOURCES) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index cf101b3e24..3c5a16ec68 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -54,6 +54,7 @@ #include "pim_rp.h" #include "pim_zlookup.h" #include "pim_msdp.h" +#include "pim_ssm.h" static struct cmd_node pim_global_node = { PIM_NODE, @@ -3598,6 +3599,153 @@ DEFUN (no_ip_pim_rp_prefix_list, return pim_no_rp_cmd_worker (vty, argv[4]->arg, NULL, argv[6]->arg); } +static int +pim_ssm_cmd_worker (struct vty *vty, const char *plist) +{ + int result = pim_ssm_range_set (VRF_DEFAULT, plist); + + if (result == PIM_SSM_ERR_NONE) + return CMD_SUCCESS; + + switch (result) + { + case PIM_SSM_ERR_NO_VRF: + vty_out (vty, "%% VRF doesn't exist%s", VTY_NEWLINE); + break; + case PIM_SSM_ERR_DUP: + vty_out (vty, "%% duplicate config%s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%% ssm range config failed%s", VTY_NEWLINE); + } + + return CMD_WARNING; +} + +DEFUN (ip_pim_ssm_prefix_list, + ip_pim_ssm_prefix_list_cmd, + "ip pim ssm prefix-list WORD", + IP_STR + "pim multicast routing\n" + "Source Specific Multicast\n" + "group range prefix-list filter\n" + "Name of a prefix-list\n") +{ + return pim_ssm_cmd_worker (vty, argv[0]->arg); +} + +DEFUN (no_ip_pim_ssm_prefix_list, + no_ip_pim_ssm_prefix_list_cmd, + "no ip pim ssm prefix-list", + NO_STR + IP_STR + "pim multicast routing\n" + "Source Specific Multicast\n" + "group range prefix-list filter\n") +{ + return pim_ssm_cmd_worker (vty, NULL); +} + +DEFUN (no_ip_pim_ssm_prefix_list_name, + no_ip_pim_ssm_prefix_list_name_cmd, + "no ip pim ssm prefix-list WORD", + NO_STR + IP_STR + "pim multicast routing\n" + "Source Specific Multicast\n" + "group range prefix-list filter\n" + "Name of a prefix-list\n") +{ + struct pim_ssm *ssm = pimg->ssm_info; + + if (ssm->plist_name && !strcmp(ssm->plist_name, argv[0]->arg)) + return pim_ssm_cmd_worker (vty, NULL); + + vty_out (vty, "%% pim ssm prefix-list %s doesn't exist%s", + argv[0]->arg, VTY_NEWLINE); + + return CMD_WARNING; +} + +static void +ip_pim_ssm_show_group_range(struct vty *vty, u_char uj) +{ + struct pim_ssm *ssm = pimg->ssm_info; + const char *range_str = ssm->plist_name?ssm->plist_name:PIM_SSM_STANDARD_RANGE; + + if (uj) + { + json_object *json; + json = json_object_new_object(); + json_object_string_add(json, "ssmGroups", range_str); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } + else + vty_out(vty, "SSM group range : %s%s", range_str, VTY_NEWLINE); +} + +DEFUN (show_ip_pim_ssm_range, + show_ip_pim_ssm_range_cmd, + "show ip pim group-type [json]", + SHOW_STR + IP_STR + PIM_STR + "PIM group type\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + ip_pim_ssm_show_group_range(vty, uj); + + return CMD_SUCCESS; +} + +static void +ip_pim_ssm_show_group_type(struct vty *vty, u_char uj, const char *group) +{ + struct in_addr group_addr; + const char *type_str; + int result; + + result = inet_pton(AF_INET, group, &group_addr); + if (result <= 0) + type_str = "invalid"; + else + { + if (pim_is_group_224_4 (group_addr)) + type_str = pim_is_grp_ssm (group_addr)?"SSM":"ASM"; + else + type_str = "not-multicast"; + } + + if (uj) + { + json_object *json; + json = json_object_new_object(); + json_object_string_add(json, "groupType", type_str); + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } + else + vty_out(vty, "Group type : %s%s", type_str, VTY_NEWLINE); +} + +DEFUN (show_ip_pim_group_type, + show_ip_pim_group_type_cmd, + "show ip pim group-type A.B.C.D [json]", + SHOW_STR + IP_STR + PIM_STR + "multicast group type\n" + "group address\n" + "JavaScript Object Notation\n") +{ + u_char uj = use_json(argc, argv); + ip_pim_ssm_show_group_type(vty, uj, argv[0]->arg); + + return CMD_SUCCESS; +} + DEFUN_HIDDEN (ip_multicast_routing, ip_multicast_routing_cmd, "ip multicast-routing", @@ -4292,7 +4440,7 @@ DEFUN (interface_no_ip_pim_drprio, } static int -pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype) +pim_cmd_interface_add (struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; @@ -4306,14 +4454,12 @@ pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype) PIM_IF_DO_PIM(pim_ifp->options); } - pim_ifp->itype = itype; pim_if_addr_add_all(ifp); pim_if_membership_refresh(ifp); return 1; } - -DEFUN (interface_ip_pim_ssm, +DEFUN_HIDDEN (interface_ip_pim_ssm, interface_ip_pim_ssm_cmd, "ip pim ssm", IP_STR @@ -4322,11 +4468,12 @@ DEFUN (interface_ip_pim_ssm, { VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_add(ifp, PIM_INTERFACE_SSM)) { - vty_out(vty, "Could not enable PIM SSM on interface%s", VTY_NEWLINE); + if (!pim_cmd_interface_add(ifp)) { + vty_out(vty, "Could not enable PIM SM on interface%s", VTY_NEWLINE); return CMD_WARNING; } + vty_out(vty, "WARN: Enabled PIM SM on interface; configure PIM SSM range if needed%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -4338,7 +4485,7 @@ DEFUN (interface_ip_pim_sm, IFACE_PIM_SM_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); - if (!pim_cmd_interface_add(ifp, PIM_INTERFACE_SM)) { + if (!pim_cmd_interface_add(ifp)) { vty_out(vty, "Could not enable PIM SM on interface%s", VTY_NEWLINE); return CMD_WARNING; } @@ -4374,7 +4521,7 @@ pim_cmd_interface_delete (struct interface *ifp) return 1; } -DEFUN (interface_no_ip_pim_ssm, +DEFUN_HIDDEN (interface_no_ip_pim_ssm, interface_no_ip_pim_ssm_cmd, "no ip pim ssm", NO_STR @@ -6029,6 +6176,9 @@ void pim_cmd_init() install_element (CONFIG_NODE, &no_ip_pim_rp_cmd); install_element (CONFIG_NODE, &ip_pim_rp_prefix_list_cmd); install_element (CONFIG_NODE, &no_ip_pim_rp_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ip_pim_ssm_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ip_pim_ssm_prefix_list_name_cmd); + install_element (CONFIG_NODE, &ip_pim_ssm_prefix_list_cmd); install_element (CONFIG_NODE, &ip_pim_register_suppress_cmd); install_element (CONFIG_NODE, &no_ip_pim_register_suppress_cmd); install_element (CONFIG_NODE, &ip_pim_joinprune_time_cmd); @@ -6186,6 +6336,8 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_msdp_sa_detail_cmd); install_element (VIEW_NODE, &show_ip_msdp_sa_sg_cmd); install_element (VIEW_NODE, &show_ip_msdp_mesh_group_cmd); + install_element (VIEW_NODE, &show_ip_pim_ssm_range_cmd); + install_element (VIEW_NODE, &show_ip_pim_group_type_cmd); install_element (INTERFACE_NODE, &interface_pim_use_source_cmd); install_element (INTERFACE_NODE, &interface_no_pim_use_source_cmd); } diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 8d332c70b0..e98c17fed2 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -58,11 +58,6 @@ struct pim_iface_upstream_switch { struct list *us; }; -enum pim_interface_type { - PIM_INTERFACE_SSM, - PIM_INTERFACE_SM -}; - enum pim_secondary_addr_flags { PIM_SEC_ADDRF_NONE = 0, PIM_SEC_ADDRF_STALE = (1 << 0) @@ -74,7 +69,6 @@ struct pim_secondary_addr { }; struct pim_interface { - enum pim_interface_type itype; uint32_t options; /* bit vector */ ifindex_t mroute_vif_index; struct in_addr primary_address; /* remember addr to detect change */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 5688d0e45d..e6b4ba92a9 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -41,6 +41,7 @@ #include "pim_macro.h" #include "pim_oil.h" #include "pim_upstream.h" +#include "pim_ssm.h" int pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2) @@ -967,6 +968,18 @@ pim_ifchannel_local_membership_add(struct interface *ifp, if (!PIM_IF_TEST_PIM(pim_ifp->options)) return 0; + /* skip (*,G) ch creation if G is of type SSM */ + if (sg->src.s_addr == INADDR_ANY) + { + if (pim_is_grp_ssm (sg->grp)) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM", + __PRETTY_FUNCTION__, pim_str_sg_dump (sg)); + return 1; + } + } + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP); if (!ch) { return 0; diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 07f2812725..d814af6b2c 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -46,7 +46,6 @@ #include "pim_zebra.h" #include "pim_msdp.h" #include "pim_iface.h" -#include "pim_rp.h" extern struct host host; @@ -120,8 +119,8 @@ int main(int argc, char** argv, char** envp) { pim_vrf_init (); access_list_init(); prefix_list_init (); - prefix_list_add_hook (pim_rp_prefix_list_update); - prefix_list_delete_hook (pim_rp_prefix_list_update); + prefix_list_add_hook (pim_prefix_list_update); + prefix_list_delete_hook (pim_prefix_list_update); pim_route_map_init (); pim_init(); diff --git a/pimd/pim_memory.c b/pimd/pim_memory.c index f46cf193bb..2acca6f49b 100644 --- a/pimd/pim_memory.c +++ b/pimd/pim_memory.c @@ -51,3 +51,4 @@ DEFINE_MTYPE(PIMD, PIM_JP_AGG_GROUP, "PIM JP AGG Group") DEFINE_MTYPE(PIMD, PIM_JP_AGG_SOURCE, "PIM JP AGG Source") DEFINE_MTYPE(PIMD, PIM_PIM_INSTANCE, "PIM global state") DEFINE_MTYPE(PIMD, PIM_NEXTHOP_CACHE, "PIM nexthop cache state") +DEFINE_MTYPE(PIMD, PIM_SSM_INFO, "PIM SSM configuration") diff --git a/pimd/pim_memory.h b/pimd/pim_memory.h index bd9e12f2fc..02446de46a 100644 --- a/pimd/pim_memory.h +++ b/pimd/pim_memory.h @@ -50,5 +50,6 @@ DECLARE_MTYPE(PIM_JP_AGG_GROUP) DECLARE_MTYPE(PIM_JP_AGG_SOURCE) DECLARE_MTYPE(PIM_PIM_INSTANCE) DECLARE_MTYPE(PIM_NEXTHOP_CACHE) +DECLARE_MTYPE(PIM_SSM_INFO) #endif /* _QUAGGA_PIM_MEMORY_H */ diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 04b6a4c697..90e30ea3d6 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -39,6 +39,7 @@ #include "pim_register.h" #include "pim_ifchannel.h" #include "pim_zlookup.h" +#include "pim_ssm.h" /* GLOBAL VARS */ static struct thread *qpim_mroute_socket_reader = NULL; @@ -127,8 +128,7 @@ pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg */ if ((pim_rpf_addr_is_inaddr_none (rpg)) || (!pim_ifp) || - (!(PIM_I_am_DR(pim_ifp))) || - (pim_ifp->itype == PIM_INTERFACE_SSM)) + (!(PIM_I_am_DR(pim_ifp)))) { if (PIM_DEBUG_MROUTE_DETAIL) zlog_debug ("%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP", @@ -178,8 +178,7 @@ pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg up->channel_oil->cc.pktcnt++; PIM_UPSTREAM_FLAG_SET_FHR(up->flags); - pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); - up->reg_state = PIM_REG_JOIN; + pim_register_join (up); return 0; } @@ -214,8 +213,7 @@ pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf) if ((pim_rpf_addr_is_inaddr_none (rpg)) || (!pim_ifp) || - (!(PIM_I_am_DR(pim_ifp))) || - (pim_ifp->itype == PIM_INTERFACE_SSM)) { + (!(PIM_I_am_DR(pim_ifp)))) { if (PIM_DEBUG_MROUTE) { zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__); } @@ -226,9 +224,18 @@ pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf) * If we've received a register suppress */ if (!up->t_rs_timer) - pim_register_send((uint8_t *)buf + sizeof(struct ip), - ntohs (ip_hdr->ip_len) - sizeof (struct ip), - pim_ifp->primary_address, rpg, 0, up); + { + if (pim_is_grp_ssm (sg.grp)) + { + if (PIM_DEBUG_PIM_REG) + zlog_debug ("%s register forward skipped as group is SSM", + pim_str_sg_dump (&sg)); + return 0; + } + pim_register_send((uint8_t *)buf + sizeof(struct ip), + ntohs (ip_hdr->ip_len) - sizeof (struct ip), + pim_ifp->primary_address, rpg, 0, up); + } return 0; } @@ -442,8 +449,7 @@ pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf) pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); up->channel_oil = oil; up->channel_oil->cc.pktcnt++; - pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); - up->reg_state = PIM_REG_JOIN; + pim_register_join (up); pim_upstream_inherited_olist (up); // Send the packet to the RP diff --git a/pimd/pim_register.c b/pimd/pim_register.c index cb9d7e3744..effc212722 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -43,9 +43,24 @@ #include "pim_zebra.h" #include "pim_join.h" #include "pim_util.h" +#include "pim_ssm.h" struct thread *send_test_packet_timer = NULL; +void +pim_register_join (struct pim_upstream *up) +{ + if (pim_is_grp_ssm (up->sg.grp)) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("%s register setup skipped as group is SSM", up->sg_str); + return; + } + + pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + up->reg_state = PIM_REG_JOIN; +} + void pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator) diff --git a/pimd/pim_register.h b/pimd/pim_register.h index 42a908b225..210a904ae9 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -40,5 +40,6 @@ int pim_register_recv (struct interface *ifp, void pim_register_send (const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, struct pim_upstream *up); void pim_register_stop_send (struct interface *ifp, struct prefix_sg *sg, struct in_addr src, struct in_addr originator); +void pim_register_join (struct pim_upstream *up); #endif diff --git a/pimd/pim_ssm.c b/pimd/pim_ssm.c new file mode 100644 index 0000000000..41bf1e5668 --- /dev/null +++ b/pimd/pim_ssm.c @@ -0,0 +1,158 @@ +/* + * IP SSM ranges for FRR + * Copyright (C) 2017 Cumulus Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include +#include + +#include "pimd.h" +#include "pim_ssm.h" +#include "pim_zebra.h" + +static void +pim_ssm_range_reevaluate (void) +{ + /* 1. Setup register state for (S,G) entries if G has changed from SSM to + * ASM. + * 2. check existing (*,G) IGMP registrations to see if they are + * still ASM. if they are now SSM delete them. + * 3. Allow channel setup for IGMP (*,G) members if G is now ASM + * 4. I could tear down all (*,G), (S,G,rpt) states. But that is an + * unnecessary sladge hammer and may not be particularly useful as it is + * likely the SPT switchover has already happened for flows along such RPTs. + * As for the RPT states it seems that the best thing to do is let them age + * out gracefully. As long as the FHR and LHR do the right thing RPTs will + * disappear in time for SSM groups. + */ + pim_upstream_register_reevaluate (); + igmp_source_forward_reevaluate_all (); +} + +void +pim_ssm_prefix_list_update (struct prefix_list *plist) +{ + struct pim_ssm *ssm = pimg->ssm_info; + + if (!ssm->plist_name || strcmp (ssm->plist_name, prefix_list_name (plist))) + { + /* not ours */ + return; + } + + pim_ssm_range_reevaluate (); +} + +static int +pim_is_grp_standard_ssm (struct prefix *group) +{ + static int first = 1; + static struct prefix group_ssm; + + if (first) + { + str2prefix (PIM_SSM_STANDARD_RANGE, &group_ssm); + first = 0; + } + + return prefix_match (&group_ssm, group); +} + +int +pim_is_grp_ssm (struct in_addr group_addr) +{ + struct pim_ssm *ssm; + struct prefix group; + struct prefix_list *plist; + + memset (&group, 0, sizeof (group)); + group.family = AF_INET; + group.u.prefix4 = group_addr; + group.prefixlen = 32; + + ssm = pimg->ssm_info; + if (!ssm->plist_name) + { + return pim_is_grp_standard_ssm (&group); + } + + plist = prefix_list_lookup (AFI_IP, ssm->plist_name); + if (!plist) + return 0; + + return (prefix_list_apply (plist, &group) == PREFIX_PERMIT); +} + +int +pim_ssm_range_set (vrf_id_t vrf_id, const char *plist_name) +{ + struct pim_ssm *ssm; + int change = 0; + + if (vrf_id != VRF_DEFAULT) + return PIM_SSM_ERR_NO_VRF; + + ssm = pimg->ssm_info; + if (plist_name) + { + if (ssm->plist_name) + { + if (!strcmp (ssm->plist_name, plist_name)) + return PIM_SSM_ERR_DUP; + XFREE (MTYPE_PIM_FILTER_NAME, ssm->plist_name); + } + ssm->plist_name = XSTRDUP (MTYPE_PIM_FILTER_NAME, plist_name); + change = 1; + } + else + { + if (ssm->plist_name) + { + change = 1; + XFREE (MTYPE_PIM_FILTER_NAME, ssm->plist_name); + } + } + + if (change) + pim_ssm_range_reevaluate (); + + return PIM_SSM_ERR_NONE; +} + +void * +pim_ssm_init (vrf_id_t vrf_id) +{ + struct pim_ssm *ssm; + + ssm = XCALLOC (MTYPE_PIM_SSM_INFO, sizeof (*ssm)); + ssm->vrf_id = vrf_id; + + return ssm; +} + +void +pim_ssm_terminate (struct pim_ssm *ssm) +{ + if (ssm && ssm->plist_name) + XFREE (MTYPE_PIM_FILTER_NAME, ssm->plist_name); +} diff --git a/pimd/pim_ssm.h b/pimd/pim_ssm.h new file mode 100644 index 0000000000..ca82d334f1 --- /dev/null +++ b/pimd/pim_ssm.h @@ -0,0 +1,44 @@ +/* + * IP SSM ranges for FRR + * Copyright (C) 2017 Cumulus Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef PIM_SSM_H +#define PIM_SSM_H + +#define PIM_SSM_STANDARD_RANGE "232.0.0.0/8" + +/* SSM error codes */ +enum pim_ssm_err +{ + PIM_SSM_ERR_NONE = 0, + PIM_SSM_ERR_NO_VRF = -1, + PIM_SSM_ERR_DUP = -2, +}; + +struct pim_ssm +{ + vrf_id_t vrf_id; + char *plist_name; /* prefix list of group ranges */ +}; + +void pim_ssm_prefix_list_update (struct prefix_list *plist); +int pim_is_grp_ssm (struct in_addr group_addr); +int pim_ssm_range_set (vrf_id_t vrf_id, const char *plist_name); +void *pim_ssm_init (vrf_id_t vrf_id); +void pim_ssm_terminate (struct pim_ssm *ssm); +#endif diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index d95849fce0..172d0d21c9 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -53,6 +53,7 @@ #include "pim_msdp.h" #include "pim_jp_agg.h" #include "pim_nht.h" +#include "pim_ssm.h" struct hash *pim_upstream_hash = NULL; struct list *pim_upstream_list = NULL; @@ -476,6 +477,51 @@ pim_upstream_could_register (struct pim_upstream *up) return 0; } +/* Source registration is supressed for SSM groups. When the SSM range changes + * we re-revaluate register setup for existing upstream entries */ +void +pim_upstream_register_reevaluate (void) +{ + struct listnode *upnode; + struct pim_upstream *up; + + for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, upnode, up)) + { + /* If FHR is set CouldRegister is True. Also check if the flow + * is actually active; if it is not kat setup will trigger source + * registration whenever the flow becomes active. */ + if (!PIM_UPSTREAM_FLAG_TEST_FHR (up->flags) || !up->t_ka_timer) + continue; + + if (pim_is_grp_ssm (up->sg.grp)) + { + /* clear the register state for SSM groups */ + if (up->reg_state != PIM_REG_NOINFO) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("Clear register for %s as G is now SSM", + up->sg_str); + /* remove regiface from the OIL if it is there*/ + pim_channel_del_oif (up->channel_oil, pim_regiface, + PIM_OIF_FLAG_PROTO_PIM); + up->reg_state = PIM_REG_NOINFO; + } + } + else + { + /* register ASM sources with the RP */ + if (up->reg_state == PIM_REG_NOINFO) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("Register %s as G is now ASM", up->sg_str); + pim_channel_add_oif (up->channel_oil, pim_regiface, + PIM_OIF_FLAG_PROTO_PIM); + up->reg_state = PIM_REG_JOIN; + } + } + } +} + void pim_upstream_switch(struct pim_upstream *up, enum pim_upstream_state new_state) @@ -507,9 +553,8 @@ pim_upstream_switch(struct pim_upstream *up, PIM_UPSTREAM_FLAG_SET_FHR(up->flags); if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { - up->reg_state = PIM_REG_JOIN; pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time); - pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); + pim_register_join (up); } } else @@ -1018,10 +1063,8 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up) zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str); PIM_UPSTREAM_FLAG_SET_FHR(up->flags); - if (up->reg_state == PIM_REG_NOINFO) { - pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM); - up->reg_state = PIM_REG_JOIN; - } + if (up->reg_state == PIM_REG_NOINFO) + pim_register_join (up); } } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index e293c0c146..6f7556f323 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -192,4 +192,5 @@ void pim_upstream_terminate (void); void join_timer_start (struct pim_upstream *up); int pim_upstream_compare (void *arg1, void *arg2); +void pim_upstream_register_reevaluate (void); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 5b6a79b95a..f4bfcc5ce0 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -38,6 +38,7 @@ #include "pim_static.h" #include "pim_rp.h" #include "pim_msdp.h" +#include "pim_ssm.h" int pim_debug_config_write (struct vty *vty) @@ -145,6 +146,7 @@ pim_debug_config_write (struct vty *vty) int pim_global_config_write(struct vty *vty) { int writes = 0; + struct pim_ssm *ssm = pimg->ssm_info; writes += pim_msdp_config_write (vty); @@ -174,6 +176,12 @@ int pim_global_config_write(struct vty *vty) qpim_packet_process, VTY_NEWLINE); ++writes; } + if (ssm->plist_name) + { + vty_out (vty, "ip pim ssm prefix-list %s%s", + ssm->plist_name, VTY_NEWLINE); + ++writes; + } if (qpim_ssmpingd_list) { struct listnode *node; @@ -206,12 +214,8 @@ int pim_interface_config_write(struct vty *vty) if (ifp->info) { struct pim_interface *pim_ifp = ifp->info; - /* IF ip pim ssm */ if (PIM_IF_TEST_PIM(pim_ifp->options)) { - if (pim_ifp->itype == PIM_INTERFACE_SSM) - vty_out(vty, " ip pim ssm%s", VTY_NEWLINE); - else - vty_out(vty, " ip pim sm%s", VTY_NEWLINE); + vty_out(vty, " ip pim sm%s", VTY_NEWLINE); ++writes; } diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index d02560bfc2..cd7c3db59a 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -46,6 +46,7 @@ #include "pim_igmpv3.h" #include "pim_jp_agg.h" #include "pim_nht.h" +#include "pim_ssm.h" #undef PIM_DEBUG_IFADDR_DUMP #define PIM_DEBUG_IFADDR_DUMP @@ -760,6 +761,84 @@ static int fib_lookup_if_vif_index(struct in_addr addr) return vif_index; } +static void +igmp_source_forward_reevaluate_one(struct igmp_source *source) +{ + struct prefix_sg sg; + struct igmp_group *group = source->source_group; + struct pim_ifchannel *ch; + + if ((source->source_addr.s_addr != INADDR_ANY) || + !IGMP_SOURCE_TEST_FORWARDING (source->source_flags)) + return; + + memset (&sg, 0, sizeof (struct prefix_sg)); + sg.src = source->source_addr; + sg.grp = group->group_addr; + + ch = pim_ifchannel_find (group->group_igmp_sock->interface, &sg); + if (pim_is_grp_ssm (group->group_addr)) + { + /* If SSM group withdraw local membership */ + if (ch && (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("local membership del for %s as G is now SSM", + pim_str_sg_dump (&sg)); + pim_ifchannel_local_membership_del (group->group_igmp_sock->interface, &sg); + } + } + else + { + /* If ASM group add local membership */ + if (!ch || (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) + { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("local membership add for %s as G is now ASM", + pim_str_sg_dump (&sg)); + pim_ifchannel_local_membership_add (group->group_igmp_sock->interface, &sg); + } + } +} + +void +igmp_source_forward_reevaluate_all(void) +{ + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) + { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO (pim_ifp->igmp_socket_list, sock_node, igmp)) + { + struct listnode *grpnode; + struct igmp_group *grp; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO (igmp->igmp_group_list, grpnode, grp)) + { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO (grp->group_source_list, + srcnode, src)) + { + igmp_source_forward_reevaluate_one (src); + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + void igmp_source_forward_start(struct igmp_source *source) { struct igmp_group *group; diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index 5dc06a4a19..2ed463efaa 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -38,6 +38,7 @@ void igmp_anysource_forward_stop(struct igmp_group *group); void igmp_source_forward_start(struct igmp_source *source); void igmp_source_forward_stop(struct igmp_source *source); +void igmp_source_forward_reevaluate_all(void); void pim_forward_start(struct pim_ifchannel *ch); void pim_forward_stop(struct pim_ifchannel *ch); diff --git a/pimd/pimd.c b/pimd/pimd.c index b67544b28e..72fe0e7046 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -41,6 +41,7 @@ #include "pim_ssmpingd.h" #include "pim_static.h" #include "pim_rp.h" +#include "pim_ssm.h" #include "pim_zlookup.h" #include "pim_nht.h" @@ -182,6 +183,13 @@ pim_rp_list_hash_clean (void *data) list_delete_all_node (pnc->upstream_list); } +void +pim_prefix_list_update (struct prefix_list *plist) +{ + pim_rp_prefix_list_update (plist); + pim_ssm_prefix_list_update (plist); +} + static void pim_instance_terminate (void) { @@ -191,6 +199,7 @@ pim_instance_terminate (void) hash_clean (pimg->rpf_hash, (void *) pim_rp_list_hash_clean); hash_free (pimg->rpf_hash); } + pim_ssm_terminate (pimg->ssm_info); XFREE (MTYPE_PIM_PIM_INSTANCE, pimg); } @@ -233,6 +242,11 @@ pim_instance_init (vrf_id_t vrf_id, afi_t afi) if (PIM_DEBUG_ZEBRA) zlog_debug ("%s: NHT rpf hash init ", __PRETTY_FUNCTION__); + pim->ssm_info = pim_ssm_init (vrf_id); + if (!pim->ssm_info) { + pim_instance_terminate (); + return NULL; + } return pim; } diff --git a/pimd/pimd.h b/pimd/pimd.h index b3bdd9e243..69aee28f8f 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -24,6 +24,9 @@ #include #include "zebra.h" #include "libfrr.h" +#include "prefix.h" +#include "vty.h" +#include "plist.h" #include "pim_str.h" #include "pim_memory.h" @@ -240,6 +243,7 @@ struct pim_instance afi_t afi; vrf_id_t vrf_id; struct hash *rpf_hash; + void *ssm_info; /* per-vrf SSM configuration */ }; extern struct pim_instance *pimg; //Pim Global Instance @@ -250,5 +254,6 @@ void pim_terminate(void); extern void pim_route_map_init (void); extern void pim_route_map_terminate(void); void pim_vrf_init (void); +void pim_prefix_list_update (struct prefix_list *plist); #endif /* PIMD_H */