From 13a6394e0c05f1eb806773ade77f36962b0e4f41 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Mon, 11 Sep 2017 15:03:51 +0100 Subject: [PATCH 1/4] Bump version to 1.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7c42f5e..b7ac60e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'ssbgp' -version '1.1-SNAPSHOT' +version '1.2-SNAPSHOT' buildscript { ext.kotlin_version = '1.1.4-3' From b9234766bc8941ffc616214f5115e13baee2f977 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Mon, 11 Sep 2017 18:29:36 +0100 Subject: [PATCH 2/4] Add support to Interdomain policies for peer* neighbors --- .../interdomain/InterdomainExtenders.kt | 45 ++-- .../kotlin/bgp/policies/interdomain/Routes.kt | 6 + .../interdomain/InterdomainExtendersTests.kt | 226 ++++++++++++++++++ 3 files changed, 261 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/bgp/policies/interdomain/InterdomainExtenders.kt b/src/main/kotlin/bgp/policies/interdomain/InterdomainExtenders.kt index 1fff59c..15642fb 100644 --- a/src/main/kotlin/bgp/policies/interdomain/InterdomainExtenders.kt +++ b/src/main/kotlin/bgp/policies/interdomain/InterdomainExtenders.kt @@ -10,68 +10,81 @@ import core.routing.Node * @author David Fialho */ -const val LOCAL_PREF_PEERPLUS: Int = 400000 +const val LOCAL_PREF_PEERPLUS: Int = 500000 +const val LOCAL_PREF_PEERSTAR: Int = 400000 const val LOCAL_PREF_CUSTOMER: Int = 300000 const val LOCAL_PREF_PEER: Int = 200000 const val LOCAL_PREF_PROVIDER: Int = 100000 -object CustomerExtender : Extender { +object CustomerExtender: Extender { override fun extend(route: BGPRoute, sender: Node): BGPRoute { return when { - route.localPref <= LOCAL_PREF_PEER -> BGPRoute.invalid() - else -> customerRoute(asPath = route.asPath.append(sender)) + route.localPref <= LOCAL_PREF_PEER || route.localPref == LOCAL_PREF_PEERSTAR -> BGPRoute.invalid() + else -> customerRoute(asPath = route.asPath.append(sender)) } } } -object PeerExtender : Extender { +object PeerExtender: Extender { override fun extend(route: BGPRoute, sender: Node): BGPRoute { return when { - route.localPref <= LOCAL_PREF_PEER -> BGPRoute.invalid() - else -> peerRoute(asPath = route.asPath.append(sender)) + route.localPref <= LOCAL_PREF_PEER || route.localPref == LOCAL_PREF_PEERSTAR -> BGPRoute.invalid() + else -> peerRoute(asPath = route.asPath.append(sender)) } } } -object ProviderExtender : Extender { +object ProviderExtender: Extender { override fun extend(route: BGPRoute, sender: Node): BGPRoute { return when { !route.isValid() -> BGPRoute.invalid() - else -> providerRoute(asPath = route.asPath.append(sender)) + else -> providerRoute(asPath = route.asPath.append(sender)) } } } -object PeerplusExtender : Extender { +object PeerplusExtender: Extender { override fun extend(route: BGPRoute, sender: Node): BGPRoute { return when { - route.localPref <= LOCAL_PREF_PEER -> BGPRoute.invalid() - else -> peerplusRoute(asPath = route.asPath.append(sender)) + route.localPref <= LOCAL_PREF_PEER || route.localPref == LOCAL_PREF_PEERSTAR -> BGPRoute.invalid() + else -> peerplusRoute(asPath = route.asPath.append(sender)) } } } -object SiblingExtender : Extender { +object PeerstarExtender: Extender { override fun extend(route: BGPRoute, sender: Node): BGPRoute { return when { - !route.isValid() -> BGPRoute.invalid() + route.localPref <= LOCAL_PREF_PEER || route.localPref == LOCAL_PREF_PEERSTAR -> BGPRoute.invalid() + else -> peerstarRoute(asPath = route.asPath.append(sender)) + } + } + +} + +object SiblingExtender: Extender { + + override fun extend(route: BGPRoute, sender: Node): BGPRoute { + + return when { + !route.isValid() -> BGPRoute.invalid() route === BGPRoute.self() -> customerRoute(siblingHops = 1, asPath = route.asPath.append(sender)) - else -> BGPRoute.with(localPref = route.localPref - 1, - asPath = route.asPath.append(sender)) + else -> BGPRoute.with(localPref = route.localPref - 1, + asPath = route.asPath.append(sender)) } } diff --git a/src/main/kotlin/bgp/policies/interdomain/Routes.kt b/src/main/kotlin/bgp/policies/interdomain/Routes.kt index 5fe804d..db6c805 100644 --- a/src/main/kotlin/bgp/policies/interdomain/Routes.kt +++ b/src/main/kotlin/bgp/policies/interdomain/Routes.kt @@ -18,6 +18,12 @@ import core.routing.emptyPath fun peerplusRoute(siblingHops: Int = 0, asPath: Path = emptyPath()) = BGPRoute.with(localPref = LOCAL_PREF_PEERPLUS - siblingHops, asPath = asPath) +/** + * Returns a peer* route. + */ +fun peerstarRoute(siblingHops: Int = 0, asPath: Path = emptyPath()) + = BGPRoute.with(localPref = LOCAL_PREF_PEERSTAR - siblingHops, asPath = asPath) + /** * Returns a customer route. */ diff --git a/src/test/kotlin/bgp/policies/interdomain/InterdomainExtendersTests.kt b/src/test/kotlin/bgp/policies/interdomain/InterdomainExtendersTests.kt index 0f0d2a0..c2b5f92 100644 --- a/src/test/kotlin/bgp/policies/interdomain/InterdomainExtendersTests.kt +++ b/src/test/kotlin/bgp/policies/interdomain/InterdomainExtendersTests.kt @@ -59,6 +59,19 @@ object InterdomainExtendersTests : Spek({ } } + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = CustomerExtender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + on("extending a peer route") { val route = peerRoute(asPath = emptyPath()) @@ -205,6 +218,19 @@ object InterdomainExtendersTests : Spek({ } } + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = PeerExtender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + on("extending a peer route") { val route = peerRoute(asPath = emptyPath()) @@ -351,6 +377,19 @@ object InterdomainExtendersTests : Spek({ } } + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = ProviderExtender.extend(route, sender) + + it("returns provider route") { + assertThat(extendedRoute, + Is(providerRoute(asPath = pathOf(sender)))) + } + } + on("extending a peer route") { val route = peerRoute(asPath = emptyPath()) @@ -497,6 +536,19 @@ object InterdomainExtendersTests : Spek({ } } + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = PeerplusExtender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + on("extending a peer route") { val route = peerRoute(asPath = emptyPath()) @@ -602,6 +654,167 @@ object InterdomainExtendersTests : Spek({ } } + given("a peer* extender") { + + val extender = PeerstarExtender + + on("extending a customer route with an empty AS-PATH") { + + val route = customerRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route with AS-PATH containing the sender") { + assertThat(extendedRoute, + Is(peerstarRoute(asPath = pathOf(sender)))) + } + } + + on("extending a customer route with an AS-PATH containing node 2") { + + val route = customerRoute(asPath = pathOf(BGPNode(id = 2))) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route with AS-PATH containing node 2 and the sender") { + assertThat(extendedRoute, + Is(peerstarRoute(asPath = pathOf(BGPNode(2), sender)))) + } + } + + on("extending a peer+ route") { + + val route = peerplusRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route") { + assertThat(extendedRoute, + Is(peerstarRoute(asPath = pathOf(sender)))) + } + } + + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + + on("extending a peer route") { + + val route = peerRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + + on("extending a provider route") { + + val route = providerRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + + on("extending a self route") { + + val route = BGPRoute.self() + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route with the AS-PATH containing the sender") { + assertThat(extendedRoute, + Is(peerstarRoute(asPath = pathOf(sender)))) + } + } + + on("extending an invalid route") { + + val route = BGPRoute.invalid() + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + + on("extending a customer route with 1 sibling hop") { + + val route = customerRoute(siblingHops = 1, asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route with 0 sibling hops") { + assertThat(extendedRoute, + Is(peerstarRoute(siblingHops = 0, asPath = pathOf(sender)))) + } + } + + on("extending a peer+ route with 1 sibling hop") { + + val route = peerplusRoute(siblingHops = 1, asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns a peer* route with 0 sibling hops") { + assertThat(extendedRoute, + Is(peerstarRoute(siblingHops = 0, asPath = pathOf(sender)))) + } + } + + on("extending a peer route with 1 sibling hop") { + + val route = peerRoute(siblingHops = 1, asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + + on("extending a provider route with 1 sibling hop") { + + val route = providerRoute(siblingHops = 1, asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = extender.extend(route, sender) + + it("returns an invalid route") { + assertThat(extendedRoute, + Is(BGPRoute.invalid())) + } + } + } + given("a sibling extender") { on("extending a customer route with 0 sibling hops and with an empty AS-PATH") { @@ -669,6 +882,19 @@ object InterdomainExtendersTests : Spek({ } } + on("extending a peer* route") { + + val route = peerstarRoute(asPath = emptyPath()) + val sender = BGPNode(id = 1) + + val extendedRoute = SiblingExtender.extend(route, sender) + + it("returns a peer* route with 1 sibling hop and with AS-PATH containing the sender") { + assertThat(extendedRoute, + Is(peerstarRoute(siblingHops = 1, asPath = pathOf(sender)))) + } + } + on("extending a peer route") { val route = peerRoute(asPath = emptyPath()) From 259d82709a8b0c6d5cbaae9c0614d2c7b45fd790 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Mon, 11 Sep 2017 18:38:06 +0100 Subject: [PATCH 3/4] Have InterdomainTopologyReader recognize R* labels --- src/main/kotlin/io/ExtenderParseFunctions.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/io/ExtenderParseFunctions.kt b/src/main/kotlin/io/ExtenderParseFunctions.kt index 3598d7c..9e260db 100644 --- a/src/main/kotlin/io/ExtenderParseFunctions.kt +++ b/src/main/kotlin/io/ExtenderParseFunctions.kt @@ -16,6 +16,7 @@ import core.routing.Extender * Parses an Interdomain Extender. The supported labels are: * * R+ - parsed as a PeerplusExtender + * R* - parsed as a PeerstarExtender * C - parsed as a CustomerExtender * R - parsed as a PeerExtender * P - parsed as a ProviderExtender @@ -33,12 +34,13 @@ fun parseInterdomainExtender(label: String, lineNumber: Int): Extender return when (label.toLowerCase()) { "r+" -> PeerplusExtender + "r*" -> PeerstarExtender "c" -> CustomerExtender "r" -> PeerExtender "p" -> ProviderExtender "s" -> SiblingExtender else -> throw ParseException("Extender label `$label` was not recognized: " + - "must be either R+, C, R, P, or S", lineNumber) + "must be either R+, R*, C, R, P, or S", lineNumber) } } From 293dc1c10367d2f766d7f2539c4399297a718962 Mon Sep 17 00:00:00 2001 From: David Fialho Date: Mon, 11 Sep 2017 18:52:22 +0100 Subject: [PATCH 4/4] Include some integration tests for the peer* relationship --- .../bgp/SSBGP2WithInterdomainRoutingTests.kt | 117 ++++++++++++++++++ src/test/kotlin/testing/TopologyExtensions.kt | 5 + 2 files changed, 122 insertions(+) create mode 100644 src/test/kotlin/bgp/SSBGP2WithInterdomainRoutingTests.kt diff --git a/src/test/kotlin/bgp/SSBGP2WithInterdomainRoutingTests.kt b/src/test/kotlin/bgp/SSBGP2WithInterdomainRoutingTests.kt new file mode 100644 index 0000000..f3f7d64 --- /dev/null +++ b/src/test/kotlin/bgp/SSBGP2WithInterdomainRoutingTests.kt @@ -0,0 +1,117 @@ +package bgp + +import bgp.policies.interdomain.customerRoute +import bgp.policies.interdomain.peerplusRoute +import core.simulator.Engine +import org.hamcrest.MatcherAssert.assertThat +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 testing.* +import testing.bgp.pathOf +import org.hamcrest.Matchers.`is` as Is + +/** + * Created on 01-09-2017 + * + * @author David Fialho + */ +object SSBGP2WithInterdomainRoutingTests: Spek({ + + + given("topology with cycle with all R+ links except one R* link") { + + val topology = bgpTopology { + node { 0 deploying SSBGP2() } + node { 1 deploying SSBGP2() } + node { 2 deploying SSBGP2() } + node { 3 deploying SSBGP2() } + + customerLink { 1 to 0 } + customerLink { 2 to 0 } + customerLink { 3 to 0 } + peerplusLink { 1 to 2 } + peerplusLink { 2 to 3 } + peerstarLink { 3 to 1 } + } + + afterEachTest { + Engine.scheduler.reset() + topology.reset() + } + + val node = topology.nodes.sortedBy { it.id } + val protocol = node.map { it.protocol as SSBGP2 } + + on("simulating with node 0 as the destination") { + + val terminated = Engine.simulate(topology, node[0], threshold = 1000) + + it("terminates") { + assertThat(terminated, Is(true)) + } + + it("finishes with node 1 selecting peer+ route via 2") { + assertThat(protocol[1].routingTable.getSelectedRoute(), + Is(peerplusRoute(0, pathOf(0, 3, 2)))) + } + + it("finishes with node 2 selecting peer+ route via 3") { + assertThat(protocol[2].routingTable.getSelectedRoute(), + Is(peerplusRoute(0, pathOf(0, 3)))) + } + + it("finishes with node 3 selecting customer route via 0") { + assertThat(protocol[3].routingTable.getSelectedRoute(), + Is(customerRoute(0, pathOf(0)))) + } + + it("finishes with link from 1 to 2 enabled") { + assertThat(protocol[1].routingTable.table.isEnabled(node[2]), Is(true)) + } + + it("finishes with link from 2 to 3 enabled") { + assertThat(protocol[2].routingTable.table.isEnabled(node[3]), Is(true)) + } + + it("finishes with link from 3 to 1 disabled") { + assertThat(protocol[3].routingTable.table.isEnabled(node[1]), Is(false)) + } + } + } + + given("topology with cycle with all R* links") { + + val topology = bgpTopology { + node { 0 deploying SSBGP2() } + node { 1 deploying SSBGP2() } + node { 2 deploying SSBGP2() } + node { 3 deploying SSBGP2() } + + customerLink { 1 to 0 } + customerLink { 2 to 0 } + customerLink { 3 to 0 } + peerstarLink { 1 to 2 } + peerstarLink { 2 to 3 } + peerstarLink { 3 to 1 } + } + + afterEachTest { + Engine.scheduler.reset() + topology.reset() + } + + val node = topology.nodes.sortedBy { it.id } + + on("simulating with node 0 as the destination") { + + val terminated = Engine.simulate(topology, node[0], threshold = 1000) + + it("does NOT terminate") { + assertThat(terminated, Is(false)) + } + } + } + +}) \ No newline at end of file diff --git a/src/test/kotlin/testing/TopologyExtensions.kt b/src/test/kotlin/testing/TopologyExtensions.kt index ad3e873..a5bd515 100644 --- a/src/test/kotlin/testing/TopologyExtensions.kt +++ b/src/test/kotlin/testing/TopologyExtensions.kt @@ -83,6 +83,11 @@ infix fun TopologyBuilder.peerplusLink(createLink: () -> TemporaryLink this.link(link.tail, link.head, PeerplusExtender) } +infix fun TopologyBuilder.peerstarLink(createLink: () -> TemporaryLink) { + val link = createLink() + this.link(link.tail, link.head, PeerstarExtender) +} + infix fun TopologyBuilder.customerLink(createLink: () -> TemporaryLink) { val link = createLink() this.link(link.tail, link.head, CustomerExtender)