Skip to content

Commit

Permalink
Release 1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
David Fialho committed Sep 7, 2017
2 parents 4d44547 + 952d8df commit 7965f14
Show file tree
Hide file tree
Showing 17 changed files with 853 additions and 89 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
group 'ssbgp'
version '1.0'
version '1.1'

buildscript {
ext.kotlin_version = '1.1.4-2'
ext.kotlin_version = '1.1.4-3'
ext.dokka_version = '0.9.15'
ext.junit_version = '1.0.0-M4'
ext.junit5_version = '5.0.0-M4'
Expand Down
45 changes: 40 additions & 5 deletions src/main/kotlin/bgp/SSBGP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ abstract class BaseSSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute>):
return
}

val prevSelectedRoute = routingTable.getSelectedRoute()

// Since a loop routing was detected, the new route via the sender node is surely invalid

// Set the route via the sender as invalid
Expand All @@ -33,7 +35,7 @@ abstract class BaseSSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute>):
wasSelectedRouteUpdated = wasSelectedRouteUpdated || updated

val alternativeRoute = routingTable.getSelectedRoute()
if (isRecurrent(node, route, alternativeRoute)) {
if (isRecurrent(node, route, alternativeRoute, prevSelectedRoute)) {
disableNeighbor(sender)
BGPNotifier.notifyDetect(DetectNotification(node, route, alternativeRoute, sender))
}
Expand All @@ -43,7 +45,8 @@ abstract class BaseSSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute>):
* Checks if the routing loop detected is recurrent.
* Subclasses must implement this method to define the detection condition.
*/
abstract fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute): Boolean
protected abstract fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute,
alternativeRoute: BGPRoute, prevSelectedRoute: BGPRoute): Boolean

/**
* Enables the specified neighbor.
Expand Down Expand Up @@ -81,7 +84,9 @@ abstract class BaseSSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute>):
class SSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.empty(BGPRoute.invalid()))
: BaseSSBGP(mrai, routingTable) {

override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute): Boolean {
override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute,
prevSelectedRoute: BGPRoute): Boolean {

return learnedRoute.localPref > alternativeRoute.localPref
}
}
Expand All @@ -93,8 +98,38 @@ class SSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.
class ISSBGP(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.empty(BGPRoute.invalid()))
: BaseSSBGP(mrai, routingTable) {

override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute): Boolean {
override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute,
prevSelectedRoute: BGPRoute): Boolean {

return learnedRoute.localPref > alternativeRoute.localPref &&
alternativeRoute.asPath == learnedRoute.asPath.subPathBefore(node)
}
}
}

/**
* SS-BGP version 2 Protocol: it uses a more generic detection condition than version 1.
*/
class SSBGP2(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.empty(BGPRoute.invalid()))
: BaseSSBGP(mrai, routingTable) {

override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute,
prevSelectedRoute: BGPRoute): Boolean {

return alternativeRoute.localPref < prevSelectedRoute.localPref
}
}

/**
* ISS-BGP version 2 Protocol: it uses the detection condition of SS-BGP2 and also checks if the tail of looping
* path matches the path of the alternative route.
*/
class ISSBGP2(mrai: Time = 0, routingTable: RoutingTable<BGPRoute> = RoutingTable.empty(BGPRoute.invalid()))
: BaseSSBGP(mrai, routingTable) {

override fun isRecurrent(node: Node<BGPRoute>, learnedRoute: BGPRoute, alternativeRoute: BGPRoute,
prevSelectedRoute: BGPRoute): Boolean {

return alternativeRoute.localPref < prevSelectedRoute.localPref &&
alternativeRoute.asPath == learnedRoute.asPath.subPathBefore(node)
}
}
10 changes: 8 additions & 2 deletions src/main/kotlin/core/routing/TopologyBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ class TopologyBuilder<R: Route> {
*
* @param id the ID to identify the new node
* @param protocol the protocol deployed by the new node
* @return this builder
* @throws ElementExistsException if a node with the specified ID was already added to the builder
*/
@Throws(ElementExistsException::class)
fun addNode(id: NodeID, protocol: Protocol<R>) {
fun addNode(id: NodeID, protocol: Protocol<R>): TopologyBuilder<R> {

if (nodes.putIfAbsent(id, Node(id, protocol)) != null) {
throw ElementExistsException("Node with ID `$id` was added twice to the topology builder")
}

return this
}

/**
Expand All @@ -47,11 +50,12 @@ class TopologyBuilder<R: Route> {
* @param from the Id of the node at the tail of the link
* @param to the protocol deployed by the new node
* @param extender the protocol deployed by the new node
* @return this builder
* @throws ElementExistsException if a node with the specified ID was already added to the builder
* @throws ElementNotFoundException if builder is missing the node with ID [from] and/or [to]
*/
@Throws(ElementExistsException::class, ElementNotFoundException::class)
fun link(from: NodeID, to: NodeID, extender: Extender<R>) {
fun link(from: NodeID, to: NodeID, extender: Extender<R>): TopologyBuilder<R> {

val tail = nodes[from] ?: throw ElementNotFoundException("Node with ID `$from` was not yet added the builder")
val head = nodes[to] ?: throw ElementNotFoundException("Node with ID `$to` was not yet added the builder")
Expand All @@ -61,6 +65,8 @@ class TopologyBuilder<R: Route> {
}

head.addInNeighbor(tail, extender)

return this
}

/**
Expand Down
47 changes: 47 additions & 0 deletions src/main/kotlin/io/ExtenderParseFunctions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io

import bgp.BGPRoute
import bgp.policies.interdomain.*
import core.routing.Extender

/**
* Created on 31-08-2017
*
* @author David Fialho
*
* This file contains functions to parse extenders from labels.
*/

/**
* Parses an Interdomain Extender. The supported labels are:
*
* R+ - parsed as a PeerplusExtender
* C - parsed as a CustomerExtender
* R - parsed as a PeerExtender
* P - parsed as a ProviderExtender
* S - parsed as a SiblingExtender
*
* This function is NOT case sensitive!
*
* @param label the label of the extender
* @param lineNumber the number of the line in which the label was found (used for the parse exception message only)
* @return the extender parsed from the label
* @throws ParseException if the label is not recognized
*/
@Throws(ParseException::class)
fun parseInterdomainExtender(label: String, lineNumber: Int): Extender<BGPRoute> {

return when (label.toLowerCase()) {
"r+" -> PeerplusExtender
"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)
}
}

fun parseInterdomainExtender(label: String): Extender<BGPRoute> {
return parseInterdomainExtender(label, lineNumber = 0)
}
54 changes: 14 additions & 40 deletions src/main/kotlin/io/InterdomainTopologyReader.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package io

import bgp.BGP
import bgp.BGPRoute
import bgp.ISSBGP
import bgp.SSBGP
import bgp.policies.interdomain.*
import bgp.*
import core.routing.*
import io.TopologyParser.Handler
import utils.toNonNegativeInt
import java.io.*

/**
* Created on 29-08-2017
*
* @author David Fialho
*/
class InterdomainTopologyReader(reader: Reader): TopologyReader, Closeable, Handler {
class InterdomainTopologyReader(reader: Reader): TopologyReader<BGPRoute>, Closeable, Handler {

/**
* Provides option to create a reader with a file object.
Expand Down Expand Up @@ -54,14 +51,21 @@ class InterdomainTopologyReader(reader: Reader): TopologyReader, Closeable, Hand
}

val protocolLabel = values[0]
val mrai = parseNonNegativeInteger(values[1], currentLine)
val mrai = try {
values[1].toNonNegativeInt()
} catch (e: NumberFormatException) {
throw ParseException("Failed to parse `${values[1]}`: must be a non-negative integer number", currentLine)
}

val protocol = when (protocolLabel.toLowerCase()) {
"bgp" -> BGP(mrai)
"ssbgp" -> SSBGP(mrai)
"issbgp" -> ISSBGP(mrai)
else -> throw ParseException("Protocol label `$protocolLabel` was not recognized: supported labels are BGP, " +
"SSBGP, and ISSBGP", currentLine)
"ssbgp2" -> SSBGP2(mrai)
"issbgp2" -> ISSBGP2(mrai)
else -> throw ParseException(
"Protocol label `$protocolLabel` was not recognized: supported labels are BGP, " +
"SSBGP, ISSBGP, SSBGP2, and ISSBGP2", currentLine)
}

try {
Expand All @@ -86,7 +90,7 @@ class InterdomainTopologyReader(reader: Reader): TopologyReader, Closeable, Hand
throw ParseException("Link is missing required values: extender label", currentLine)
}

val extender = parseExtender(values[0], currentLine)
val extender = parseInterdomainExtender(values[0], currentLine)

try {
builder.link(tail, head, extender)
Expand All @@ -98,36 +102,6 @@ class InterdomainTopologyReader(reader: Reader): TopologyReader, Closeable, Hand
}
}

@Throws(ParseException::class)
private fun parseNonNegativeInteger(value: String, currentLine: Int): Int {

try {
val intValue = value.toInt()
if (intValue < 0) {
throw NumberFormatException()
}

return intValue

} catch (e: NumberFormatException) {
throw ParseException("Failed to parse value `$value`: must be a non-negative integer value", currentLine)
}
}

@Throws(ParseException::class)
private fun parseExtender(label: String, currentLine: Int): Extender<BGPRoute> {

return when (label) {
"r+" -> PeerplusExtender
"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", currentLine)
}
}

/**
* Closes the stream and releases any system resources associated with it.
*/
Expand Down
116 changes: 116 additions & 0 deletions src/main/kotlin/io/StubParser.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package io

import core.routing.NodeID
import utils.toNonNegativeInt
import java.io.*

/**
* Created on 31-08-2017
*
* @author David Fialho
*/
class StubParser(reader: Reader): Closeable {

companion object {

/**
* Creates a stub parser with a file reader to parse a file.
*
* @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for
* some other reason cannot be opened for reading.
*/
@Throws(FileNotFoundException::class)
fun useFile(stubFile: File): StubParser {
return StubParser(FileReader(stubFile))
}

}

/**
* Interface for an handler that is called when a new stub item is parsed.
*/
interface Handler {

/**
* Invoked when a new stub item is found.
*
* @param id the ID of the stub
* @param inNeighbor the ID of the stub's in-neighbor
* @param label the label of the extender associated with the link between the neighbor and the stub
* @param currentLine line number where the stub link was parsed
*/
fun onStubLink(id: NodeID, inNeighbor: NodeID, label: String, currentLine: Int)

}

private val reader = BufferedReader(reader)

/**
* Parses the stub file and notifies the handler once a new stub is parsed.
*
* @param handler the handler that will be notified of new stub items
* @throws IOException If an I/O error occurs
* @throws ParseException If a parse error occurs
*/
@Throws(IOException::class, ParseException::class)
fun parse(handler: Handler) {

var line: String? = reader.readLine() ?: return
var currentLine = 1

while (line != null) {

// Ignore blank lines
if (!line.isBlank())
parseLine(line, handler, currentLine)

line = reader.readLine()
currentLine++
}

}

private fun parseLine(line: String, handler: Handler, currentLine: Int) {

val values = line.split("|").map { it.trim() }

if (values.size != 3) {
throw ParseException("A stub item requires 3 values, but ${values.size} were provided", currentLine)
}

val stubID = try {
values[0].toNonNegativeInt()
} catch (e: NumberFormatException) {
throw ParseException("Stub ID must be non-negative integer number: was `${values[0]}`", currentLine)
}

val inNeighborID = try {
values[1].toNonNegativeInt()
} catch (e: NumberFormatException) {
throw ParseException("In-neighbor ID must be non-negative integer number: was `${values[1]}`", currentLine)
}

val label = values[2]
if (label.isBlank()) throw ParseException("Stub item is missing label value", currentLine)

handler.onStubLink(stubID, inNeighborID, label, currentLine)
}

/**
* Resets the input stream.
*
* @throws IOException If the stream does not support reset(), or if some other I/O error occurs
*/
@Throws(IOException::class)
fun reset() {
if (!reader.markSupported())
reader.reset()
}

/**
* Closes the input stream.
*/
override fun close() {
reader.close()
}
}
Loading

0 comments on commit 7965f14

Please sign in to comment.