Pavlin Radoslavov

Added support for parsing and handling BGP Confederation related AS Path

attributes.
Note: BGP Confedertions are not supported (yet).

Also, updated/simplified the MED comparison in the BGP Path Comparison
implementation.

Change-Id: Iabe01facffd2c6912f33f647841c1244d85282f3
......@@ -168,6 +168,12 @@ public final class BgpConstants {
/** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
public static final int AS_SEQUENCE = 2;
/** BGP UPDATE AS_PATH Type: AS_CONFED_SEQUENCE. */
public static final int AS_CONFED_SEQUENCE = 3;
/** BGP UPDATE AS_PATH Type: AS_CONFED_SET. */
public static final int AS_CONFED_SET = 4;
/**
* Gets the BGP AS_PATH type as a string.
*
......@@ -184,6 +190,12 @@ public final class BgpConstants {
case AS_SEQUENCE:
typeString = "AS_SEQUENCE";
break;
case AS_CONFED_SEQUENCE:
typeString = "AS_CONFED_SEQUENCE";
break;
case AS_CONFED_SET:
typeString = "AS_CONFED_SET";
break;
default:
break;
}
......
......@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Objects;
import org.onlab.onos.sdnip.RouteEntry;
import org.onlab.onos.sdnip.bgp.BgpConstants.Update;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
......@@ -35,8 +36,7 @@ public class BgpRouteEntry extends RouteEntry {
private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
private final AsPath asPath; // The AS Path
private final long localPref; // The local preference for the route
private long multiExitDisc =
BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
private long multiExitDisc = Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
/**
* Class constructor.
......@@ -116,21 +116,33 @@ public class BgpRouteEntry extends RouteEntry {
* Tests whether the route is originated from the local AS.
* <p>
* The route is considered originated from the local AS if the AS Path
* is empty or if it begins with an AS_SET.
* is empty or if it begins with an AS_SET (after skipping
* AS_CONFED_SEQUENCE and AS_CONFED_SET).
* </p>
*
* @return true if the route is originated from the local AS, otherwise
* false
*/
boolean isLocalRoute() {
if (asPath.getPathSegments().isEmpty()) {
return true;
PathSegment firstPathSegment = null;
// Find the first Path Segment by ignoring the AS_CONFED_* segments
for (PathSegment pathSegment : asPath.getPathSegments()) {
if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
(pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
firstPathSegment = pathSegment;
break;
}
}
if (firstPathSegment == null) {
return true; // Local route: no path segments
}
PathSegment firstPathSegment = asPath.getPathSegments().get(0);
if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
// If the first path segment is AS_SET, the route is considered local
if (firstPathSegment.getType() == Update.AsPath.AS_SET) {
return true;
}
return false;
return false; // The route is not local
}
/**
......@@ -143,10 +155,25 @@ public class BgpRouteEntry extends RouteEntry {
* @return the BGP Neighbor AS number the route was received from.
*/
long getNeighborAs() {
PathSegment firstPathSegment = null;
if (isLocalRoute()) {
return BgpConstants.BGP_AS_0;
}
PathSegment firstPathSegment = asPath.getPathSegments().get(0);
// Find the first Path Segment by ignoring the AS_CONFED_* segments
for (PathSegment pathSegment : asPath.getPathSegments()) {
if ((pathSegment.getType() == Update.AsPath.AS_SET) ||
(pathSegment.getType() == Update.AsPath.AS_SEQUENCE)) {
firstPathSegment = pathSegment;
break;
}
}
if (firstPathSegment == null) {
// NOTE: Shouldn't happen - should be captured by isLocalRoute()
return BgpConstants.BGP_AS_0;
}
if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
// TODO: Shouldn't happen. Should check during the parsing.
return BgpConstants.BGP_AS_0;
......@@ -211,18 +238,16 @@ public class BgpRouteEntry extends RouteEntry {
// Compare the MED if the neighbor AS is same: larger is better
medLabel: {
boolean thisIsLocalRoute = isLocalRoute();
if (thisIsLocalRoute != other.isLocalRoute()) {
break medLabel; // AS number is different
if (isLocalRoute() || other.isLocalRoute()) {
// Compare MEDs for non-local routes only
break medLabel;
}
if (!thisIsLocalRoute) {
long thisNeighborAs = getNeighborAs();
if (thisNeighborAs != other.getNeighborAs()) {
break medLabel; // AS number is different
}
if (thisNeighborAs == BgpConstants.BGP_AS_0) {
break medLabel; // Invalid AS number
}
long thisNeighborAs = getNeighborAs();
if (thisNeighborAs != other.getNeighborAs()) {
break medLabel; // AS number is different
}
if (thisNeighborAs == BgpConstants.BGP_AS_0) {
break medLabel; // Invalid AS number
}
// Compare the MED
......@@ -253,13 +278,16 @@ public class BgpRouteEntry extends RouteEntry {
* A class to represent AS Path Segment.
*/
public static class PathSegment {
private final byte type; // Segment type: AS_SET, AS_SEQUENCE
// Segment type: AS_SET(1), AS_SEQUENCE(2), AS_CONFED_SEQUENCE(3),
// AS_CONFED_SET(4)
private final byte type;
private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
/**
* Constructor.
*
* @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
* @param type the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
* AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
* @param segmentAsNumbers the Segment AS numbers
*/
PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
......@@ -268,9 +296,11 @@ public class BgpRouteEntry extends RouteEntry {
}
/**
* Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
* Gets the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
* AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4).
*
* @return the Path Segment Type: AS_SET, AS_SEQUENCE
* @return the Path Segment Type: AS_SET(1), AS_SEQUENCE(2),
* AS_CONFED_SEQUENCE(3), AS_CONFED_SET(4)
*/
public byte getType() {
return type;
......@@ -309,7 +339,7 @@ public class BgpRouteEntry extends RouteEntry {
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("type", BgpConstants.Update.AsPath.typeToString(type))
.add("type", Update.AsPath.typeToString(type))
.add("segmentAsNumbers", this.segmentAsNumbers)
.toString();
}
......@@ -333,15 +363,27 @@ public class BgpRouteEntry extends RouteEntry {
//
// Precompute the AS Path Length:
// - AS_SET counts as 1
// - AS_SEQUENCE counts how many AS numbers are included
// - AS_CONFED_SEQUENCE and AS_CONFED_SET are ignored
//
int pl = 0;
for (PathSegment pathSegment : pathSegments) {
if (pathSegment.getType() ==
BgpConstants.Update.AsPath.AS_SET) {
pl++;
continue;
switch (pathSegment.getType()) {
case Update.AsPath.AS_SET:
pl++; // AS_SET counts as 1
break;
case Update.AsPath.AS_SEQUENCE:
// Count each AS number
pl += pathSegment.getSegmentAsNumbers().size();
break;
case Update.AsPath.AS_CONFED_SEQUENCE:
break; // Ignore
case Update.AsPath.AS_CONFED_SET:
break; // Ignore
default:
// TODO: What to do if the Path Segment type is unknown?
break;
}
pl += pathSegment.getSegmentAsNumbers().size();
}
asPathLength = pl;
}
......@@ -444,7 +486,7 @@ public class BgpRouteEntry extends RouteEntry {
.add("prefix", prefix())
.add("nextHop", nextHop())
.add("bgpId", bgpSession.getRemoteBgpId())
.add("origin", BgpConstants.Update.Origin.typeToString(origin))
.add("origin", Update.Origin.typeToString(origin))
.add("asPath", asPath)
.add("localPref", localPref)
.add("multiExitDisc", multiExitDisc)
......
......@@ -975,6 +975,10 @@ public class BgpSession extends SimpleChannelHandler {
case BgpConstants.Update.AsPath.AS_SET:
// FALLTHROUGH
case BgpConstants.Update.AsPath.AS_SEQUENCE:
// FALLTHROUGH
case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
// FALLTHROUGH
case BgpConstants.Update.AsPath.AS_CONFED_SET:
break;
default:
// ERROR: Invalid Path Segment Type
......
......@@ -28,30 +28,63 @@ import org.junit.Test;
*/
public class AsPathTest {
/**
* Generates an AS Path.
* Generates Path Segments.
*
* @return a generated AS Path
* @return the generated Path Segments
*/
private BgpRouteEntry.AsPath generateAsPath() {
private ArrayList<BgpRouteEntry.PathSegment> generatePathSegments() {
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
byte pathSegmentType;
ArrayList<Long> segmentAsNumbers;
BgpRouteEntry.PathSegment pathSegment;
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 5);
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 4);
segmentAsNumbers.add((long) 5);
segmentAsNumbers.add((long) 6);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 7);
segmentAsNumbers.add((long) 8);
segmentAsNumbers.add((long) 9);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 10);
segmentAsNumbers.add((long) 11);
segmentAsNumbers.add((long) 12);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
return pathSegments;
}
/**
* Generates an AS Path.
*
* @return a generated AS Path
*/
private BgpRouteEntry.AsPath generateAsPath() {
ArrayList<BgpRouteEntry.PathSegment> pathSegments =
generatePathSegments();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
return asPath;
......@@ -65,9 +98,11 @@ public class AsPathTest {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=AS_SET, segmentAsNumbers=[4, 5, 6]}]}";
"AsPath{pathSegments=[" +
"PathSegment{type=AS_CONFED_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=AS_CONFED_SET, segmentAsNumbers=[4, 5, 6]}, " +
"PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[7, 8, 9]}, " +
"PathSegment{type=AS_SET, segmentAsNumbers=[10, 11, 12]}]}";
assertThat(asPath.toString(), is(expectedString));
}
......@@ -86,24 +121,8 @@ public class AsPathTest {
@Test
public void testGetFields() {
// Create the fields to compare against
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 5);
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
ArrayList<BgpRouteEntry.PathSegment> pathSegments =
generatePathSegments();
// Generate the entry to test
BgpRouteEntry.AsPath asPath = generateAsPath();
......@@ -116,6 +135,11 @@ public class AsPathTest {
*/
@Test
public void testGetAsPathLength() {
//
// NOTE:
// - AS_CONFED_SEQUENCE and AS_CONFED_SET are excluded
// - AS_SET counts as a single hop
//
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getAsPathLength(), is(4));
......@@ -145,23 +169,45 @@ public class AsPathTest {
// Setup AS Path 2
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 1);
segmentAsNumbers1.add((long) 2);
segmentAsNumbers1.add((long) 3);
BgpRouteEntry.PathSegment pathSegment1 =
new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
pathSegments.add(pathSegment1);
byte pathSegmentType;
ArrayList<Long> segmentAsNumbers;
BgpRouteEntry.PathSegment pathSegment;
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 4);
segmentAsNumbers.add((long) 5);
segmentAsNumbers.add((long) 6);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 7);
segmentAsNumbers.add((long) 8);
segmentAsNumbers.add((long) 9);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
segmentAsNumbers2.add((long) 4);
segmentAsNumbers2.add((long) 55); // Different
segmentAsNumbers2.add((long) 6);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET;
segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 10);
segmentAsNumbers.add((long) 111); // Different
segmentAsNumbers.add((long) 12);
pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
pathSegments.add(pathSegment);
//
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
......@@ -176,9 +222,11 @@ public class AsPathTest {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=AS_SET, segmentAsNumbers=[4, 5, 6]}]}";
"AsPath{pathSegments=[" +
"PathSegment{type=AS_CONFED_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=AS_CONFED_SET, segmentAsNumbers=[4, 5, 6]}, " +
"PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[7, 8, 9]}, " +
"PathSegment{type=AS_SET, segmentAsNumbers=[10, 11, 12]}]}";
assertThat(asPath.toString(), is(expectedString));
}
}
......