-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
110 changed files
with
9,295 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,5 +40,6 @@ gradle-app.setting | |
|
||
### JetBrains template | ||
.idea/ | ||
out/ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,88 @@ | ||
group 'ssbgp' | ||
version '1.0-SNAPSHOT' | ||
version '1.0' | ||
|
||
buildscript { | ||
ext.kotlin_version = '1.1.3-2' | ||
ext.kotlin_version = '1.1.4-2' | ||
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() | ||
jcenter() | ||
} | ||
dependencies { | ||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||
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' | ||
|
||
junitPlatform { | ||
filters { | ||
engines { | ||
include 'spek' | ||
} | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
maven { | ||
url "https://oss.sonatype.org/content/repositories/snapshots" | ||
} | ||
} | ||
|
||
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" | ||
testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0" | ||
|
||
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" | ||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" | ||
compile "com.github.inamik.text.tables:inamik-text-tables:1.0-SNAPSHOT" | ||
compile 'org.apache.commons:commons-csv:1.4' | ||
compile group: 'commons-cli', name: 'commons-cli', version: '1.4' | ||
} | ||
|
||
compileKotlin { | ||
kotlinOptions.jvmTarget = "1.8" | ||
} | ||
|
||
compileTestKotlin { | ||
kotlinOptions.jvmTarget = "1.8" | ||
} | ||
|
||
dokka { | ||
outputFormat = 'html' | ||
outputDirectory = "$buildDir/javadoc" | ||
} | ||
|
||
task enabledMockingFinalClasses << { | ||
def mockMakerFile = new File("$projectDir/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker") | ||
mockMakerFile.parentFile.mkdirs() | ||
mockMakerFile.createNewFile() | ||
mockMakerFile.write("mock-maker-inline") | ||
} | ||
|
||
jar { | ||
manifest { | ||
attributes 'Main-Class': 'main.MainKt' | ||
} | ||
|
||
// This line of code recursively collects and copies all of a project's files | ||
// and adds them to the JAR itself. One can extend this task, to skip certain | ||
// files or particular types at will | ||
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package bgp | ||
|
||
import bgp.notifications.* | ||
import core.routing.* | ||
import core.simulator.Time | ||
import core.simulator.Timer | ||
|
||
/** | ||
* Created on 21-07-2017 | ||
* | ||
* @author David Fialho | ||
*/ | ||
abstract class BaseBGP(val mrai: Time, routingTable: RoutingTable<BGPRoute>): Protocol<BGPRoute> { | ||
|
||
/** | ||
* This data structure contains all neighbors that the protocol needs to send routes to when a new | ||
* route is selected. | ||
*/ | ||
protected val neighbors = ArrayList<Neighbor<BGPRoute>>() | ||
|
||
/** | ||
* Collection of all the in-neighbors added to the protocol. | ||
*/ | ||
override val inNeighbors: Collection<Neighbor<BGPRoute>> | ||
get() = neighbors | ||
|
||
/** | ||
* Routing table containing the candidate routes. | ||
* Uses a route selector to perform the route selection. | ||
* The route selector wraps the provided routing table if one is provided. Otherwise, it wraps a new routing table. | ||
*/ | ||
val routingTable = RouteSelector.wrap(routingTable, ::bgpRouteCompare) | ||
|
||
var mraiTimer = Timer.disabled() | ||
protected set | ||
|
||
/** | ||
* 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 wasSelectedRouteUpdated: Boolean = false | ||
|
||
/** | ||
* Adds a new in-neighbor for the protocol to export selected routes to. | ||
* | ||
* It does not check if the neighbor was already added to the protocol. Thus, the same neighbor can be added | ||
* twice, which means that it will be notified twice every time a new route is selected. | ||
*/ | ||
override fun addInNeighbor(neighbor: Neighbor<BGPRoute>) { | ||
neighbors.add(neighbor) | ||
} | ||
|
||
/** | ||
* Announces [node] as the destination. | ||
*/ | ||
override fun start(node: Node<BGPRoute>) { | ||
val selfRoute = BGPRoute.self() | ||
routingTable.update(node, selfRoute) | ||
export(node) | ||
} | ||
|
||
/** | ||
* Processes a BGP message received by a node. | ||
* May update the routing table and the selected route/neighbor. | ||
* | ||
* @param message the message to be processed | ||
*/ | ||
override fun process(message: Message<BGPRoute>) { | ||
|
||
val importedRoute = import(message.sender, message.route, message.extender) | ||
BGPNotifier.notifyImport(ImportNotification(message.receiver, importedRoute, message.sender)) | ||
|
||
process(message.receiver, message.sender, importedRoute) | ||
} | ||
|
||
/** | ||
* Processes a BGP route imported by a node. | ||
* May update the routing table and the selected route/neighbor. | ||
* | ||
* @param node the node that imported the route | ||
* @param neighbor the neighbor that exported the route | ||
* @param importedRoute the route imported by [node] | ||
*/ | ||
fun process(node: Node<BGPRoute>, neighbor: Node<BGPRoute>, importedRoute: BGPRoute) { | ||
|
||
// Store the route the node was selecting before processing this message | ||
val previousSelectedRoute = routingTable.getSelectedRoute() | ||
|
||
val learnedRoute = learn(node, neighbor, importedRoute) | ||
BGPNotifier.notifyLearn(LearnNotification(node, learnedRoute, neighbor)) | ||
|
||
val updated = routingTable.update(neighbor, learnedRoute) | ||
|
||
// Set updated flag to true if 'updated' is true or keep its current state | ||
wasSelectedRouteUpdated = wasSelectedRouteUpdated || updated | ||
|
||
if (wasSelectedRouteUpdated) { | ||
|
||
val selectedRoute = routingTable.getSelectedRoute() | ||
BGPNotifier.notifySelect( | ||
SelectNotification(node, selectedRoute, previousSelectedRoute)) | ||
|
||
export(node) | ||
wasSelectedRouteUpdated = false | ||
} | ||
} | ||
|
||
/** | ||
* Implements the process of importing a route. | ||
* Returns the result of extending the given route with the given extender. | ||
* | ||
* @param sender the node the sent the 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) | ||
*/ | ||
protected fun import(sender: Node<BGPRoute>, route: BGPRoute, extender: Extender<BGPRoute>): BGPRoute { | ||
return extender.extend(route, sender) | ||
} | ||
|
||
/** | ||
* 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) | ||
* @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. | ||
*/ | ||
protected fun learn(node: Node<BGPRoute>, sender: Node<BGPRoute>, route: BGPRoute): BGPRoute { | ||
|
||
if (node in route.asPath) { | ||
// Notify the implementations that a loop was detected | ||
onLoopDetected(node, sender, route) | ||
|
||
return BGPRoute.invalid() | ||
} else { | ||
return route | ||
} | ||
} | ||
|
||
/** | ||
* Implements the process of exporting a route. It exports the currently selected by the node. | ||
* | ||
* @param node the node exporting the node | ||
*/ | ||
protected fun export(node: Node<BGPRoute>, restartMRAITimer: Boolean = true) { | ||
|
||
if (!mraiTimer.expired) { | ||
// The MRAI timer is still running: no route is exported while the MRAI timer is running | ||
return | ||
} | ||
|
||
val selectedRoute = routingTable.getSelectedRoute() | ||
|
||
// Export the route currently selected | ||
node.send(selectedRoute) | ||
BGPNotifier.notifyExport(ExportNotification(node, selectedRoute)) | ||
|
||
if (restartMRAITimer && mrai > 0) { | ||
// Restart the MRAI timer | ||
mraiTimer = Timer.enabled(mrai) { | ||
export(node, restartMRAITimer = false) | ||
} // when the timer expires | ||
mraiTimer.start() | ||
} | ||
} | ||
|
||
/** | ||
* Resets the state of the protocol as if it was just created. | ||
*/ | ||
override fun reset() { | ||
routingTable.clear() | ||
wasSelectedRouteUpdated = false | ||
mraiTimer.cancel() | ||
mraiTimer = Timer.disabled() | ||
} | ||
|
||
/** | ||
* Called by the protocol when it detects a routing loop. | ||
*/ | ||
abstract protected fun onLoopDetected(node: Node<BGPRoute>, sender: Node<BGPRoute>, route: BGPRoute) | ||
|
||
} | ||
|
||
/** | ||
* BGP: when a loop is detected it does nothing extra. | ||
*/ | ||
class BGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.empty(BGPRoute.invalid())) | ||
: BaseBGP(mrai, routingTable) { | ||
|
||
override fun onLoopDetected(node: Node<BGPRoute>, sender: Node<BGPRoute>, route: BGPRoute) = Unit | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
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! | ||
*/ | ||
sealed class BGPRoute : Route { | ||
|
||
abstract val localPref: Int | ||
abstract val asPath: Path | ||
|
||
companion object Factory { | ||
|
||
/** | ||
* Returns a BGP route with the specified LOCAL-PREF and AS-PATH. | ||
*/ | ||
fun with(localPref: Int, asPath: Path): BGPRoute = ValidBGPRoute(localPref, asPath) | ||
|
||
/** | ||
* Returns an invalid BGP route. | ||
*/ | ||
fun invalid(): BGPRoute = InvalidBGPRoute | ||
|
||
/** | ||
* Returns a self BGP route. A self BGP route is the BGP route with the highest preference possible. | ||
*/ | ||
fun self(): BGPRoute = SelfBGPRoute | ||
|
||
} | ||
|
||
/** | ||
* 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 | ||
} | ||
|
||
/** | ||
* 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 | ||
override fun toString(): String = "•" | ||
} | ||
|
||
/** | ||
* An implementation for a self BGP route. A self BGP route is the BGP route with the highest preference possible. | ||
*/ | ||
private object SelfBGPRoute : BGPRoute() { | ||
override val localPref: Int = Int.MAX_VALUE | ||
override val asPath: Path = emptyPath() | ||
override fun isValid(): Boolean = true | ||
override fun toString(): String = "◦" | ||
} | ||
|
||
override fun toString(): String { | ||
return "BGPRoute(localPref=$localPref, asPath=$asPath)" | ||
} | ||
} | ||
|
||
/** | ||
* 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 | ||
} |
Oops, something went wrong.