Skip to content

Commit

Permalink
Release 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
David Fialho committed Sep 1, 2017
2 parents 43451fc + b3b96ed commit 4d44547
Show file tree
Hide file tree
Showing 110 changed files with 9,295 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ gradle-app.setting

### JetBrains template
.idea/
out/


62 changes: 60 additions & 2 deletions build.gradle
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) } }
}
194 changes: 194 additions & 0 deletions src/main/kotlin/bgp/BGP.kt
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
}
101 changes: 101 additions & 0 deletions src/main/kotlin/bgp/BGPRoute.kt
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
}
Loading

0 comments on commit 4d44547

Please sign in to comment.