]> git.proxmox.com Git - mirror_frr.git/commitdiff
tests: adding 11 bgp v4 over v6 test cases.
authornguggarigoud <nguggarigoud@vmware.com>
Tue, 24 Aug 2021 00:19:00 +0000 (17:19 -0700)
committernguggarigoud <nguggarigoud@vmware.com>
Tue, 24 Aug 2021 00:20:24 +0000 (17:20 -0700)
Signed-off-by: nguggarigoud <nguggarigoud@vmware.com>
tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py [new file with mode: 0644]
tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py [new file with mode: 0644]

diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json
new file mode 100644 (file)
index 0000000..7f928b9
--- /dev/null
@@ -0,0 +1,85 @@
+{
+    "ipv4base": "10.0.0.0",
+    "ipv4mask": 24,
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+    "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+    "routers": {
+        "r0": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+        },
+        "r1": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+                "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }}},
+        "r2": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+                "r3": {"ipv4": "auto", "ipv6": "auto"},
+                "r4": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "200",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop"
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}}}
+                        }
+                    }
+                }}},
+        "r3": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "200",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }}},
+        "r4": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}}
+        }}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json
new file mode 100644 (file)
index 0000000..8e0f448
--- /dev/null
@@ -0,0 +1,95 @@
+{
+    "ipv4base": "10.0.0.0",
+    "ipv4mask": 24,
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+    "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+    "routers": {
+        "r0": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+        },
+        "r1": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+                "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }}},
+        "r2": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+                "r3": {"ipv4": "auto", "ipv6": "auto"},
+                "r4": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "200",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}}}
+                        }
+                    }}}},
+        "r3": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "300",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }}},
+        "r4": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "400",
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+                }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json
new file mode 100644 (file)
index 0000000..72d3a93
--- /dev/null
@@ -0,0 +1,97 @@
+{
+    "ipv4base": "10.0.0.0",
+    "ipv4mask": 24,
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+    "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+    "routers": {
+        "r0": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+        },
+        "r1": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+                "r2-link0": {"ipv4": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                            "neighbor_type": "unnumbered"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }}},
+        "r2": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link0": {"ipv4": "auto"},
+                "r3": {"ipv4": "auto", "ipv6": "auto"},
+                "r4": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "200",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                            "neighbor_type": "unnumbered"
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}}}
+                        }
+                    }}}},
+        "r3": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "300",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }}},
+        "r4": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "400",
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+                }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json
new file mode 100644 (file)
index 0000000..a7ea0c8
--- /dev/null
@@ -0,0 +1,95 @@
+{
+    "ipv4base": "10.0.0.0",
+    "ipv4mask": 24,
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+    "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+    "routers": {
+        "r0": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+        },
+        "r1": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+                "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+                "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }}},
+        "r2": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+                "r3": {"ipv4": "auto", "ipv6": "auto"},
+                "r4": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}}}
+                        }
+                    }}}},
+        "r3": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "300",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }}},
+        "r4": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "400",
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+                }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json
new file mode 100644 (file)
index 0000000..5e90d6b
--- /dev/null
@@ -0,0 +1,97 @@
+{
+    "ipv4base": "10.0.0.0",
+    "ipv4mask": 24,
+    "ipv6base": "fd00::",
+    "ipv6mask": 64,
+    "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+    "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+    "routers": {
+        "r0": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link1": {"ipv4": "auto"},
+                "r1-link2": {"ipv4": "auto"},
+                "r1-link3": {"ipv4": "auto"},
+                "r1-link4": {"ipv4": "auto"},
+                "r1-link5": {"ipv4": "auto"}}
+        },
+        "r1": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r0-link1": {"ipv4": "auto"},
+                "r0-link2": {"ipv4": "auto"},
+                "r0-link3": {"ipv4": "auto"},
+                "r0-link4": {"ipv4": "auto"},
+                "r0-link5": {"ipv4": "auto"},
+                "r2-link0": {"ipv4": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "neighbor_type": "unnumbered",
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }}},
+        "r2": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r1-link0": {"ipv4": "auto"},
+                "r3": {"ipv4": "auto", "ipv6": "auto"},
+                "r4": {"ipv4": "auto"}},
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "neighbor": {
+                                "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+                            }
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "neighbor_type": "unnumbered",
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4"
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}}}
+                        }
+                    }}}},
+        "r3": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto", "ipv6": "auto"}},
+            "bgp": {
+                "local_as": "300",
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }}},
+        "r4": {
+            "links": {
+                "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+                "r2": {"ipv4": "auto"}},
+            "bgp": {
+                "local_as": "400",
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+                }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py
new file mode 100644 (file)
index 0000000..4f72cbb
--- /dev/null
@@ -0,0 +1,987 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import json
+import pytest
+from copy import deepcopy
+import ipaddr
+from re import search as re_search
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+    start_topology,
+    write_test_header,
+    get_frr_ipv6_linklocal,
+    write_test_footer,
+    create_prefix_lists,
+    verify_rib,
+    create_static_routes,
+    check_address_types,
+    reset_config_on_routers,
+    step,
+    create_route_maps,
+    create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.bgp import (
+    clear_bgp_and_verify,
+    verify_bgp_convergence,
+    create_router_bgp,
+    verify_bgp_rib,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/rfc5549_ebgp_ibgp_nbr.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+    "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+    "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+ADDR_TYPES = check_address_types()
+TOPOOLOGY = """
+      Please view in a fixed-width font such as Courier.
+
+                                      +----+
+                                      | R4 |
+                                      |    |
+                                      +--+-+
+                                         | ipv4 nbr
+          no bgp           ebgp/ibgp     |
+                                         |     ebgp/ibgp
+    +----+ 5links   +----+  8links    +--+-+             +----+
+    |R0  +----------+ R1 +------------+ R2 |    ipv6 nbr |R3  |
+    |    +----------+    +------------+    +-------------+    |
+    +----+          +----+   ipv6 nbr +----+             +----+
+"""
+
+TESTCASES = """
+1. Verify Ipv4 route next hop is changed when advertised using
+next hop -self command
+2. Verify IPv4 route advertised to peer when IPv6 BGP session established
+ using peer-group
+3. Verify IPv4 routes received with IPv6 nexthop are getting advertised
+ to another IBGP peer without changing the nexthop
+4. Verify IPv4 routes advertised with correct nexthop when nexthop
+unchange is configure on EBGP peers
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """Set up the pytest environment."""
+
+    global topo
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+        BGP_CONVERGENCE
+    )
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """
+    Teardown the pytest environment.
+
+    * `mod`: module name
+    """
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+    """
+    API to get the link local ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `fromnode`: Source node
+    * `tonode` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_llip('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) link local ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    tgen = get_topogen()
+    intf = topo["routers"][onrouter]["links"][intf]["interface"]
+    llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+    if llip:
+        logger.info("llip ipv6 address to be set as NH is %s", llip)
+        return llip
+    return None
+
+
+def get_glipv6(onrouter, intf):
+    """
+    API to get the global ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `onrouter`: Source node
+    * `intf` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_glipv6('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) global ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+    if glipv6:
+        logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+        return glipv6
+    return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+def test_ibgp_to_ibgp_p1(request):
+    """
+
+    Test Capability extended nexthop.
+
+    Verify IPv4 routes received with IPv6 nexthop are getting advertised to
+    another IBGP peer without changing the nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    global topo
+    topo23 = deepcopy(topo)
+    build_config_from_json(tgen, topo23, save_bkup=False)
+
+    step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address")
+    step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address")
+    step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+    step("Activate same IPv6 nbr from IPv4 unicast family")
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+
+    # verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.
+    bgp_convergence = verify_bgp_convergence(tgen, topo23)
+    assert bgp_convergence is True, "Testcase  :Failed \n Error:" " {}".format(
+        bgp_convergence
+    )
+
+    step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo23, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    gllip = get_llip("r1", "r2-link0")
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r3": {
+                                    "dest_link": {
+                                        "r2": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r3 = {
+        "r3": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r3": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 routes installed on R3 with global address without "
+        "changing the nexthop ( nexthop should IPv6 link local which is"
+        " received from R1)"
+    )
+    gipv6 = get_glipv6("r1", "r2-link0")
+    dut = "r3"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gipv6,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gipv6
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    write_test_footer(tc_name)
+
+
+def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request):
+    """
+
+    Test Extended capability next hop, with ibgp peer.
+
+    Verify IPv4 routes advertise using "redistribute static" and
+    "network command" are received on EBGP peer with IPv6 nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    step(
+        " Configure IPv6 EBGP session between R1 & R2 with global IPv6 address"
+        " Enable capability extended-nexthop on the nbr from both the routers"
+        " Activate same IPv6 nbr from IPv4 unicast family"
+    )
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r3": {
+                                    "dest_link": {
+                                        "r2": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4",
+                                            "next_hop_self": True,
+                                            "activate": "ipv4",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r3 = {
+        "r3": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r3": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    gllip = get_llip("r1", "r2-link0")
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    gllip = get_glipv6("r2", "r3")
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r3"
+    protocol = "bgp"
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+def test_bgp_peer_group_p1(request):
+    """
+    Test extended capability next hop with peer groups.
+
+    Verify IPv4 routes received with IPv6 nexthop are getting advertised to
+    another IBGP peer without changing the nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    global topo
+    topo1 = deepcopy(topo)
+    step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address")
+    step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address")
+    step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+    step("Activate same IPv6 nbr from IPv4 unicast family")
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "peer-group": {
+                    "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"}
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                            "peer-group": "rfc5549",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "peer-group": {
+                    "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"}
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4",
+                                            "peer-group": "rfc5549",
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}},
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r3 = {
+        "r3": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase  :Failed \n Error:" " {}".format(
+        bgp_convergence
+    )
+
+    step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    gllip = get_llip("r1", "r2-link0")
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "peer-group": {
+                    "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"}
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "r1-link0": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                            "peer-group": "rfc5549",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "default_ipv4_unicast": "False",
+                "peer-group": {
+                    "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"}
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "r2-link0": {
+                                            "capability": "extended-nexthop",
+                                            "activate": "ipv4",
+                                            "peer-group": "rfc5549",
+                                        }
+                                    }
+                                },
+                                "r3": {"dest_link": {"r2": {}}},
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r3 = {
+        "r3": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase  :Failed \n Error:" " {}".format(
+        bgp_convergence
+    )
+
+    step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+    for rte in range(0, NO_OF_RTES):
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    gllip = get_llip("r1", "r2-link0")
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py
new file mode 100644 (file)
index 0000000..12237fe
--- /dev/null
@@ -0,0 +1,661 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import json
+import pytest
+import datetime
+from copy import deepcopy
+import ipaddr
+from re import search as re_search
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+    start_topology,
+    write_test_header,
+    write_test_footer,
+    create_prefix_lists,
+    get_frr_ipv6_linklocal,
+    verify_rib,
+    create_static_routes,
+    check_address_types,
+    reset_config_on_routers,
+    step,
+    create_route_maps,
+    addKernelRoute,
+    kill_router_daemons,
+    start_router_daemons,
+    create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.bgp import (
+    clear_bgp_and_verify,
+    clear_bgp,
+    verify_bgp_convergence,
+    create_router_bgp,
+    verify_bgp_rib,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/rfc5549_ebgp_nbr.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+    "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+    "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+ADDR_TYPES = check_address_types()
+BGP_CONVERGENCE_TIMEOUT = 10
+TOPOOLOGY = """
+      Please view in a fixed-width font such as Courier.
+                                      +----+
+                                      | R4 |
+                                      |    |
+                                      +--+-+
+                                         | ipv4 nbr
+          no bgp           ebgp          |
+                                         |     ebgp/ibgp
+    +----+ 5links   +----+            +--+-+             +----+
+    |R0  +----------+ R1 |            | R2 |    ipv6 nbr |R3  |
+    |    +----------+    +------------+    +-------------+    |
+    +----+          +----+   ipv6 nbr +----+             +----+
+"""
+
+TESTCASES = """
+TC6. Verify BGP speaker advertise IPv4 route to peer only if "extended
+ nexthop capability" is negotiated
+TC7. Verify ipv4 route nexthop updated dynamically when in route-map is
+ applied on receiving BGP peer
+TC8. Verify IPv4 routes advertise using "redistribute static" and "network
+ command" are received on EBGP peer with IPv6 nexthop
+TC10. Verify IPv4 routes are deleted after un-configuring of "network
+command" and "redistribute static knob"
+TC18. Verify IPv4 routes installed with correct nexthop after deactivate
+ and activate neighbor from address family
+TC19. Verify IPv4 route ping is working fine and nexhop installed in kernel
+ as IPv4 link-local address
+TC24. Verify IPv4 prefix-list routes advertised to peer when prefix -list
+ applied in out direction
+TC27. Verify IPv4 routes are intact after BGPd process restart
+TC30. Verify Ipv4 route installed with correct next hop when same route
+ is advertised via IPV4 and IPv6 BGP peers
+TC32. Verify IPv4 route received with IPv6 nexthop can be advertised to
+ another IPv4 BGP peers
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """Set up the pytest environment."""
+    global topo, ADDR_TYPES
+
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+        BGP_CONVERGENCE
+    )
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """Teardown the pytest environment."""
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+    """
+    API to get the link local ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `fromnode`: Source node
+    * `tonode` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_llip('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) link local ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    tgen = get_topogen()
+    intf = topo["routers"][onrouter]["links"][intf]["interface"]
+    llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+    if llip:
+        logger.info("llip ipv6 address to be set as NH is %s", llip)
+        return llip
+    return None
+
+
+def get_glipv6(onrouter, intf):
+    """
+    API to get the global ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `onrouter`: Source node
+    * `intf` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_glipv6('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) global ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+    if glipv6:
+        logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+        return glipv6
+    return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ebgp_peer_tc8_p0(request):
+    """
+
+    Test exted capability nexthop with route map in.
+
+    Verify IPv4 routes advertise using "redistribute static" and
+    "network command" are received on EBGP peer with IPv6 nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    step("Configure IPv6 EBGP session between R1 and R2 with global" " IPv6 address")
+    reset_config_on_routers(tgen)
+
+    step(
+        "Enable capability extended-nexthop on the nbr from both the  "
+        " routers Activate same IPv6 nbr from IPv4 unicast family"
+    )
+    step(
+        " Configure 2 IPv4 static  "
+        "routes on R1 (nexthop for static route exists on different  "
+        "link of R0"
+    )
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv6"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv6"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "True",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    },
+                    "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    glip = get_llip("r1", "r2-link0")
+    assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 and IPv6 routes advertised using static and network command "
+        "are received on R2 BGP & routing table , verify using show ip bgp "
+        "show ip route for IPv4 routes and show bgp ipv6,show ipv6 routes "
+        "for IPv6 routes ."
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    for addr_type in ADDR_TYPES:
+        # verify the routes with nh as ext_nh
+        verify_nh_for_static_rtes = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK[addr_type][0],
+                        "no_of_ip": 2,
+                        "next_hop": glip,
+                    }
+                ]
+            }
+        }
+        bgp_rib = verify_bgp_rib(
+            tgen, addr_type, dut, verify_nh_for_static_rtes, next_hop=glip
+        )
+        assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, bgp_rib
+        )
+        result = verify_rib(
+            tgen,
+            addr_type,
+            dut,
+            verify_nh_for_static_rtes,
+            next_hop=glip,
+            protocol=protocol,
+        )
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Verify IPv4 routes are installed with IPv6 global nexthop of R1"
+        " R1 to R2 connected link"
+    )
+
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": glip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+
+def test_ext_nh_cap_remove_red_static_network_ebgp_peer_tc10_p1(request):
+    """
+
+    Test exted capability nexthop with route map in.
+
+    Verify IPv4 routes are deleted after un-configuring of
+    network command and redistribute static knob
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    step(
+        "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+        " address Enable capability extended-nexthop on the nbr from both"
+        " the routers , Activate same IPv6 nbr from IPv4 unicast family"
+    )
+    step(
+        " Configure 2 IPv4 static routes "
+        " on R1 nexthop for static route exists on different link of R0"
+    )
+    reset_config_on_routers(tgen)
+
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 unicast"
+        " family respectively from R1. Configure loopback on R1 with IPv4 "
+        "address Advertise loobak from IPv4 unicast family using network "
+        "command from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "local_as": "100",
+                "default_ipv4_unicast": "True",
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                },
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 and IPv6 routes advertised using static and network command are"
+        " received on R2 BGP and routing table , verify using show ip bgp"
+        " show ip route for IPv4 routes and show bgp, show ipv6 routes"
+        " for IPv6 routes ."
+    )
+
+    glipv6 = get_llip("r1", "r2-link0")
+    assert glipv6 is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "advertise_networks": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": get_glipv6,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=get_glipv6
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen,
+        "ipv4",
+        dut,
+        verify_nh_for_static_rtes,
+        next_hop=get_glipv6,
+        protocol=protocol,
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Verify IPv4 routes are installed with IPv6 global nexthop of R1 "
+        " R1 to R2 connected link"
+    )
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "advertise_networks": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": glipv6,
+                }
+            ]
+        }
+    }
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static", "delete": True}]
+                        }
+                    },
+                    "ipv6": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static", "delete": True}]
+                        }
+                    },
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": glipv6,
+                }
+            ]
+        }
+    }
+
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glipv6, expected=False
+    )
+    assert (
+        bgp_rib is not True
+    ), "Testcase {} : Failed \n Error: Routes still" " present in BGP rib".format(
+        tc_name
+    )
+    result = verify_rib(
+        tgen,
+        "ipv4",
+        dut,
+        verify_nh_for_static_rtes,
+        next_hop=glipv6,
+        protocol=protocol,
+        expected=False,
+    )
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Error: Routes " "still present in RIB".format(tc_name)
+
+    step(
+        "After removing IPv4 routes from redistribute static those routes"
+        " are removed from R2, after re-advertising routes which are "
+        " advertised using network are still present in the on R2 with "
+        " IPv6 global nexthop, verify using show ip bgp and show ip routes"
+    )
+
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": glipv6,
+                }
+            ]
+        }
+    }
+
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "advertise_networks": [
+                                {
+                                    "network": NETWORK_CMD_IP,
+                                    "no_of_network": 1,
+                                    "delete": True,
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    result = verify_rib(
+        tgen,
+        "ipv4",
+        dut,
+        verify_nh_for_nw_cmd_rtes,
+        next_hop=glipv6,
+        protocol=protocol,
+        expected=False,
+    )
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n " "Error: Routes still present in BGP rib".format(
+        tc_name
+    )
+
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
new file mode 100644 (file)
index 0000000..2675f3a
--- /dev/null
@@ -0,0 +1,613 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import json
+import pytest
+import ipaddr
+from copy import deepcopy
+from re import search as re_search
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+    write_test_header,
+    start_topology,
+    create_route_maps,
+    write_test_footer,
+    start_router,
+    stop_router,
+    verify_rib,
+    create_static_routes,
+    check_address_types,
+    reset_config_on_routers,
+    step,
+    shutdown_bringup_interface,
+    create_interfaces_cfg,
+    get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import clear_bgp, verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/rfc5549_ebgp_unnumbered_nbr.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+    "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+    "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+INTF_LIST = [
+    "r2-link0",
+    "r2-link1",
+    "r2-link2",
+    "r2-link3",
+    "r2-link4",
+    "r2-link5",
+    "r2-link6",
+    "r2-link7",
+]
+ADDR_TYPES = check_address_types()
+TOPOOLOGY = """
+      Please view in a fixed-width font such as Courier.
+
+                                      +----+
+                                      | R4 |
+                                      |    |
+                                      +--+-+
+                                         | ipv4 nbr
+          no bgp           ebgp/ibgp     |
+                                         |     ebgp/ibgp
+    +----+ 5links   +----+  8links    +--+-+             +----+
+    |R0  +----------+ R1 +------------+ R2 |    ipv6 nbr |R3  |
+    |    +----------+    +------------+    +-------------+    |
+    +----+          +----+   ipv6 nbr +----+             +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 routes are advertised when IPv6 EBGP loopback session
+ established using Unnumbered interface
+2. Verify IPv4 routes are installed with correct nexthop after
+shut / no shut of nexthop and BGP peer interfaces
+3. Verify IPv4 routes are intact after stop and start the FRR services
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """Set up the pytest environment."""
+    global topo, ADDR_TYPES
+
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+        BGP_CONVERGENCE
+    )
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """Teardown the pytest environment."""
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+    """
+    API to get the link local ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `fromnode`: Source node
+    * `tonode` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_llip('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) link local ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    tgen = get_topogen()
+    intf = topo["routers"][onrouter]["links"][intf]["interface"]
+    llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+    if llip:
+        logger.info("llip ipv6 address to be set as NH is %s", llip)
+        return llip
+    return None
+
+
+def get_glipv6(onrouter, intf):
+    """
+    API to get the global ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `onrouter`: Source node
+    * `intf` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_glipv6('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) global ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+    if glipv6:
+        logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+        return glipv6
+    return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_unnumbered_loopback_ebgp_nbr_p0(request):
+    """
+
+    Test extended capability nexthop with un numbered ebgp.
+
+    Verify IPv4 routes are advertised when IPv6 EBGP loopback
+    session established using Unnumbered interface
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+
+    step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
+    step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+    step("Activate same IPv6 nbr from IPv4 unicast family")
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+        tc_name, bgp_convergence
+    )
+
+    step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    },
+                    "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    llip = get_llip("r1", "r2-link0")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+
+    dut = "r2"
+    protocol = "bgp"
+    for rte in range(0, NO_OF_RTES):
+        # verify the routes with nh as ext_nh
+        verify_nh_for_static_rtes = {
+            "r1": {
+                "static_routes": [
+                    {"network": NETWORK["ipv4"][rte], "no_of_ip": 1, "next_hop": llip}
+                ]
+            }
+        }
+        """         interface_list = ['r1-link0','r1-link1']
+        nh_list =[]
+        for i in range(NO_OF_RTES):
+            nh_list.append(topo['routers']['r2']['links'][i][
+                'interface']) """
+        bgp_rib = verify_rib(
+            tgen,
+            "ipv4",
+            dut,
+            # verify_nh_for_static_rtes, next_hop='r2-r1-eth0')
+            verify_nh_for_static_rtes,
+            next_hop=llip,
+        )
+        assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, bgp_rib
+        )
+        result = verify_rib(
+            tgen,
+            "ipv4",
+            dut,
+            verify_nh_for_static_rtes,
+            next_hop=llip,
+            protocol=protocol,
+        )
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    # verify the routes with nh as ext_nh
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+            ]
+        }
+    }
+
+    bgp_rib = verify_rib(
+        tgen,
+        "ipv4",
+        dut,
+        # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0')
+        verify_nh_for_nw_rtes,
+        next_hop=llip,
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    # stop/start -> restart FRR router and verify
+    stop_router(tgen, "r1")
+    stop_router(tgen, "r2")
+    start_router(tgen, "r1")
+    start_router(tgen, "r2")
+    step(
+        "After stop/start of FRR services , verify session up and routes "
+        "came up fine ,nh is proper using show bgp & show ipv6 route on R2 "
+    )
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+        tc_name, bgp_convergence
+    )
+
+    llip = get_llip("r1", "r2-link0")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": llip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen,
+        "ipv4",
+        dut,
+        # verify_nh_for_static_rtes, next_hop='r2-r1-eth0')
+        verify_nh_for_static_rtes,
+        next_hop=llip,
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+            ]
+        }
+    }
+    bgp_rib = verify_rib(
+        tgen,
+        "ipv4",
+        dut,
+        # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0')
+        verify_nh_for_nw_rtes,
+        next_hop=llip,
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    write_test_footer(tc_name)
+
+
+def test_restart_frr_p2(request):
+    """
+
+    Test extended capability nexthop , restart frr.
+
+    Verify IPv4 routes are intact after stop and start the FRR services
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
+    step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+    step("Activate same IPv6 nbr from IPv4 unicast family")
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+    reset_config_on_routers(tgen)
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+        tc_name, bgp_convergence
+    )
+
+    step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    },
+                    "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    llip = get_llip("r1", "r2-link0")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": llip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+            ]
+        }
+    }
+
+    bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # stop/start -> restart FRR router and verify
+    stop_router(tgen, "r1")
+    stop_router(tgen, "r2")
+    start_router(tgen, "r1")
+    start_router(tgen, "r2")
+
+    step(
+        "After stop/start of FRR services , verify session up and routes "
+        "came up fine ,nh is proper using show bgp & show ipv6 route on R2 "
+    )
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+        tc_name, bgp_convergence
+    )
+
+    llip = get_llip("r1", "r2-link0")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": llip}
+            ]
+        }
+    }
+    bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # verify the routes with nh as ext_nh
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+            ]
+        }
+    }
+    bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py
new file mode 100644 (file)
index 0000000..871f6b1
--- /dev/null
@@ -0,0 +1,1013 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import json
+import pytest
+from copy import deepcopy
+import ipaddr
+from re import search as re_search
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+    start_topology,
+    write_test_header,
+    addKernelRoute,
+    write_test_footer,
+    create_prefix_lists,
+    verify_rib,
+    create_static_routes,
+    check_address_types,
+    reset_config_on_routers,
+    step,
+    create_route_maps,
+    create_interfaces_cfg,
+    get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import (
+    clear_bgp_and_verify,
+    verify_bgp_convergence,
+    create_router_bgp,
+    verify_bgp_rib,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/rfc5549_ibgp_nbr.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+    "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+    "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NETWORK_CMD_IP = "1.0.1.17/32"
+NO_OF_RTES = 2
+TOPOOLOGY = """
+      Please view in a fixed-width font such as Courier.
+
+                                      +----+
+                                      | R4 |
+                                      |    |
+                                      +--+-+
+                                         | ipv4 nbr
+          no bgp           ebgp/ibgp     |
+                                         |     ebgp/ibgp
+    +----+ 5links   +----+            +--+-+             +----+
+    |R0  +----------+ R1 |            | R2 |    ipv6 nbr |R3  |
+    |    +----------+    +------------+    +-------------+    |
+    +----+          +----+   ipv6 nbr +----+             +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 and IPv6 routes advertise using "redistribute static"
+ and "network command" are received on IBGP peer with IPv6 nexthop
+2. Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP session
+ established using loopback interface
+3. Verify IPv4 routes are advertised to peer when static routes are
+ configured with ADMIN distance and tag option
+4. Verify IPv4 routes advertised to peer when BGP session established
+ using link-local address
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """Set up the pytest environment."""
+
+    global topo
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+        BGP_CONVERGENCE
+    )
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """Teardown the pytest environment."""
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+    """
+    API to get the link local ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `fromnode`: Source node
+    * `tonode` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_llip('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) link local ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    tgen = get_topogen()
+    intf = topo["routers"][onrouter]["links"][intf]["interface"]
+    llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+
+    if llip:
+        logger.info("llip ipv6 address to be set as NH is %s", llip)
+        return llip
+    return None
+
+
+def get_glipv6(onrouter, intf):
+    """
+    API to get the global ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `onrouter`: Source node
+    * `intf` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_glipv6('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) global ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+    if glipv6:
+        logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+        return glipv6
+    return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request):
+    """
+
+    Test extended capability nexthop with ibgp peer.
+
+    Verify IPv4 and IPv6 routes advertise using "redistribute static"
+    and "network command" are received on IBGP peer with IPv6 nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    step(
+        "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+        " address Enable capability extended-nexthop on the nbr from both"
+        " the routers"
+    )
+    step(
+        "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6"
+        " nbr from IPv4 unicast family "
+    )
+
+    step(
+        " Configure 5 IPv4 static routes"
+        " on R1 nexthop for static route exists on different link of R0"
+    )
+
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 unicast"
+        " family respectively from R1.Configure loopback on R1 with IPv4 addr"
+        " & Advertise loopback from IPv4 unicast family using network cmd "
+        " from R1"
+    )
+    # this test case needs ipv6 routes to be configured
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    },
+                    "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    glip = get_llip("r1", "r2-link0")
+    assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 and IPv6 routes advertised using static & network command are"
+        "received on R2 BGP and routing table , verify using show ip bgp"
+        "show ip route  for IPv4 routes and show bgp, show ipv6 routes"
+        "for IPv6 routes ."
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": glip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Verify IPv4 routes are installed with IPv6 global nexthop of R1"
+        "R1 to R2 connected link"
+    )
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": glip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+def test_ext_nh_cap_admin_dist_tag_ibgp_peer_p1(request):
+    """
+
+    Test extended capability nexthop with admin distance and route tag.
+
+    Verify IPv4 routes are advertised to peer when static routes
+    are configured with ADMIN distance and tag option
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    step(
+        "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+        " address Enable capability extended-nexthop on the nbr from both"
+        " the routers"
+    )
+    step(
+        "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6"
+        " nbr from IPv4 unicast family "
+    )
+    step(
+        " Configure 5 IPv4 static routes"
+        " on R1 nexthop for static route exists on different link of R0"
+    )
+    count = 0
+    for rte in range(0, NO_OF_RTES):
+        count += 1
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                        "admin_distance": 100 + count,
+                        "tag": 4001 + count,
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+    step(
+        "Advertise static routes from IPv4 unicast family & IPv6 unicast"
+        " family respectively from R1.Configure loopback on R1 with IPv4 "
+        "address & Advertise loopback from IPv4 unicast family "
+        "using network cmd from R1"
+    )
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    glip = get_llip("r1", "r2-link0")
+    assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 and IPv6 routes advertised using static & network cmd are"
+        "received on R2 BGP and routing table , verify using show ip bgp"
+        "show ip route  for IPv4 routes and show bgp, show ipv6 routes"
+        "for IPv6 routes ."
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    count = 0
+    # verify the routes with nh as ext_nh
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": glip,
+                    "admin_distance": 100 + count,
+                    "tag": 4001 + count,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    count = 0
+    for rte in range(0, NO_OF_RTES):
+        count += 10
+        input_dict_2 = {
+            "r3": {
+                "prefix_lists": {
+                    "ipv4": {
+                        "pf_list_1_ipv4": [
+                            {
+                                "seqid": 0 + count,
+                                "action": "permit",
+                                "network": NETWORK["ipv4"][rte],
+                            }
+                        ]
+                    }
+                }
+            }
+        }
+        result = create_prefix_lists(tgen, input_dict_2)
+        assert result is True, "Testcase {} : Failed \n " "Error: {}".format(
+            tc_name, result
+        )
+
+        # Create route map
+        input_dict_6 = {
+            "r3": {
+                "route_maps": {
+                    "rmap_match_tag_1_{}".format("ipv4"): [
+                        {
+                            "action": "deny",
+                            "match": {
+                                "ipv4": {"prefix_lists": "pf_list_1_{}".format("ipv4")}
+                            },
+                        }
+                    ]
+                }
+            }
+        }
+        result = create_route_maps(tgen, input_dict_6)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+        # Configure neighbor for route map
+        input_dict_7 = {
+            "r1": {
+                "bgp": {
+                    "address_family": {
+                        "ipv6": {
+                            "unicast": {
+                                "neighbor": {
+                                    "r2": {
+                                        "dest_link": {
+                                            "r1-link0": {
+                                                "route_maps": [
+                                                    {
+                                                        "name": "rmap_match_tag_1_ipv4",
+                                                        "direction": "out",
+                                                    }
+                                                ]
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        result = create_router_bgp(tgen, topo, input_dict_7)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    write_test_footer(tc_name)
+
+
+def test_ibgp_loopback_nbr_p1(request):
+    """
+    Verify Extended capability nexthop with loopback interface.
+
+    Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP
+    session established using loopback interface
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    global topo
+    topo1 = deepcopy(topo)
+    reset_config_on_routers(tgen)
+    step("Configure IPv6 global address between R1 and R2")
+    step(
+        "Configure loopback on R1 and R2 and establish EBGP session "
+        "between R1 and R2 over loopback global ip"
+    )
+    step("Configure static route on R1 and R2 for loopback reachability")
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+
+    for routerN in ["r1", "r2"]:
+        for addr_type in ["ipv6"]:
+            for bgp_neighbor in topo1["routers"][routerN]["bgp"]["address_family"][
+                addr_type
+            ]["unicast"]["neighbor"].keys():
+                # Adding ['source_link'] = 'lo' key:value pair
+                if bgp_neighbor == "r1" or bgp_neighbor == "r2":
+                    topo1["routers"][routerN]["bgp"]["address_family"][addr_type][
+                        "unicast"
+                    ]["neighbor"][bgp_neighbor]["dest_link"] = {
+                        "lo": {
+                            "source_link": "lo",
+                            "ebgp_multihop": 2,
+                            "capability": "extended-nexthop",
+                            "activate": "ipv4",
+                        }
+                    }
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo1, save_bkup=False)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {"r1-link0": {"deactivate": "ipv6"}}
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {"r2-link0": {"deactivate": "ipv6"}}
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {"r1-link0": {"deactivate": "ipv4"}}
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r2 = {
+        "r2": {
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {"r2-link0": {"deactivate": "ipv4"}}
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"]
+    r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"]
+    r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+    r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+    r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+    r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+    r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0]
+    r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0]
+
+    ipv4_list = [("r1", r1_r2_intf, [r2_lo_v4]), ("r2", r2_r1_intf, [r1_lo_v4])]
+
+    ipv6_list = [
+        ("r1", r1_r2_intf, [r2_lo_v6], r2_r1_v6_nh),
+        ("r2", r2_r1_intf, [r1_lo_v6], r1_r2_v6_nh),
+    ]
+
+    for dut, intf, loop_addr in ipv4_list:
+        result = addKernelRoute(tgen, dut, intf, loop_addr)
+        # assert result is True, "Testcase {}:Failed \n Error: {}". \
+        #    format(tc_name, result)
+
+    for dut, intf, loop_addr, next_hop in ipv6_list:
+        result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop)
+        # assert result is True, "Testcase {}:Failed \n Error: {}". \
+        #    format(tc_name, result)
+
+    r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"]
+    r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"]
+    r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+    r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+    r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+    r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+    r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0]
+    r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0]
+
+    r1_r2_v4_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0]
+    r2_r1_v4_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv4"].split("/")[0]
+
+    input_dict = {
+        "r1": {
+            "static_routes": [
+                {"network": r2_lo_v4, "next_hop": r2_r1_v4_nh},
+                {"network": r2_lo_v6, "next_hop": r2_r1_v6_nh},
+            ]
+        },
+        "r2": {
+            "static_routes": [
+                {"network": r1_lo_v4, "next_hop": r1_r2_v4_nh},
+                {"network": r1_lo_v6, "next_hop": r1_r2_v6_nh},
+            ]
+        },
+    }
+    result = create_static_routes(tgen, input_dict)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    # Api call verify whether BGP is converged
+    result = verify_bgp_convergence(tgen, topo1)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+    configure_bgp_on_r1 = {
+        "r1": {
+            "default_ipv4_unicast": False,
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r2": {
+                                    "dest_link": {
+                                        "lo": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r2 = {
+        "r2": {
+            "default_ipv4_unicast": False,
+            "bgp": {
+                "address_family": {
+                    "ipv6": {
+                        "unicast": {
+                            "neighbor": {
+                                "r1": {
+                                    "dest_link": {
+                                        "lo": {
+                                            "activate": "ipv4",
+                                            "capability": "extended-nexthop",
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify bgp convergence.")
+    bgp_convergence = verify_bgp_convergence(tgen, topo1)
+    assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, bgp_convergence
+    )
+
+    step("Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 "
+        "unicast family respectively from R1 using red static cmd "
+        "Advertise loopback from IPv4 unicast family using network command "
+        "from R1"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "IPv4 routes advertised using static and network command are "
+        " received on R2 BGP and routing table , "
+        "verify using show ip bgp, show ip route for IPv4 routes ."
+    )
+
+    gllip = (topo1["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]).lower()
+    assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+        tc_name, result
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": gllip,
+                }
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+    )
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Remove IPv4 routes advertised using network command"
+        " from R1 and advertise again"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {
+                                    "network": NETWORK_CMD_IP,
+                                    "no_of_network": 1,
+                                    "delete": True,
+                                }
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {
+                                    "network": NETWORK_CMD_IP,
+                                    "no_of_network": 1,
+                                }
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "After removing IPv4 routes from network command , routes which are "
+        "advertised using redistribute static are still present in the on "
+        "R2 , verify using show ip bgp and show ip route"
+    )
+
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Remove IPv4 routes advertised using redistribute static"
+        " command from R1 and advertise again"
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static", "delete": True}]
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        "After removing IPv4 routes from redistribute static , routes which"
+        " are advertised using network are still present in the on R2 , "
+        "verify using show ip bgp and show ip route"
+    )
+
+    verify_nh_for_nw_rtes = {
+        "r1": {
+            "static_routes": [
+                {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+            ]
+        }
+    }
+    bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+    assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py
new file mode 100644 (file)
index 0000000..5f4292b
--- /dev/null
@@ -0,0 +1,347 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import json
+import pytest
+import ipaddr
+from re import search as re_search
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+    start_topology,
+    write_test_header,
+    write_test_footer,
+    create_interfaces_cfg,
+    verify_rib,
+    create_static_routes,
+    check_address_types,
+    step,
+    reset_config_on_routers,
+    get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import clear_bgp, verify_bgp_convergence, create_router_bgp
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/rfc5549_ibgp_unnumbered_nbr.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+
+# Global variables
+NETWORK_CMD_IP = "1.0.1.17/32"
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+    "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+    "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+ADDR_TYPES = check_address_types()
+NO_OF_RTES = 2
+TOPOOLOGY = """
+      Please view in a fixed-width font such as Courier.
+                                      +----+
+                                      | R4 |
+                                      |    |
+                                      +--+-+
+                                         | ipv4 nbr
+          no bgp           ebgp/ibgp     |
+                                         |     ebgp/ibgp
+    +----+ 2links   +----+  8links    +--+-+             +----+
+    |R0  +----------+ R1 +            + R2 |    ipv6 nbr |R3  |
+    |    +----------+    +------------+    +-------------+    |
+    +----+          +----+   ipv6 nbr +----+             +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 routes are deleted after un-configuring "network command
+" and "redistribute static knob" with Unnumbered IPv6 IBGP session
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """Set up the pytest environment."""
+
+    global topo
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+    assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+        BGP_CONVERGENCE
+    )
+
+    logger.info("Running setup_module() done")
+
+
+def teardown_module():
+    """Teardown the pytest environment."""
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+    """
+    API to get the link local ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `fromnode`: Source node
+    * `tonode` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_llip('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) link local ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    tgen = get_topogen()
+    intf = topo["routers"][onrouter]["links"][intf]["interface"]
+    llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+
+    if llip:
+        logger.info("llip ipv6 address to be set as NH is %s", llip)
+        return llip
+    return None
+
+
+def get_glipv6(onrouter, intf):
+    """
+    API to get the global ipv6 address of a perticular interface
+
+    Parameters
+    ----------
+    * `onrouter`: Source node
+    * `intf` : interface for which link local ip needs to be returned.
+
+    Usage
+    -----
+    result = get_glipv6('r1', 'r2-link0')
+
+    Returns
+    -------
+    1) global ipv6 address from the interface.
+    2) errormsg - when link local ip not found.
+    """
+    glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+    if glipv6:
+        logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+        return glipv6
+    return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ebgp_peer_unnumbered_nbr_p1(request):
+    """
+
+    Test extended capability nexthop.
+
+    Verify IPv4 routes advertise using "redistribute static" and
+    "network command" are received on EBGP peer with IPv6 nexthop
+    """
+    tc_name = request.node.name
+    write_test_header(tc_name)
+    tgen = get_topogen()
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+    reset_config_on_routers(tgen)
+    step(
+        "Configure IPv6 IBGP Unnumbered session between R1 and R2 and enable "
+        "ipv6 nd ra-interval 10 in the interface"
+    )
+
+    step(
+        "Enable capability extended-nexthop"
+        "on the neighbor from both the routers and "
+        "ipv6 nd ra-interval 10 on link connected between R1 and R2"
+    )
+
+    bgp_convergence = verify_bgp_convergence(tgen, topo)
+    assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format(
+        bgp_convergence
+    )
+
+    for rte in range(0, NO_OF_RTES):
+        # Create Static routes
+        input_dict = {
+            "r1": {
+                "static_routes": [
+                    {
+                        "network": NETWORK["ipv4"][rte],
+                        "no_of_ip": 1,
+                        "next_hop": NEXT_HOP["ipv4"][rte],
+                    }
+                ]
+            }
+        }
+        result = create_static_routes(tgen, input_dict)
+        assert result is True, "Testcase {} : Failed \n Error: {}".format(
+            tc_name, result
+        )
+    step(
+        "Advertise static routes from IPv4 unicast family and IPv6 unicast "
+        "family respectively from R1 "
+        "Configure loopback on R1 with IPv4 address Advertise loopback "
+        "from IPv4 unicast family using network cmd from R1 "
+    )
+
+    configure_bgp_on_r1 = {
+        "r1": {
+            "bgp": {
+                "address_family": {
+                    "ipv4": {
+                        "unicast": {
+                            "redistribute": [{"redist_type": "static"}],
+                            "advertise_networks": [
+                                {"network": NETWORK_CMD_IP, "no_of_network": 1}
+                            ],
+                        }
+                    }
+                }
+            }
+        }
+    }
+    result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    llip = get_llip("r1", "r2-link0")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+    step(
+        " IPv4 and IPv6 routes advertised using static and network command are"
+        " received on R2 BGP and routing table , verify using show ip bgp"
+        " show ip route for IPv4 routes and show bgp show ipv6 routes"
+        " for IPv6 routes ."
+    )
+
+    dut = "r2"
+    protocol = "bgp"
+    verify_nh_for_static_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv4"][0],
+                    "no_of_ip": NO_OF_RTES,
+                    "next_hop": llip,
+                }
+            ]
+        }
+    }
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    verify_nh_for_nw_cmd_rtes = {
+        "r1": {
+            "static_routes": [
+                {
+                    "network": NETWORK_CMD_IP,
+                    "no_of_ip": 1,
+                    "next_hop": llip,
+                }
+            ]
+        }
+    }
+
+    result = verify_rib(
+        tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=llip, protocol=protocol
+    )
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))