summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonald Sharp <sharpd@cumulusnetworks.com>2021-06-10 15:44:28 +0200
committerGitHub <noreply@github.com>2021-06-10 15:44:28 +0200
commit59ec133a03bafbf41ab1759c3f67b142bddbc0a2 (patch)
treebca73efa5dc9206d2c721a5e136475781cb3a139
parentMerge pull request #8733 from idryzhov/ipv6-ospf6-area (diff)
parentpimd: rename some MSDP functions (diff)
downloadfrr-59ec133a03bafbf41ab1759c3f67b142bddbc0a2.tar.xz
frr-59ec133a03bafbf41ab1759c3f67b142bddbc0a2.zip
Merge pull request #8561 from opensourcerouting/msdp-refactor-v2
pimd: rework MSDP mesh groups
-rw-r--r--pimd/pim_cmd.c449
-rw-r--r--pimd/pim_msdp.c314
-rw-r--r--pimd/pim_msdp.h55
-rw-r--r--pimd/pim_nb.c25
-rw-r--r--pimd/pim_nb.h22
-rw-r--r--pimd/pim_nb_config.c341
-rwxr-xr-xtests/topotests/lib/mcast-tester.py129
-rw-r--r--tests/topotests/lib/topogen.py16
-rw-r--r--tests/topotests/msdp_mesh_topo1/__init__.py0
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/bgpd.conf7
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/ospfd.conf8
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/pimd.conf15
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/bgpd.conf10
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/ospfd.conf13
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/pimd.conf14
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/bgpd.conf7
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/ospfd.conf8
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/pimd.conf15
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot88
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.pngbin0 -> 35201 bytes
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py296
-rw-r--r--yang/frr-pim.yang34
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
new file mode 100644
index 000000000..9a15b8b08
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png
Binary files differ
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.";
+ }
}
}