Thomas Vachuska

Merge remote-tracking branch 'origin/master'

......@@ -42,6 +42,16 @@
<artifactId>onlab-thirdparty</artifactId>
</dependency>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-misc</artifactId>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.junit.Test;
/**
* Unit tests for the BgpRouteEntry.AsPath class.
*/
public class AsPathTest {
/**
* Generates an AS Path.
*
* @return a generated AS Path
*/
private BgpRouteEntry.AsPath generateAsPath() {
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
return asPath;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
assertThat(asPath.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null Path Segments.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullPathSegments() {
ArrayList<BgpRouteEntry.PathSegment> pathSegments = null;
new BgpRouteEntry.AsPath(pathSegments);
}
/**
* Tests getting the fields of an AS Path.
*/
@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);
// Generate the entry to test
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getPathSegments(), is(pathSegments));
}
/**
* Tests getting the AS Path Length.
*/
@Test
public void testGetAsPathLength() {
BgpRouteEntry.AsPath asPath = generateAsPath();
assertThat(asPath.getAsPathLength(), is(4));
// Create an empty AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
asPath = new BgpRouteEntry.AsPath(pathSegments);
assertThat(asPath.getAsPathLength(), is(0));
}
/**
* Tests equality of {@link BgpRouteEntry.AsPath}.
*/
@Test
public void testEquality() {
BgpRouteEntry.AsPath asPath1 = generateAsPath();
BgpRouteEntry.AsPath asPath2 = generateAsPath();
assertThat(asPath1, is(asPath2));
}
/**
* Tests non-equality of {@link BgpRouteEntry.AsPath}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry.AsPath asPath1 = generateAsPath();
// 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 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);
//
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
assertThat(asPath1, is(not(asPath2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry.AsPath asPath = generateAsPath();
String expectedString =
"AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
assertThat(asPath.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Unit tests for the BgpRouteEntry class.
*/
public class BgpRouteEntryTest {
private BgpSession bgpSession;
private static final IpAddress BGP_SESSION_BGP_ID =
IpAddress.valueOf("10.0.0.1");
private static final IpAddress BGP_SESSION_IP_ADDRESS =
IpAddress.valueOf("20.0.0.1");
private BgpSession bgpSession2;
private static final IpAddress BGP_SESSION_BGP_ID2 =
IpAddress.valueOf("10.0.0.2");
private static final IpAddress BGP_SESSION_IP_ADDRESS2 =
IpAddress.valueOf("20.0.0.1");
private BgpSession bgpSession3;
private static final IpAddress BGP_SESSION_BGP_ID3 =
IpAddress.valueOf("10.0.0.1");
private static final IpAddress BGP_SESSION_IP_ADDRESS3 =
IpAddress.valueOf("20.0.0.2");
@Before
public void setUp() throws Exception {
// Mock objects for testing
bgpSession = createMock(BgpSession.class);
bgpSession2 = createMock(BgpSession.class);
bgpSession3 = createMock(BgpSession.class);
// Setup the BGP Sessions
expect(bgpSession.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID).anyTimes();
expect(bgpSession.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS).anyTimes();
//
expect(bgpSession2.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID2).anyTimes();
expect(bgpSession2.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS2).anyTimes();
//
expect(bgpSession3.getRemoteBgpId())
.andReturn(BGP_SESSION_BGP_ID3).anyTimes();
expect(bgpSession3.getRemoteIp4Address())
.andReturn(BGP_SESSION_IP_ADDRESS3).anyTimes();
replay(bgpSession);
replay(bgpSession2);
replay(bgpSession3);
}
/**
* Generates a BGP Route Entry.
*
* @return a generated BGP Route Entry
*/
private BgpRouteEntry generateBgpRouteEntry() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
long multiExitDisc = 20;
BgpRouteEntry bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
return bgpRouteEntry;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
String expectedString =
"BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
"bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
"localPref=100, multiExitDisc=20}";
assertThat(bgpRouteEntry.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null BGP Session.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullBgpSession() {
BgpSession bgpSessionNull = null;
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
new BgpRouteEntry(bgpSessionNull, prefix, nextHop, origin, asPath,
localPref);
}
/**
* Tests invalid class constructor for null AS Path.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullAsPath() {
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
BgpRouteEntry.AsPath asPath = null;
long localPref = 100;
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
}
/**
* Tests getting the fields of a BGP route entry.
*/
@Test
public void testGetFields() {
// Create the fields to compare against
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
long multiExitDisc = 20;
// Generate the entry to test
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.prefix(), is(prefix));
assertThat(bgpRouteEntry.nextHop(), is(nextHop));
assertThat(bgpRouteEntry.getBgpSession(), is(bgpSession));
assertThat(bgpRouteEntry.getOrigin(), is(origin));
assertThat(bgpRouteEntry.getAsPath(), is(asPath));
assertThat(bgpRouteEntry.getLocalPref(), is(localPref));
assertThat(bgpRouteEntry.getMultiExitDisc(), is(multiExitDisc));
}
/**
* Tests whether a BGP route entry is a local route.
*/
@Test
public void testIsLocalRoute() {
//
// Test non-local route
//
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.isLocalRoute(), is(false));
//
// Test local route with AS Path that begins with AS_SET
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SET;
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_SEQUENCE;
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
long multiExitDisc = 20;
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.isLocalRoute(), is(true));
//
// Test local route with empty AS Path
//
pathSegments = new ArrayList<>();
asPath = new BgpRouteEntry.AsPath(pathSegments);
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.isLocalRoute(), is(true));
}
/**
* Tests getting the BGP Neighbor AS number for a route.
*/
@Test
public void testGetNeighborAs() {
//
// Get neighbor AS for non-local route
//
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
assertThat(bgpRouteEntry.getNeighborAs(), is((long) 1));
//
// Get neighbor AS for a local route
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 100;
long multiExitDisc = 20;
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry.getNeighborAs(), is(BgpConstants.BGP_AS_0));
}
/**
* Tests whether a BGP route entry has AS Path loop.
*/
@Test
public void testHasAsPathLoop() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
// Test for loops: test each AS number in the interval [1, 6]
for (int i = 1; i <= 6; i++) {
assertThat(bgpRouteEntry.hasAsPathLoop(i), is(true));
}
// Test for non-loops
assertThat(bgpRouteEntry.hasAsPathLoop(500), is(false));
}
/**
* Tests the BGP Decision Process comparison of BGP routes.
*/
@Test
public void testBgpDecisionProcessComparison() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
//
// Compare two routes that are same
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
//
// Compare two routes with different LOCAL_PREF
//
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 50; // Different
long multiExitDisc = 20;
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
localPref = bgpRouteEntry1.getLocalPref(); // Restore
//
// Compare two routes with different AS_PATH length
//
ArrayList<BgpRouteEntry.PathSegment> pathSegments2 = new ArrayList<>();
pathSegments2.add(pathSegment1);
// Different AS Path
BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments2);
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath2,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(false));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
//
// Compare two routes with different ORIGIN
//
origin = BgpConstants.Update.Origin.EGP; // Different
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
origin = bgpRouteEntry1.getOrigin(); // Restore
//
// Compare two routes with different MULTI_EXIT_DISC
//
multiExitDisc = 10; // Different
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
multiExitDisc = bgpRouteEntry1.getMultiExitDisc(); // Restore
//
// Compare two routes with different BGP ID
//
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession2, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
//
// Compare two routes with different BGP address
//
bgpRouteEntry2 =
new BgpRouteEntry(bgpSession3, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
//
assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
}
/**
* Tests equality of {@link BgpRouteEntry}.
*/
@Test
public void testEquality() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
assertThat(bgpRouteEntry1, is(bgpRouteEntry2));
}
/**
* Tests non-equality of {@link BgpRouteEntry}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
// Setup BGP Route 2
IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
byte origin = BgpConstants.Update.Origin.IGP;
// Setup the AS Path
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);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
long localPref = 500; // Different
long multiExitDisc = 20;
BgpRouteEntry bgpRouteEntry2 =
new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
localPref);
bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
String expectedString =
"BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
"bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
"[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
"PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
"localPref=100, multiExitDisc=20}";
assertThat(bgpRouteEntry.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.sdnip.RouteListener;
import org.onlab.onos.sdnip.RouteUpdate;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.util.TestUtils;
import org.onlab.util.TestUtils.TestUtilsException;
import com.google.common.net.InetAddresses;
/**
* Unit tests for the BgpSessionManager class.
*/
public class BgpSessionManagerTest {
private static final IpAddress IP_LOOPBACK_ID =
IpAddress.valueOf("127.0.0.1");
private static final IpAddress BGP_PEER1_ID = IpAddress.valueOf("10.0.0.1");
private static final long DEFAULT_LOCAL_PREF = 10;
private static final long DEFAULT_MULTI_EXIT_DISC = 20;
// The BGP Session Manager to test
private BgpSessionManager bgpSessionManager;
// Remote Peer state
private ClientBootstrap peerBootstrap;
private TestBgpPeerChannelHandler peerChannelHandler =
new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF);
private TestBgpPeerFrameDecoder peerFrameDecoder =
new TestBgpPeerFrameDecoder();
// The socket that the Remote Peer should connect to
private InetSocketAddress connectToSocket;
private final DummyRouteListener dummyRouteListener =
new DummyRouteListener();
/**
* Dummy implementation for the RouteListener interface.
*/
private class DummyRouteListener implements RouteListener {
@Override
public void update(RouteUpdate routeUpdate) {
// Nothing to do
}
}
@Before
public void setUp() throws Exception {
//
// Setup the BGP Session Manager to test, and start listening for BGP
// connections.
//
bgpSessionManager = new BgpSessionManager(dummyRouteListener);
// NOTE: We use port 0 to bind on any available port
bgpSessionManager.startUp(0);
// Get the port number the BGP Session Manager is listening on
Channel serverChannel = TestUtils.getField(bgpSessionManager,
"serverChannel");
SocketAddress socketAddress = serverChannel.getLocalAddress();
InetSocketAddress inetSocketAddress =
(InetSocketAddress) socketAddress;
//
// Setup the BGP Peer, i.e., the "remote" BGP router that will
// initiate the BGP connection, send BGP UPDATE messages, etc.
//
ChannelFactory channelFactory =
new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
// Setup the transmitting pipeline
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("TestBgpPeerFrameDecoder",
peerFrameDecoder);
pipeline.addLast("TestBgpPeerChannelHandler",
peerChannelHandler);
return pipeline;
}
};
peerBootstrap = new ClientBootstrap(channelFactory);
peerBootstrap.setOption("child.keepAlive", true);
peerBootstrap.setOption("child.tcpNoDelay", true);
peerBootstrap.setPipelineFactory(pipelineFactory);
InetAddress connectToAddress = InetAddresses.forString("127.0.0.1");
connectToSocket = new InetSocketAddress(connectToAddress,
inetSocketAddress.getPort());
}
@After
public void tearDown() throws Exception {
bgpSessionManager.shutDown();
bgpSessionManager = null;
}
/**
* Gets BGP RIB-IN routes by waiting until they are received.
* <p/>
* NOTE: We keep checking once a second the number of received routes,
* up to 5 seconds.
*
* @param bgpSession the BGP session that is expected to receive the
* routes
* @param expectedRoutes the expected number of routes
* @return the BGP RIB-IN routes as received within the expected
* time interval
*/
private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
long expectedRoutes)
throws InterruptedException {
Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
final int maxChecks = 5; // Max wait of 5 seconds
for (int i = 0; i < maxChecks; i++) {
if (bgpRibIn.size() == expectedRoutes) {
break;
}
Thread.sleep(1000);
bgpRibIn = bgpSession.getBgpRibIn();
}
return bgpRibIn;
}
/**
* Gets BGP merged routes by waiting until they are received.
* <p/>
* NOTE: We keep checking once a second the number of received routes,
* up to 5 seconds.
*
* @param expectedRoutes the expected number of routes
* @return the BGP Session Manager routes as received within the expected
* time interval
*/
private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
throws InterruptedException {
Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
final int maxChecks = 5; // Max wait of 5 seconds
for (int i = 0; i < maxChecks; i++) {
if (bgpRoutes.size() == expectedRoutes) {
break;
}
Thread.sleep(1000);
bgpRoutes = bgpSessionManager.getBgpRoutes();
}
return bgpRoutes;
}
/**
* Tests that the BGP OPEN messages have been exchanged, followed by
* KEEPALIVE.
* <p>
* The BGP Peer opens the sessions and transmits OPEN Message, eventually
* followed by KEEPALIVE. The tested BGP listener should respond by
* OPEN Message, followed by KEEPALIVE.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testExchangedBgpOpenMessages()
throws InterruptedException, TestUtilsException {
// Initiate the connection
peerBootstrap.connect(connectToSocket);
// Wait until the OPEN message is received
peerFrameDecoder.receivedOpenMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Wait until the KEEPALIVE message is received
peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
//
// Test the fields from the BGP OPEN message:
// BGP version, AS number, BGP ID
//
assertThat(peerFrameDecoder.remoteBgpVersion,
is(BgpConstants.BGP_VERSION));
assertThat(peerFrameDecoder.remoteAs,
is(TestBgpPeerChannelHandler.PEER_AS));
assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID));
//
// Test that a BgpSession instance has been created
//
assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
assertThat(bgpSessionManager.getBgpSessions(), hasSize(1));
BgpSession bgpSession =
bgpSessionManager.getBgpSessions().iterator().next();
assertThat(bgpSession, notNullValue());
long sessionAs = TestUtils.getField(bgpSession, "localAs");
assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS));
}
/**
* Tests that the BGP UPDATE messages have been received and processed.
*/
@Test
public void testProcessedBgpUpdateMessages() throws InterruptedException {
BgpSession bgpSession;
IpAddress nextHopRouter;
BgpRouteEntry bgpRouteEntry;
ChannelBuffer message;
Collection<BgpRouteEntry> bgpRibIn;
Collection<BgpRouteEntry> bgpRoutes;
// Initiate the connection
peerBootstrap.connect(connectToSocket);
// Wait until the OPEN message is received
peerFrameDecoder.receivedOpenMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Wait until the KEEPALIVE message is received
peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
TimeUnit.MILLISECONDS);
// Get the BGP Session handler
bgpSession = bgpSessionManager.getBgpSessions().iterator().next();
// Prepare routes to add/delete
nextHopRouter = IpAddress.valueOf("10.20.30.40");
Collection<IpPrefix> addedRoutes = new LinkedList<>();
Collection<IpPrefix> withdrawnRoutes = new LinkedList<>();
addedRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
addedRoutes.add(IpPrefix.valueOf("20.0.0.0/8"));
addedRoutes.add(IpPrefix.valueOf("30.0.0.0/16"));
addedRoutes.add(IpPrefix.valueOf("40.0.0.0/24"));
addedRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
withdrawnRoutes.add(IpPrefix.valueOf("60.0.0.0/8"));
withdrawnRoutes.add(IpPrefix.valueOf("70.0.0.0/16"));
withdrawnRoutes.add(IpPrefix.valueOf("80.0.0.0/24"));
withdrawnRoutes.add(IpPrefix.valueOf("90.0.0.0/32"));
// Write the routes
message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
addedRoutes,
withdrawnRoutes);
peerChannelHandler.savedCtx.getChannel().write(message);
// Check that the routes have been received, processed and stored
bgpRibIn = waitForBgpRibIn(bgpSession, 5);
assertThat(bgpRibIn, hasSize(5));
bgpRoutes = waitForBgpRoutes(5);
assertThat(bgpRoutes, hasSize(5));
// Setup the AS Path
ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
segmentAsNumbers1.add((long) 65010);
segmentAsNumbers1.add((long) 65020);
segmentAsNumbers1.add((long) 65030);
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) 65041);
segmentAsNumbers2.add((long) 65042);
segmentAsNumbers2.add((long) 65043);
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
pathSegments.add(pathSegment2);
//
BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("0.0.0.0/0"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("20.0.0.0/8"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("30.0.0.0/16"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("40.0.0.0/24"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("50.0.0.0/32"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
// Delete some routes
addedRoutes = new LinkedList<>();
withdrawnRoutes = new LinkedList<>();
withdrawnRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
withdrawnRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
// Write the routes
message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
addedRoutes,
withdrawnRoutes);
peerChannelHandler.savedCtx.getChannel().write(message);
// Check that the routes have been received, processed and stored
bgpRibIn = waitForBgpRibIn(bgpSession, 3);
assertThat(bgpRibIn, hasSize(3));
bgpRoutes = waitForBgpRoutes(3);
assertThat(bgpRoutes, hasSize(3));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("20.0.0.0/8"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("30.0.0.0/16"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
//
bgpRouteEntry =
new BgpRouteEntry(bgpSession,
IpPrefix.valueOf("40.0.0.0/24"),
nextHopRouter,
(byte) BgpConstants.Update.Origin.IGP,
asPath,
DEFAULT_LOCAL_PREF);
bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
assertThat(bgpRibIn, hasItem(bgpRouteEntry));
// Close the channel and test there are no routes
peerChannelHandler.closeChannel();
bgpRoutes = waitForBgpRoutes(0);
assertThat(bgpRoutes, hasSize(0));
}
}
package org.onlab.onos.sdnip.bgp;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import org.junit.Test;
/**
* Unit tests for the BgpRouteEntry.PathSegment class.
*/
public class PathSegmentTest {
/**
* Generates a Path Segment.
*
* @return a generated PathSegment
*/
private BgpRouteEntry.PathSegment generatePathSegment() {
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
BgpRouteEntry.PathSegment pathSegment =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
return pathSegment;
}
/**
* Tests valid class constructor.
*/
@Test
public void testConstructor() {
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
String expectedString =
"PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
assertThat(pathSegment.toString(), is(expectedString));
}
/**
* Tests invalid class constructor for null Segment AS Numbers.
*/
@Test(expected = NullPointerException.class)
public void testInvalidConstructorNullSegmentAsNumbers() {
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = null;
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
}
/**
* Tests getting the fields of a Path Segment.
*/
@Test
public void testGetFields() {
// Create the fields to compare against
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 2);
segmentAsNumbers.add((long) 3);
// Generate the entry to test
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
assertThat(pathSegment.getType(), is(pathSegmentType));
assertThat(pathSegment.getSegmentAsNumbers(), is(segmentAsNumbers));
}
/**
* Tests equality of {@link BgpRouteEntry.PathSegment}.
*/
@Test
public void testEquality() {
BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
BgpRouteEntry.PathSegment pathSegment2 = generatePathSegment();
assertThat(pathSegment1, is(pathSegment2));
}
/**
* Tests non-equality of {@link BgpRouteEntry.PathSegment}.
*/
@Test
public void testNonEquality() {
BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
// Setup Path Segment 2
byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
ArrayList<Long> segmentAsNumbers = new ArrayList<>();
segmentAsNumbers.add((long) 1);
segmentAsNumbers.add((long) 22); // Different
segmentAsNumbers.add((long) 3);
//
BgpRouteEntry.PathSegment pathSegment2 =
new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
assertThat(pathSegment1, is(not(pathSegment2)));
}
/**
* Tests object string representation.
*/
@Test
public void testToString() {
BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
String expectedString =
"PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
assertThat(pathSegment.toString(), is(expectedString));
}
}
package org.onlab.onos.sdnip.bgp;
import java.util.Collection;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
/**
* Class for handling the remote BGP Peer session.
*/
class TestBgpPeerChannelHandler extends SimpleChannelHandler {
static final long PEER_AS = 65001;
static final int PEER_HOLDTIME = 120; // 120 seconds
final IpAddress bgpId; // The BGP ID
final long localPref; // Local preference for routes
final long multiExitDisc = 20; // MED value
ChannelHandlerContext savedCtx;
/**
* Constructor for given BGP ID.
*
* @param bgpId the BGP ID to use
* @param localPref the local preference for the routes to use
*/
TestBgpPeerChannelHandler(IpAddress bgpId,
long localPref) {
this.bgpId = bgpId;
this.localPref = localPref;
}
/**
* Closes the channel.
*/
void closeChannel() {
savedCtx.getChannel().close();
}
@Override
public void channelConnected(ChannelHandlerContext ctx,
ChannelStateEvent channelEvent) {
this.savedCtx = ctx;
// Prepare and transmit BGP OPEN message
ChannelBuffer message = prepareBgpOpen();
ctx.getChannel().write(message);
// Prepare and transmit BGP KEEPALIVE message
message = prepareBgpKeepalive();
ctx.getChannel().write(message);
}
@Override
public void channelDisconnected(ChannelHandlerContext ctx,
ChannelStateEvent channelEvent) {
// Nothing to do
}
/**
* Prepares BGP OPEN message.
*
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpOpen() {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
message.writeByte(BgpConstants.BGP_VERSION);
message.writeShort((int) PEER_AS);
message.writeShort(PEER_HOLDTIME);
message.writeInt(bgpId.toInt());
message.writeByte(0); // No Optional Parameters
return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
}
/**
* Prepares BGP UPDATE message.
*
* @param nextHopRouter the next-hop router address for the routes to add
* @param addedRoutes the routes to add
* @param withdrawnRoutes the routes to withdraw
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpUpdate(IpAddress nextHopRouter,
Collection<IpPrefix> addedRoutes,
Collection<IpPrefix> withdrawnRoutes) {
int attrFlags;
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
ChannelBuffer pathAttributes =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Encode the Withdrawn Routes
ChannelBuffer encodedPrefixes = encodePackedPrefixes(withdrawnRoutes);
message.writeShort(encodedPrefixes.readableBytes());
message.writeBytes(encodedPrefixes);
// Encode the Path Attributes
// ORIGIN: IGP
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.Origin.TYPE);
pathAttributes.writeByte(1); // Data length
pathAttributes.writeByte(BgpConstants.Update.Origin.IGP);
// AS_PATH: Two Path Segments of 3 ASes each
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE);
pathAttributes.writeByte(16); // Data length
byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
pathAttributes.writeByte(pathSegmentType1);
pathAttributes.writeByte(3); // Three ASes
pathAttributes.writeShort(65010); // AS=65010
pathAttributes.writeShort(65020); // AS=65020
pathAttributes.writeShort(65030); // AS=65030
byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
pathAttributes.writeByte(pathSegmentType2);
pathAttributes.writeByte(3); // Three ASes
pathAttributes.writeShort(65041); // AS=65041
pathAttributes.writeShort(65042); // AS=65042
pathAttributes.writeShort(65043); // AS=65043
// NEXT_HOP: nextHopRouter
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.NextHop.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt(nextHopRouter.toInt()); // Next-hop router
// LOCAL_PREF: localPref
attrFlags = 0x40; // Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.LocalPref.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt((int) localPref); // Preference value
// MULTI_EXIT_DISC: multiExitDisc
attrFlags = 0x80; // Optional
// Non-Transitive flag
pathAttributes.writeByte(attrFlags);
pathAttributes.writeByte(BgpConstants.Update.MultiExitDisc.TYPE);
pathAttributes.writeByte(4); // Data length
pathAttributes.writeInt((int) multiExitDisc); // Preference value
// The NLRI prefixes
encodedPrefixes = encodePackedPrefixes(addedRoutes);
// Write the Path Attributes, beginning with its length
message.writeShort(pathAttributes.readableBytes());
message.writeBytes(pathAttributes);
message.writeBytes(encodedPrefixes);
return prepareBgpMessage(BgpConstants.BGP_TYPE_UPDATE, message);
}
/**
* Encodes a collection of IPv4 network prefixes in a packed format.
* <p>
* The IPv4 prefixes are encoded in the form:
* <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
* and Prefix is the IPv4 prefix (padded with trailing bits to the end
* of an octet).
*
* @param prefixes the prefixes to encode
* @return the buffer with the encoded prefixes
*/
private ChannelBuffer encodePackedPrefixes(Collection<IpPrefix> prefixes) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Write each of the prefixes
for (IpPrefix prefix : prefixes) {
int prefixBitlen = prefix.prefixLength();
int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
message.writeByte(prefixBitlen);
IpAddress address = prefix.toIpAddress();
long value = address.toInt() & 0xffffffffL;
for (int i = 0; i < IpAddress.INET_LEN; i++) {
if (prefixBytelen-- == 0) {
break;
}
long nextByte =
(value >> ((IpAddress.INET_LEN - i - 1) * 8)) & 0xff;
message.writeByte((int) nextByte);
}
}
return message;
}
/**
* Prepares BGP KEEPALIVE message.
*
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpKeepalive() {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
}
/**
* Prepares BGP NOTIFICATION message.
*
* @param errorCode the BGP NOTIFICATION Error Code
* @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
* otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
* @param payload the BGP NOTIFICATION Data if applicable, otherwise null
* @return the message to transmit (BGP header included)
*/
ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
ChannelBuffer data) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
// Prepare the NOTIFICATION message payload
message.writeByte(errorCode);
message.writeByte(errorSubcode);
if (data != null) {
message.writeBytes(data);
}
return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
}
/**
* Prepares BGP message.
*
* @param type the BGP message type
* @param payload the message payload to transmit (BGP header excluded)
* @return the message to transmit (BGP header included)
*/
private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
ChannelBuffer message =
ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
payload.readableBytes());
// Write the marker
for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
message.writeByte(0xff);
}
// Write the rest of the BGP header
message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
payload.readableBytes());
message.writeByte(type);
// Write the payload
message.writeBytes(payload);
return message;
}
}
package org.onlab.onos.sdnip.bgp;
import java.util.concurrent.CountDownLatch;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.onlab.packet.IpAddress;
/**
* Class for handling the decoding of the BGP messages at the remote
* BGP peer session.
*/
class TestBgpPeerFrameDecoder extends FrameDecoder {
int remoteBgpVersion; // 1 octet
long remoteAs; // 2 octets
long remoteHoldtime; // 2 octets
IpAddress remoteBgpIdentifier; // 4 octets -> IPv4 address
final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1);
final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1);
@Override
protected Object decode(ChannelHandlerContext ctx,
Channel channel,
ChannelBuffer buf) throws Exception {
// Test for minimum length of the BGP message
if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
// No enough data received
return null;
}
//
// Mark the current buffer position in case we haven't received
// the whole message.
//
buf.markReaderIndex();
//
// Read and check the BGP message Marker field: it must be all ones
//
byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
buf.readBytes(marker);
for (int i = 0; i < marker.length; i++) {
if (marker[i] != (byte) 0xff) {
// ERROR: Connection Not Synchronized. Close the channel.
ctx.getChannel().close();
return null;
}
}
//
// Read and check the BGP message Length field
//
int length = buf.readUnsignedShort();
if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
(length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return null;
}
//
// Test whether the rest of the message is received:
// So far we have read the Marker (16 octets) and the
// Length (2 octets) fields.
//
int remainingMessageLen =
length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
if (buf.readableBytes() < remainingMessageLen) {
// No enough data received
buf.resetReaderIndex();
return null;
}
//
// Read the BGP message Type field, and process based on that type
//
int type = buf.readUnsignedByte();
remainingMessageLen--; // Adjust after reading the type
ChannelBuffer message = buf.readBytes(remainingMessageLen);
//
// Process the remaining of the message based on the message type
//
switch (type) {
case BgpConstants.BGP_TYPE_OPEN:
processBgpOpen(ctx, message);
break;
case BgpConstants.BGP_TYPE_UPDATE:
// NOTE: Not used as part of the test, because ONOS does not
// originate UPDATE messages.
break;
case BgpConstants.BGP_TYPE_NOTIFICATION:
// NOTE: Not used as part of the testing (yet)
break;
case BgpConstants.BGP_TYPE_KEEPALIVE:
processBgpKeepalive(ctx, message);
break;
default:
// ERROR: Bad Message Type. Close the channel.
ctx.getChannel().close();
return null;
}
return null;
}
/**
* Processes BGP OPEN message.
*
* @param ctx the Channel Handler Context.
* @param message the message to process.
*/
private void processBgpOpen(ChannelHandlerContext ctx,
ChannelBuffer message) {
int minLength =
BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
if (message.readableBytes() < minLength) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
//
// Parse the OPEN message
//
remoteBgpVersion = message.readUnsignedByte();
remoteAs = message.readUnsignedShort();
remoteHoldtime = message.readUnsignedShort();
remoteBgpIdentifier = IpAddress.valueOf((int) message.readUnsignedInt());
// Optional Parameters
int optParamLen = message.readUnsignedByte();
if (message.readableBytes() < optParamLen) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
message.readBytes(optParamLen); // NOTE: data ignored
// BGP OPEN message successfully received
receivedOpenMessageLatch.countDown();
}
/**
* Processes BGP KEEPALIVE message.
*
* @param ctx the Channel Handler Context.
* @param message the message to process.
*/
private void processBgpKeepalive(ChannelHandlerContext ctx,
ChannelBuffer message) {
if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
// ERROR: Bad Message Length. Close the channel.
ctx.getChannel().close();
return;
}
// BGP KEEPALIVE message successfully received
receivedKeepaliveMessageLatch.countDown();
}
}
package org.onlab.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Utilities for testing.
*/
public final class TestUtils {
/**
* Sets the field, bypassing scope restriction.
*
* @param subject Object where the field belongs
* @param fieldName name of the field to set
* @param value value to set to the field.
* @param <T> subject type
* @param <U> value type
* @throws TestUtilsException if there are reflection errors while setting
* the field
*/
public static <T, U> void setField(T subject, String fieldName, U value)
throws TestUtilsException {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(subject, value);
} catch (NoSuchFieldException | SecurityException |
IllegalArgumentException | IllegalAccessException e) {
throw new TestUtilsException("setField failed", e);
}
}
/**
* Gets the field, bypassing scope restriction.
*
* @param subject Object where the field belongs
* @param fieldName name of the field to get
* @return value of the field.
* @param <T> subject type
* @param <U> field value type
* @throws TestUtilsException if there are reflection errors while getting
* the field
*/
public static <T, U> U getField(T subject, String fieldName)
throws TestUtilsException {
try {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
@SuppressWarnings("unchecked")
U result = (U) field.get(subject);
return result;
} catch (NoSuchFieldException | SecurityException |
IllegalArgumentException | IllegalAccessException e) {
throw new TestUtilsException("getField failed", e);
}
}
/**
* Calls the method, bypassing scope restriction.
*
* @param subject Object where the method belongs
* @param methodName name of the method to call
* @param paramTypes formal parameter type array
* @param args arguments
* @return return value or null if void
* @param <T> subject type
* @param <U> return value type
* @throws TestUtilsException if there are reflection errors while calling
* the method
*/
public static <T, U> U callMethod(T subject, String methodName,
Class<?>[] paramTypes, Object...args) throws TestUtilsException {
try {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) subject.getClass();
final Method method;
if (paramTypes == null || paramTypes.length == 0) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramTypes);
}
method.setAccessible(true);
@SuppressWarnings("unchecked")
U result = (U) method.invoke(subject, args);
return result;
} catch (NoSuchMethodException | SecurityException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new TestUtilsException("callMethod failed", e);
}
}
/**
* Calls the method, bypassing scope restriction.
*
* @param subject Object where the method belongs
* @param methodName name of the method to call
* @param paramType formal parameter type
* @param arg argument
* @return return value or null if void
* @param <T> subject type
* @param <U> return value type
* @throws TestUtilsException if there are reflection errors while calling
* the method
*/
public static <T, U> U callMethod(T subject, String methodName,
Class<?> paramType, Object arg) throws TestUtilsException {
return callMethod(subject, methodName, new Class<?>[]{paramType}, arg);
}
/**
* Triggers an allocation of an object of type <T> and forces a call to
* the private constructor.
*
* @param constructor Constructor to call
* @param <T> type of the object to create
* @return created object of type <T>
* @throws TestUtilsException if there are reflection errors while calling
* the constructor
*/
public static <T> T callConstructor(Constructor<T> constructor)
throws TestUtilsException {
try {
constructor.setAccessible(true);
return constructor.newInstance();
} catch (InstantiationException | IllegalAccessException |
InvocationTargetException error) {
throw new TestUtilsException("callConstructor failed", error);
}
}
/**
* Avoid instantiation.
*/
private TestUtils() {}
/**
* Exception that can be thrown if problems are encountered while executing
* the utility method. These are usually problems accessing fields/methods
* through reflection. The original exception can be found by examining the
* cause.
*/
public static class TestUtilsException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with the specified detail message and
* cause.
*
* @param message the detail message
* @param cause the original cause of this exception
*/
public TestUtilsException(String message, Throwable cause) {
super(message, cause);
}
}
}
package org.onlab.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Before;
import org.junit.Test;
import org.onlab.util.TestUtils.TestUtilsException;
/**
* Test and usage examples for TestUtils.
*/
public class TestUtilsTest {
/**
* Test data.
*/
private static final class TestClass {
@SuppressWarnings("unused")
private int privateField = 42;
@SuppressWarnings("unused")
protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE
/**
* Protected method with multiple argument.
*
* @param x simply returns
* @param y not used
* @return x
*/
@SuppressWarnings("unused")
private int privateMethod(Number x, Long y) {
return x.intValue();
}
/**
* Protected method with no argument.
*
* @return int
*/
@SuppressWarnings("unused")
protected int protectedMethod() {
return 42;
}
/**
* Method returning array.
*
* @param ary random array
* @return ary
*/
@SuppressWarnings("unused")
private int[] arrayReturnMethod(int[] ary) {
return ary;
}
/**
* Method without return value.
*
* @param s ignored
*/
@SuppressWarnings("unused")
private void voidMethod(String s) {
System.out.println(s);
}
}
private TestClass test;
/**
* Sets up the test fixture.
*/
@Before
public void setUp() {
test = new TestClass();
}
/**
* Example to access private field.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testSetGetPrivateField() throws TestUtilsException {
assertEquals(42, TestUtils.getField(test, "privateField"));
TestUtils.setField(test, "privateField", 0xDEAD);
assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
}
/**
* Example to access protected field.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testSetGetProtectedField() throws TestUtilsException {
assertEquals(2501, TestUtils.getField(test, "protectedField"));
TestUtils.setField(test, "protectedField", 0xBEEF);
assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
}
/**
* Example to call private method and multiple parameters.
* <p/>
* It also illustrates that paramTypes must match declared type,
* not the runtime types of arguments.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallPrivateMethod() throws TestUtilsException {
int result = TestUtils.callMethod(test, "privateMethod",
new Class<?>[] {Number.class, Long.class},
Long.valueOf(42), Long.valueOf(32));
assertEquals(42, result);
}
/**
* Example to call protected method and no parameters.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallProtectedMethod() throws TestUtilsException {
int result = TestUtils.callMethod(test, "protectedMethod",
new Class<?>[] {});
assertEquals(42, result);
}
/**
* Example to call method returning array.
* <p/>
* Note: It is not required to receive as Object.
* Following is just verifying it is not Boxed arrays.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallArrayReturnMethod() throws TestUtilsException {
int[] array = {1, 2, 3};
Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod",
new Class<?>[] {int[].class}, array);
assertEquals(int[].class, aryResult.getClass());
assertArrayEquals(array, (int[]) aryResult);
}
/**
* Example to call void returning method.
* <p/>
* Note: Return value will be null for void methods.
*
* @throws TestUtilsException TestUtils error
*/
@Test
public void testCallVoidReturnMethod() throws TestUtilsException {
Object voidResult = TestUtils.callMethod(test, "voidMethod",
String.class, "foobar");
assertNull(voidResult);
}
}