From 0213a658006216ddcd796b77c040797b1f5a01a4 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 20 Apr 2021 14:54:01 -0300 Subject: [PATCH 1/5] yang: rework PIM MSDP mesh group Allow PIM MSDP configuration to support multiple MSDP mesh groups per PIM instance. Signed-off-by: Rafael Zalamena --- yang/frr-pim.yang | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 70adb37b26..4c4819ac25 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."; + } } } From e2809e618bb18cddb18efbbc1d743490db7596b6 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 20 Apr 2021 14:54:09 -0300 Subject: [PATCH 2/5] pimd: rework MSDP mesh-group code Fully utilize the northbound to hold pointers to our private data instead of searching for data structures every time we need to change a configuration. Highlights: * Support multiple mesh groups per PIM instance (instead of one) * Use DEFPY instead of DEFUN to reduce code complexity * Use northbound private pointers to store data structures * Reduce callback names size Signed-off-by: Rafael Zalamena --- pimd/pim_cmd.c | 489 ++++++++++++++++++++----------------------- pimd/pim_msdp.c | 318 +++++++++------------------- pimd/pim_msdp.h | 56 +++-- pimd/pim_nb.c | 25 +-- pimd/pim_nb.h | 22 +- pimd/pim_nb_config.c | 395 ++++++++-------------------------- 6 files changed, 472 insertions(+), 833 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index f6072b1771..b3d4444652 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, - 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); - } - - vty_out(vty, "%% mesh-group member does not exist\n"); - - return CMD_SUCCESS; -} - -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") -{ - const char *vrfname; - char msdp_mesh_source_ip_xpath[XPATH_MAXLEN]; - char msdp_mesh_group_name_xpath[XPATH_MAXLEN]; - - vrfname = pim_cli_get_vrf_name(vty); - if (vrfname == NULL) + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) { + vty_out(vty, "%% mesh-group does not exist\n"); 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)); - - 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); - - 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") -{ - 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; - - 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)); - - 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)); - - 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)); - - 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; - } } + /* 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, - group_member_xpath)) { - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, - NULL); - return nb_cli_apply_changes(vty, NULL); + xpath_member_value)) { + vty_out(vty, "%% mesh-group member does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; } - nb_cli_enqueue_change(vty, source_xpath, NB_OP_DESTROY, NULL); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); + + /* + * 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 (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(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; - 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)); + /* 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); - if (yang_dnode_exists(running_config->dnode, xpath) == true) { - mesh_group_name = - yang_dnode_get_string(running_config->dnode, - xpath); + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str); - 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)); - - nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -static void print_empty_json_obj(struct vty *vty) +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") { - 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); + const char *vrfname; + char xpath_value[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + /* 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); + + /* Create mesh group member. */ + strlcat(xpath_value, "/source", sizeof(xpath_value)); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL); + + /* + * 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); } -static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty, - bool uj) +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; + char xpath_value[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + /* 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, xpath_value, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +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("", 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 9cf73c38c3..6de793ff4d 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_do_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); - XFREE(MTYPE_PIM_MSDP_MG, pim->msdp.mg); + if ((*mgp)->mbr_list) + list_delete(&(*mgp)->mbr_list); + + 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,54 +1290,12 @@ 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); } + + SLIST_INSERT_HEAD(&pim->msdp.mglist, mg, mg_entry); + 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; - } - - return PIM_MSDP_ERR_NONE; -} - static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2) { const struct pim_msdp_mg_mbr *mbr1 = p1; @@ -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_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_do_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_mg_src_do_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("", 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; + + SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) { + if (mg->src_ip.s_addr != INADDR_ANY) { + pim_inet4_dump("", 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_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; + } } - if (mg->src_ip.s_addr != INADDR_ANY) { - pim_inet4_dump("", 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_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) { + struct pim_msdp_mg *mg; + pim_msdp_sa_adv_timer_setup(pim, false); - /* XXX: stop listener and delete all peer sessions */ - - 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_change_source(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_mg_src_do_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_add_peer(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 4d01880fbf..f4d58cd56b 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,40 @@ 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_change_source(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_add_peer(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_do_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 37c539883d..ea53f1ef12 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 440384e45c..1959b403ff 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 11e8da3b87..10a83e889a 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,16 +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"); + mg = pim_msdp_mg_new(vrf->info, yang_dnode_get_string( + args->dnode, "./name")); + nb_running_set_entry(args->dnode, mg); + break; + } - result = ip_no_msdp_mesh_group_cmd_worker(pim, mesh_group_name, - args->errmsg, - args->errmsg_len); + return NB_OK; +} - if (result != PIM_MSDP_ERR_NONE) - return NB_ERR_INCONSISTENCY; +int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args) +{ + struct pim_msdp_mg *mg; + struct vrf *vrf; + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + 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; } @@ -1194,67 +1050,71 @@ 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/mesh-group-name + * 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_mesh_group_name_modify( - struct nb_cb_modify_args *args) +int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args) { - const char *mesh_group_name; - const char *mesh_group_name_old; - char xpath[XPATH_MAXLEN]; + const struct lyd_node *vrf_dnode; + struct pim_msdp_mg *mg; + struct vrf *vrf; + struct ipaddr ip; - 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: + break; case NB_EV_APPLY: + 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_change_source(vrf->info, mg, &ip.ip._v4_addr); break; } - return NB_OK; } +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 in_addr addr; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + 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_change_source(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/member-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_member_ip_create( - struct nb_cb_create_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 mbr_ip; - enum pim_msdp_err result; + struct ipaddr ip; switch (args->event) { case NB_EV_VALIDATE: @@ -1262,33 +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(&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, "address"); + mbr = pim_msdp_mg_add_peer(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_member_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; - struct ipaddr mbr_ip; - enum pim_msdp_err result; + struct pim_msdp_mg_mbr *mbr; + struct pim_msdp_mg *mg; switch (args->event) { case NB_EV_VALIDATE: @@ -1296,92 +1147,16 @@ 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; + mbr = nb_running_get_entry(args->dnode, NULL, true); + mg = nb_running_get_entry(args->dnode, "../", true); + pim_msdp_mg_mbr_do_del(mg, mbr); break; } return NB_OK; } -/* - * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip - */ -int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify( - struct nb_cb_modify_args *args) -{ - struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - struct ipaddr src_ip; - enum pim_msdp_err result; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - 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; - - 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) -{ - struct vrf *vrf; - struct pim_instance *pim; - const char *mesh_group_name; - enum pim_msdp_err result; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - 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; - - break; - } - return NB_OK; -} - /* * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-peer */ From ab59579a124fa41d9efd0345761ef4df0dea638d Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 20 Apr 2021 14:54:20 -0300 Subject: [PATCH 3/5] topotests: support adding hosts Add API to topogen so we can build topology with simple hosts instead of routers. Signed-off-by: Rafael Zalamena --- tests/topotests/lib/topogen.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 553f2bc6cf..4b0f07eb1e 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 From 1771900c0266b9b257dd642f0f680a1e38925f25 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 20 Apr 2021 14:54:27 -0300 Subject: [PATCH 4/5] topotests: new test topology for MSDP Add basic topology test for MSDP meshed groups. Signed-off-by: Rafael Zalamena --- tests/topotests/lib/mcast-tester.py | 129 ++++++++ tests/topotests/msdp_mesh_topo1/__init__.py | 0 tests/topotests/msdp_mesh_topo1/r1/bgpd.conf | 7 + tests/topotests/msdp_mesh_topo1/r1/ospfd.conf | 8 + tests/topotests/msdp_mesh_topo1/r1/pimd.conf | 15 + tests/topotests/msdp_mesh_topo1/r1/zebra.conf | 11 + tests/topotests/msdp_mesh_topo1/r2/bgpd.conf | 10 + tests/topotests/msdp_mesh_topo1/r2/ospfd.conf | 13 + tests/topotests/msdp_mesh_topo1/r2/pimd.conf | 14 + tests/topotests/msdp_mesh_topo1/r2/zebra.conf | 11 + tests/topotests/msdp_mesh_topo1/r3/bgpd.conf | 7 + tests/topotests/msdp_mesh_topo1/r3/ospfd.conf | 8 + tests/topotests/msdp_mesh_topo1/r3/pimd.conf | 15 + tests/topotests/msdp_mesh_topo1/r3/zebra.conf | 11 + .../msdp_mesh_topo1/test_msdp_mesh_topo1.dot | 88 ++++++ .../msdp_mesh_topo1/test_msdp_mesh_topo1.png | Bin 0 -> 35201 bytes .../msdp_mesh_topo1/test_msdp_mesh_topo1.py | 296 ++++++++++++++++++ 17 files changed, 643 insertions(+) create mode 100755 tests/topotests/lib/mcast-tester.py create mode 100644 tests/topotests/msdp_mesh_topo1/__init__.py create mode 100644 tests/topotests/msdp_mesh_topo1/r1/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r1/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r2/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/bgpd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/ospfd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/pimd.conf create mode 100644 tests/topotests/msdp_mesh_topo1/r3/zebra.conf create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png create mode 100644 tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py new file mode 100755 index 0000000000..07e4ab8773 --- /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/msdp_mesh_topo1/__init__.py b/tests/topotests/msdp_mesh_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 0000000000..953d90aa03 --- /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 0000000000..c1adbd5440 --- /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 0000000000..49341efa57 --- /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 0000000000..42c850f00f --- /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 0000000000..f442efc60f --- /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 0000000000..9e9ac5fb2e --- /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 0000000000..9005263ed7 --- /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 0000000000..6b26194218 --- /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 0000000000..6c3f89ad97 --- /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 0000000000..7b7b1abe62 --- /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 0000000000..30e1148561 --- /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 0000000000..a8a15f3c0f --- /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 0000000000..8792e2c7bb --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..9a15b8b088f50394504706bf69abbc9da25a54eb GIT binary patch literal 35201 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 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)) From fb35f654e3e31a762b1b453efc6248df78c1717e Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Wed, 9 Jun 2021 12:40:04 -0300 Subject: [PATCH 5/5] pimd: rename some MSDP functions Rename and shorten function names to make them uniform. Signed-off-by: Rafael Zalamena --- pimd/pim_msdp.c | 18 +++++++++--------- pimd/pim_msdp.h | 13 ++++++------- pimd/pim_nb_config.c | 8 ++++---- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 6de793ff4d..095c6de549 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -1260,7 +1260,7 @@ void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp) /* SIP is being removed - tear down all active peer sessions */ for (ALL_LIST_ELEMENTS((*mgp)->mbr_list, n, nn, mbr)) - pim_msdp_mg_mbr_do_del((*mgp), mbr); + pim_msdp_mg_mbr_del((*mgp), mbr); if (PIM_DEBUG_MSDP_EVENTS) { zlog_debug("MSDP mesh-group %s deleted", @@ -1315,7 +1315,7 @@ static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr) XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr); } -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) { @@ -1335,7 +1335,7 @@ void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr) } } -static void pim_msdp_mg_src_do_del(struct pim_msdp_mg *mg) +static void pim_msdp_src_del(struct pim_msdp_mg *mg) { struct pim_msdp_mg_mbr *mbr; struct listnode *mbr_node; @@ -1482,14 +1482,14 @@ void pim_msdp_exit(struct pim_instance *pim) pim->msdp.work_obuf = NULL; } -void pim_msdp_mg_change_source(struct pim_instance *pim, struct pim_msdp_mg *mg, - struct in_addr *ai) +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_mg_src_do_del(mg); + pim_msdp_src_del(mg); /* Set new address. */ mg->src_ip = *ai; @@ -1512,9 +1512,9 @@ void pim_msdp_mg_change_source(struct pim_instance *pim, struct pim_msdp_mg *mg, mg->mesh_group_name, &mg->src_ip); } -struct pim_msdp_mg_mbr *pim_msdp_mg_add_peer(struct pim_instance *pim, - struct pim_msdp_mg *mg, - struct in_addr *ia) +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; diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index f4d58cd56b..bb7ee01ad8 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -272,21 +272,20 @@ void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp); * - Recreate peers data structure * - Start TCP connections with new local address. */ -void pim_msdp_mg_change_source(struct pim_instance *pim, struct pim_msdp_mg *mg, - struct in_addr *ai); +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_add_peer(struct pim_instance *pim, - struct pim_msdp_mg *mg, - struct in_addr *ia); +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_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); #endif diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 10a83e889a..b70656ea7b 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1072,7 +1072,7 @@ int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args) vrf = nb_running_get_entry(vrf_dnode, "../../", true); yang_dnode_get_ip(&ip, args->dnode, NULL); - pim_msdp_mg_change_source(vrf->info, mg, &ip.ip._v4_addr); + pim_msdp_mg_src_add(vrf->info, mg, &ip.ip._v4_addr); break; } return NB_OK; @@ -1097,7 +1097,7 @@ int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args) vrf = nb_running_get_entry(vrf_dnode, "../../", true); addr.s_addr = INADDR_ANY; - pim_msdp_mg_change_source(vrf->info, mg, &addr); + pim_msdp_mg_src_add(vrf->info, mg, &addr); break; } return NB_OK; @@ -1128,7 +1128,7 @@ int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args) vrf = nb_running_get_entry(vrf_dnode, "../../", true); yang_dnode_get_ip(&ip, args->dnode, "address"); - mbr = pim_msdp_mg_add_peer(vrf->info, mg, &ip.ip._v4_addr); + mbr = pim_msdp_mg_mbr_add(vrf->info, mg, &ip.ip._v4_addr); nb_running_set_entry(args->dnode, mbr); break; } @@ -1150,7 +1150,7 @@ int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args) mbr = nb_running_get_entry(args->dnode, NULL, true); mg = nb_running_get_entry(args->dnode, "../", true); - pim_msdp_mg_mbr_do_del(mg, mbr); + pim_msdp_mg_mbr_del(mg, mbr); break; }