diff options
author | Donald Sharp <sharpd@cumulusnetworks.com> | 2021-06-10 15:44:28 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-10 15:44:28 +0200 |
commit | 59ec133a03bafbf41ab1759c3f67b142bddbc0a2 (patch) | |
tree | bca73efa5dc9206d2c721a5e136475781cb3a139 | |
parent | Merge pull request #8733 from idryzhov/ipv6-ospf6-area (diff) | |
parent | pimd: rename some MSDP functions (diff) | |
download | frr-59ec133a03bafbf41ab1759c3f67b142bddbc0a2.tar.xz frr-59ec133a03bafbf41ab1759c3f67b142bddbc0a2.zip |
Merge pull request #8561 from opensourcerouting/msdp-refactor-v2
pimd: rework MSDP mesh groups
25 files changed, 1103 insertions, 796 deletions
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index f6072b177..b3d444465 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3853,6 +3853,54 @@ static const char *pim_cli_get_vrf_name(struct vty *vty) return yang_dnode_get_string(vrf_node, "./name"); } +/** + * Compatibility function to keep the legacy mesh group CLI behavior: + * Delete group when there are no more configurations in it. + * + * NOTE: + * Don't forget to call `nb_cli_apply_changes` after this. + */ +static void pim_cli_legacy_mesh_group_behavior(struct vty *vty, + const char *gname) +{ + const char *vrfname; + char xpath_value[XPATH_MAXLEN]; + char xpath_member_value[XPATH_MAXLEN]; + const struct lyd_node *member_dnode; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return; + + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + /* Group must exists, otherwise just quit. */ + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) + return; + + /* Group members check: */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/members", sizeof(xpath_member_value)); + if (yang_dnode_exists(vty->candidate_config->dnode, + xpath_member_value)) { + member_dnode = yang_dnode_get(vty->candidate_config->dnode, + xpath_member_value); + if (!yang_is_last_list_dnode(member_dnode)) + return; + } + + /* Source address check: */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/source", sizeof(xpath_member_value)); + if (yang_dnode_exists(vty->candidate_config->dnode, xpath_member_value)) + return; + + /* No configurations found: delete it. */ + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); +} + DEFUN (clear_ip_interfaces, clear_ip_interfaces_cmd, "clear ip interfaces [vrf NAME]", @@ -9685,305 +9733,199 @@ DEFUN (no_ip_msdp_peer, return nb_cli_apply_changes(vty, NULL); } -DEFUN (ip_msdp_mesh_group_member, - ip_msdp_mesh_group_member_cmd, - "ip msdp mesh-group WORD member A.B.C.D", - IP_STR - CFG_MSDP_STR - "Configure MSDP mesh-group\n" - "mesh group name\n" - "mesh group member\n" - "peer ip address\n") +DEFPY(ip_msdp_mesh_group_member, + ip_msdp_mesh_group_member_cmd, + "ip msdp mesh-group WORD$gname member A.B.C.D$maddr", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "Mesh group name\n" + "Mesh group member\n" + "Peer IP address\n") { const char *vrfname; - char msdp_mesh_group_name_xpath[XPATH_MAXLEN]; - char msdp_mesh_group_member_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(msdp_mesh_group_name_xpath)); - snprintf(msdp_mesh_group_member_xpath, - sizeof(msdp_mesh_group_member_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_member_xpath, "/msdp-mesh-group/member-ip", - sizeof(msdp_mesh_group_member_xpath)); + /* Create mesh group. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY, - argv[3]->arg); - nb_cli_enqueue_change(vty, msdp_mesh_group_member_xpath, NB_OP_CREATE, - argv[5]->arg); + /* Create mesh group member. */ + strlcat(xpath_value, "/members[address='", sizeof(xpath_value)); + strlcat(xpath_value, maddr_str, sizeof(xpath_value)); + strlcat(xpath_value, "']", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group_member, - no_ip_msdp_mesh_group_member_cmd, - "no ip msdp mesh-group WORD member A.B.C.D", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group member\n" - "mesh group name\n" - "mesh group member\n" - "peer ip address\n") +DEFPY(no_ip_msdp_mesh_group_member, + no_ip_msdp_mesh_group_member_cmd, + "no ip msdp mesh-group WORD$gname member A.B.C.D$maddr", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group member\n" + "Mesh group name\n" + "Mesh group member\n" + "Peer IP address\n") { const char *vrfname; - char pim_af_xpath[XPATH_MAXLEN]; - char mesh_group_xpath[XPATH_MAXLEN + 32]; - char group_member_list_xpath[XPATH_MAXLEN + 64]; - char group_member_xpath[XPATH_MAXLEN + 128]; - char source_xpath[XPATH_MAXLEN + 64]; - char mesh_group_name_xpath[XPATH_MAXLEN + 64]; - const char *mesh_group_name; - const struct lyd_node *member_dnode; + char xpath_value[XPATH_MAXLEN]; + char xpath_member_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(pim_af_xpath, sizeof(pim_af_xpath), FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); - snprintf(mesh_group_xpath, sizeof(mesh_group_xpath), - "%s/msdp-mesh-group", pim_af_xpath); - - snprintf(group_member_list_xpath, sizeof(group_member_list_xpath), - "%s/msdp-mesh-group/member-ip", pim_af_xpath); - - snprintf(group_member_xpath, sizeof(group_member_xpath), "%s[.='%s']", - group_member_list_xpath, argv[6]->arg); - - snprintf(source_xpath, sizeof(source_xpath), - "%s/msdp-mesh-group/source-ip", pim_af_xpath); - - snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath), - "%s/msdp-mesh-group/mesh-group-name", pim_af_xpath); - - if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath) - == true) { - mesh_group_name = yang_dnode_get_string(running_config->dnode, - mesh_group_name_xpath); - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) { + vty_out(vty, "%% mesh-group does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; } - if (yang_dnode_exists(vty->candidate_config->dnode, - group_member_xpath)) { - if (!yang_dnode_exists(vty->candidate_config->dnode, - source_xpath)) { - member_dnode = yang_dnode_get( - vty->candidate_config->dnode, - group_member_xpath); - if (yang_is_last_list_dnode(member_dnode)) { - nb_cli_enqueue_change(vty, mesh_group_xpath, - NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, group_member_list_xpath, - NB_OP_DESTROY, argv[6]->arg); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, group_member_list_xpath, - NB_OP_DESTROY, argv[6]->arg); - return nb_cli_apply_changes(vty, NULL); + /* Remove mesh group member. */ + strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "/members[address='", + sizeof(xpath_member_value)); + strlcat(xpath_member_value, maddr_str, sizeof(xpath_member_value)); + strlcat(xpath_member_value, "']", sizeof(xpath_member_value)); + if (!yang_dnode_exists(vty->candidate_config->dnode, + xpath_member_value)) { + vty_out(vty, "%% mesh-group member does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; } - vty_out(vty, "%% mesh-group member does not exist\n"); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); - return CMD_SUCCESS; + /* + * If this is the last member, then we must remove the group altogether + * to not break legacy CLI behaviour. + */ + pim_cli_legacy_mesh_group_behavior(vty, gname); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (ip_msdp_mesh_group_source, - ip_msdp_mesh_group_source_cmd, - "ip msdp mesh-group WORD source A.B.C.D", - IP_STR - CFG_MSDP_STR - "Configure MSDP mesh-group\n" - "mesh group name\n" - "mesh group local address\n" - "source ip address for the TCP connection\n") +DEFPY(ip_msdp_mesh_group_source, + ip_msdp_mesh_group_source_cmd, + "ip msdp mesh-group WORD$gname source A.B.C.D$saddr", + IP_STR + CFG_MSDP_STR + "Configure MSDP mesh-group\n" + "Mesh group name\n" + "Mesh group local address\n" + "Source IP address for the TCP connection\n") { const char *vrfname; - char msdp_mesh_source_ip_xpath[XPATH_MAXLEN]; - char msdp_mesh_group_name_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(msdp_mesh_group_name_xpath)); - - snprintf(msdp_mesh_source_ip_xpath, sizeof(msdp_mesh_source_ip_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_source_ip_xpath, "/msdp-mesh-group/source-ip", - sizeof(msdp_mesh_source_ip_xpath)); + /* Create mesh group. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY, - argv[3]->arg); - nb_cli_enqueue_change(vty, msdp_mesh_source_ip_xpath, NB_OP_MODIFY, - argv[5]->arg); + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group_source, - no_ip_msdp_mesh_group_source_cmd, - "no ip msdp mesh-group WORD source [A.B.C.D]", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group source\n" - "mesh group name\n" - "mesh group source\n" - "mesh group local address\n") +DEFPY(no_ip_msdp_mesh_group_source, + no_ip_msdp_mesh_group_source_cmd, + "no ip msdp mesh-group WORD$gname source [A.B.C.D]", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group source\n" + "Mesh group name\n" + "Mesh group source\n" + "Mesh group local address\n") { const char *vrfname; - char msdp_mesh_xpath[XPATH_MAXLEN]; - char source_xpath[XPATH_MAXLEN]; - char group_member_xpath[XPATH_MAXLEN]; - char mesh_group_name_xpath[XPATH_MAXLEN]; - const char *mesh_group_name; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath)); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL); - snprintf(source_xpath, sizeof(source_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(source_xpath, "/msdp-mesh-group/source-ip", - sizeof(source_xpath)); + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); - snprintf(group_member_xpath, - sizeof(group_member_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(group_member_xpath, "/msdp-mesh-group/member-ip", - sizeof(group_member_xpath)); + /* + * If this is the last member, then we must remove the group altogether + * to not break legacy CLI behaviour. + */ + pim_cli_legacy_mesh_group_behavior(vty, gname); - snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(mesh_group_name_xpath)); - - if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath) - == true) { - mesh_group_name = yang_dnode_get_string(running_config->dnode, - mesh_group_name_xpath); - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if (!yang_dnode_exists(vty->candidate_config->dnode, - group_member_xpath)) { - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, - NULL); - return nb_cli_apply_changes(vty, NULL); - } - nb_cli_enqueue_change(vty, source_xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_ip_msdp_mesh_group, - no_ip_msdp_mesh_group_cmd, - "no ip msdp mesh-group [WORD]", - NO_STR - IP_STR - CFG_MSDP_STR - "Delete MSDP mesh-group\n" - "mesh group name") +DEFPY(no_ip_msdp_mesh_group, + no_ip_msdp_mesh_group_cmd, + "no ip msdp mesh-group WORD$gname", + NO_STR + IP_STR + CFG_MSDP_STR + "Delete MSDP mesh-group\n" + "Mesh group name") { const char *vrfname; - const char *mesh_group_name; - char xpath[XPATH_MAXLEN]; - char msdp_mesh_xpath[XPATH_MAXLEN]; + char xpath_value[XPATH_MAXLEN]; vrfname = pim_cli_get_vrf_name(vty); if (vrfname == NULL) return CMD_WARNING_CONFIG_FAILED; - if (argc == 5) { - snprintf(xpath, sizeof(xpath), FRR_PIM_AF_XPATH, "frr-pim:pimd", - "pim", vrfname, "frr-routing:ipv4"); - strlcat(xpath, "/msdp-mesh-group/mesh-group-name", - sizeof(xpath)); - - if (yang_dnode_exists(running_config->dnode, xpath) == true) { - mesh_group_name = - yang_dnode_get_string(running_config->dnode, - xpath); - - if (strcmp(mesh_group_name, argv[4]->arg)) { - vty_out(vty, "%% mesh-group does not exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - } - - snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath), - FRR_PIM_AF_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4"); - strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath)); + /* Get mesh group base XPath. */ + snprintf(xpath_value, sizeof(xpath_value), + FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']", + "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname); + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) + return CMD_SUCCESS; - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -static void print_empty_json_obj(struct vty *vty) -{ - json_object *json; - json = json_object_new_object(); - vty_out(vty, "%s\n", - json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); -} - -static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, - bool uj) +static void ip_msdp_show_mesh_group(struct vty *vty, struct pim_msdp_mg *mg, + struct json_object *json) { struct listnode *mbrnode; struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; char mbr_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; char state_str[PIM_MSDP_STATE_STRLEN]; enum pim_msdp_peer_state state; - json_object *json = NULL; json_object *json_mg_row = NULL; json_object *json_members = NULL; json_object *json_row = NULL; - if (!mg) { - if (uj) - print_empty_json_obj(vty); - return; - } - pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str)); - if (uj) { - json = json_object_new_object(); + if (json) { /* currently there is only one mesh group but we should still * make * it a dict with mg-name as key */ @@ -10005,7 +9947,7 @@ static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, state = PIM_MSDP_DISABLED; } pim_msdp_state_dump(state, state_str, sizeof(state_str)); - if (uj) { + if (json) { json_row = json_object_new_object(); json_object_string_add(json_row, "member", mbr_str); json_object_string_add(json_row, "state", state_str); @@ -10020,12 +9962,8 @@ static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, } } - if (uj) { + if (json) json_object_object_add(json, mg->mesh_group_name, json_mg_row); - vty_out(vty, "%s\n", json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); - } } DEFUN (show_ip_msdp_mesh_group, @@ -10040,12 +9978,34 @@ DEFUN (show_ip_msdp_mesh_group, { bool uj = use_json(argc, argv); int idx = 2; + struct pim_msdp_mg *mg; struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct pim_instance *pim = vrf->info; + struct json_object *json = NULL; if (!vrf) return CMD_WARNING; - ip_msdp_show_mesh_group(vrf->info, vty, uj); + /* Quick case: list is empty. */ + if (SLIST_EMPTY(&pim->msdp.mglist)) { + if (uj) + vty_out(vty, "{}\n"); + + return CMD_SUCCESS; + } + + if (uj) + json = json_object_new_object(); + + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) + ip_msdp_show_mesh_group(vty, mg, json); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } return CMD_SUCCESS; } @@ -10061,23 +10021,32 @@ DEFUN (show_ip_msdp_mesh_group_vrf_all, JSON_STR) { bool uj = use_json(argc, argv); + struct json_object *json = NULL, *vrf_json = NULL; + struct pim_instance *pim; + struct pim_msdp_mg *mg; struct vrf *vrf; - bool first = true; if (uj) - vty_out(vty, "{ "); + json = json_object_new_object(); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { if (uj) { - if (!first) - vty_out(vty, ", "); - vty_out(vty, " \"%s\": ", vrf->name); - first = false; + vrf_json = json_object_new_object(); + json_object_object_add(json, vrf->name, vrf_json); } else vty_out(vty, "VRF: %s\n", vrf->name); - ip_msdp_show_mesh_group(vrf->info, vty, uj); + + pim = vrf->info; + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) + ip_msdp_show_mesh_group(vty, mg, vrf_json); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); } - if (uj) - vty_out(vty, "}\n"); return CMD_SUCCESS; } diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 9cf73c38c..095c6de54 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -58,8 +58,6 @@ static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags); static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2); static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr); -static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, - struct pim_msdp_mg_mbr *mbr); /************************ SA cache management ******************************/ static void pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, @@ -1252,27 +1250,33 @@ static int pim_msdp_peer_comp(const void *p1, const void *p2) } /************************** Mesh group management **************************/ -static void pim_msdp_mg_free(struct pim_instance *pim) +void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp) { - struct pim_msdp_mg *mg = pim->msdp.mg; + struct pim_msdp_mg_mbr *mbr; + struct listnode *n, *nn; - /* If the mesh-group has valid member or src_ip don't delete it */ - if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) { + if (*mgp == NULL) return; - } + + /* SIP is being removed - tear down all active peer sessions */ + for (ALL_LIST_ELEMENTS((*mgp)->mbr_list, n, nn, mbr)) + pim_msdp_mg_mbr_del((*mgp), mbr); if (PIM_DEBUG_MSDP_EVENTS) { - zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name); + zlog_debug("MSDP mesh-group %s deleted", + (*mgp)->mesh_group_name); } - XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name); - if (mg->mbr_list) - list_delete(&mg->mbr_list); + XFREE(MTYPE_PIM_MSDP_MG_NAME, (*mgp)->mesh_group_name); + + if ((*mgp)->mbr_list) + list_delete(&(*mgp)->mbr_list); - XFREE(MTYPE_PIM_MSDP_MG, pim->msdp.mg); + XFREE(MTYPE_PIM_MSDP_MG, (*mgp)); } -static struct pim_msdp_mg *pim_msdp_mg_new(const char *mesh_group_name) +struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim, + const char *mesh_group_name) { struct pim_msdp_mg *mg; @@ -1286,52 +1290,10 @@ static struct pim_msdp_mg *pim_msdp_mg_new(const char *mesh_group_name) if (PIM_DEBUG_MSDP_EVENTS) { zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name); } - return mg; -} - -enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim, - const char *mesh_group_name) -{ - struct pim_msdp_mg *mg = pim->msdp.mg; - struct pim_msdp_mg_mbr *mbr; - - if (!mg - || (mesh_group_name - && strcmp(mg->mesh_group_name, mesh_group_name))) { - return PIM_MSDP_ERR_NO_MG; - } - - /* delete all the mesh-group members */ - while (!list_isempty(mg->mbr_list)) { - mbr = listnode_head(mg->mbr_list); - pim_msdp_mg_mbr_do_del(mg, mbr); - } - - /* clear src ip */ - mg->src_ip.s_addr = INADDR_ANY; - - /* free up the mesh-group */ - pim_msdp_mg_free(pim); - return PIM_MSDP_ERR_NONE; -} - -static enum pim_msdp_err pim_msdp_mg_add(struct pim_instance *pim, - const char *mesh_group_name) -{ - if (pim->msdp.mg) { - if (!strcmp(pim->msdp.mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NONE; - } - /* currently only one mesh-group can exist at a time */ - return PIM_MSDP_ERR_MAX_MESH_GROUPS; - } - pim->msdp.mg = pim_msdp_mg_new(mesh_group_name); - if (!pim->msdp.mg) { - return PIM_MSDP_ERR_OOM; - } + SLIST_INSERT_HEAD(&pim->msdp.mglist, mg, mg_entry); - return PIM_MSDP_ERR_NONE; + return mg; } static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2) @@ -1353,66 +1315,7 @@ static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr) XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr); } -static struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_find(struct pim_instance *pim, - struct in_addr mbr_ip) -{ - struct pim_msdp_mg_mbr *mbr; - struct listnode *mbr_node; - - if (!pim->msdp.mg) { - return NULL; - } - /* we can move this to a hash but considering that number of peers in - * a mesh-group that seems like bit of an overkill */ - for (ALL_LIST_ELEMENTS_RO(pim->msdp.mg->mbr_list, mbr_node, mbr)) { - if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) { - return mbr; - } - } - return mbr; -} - -enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip) -{ - int rc; - struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg; - - rc = pim_msdp_mg_add(pim, mesh_group_name); - if (rc != PIM_MSDP_ERR_NONE) { - return rc; - } - - mg = pim->msdp.mg; - mbr = pim_msdp_mg_mbr_find(pim, mbr_ip); - if (mbr) { - return PIM_MSDP_ERR_MG_MBR_EXISTS; - } - - mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr)); - mbr->mbr_ip = mbr_ip; - listnode_add_sort(mg->mbr_list, mbr); - - /* if valid SIP has been configured add peer session */ - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_msdp_peer_add(pim, mbr_ip, mg->src_ip, mesh_group_name, - &mbr->mp); - } - - if (PIM_DEBUG_MSDP_EVENTS) { - char ip_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str)); - zlog_debug("MSDP mesh-group %s mbr %s created", - mg->mesh_group_name, ip_str); - } - ++mg->mbr_cnt; - return PIM_MSDP_ERR_NONE; -} - -static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, - struct pim_msdp_mg_mbr *mbr) +void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr) { /* Delete active peer session if any */ if (mbr->mp) { @@ -1432,34 +1335,10 @@ static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, } } -enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip) -{ - struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; - - if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NO_MG; - } - - mbr = pim_msdp_mg_mbr_find(pim, mbr_ip); - if (!mbr) { - return PIM_MSDP_ERR_NO_MG_MBR; - } - - pim_msdp_mg_mbr_do_del(mg, mbr); - /* if there are no references to the mg free it */ - pim_msdp_mg_free(pim); - - return PIM_MSDP_ERR_NONE; -} - -static void pim_msdp_mg_src_do_del(struct pim_instance *pim) +static void pim_msdp_src_del(struct pim_msdp_mg *mg) { struct pim_msdp_mg_mbr *mbr; struct listnode *mbr_node; - struct pim_msdp_mg *mg = pim->msdp.mg; /* SIP is being removed - tear down all active peer sessions */ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { @@ -1474,91 +1353,38 @@ static void pim_msdp_mg_src_do_del(struct pim_instance *pim) } } -enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim, - const char *mesh_group_name) -{ - struct pim_msdp_mg *mg = pim->msdp.mg; - - if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) { - return PIM_MSDP_ERR_NO_MG; - } - - if (mg->src_ip.s_addr != INADDR_ANY) { - mg->src_ip.s_addr = INADDR_ANY; - pim_msdp_mg_src_do_del(pim); - /* if there are no references to the mg free it */ - pim_msdp_mg_free(pim); - } - return PIM_MSDP_ERR_NONE; -} - -enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr src_ip) -{ - int rc; - struct pim_msdp_mg_mbr *mbr; - struct listnode *mbr_node; - struct pim_msdp_mg *mg; - - if (src_ip.s_addr == INADDR_ANY) { - pim_msdp_mg_src_del(pim, mesh_group_name); - return PIM_MSDP_ERR_NONE; - } - - rc = pim_msdp_mg_add(pim, mesh_group_name); - if (rc != PIM_MSDP_ERR_NONE) { - return rc; - } - - mg = pim->msdp.mg; - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_msdp_mg_src_do_del(pim); - } - mg->src_ip = src_ip; - - for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) { - pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, mesh_group_name, - &mbr->mp); - } - - if (PIM_DEBUG_MSDP_EVENTS) { - char ip_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str)); - zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, - ip_str); - } - return PIM_MSDP_ERR_NONE; -} - /*********************** MSDP feature APIs *********************************/ int pim_msdp_config_write(struct pim_instance *pim, struct vty *vty, const char *spaces) { + struct pim_msdp_mg *mg; struct listnode *mbrnode; struct pim_msdp_mg_mbr *mbr; - struct pim_msdp_mg *mg = pim->msdp.mg; char mbr_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; int count = 0; - if (!mg) { + if (SLIST_EMPTY(&pim->msdp.mglist)) return count; - } - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str)); - vty_out(vty, "%sip msdp mesh-group %s source %s\n", spaces, - mg->mesh_group_name, src_str); - ++count; - } + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) { + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_inet4_dump("<src?>", mg->src_ip, src_str, + sizeof(src_str)); + vty_out(vty, "%sip msdp mesh-group %s source %s\n", + spaces, mg->mesh_group_name, src_str); + ++count; + } - for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { - pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str)); - vty_out(vty, "%sip msdp mesh-group %s member %s\n", spaces, - mg->mesh_group_name, mbr_str); - ++count; + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) { + pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, + sizeof(mbr_str)); + vty_out(vty, "%sip msdp mesh-group %s member %s\n", + spaces, mg->mesh_group_name, mbr_str); + ++count; + } } + return count; } @@ -1623,11 +1449,13 @@ void pim_msdp_init(struct pim_instance *pim, struct thread_master *master) /* counterpart to MSDP init; XXX: unused currently */ void pim_msdp_exit(struct pim_instance *pim) { - pim_msdp_sa_adv_timer_setup(pim, false); + struct pim_msdp_mg *mg; - /* XXX: stop listener and delete all peer sessions */ + pim_msdp_sa_adv_timer_setup(pim, false); - pim_msdp_mg_free(pim); + /* Stop listener and delete all peer sessions */ + while ((mg = SLIST_FIRST(&pim->msdp.mglist)) != NULL) + pim_msdp_mg_free(pim, &mg); if (pim->msdp.peer_hash) { hash_clean(pim->msdp.peer_hash, NULL); @@ -1653,3 +1481,57 @@ void pim_msdp_exit(struct pim_instance *pim) stream_free(pim->msdp.work_obuf); pim->msdp.work_obuf = NULL; } + +void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg, + struct in_addr *ai) +{ + struct pim_msdp_mg_mbr *mbr; + struct listnode *mbr_node; + + /* Stop all connections and remove data structures. */ + pim_msdp_src_del(mg); + + /* Set new address. */ + mg->src_ip = *ai; + + /* No new address, disable everyone. */ + if (ai->s_addr == INADDR_ANY) { + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s src unset", + mg->mesh_group_name); + return; + } + + /* Create data structures and start TCP connection. */ + for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) + pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, + mg->mesh_group_name, &mbr->mp); + + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s src %pI4 set", + mg->mesh_group_name, &mg->src_ip); +} + +struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim, + struct pim_msdp_mg *mg, + struct in_addr *ia) +{ + struct pim_msdp_mg_mbr *mbr; + + mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr)); + mbr->mbr_ip = *ia; + listnode_add_sort(mg->mbr_list, mbr); + + /* if valid SIP has been configured add peer session */ + if (mg->src_ip.s_addr != INADDR_ANY) + pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, + mg->mesh_group_name, &mbr->mp); + + if (PIM_DEBUG_MSDP_EVENTS) + zlog_debug("MSDP mesh-group %s mbr %pI4 created", + mg->mesh_group_name, &mbr->mbr_ip); + + ++mg->mbr_cnt; + + return mbr; +} diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index 4d01880fb..bb7ee01ad 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -19,6 +19,8 @@ #ifndef PIM_MSDP_H #define PIM_MSDP_H +#include "lib/openbsd-queue.h" + enum pim_msdp_peer_state { PIM_MSDP_DISABLED, PIM_MSDP_INACTIVE, @@ -160,8 +162,13 @@ struct pim_msdp_mg { struct in_addr src_ip; uint32_t mbr_cnt; struct list *mbr_list; + + /** Belongs to PIM instance list. */ + SLIST_ENTRY(pim_msdp_mg) mg_entry; }; +SLIST_HEAD(pim_mesh_group_list, pim_msdp_mg); + enum pim_msdp_flags { PIM_MSDPF_NONE = 0, PIM_MSDPF_ENABLE = (1 << 0), @@ -196,8 +203,8 @@ struct pim_msdp { struct in_addr originator_id; - /* currently only one mesh-group is supported - so just stash it here */ - struct pim_msdp_mg *mg; + /** List of mesh groups. */ + struct pim_mesh_group_list mglist; }; #define PIM_MSDP_PEER_READ_ON(mp) \ @@ -246,17 +253,39 @@ bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp); void pim_msdp_up_join_state_changed(struct pim_instance *pim, struct pim_upstream *xg_up); void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg); -enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip); -enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr mbr_ip); -enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim, - const char *mesh_group_name); -enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim, - const char *mesh_group_name, - struct in_addr src_ip); enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim, const char *mesh_group_name); + +/** + * Allocates a new mesh group data structure under PIM instance. + */ +struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim, + const char *mesh_group_name); +/** + * Deallocates mesh group data structure under PIM instance. + */ +void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp); + +/** + * Change the source address of a mesh group peers. It will do the following: + * - Close all peers TCP connections + * - Recreate peers data structure + * - Start TCP connections with new local address. + */ +void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg, + struct in_addr *ai); + +/** + * Add new peer to mesh group and starts the connection if source address is + * configured. + */ +struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim, + struct pim_msdp_mg *mg, + struct in_addr *ia); + +/** + * Stops the connection and removes the peer data structures. + */ +void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr); + #endif diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c index 37c539883..ea53f1ef1 100644 --- a/pimd/pim_nb.c +++ b/pimd/pim_nb.c @@ -118,31 +118,24 @@ const struct frr_yang_module_info frr_pim_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy, + .create = pim_msdp_mesh_group_create, + .destroy = pim_msdp_mesh_group_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source", .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy, + .modify = pim_msdp_mesh_group_source_modify, + .destroy = pim_msdp_mesh_group_source_destroy, } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip", + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members", .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy, + .create = pim_msdp_mesh_group_members_create, + .destroy = pim_msdp_mesh_group_members_destroy, } }, { diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 440384e45..1959b403f 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -60,22 +60,12 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ssm_pingd_source_ip_destroy( struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy( - struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_create(struct nb_cb_create_args *args); +int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args); +int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args); +int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args); +int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_destroy( diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 11e8da3b8..b70656ea7 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -243,147 +243,6 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, const char *plist, return ret; } -static int ip_no_msdp_mesh_group_cmd_worker(struct pim_instance *pim, - const char *mg, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_del(pim, mg); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "mesh-group source del failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr mbr_ip, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - int ret = NB_OK; - - result = pim_msdp_mg_mbr_add(pim, mg, mbr_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_OOM: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% Out of memory"); - break; - case PIM_MSDP_ERR_MG_MBR_EXISTS: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% mesh-group member exists"); - break; - case PIM_MSDP_ERR_MAX_MESH_GROUPS: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% Only one mesh-group allowed currently"); - break; - default: - ret = NB_ERR; - snprintf(errmsg, errmsg_len, - "%% member add failed"); - } - - return ret; -} - -static int ip_no_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr mbr_ip, - char *errmsg, - size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_mbr_del(pim, mg, mbr_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - case PIM_MSDP_ERR_NO_MG_MBR: - snprintf(errmsg, errmsg_len, - "%% mesh-group member does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% mesh-group member del failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim, - const char *mg, - struct in_addr src_ip, - char *errmsg, size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_src_add(pim, mg, src_ip); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_OOM: - snprintf(errmsg, errmsg_len, - "%% Out of memory"); - break; - case PIM_MSDP_ERR_MAX_MESH_GROUPS: - snprintf(errmsg, errmsg_len, - "%% Only one mesh-group allowed currently"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% source add failed"); - } - - return result ? NB_ERR : NB_OK; -} - -static int ip_no_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim, - const char *mg, - char *errmsg, - size_t errmsg_len) -{ - enum pim_msdp_err result; - - result = pim_msdp_mg_src_del(pim, mg); - - switch (result) { - case PIM_MSDP_ERR_NONE: - break; - case PIM_MSDP_ERR_NO_MG: - snprintf(errmsg, errmsg_len, - "%% mesh-group does not exist"); - break; - default: - snprintf(errmsg, errmsg_len, - "%% mesh-group source del failed"); - } - - return result ? NB_ERR : NB_OK; -} - static int ip_msdp_peer_cmd_worker(struct pim_instance *pim, struct in_addr peer_addr, struct in_addr local_addr, @@ -1146,29 +1005,13 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss } /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create( - struct nb_cb_create_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_create(struct nb_cb_create_args *args) { + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - int result; switch (args->event) { case NB_EV_VALIDATE: @@ -1177,67 +1020,29 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms break; case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, "mesh-group-name"); - - result = ip_no_msdp_mesh_group_cmd_worker(pim, mesh_group_name, - args->errmsg, - args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; - + mg = pim_msdp_mg_new(vrf->info, yang_dnode_get_string( + args->dnode, "./name")); + nb_running_set_entry(args->dnode, mg); break; } return NB_OK; } -/* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name - */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify( - struct nb_cb_modify_args *args) +int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args) { - const char *mesh_group_name; - const char *mesh_group_name_old; - char xpath[XPATH_MAXLEN]; + struct pim_msdp_mg *mg; + struct vrf *vrf; switch (args->event) { case NB_EV_VALIDATE: - mesh_group_name = yang_dnode_get_string(args->dnode, "."); - yang_dnode_get_path(args->dnode, xpath, sizeof(xpath)); - - if (yang_dnode_exists(running_config->dnode, xpath) == false) - break; - - mesh_group_name_old = yang_dnode_get_string( - running_config->dnode, - xpath); - if (strcmp(mesh_group_name, mesh_group_name_old)) { - /* currently only one mesh-group can exist at a time */ - snprintf(args->errmsg, args->errmsg_len, - "Only one mesh-group allowed currently"); - return NB_ERR_VALIDATION; - } - break; case NB_EV_PREPARE: case NB_EV_ABORT: - case NB_EV_APPLY: break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy( - struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: case NB_EV_APPLY: + mg = nb_running_unset_entry(args->dnode); + vrf = nb_running_get_entry(args->dnode, NULL, true); + pim_msdp_mg_free(vrf->info, &mg); break; } @@ -1245,16 +1050,15 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms } /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create( - struct nb_cb_create_args *args) +int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr mbr_ip; - enum pim_msdp_err result; + struct ipaddr ip; switch (args->event) { case NB_EV_VALIDATE: @@ -1262,33 +1066,24 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&mbr_ip, args->dnode, NULL); - - result = ip_msdp_mesh_group_member_cmd_worker( - pim, mesh_group_name, mbr_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + yang_dnode_get_ip(&ip, args->dnode, NULL); + pim_msdp_mg_src_add(vrf->info, mg, &ip.ip._v4_addr); break; } - return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr mbr_ip; - enum pim_msdp_err result; + struct in_addr addr; switch (args->event) { case NB_EV_VALIDATE: @@ -1296,36 +1091,30 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&mbr_ip, args->dnode, NULL); - - result = ip_no_msdp_mesh_group_member_cmd_worker( - pim, mesh_group_name, mbr_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + addr.s_addr = INADDR_ANY; + pim_msdp_mg_src_add(vrf->info, mg, &addr); break; } - return NB_OK; } + /* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify( - struct nb_cb_modify_args *args) +int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args) { + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr src_ip; - enum pim_msdp_err result; + struct ipaddr ip; switch (args->event) { case NB_EV_VALIDATE: @@ -1333,31 +1122,24 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - yang_dnode_get_ip(&src_ip, args->dnode, NULL); - - result = ip_msdp_mesh_group_source_cmd_worker( - pim, mesh_group_name, src_ip.ip._v4_addr, - args->errmsg, args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mg = nb_running_get_entry(args->dnode, NULL, true); + vrf_dnode = + yang_dnode_get_parent(args->dnode, "address-family"); + vrf = nb_running_get_entry(vrf_dnode, "../../", true); + yang_dnode_get_ip(&ip, args->dnode, "address"); + mbr = pim_msdp_mg_mbr_add(vrf->info, mg, &ip.ip._v4_addr); + nb_running_set_entry(args->dnode, mbr); break; } + return NB_OK; } -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy( - struct nb_cb_destroy_args *args) +int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args) { - struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - enum pim_msdp_err result; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; switch (args->event) { case NB_EV_VALIDATE: @@ -1365,20 +1147,13 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ms case NB_EV_ABORT: break; case NB_EV_APPLY: - vrf = nb_running_get_entry(args->dnode, NULL, true); - pim = vrf->info; - mesh_group_name = yang_dnode_get_string(args->dnode, - "../mesh-group-name"); - - result = ip_no_msdp_mesh_group_source_cmd_worker( - pim, mesh_group_name, args->errmsg, - args->errmsg_len); - - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; + mbr = nb_running_get_entry(args->dnode, NULL, true); + mg = nb_running_get_entry(args->dnode, "../", true); + pim_msdp_mg_mbr_del(mg, mbr); break; } + return NB_OK; } diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py new file mode 100755 index 000000000..07e4ab877 --- /dev/null +++ b/tests/topotests/lib/mcast-tester.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +""" +Subscribe to a multicast group so that the kernel sends an IGMP JOIN +for the multicast group we subscribed to. +""" + +import argparse +import os +import json +import socket +import subprocess +import struct +import sys +import time + +# +# Functions +# +def interface_name_to_index(name): + "Gets the interface index using its name. Returns None on failure." + interfaces = json.loads( + subprocess.check_output('ip -j link show', shell=True)) + + for interface in interfaces: + if interface['ifname'] == name: + return interface['ifindex'] + + return None + + +def multicast_join(sock, ifindex, group, port): + "Joins a multicast group." + mreq = struct.pack( + "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex + ) + + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((group, port)) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + + +# +# Main code. +# +parser = argparse.ArgumentParser(description="Multicast RX utility") +parser.add_argument('socket', help='Point to topotest UNIX socket') +parser.add_argument('group', help='Multicast IP') +parser.add_argument('interface', help='Interface name') +parser.add_argument( + '--send', + help='Transmit instead of join with interval (defaults to 0.7 sec)', + type=float, default=0) +args = parser.parse_args() + +ttl = 16 +port = 1000 + +# Get interface index/validate. +ifindex = interface_name_to_index(args.interface) +if ifindex is None: + sys.stderr.write('Interface {} does not exists\n'.format(args.interface)) + sys.exit(1) + +# We need root privileges to set up multicast. +if os.geteuid() != 0: + sys.stderr.write("ERROR: You must have root privileges\n") + sys.exit(1) + +# Wait for topotest to synchronize with us. +toposock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) +while True: + try: + toposock.connect(args.socket) + break + except ConnectionRefusedError: + time.sleep(1) + continue + +msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +if args.send > 0: + # Prepare multicast bit in that interface. + msock.setsockopt( + socket.SOL_SOCKET, 25, + struct.pack("%ds" % len(args.interface), + args.interface.encode('utf-8'))) + # Set packets TTL. + msock.setsockopt( + socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl)) + # Block to ensure packet send. + msock.setblocking(True) + # Set topotest socket non blocking so we can multiplex the main loop. + toposock.setblocking(False) +else: + multicast_join(msock, ifindex, args.group, port) + +counter = 0 +while True: + if args.send > 0: + msock.sendto(b"test %d" % counter, (args.group, port)) + counter += 1 + time.sleep(args.send) + + try: + data = toposock.recv(1) + if data == b'': + print(' -> Connection closed') + break + except BlockingIOError: + continue + +msock.close() + +sys.exit(0) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 553f2bc6c..4b0f07eb1 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -222,6 +222,22 @@ class Topogen(object): self.peern += 1 return self.gears[name] + def add_host(self, name, ip, defaultRoute): + """ + Adds a new host to the topology. This function has the following + parameters: + * `ip`: the peer address (e.g. '1.2.3.4/24') + * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1') + """ + if name is None: + name = "host{}".format(self.peern) + if name in self.gears: + raise KeyError("host already exists") + + self.gears[name] = TopoHost(self, name, ip=ip, defaultRoute=defaultRoute) + self.peern += 1 + return self.gears[name] + def add_link(self, node1, node2, ifname1=None, ifname2=None): """ Creates a connection between node1 and node2. The nodes can be the diff --git a/tests/topotests/msdp_mesh_topo1/__init__.py b/tests/topotests/msdp_mesh_topo1/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/__init__.py diff --git a/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf new file mode 100644 index 000000000..953d90aa0 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 10.254.254.2 remote-as 65000 + neighbor 10.254.254.2 update-source 10.254.254.1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf new file mode 100644 index 000000000..c1adbd544 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf @@ -0,0 +1,8 @@ +interface r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf new file mode 100644 index 000000000..49341efa5 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.1 +! +interface r1-eth0 + ip pim +! +interface r1-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.1 +ip msdp mesh-group mg-1 source 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r1/zebra.conf b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf new file mode 100644 index 000000000..42c850f00 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.2/24 +! +interface r1-eth1 + ip address 192.168.10.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf new file mode 100644 index 000000000..f442efc60 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf @@ -0,0 +1,10 @@ +router bgp 65000 + neighbor pg-1 peer-group + neighbor pg-1 update-source 10.254.254.1 + neighbor pg-1 remote-as 65000 + neighbor 10.254.254.1 peer-group pg-1 + neighbor 10.254.254.3 peer-group pg-1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf new file mode 100644 index 000000000..9e9ac5fb2 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf @@ -0,0 +1,13 @@ +interface r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf new file mode 100644 index 000000000..9005263ed --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf @@ -0,0 +1,14 @@ +interface lo + ip pim + ip pim use-source 10.254.254.2 +! +interface r2-eth0 + ip pim +! +interface r2-eth1 + ip pim +! +ip pim rp 10.254.254.2 +ip msdp mesh-group mg-1 source 10.254.254.2 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.3 diff --git a/tests/topotests/msdp_mesh_topo1/r2/zebra.conf b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf new file mode 100644 index 000000000..6b2619421 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.1/24 +! +interface r2-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf new file mode 100644 index 000000000..6c3f89ad9 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65000 + neighbor 192.168.2.1 remote-as 65000 + neighbor 192.168.2.1 update-source 10.254.254.3 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf new file mode 100644 index 000000000..7b7b1abe6 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf @@ -0,0 +1,8 @@ +interface r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 192.168.2.0/24 area 0.0.0.0 + redistribute connected +! diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf new file mode 100644 index 000000000..30e114856 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf @@ -0,0 +1,15 @@ +interface lo + ip pim + ip pim use-source 10.254.254.3 +! +interface r3-eth0 + ip pim +! +interface r3-eth1 + ip pim + ip igmp +! +ip pim rp 10.254.254.3 +ip msdp mesh-group mg-1 source 10.254.254.3 +ip msdp mesh-group mg-1 member 10.254.254.1 +ip msdp mesh-group mg-1 member 10.254.254.2 diff --git a/tests/topotests/msdp_mesh_topo1/r3/zebra.conf b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf new file mode 100644 index 000000000..a8a15f3c0 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf @@ -0,0 +1,11 @@ +ip forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +interface r3-eth1 + ip address 192.168.30.1/24 +! diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot new file mode 100644 index 000000000..8792e2c7b --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot @@ -0,0 +1,88 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="msdp_mesh_topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + h1 [ + shape=doubleoctagon + label="h1", + fillcolor="#4f4f4f", + style=filled, + ]; + h2 [ + shape=doubleoctagon + label="h2", + fillcolor="#4f4f4f", + style=filled, + ]; + + # Switches + s1 [ + shape=oval, + label="sw1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="sw2\n192.168.2.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + shape=oval, + label="sw3\n192.168.10.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + shape=oval, + label="sw3\n192.168.30.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.2"]; + r2 -- s1 [label="eth0\n.1"]; + + r2 -- s2 [label="eth1\n.1"]; + r3 -- s2 [label="eth0\n.2"]; + + r1 -- s3 [label="eth1\n.1"]; + h1 -- s3 [label="eth0\n.2"]; + + r3 -- s4 [label="eth1\n.1"]; + h2 -- s4 [label="eth0\n.2"]; +} diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png Binary files differnew file mode 100644 index 000000000..9a15b8b08 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py new file mode 100644 index 000000000..719ead091 --- /dev/null +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python + +# +# test_msdp_mesh_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (C) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_msdp_mesh_topo1.py: Test the FRR PIM MSDP mesh groups. +""" + +import os +import sys +import json +from functools import partial +import pytest +import socket + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pimd] + +# +# Test global variables: +# They are used to handle communicating with external application. +# +APP_SOCK_PATH = '/tmp/topotests/apps.sock' +HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py") +app_listener = None +app_clients = {} + +def listen_to_applications(): + "Start listening socket to connect with applications." + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) + sock.bind(APP_SOCK_PATH) + sock.listen(10) + global app_listener + app_listener = sock + +def accept_host(host): + "Accept connection from application running in hosts." + global app_listener, app_clients + conn = app_listener.accept() + app_clients[host] = { + 'fd': conn[0], + 'address': conn[1] + } + +def close_applications(): + "Signal applications to stop and close all sockets." + global app_listener, app_clients + + # Close listening socket. + app_listener.close() + + # Remove old socket. + try: + os.unlink(APP_SOCK_PATH) + except OSError: + pass + + # Close all host connections. + for host in ["h1", "h2"]: + if app_clients.get(host) is None: + continue + app_clients["h1"]["fd"].close() + + +class MSDPMeshTopo1(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + # Create stub networks for multicast traffic. + tgen.add_host("h1", "192.168.10.2/24", "192.168.10.1") + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + tgen.add_host("h2", "192.168.30.2/24", "192.168.30.1") + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(MSDPMeshTopo1, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + daemon_file = "{}/{}/ospfd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF, daemon_file) + + daemon_file = "{}/{}/pimd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_PIM, daemon_file) + + # Initialize all routers. + tgen.start_router() + + # Start applications socket. + listen_to_applications() + + +def test_wait_ospf_convergence(): + "Wait for OSPF to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info("waiting route {} in {}".format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + {route: [{"protocol": proto}]} + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route("r1", "ip", "10.254.254.2/32", "ospf") + # Wait for R1 <-> R3 convergence. + expect_loopback_route("r1", "ip", "10.254.254.3/32", "ospf") + + # Wait for R2 <-> R1 convergence. + expect_loopback_route("r2", "ip", "10.254.254.1/32", "ospf") + # Wait for R2 <-> R3 convergence. + expect_loopback_route("r2", "ip", "10.254.254.3/32", "ospf") + + # Wait for R3 <-> R1 convergence. + expect_loopback_route("r3", "ip", "10.254.254.1/32", "ospf") + # Wait for R3 <-> R2 convergence. + expect_loopback_route("r3", "ip", "10.254.254.2/32", "ospf") + + +def test_wait_msdp_convergence(): + "Wait for MSDP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP convergence") + + tgen.gears["h1"].run("{} --send='0.7' '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h1-eth0')) + accept_host("h1") + + tgen.gears["h2"].run("{} '{}' '{}' '{}' &".format( + HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h2-eth0')) + accept_host("h2") + + def expect_msdp_peer(router, peer, sa_count=0): + "Expect MSDP peer connection to be established with SA amount." + logger.info("waiting MSDP connection from peer {} on router {}".format(peer, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp peer json", + {peer: {"state": "established", "saCount": sa_count}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP connection failure'.format(router) + assert result is None, assertmsg + + # R1 peers. + expect_msdp_peer("r1", "10.254.254.2") + expect_msdp_peer("r1", "10.254.254.3") + + # R2 peers. + expect_msdp_peer("r2", "10.254.254.1", 1) + expect_msdp_peer("r2", "10.254.254.3") + + # R3 peers. + expect_msdp_peer("r3", "10.254.254.1", 1) + expect_msdp_peer("r3", "10.254.254.2") + + +def test_msdp_sa_configuration(): + "Expect the multicast traffic SA to be created" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test MSDP SA") + + def expect_msdp_sa(router, source, group, local, rp, spt_setup): + "Expect MSDP SA." + logger.info("waiting MSDP SA on router {}".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ip msdp sa json", + {group: {source: {"local": local, "rp": rp, "sptSetup": spt_setup}}} + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = '"{}" MSDP SA failure'.format(router) + assert result is None, assertmsg + + source = "192.168.10.2" + group = "229.0.1.10" + rp = "10.254.254.1" + + # R1 SA. + expect_msdp_sa("r1", source, group, "yes", "-", "-") + + # R2 SA. + expect_msdp_sa("r2", source, group, "no", rp, "no") + + # R3 peers. + expect_msdp_sa("r3", source, group, "no", rp, "yes") + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + close_applications() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 70adb37b2..4c4819ac2 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -174,26 +174,36 @@ module frr-pim { "Enable ssmpingd operation."; } - container msdp-mesh-group { - presence - "Configure MSDP mesh-group."; + list msdp-mesh-groups { + key "name"; + description + "RFC 3618 Section 10.2. MSDP mesh-group semantics - leaf mesh-group-name { - type string; + Groups multiple MSDP peers to reduce SA flooding typically used + in intra-domain settings."; + + leaf name { + type string { + length 1..64; + } description - "MSDP mesh group name."; + "The mesh group name."; } - leaf-list member-ip { + leaf source { type inet:ip-address; description - "Peer ip address."; + "Source IP address for the TCP connections."; } - leaf source-ip { - type inet:ip-address; - description - "Source ip address for the TCP connection."; + list members { + key "address"; + + leaf address { + type inet:ip-address; + description + "Peer member IP address."; + } } } |