From b7dcb198344427a5de5b44e9182e2a768625de18 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Wed, 19 Jul 2017 08:57:48 +0100 Subject: [PATCH 001/239] Added dokka tool as a dependency --- build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.gradle b/build.gradle index c5166de..dbd8eb5 100644 --- a/build.gradle +++ b/build.gradle @@ -6,13 +6,16 @@ buildscript { repositories { mavenCentral() + jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.15" } } apply plugin: 'kotlin' +apply plugin: 'org.jetbrains.dokka' repositories { mavenCentral() @@ -25,6 +28,12 @@ dependencies { compileKotlin { kotlinOptions.jvmTarget = "1.8" } + compileTestKotlin { kotlinOptions.jvmTarget = "1.8" +} + +dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" } \ No newline at end of file From 8e13f6dabbd8b50ce1f28c5e47d7460a5e0d4921 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 08:43:53 +0100 Subject: [PATCH 002/239] Included JUnit5 as a dependency --- build.gradle | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dbd8eb5..1b7ba1d 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,9 @@ version '1.0-SNAPSHOT' buildscript { ext.kotlin_version = '1.1.3-2' + ext.dokka_version = '0.9.15' + ext.junit_version = '1.0.0-M4' + ext.junit5_version = '5.0.0-M4' repositories { mavenCentral() @@ -10,18 +13,23 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.15" + classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" + classpath "org.junit.platform:junit-platform-gradle-plugin:$junit_version" } } apply plugin: 'kotlin' apply plugin: 'org.jetbrains.dokka' +apply plugin: 'org.junit.platform.gradle.plugin' repositories { mavenCentral() } dependencies { + testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" + testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5_version" + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" } From 4b5481d890c6cb95147cfc23bbd4b29998ecbbeb Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 08:51:02 +0100 Subject: [PATCH 003/239] Included Spek, hamcrest, and mockito as dependencies --- build.gradle | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/build.gradle b/build.gradle index 1b7ba1d..7d1c34a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { ext.dokka_version = '0.9.15' ext.junit_version = '1.0.0-M4' ext.junit5_version = '5.0.0-M4' + ext.spek_version = '1.1.2' repositories { mavenCentral() @@ -22,13 +23,26 @@ apply plugin: 'kotlin' apply plugin: 'org.jetbrains.dokka' apply plugin: 'org.junit.platform.gradle.plugin' +junitPlatform { + filters { + engines { + include 'spek' + } + } +} + repositories { mavenCentral() } dependencies { testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" + testCompile "org.jetbrains.spek:spek-api:$spek_version" + testCompile "org.hamcrest:hamcrest-all:1.3" + testCompile "org.mockito:mockito-core:2.8.9" + testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5_version" + testRuntime "org.jetbrains.spek:spek-junit-platform-engine:$spek_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" } From 8c355c208f2c9c8473b5a484bdb725d6a335ad93 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:06:07 +0100 Subject: [PATCH 004/239] Defined the Node class --- src/main/kotlin/core/routing/Node.kt | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/kotlin/core/routing/Node.kt diff --git a/src/main/kotlin/core/routing/Node.kt b/src/main/kotlin/core/routing/Node.kt new file mode 100644 index 0000000..191149e --- /dev/null +++ b/src/main/kotlin/core/routing/Node.kt @@ -0,0 +1,43 @@ +package core.routing + +typealias NodeID = Int + +/** + * Created on 19-07-17 + * + * @author David Fialho + * + * A node is the fundamental component of a topology @see Topology. A node is some entity that is able to speak with + * other nodes using a common protocol. + * + * This is the base class used to implement actual nodes that support some protocol. In its most simple form a node + * is characterized by a unique ID. Subclasses of Node should provide the tools and information required for it to + * communicate with other nodes of the same type using some common protocol. + * + * @property id The ID of the node. This ID uniquely identifies it inside a topology + */ +abstract class Node(val id: NodeID) { + + /** + * Two nodes are considered equal if they have exactly the same ID. + * Subclasses of node should not override this method. + */ + final override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as Node + + if (id != other.id) return false + + return true + } + + /** + * Follows the equals/hashCode contract. + */ + final override fun hashCode(): Int { + return id + } + +} \ No newline at end of file From c0b518b436ee5aa0da211851655eb2ea82e641b7 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:06:34 +0100 Subject: [PATCH 005/239] Defined the Route interface and the relationship data class --- src/main/kotlin/core/routing/Relationship.kt | 12 +++++++++++ src/main/kotlin/core/routing/Route.kt | 22 ++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/main/kotlin/core/routing/Relationship.kt create mode 100644 src/main/kotlin/core/routing/Route.kt diff --git a/src/main/kotlin/core/routing/Relationship.kt b/src/main/kotlin/core/routing/Relationship.kt new file mode 100644 index 0000000..7985888 --- /dev/null +++ b/src/main/kotlin/core/routing/Relationship.kt @@ -0,0 +1,12 @@ +package core + +import core.routing.Extender +import core.routing.Node +import core.routing.Route + +/** + * Created on 20-07-2017 + * + * @author David Fialho + */ +data class Relationship(val node: N, val extender: Extender) \ No newline at end of file diff --git a/src/main/kotlin/core/routing/Route.kt b/src/main/kotlin/core/routing/Route.kt new file mode 100644 index 0000000..2511c5a --- /dev/null +++ b/src/main/kotlin/core/routing/Route.kt @@ -0,0 +1,22 @@ +package core.routing + +/** + * Created on 19-07-2017 + * + * @author David Fialho + * + * Routes are pieces of routing information that associate a set of attributes with a destination. Usually, nodes + * exchange routes with their neighbors in other to provide connectivity to one another. + * + * Routes may be invalid. An invalid route indicates that there is no electable route to the destination via some + * neighbor, which means that an invalid route should NEVER be elected by a node. Routes can be checked for validity + * through the isInvalid() method included in the Route interface. + */ +interface Route { + + /** + * Returns true if a route is valid or false if otherwise. + */ + fun isValid(): Boolean + +} \ No newline at end of file From 092aae417cde5f179de91628e92b67e949a7b66d Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:06:53 +0100 Subject: [PATCH 006/239] Defined the Extender interface --- src/main/kotlin/core/routing/Extender.kt | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/kotlin/core/routing/Extender.kt diff --git a/src/main/kotlin/core/routing/Extender.kt b/src/main/kotlin/core/routing/Extender.kt new file mode 100644 index 0000000..c10b25f --- /dev/null +++ b/src/main/kotlin/core/routing/Extender.kt @@ -0,0 +1,25 @@ +package core.routing + +/** + * Created on 19-07-2017 + * + * @author David Fialho + * + * An extender is a transformation function. It is always associated with a link between two neighboring nodes. + * This function describes how each route elected by the head node of the link is transformed at the tail node. + * It describes both the export policies of the head node and the import policies of the tail node. + * + * TODO improve the documentation for extender + */ +interface Extender { + + /** + * Takes a route and returns a new route with the attributes defined according to the implementation of the + * extender function. + * + * @param route the route to be extended + * @return the extended route + */ + fun extend(route: R): R + +} \ No newline at end of file From 3fa51308e82641b5f53dce0a1201b603aa3adb82 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:30:33 +0100 Subject: [PATCH 007/239] Fixed issue with JUnit5 that class failed to be loaded --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 7d1c34a..a4ad2fb 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ dependencies { testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5_version" testRuntime "org.jetbrains.spek:spek-junit-platform-engine:$spek_version" + testRuntime "org.junit.platform:junit-platform-launcher:$junit_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" } From 4bdb8b95a141ba2170994b5b32a60cfd132bf8d5 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:34:41 +0100 Subject: [PATCH 008/239] Defined the Path class with a size property Implemented only creating paths --- src/main/kotlin/core/routing/Path.kt | 44 +++++++++++++++++++++++ src/main/kotlin/core/routing/Route.kt | 2 ++ src/test/kotlin/core/routing/PathTests.kt | 37 +++++++++++++++++++ src/test/kotlin/testing/FakeNode.kt | 20 +++++++++++ 4 files changed, 103 insertions(+) create mode 100644 src/main/kotlin/core/routing/Path.kt create mode 100644 src/test/kotlin/core/routing/PathTests.kt create mode 100644 src/test/kotlin/testing/FakeNode.kt diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt new file mode 100644 index 0000000..5863d03 --- /dev/null +++ b/src/main/kotlin/core/routing/Path.kt @@ -0,0 +1,44 @@ +package core.routing + +/** + * Created on 21-07-2017 + * + * @author David Fialho + * + * A Path is a sequence of nodes that form a path in a network. Nodes appended to a path are kept in order. + * Nodes can be repeated in a path. That is, a path may include two or more nodes with the same ID. + * + * Path instances are immutable! + * + * @property size expresses the number of nodes in the path + */ +class Path(private val nodes: List) { + + val size: Int = nodes.size + +} + +//region Factory methods + +/** + * Returns a path with no nodes. + */ +fun emptyPath(): Path { + return Path(emptyList()) +} + +/** + * Returns a path containing the given nodes in the same order as they are given. + */ +fun pathOf(vararg nodes: N): Path { + return Path(listOf(*nodes)) +} + +/** + * Returns an empty path. + */ +inline fun pathOf(): Path { + return emptyPath() +} + +//endregion \ No newline at end of file diff --git a/src/main/kotlin/core/routing/Route.kt b/src/main/kotlin/core/routing/Route.kt index 2511c5a..5b97498 100644 --- a/src/main/kotlin/core/routing/Route.kt +++ b/src/main/kotlin/core/routing/Route.kt @@ -11,6 +11,8 @@ package core.routing * Routes may be invalid. An invalid route indicates that there is no electable route to the destination via some * neighbor, which means that an invalid route should NEVER be elected by a node. Routes can be checked for validity * through the isInvalid() method included in the Route interface. + * + * Implementations of the Route interface should be always immutable! */ interface Route { diff --git a/src/test/kotlin/core/routing/PathTests.kt b/src/test/kotlin/core/routing/PathTests.kt new file mode 100644 index 0000000..f97924e --- /dev/null +++ b/src/test/kotlin/core/routing/PathTests.kt @@ -0,0 +1,37 @@ +package core.routing + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.jetbrains.spek.api.dsl.on +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import testing.node + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object PathTests : Spek({ + + given("an empty path") { + + val emptyPath = emptyPath() + + it("has size 0") { + assertThat(emptyPath.size, equalTo(0)) + } + + } + + given("a path with 1 node") { + val path = pathOf(node(1)) + + it("has size 1") { + assertThat(path.size, equalTo(1)) + } + + } + +}) \ No newline at end of file diff --git a/src/test/kotlin/testing/FakeNode.kt b/src/test/kotlin/testing/FakeNode.kt new file mode 100644 index 0000000..824ea80 --- /dev/null +++ b/src/test/kotlin/testing/FakeNode.kt @@ -0,0 +1,20 @@ +package testing + +import core.routing.Node +import core.routing.NodeID + +/** + * Created on 21-07-2017 + * + * @author David Fialho + * + * Fake implementation of a node to be used in tests that require any node instance. + */ +class FakeNode(id: NodeID) : Node(id) + +/** + * Returns a node instance associated with the given ID + */ +fun node(id: NodeID): Node { + return FakeNode(id) +} \ No newline at end of file From 3db9c2cb3839e8f4b47f35c87a156d9718211762 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:41:50 +0100 Subject: [PATCH 009/239] Added the append() method to Path --- src/main/kotlin/core/routing/Path.kt | 11 +++++++++ src/test/kotlin/core/routing/PathTests.kt | 29 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index 5863d03..4cdb133 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -16,6 +16,17 @@ class Path(private val nodes: List) { val size: Int = nodes.size + /** + * Returns a new path instance containing the nodes in the same order as this path and with the given node + * appended to it. + */ + fun append(node: N): Path { + val nodesCopy = ArrayList(nodes) + nodesCopy.add(node) + + return Path(nodesCopy) + } + } //region Factory methods diff --git a/src/test/kotlin/core/routing/PathTests.kt b/src/test/kotlin/core/routing/PathTests.kt index f97924e..ccd15f2 100644 --- a/src/test/kotlin/core/routing/PathTests.kt +++ b/src/test/kotlin/core/routing/PathTests.kt @@ -23,15 +23,44 @@ object PathTests : Spek({ assertThat(emptyPath.size, equalTo(0)) } + on("appending a new node") { + + val appendedPath = emptyPath.append(node(1)) + + it("returns a path of size 1") { + assertThat(appendedPath.size, equalTo(1)) + } + + it("keeps the original path empty") { + assertThat(emptyPath.size, equalTo(0)) + } + + } + } given("a path with 1 node") { + val path = pathOf(node(1)) it("has size 1") { assertThat(path.size, equalTo(1)) } + on("appending a new node") { + + val appendedPath = path.append(node(2)) + + it("returns a path of size 2") { + assertThat(appendedPath.size, equalTo(2)) + } + + it("keeps the original path with size 1") { + assertThat(path.size, equalTo(1)) + } + + } + } }) \ No newline at end of file From a04ba2ba3835027506d2c4a4f7a317bf4d115b73 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:46:37 +0100 Subject: [PATCH 010/239] Overridden the equals, hashCode, and toString methods --- src/main/kotlin/core/routing/Path.kt | 22 +++++++++++ src/test/kotlin/core/routing/PathTests.kt | 45 +++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index 4cdb133..95622d1 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -27,6 +27,28 @@ class Path(private val nodes: List) { return Path(nodesCopy) } + /** + * Two paths are considered equal if they have the exact same nodes in the exact same order. + */ + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as Path<*> + + if (nodes != other.nodes) return false + + return true + } + + override fun hashCode(): Int { + return nodes.hashCode() + } + + override fun toString(): String { + return "$nodes" + } + } //region Factory methods diff --git a/src/test/kotlin/core/routing/PathTests.kt b/src/test/kotlin/core/routing/PathTests.kt index ccd15f2..66d71ac 100644 --- a/src/test/kotlin/core/routing/PathTests.kt +++ b/src/test/kotlin/core/routing/PathTests.kt @@ -63,4 +63,49 @@ object PathTests : Spek({ } + given("two paths with the same size and the same nodes in the exact same order") { + + val path1 = pathOf(node(1), node(2)) + val path2 = pathOf(node(1), node(2)) + + it("is true that the two are equal") { + assertThat(path1, equalTo(path2)) + } + + } + + given("two paths with different sizes and matching nodes") { + + val path1 = pathOf(node(1), node(2), node(3)) + val path2 = pathOf(node(1), node(2)) + + it("is true that the two are different") { + assertThat(path1, not(equalTo(path2))) + } + + } + + given("two paths with the same size but different nodes") { + + val path1 = pathOf(node(1), node(2)) + val path2 = pathOf(node(2), node(3)) + + it("is true that the two are different") { + assertThat(path1, not(equalTo(path2))) + } + + } + + given("two paths with the same size and the same nodes in different order") { + + val path1 = pathOf(node(1), node(2)) + val path2 = pathOf(node(2), node(1)) + + it("is true that the two are different") { + assertThat(path1, not(equalTo(path2))) + } + + } + + }) \ No newline at end of file From eac008fc9cd73b7fd44ceb9dffa275c2b3bb0cc4 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:48:49 +0100 Subject: [PATCH 011/239] Implemented the 'contains' operator --- src/main/kotlin/core/routing/Path.kt | 5 +++++ src/test/kotlin/core/routing/PathTests.kt | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index 95622d1..59011fa 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -27,6 +27,11 @@ class Path(private val nodes: List) { return Path(nodesCopy) } + /** + * Checks if this path contains the given node. + */ + operator fun contains(node: N) = node in nodes + /** * Two paths are considered equal if they have the exact same nodes in the exact same order. */ diff --git a/src/test/kotlin/core/routing/PathTests.kt b/src/test/kotlin/core/routing/PathTests.kt index 66d71ac..b2a0405 100644 --- a/src/test/kotlin/core/routing/PathTests.kt +++ b/src/test/kotlin/core/routing/PathTests.kt @@ -31,6 +31,10 @@ object PathTests : Spek({ assertThat(appendedPath.size, equalTo(1)) } + it("returns a path containing that new node") { + assertThat(node(1) in appendedPath, `is`(true)) + } + it("keeps the original path empty") { assertThat(emptyPath.size, equalTo(0)) } @@ -55,6 +59,11 @@ object PathTests : Spek({ assertThat(appendedPath.size, equalTo(2)) } + it("returns a path containing that previous node and the new node") { + assertThat(node(1) in appendedPath, `is`(true)) + assertThat(node(2) in appendedPath, `is`(true)) + } + it("keeps the original path with size 1") { assertThat(path.size, equalTo(1)) } From 0116a840c839fe1045e9f4e4ca903265db460bba Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:55:08 +0100 Subject: [PATCH 012/239] Added a copy() method to the Path --- src/main/kotlin/core/routing/Path.kt | 5 +++++ src/test/kotlin/core/routing/PathTests.kt | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index 59011fa..cc28c2c 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -32,6 +32,11 @@ class Path(private val nodes: List) { */ operator fun contains(node: N) = node in nodes + /** + * Returns a path instance containing exactly the same nodes as this path and exactly in the same order. + */ + fun copy(): Path = Path(nodes) + /** * Two paths are considered equal if they have the exact same nodes in the exact same order. */ diff --git a/src/test/kotlin/core/routing/PathTests.kt b/src/test/kotlin/core/routing/PathTests.kt index b2a0405..83056b1 100644 --- a/src/test/kotlin/core/routing/PathTests.kt +++ b/src/test/kotlin/core/routing/PathTests.kt @@ -67,7 +67,23 @@ object PathTests : Spek({ it("keeps the original path with size 1") { assertThat(path.size, equalTo(1)) } + } + + on("copying the node") { + + val pathCopy = path.copy() + + it("returns a new path instance") { + assertThat(path !== pathCopy, `is`(true)) + } + + it("returns a path equal to the initial path") { + assertThat(pathCopy, equalTo(path)) + } + it("returns a path equal to the initial path") { + assertThat(pathCopy, equalTo(path)) + } } } From 5dce28f947e70ee17d6d4fbd30023fe6a8a7f520 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:56:30 +0100 Subject: [PATCH 013/239] Gradle: fixed issue with conflicting kotlin versions --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index a4ad2fb..563b7c3 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,7 @@ dependencies { testRuntime "org.junit.platform:junit-platform-launcher:$junit_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" } compileKotlin { From 6cb72aa2ada451698772ccd99a900d11cbddeeb1 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:57:21 +0100 Subject: [PATCH 014/239] Added the BGPRoute --- src/main/kotlin/bgp/BGPRoute.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/kotlin/bgp/BGPRoute.kt diff --git a/src/main/kotlin/bgp/BGPRoute.kt b/src/main/kotlin/bgp/BGPRoute.kt new file mode 100644 index 0000000..c4275c4 --- /dev/null +++ b/src/main/kotlin/bgp/BGPRoute.kt @@ -0,0 +1,10 @@ +package bgp + +import core.routing.Route + +/** + * Created on 20-07-2017 + * + * @author David Fialho + */ +interface BGPRoute : Route \ No newline at end of file From 4e184234e8880167e2ec7cc3d68a6f219c62f931 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 09:57:53 +0100 Subject: [PATCH 015/239] Started defining a BGPNode --- src/main/kotlin/bgp/BGPNode.kt | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/kotlin/bgp/BGPNode.kt diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt new file mode 100644 index 0000000..b17dc1f --- /dev/null +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -0,0 +1,40 @@ +package bgp + +import core.Relationship +import core.routing.Node +import core.routing.NodeID + +/** + * Created on 20-07-2017 + * + * @author David Fialho + */ +class BGPNode +internal constructor(id: NodeID, val relationships: MutableList>) : Node(id) { + + override fun toString(): String { + return "BGPNode(id=$id)" + } +} + +//region Factory methods + +/** + * Returns a BGP node with the given ID an relationships. The relationships parameter is optional, if no value is + * provided for it returns a BGPNode with no neighbors. + * + * @param id the ID to assign to the new node + * @param relationships a list containing all the relationships the node has (each one must be unique) + * @throws IllegalArgumentException if the given relationships list contains any duplicate relationships + */ +@Throws(java.lang.IllegalArgumentException::class) +fun BGPNodeWith(id: NodeID, relationships: MutableList> = ArrayList()): BGPNode { + + if (setOf(relationships).size != relationships.size) { + throw IllegalArgumentException("Can not create a BGP node with duplicate relationships") + } + + return BGPNode(id, relationships) +} + +//endregion \ No newline at end of file From 08bed8c6c850a0928ca1e2a783583d5f40766d69 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 10:30:12 +0100 Subject: [PATCH 016/239] Started implementing the BGP topology: added support for adding new nodes by ID --- src/main/kotlin/bgp/BGPNode.kt | 2 +- src/main/kotlin/bgp/BGPTopology.kt | 28 ++++++++ src/main/kotlin/bgp/BGPTopologyBuilder.kt | 31 ++++++++ src/main/kotlin/core/routing/Topology.kt | 55 +++++++++++++++ .../kotlin/bgp/BGPTopologyBuilderTests.kt | 70 +++++++++++++++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/bgp/BGPTopology.kt create mode 100644 src/main/kotlin/bgp/BGPTopologyBuilder.kt create mode 100644 src/main/kotlin/core/routing/Topology.kt create mode 100644 src/test/kotlin/bgp/BGPTopologyBuilderTests.kt diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index b17dc1f..6609d9b 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -30,7 +30,7 @@ internal constructor(id: NodeID, val relationships: MutableList> = ArrayList()): BGPNode { - if (setOf(relationships).size != relationships.size) { + if (HashSet(relationships).size != relationships.size) { throw IllegalArgumentException("Can not create a BGP node with duplicate relationships") } diff --git a/src/main/kotlin/bgp/BGPTopology.kt b/src/main/kotlin/bgp/BGPTopology.kt new file mode 100644 index 0000000..dcfc4de --- /dev/null +++ b/src/main/kotlin/bgp/BGPTopology.kt @@ -0,0 +1,28 @@ +package bgp + +import core.routing.Topology + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +class BGPTopology(private val nodes: List) : Topology { + + override val size: Int = nodes.size + + override fun getNode(id: Int): BGPNode { + TODO("not implemented") + } + + override fun getNodes(): Collection = nodes + + override fun nodeCount(): Int { + TODO("not implemented") + } + + override fun linkCount(): Int { + TODO("not implemented") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/bgp/BGPTopologyBuilder.kt b/src/main/kotlin/bgp/BGPTopologyBuilder.kt new file mode 100644 index 0000000..ad4a297 --- /dev/null +++ b/src/main/kotlin/bgp/BGPTopologyBuilder.kt @@ -0,0 +1,31 @@ +package bgp + +import core.routing.NodeID + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +class BGPTopologyBuilder { + + private val ids = mutableSetOf() + + /** + * Indicates to the builder that the topology to be built must include a node with the given ID. + * + * @return true if the ID was not added yet to the builder or false if otherwise + */ + fun addNode(id: NodeID): Boolean { + return ids.add(id) + } + + /** + * Returns a BGPTopology containing the nodes and relationships defined in the builder at the time the method is + * called. + */ + fun build(): BGPTopology { + return BGPTopology(ids.map { BGPNodeWith(id = it) }.toList()) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/core/routing/Topology.kt b/src/main/kotlin/core/routing/Topology.kt new file mode 100644 index 0000000..8858ee1 --- /dev/null +++ b/src/main/kotlin/core/routing/Topology.kt @@ -0,0 +1,55 @@ +package core.routing + +/** + * Created on 16-07-2017. + * + * @author David Fialho + * + * Topology is an high-level abstraction of a network composed of nodes and their interconnections. + * This interface defines the interface that is common to all topologies. + * + * Notice the Topology interface does not define any methods to add/remove nodes or links. Topology implementations + * should be immutable! That is, there should be no way to add or remove nodes from the topology and the same should + * be true for links. A topology should be built using a topology builder @see TopologyBuilder. + * + * Each node in a topology is uniquely identified by its ID. Therefore, the topology interface provides methods to + * access topology nodes using their IDs. + * + * This interface takes a generic type N that extends from Node. N is the type of nodes that the topology holds. + * + * @property size the number of nodes in the topology + */ +interface Topology { + + val size: Int + + /** + * Returns the node associated with the given ID. + + * @param id the ID of the node to get from the network. + * @return the node associated with the given ID or null if the topology does not contain a node with such an ID. + */ + fun getNode(id: Int): N + + /** + * Returns a collection with all nodes contained in the topology in no particular order. + * + * @return a collection with all nodes contained in the topology in no particular order. + */ + fun getNodes(): Collection + + /** + * Returns the number of nodes currently in the topology. + * + * @return the number of nodes currently in the topology + */ + fun nodeCount(): Int + + /** + * Returns the number of different links between nodes in the topology. + * + * @return the number of links currently in the topology + */ + fun linkCount(): Int + +} \ No newline at end of file diff --git a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt new file mode 100644 index 0000000..7f1d0dd --- /dev/null +++ b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt @@ -0,0 +1,70 @@ +package bgp + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.jetbrains.spek.api.dsl.on +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object BGPTopologyBuilderTests : Spek({ + + given("a clean builder") { + + val builder = BGPTopologyBuilder() + + on("adding a node with ID 1") { + it("returns true indicating the node was added") { + assertThat(builder.addNode(1), `is`(true)) + } + } + + on("adding a node with ID 1 and calling build") { + + builder.addNode(1) + val topology = builder.build() + + it("returns a topology containing a single node with ID 1") { + assertThat(topology.getNodes(), containsInAnyOrder(BGPNodeWith(id = 1))) + } + + } + + on("adding two nodes with IDs 1 and 2 and calling build") { + + builder.addNode(1) + builder.addNode(2) + val topology = builder.build() + + it("returns a topology containing two nodes with IDs 1 and 2") { + assertThat(topology.getNodes(), containsInAnyOrder(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + } + + } + + } + + given("a builder already containing one node with ID 1") { + + val builder = BGPTopologyBuilder() + builder.addNode(1) + + on("adding a second node with ID 1") { + it("returns false") { + assertThat(builder.addNode(1), `is`(false)) + } + } + + on("adding a second node with ID 2") { + it("returns true") { + assertThat(builder.addNode(2), `is`(true)) + } + } + } + +}) \ No newline at end of file From 91d45ca9d4a28016be3fe779d5212f958d854b31 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 11:01:28 +0100 Subject: [PATCH 017/239] Added implementations for BGPRoute --- src/main/kotlin/bgp/BGPRoute.kt | 56 ++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/bgp/BGPRoute.kt b/src/main/kotlin/bgp/BGPRoute.kt index c4275c4..8aaec0c 100644 --- a/src/main/kotlin/bgp/BGPRoute.kt +++ b/src/main/kotlin/bgp/BGPRoute.kt @@ -1,10 +1,64 @@ package bgp +import core.routing.Path import core.routing.Route +import core.routing.emptyPath /** * Created on 20-07-2017 * * @author David Fialho + * + * A BGP route is composed of two attributes: the LOCAL-PREF and the AS-PATH. The LOCAL-PREF is assigned locally by + * each node and indicates the degree of preference that node assigns to each route. The AS-PATH contains the + * sequence of nodes traversed by the route from the original advertiser to the current node holding the route. + * + * BGP routes are always immutable instances! + */ +interface BGPRoute : Route { + + val localPref: Int + val asPath: Path + +} + +/** + * An implementation for a valid BGP route. + */ +internal class ValidBGPRoute(override val localPref: Int, override val asPath: Path) : BGPRoute { + + // A valid bgp route is always valid + override fun isValid(): Boolean = true + +} + +/** + * An implementation for a invalid BGP route. */ -interface BGPRoute : Route \ No newline at end of file +object InvalidBGPRoute : BGPRoute { + + override val localPref: Int = Int.MIN_VALUE + override val asPath: Path = emptyPath() + + // A invalid bgp route is always invalid + override fun isValid(): Boolean = false + +} + +//region Factory functions + +/** + * Returns a valid BGP route with the given LOCAL-PREF and AS-PATH. + */ +fun BGPRouteWith(localPref: Int, asPath: Path): BGPRoute { + return ValidBGPRoute(localPref, asPath) +} + +/** + * Returns an invalid BGP route + */ +fun invalidBGPRoute(): BGPRoute { + return InvalidBGPRoute +} + +//endregion From cfe9aefe797c2054a5c6d4ead08e999f059276bb Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 11:02:06 +0100 Subject: [PATCH 018/239] Defined teh Link class and added getLinks() method to Topology --- src/main/kotlin/bgp/BGPTopology.kt | 16 ++++++++++++---- src/main/kotlin/core/routing/Topology.kt | 16 ++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/bgp/BGPTopology.kt b/src/main/kotlin/bgp/BGPTopology.kt index dcfc4de..1250f2a 100644 --- a/src/main/kotlin/bgp/BGPTopology.kt +++ b/src/main/kotlin/bgp/BGPTopology.kt @@ -1,5 +1,8 @@ package bgp +import core.routing.Extender +import core.routing.Link +import core.routing.NodeID import core.routing.Topology /** @@ -7,7 +10,7 @@ import core.routing.Topology * * @author David Fialho */ -class BGPTopology(private val nodes: List) : Topology { +class BGPTopology(private val nodes: List) : Topology { override val size: Int = nodes.size @@ -17,12 +20,17 @@ class BGPTopology(private val nodes: List) : Topology { override fun getNodes(): Collection = nodes - override fun nodeCount(): Int { - TODO("not implemented") + override fun getLinks(): Collection> { + return emptyList() } + override fun nodeCount(): Int = size + override fun linkCount(): Int { TODO("not implemented") } -} \ No newline at end of file +} + +typealias BGPLink = Link +typealias BGPExtender = Extender \ No newline at end of file diff --git a/src/main/kotlin/core/routing/Topology.kt b/src/main/kotlin/core/routing/Topology.kt index 8858ee1..fb0e907 100644 --- a/src/main/kotlin/core/routing/Topology.kt +++ b/src/main/kotlin/core/routing/Topology.kt @@ -19,7 +19,7 @@ package core.routing * * @property size the number of nodes in the topology */ -interface Topology { +interface Topology { val size: Int @@ -38,6 +38,13 @@ interface Topology { */ fun getNodes(): Collection + /** + * Returns a collection with all links contained in the topology in no particular order. + * + * @return a collection with all links contained in the topology in no particular order. + */ + fun getLinks(): Collection> + /** * Returns the number of nodes currently in the topology. * @@ -52,4 +59,9 @@ interface Topology { */ fun linkCount(): Int -} \ No newline at end of file +} + +/** + * Represents a link in the topology. + */ +data class Link(val tail: N, val head: N, val extender: Extender) From 9080e86549fc20501261860be23203a63a7f4bdb Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:04:36 +0100 Subject: [PATCH 019/239] Refactored BGP topology builder tests --- .../kotlin/bgp/BGPTopologyBuilderTests.kt | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt index 7f1d0dd..5f455b2 100644 --- a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt +++ b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt @@ -1,11 +1,14 @@ package bgp +import core.routing.Extender +import core.routing.NodeID import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.given import org.jetbrains.spek.api.dsl.it import org.jetbrains.spek.api.dsl.on import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.* +import org.jetbrains.spek.api.dsl.context /** * Created on 21-07-2017 @@ -24,9 +27,8 @@ object BGPTopologyBuilderTests : Spek({ } } - on("adding a node with ID 1 and calling build") { + on("calling build") { - builder.addNode(1) val topology = builder.build() it("returns a topology containing a single node with ID 1") { @@ -35,10 +37,31 @@ object BGPTopologyBuilderTests : Spek({ } - on("adding two nodes with IDs 1 and 2 and calling build") { + on("adding second node with ID 1 ") { + val added = builder.addNode(1) + + it("returns true indicating the node was added") { + assertThat(added, `is`(false)) + } + } + + on("calling build") { + + val topology = builder.build() + + it("returns a topology of size 1") { + assertThat(topology.size, equalTo(1)) + } + } + + on("adding second node with ID 2") { + it("returns true indicating the node was added") { + assertThat(builder.addNode(2), `is`(true)) + } + } + + on("calling build") { - builder.addNode(1) - builder.addNode(2) val topology = builder.build() it("returns a topology containing two nodes with IDs 1 and 2") { @@ -49,22 +72,24 @@ object BGPTopologyBuilderTests : Spek({ } - given("a builder already containing one node with ID 1") { - val builder = BGPTopologyBuilder() - builder.addNode(1) +}) - on("adding a second node with ID 1") { - it("returns false") { - assertThat(builder.addNode(1), `is`(false)) - } - } +/** + * Returns a BGP link connecting two BGP nodes with the given IDs. The returned link is associated with a extender + * obtained using the someExtender() method. + */ +fun BGPLink(from: NodeID, to: NodeID): BGPLink { + return BGPLink(BGPNodeWith(from), BGPNodeWith(to), someExtender()) +} - on("adding a second node with ID 2") { - it("returns true") { - assertThat(builder.addNode(2), `is`(true)) - } - } - } +/** + * Returns an extender when it is needed one but it is not important which one. + */ +fun someExtender(): BGPExtender { + return FakeExtender +} -}) \ No newline at end of file +object FakeExtender : BGPExtender { + override fun extend(route: BGPRoute): BGPRoute = invalidBGPRoute() +} From 6e58d0a599be4c3a3859a49ccd20bb6dad58a5c4 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:05:32 +0100 Subject: [PATCH 020/239] Implemented adding links to the BGP topology --- src/main/kotlin/bgp/BGPNode.kt | 4 +++ src/main/kotlin/bgp/BGPTopology.kt | 18 +++++++++-- src/main/kotlin/bgp/BGPTopologyBuilder.kt | 19 +++++++++++- .../kotlin/bgp/BGPTopologyBuilderTests.kt | 30 +++++++++++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 6609d9b..1623ae1 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -12,6 +12,10 @@ import core.routing.NodeID class BGPNode internal constructor(id: NodeID, val relationships: MutableList>) : Node(id) { + fun addRelationship(neighbor: BGPNode, extender: BGPExtender) { + relationships.add(Relationship(neighbor, extender)) + } + override fun toString(): String { return "BGPNode(id=$id)" } diff --git a/src/main/kotlin/bgp/BGPTopology.kt b/src/main/kotlin/bgp/BGPTopology.kt index 1250f2a..9ef2e60 100644 --- a/src/main/kotlin/bgp/BGPTopology.kt +++ b/src/main/kotlin/bgp/BGPTopology.kt @@ -21,13 +21,27 @@ class BGPTopology(private val nodes: List) : Topology = nodes override fun getLinks(): Collection> { - return emptyList() + + val links = ArrayList() + + for (node in nodes) { + for ((neighbor, extender) in node.relationships) { + links.add(BGPLink(neighbor, node, extender)) + } + } + + return links } override fun nodeCount(): Int = size override fun linkCount(): Int { - TODO("not implemented") + + val counter: Int = nodes + .flatMap { it.relationships } + .count() + + return counter } } diff --git a/src/main/kotlin/bgp/BGPTopologyBuilder.kt b/src/main/kotlin/bgp/BGPTopologyBuilder.kt index ad4a297..8d09cbb 100644 --- a/src/main/kotlin/bgp/BGPTopologyBuilder.kt +++ b/src/main/kotlin/bgp/BGPTopologyBuilder.kt @@ -1,5 +1,6 @@ package bgp +import core.routing.Extender import core.routing.NodeID /** @@ -9,7 +10,10 @@ import core.routing.NodeID */ class BGPTopologyBuilder { + data class Link(val tailID: NodeID, val headID: NodeID, val extender: BGPExtender) + private val ids = mutableSetOf() + private val links = mutableListOf() /** * Indicates to the builder that the topology to be built must include a node with the given ID. @@ -20,12 +24,25 @@ class BGPTopologyBuilder { return ids.add(id) } + fun addLink(from: NodeID, to: NodeID, extender: BGPExtender): Boolean { + + links.add(Link(from, to, extender)) + return true + } + /** * Returns a BGPTopology containing the nodes and relationships defined in the builder at the time the method is * called. */ fun build(): BGPTopology { - return BGPTopology(ids.map { BGPNodeWith(id = it) }.toList()) + + val nodes = ids.map { it to BGPNodeWith(id = it) }.toMap() + + for ((tail, head, extender) in links) { + nodes[head]!!.addRelationship(nodes[tail]!!, extender) + } + + return BGPTopology(nodes.values.toList()) } } \ No newline at end of file diff --git a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt index 5f455b2..fb26ec6 100644 --- a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt +++ b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt @@ -72,6 +72,36 @@ object BGPTopologyBuilderTests : Spek({ } + given("a builder with two nodes with IDs 1 and 2") { + + val builder = BGPTopologyBuilder() + builder.addNode(1) + builder.addNode(2) + + on("adding a relationship from 1 to 2") { + + val added = builder.addLink(from = 1, to = 2, extender = someExtender()) + + it("returns true") { + assertThat(added, `is`(true)) + } + + } + + on("calling build") { + val topology = builder.build() + val links = topology.getLinks() + + it("returns a topology with 1 link") { + assertThat(topology.linkCount(), equalTo(1)) + } + + it("returns a topology with a link from 1 to 2") { + assertThat(links, contains(BGPLink(from = 1, to = 2))) + } + } + + } }) From 8bab6850075f9c2b05fe278f81295b606971e509 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:13:51 +0100 Subject: [PATCH 021/239] Builder now checks if links added are unique --- src/main/kotlin/bgp/BGPTopologyBuilder.kt | 7 ++-- .../kotlin/bgp/BGPTopologyBuilderTests.kt | 36 +++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/bgp/BGPTopologyBuilder.kt b/src/main/kotlin/bgp/BGPTopologyBuilder.kt index 8d09cbb..331bad1 100644 --- a/src/main/kotlin/bgp/BGPTopologyBuilder.kt +++ b/src/main/kotlin/bgp/BGPTopologyBuilder.kt @@ -1,6 +1,5 @@ package bgp -import core.routing.Extender import core.routing.NodeID /** @@ -13,7 +12,7 @@ class BGPTopologyBuilder { data class Link(val tailID: NodeID, val headID: NodeID, val extender: BGPExtender) private val ids = mutableSetOf() - private val links = mutableListOf() + private val links = mutableSetOf() /** * Indicates to the builder that the topology to be built must include a node with the given ID. @@ -25,9 +24,7 @@ class BGPTopologyBuilder { } fun addLink(from: NodeID, to: NodeID, extender: BGPExtender): Boolean { - - links.add(Link(from, to, extender)) - return true + return links.add(Link(from, to, extender)) } /** diff --git a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt index fb26ec6..4b95f2e 100644 --- a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt +++ b/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt @@ -1,6 +1,5 @@ package bgp -import core.routing.Extender import core.routing.NodeID import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.given @@ -8,7 +7,6 @@ import org.jetbrains.spek.api.dsl.it import org.jetbrains.spek.api.dsl.on import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.* -import org.jetbrains.spek.api.dsl.context /** * Created on 21-07-2017 @@ -101,6 +99,40 @@ object BGPTopologyBuilderTests : Spek({ } } + on("trying to add a relationship from 1 to 2 again") { + + val added = builder.addLink(from = 1, to = 2, extender = someExtender()) + + it("returns false") { + assertThat(added, `is`(false)) + } + + } + + on("adding a relationship from 2 to 1") { + + val added = builder.addLink(from = 2, to = 1, extender = someExtender()) + + it("returns true") { + assertThat(added, `is`(true)) + } + + } + + on("calling build") { + + val topology = builder.build() + val links = topology.getLinks() + + it("returns a topology with 2 links") { + assertThat(topology.linkCount(), equalTo(1)) + } + + it("returns a topology with a link from 1 to 2 and another from 2 to 1") { + assertThat(links, containsInAnyOrder(BGPLink(from = 1, to = 2), BGPLink(from = 2, to = 1))) + } + } + } }) From c8550a38d3146abb9ae9abc0e1ed307f819a17db Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:21:18 +0100 Subject: [PATCH 022/239] Implemented the getNode() method of the BGPTopology --- src/main/kotlin/bgp/BGPTopology.kt | 12 +++++------- src/main/kotlin/bgp/BGPTopologyBuilder.kt | 7 +++++-- src/main/kotlin/core/routing/Topology.kt | 4 +++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/bgp/BGPTopology.kt b/src/main/kotlin/bgp/BGPTopology.kt index 9ef2e60..f8a417c 100644 --- a/src/main/kotlin/bgp/BGPTopology.kt +++ b/src/main/kotlin/bgp/BGPTopology.kt @@ -10,21 +10,19 @@ import core.routing.Topology * * @author David Fialho */ -class BGPTopology(private val nodes: List) : Topology { +class BGPTopology(private val nodes: Map) : Topology { override val size: Int = nodes.size - override fun getNode(id: Int): BGPNode { - TODO("not implemented") - } + override fun getNode(id: NodeID): BGPNode? = nodes[id] - override fun getNodes(): Collection = nodes + override fun getNodes(): Collection = nodes.values override fun getLinks(): Collection> { val links = ArrayList() - for (node in nodes) { + for (node in nodes.values) { for ((neighbor, extender) in node.relationships) { links.add(BGPLink(neighbor, node, extender)) } @@ -38,7 +36,7 @@ class BGPTopology(private val nodes: List) : Topology { * @param id the ID of the node to get from the network. * @return the node associated with the given ID or null if the topology does not contain a node with such an ID. */ - fun getNode(id: Int): N + fun getNode(id: Int): BGPNode? /** * Returns a collection with all nodes contained in the topology in no particular order. From 7221b1260ae2fbd6c2a6ca95e049239eaa13284a Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:30:38 +0100 Subject: [PATCH 023/239] Builder now creates an hash map to hold the nodes instead of a linked hash map Performed some refactoring --- src/main/kotlin/bgp/BGPNode.kt | 3 +++ src/main/kotlin/bgp/BGPTopology.kt | 11 ++--------- src/main/kotlin/bgp/BGPTopologyBuilder.kt | 10 ++++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 1623ae1..4c84516 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -12,6 +12,9 @@ import core.routing.NodeID class BGPNode internal constructor(id: NodeID, val relationships: MutableList>) : Node(id) { + /** + * Adds a relationship to this node. + */ fun addRelationship(neighbor: BGPNode, extender: BGPExtender) { relationships.add(Relationship(neighbor, extender)) } diff --git a/src/main/kotlin/bgp/BGPTopology.kt b/src/main/kotlin/bgp/BGPTopology.kt index f8a417c..7ec6005 100644 --- a/src/main/kotlin/bgp/BGPTopology.kt +++ b/src/main/kotlin/bgp/BGPTopology.kt @@ -31,16 +31,9 @@ class BGPTopology(private val nodes: Map) : Topology(ids.size) + // Create a node for each ID + ids.forEach { nodes.put(it, BGPNodeWith(it)) } + + // Establish relationships based on the links stored in the builder for ((tail, head, extender) in links) { nodes[head]!!.addRelationship(nodes[tail]!!, extender) } From 0699dc6b4d25ea64fb54d9419453fb4463388d70 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 12:33:29 +0100 Subject: [PATCH 024/239] Renamed BGPTopologyBuilderTests to BGPTopologyTests --- .../bgp/{BGPTopologyBuilderTests.kt => TopologyBuilderTests.kt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/kotlin/bgp/{BGPTopologyBuilderTests.kt => TopologyBuilderTests.kt} (99%) diff --git a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt b/src/test/kotlin/bgp/TopologyBuilderTests.kt similarity index 99% rename from src/test/kotlin/bgp/BGPTopologyBuilderTests.kt rename to src/test/kotlin/bgp/TopologyBuilderTests.kt index 4b95f2e..9f2343e 100644 --- a/src/test/kotlin/bgp/BGPTopologyBuilderTests.kt +++ b/src/test/kotlin/bgp/TopologyBuilderTests.kt @@ -13,7 +13,7 @@ import org.hamcrest.Matchers.* * @author David Fialho */ -object BGPTopologyBuilderTests : Spek({ +object TopologyBuilderTests : Spek({ given("a clean builder") { From 0fa3f58839135c55ce4c6ab87ea26359d1509a24 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 15:15:20 +0100 Subject: [PATCH 025/239] ValidBGPRoute is now a data class --- src/main/kotlin/bgp/BGPRoute.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/bgp/BGPRoute.kt b/src/main/kotlin/bgp/BGPRoute.kt index 8aaec0c..3c64bc6 100644 --- a/src/main/kotlin/bgp/BGPRoute.kt +++ b/src/main/kotlin/bgp/BGPRoute.kt @@ -25,7 +25,7 @@ interface BGPRoute : Route { /** * An implementation for a valid BGP route. */ -internal class ValidBGPRoute(override val localPref: Int, override val asPath: Path) : BGPRoute { +internal data class ValidBGPRoute(override val localPref: Int, override val asPath: Path) : BGPRoute { // A valid bgp route is always valid override fun isValid(): Boolean = true From e37eb040dc35d4bbea23fc90a9047705962496c4 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 15:40:37 +0100 Subject: [PATCH 026/239] Implemented the routing table --- src/main/kotlin/core/routing/RoutingTable.kt | 28 ++++ .../kotlin/core/routing/RoutingTableTest.kt | 143 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/main/kotlin/core/routing/RoutingTable.kt create mode 100644 src/test/kotlin/core/routing/RoutingTableTest.kt diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt new file mode 100644 index 0000000..0aa8591 --- /dev/null +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -0,0 +1,28 @@ +package core.routing + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +class RoutingTable(private val invalidRoute: R, neighbors: Collection = emptyList()) { + + private val routes = HashMap(neighbors.size) + init { + neighbors.forEach { routes[it] = invalidRoute } + } + + operator fun get(neighbor: N): R { + return routes[neighbor] ?: invalidRoute + } + + operator fun set(neighbor: N, route: R) { + routes.replace(neighbor, route) + } + + fun clear() { + // Sets invalid route for all neighbors + routes.replaceAll { _,_ -> invalidRoute } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/core/routing/RoutingTableTest.kt b/src/test/kotlin/core/routing/RoutingTableTest.kt new file mode 100644 index 0000000..0f44c84 --- /dev/null +++ b/src/test/kotlin/core/routing/RoutingTableTest.kt @@ -0,0 +1,143 @@ +package core.routing + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.jetbrains.spek.api.dsl.on +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import testing.node + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object RoutingTableTest : Spek({ + + //region Helper methods + + /** + * Returns a valid route with the given preference value. + */ + fun route(preference: Int): Route = ValidFakeRoute(preference) + + /** + * Returns an invalid route. + */ + fun invalidRoute(): Route = InvalidFakeRoute + + //endregion + + given("an empty routing table") { + + val table = RoutingTable(invalidRoute()) + + on("getting the route for any neighbor") { + + val neighborRoute = table[node(1)] + + it("returns an invalid route") { + assertThat(neighborRoute.isValid(), `is`(false)) + } + } + + } + + given("a routing table with neighbor with ID 1") { + + val table = RoutingTable(invalidRoute(), listOf(node(1))) + + on("getting the route for neighbor 1") { + + val neighborRoute = table[node(1)] + + it("returns an invalid route") { + assertThat(neighborRoute.isValid(), `is`(false)) + } + } + + on("setting route with preference 10 for neighbor 1") { + + table[node(1)] = route(preference = 10) + + it("returns a route with preference 10 when getting the route for neighbor 1") { + assertThat(table[node(1)], `is`(route(preference = 10))) + } + } + + } + + given("a routing table with two neighbors with IDs 1 and 2") { + + val table = RoutingTable(invalidRoute(), listOf(node(1), node(2))) + + on("setting route with preference 10 for neighbor 1") { + + table[node(1)] = route(preference = 10) + + it("returns a valid route with preference 10 when getting the route for neighbor 1") { + assertThat(table[node(1)], `is`(route(preference = 10))) + } + + it("returns an invalid route when getting the route for neighbor 2") { + assertThat(table[node(2)], `is`(invalidRoute())) + } + } + + on("setting valid route for neighbor not included in the table") { + + table[node(5)] = route(preference = 10) + + it("returns an invalid route when getting route for that neighbor") { + // returning an invalid route indicates the neighbor was not added to the table + assertThat(table[node(5)], `is`(invalidRoute())) + } + } + + on("clearing the table") { + table.clear() + + it("returns an invalid route when getting the route for neighbor 1") { + assertThat(table[node(1)], `is`(invalidRoute())) + } + + it("returns an invalid route when getting the route for neighbor 2") { + assertThat(table[node(2)], `is`(invalidRoute())) + } + } + + on("setting route with preference 10 to neighbor 1") { + + table[node(1)] = route(preference = 10) + + it("returns a valid route with preference 10 when getting the route for neighbor 1") { + // this indicates the neighbors were not removed from the table when the table was cleared + assertThat(table[node(1)], `is`(route(preference = 10))) + } + } + + } + +}) + +//region Route implementation used for the tests + +/** + * Fake route implementation used to test the routing table. A fake route has a single integer attribute called + * preference. + */ +interface FakeRoute : Route { + val preference: Int +} + +data class ValidFakeRoute(override val preference: Int) : FakeRoute { + override fun isValid() = true +} + +object InvalidFakeRoute : FakeRoute { + override val preference = Int.MIN_VALUE + override fun isValid() = false +} + +//endregion From 57e2c01027abcebad7834c4e64ce38ed77b0fbf9 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 15:48:46 +0100 Subject: [PATCH 027/239] Moved Relationship class to the core.routing package --- src/main/kotlin/bgp/BGPNode.kt | 2 +- src/main/kotlin/core/routing/Relationship.kt | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 4c84516..8622a81 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -1,8 +1,8 @@ package bgp -import core.Relationship import core.routing.Node import core.routing.NodeID +import core.routing.Relationship /** * Created on 20-07-2017 diff --git a/src/main/kotlin/core/routing/Relationship.kt b/src/main/kotlin/core/routing/Relationship.kt index 7985888..3324ae5 100644 --- a/src/main/kotlin/core/routing/Relationship.kt +++ b/src/main/kotlin/core/routing/Relationship.kt @@ -1,8 +1,4 @@ -package core - -import core.routing.Extender -import core.routing.Node -import core.routing.Route +package core.routing /** * Created on 20-07-2017 From e3021281562adc5ee311786134ade7918e15c286 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 15:56:08 +0100 Subject: [PATCH 028/239] Moved FakeRoute implementation to its own file --- src/main/kotlin/core/routing/RoutingTable.kt | 3 +- .../kotlin/core/routing/RoutingTableTest.kt | 36 +--------------- src/test/kotlin/testing/FakeRoute.kt | 41 +++++++++++++++++++ 3 files changed, 45 insertions(+), 35 deletions(-) create mode 100644 src/test/kotlin/testing/FakeRoute.kt diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt index 0aa8591..14ad583 100644 --- a/src/main/kotlin/core/routing/RoutingTable.kt +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -5,7 +5,8 @@ package core.routing * * @author David Fialho */ -class RoutingTable(private val invalidRoute: R, neighbors: Collection = emptyList()) { +class RoutingTable +(private val invalidRoute: R, neighbors: Collection = emptyList()) { private val routes = HashMap(neighbors.size) init { diff --git a/src/test/kotlin/core/routing/RoutingTableTest.kt b/src/test/kotlin/core/routing/RoutingTableTest.kt index 0f44c84..e6b6162 100644 --- a/src/test/kotlin/core/routing/RoutingTableTest.kt +++ b/src/test/kotlin/core/routing/RoutingTableTest.kt @@ -6,7 +6,9 @@ import org.jetbrains.spek.api.dsl.it import org.jetbrains.spek.api.dsl.on import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.* +import testing.invalidRoute import testing.node +import testing.route /** * Created on 21-07-2017 @@ -15,20 +17,6 @@ import testing.node */ object RoutingTableTest : Spek({ - //region Helper methods - - /** - * Returns a valid route with the given preference value. - */ - fun route(preference: Int): Route = ValidFakeRoute(preference) - - /** - * Returns an invalid route. - */ - fun invalidRoute(): Route = InvalidFakeRoute - - //endregion - given("an empty routing table") { val table = RoutingTable(invalidRoute()) @@ -121,23 +109,3 @@ object RoutingTableTest : Spek({ }) -//region Route implementation used for the tests - -/** - * Fake route implementation used to test the routing table. A fake route has a single integer attribute called - * preference. - */ -interface FakeRoute : Route { - val preference: Int -} - -data class ValidFakeRoute(override val preference: Int) : FakeRoute { - override fun isValid() = true -} - -object InvalidFakeRoute : FakeRoute { - override val preference = Int.MIN_VALUE - override fun isValid() = false -} - -//endregion diff --git a/src/test/kotlin/testing/FakeRoute.kt b/src/test/kotlin/testing/FakeRoute.kt new file mode 100644 index 0000000..edb84c8 --- /dev/null +++ b/src/test/kotlin/testing/FakeRoute.kt @@ -0,0 +1,41 @@ +package testing + +import core.routing.Route + +/** + * Created on 21-07-2017 + * + * @author David Fialho + * + * This file contains a fake implementation for the route interface to be used in the test. It also includes some + * methods to obtain a route from this implementation. + * + * Fake route implementation used to test the routing table. A fake route has a single integer attribute called + * preference. + */ +interface FakeRoute : Route { + val preference: Int +} + +data class ValidFakeRoute(override val preference: Int) : FakeRoute { + override fun isValid() = true +} + +object InvalidFakeRoute : FakeRoute { + override val preference = Int.MIN_VALUE + override fun isValid() = false +} + +//region Factory methods + +/** + * Returns a valid route with the given preference value. + */ +fun route(preference: Int): Route = ValidFakeRoute(preference) + +/** + * Returns an invalid route. + */ +fun invalidRoute(): Route = InvalidFakeRoute + +//endregion \ No newline at end of file From 5925f393d567208f505e352c0c6a386e2f3283cb Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 16:06:09 +0100 Subject: [PATCH 029/239] Refactored the FakeRoute - it is now a sealed class --- src/test/kotlin/testing/FakeRoute.kt | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/test/kotlin/testing/FakeRoute.kt b/src/test/kotlin/testing/FakeRoute.kt index edb84c8..f2dbc71 100644 --- a/src/test/kotlin/testing/FakeRoute.kt +++ b/src/test/kotlin/testing/FakeRoute.kt @@ -13,17 +13,18 @@ import core.routing.Route * Fake route implementation used to test the routing table. A fake route has a single integer attribute called * preference. */ -interface FakeRoute : Route { - val preference: Int -} +sealed class FakeRoute : Route { -data class ValidFakeRoute(override val preference: Int) : FakeRoute { - override fun isValid() = true -} + abstract val preference: Int + + internal data class ValidFakeRoute(override val preference: Int) : FakeRoute() { + override fun isValid() = true + } -object InvalidFakeRoute : FakeRoute { - override val preference = Int.MIN_VALUE - override fun isValid() = false + internal object InvalidFakeRoute : FakeRoute() { + override val preference = Int.MIN_VALUE + override fun isValid() = false + } } //region Factory methods @@ -31,11 +32,11 @@ object InvalidFakeRoute : FakeRoute { /** * Returns a valid route with the given preference value. */ -fun route(preference: Int): Route = ValidFakeRoute(preference) +fun route(preference: Int): Route = FakeRoute.ValidFakeRoute(preference) /** * Returns an invalid route. */ -fun invalidRoute(): Route = InvalidFakeRoute +fun invalidRoute(): Route = FakeRoute.InvalidFakeRoute //endregion \ No newline at end of file From 3513b551dbdda6de190886c3d982c4d5bce8c258 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 17:49:08 +0100 Subject: [PATCH 030/239] Defined RouteSelector as an interface and implemented a base class --- .../kotlin/core/routing/BaseRouteSelector.kt | 74 ++++++++++++ src/main/kotlin/core/routing/RouteSelector.kt | 22 ++++ .../core/routing/BaseRouteSelectorTests.kt | 113 ++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 src/main/kotlin/core/routing/BaseRouteSelector.kt create mode 100644 src/main/kotlin/core/routing/RouteSelector.kt create mode 100644 src/test/kotlin/core/routing/BaseRouteSelectorTests.kt diff --git a/src/main/kotlin/core/routing/BaseRouteSelector.kt b/src/main/kotlin/core/routing/BaseRouteSelector.kt new file mode 100644 index 0000000..6937d50 --- /dev/null +++ b/src/main/kotlin/core/routing/BaseRouteSelector.kt @@ -0,0 +1,74 @@ +package core.routing + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +abstract class BaseRouteSelector(protected val defaultRoute: R) : RouteSelector { + + private val routes = HashMap() + private var hasSelectedNewRoute = true + + private var route = defaultRoute + private var neighbor: N? = null + + override var selectedRoute: R + get() { + hasSelectedNewRoute = false + return route + } + protected set(value) { + route = value + } + + override var selectedNeighbor: N? + get() { + hasSelectedNewRoute = false + return neighbor + } + protected set(value) { + neighbor = value + } + + override fun update(neighbor: N, route: R) { + routes[neighbor] = route + + if (neighbor == selectedNeighbor && compare(route, selectedRoute) != 0) { + val (newlySelectedRoute, newlySelectedNeighbor) = reselect() + updateSelected(newlySelectedRoute, newlySelectedNeighbor) + + } else if (compare(route, selectedRoute) > 0) { + updateSelected(route, neighbor) + } + + } + + override fun hasSelectedNewRoute() = hasSelectedNewRoute + + override fun disable(neighbor: N) { + TODO("not implemented") + } + + abstract protected fun compare(route1: R, route2: R): Int + + private fun updateSelected(route: R, neighbor: N?) { + this.route = route + this.neighbor = neighbor + hasSelectedNewRoute = true + } + + private fun reselect(): Pair { + + var selectedRoute = defaultRoute + var selectedNeighbor: N? = null + + for ((neighbor, route) in routes) if (compare(route, selectedRoute) > 0) { + selectedRoute = route + selectedNeighbor = neighbor + } + + return Pair(selectedRoute, selectedNeighbor) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt new file mode 100644 index 0000000..073e691 --- /dev/null +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -0,0 +1,22 @@ +package core.routing + +/** + * Created on 21-07-2017 + * + * @author David Fialho + * + * A route selector is responsible for selecting the best route in a routing table. + * It also works as a cache for the routing table + */ +interface RouteSelector { + + val selectedRoute: R + val selectedNeighbor: N? + + fun update(neighbor: N, route: R) + + fun hasSelectedNewRoute(): Boolean + + fun disable(neighbor: N) + +} \ No newline at end of file diff --git a/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt b/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt new file mode 100644 index 0000000..7d28295 --- /dev/null +++ b/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt @@ -0,0 +1,113 @@ +package core.routing + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import org.jetbrains.spek.api.dsl.on +import testing.* + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object BaseRouteSelectorTests : Spek({ + + given("a clean route selector") { + + val selector = routeSelector() + + it("has selected an invalid route") { + assertThat(selector.selectedRoute, `is`(invalidRoute())) + } + + it("has selected a neighbor that is null") { + assertThat(selector.selectedNeighbor, `is`(nullValue())) + } + + on("updating the route of neighbor 1 to route with preference 10") { + + selector.update(node(1), route(preference = 10)) + + it("has selected route with preference 10") { + assertThat(selector.selectedRoute, `is`(route(preference = 10))) + } + + it("has selected neighbor 1") { + assertThat(selector.selectedNeighbor, `is`(node(1))) + } + + } + + on("updating the route of neighbor 1 to route with preference 5") { + + selector.update(node(1), route(preference = 5)) + + it("has selected route with preference 5") { + assertThat(selector.selectedRoute, `is`(route(preference = 5))) + } + + it("has selected neighbor 1") { + assertThat(selector.selectedNeighbor, `is`(node(1))) + } + + } + + on("updating the route of neighbor 2 to route with preference 15") { + + selector.update(node(2), route(preference = 15)) + + it("has selected route with preference 15") { + assertThat(selector.selectedRoute, `is`(route(preference = 15))) + } + + it("has selected neighbor 2") { + assertThat(selector.selectedNeighbor, `is`(node(2))) + } + + } + + on("updating the route of neighbor 2 to route with preference 1") { + + selector.update(node(2), route(preference = 1)) + + it("has selected route with preference 5") { + assertThat(selector.selectedRoute, `is`(route(preference = 5))) + } + + it("has selected neighbor 1") { + assertThat(selector.selectedNeighbor, `is`(node(1))) + } + + } + } + +}) + +//region Fake implementation of RouteSelector used in the tests + +/** + * This is a fake implementation for the BaseRouteSelector class to be used for testing. It assumes the nodes and + * routes are FakeNode and FakeRoute, respectively. + * + * Not surprisingly, it considers routes with higher preference value to be preferred to routes with lower preference + * values. + */ +class FakeRouteSelector : BaseRouteSelector(FakeRoute.InvalidFakeRoute) { + + override fun compare(route1: Route, route2: Route): Int { + route1 as FakeRoute + route2 as FakeRoute + + return route1.preference.compareTo(route2.preference) + } +} + +/** + * Returns a route selector for testing. + */ +fun routeSelector(): RouteSelector = FakeRouteSelector() + +//endregion \ No newline at end of file From bf07ee39c5f5aa7e0645da83d8db9cd69ba53a05 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 18:15:28 +0100 Subject: [PATCH 031/239] Added forEach method to RoutingTable --- src/main/kotlin/core/routing/RoutingTable.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt index 14ad583..8729ddd 100644 --- a/src/main/kotlin/core/routing/RoutingTable.kt +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -5,8 +5,7 @@ package core.routing * * @author David Fialho */ -class RoutingTable -(private val invalidRoute: R, neighbors: Collection = emptyList()) { +class RoutingTable(val invalidRoute: R, neighbors: Collection = emptyList()) { private val routes = HashMap(neighbors.size) init { @@ -26,4 +25,10 @@ class RoutingTable routes.replaceAll { _,_ -> invalidRoute } } + internal fun forEach(operation: (N, R) -> Unit) { + for (route in routes) { + operation(route.key, route.value) + } + } + } \ No newline at end of file From 1c2cd92edb08d63e9a939568ec62034dfbd767a6 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 18:43:42 +0100 Subject: [PATCH 032/239] Added update() method to routing table --- src/main/kotlin/core/routing/RoutingTable.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt index 8729ddd..c069d9b 100644 --- a/src/main/kotlin/core/routing/RoutingTable.kt +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -20,6 +20,10 @@ class RoutingTable(val invalidRoute: R, neighbors: Collection routes.replace(neighbor, route) } + fun update(neighbor: N, route: R): Boolean { + return routes.replace(neighbor, route) != null + } + fun clear() { // Sets invalid route for all neighbors routes.replaceAll { _,_ -> invalidRoute } From 7c05ae766692a1ee52af5629fc19458c7d0a70d5 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 19:16:59 +0100 Subject: [PATCH 033/239] Reimplemented RouteSelector to be a single class that takes a compare function Removed BaseRouteSelector Adjusted the tests to the new RouteSelector --- .../kotlin/core/routing/BaseRouteSelector.kt | 74 ------- src/main/kotlin/core/routing/RouteSelector.kt | 53 ++++- .../core/routing/BaseRouteSelectorTests.kt | 113 ----------- .../kotlin/core/routing/RouteSelectorTests.kt | 191 ++++++++++++++++++ src/test/kotlin/testing/FakeRoute.kt | 13 ++ 5 files changed, 251 insertions(+), 193 deletions(-) delete mode 100644 src/main/kotlin/core/routing/BaseRouteSelector.kt delete mode 100644 src/test/kotlin/core/routing/BaseRouteSelectorTests.kt create mode 100644 src/test/kotlin/core/routing/RouteSelectorTests.kt diff --git a/src/main/kotlin/core/routing/BaseRouteSelector.kt b/src/main/kotlin/core/routing/BaseRouteSelector.kt deleted file mode 100644 index 6937d50..0000000 --- a/src/main/kotlin/core/routing/BaseRouteSelector.kt +++ /dev/null @@ -1,74 +0,0 @@ -package core.routing - -/** - * Created on 21-07-2017 - * - * @author David Fialho - */ -abstract class BaseRouteSelector(protected val defaultRoute: R) : RouteSelector { - - private val routes = HashMap() - private var hasSelectedNewRoute = true - - private var route = defaultRoute - private var neighbor: N? = null - - override var selectedRoute: R - get() { - hasSelectedNewRoute = false - return route - } - protected set(value) { - route = value - } - - override var selectedNeighbor: N? - get() { - hasSelectedNewRoute = false - return neighbor - } - protected set(value) { - neighbor = value - } - - override fun update(neighbor: N, route: R) { - routes[neighbor] = route - - if (neighbor == selectedNeighbor && compare(route, selectedRoute) != 0) { - val (newlySelectedRoute, newlySelectedNeighbor) = reselect() - updateSelected(newlySelectedRoute, newlySelectedNeighbor) - - } else if (compare(route, selectedRoute) > 0) { - updateSelected(route, neighbor) - } - - } - - override fun hasSelectedNewRoute() = hasSelectedNewRoute - - override fun disable(neighbor: N) { - TODO("not implemented") - } - - abstract protected fun compare(route1: R, route2: R): Int - - private fun updateSelected(route: R, neighbor: N?) { - this.route = route - this.neighbor = neighbor - hasSelectedNewRoute = true - } - - private fun reselect(): Pair { - - var selectedRoute = defaultRoute - var selectedNeighbor: N? = null - - for ((neighbor, route) in routes) if (compare(route, selectedRoute) > 0) { - selectedRoute = route - selectedNeighbor = neighbor - } - - return Pair(selectedRoute, selectedNeighbor) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index 073e691..a3a931d 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -7,16 +7,57 @@ package core.routing * * A route selector is responsible for selecting the best route in a routing table. * It also works as a cache for the routing table + * */ -interface RouteSelector { +class RouteSelector +(private val table: RoutingTable, forceReselect: Boolean = false, private val compare: (R, R) -> Int) { + + private var selectedRoute: R = table.invalidRoute + private var selectedNeighbor: N? = null + init { + if (forceReselect) { + val (route, neighbor) = reselect() + updateSelectedTo(route, neighbor) + } + } + + fun getSelectedRoute(): R = selectedRoute + + fun getSelectedNeighbor(): N? = selectedNeighbor + + fun update(neighbor: N, route: R) { + + val neighborExists = table.update(neighbor, route) + + if (!neighborExists) return + + if (neighbor == selectedNeighbor && compare(route, selectedRoute) != 0) { + val (newlySelectedRoute, newlySelectedNeighbor) = reselect() + updateSelectedTo(newlySelectedRoute, newlySelectedNeighbor) + + } else if (compare(route, selectedRoute) > 0) { + updateSelectedTo(route, neighbor) + } + } + + fun reselect(): Pair { - val selectedRoute: R - val selectedNeighbor: N? + var selectedRoute = table.invalidRoute + var selectedNeighbor: N? = null - fun update(neighbor: N, route: R) + table.forEach { neighbor, route -> if (compare(route, selectedRoute) > 0) { + selectedRoute = route + selectedNeighbor = neighbor + } + } - fun hasSelectedNewRoute(): Boolean + return Pair(selectedRoute, selectedNeighbor) + } - fun disable(neighbor: N) + @Suppress("NOTHING_TO_INLINE") + inline private fun updateSelectedTo(route: R, neighbor: N?) { + selectedRoute = route + selectedNeighbor = neighbor + } } \ No newline at end of file diff --git a/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt b/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt deleted file mode 100644 index 7d28295..0000000 --- a/src/test/kotlin/core/routing/BaseRouteSelectorTests.kt +++ /dev/null @@ -1,113 +0,0 @@ -package core.routing - -import org.jetbrains.spek.api.Spek -import org.jetbrains.spek.api.dsl.given -import org.jetbrains.spek.api.dsl.it -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.* -import org.jetbrains.spek.api.dsl.on -import testing.* - -/** - * Created on 21-07-2017 - - * @author David Fialho - */ -object BaseRouteSelectorTests : Spek({ - - given("a clean route selector") { - - val selector = routeSelector() - - it("has selected an invalid route") { - assertThat(selector.selectedRoute, `is`(invalidRoute())) - } - - it("has selected a neighbor that is null") { - assertThat(selector.selectedNeighbor, `is`(nullValue())) - } - - on("updating the route of neighbor 1 to route with preference 10") { - - selector.update(node(1), route(preference = 10)) - - it("has selected route with preference 10") { - assertThat(selector.selectedRoute, `is`(route(preference = 10))) - } - - it("has selected neighbor 1") { - assertThat(selector.selectedNeighbor, `is`(node(1))) - } - - } - - on("updating the route of neighbor 1 to route with preference 5") { - - selector.update(node(1), route(preference = 5)) - - it("has selected route with preference 5") { - assertThat(selector.selectedRoute, `is`(route(preference = 5))) - } - - it("has selected neighbor 1") { - assertThat(selector.selectedNeighbor, `is`(node(1))) - } - - } - - on("updating the route of neighbor 2 to route with preference 15") { - - selector.update(node(2), route(preference = 15)) - - it("has selected route with preference 15") { - assertThat(selector.selectedRoute, `is`(route(preference = 15))) - } - - it("has selected neighbor 2") { - assertThat(selector.selectedNeighbor, `is`(node(2))) - } - - } - - on("updating the route of neighbor 2 to route with preference 1") { - - selector.update(node(2), route(preference = 1)) - - it("has selected route with preference 5") { - assertThat(selector.selectedRoute, `is`(route(preference = 5))) - } - - it("has selected neighbor 1") { - assertThat(selector.selectedNeighbor, `is`(node(1))) - } - - } - } - -}) - -//region Fake implementation of RouteSelector used in the tests - -/** - * This is a fake implementation for the BaseRouteSelector class to be used for testing. It assumes the nodes and - * routes are FakeNode and FakeRoute, respectively. - * - * Not surprisingly, it considers routes with higher preference value to be preferred to routes with lower preference - * values. - */ -class FakeRouteSelector : BaseRouteSelector(FakeRoute.InvalidFakeRoute) { - - override fun compare(route1: Route, route2: Route): Int { - route1 as FakeRoute - route2 as FakeRoute - - return route1.preference.compareTo(route2.preference) - } -} - -/** - * Returns a route selector for testing. - */ -fun routeSelector(): RouteSelector = FakeRouteSelector() - -//endregion \ No newline at end of file diff --git a/src/test/kotlin/core/routing/RouteSelectorTests.kt b/src/test/kotlin/core/routing/RouteSelectorTests.kt new file mode 100644 index 0000000..cd74171 --- /dev/null +++ b/src/test/kotlin/core/routing/RouteSelectorTests.kt @@ -0,0 +1,191 @@ +package core.routing + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* +import org.jetbrains.spek.api.dsl.on +import testing.* + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object RouteSelectorTests : Spek({ + + //region Helper methods + + /** + * Returns a route selector for testing based on fake nodes and fake routes. + */ + fun routeSelector(table: RoutingTable): RouteSelector { + return RouteSelector(table, forceReselect = true, compare = ::fakeCompare) + } + + /** + * Returns an initialized routing table based on the pairs that are provided. + */ + fun table(vararg routes: Pair): RoutingTable { + + val neighbors = routes.map { it.first }.toList() + val table = RoutingTable(invalidRoute(), neighbors) + + for ((neighbor, route) in routes) { + table[neighbor] = route + } + + return table + } + + /** + * Allows us to write something like 'route(1) via node(1)' + */ + infix fun Route.via(neighbor: Node): Pair { + return Pair(neighbor, this) + } + + //endregion + + given("a route selector using a table with no neighbors") { + + val selector = routeSelector(table()) + + it("selects an invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("selects a null neighbor") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + + on("updating the route of some neighbor with ID 1 to route with preference 10") { + + selector.update(node(1), route(preference = 10)) + + it("still selects an invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("still selects a null neighbor") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + } + } + + given("a route selector using a table containing a route with preference 10 via a neighbor with ID 1") { + + val selector = routeSelector(table( + route(preference = 10) via node(1) + )) + + it("selects route with preference 10") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 10))) + } + + it("selects neighbor with ID 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + on("updating the route neighbor with ID 1 to route with preference 15") { + + selector.update(node(1), route(preference = 15)) + + it("selects route with preference 15") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) + } + + it("selects neighbor with ID 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + } + + on("updating the route of neighbor with ID 1 to invalid route") { + + selector.update(node(1), invalidRoute()) + + it("selects route invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("selects null neighbor") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + } + + } + + given("a route selector using a table containing invalid routes via neighbors 1 and 2") { + + val selector = routeSelector(table( + invalidRoute() via node(1), + invalidRoute() via node(2) + )) + + it("selects an invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("selects a neighbor that is null") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + + on("updating the route of neighbor 1 to route with preference 10") { + + selector.update(node(1), route(preference = 10)) + + it("selects route with preference 10") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 10))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + + on("updating the route of neighbor 1 to route with preference 5") { + + selector.update(node(1), route(preference = 5)) + + it("selects route with preference 5") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + + on("updating the route of neighbor 2 to route with preference 15") { + + selector.update(node(2), route(preference = 15)) + + it("selects route with preference 15") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) + } + + it("selects neighbor 2") { + assertThat(selector.getSelectedNeighbor(), `is`(node(2))) + } + + } + + on("updating the route of neighbor 2 to route with preference 1") { + + selector.update(node(2), route(preference = 1)) + + it("selects route with preference 5") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + } + +}) \ No newline at end of file diff --git a/src/test/kotlin/testing/FakeRoute.kt b/src/test/kotlin/testing/FakeRoute.kt index f2dbc71..7ca0d73 100644 --- a/src/test/kotlin/testing/FakeRoute.kt +++ b/src/test/kotlin/testing/FakeRoute.kt @@ -27,6 +27,19 @@ sealed class FakeRoute : Route { } } +/** + * A compare method for fake routes. + * + * Not surprisingly, it considers routes with higher preference value to be preferred to routes with lower + * preference values. + */ +fun fakeCompare(route1: Route, route2: Route): Int { + route1 as FakeRoute + route2 as FakeRoute + + return route1.preference.compareTo(route2.preference) +} + //region Factory methods /** From 3a9f1d7f47ecaac8bb6995bbe00cbc53a7bd1a47 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 19:26:18 +0100 Subject: [PATCH 034/239] Added documentation to route selector --- src/main/kotlin/core/routing/RouteSelector.kt | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index a3a931d..7903ab8 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -5,26 +5,54 @@ package core.routing * * @author David Fialho * - * A route selector is responsible for selecting the best route in a routing table. - * It also works as a cache for the routing table + * A route selector is responsible for selecting the best route in a routing table. It works similar to a cache: to + * update the routing table the update() method of the selector should be used. This allows the selector to adjust + * the selected route/neighbor in the most efficient way possible. * + * DO NOT update the routing table outside of the selector. Doing so will prevent the selector from working correctly + * since it is not informed of the changes performed to the table. + * + * The constructor takes the routing table holding the routes, a forceSelect flag, and a compare method. The force + * reselect flag if true will force the selector to reselect the route/neighbor based on the current routes of the + * given table. This flag should be set to true if and only if the table provided in the constructor already includes + * valid routes. The compare method should take to routes and compare their preferences. It should as a common + * compare method which returns a positive value if the left route has higher preference than the right route, 0 if + * they have the same preference and a negative value if the left route has a lower preference than the right route. + * + * @param table the table to select routes from + * @param forceReselect if set to true the selector will perform a reselect operation in the initializer + * @param compare the method used by the selector to compare the routes */ class RouteSelector (private val table: RoutingTable, forceReselect: Boolean = false, private val compare: (R, R) -> Int) { + // Stores the currently selected route private var selectedRoute: R = table.invalidRoute + // Stores the currently selected neighbor private var selectedNeighbor: N? = null + init { if (forceReselect) { - val (route, neighbor) = reselect() - updateSelectedTo(route, neighbor) + reselect() } } + /** + * Returns the currently selected route + */ fun getSelectedRoute(): R = selectedRoute + /** + * Returns the currently selected neighbor. + */ fun getSelectedNeighbor(): N? = selectedNeighbor + /** + * This method should always be used to update the routing table when a selector is being used. + * + * Updates the routing table, setting the given route as the candidate route via the given neighbor. + * The selected route/neighbor may also be updated if the given route/neighbor forces a reselection. + */ fun update(neighbor: N, route: R) { val neighborExists = table.update(neighbor, route) @@ -32,26 +60,27 @@ class RouteSelector if (!neighborExists) return if (neighbor == selectedNeighbor && compare(route, selectedRoute) != 0) { - val (newlySelectedRoute, newlySelectedNeighbor) = reselect() - updateSelectedTo(newlySelectedRoute, newlySelectedNeighbor) + reselect() } else if (compare(route, selectedRoute) > 0) { updateSelectedTo(route, neighbor) } } - fun reselect(): Pair { + /** + * Forces the selector to reselect the route/neighbor based on the current candidates routes available in the + * routing table. + */ + fun reselect() { - var selectedRoute = table.invalidRoute - var selectedNeighbor: N? = null + selectedRoute = table.invalidRoute + selectedNeighbor = null table.forEach { neighbor, route -> if (compare(route, selectedRoute) > 0) { selectedRoute = route selectedNeighbor = neighbor } } - - return Pair(selectedRoute, selectedNeighbor) } @Suppress("NOTHING_TO_INLINE") From 32a8ede61d6d0fb866389dfc933d6d0c75f4a8f0 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 19:44:47 +0100 Subject: [PATCH 035/239] Added documentation to teh routing table --- src/main/kotlin/core/routing/RoutingTable.kt | 26 ++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt index c069d9b..2291b6d 100644 --- a/src/main/kotlin/core/routing/RoutingTable.kt +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -4,6 +4,9 @@ package core.routing * Created on 21-07-2017 * * @author David Fialho + * + * The routing table stores a candidate route for each defined out-neighbor. + * It does not perform any route selection! For that use the RouteSelector. */ class RoutingTable(val invalidRoute: R, neighbors: Collection = emptyList()) { @@ -12,26 +15,45 @@ class RoutingTable(val invalidRoute: R, neighbors: Collection neighbors.forEach { routes[it] = invalidRoute } } + /** + * Returns the candidate route via a neighbor. + */ operator fun get(neighbor: N): R { return routes[neighbor] ?: invalidRoute } + /** + * Sets the candidate route via a neighbor. + * If the given node is not defined as a neighbor, then the table is not modified. + */ operator fun set(neighbor: N, route: R) { routes.replace(neighbor, route) } + /** + * Sets the candidate route via a neighbor. + * If the given node is not defined as a neighbor, then the table is not modified and the method returns false. + * + * @return true if the neighbor is defined and false if otherwise + */ fun update(neighbor: N, route: R): Boolean { return routes.replace(neighbor, route) != null } + /** + * Sets invalid routes for all defined neighbors. + */ fun clear() { // Sets invalid route for all neighbors routes.replaceAll { _,_ -> invalidRoute } } + /** + * Provides way to iterate over each entry in the table. + */ internal fun forEach(operation: (N, R) -> Unit) { - for (route in routes) { - operation(route.key, route.value) + for (entry in routes) { + operation(entry.key, entry.value) } } From 93046238328075f5a35b3494d4b77f88947850b8 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 20:53:57 +0100 Subject: [PATCH 036/239] Updated method from RouteSelector now indicates if it updated the selected route/neighbor or not --- src/main/kotlin/core/routing/RouteSelector.kt | 10 ++- .../kotlin/core/routing/RouteSelectorTests.kt | 77 +++++++++++++++++-- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index 7903ab8..0b1d667 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -52,19 +52,25 @@ class RouteSelector * * Updates the routing table, setting the given route as the candidate route via the given neighbor. * The selected route/neighbor may also be updated if the given route/neighbor forces a reselection. + * + * @return true if the selected route/neighbor was updated or false if otherwise */ - fun update(neighbor: N, route: R) { + fun update(neighbor: N, route: R): Boolean { val neighborExists = table.update(neighbor, route) - if (!neighborExists) return + if (!neighborExists) return false if (neighbor == selectedNeighbor && compare(route, selectedRoute) != 0) { reselect() + return true } else if (compare(route, selectedRoute) > 0) { updateSelectedTo(route, neighbor) + return true } + + return false } /** diff --git a/src/test/kotlin/core/routing/RouteSelectorTests.kt b/src/test/kotlin/core/routing/RouteSelectorTests.kt index cd74171..4962eaa 100644 --- a/src/test/kotlin/core/routing/RouteSelectorTests.kt +++ b/src/test/kotlin/core/routing/RouteSelectorTests.kt @@ -62,7 +62,7 @@ object RouteSelectorTests : Spek({ on("updating the route of some neighbor with ID 1 to route with preference 10") { - selector.update(node(1), route(preference = 10)) + val updated = selector.update(node(1), route(preference = 10)) it("still selects an invalid route") { assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) @@ -71,6 +71,10 @@ object RouteSelectorTests : Spek({ it("still selects a null neighbor") { assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) } + + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } } } @@ -90,7 +94,7 @@ object RouteSelectorTests : Spek({ on("updating the route neighbor with ID 1 to route with preference 15") { - selector.update(node(1), route(preference = 15)) + val updated = selector.update(node(1), route(preference = 15)) it("selects route with preference 15") { assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) @@ -99,11 +103,15 @@ object RouteSelectorTests : Spek({ it("selects neighbor with ID 1") { assertThat(selector.getSelectedNeighbor(), `is`(node(1))) } + + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } } on("updating the route of neighbor with ID 1 to invalid route") { - selector.update(node(1), invalidRoute()) + val updated = selector.update(node(1), invalidRoute()) it("selects route invalid route") { assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) @@ -112,6 +120,27 @@ object RouteSelectorTests : Spek({ it("selects null neighbor") { assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) } + + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + } + + on("updating the route of neighbor with ID 1 to invalid route again") { + + val updated = selector.update(node(1), invalidRoute()) + + it("selects route invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("selects null neighbor") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } } } @@ -133,7 +162,7 @@ object RouteSelectorTests : Spek({ on("updating the route of neighbor 1 to route with preference 10") { - selector.update(node(1), route(preference = 10)) + val updated = selector.update(node(1), route(preference = 10)) it("selects route with preference 10") { assertThat(selector.getSelectedRoute(), `is`(route(preference = 10))) @@ -143,11 +172,15 @@ object RouteSelectorTests : Spek({ assertThat(selector.getSelectedNeighbor(), `is`(node(1))) } + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + } on("updating the route of neighbor 1 to route with preference 5") { - selector.update(node(1), route(preference = 5)) + val updated = selector.update(node(1), route(preference = 5)) it("selects route with preference 5") { assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) @@ -157,11 +190,15 @@ object RouteSelectorTests : Spek({ assertThat(selector.getSelectedNeighbor(), `is`(node(1))) } + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + } on("updating the route of neighbor 2 to route with preference 15") { - selector.update(node(2), route(preference = 15)) + val updated = selector.update(node(2), route(preference = 15)) it("selects route with preference 15") { assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) @@ -171,11 +208,33 @@ object RouteSelectorTests : Spek({ assertThat(selector.getSelectedNeighbor(), `is`(node(2))) } + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + } on("updating the route of neighbor 2 to route with preference 1") { - selector.update(node(2), route(preference = 1)) + val updated = selector.update(node(2), route(preference = 1)) + + it("selects route with preference 5") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + + } + + on("updating the route of neighbor 2 to route with preference 3") { + + val updated = selector.update(node(2), route(preference = 3)) it("selects route with preference 5") { assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) @@ -185,6 +244,10 @@ object RouteSelectorTests : Spek({ assertThat(selector.getSelectedNeighbor(), `is`(node(1))) } + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } + } } From d4b9b160a240051792bc70a637a22857950a4cde Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 21:29:45 +0100 Subject: [PATCH 037/239] Implemented disabling and enabling neighbors --- src/main/kotlin/core/routing/RouteSelector.kt | 49 +++++++- .../kotlin/core/routing/RouteSelectorTests.kt | 114 ++++++++++++++++++ 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index 0b1d667..96eca7c 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -31,6 +31,8 @@ class RouteSelector // Stores the currently selected neighbor private var selectedNeighbor: N? = null + private val disabledNeighbors = HashSet() + init { if (forceReselect) { reselect() @@ -65,7 +67,7 @@ class RouteSelector reselect() return true - } else if (compare(route, selectedRoute) > 0) { + } else if (neighbor !in disabledNeighbors && compare(route, selectedRoute) > 0) { updateSelectedTo(route, neighbor) return true } @@ -73,6 +75,49 @@ class RouteSelector return false } + /** + * Disables a neighbor. Routes learned from a disabled neighbor are still stored in the routing table, but the + * selector will never select a candidate route associated with that neighbor. + * + * @return true if the selected route/neighbor was updated or false if otherwise + */ + fun disable(neighbor: N): Boolean { + + if (disabledNeighbors.add(neighbor)) { + // if i'm here then it actually disabled a neighbor + + if (neighbor == selectedNeighbor) { + reselect() + return true + } + + } + + return false + } + + /** + * Enables a neighbor that was disabled. If the neighbor was not disabled than nothing changes. + * + * @return true if the selected route/neighbor was updated or false if otherwise + */ + fun enable(neighbor: N): Boolean { + + if (disabledNeighbors.remove(neighbor)) { + // if i'm here then it actually enabled a neighbor + + val route = table[neighbor] + + if (compare(route, selectedRoute) > 0) { + updateSelectedTo(route, neighbor) + return true + } + + } + + return false + } + /** * Forces the selector to reselect the route/neighbor based on the current candidates routes available in the * routing table. @@ -82,7 +127,7 @@ class RouteSelector selectedRoute = table.invalidRoute selectedNeighbor = null - table.forEach { neighbor, route -> if (compare(route, selectedRoute) > 0) { + table.forEach { neighbor, route -> if (neighbor !in disabledNeighbors && compare(route, selectedRoute) > 0) { selectedRoute = route selectedNeighbor = neighbor } diff --git a/src/test/kotlin/core/routing/RouteSelectorTests.kt b/src/test/kotlin/core/routing/RouteSelectorTests.kt index 4962eaa..cb9715f 100644 --- a/src/test/kotlin/core/routing/RouteSelectorTests.kt +++ b/src/test/kotlin/core/routing/RouteSelectorTests.kt @@ -251,4 +251,118 @@ object RouteSelectorTests : Spek({ } } + given("a route selector using a table containing valid routes via neighbors 1 and 2 and selecting route via 1") { + + val selector = routeSelector(table( + route(preference = 10) via node(1), + route(preference = 5) via node(2) + )) + + on("disabling neighbor 1") { + + val updated = selector.disable(node(1)) + + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + + it("selects route via neighbor 2") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) + } + + it("selects neighbor 2") { + assertThat(selector.getSelectedNeighbor(), `is`(node(2))) + } + + } + + on("updating route via neighbor 1 to a route with preference 15") { + + val updated = selector.update(node(1), route(preference = 15)) + + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } + + it("selects route via neighbor 2") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 5))) + } + + it("selects neighbor 2") { + assertThat(selector.getSelectedNeighbor(), `is`(node(2))) + } + + } + + on("enabling neighbor 1") { + + val updated = selector.enable(node(1)) + + it("indicates the selected route/neighbor was updated") { + assertThat(updated, `is`(true)) + } + + it("selects route with preference 15") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + + on("disabling neighbor 2") { + + val updated = selector.disable(node(2)) + + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } + + it("selects route with preference 15") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + + on("enabling neighbor 2") { + + val updated = selector.enable(node(2)) + + it("indicates the selected route/neighbor was NOT updated") { + assertThat(updated, `is`(false)) + } + + it("selects route with preference 15") { + assertThat(selector.getSelectedRoute(), `is`(route(preference = 15))) + } + + it("selects neighbor 1") { + assertThat(selector.getSelectedNeighbor(), `is`(node(1))) + } + + } + + on("disabling neighbors 1 and 2") { + + selector.disable(node(1)) + selector.disable(node(2)) + + it("selects invalid route") { + assertThat(selector.getSelectedRoute(), `is`(invalidRoute())) + } + + it("selects null neighbor") { + assertThat(selector.getSelectedNeighbor(), `is`(nullValue())) + } + + } + + } + }) \ No newline at end of file From 66445913e9bfeb1565c983f388082e33c4af2337 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 21:32:35 +0100 Subject: [PATCH 038/239] Changed forceReselect default value to true --- src/main/kotlin/core/routing/RouteSelector.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index 96eca7c..72a0785 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -12,19 +12,20 @@ package core.routing * DO NOT update the routing table outside of the selector. Doing so will prevent the selector from working correctly * since it is not informed of the changes performed to the table. * - * The constructor takes the routing table holding the routes, a forceSelect flag, and a compare method. The force - * reselect flag if true will force the selector to reselect the route/neighbor based on the current routes of the - * given table. This flag should be set to true if and only if the table provided in the constructor already includes - * valid routes. The compare method should take to routes and compare their preferences. It should as a common - * compare method which returns a positive value if the left route has higher preference than the right route, 0 if - * they have the same preference and a negative value if the left route has a lower preference than the right route. + * The constructor takes the routing table holding the routes, a forceSelect flag, and a compare method. If the force + * reselect flag is set to true it will force the selector to reselect the route/neighbor based on the initial routes of + * the given table. By default, the flag is set to true. This flag should be set to false if and only if you are + * sure the table contains only invalid routes. The compare method should take to routes and compare their + * preferences. It should as a common compare method which returns a positive value if the left route has higher + * preference than the right route, 0 if they have the same preference and a negative value if the left route has a + * lower preference than the right route. * * @param table the table to select routes from * @param forceReselect if set to true the selector will perform a reselect operation in the initializer * @param compare the method used by the selector to compare the routes */ class RouteSelector -(private val table: RoutingTable, forceReselect: Boolean = false, private val compare: (R, R) -> Int) { +(private val table: RoutingTable, forceReselect: Boolean = true, private val compare: (R, R) -> Int) { // Stores the currently selected route private var selectedRoute: R = table.invalidRoute From 8251b2df3be917a65d012a6b5504af7dabffddbd Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 21:53:44 +0100 Subject: [PATCH 039/239] Reimplemented disabling neighbors using a flag in the routing table --- src/main/kotlin/core/routing/RouteSelector.kt | 31 +++++------- src/main/kotlin/core/routing/RoutingTable.kt | 47 +++++++++++++++---- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/core/routing/RouteSelector.kt b/src/main/kotlin/core/routing/RouteSelector.kt index 72a0785..db7f84a 100644 --- a/src/main/kotlin/core/routing/RouteSelector.kt +++ b/src/main/kotlin/core/routing/RouteSelector.kt @@ -32,8 +32,6 @@ class RouteSelector // Stores the currently selected neighbor private var selectedNeighbor: N? = null - private val disabledNeighbors = HashSet() - init { if (forceReselect) { reselect() @@ -68,7 +66,7 @@ class RouteSelector reselect() return true - } else if (neighbor !in disabledNeighbors && compare(route, selectedRoute) > 0) { + } else if (table.isEnabled(neighbor) && compare(route, selectedRoute) > 0) { updateSelectedTo(route, neighbor) return true } @@ -84,14 +82,11 @@ class RouteSelector */ fun disable(neighbor: N): Boolean { - if (disabledNeighbors.add(neighbor)) { - // if i'm here then it actually disabled a neighbor - - if (neighbor == selectedNeighbor) { - reselect() - return true - } + table.setEnabled(neighbor, false) + if (neighbor == selectedNeighbor) { + reselect() + return true } return false @@ -104,16 +99,13 @@ class RouteSelector */ fun enable(neighbor: N): Boolean { - if (disabledNeighbors.remove(neighbor)) { - // if i'm here then it actually enabled a neighbor + table.setEnabled(neighbor, true) - val route = table[neighbor] - - if (compare(route, selectedRoute) > 0) { - updateSelectedTo(route, neighbor) - return true - } + val route = table[neighbor] + if (compare(route, selectedRoute) > 0) { + updateSelectedTo(route, neighbor) + return true } return false @@ -128,7 +120,8 @@ class RouteSelector selectedRoute = table.invalidRoute selectedNeighbor = null - table.forEach { neighbor, route -> if (neighbor !in disabledNeighbors && compare(route, selectedRoute) > 0) { + table.forEach { neighbor, route, enabled -> if (enabled && compare(route, + selectedRoute) > 0) { selectedRoute = route selectedNeighbor = neighbor } diff --git a/src/main/kotlin/core/routing/RoutingTable.kt b/src/main/kotlin/core/routing/RoutingTable.kt index 2291b6d..a9740d5 100644 --- a/src/main/kotlin/core/routing/RoutingTable.kt +++ b/src/main/kotlin/core/routing/RoutingTable.kt @@ -6,20 +6,26 @@ package core.routing * @author David Fialho * * The routing table stores a candidate route for each defined out-neighbor. + * For each neighbor it holds a flag indicating if the neighbor is enabled or not. If the neighbor is set as disabled + * the route associated with that neighbor should not ever be selected. + * * It does not perform any route selection! For that use the RouteSelector. */ class RoutingTable(val invalidRoute: R, neighbors: Collection = emptyList()) { - private val routes = HashMap(neighbors.size) + data class Entry(var route: R, var enabled: Boolean) + + private val routes = HashMap>(neighbors.size) init { - neighbors.forEach { routes[it] = invalidRoute } + // By default, all neighbors are enabled + neighbors.forEach { routes[it] = Entry(invalidRoute, enabled = true) } } /** * Returns the candidate route via a neighbor. */ operator fun get(neighbor: N): R { - return routes[neighbor] ?: invalidRoute + return routes[neighbor]?.route ?: invalidRoute } /** @@ -27,7 +33,7 @@ class RoutingTable(val invalidRoute: R, neighbors: Collection * If the given node is not defined as a neighbor, then the table is not modified. */ operator fun set(neighbor: N, route: R) { - routes.replace(neighbor, route) + routes[neighbor]?.route = route } /** @@ -37,23 +43,44 @@ class RoutingTable(val invalidRoute: R, neighbors: Collection * @return true if the neighbor is defined and false if otherwise */ fun update(neighbor: N, route: R): Boolean { - return routes.replace(neighbor, route) != null + val entry = routes[neighbor] + + if (entry != null) { + entry.route = route + return true + } else { + return false + } } /** - * Sets invalid routes for all defined neighbors. + * Sets invalid routes for all defined neighbors an enables all disabled neighbors. */ fun clear() { // Sets invalid route for all neighbors - routes.replaceAll { _,_ -> invalidRoute } + routes.forEach { _, entry -> entry.route = invalidRoute } + } + + /** + * Sets the enable/disable flag for the given neighbor. + */ + fun setEnabled(neighbor: N, enabled: Boolean) { + routes[neighbor]?.enabled = enabled + } + + /** + * Checks if a neighbor is enabled or not + */ + fun isEnabled(neighbor: N): Boolean { + return routes[neighbor]?.enabled ?: false } /** * Provides way to iterate over each entry in the table. */ - internal fun forEach(operation: (N, R) -> Unit) { - for (entry in routes) { - operation(entry.key, entry.value) + internal fun forEach(operation: (N, R, Boolean) -> Unit) { + for ((neighbor, entry) in routes) { + operation(neighbor, entry.route, entry.enabled) } } From 84afbfd557d7b32417f0bbe8f57510fae5e876d5 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 16:04:06 +0100 Subject: [PATCH 040/239] Refactored the BGPRoute: it is now a sealed class --- src/main/kotlin/bgp/BGPRoute.kt | 65 +++++++++------------ src/test/kotlin/bgp/TopologyBuilderTests.kt | 2 +- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/bgp/BGPRoute.kt b/src/main/kotlin/bgp/BGPRoute.kt index 3c64bc6..5fd25f5 100644 --- a/src/main/kotlin/bgp/BGPRoute.kt +++ b/src/main/kotlin/bgp/BGPRoute.kt @@ -15,50 +15,37 @@ import core.routing.emptyPath * * BGP routes are always immutable instances! */ -interface BGPRoute : Route { +sealed class BGPRoute : Route { - val localPref: Int - val asPath: Path + abstract val localPref: Int + abstract val asPath: Path -} + companion object Factories { -/** - * An implementation for a valid BGP route. - */ -internal data class ValidBGPRoute(override val localPref: Int, override val asPath: Path) : BGPRoute { - - // A valid bgp route is always valid - override fun isValid(): Boolean = true - -} - -/** - * An implementation for a invalid BGP route. - */ -object InvalidBGPRoute : BGPRoute { + fun with(localPref: Int, asPath: Path): BGPRoute { + return ValidBGPRoute(localPref, asPath) + } - override val localPref: Int = Int.MIN_VALUE - override val asPath: Path = emptyPath() + fun invalid(): BGPRoute { + return InvalidBGPRoute + } - // A invalid bgp route is always invalid - override fun isValid(): Boolean = false + } -} + /** + * An implementation for a valid BGP route. + */ + private data class ValidBGPRoute(override val localPref: Int, override val asPath: Path) : BGPRoute() { + override fun isValid(): Boolean = true + } -//region Factory functions - -/** - * Returns a valid BGP route with the given LOCAL-PREF and AS-PATH. - */ -fun BGPRouteWith(localPref: Int, asPath: Path): BGPRoute { - return ValidBGPRoute(localPref, asPath) -} - -/** - * Returns an invalid BGP route - */ -fun invalidBGPRoute(): BGPRoute { - return InvalidBGPRoute -} + /** + * An implementation for a invalid BGP route. + */ + private object InvalidBGPRoute : BGPRoute() { + override val localPref: Int = Int.MIN_VALUE + override val asPath: Path = emptyPath() + override fun isValid(): Boolean = false + } -//endregion +} \ No newline at end of file diff --git a/src/test/kotlin/bgp/TopologyBuilderTests.kt b/src/test/kotlin/bgp/TopologyBuilderTests.kt index 9f2343e..36d488a 100644 --- a/src/test/kotlin/bgp/TopologyBuilderTests.kt +++ b/src/test/kotlin/bgp/TopologyBuilderTests.kt @@ -153,5 +153,5 @@ fun someExtender(): BGPExtender { } object FakeExtender : BGPExtender { - override fun extend(route: BGPRoute): BGPRoute = invalidBGPRoute() + override fun extend(route: BGPRoute): BGPRoute = BGPRoute.invalid() } From b1859f2f15db1238921b07df58eef38fbd9c3741 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 22:09:27 +0100 Subject: [PATCH 041/239] Path's constructor is now internal --- src/main/kotlin/core/routing/Path.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index cc28c2c..7c551d2 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -12,7 +12,7 @@ package core.routing * * @property size expresses the number of nodes in the path */ -class Path(private val nodes: List) { +class Pathinternal constructor(private val nodes: List) { val size: Int = nodes.size @@ -80,6 +80,7 @@ fun pathOf(vararg nodes: N): Path { /** * Returns an empty path. */ +@Suppress("NOTHING_TO_INLINE") inline fun pathOf(): Path { return emptyPath() } From 0a71f64027877df1f1aa674e9bc4d058d8bf4527 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 22:38:39 +0100 Subject: [PATCH 042/239] Added nextHop() method to Path --- src/main/kotlin/core/routing/Path.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/kotlin/core/routing/Path.kt b/src/main/kotlin/core/routing/Path.kt index 7c551d2..601c3dd 100644 --- a/src/main/kotlin/core/routing/Path.kt +++ b/src/main/kotlin/core/routing/Path.kt @@ -27,6 +27,13 @@ class Pathinternal constructor(private val nodes: List) { return Path(nodesCopy) } + /** + * Returns the next-hop node of the path. + */ + fun nextHop(): N? { + return nodes.lastOrNull() + } + /** * Checks if this path contains the given node. */ From 43abeab98f8d642e32c4818799881e4ffa158603 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 22:38:54 +0100 Subject: [PATCH 043/239] Implemented method to compare BGP routes --- src/main/kotlin/bgp/BGPRoute.kt | 29 +++++++++ src/test/kotlin/bgp/BGPRouteTests.kt | 93 ++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/test/kotlin/bgp/BGPRouteTests.kt diff --git a/src/main/kotlin/bgp/BGPRoute.kt b/src/main/kotlin/bgp/BGPRoute.kt index 5fd25f5..9b32647 100644 --- a/src/main/kotlin/bgp/BGPRoute.kt +++ b/src/main/kotlin/bgp/BGPRoute.kt @@ -48,4 +48,33 @@ sealed class BGPRoute : Route { override fun isValid(): Boolean = false } +} + +/** + * Compare function for BGP routes. It compares the preference of two BGP routes. + * + * The preference of a BGP route is determined based on the following attributes: + * + * 1. the LOCAL-PREF + * 2. the length of the AS-PATH + * 3. the ID of the next-hop node + * + * @return positive value if route1 is preferred to route 2; zero if they have the same preference; and negative + * value if route2 is preferred to route1 + */ +fun bgpRouteCompare(route1: BGPRoute, route2: BGPRoute): Int { + + var difference = route1.localPref.compareTo(route2.localPref) + if (difference == 0) { + difference = route2.asPath.size.compareTo(route1.asPath.size) + if (difference == 0) { + + val nextHop1 = route1.asPath.nextHop() ?: return 0 + val nextHop2 = route2.asPath.nextHop() ?: return 0 + + difference = nextHop2.id.compareTo(nextHop1.id) + } + } + + return difference } \ No newline at end of file diff --git a/src/test/kotlin/bgp/BGPRouteTests.kt b/src/test/kotlin/bgp/BGPRouteTests.kt new file mode 100644 index 0000000..96d2a95 --- /dev/null +++ b/src/test/kotlin/bgp/BGPRouteTests.kt @@ -0,0 +1,93 @@ +package bgp + +import core.routing.emptyPath +import core.routing.pathOf +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object BGPRouteTests : Spek({ + + given("route1 has higher LOCAL-PREF than route2") { + + it("returns positive value if length of the AS-PATH of route1 is shorter than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1))) + val route2 = BGPRoute.with(localPref = 5, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + + assertThat(bgpRouteCompare(route1, route2), greaterThan(0)) + } + + it("returns positive value if length of the AS-PATH of route1 is equal than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1))) + val route2 = BGPRoute.with(localPref = 5, asPath = pathOf(BGPNodeWith(id = 1))) + + assertThat(bgpRouteCompare(route1, route2), greaterThan(0)) + } + + it("returns positive value if length of the AS-PATH of route1 is longer than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1))) + val route2 = BGPRoute.with(localPref = 5, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + + assertThat(bgpRouteCompare(route1, route2), greaterThan(0)) + } + + } + + given("route1 has same LOCAL-PREF as route2 and they have different AS-PATH lengths") { + + it("returns positive value if length of the AS-PATH of route1 is shorter than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1))) + val route2 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + + assertThat(bgpRouteCompare(route1, route2), greaterThan(0)) + } + + it("returns negative value if length of the AS-PATH of route1 is longer than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + val route2 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1))) + + assertThat(bgpRouteCompare(route1, route2), lessThan(0)) + } + + } + + given("routes 1 and 2 have the same LOCAL-PREF the same AS-PATH length") { + + it("returns positive value if the ID of the next-hop node of route1 is lower than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + val route2 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 2), BGPNodeWith(id = 3))) + + assertThat(bgpRouteCompare(route1, route2), greaterThan(0)) + } + + it("returns negative value if the ID of the next-hop node of route1 is higher than route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 2), BGPNodeWith(id = 3))) + val route2 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 1), BGPNodeWith(id = 2))) + + assertThat(bgpRouteCompare(route1, route2), lessThan(0)) + } + + it("returns zero if the ID of the next-hop node of route1 is equal to route2's") { + val route1 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 2), BGPNodeWith(id = 1))) + val route2 = BGPRoute.with(localPref = 10, asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 1))) + + assertThat(bgpRouteCompare(route1, route2), equalTo(0)) + } + + it("returns zero if both AS-PATH are empty") { + val route1 = BGPRoute.with(localPref = 10, asPath = emptyPath()) + val route2 = BGPRoute.with(localPref = 10, asPath = emptyPath()) + + assertThat(bgpRouteCompare(route1, route2), equalTo(0)) + } + + } + +}) From d60b45a95313f79e2d783effa23ae67109ae39f1 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 23:35:46 +0100 Subject: [PATCH 044/239] Defined the message interface --- src/main/kotlin/core/routing/Message.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/kotlin/core/routing/Message.kt diff --git a/src/main/kotlin/core/routing/Message.kt b/src/main/kotlin/core/routing/Message.kt new file mode 100644 index 0000000..87f03df --- /dev/null +++ b/src/main/kotlin/core/routing/Message.kt @@ -0,0 +1,16 @@ +package core.routing + +/** + * Created on 21-07-2017 + * + * @author David Fialho + * + * Represents a message sent from one node to another. + */ +interface Message { + + /** + * Sends the message to the receiver. The receiver to which the message is sent is dependent on the implementation. + */ + fun send() +} \ No newline at end of file From 00ea25f6e0577b41819a1f14cd3a4f0158817d61 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 23:47:18 +0100 Subject: [PATCH 045/239] Defined the BGP message --- src/main/kotlin/bgp/BGPMessage.kt | 20 ++++++++++++++++++++ src/main/kotlin/bgp/BGPNode.kt | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/kotlin/bgp/BGPMessage.kt diff --git a/src/main/kotlin/bgp/BGPMessage.kt b/src/main/kotlin/bgp/BGPMessage.kt new file mode 100644 index 0000000..afeaa77 --- /dev/null +++ b/src/main/kotlin/bgp/BGPMessage.kt @@ -0,0 +1,20 @@ +package bgp + +import core.routing.Message + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +data class BGPMessage +(val sender: BGPNode, val receiver: BGPNode, val route: BGPRoute, val extender: BGPExtender) : Message { + + /** + * Sends the message to the receiver BGP node. + */ + override fun send() { + receiver.onReceivingMessage(this) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 8622a81..f22fb7e 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -12,6 +12,13 @@ import core.routing.Relationship class BGPNode internal constructor(id: NodeID, val relationships: MutableList>) : Node(id) { + /** + * This method should be called when a message is received by the node. + */ + fun onReceivingMessage(message: BGPMessage) { + TODO("not implemented yet") + } + /** * Adds a relationship to this node. */ From 3cd3afc431b6e1be819c030a6f7641ce2130bed2 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Fri, 21 Jul 2017 23:58:47 +0100 Subject: [PATCH 046/239] Added export method to BGPNode --- src/main/kotlin/bgp/BGPNode.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index f22fb7e..1f7e952 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -4,13 +4,15 @@ import core.routing.Node import core.routing.NodeID import core.routing.Relationship +typealias BGPRelationship = Relationship + /** * Created on 20-07-2017 * * @author David Fialho */ class BGPNode -internal constructor(id: NodeID, val relationships: MutableList>) : Node(id) { +internal constructor(id: NodeID, private val relationships: MutableList) : Node(id) { /** * This method should be called when a message is received by the node. @@ -19,11 +21,18 @@ internal constructor(id: NodeID, val relationships: MutableList Date: Sat, 22 Jul 2017 00:23:08 +0100 Subject: [PATCH 047/239] Defined the structure for implementing the protocols --- src/main/kotlin/bgp/BaseBGPProtocol.kt | 95 +++++++++++++++++++++++++ src/test/kotlin/bgp/BGPProtocolTests.kt | 26 +++++++ 2 files changed, 121 insertions(+) create mode 100644 src/main/kotlin/bgp/BaseBGPProtocol.kt create mode 100644 src/test/kotlin/bgp/BGPProtocolTests.kt diff --git a/src/main/kotlin/bgp/BaseBGPProtocol.kt b/src/main/kotlin/bgp/BaseBGPProtocol.kt new file mode 100644 index 0000000..8822d0a --- /dev/null +++ b/src/main/kotlin/bgp/BaseBGPProtocol.kt @@ -0,0 +1,95 @@ +package bgp + +import com.sun.xml.internal.ws.api.message.Message + +/** + * Created on 21-07-2017 + * + * @author David Fialho + */ +sealed class BaseBGPProtocol { + + /** + * Flag that indicates if a new route was selected as a result of processing a new incoming message. This flag is + * always set to false when a new message arrives and should only be set to true if a new route is selected when + * the message is being processed. + */ + protected var wasNewRouteSelected = false + + /** + * Processes a BGP message received by a node. + * May updated the routing table and the selected route/neighbor. + * + * @param message the message to be processed + */ + fun process(message: BGPMessage) { + + } + + /** + * Implements the process of importing a route. + * + * @param route the route received by the node (route obtained directly from the message) + * @param extender the extender used to import the route (extender included in the message) + */ + fun import(route: BGPRoute, extender: BGPExtender): BGPRoute { + return BGPRoute.invalid() + } + + /** + * Implements the process of learning a route. + * + * @param node the node processing the route + * @param sender the out-neighbor that sent the route + * @param route the route imported by the node (route obtained after applying the extender) + */ + fun learn(node: BGPNode, sender: BGPNode, route: BGPRoute): BGPRoute { + return BGPRoute.invalid() + } + + /** + * Implements the process of exporting a route. + * + * @param node the node processing the route + * @param selectedRoute the route imported by the node (route obtained after applying the extender) + */ + fun export(node: BGPNode, selectedRoute: BGPRoute) { + + } + + /** + * Called by the protocol when it detects a routing loop. + */ + protected abstract fun onLoopDetected(sender: BGPNode, route: BGPRoute) + +} + +//region Subclasses + +/** + * BGP Protocol: when a loop is detected it does nothing + */ +class BGPProtocol : BaseBGPProtocol() { override fun onLoopDetected(sender: BGPNode, route: BGPRoute) = Unit } + +/** + * SS-BGP Protocol: when a loop is detected it tries to detect if the loop is recurrent using the WEAK detection + * condition. If it determines the loop is recurrent, it disables the neighbor that exported the route. + */ +class SSBGPProtocol : BaseBGPProtocol() { + + override fun onLoopDetected(sender: BGPNode, route: BGPRoute) { + TODO("not implemented") + } +} + +/** + * SS-BGP Protocol: when a loop is detected it tries to detect if the loop is recurrent using the STRONG detection + * condition. If it determines the loop is recurrent, it disables the neighbor that exported the route. + */ +class ISSBGPProtocol : BaseBGPProtocol() { + override fun onLoopDetected(sender: BGPNode, route: BGPRoute) { + TODO("not implemented") + } +} + +//endregion \ No newline at end of file diff --git a/src/test/kotlin/bgp/BGPProtocolTests.kt b/src/test/kotlin/bgp/BGPProtocolTests.kt new file mode 100644 index 0000000..ba06403 --- /dev/null +++ b/src/test/kotlin/bgp/BGPProtocolTests.kt @@ -0,0 +1,26 @@ +package bgp + +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.* + +/** + * Created on 21-07-2017 + + * @author David Fialho + */ +object BGPProtocolLearnTests : Spek({ + + given("") { + + + } + +}) + +object BGPProtocolProcessTests : Spek({ + + +}) From daaf83ed770fd116f9ddfd76e2f33d9282a460e3 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 08:41:19 +0100 Subject: [PATCH 048/239] Implemented the import() method --- src/main/kotlin/bgp/BaseBGPProtocol.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/bgp/BaseBGPProtocol.kt b/src/main/kotlin/bgp/BaseBGPProtocol.kt index 8822d0a..be3ab50 100644 --- a/src/main/kotlin/bgp/BaseBGPProtocol.kt +++ b/src/main/kotlin/bgp/BaseBGPProtocol.kt @@ -1,7 +1,5 @@ package bgp -import com.sun.xml.internal.ws.api.message.Message - /** * Created on 21-07-2017 * @@ -28,12 +26,13 @@ sealed class BaseBGPProtocol { /** * Implements the process of importing a route. + * Returns the result of extending the given route with the given extender. * * @param route the route received by the node (route obtained directly from the message) * @param extender the extender used to import the route (extender included in the message) */ fun import(route: BGPRoute, extender: BGPExtender): BGPRoute { - return BGPRoute.invalid() + return extender.extend(route) } /** @@ -50,10 +49,10 @@ sealed class BaseBGPProtocol { /** * Implements the process of exporting a route. * - * @param node the node processing the route - * @param selectedRoute the route imported by the node (route obtained after applying the extender) + * @param node the node processing the route + * @param route the route to be exported (route selected by the node) */ - fun export(node: BGPNode, selectedRoute: BGPRoute) { + fun export(node: BGPNode, route: BGPRoute) { } From d94efa88a83edf7296a8eef613b6b5684f5d6e8b Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 09:01:34 +0100 Subject: [PATCH 049/239] Fixed compile issue: BGP node now provides access to the relationships through an immutable list --- src/main/kotlin/bgp/BGPNode.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 1f7e952..270c02a 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -12,7 +12,11 @@ typealias BGPRelationship = Relationship * @author David Fialho */ class BGPNode -internal constructor(id: NodeID, private val relationships: MutableList) : Node(id) { +internal constructor(id: NodeID, private val mutableRelationships: MutableList) : Node(id) { + + // Gives public access to the relationships of the node without providing the ability to change the relationships + val relationships: List + get () = mutableRelationships /** * This method should be called when a message is received by the node. @@ -32,7 +36,7 @@ internal constructor(id: NodeID, private val relationships: MutableList Date: Sat, 22 Jul 2017 09:29:51 +0100 Subject: [PATCH 050/239] Implemented the 'learn' method --- src/main/kotlin/bgp/BaseBGPProtocol.kt | 20 +++++++- src/test/kotlin/bgp/BGPProtocolTests.kt | 68 ++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/bgp/BaseBGPProtocol.kt b/src/main/kotlin/bgp/BaseBGPProtocol.kt index be3ab50..39f84d1 100644 --- a/src/main/kotlin/bgp/BaseBGPProtocol.kt +++ b/src/main/kotlin/bgp/BaseBGPProtocol.kt @@ -12,7 +12,12 @@ sealed class BaseBGPProtocol { * always set to false when a new message arrives and should only be set to true if a new route is selected when * the message is being processed. */ - protected var wasNewRouteSelected = false + private var wasNewRouteSelected: Boolean = false + + /** + * Indicates if the selected route was updated in the last call to process. + */ + fun wasSelectedRouteUpdated(): Boolean = wasNewRouteSelected /** * Processes a BGP message received by a node. @@ -41,9 +46,20 @@ sealed class BaseBGPProtocol { * @param node the node processing the route * @param sender the out-neighbor that sent the route * @param route the route imported by the node (route obtained after applying the extender) + * @return the imported route if the route's AS-PATH does not include the node learning the route or it returns + * an invalid if the route's AS-PATH includes the learning node. Note that it may also return an invalid route if + * the imported route is invalid. */ fun learn(node: BGPNode, sender: BGPNode, route: BGPRoute): BGPRoute { - return BGPRoute.invalid() + + if (node in route.asPath) { + // Notify the implementations that a loop was detected + onLoopDetected(sender, route) + + return BGPRoute.invalid() + } else { + return route + } } /** diff --git a/src/test/kotlin/bgp/BGPProtocolTests.kt b/src/test/kotlin/bgp/BGPProtocolTests.kt index ba06403..5fd9039 100644 --- a/src/test/kotlin/bgp/BGPProtocolTests.kt +++ b/src/test/kotlin/bgp/BGPProtocolTests.kt @@ -1,21 +1,85 @@ package bgp +import core.routing.Path +import core.routing.pathOf import org.jetbrains.spek.api.Spek import org.jetbrains.spek.api.dsl.given import org.jetbrains.spek.api.dsl.it import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.* +import org.jetbrains.spek.api.dsl.context /** * Created on 21-07-2017 * @author David Fialho */ -object BGPProtocolLearnTests : Spek({ +object BGPProtocolTests : Spek({ - given("") { + //region Helper methods + /** + * Shorter way to create a valid BGP route. + */ + fun route(localPref: Int, asPath: Path) = BGPRoute.with(localPref, asPath) + /** + * Shorter way to create an invalid BGP route. + */ + fun invalid() = BGPRoute.invalid() + + //endregion + + context("BGP protocol: node with ID 1 learns a route imported from node with ID 2") { + + val protocol = BGPProtocol() + val node = BGPNodeWith(id = 1) + val sender = BGPNodeWith(id = 2) + + given("imported route is invalid") { + + val learnedRoute = protocol.learn(node, sender, invalid()) + + it("learns an invalid route") { + assertThat(learnedRoute, `is`(invalid())) + } + + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } + } + + given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 2]") { + + val importedRoute = route(localPref = 10, asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 2))) + val learnedRoute = protocol.learn(node, sender, importedRoute) + + it("learns the imported route") { + assertThat(learnedRoute, `is`(importedRoute)) + } + + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } + } + + given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 1, 2]") { + + val importedRoute = route( + localPref = 10, + asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 1), BGPNodeWith(id = 2)) + ) + + val learnedRoute = protocol.learn(node, sender, importedRoute) + + it("learns an invalid route") { + assertThat(learnedRoute, `is`(invalid())) + } + + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } + } } }) From b897b0b6e0b3c3e1e1a436d02c46bcc62177ba62 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 09:46:50 +0100 Subject: [PATCH 051/239] Refactored tests for BGP protocol --- src/test/kotlin/bgp/BGPProtocolTests.kt | 67 +++++++++++++------------ 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/test/kotlin/bgp/BGPProtocolTests.kt b/src/test/kotlin/bgp/BGPProtocolTests.kt index 5fd9039..729556d 100644 --- a/src/test/kotlin/bgp/BGPProtocolTests.kt +++ b/src/test/kotlin/bgp/BGPProtocolTests.kt @@ -30,56 +30,61 @@ object BGPProtocolTests : Spek({ //endregion - context("BGP protocol: node with ID 1 learns a route imported from node with ID 2") { + context("BGP Protocol") { val protocol = BGPProtocol() - val node = BGPNodeWith(id = 1) - val sender = BGPNodeWith(id = 2) - given("imported route is invalid") { + context("node with ID 1 learns a route imported from node with ID 2") { - val learnedRoute = protocol.learn(node, sender, invalid()) + val node = BGPNodeWith(id = 1) + val sender = BGPNodeWith(id = 2) - it("learns an invalid route") { - assertThat(learnedRoute, `is`(invalid())) - } + given("imported route is invalid") { + + val learnedRoute = protocol.learn(node, sender, invalid()) - it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + it("learns an invalid route") { + assertThat(learnedRoute, `is`(invalid())) + } + + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } } - } - given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 2]") { + given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 2]") { - val importedRoute = route(localPref = 10, asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 2))) - val learnedRoute = protocol.learn(node, sender, importedRoute) + val importedRoute = route(localPref = 10, asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 2))) + val learnedRoute = protocol.learn(node, sender, importedRoute) - it("learns the imported route") { - assertThat(learnedRoute, `is`(importedRoute)) - } + it("learns the imported route") { + assertThat(learnedRoute, `is`(importedRoute)) + } - it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } } - } - given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 1, 2]") { + given("imported route is valid with LOCAL-PREF 10 and AS-PATH [3, 1, 2]") { - val importedRoute = route( - localPref = 10, - asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 1), BGPNodeWith(id = 2)) - ) + val importedRoute = route( + localPref = 10, + asPath = pathOf(BGPNodeWith(id = 3), BGPNodeWith(id = 1), BGPNodeWith(id = 2)) + ) - val learnedRoute = protocol.learn(node, sender, importedRoute) + val learnedRoute = protocol.learn(node, sender, importedRoute) - it("learns an invalid route") { - assertThat(learnedRoute, `is`(invalid())) - } + it("learns an invalid route") { + assertThat(learnedRoute, `is`(invalid())) + } - it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + it("indicates the selected route was not updated") { + assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + } } } + } }) From fe9af311e615eeb33546f7f537a8dba2aaacdc8c Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 09:58:53 +0100 Subject: [PATCH 052/239] Added routing table to BGP node --- src/main/kotlin/bgp/BGPNode.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 270c02a..aaf8d73 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -1,8 +1,6 @@ package bgp -import core.routing.Node -import core.routing.NodeID -import core.routing.Relationship +import core.routing.* typealias BGPRelationship = Relationship @@ -18,6 +16,14 @@ internal constructor(id: NodeID, private val mutableRelationships: MutableList get () = mutableRelationships + val routingTable = RouteSelector( + table = RoutingTable( + invalidRoute = BGPRoute.invalid(), + neighbors = relationships.map { it.node }), + forceReselect = false, + compare = ::bgpRouteCompare + ) + /** * This method should be called when a message is received by the node. */ From b5483ad12477e9116cb2c4c3483cdf59fd802f64 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 09:59:26 +0100 Subject: [PATCH 053/239] Implemented the 'process' method --- src/main/kotlin/bgp/BaseBGPProtocol.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/kotlin/bgp/BaseBGPProtocol.kt b/src/main/kotlin/bgp/BaseBGPProtocol.kt index 39f84d1..75c358f 100644 --- a/src/main/kotlin/bgp/BaseBGPProtocol.kt +++ b/src/main/kotlin/bgp/BaseBGPProtocol.kt @@ -27,6 +27,19 @@ sealed class BaseBGPProtocol { */ fun process(message: BGPMessage) { + val node = message.receiver + val importedRoute = import(message.route, message.extender) + val learnedRoute = learn(node, message.sender, importedRoute) + + val updated = node.routingTable.update(message.sender, learnedRoute) + + // Set updated flag to true if 'updated' is true or keep its current state + wasNewRouteSelected = wasNewRouteSelected || updated + + if (wasNewRouteSelected) { + export(node, node.routingTable.getSelectedRoute()) + } + } /** From ff3a06822b35a53c38cf0a62b8bf9c9d25cb64a3 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 10:01:58 +0100 Subject: [PATCH 054/239] Refactored selected route updated flag --- src/main/kotlin/bgp/BaseBGPProtocol.kt | 12 ++++-------- src/test/kotlin/bgp/BGPProtocolTests.kt | 6 +++--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/bgp/BaseBGPProtocol.kt b/src/main/kotlin/bgp/BaseBGPProtocol.kt index 75c358f..18d53f3 100644 --- a/src/main/kotlin/bgp/BaseBGPProtocol.kt +++ b/src/main/kotlin/bgp/BaseBGPProtocol.kt @@ -12,12 +12,8 @@ sealed class BaseBGPProtocol { * always set to false when a new message arrives and should only be set to true if a new route is selected when * the message is being processed. */ - private var wasNewRouteSelected: Boolean = false - - /** - * Indicates if the selected route was updated in the last call to process. - */ - fun wasSelectedRouteUpdated(): Boolean = wasNewRouteSelected + var wasSelectedRouteUpdated: Boolean = false + private set /** * Processes a BGP message received by a node. @@ -34,9 +30,9 @@ sealed class BaseBGPProtocol { val updated = node.routingTable.update(message.sender, learnedRoute) // Set updated flag to true if 'updated' is true or keep its current state - wasNewRouteSelected = wasNewRouteSelected || updated + wasSelectedRouteUpdated = wasSelectedRouteUpdated || updated - if (wasNewRouteSelected) { + if (wasSelectedRouteUpdated) { export(node, node.routingTable.getSelectedRoute()) } diff --git a/src/test/kotlin/bgp/BGPProtocolTests.kt b/src/test/kotlin/bgp/BGPProtocolTests.kt index 729556d..4a281e1 100644 --- a/src/test/kotlin/bgp/BGPProtocolTests.kt +++ b/src/test/kotlin/bgp/BGPProtocolTests.kt @@ -48,7 +48,7 @@ object BGPProtocolTests : Spek({ } it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + assertThat(protocol.wasSelectedRouteUpdated, `is`(false)) } } @@ -62,7 +62,7 @@ object BGPProtocolTests : Spek({ } it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + assertThat(protocol.wasSelectedRouteUpdated, `is`(false)) } } @@ -80,7 +80,7 @@ object BGPProtocolTests : Spek({ } it("indicates the selected route was not updated") { - assertThat(protocol.wasSelectedRouteUpdated(), `is`(false)) + assertThat(protocol.wasSelectedRouteUpdated, `is`(false)) } } } From 8cd2452c52c57079a64f8bbbc0429382ffa8a65d Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 10:29:21 +0100 Subject: [PATCH 055/239] Fixed compile error related to BGPNode relationships --- src/main/kotlin/bgp/BGPNode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/bgp/BGPNode.kt b/src/main/kotlin/bgp/BGPNode.kt index 1f7e952..bdab634 100644 --- a/src/main/kotlin/bgp/BGPNode.kt +++ b/src/main/kotlin/bgp/BGPNode.kt @@ -12,7 +12,7 @@ typealias BGPRelationship = Relationship * @author David Fialho */ class BGPNode -internal constructor(id: NodeID, private val relationships: MutableList) : Node(id) { +internal constructor(id: NodeID, val relationships: MutableList) : Node(id) { /** * This method should be called when a message is received by the node. From 6f32c2c8ed62c4be0c36c02c365827a658c83e8a Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 10:08:10 +0100 Subject: [PATCH 056/239] Defined Time as an alias for Int --- src/main/kotlin/core/simulator/Time.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/kotlin/core/simulator/Time.kt diff --git a/src/main/kotlin/core/simulator/Time.kt b/src/main/kotlin/core/simulator/Time.kt new file mode 100644 index 0000000..c25c326 --- /dev/null +++ b/src/main/kotlin/core/simulator/Time.kt @@ -0,0 +1,8 @@ +package core.simulator + +/** + * Created on 22-07-2017 + * + * @author David Fialho + */ +typealias Time = Int \ No newline at end of file From 3e2efc646874d11a806dc98ff6699a08ff19c089 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 10:27:27 +0100 Subject: [PATCH 057/239] Defined the Event interface --- src/main/kotlin/core/simulator/Event.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/kotlin/core/simulator/Event.kt diff --git a/src/main/kotlin/core/simulator/Event.kt b/src/main/kotlin/core/simulator/Event.kt new file mode 100644 index 0000000..65d638f --- /dev/null +++ b/src/main/kotlin/core/simulator/Event.kt @@ -0,0 +1,14 @@ +package core.simulator + +/** + * Created on 22-07-2017 + * + * @author David Fialho + */ +interface Event { + + /** + * Processes this event. + */ + fun processIt() +} \ No newline at end of file From 11880eee219c16103eb5018fcf732ae6c1f63ff3 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Sat, 22 Jul 2017 10:27:45 +0100 Subject: [PATCH 058/239] Defined the interface for the Scheduler class --- src/main/kotlin/core/simulator/Scheduler.kt | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/kotlin/core/simulator/Scheduler.kt diff --git a/src/main/kotlin/core/simulator/Scheduler.kt b/src/main/kotlin/core/simulator/Scheduler.kt new file mode 100644 index 0000000..44eee37 --- /dev/null +++ b/src/main/kotlin/core/simulator/Scheduler.kt @@ -0,0 +1,57 @@ +package core.simulator + +/** + * Created on 22-07-2017 + * + * @author David Fialho + */ +class Scheduler { + + /** + * A scheduled event associates a timestamp with an event. The timestamp is used by the scheduler to determine + * the order in which the event occur. + */ + private data class ScheduledEvent(val time: Time, val event: Event) : Comparable