diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/apps/routing/src/test/java/org | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/apps/routing/src/test/java/org')
9 files changed, 2890 insertions, 0 deletions
diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java new file mode 100644 index 00000000..9213e4e0 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/AsPathTest.java @@ -0,0 +1,233 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.ArrayList; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +/** + * Unit tests for the BgpRouteEntry.AsPath class. + */ +public class AsPathTest { + /** + * Generates Path Segments. + * + * @return the generated Path Segments + */ + private ArrayList<BgpRouteEntry.PathSegment> generatePathSegments() { + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); + byte pathSegmentType; + ArrayList<Long> segmentAsNumbers; + BgpRouteEntry.PathSegment pathSegment; + + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(1L); + segmentAsNumbers.add(2L); + segmentAsNumbers.add(3L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(4L); + segmentAsNumbers.add(5L); + segmentAsNumbers.add(6L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(7L); + segmentAsNumbers.add(8L); + segmentAsNumbers.add(9L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(10L); + segmentAsNumbers.add(11L); + segmentAsNumbers.add(12L); + 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; + } + + /** + * Tests valid class constructor. + */ + @Test + public void testConstructor() { + BgpRouteEntry.AsPath asPath = generateAsPath(); + + String expectedString = + "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)); + } + + /** + * 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 = + generatePathSegments(); + + // Generate the entry to test + BgpRouteEntry.AsPath asPath = generateAsPath(); + + assertThat(asPath.getPathSegments(), is(pathSegments)); + } + + /** + * Tests getting the AS Path Length. + */ + @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)); + + // 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 pathSegmentType; + ArrayList<Long> segmentAsNumbers; + BgpRouteEntry.PathSegment pathSegment; + + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(1L); + segmentAsNumbers.add(2L); + segmentAsNumbers.add(3L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_CONFED_SET; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(4L); + segmentAsNumbers.add(5L); + segmentAsNumbers.add(6L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(7L); + segmentAsNumbers.add(8L); + segmentAsNumbers.add(9L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SET; + segmentAsNumbers = new ArrayList<>(); + segmentAsNumbers.add(10L); + segmentAsNumbers.add(111L); // Different + segmentAsNumbers.add(12L); + pathSegment = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + pathSegments.add(pathSegment); + // + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments); + + assertThat(asPath1, Matchers.is(not(asPath2))); + } + + /** + * Tests object string representation. + */ + @Test + public void testToString() { + BgpRouteEntry.AsPath asPath = generateAsPath(); + + String expectedString = + "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)); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java new file mode 100644 index 00000000..446056e9 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpRouteEntryTest.java @@ -0,0 +1,520 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +import org.easymock.EasyMock; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; + +import java.util.ArrayList; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +/** + * Unit tests for the BgpRouteEntry class. + */ +public class BgpRouteEntryTest { + private BgpSession bgpSession; + private static final Ip4Address BGP_SESSION_BGP_ID = + Ip4Address.valueOf("10.0.0.1"); + private static final Ip4Address BGP_SESSION_IP_ADDRESS = + Ip4Address.valueOf("20.0.0.1"); + + private BgpSession bgpSession2; + private static final Ip4Address BGP_SESSION_BGP_ID2 = + Ip4Address.valueOf("10.0.0.2"); + private static final Ip4Address BGP_SESSION_IP_ADDRESS2 = + Ip4Address.valueOf("20.0.0.1"); + + private BgpSession bgpSession3; + private static final Ip4Address BGP_SESSION_BGP_ID3 = + Ip4Address.valueOf("10.0.0.1"); + private static final Ip4Address BGP_SESSION_IP_ADDRESS3 = + Ip4Address.valueOf("20.0.0.2"); + + private final BgpSessionInfo localInfo = new BgpSessionInfo(); + private final BgpSessionInfo remoteInfo = new BgpSessionInfo(); + + private final BgpSessionInfo localInfo2 = new BgpSessionInfo(); + private final BgpSessionInfo remoteInfo2 = new BgpSessionInfo(); + + private final BgpSessionInfo localInfo3 = new BgpSessionInfo(); + private final BgpSessionInfo remoteInfo3 = new BgpSessionInfo(); + + @Before + public void setUp() throws Exception { + // Mock objects for testing + bgpSession = EasyMock.createMock(BgpSession.class); + bgpSession2 = EasyMock.createMock(BgpSession.class); + bgpSession3 = EasyMock.createMock(BgpSession.class); + + // Setup the BGP Sessions + remoteInfo.setIp4Address(BGP_SESSION_IP_ADDRESS); + remoteInfo2.setIp4Address(BGP_SESSION_IP_ADDRESS2); + remoteInfo3.setIp4Address(BGP_SESSION_IP_ADDRESS3); + remoteInfo.setBgpId(BGP_SESSION_BGP_ID); + remoteInfo2.setBgpId(BGP_SESSION_BGP_ID2); + remoteInfo3.setBgpId(BGP_SESSION_BGP_ID3); + + EasyMock.expect(bgpSession.localInfo()).andReturn(localInfo).anyTimes(); + EasyMock.expect(bgpSession.remoteInfo()).andReturn(remoteInfo).anyTimes(); + EasyMock.expect(bgpSession2.localInfo()).andReturn(localInfo2).anyTimes(); + EasyMock.expect(bgpSession2.remoteInfo()).andReturn(remoteInfo2).anyTimes(); + EasyMock.expect(bgpSession3.localInfo()).andReturn(localInfo3).anyTimes(); + EasyMock.expect(bgpSession3.remoteInfo()).andReturn(remoteInfo3).anyTimes(); + + EasyMock.replay(bgpSession); + EasyMock.replay(bgpSession2); + EasyMock.replay(bgpSession3); + } + + /** + * Generates a BGP Route Entry. + * + * @return a generated BGP Route Entry + */ + private BgpRouteEntry generateBgpRouteEntry() { + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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(1L); + segmentAsNumbers1.add(2L); + segmentAsNumbers1.add(3L); + 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(4L); + segmentAsNumbers2.add(5L); + segmentAsNumbers2.add(6L); + 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=IGP, asPath=AsPath{pathSegments=" + + "[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " + + "PathSegment{type=AS_SET, 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; + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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() { + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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 + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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(1L); + segmentAsNumbers1.add(2L); + segmentAsNumbers1.add(3L); + 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(4L); + segmentAsNumbers2.add(5L); + segmentAsNumbers2.add(6L); + 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 + // + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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(1L); + segmentAsNumbers1.add(2L); + segmentAsNumbers1.add(3L); + 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(4L); + segmentAsNumbers2.add(5L); + segmentAsNumbers2.add(6L); + 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(1L)); + + // + // Get neighbor AS for a local route + // + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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 + // + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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(1L); + segmentAsNumbers1.add(2L); + segmentAsNumbers1.add(3L); + 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(4L); + segmentAsNumbers2.add(5L); + segmentAsNumbers2.add(6L); + 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 + Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); + Ip4Address nextHop = Ip4Address.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(1L); + segmentAsNumbers1.add(2L); + segmentAsNumbers1.add(3L); + 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(4L); + segmentAsNumbers2.add(5L); + segmentAsNumbers2.add(6L); + 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, Matchers.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=IGP, asPath=AsPath{pathSegments=" + + "[PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}, " + + "PathSegment{type=AS_SET, segmentAsNumbers=[4, 5, 6]}]}, " + + "localPref=100, multiExitDisc=20}"; + assertThat(bgpRouteEntry.toString(), is(expectedString)); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java new file mode 100644 index 00000000..634e87b9 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java @@ -0,0 +1,895 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +import com.google.common.net.InetAddresses; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +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.junit.TestUtils; +import org.onlab.junit.TestUtils.TestUtilsException; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onosproject.routing.RouteListener; +import org.onosproject.routing.RouteUpdate; +import org.osgi.service.component.ComponentContext; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.LinkedList; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * Unit tests for the BgpSessionManager class. + */ +public class BgpSessionManagerTest { + private static final Ip4Address IP_LOOPBACK_ID = + Ip4Address.valueOf("127.0.0.1"); + private static final Ip4Address BGP_PEER1_ID = + Ip4Address.valueOf("10.0.0.1"); + private static final Ip4Address BGP_PEER2_ID = + Ip4Address.valueOf("10.0.0.2"); + private static final Ip4Address BGP_PEER3_ID = + Ip4Address.valueOf("10.0.0.3"); + private static final Ip4Address NEXT_HOP1_ROUTER = + Ip4Address.valueOf("10.20.30.41"); + private static final Ip4Address NEXT_HOP2_ROUTER = + Ip4Address.valueOf("10.20.30.42"); + private static final Ip4Address NEXT_HOP3_ROUTER = + Ip4Address.valueOf("10.20.30.43"); + + private static final long DEFAULT_LOCAL_PREF = 10; + private static final long BETTER_LOCAL_PREF = 20; + private static final long DEFAULT_MULTI_EXIT_DISC = 20; + private static final long BETTER_MULTI_EXIT_DISC = 30; + + BgpRouteEntry.AsPath asPathShort; + BgpRouteEntry.AsPath asPathLong; + + // Timeout waiting for a message to be received + private static final int MESSAGE_TIMEOUT_MS = 5000; // 5s + + // The BGP Session Manager to test + private BgpSessionManager bgpSessionManager; + + // Remote Peer state + private final Collection<TestBgpPeer> peers = new LinkedList<>(); + TestBgpPeer peer1; + TestBgpPeer peer2; + TestBgpPeer peer3; + + // Local BGP per-peer session state + BgpSession bgpSession1; + BgpSession bgpSession2; + BgpSession bgpSession3; + + // The socket that the remote peers 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(Collection<RouteUpdate> routeUpdate) { + // Nothing to do + } + } + + /** + * A class to capture the state for a BGP peer. + */ + private final class TestBgpPeer { + private final Ip4Address peerId; + private ClientBootstrap peerBootstrap; + private TestBgpPeerChannelHandler peerChannelHandler; + private TestBgpPeerFrameDecoder peerFrameDecoder = + new TestBgpPeerFrameDecoder(); + + /** + * Constructor. + * + * @param peerId the peer ID + */ + private TestBgpPeer(Ip4Address peerId) { + this.peerId = peerId; + peerChannelHandler = new TestBgpPeerChannelHandler(peerId); + } + + /** + * Starts up the BGP peer and connects it to the tested SDN-IP + * instance. + * + * @param connectToSocket the socket to connect to + */ + private void connect(InetSocketAddress connectToSocket) + throws InterruptedException { + // + // 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); + peerBootstrap.connect(connectToSocket); + + boolean result; + // Wait until the OPEN message is received + result = peerFrameDecoder.receivedOpenMessageLatch.await( + MESSAGE_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + assertThat(result, is(true)); + // Wait until the KEEPALIVE message is received + result = peerFrameDecoder.receivedKeepaliveMessageLatch.await( + MESSAGE_TIMEOUT_MS, + TimeUnit.MILLISECONDS); + assertThat(result, is(true)); + + for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) { + if (bgpSession.remoteInfo().bgpId().equals(BGP_PEER1_ID)) { + bgpSession1 = bgpSession; + } + if (bgpSession.remoteInfo().bgpId().equals(BGP_PEER2_ID)) { + bgpSession2 = bgpSession; + } + if (bgpSession.remoteInfo().bgpId().equals(BGP_PEER3_ID)) { + bgpSession3 = bgpSession; + } + } + } + } + + /** + * Class that implements a matcher for BgpRouteEntry by considering + * the BGP peer the entry was received from. + */ + private static final class BgpRouteEntryAndPeerMatcher + extends TypeSafeMatcher<Collection<BgpRouteEntry>> { + private final BgpRouteEntry bgpRouteEntry; + + private BgpRouteEntryAndPeerMatcher(BgpRouteEntry bgpRouteEntry) { + this.bgpRouteEntry = bgpRouteEntry; + } + + @Override + public boolean matchesSafely(Collection<BgpRouteEntry> entries) { + for (BgpRouteEntry entry : entries) { + if (bgpRouteEntry.equals(entry) && + bgpRouteEntry.getBgpSession() == entry.getBgpSession()) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("BGP route entry lookup for entry \""). + appendText(bgpRouteEntry.toString()). + appendText("\""); + } + } + + /** + * A helper method used for testing whether a collection of + * BGP route entries contains an entry from a specific BGP peer. + * + * @param bgpRouteEntry the BGP route entry to test + * @return an instance of BgpRouteEntryAndPeerMatcher that implements + * the matching logic + */ + private static BgpRouteEntryAndPeerMatcher hasBgpRouteEntry( + BgpRouteEntry bgpRouteEntry) { + return new BgpRouteEntryAndPeerMatcher(bgpRouteEntry); + } + + @SuppressWarnings("unchecked") + private Dictionary<String, String> + getDictionaryMock(ComponentContext componentContext) { + Dictionary<String, String> dictionary = createMock(Dictionary.class); + expect(dictionary.get("bgpPort")).andReturn("0"); + replay(dictionary); + expect(componentContext.getProperties()).andReturn(dictionary); + return dictionary; + } + + @Before + public void setUp() throws Exception { + peer1 = new TestBgpPeer(BGP_PEER1_ID); + peer2 = new TestBgpPeer(BGP_PEER2_ID); + peer3 = new TestBgpPeer(BGP_PEER3_ID); + peers.clear(); + peers.add(peer1); + peers.add(peer2); + peers.add(peer3); + + // + // Setup the BGP Session Manager to test, and start listening for BGP + // connections. + // + bgpSessionManager = new BgpSessionManager(); + // NOTE: We use port 0 to bind on any available port + ComponentContext componentContext = createMock(ComponentContext.class); + Dictionary<String, String> dictionary = getDictionaryMock(componentContext); + replay(componentContext); + bgpSessionManager.activate(componentContext); + bgpSessionManager.start(dummyRouteListener); + + // 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; + InetAddress connectToAddress = InetAddresses.forString("127.0.0.1"); + connectToSocket = new InetSocketAddress(connectToAddress, + inetSocketAddress.getPort()); + + // + // Setup the AS Paths + // + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); + segmentAsNumbers1.add(65010L); + segmentAsNumbers1.add(65020L); + segmentAsNumbers1.add(65030L); + BgpRouteEntry.PathSegment pathSegment1 = + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); + pathSegments.add(pathSegment1); + asPathShort = new BgpRouteEntry.AsPath(new ArrayList<>(pathSegments)); + // + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); + segmentAsNumbers2.add(65041L); + segmentAsNumbers2.add(65042L); + segmentAsNumbers2.add(65043L); + BgpRouteEntry.PathSegment pathSegment2 = + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); + pathSegments.add(pathSegment2); + // + asPathLong = new BgpRouteEntry.AsPath(pathSegments); + } + + @After + public void tearDown() throws Exception { + bgpSessionManager.stop(); + bgpSessionManager = null; + } + + /** + * Gets BGP RIB-IN routes by waiting until they are received. + * <p> + * NOTE: We keep checking once every 10ms the number of received routes, + * up to 5 seconds. + * </p> + * + * @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.getBgpRibIn4(); + + final int maxChecks = 500; // Max wait of 5 seconds + for (int i = 0; i < maxChecks; i++) { + if (bgpRibIn.size() == expectedRoutes) { + break; + } + Thread.sleep(10); + bgpRibIn = bgpSession.getBgpRibIn4(); + } + + return bgpRibIn; + } + + /** + * Gets BGP merged routes by waiting until they are received. + * <p> + * NOTE: We keep checking once every 10ms the number of received routes, + * up to 5 seconds. + * </p> + * + * @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.getBgpRoutes4(); + + final int maxChecks = 500; // Max wait of 5 seconds + for (int i = 0; i < maxChecks; i++) { + if (bgpRoutes.size() == expectedRoutes) { + break; + } + Thread.sleep(10); + bgpRoutes = bgpSessionManager.getBgpRoutes4(); + } + + return bgpRoutes; + } + + /** + * Gets a merged BGP route by waiting until it is received. + * <p> + * NOTE: We keep checking once every 10ms whether the route is received, + * up to 5 seconds. + * </p> + * + * @param expectedRoute the expected route + * @return the merged BGP route if received within the expected time + * interval, otherwise null + */ + private BgpRouteEntry waitForBgpRoute(BgpRouteEntry expectedRoute) + throws InterruptedException { + Collection<BgpRouteEntry> bgpRoutes = + bgpSessionManager.getBgpRoutes4(); + + final int maxChecks = 500; // Max wait of 5 seconds + for (int i = 0; i < maxChecks; i++) { + for (BgpRouteEntry bgpRouteEntry : bgpRoutes) { + if (bgpRouteEntry.equals(expectedRoute) && + bgpRouteEntry.getBgpSession() == + expectedRoute.getBgpSession()) { + return bgpRouteEntry; + } + } + Thread.sleep(10); + bgpRoutes = bgpSessionManager.getBgpRoutes4(); + } + + return null; + } + + /** + * 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. + * </p> + * + * @throws TestUtilsException TestUtils error + */ + @Test + public void testExchangedBgpOpenMessages() + throws InterruptedException, TestUtilsException { + // Initiate the connections + peer1.connect(connectToSocket); + peer2.connect(connectToSocket); + peer3.connect(connectToSocket); + + // + // Test the fields from the BGP OPEN message: + // BGP version, AS number, BGP ID + // + for (TestBgpPeer peer : peers) { + assertThat(peer.peerFrameDecoder.remoteInfo.bgpVersion(), + is(BgpConstants.BGP_VERSION)); + assertThat(peer.peerFrameDecoder.remoteInfo.bgpId(), + is(IP_LOOPBACK_ID)); + assertThat(peer.peerFrameDecoder.remoteInfo.asNumber(), + is(TestBgpPeerChannelHandler.PEER_AS)); + } + + // + // Test that the BgpSession instances have been created + // + assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID)); + assertThat(bgpSessionManager.getBgpSessions(), hasSize(3)); + assertThat(bgpSession1, notNullValue()); + assertThat(bgpSession2, notNullValue()); + assertThat(bgpSession3, notNullValue()); + for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) { + long sessionAs = bgpSession.localInfo().asNumber(); + assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS)); + } + } + + + /** + * Tests that the BGP OPEN with Capability 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. + * </p> + * + * @throws TestUtilsException TestUtils error + */ + @Test + public void testExchangedBgpOpenCapabilityMessages() + throws InterruptedException, TestUtilsException { + // + // Setup the BGP Capabilities for all peers + // + for (TestBgpPeer peer : peers) { + peer.peerChannelHandler.localInfo.setIpv4Unicast(); + peer.peerChannelHandler.localInfo.setIpv4Multicast(); + peer.peerChannelHandler.localInfo.setIpv6Unicast(); + peer.peerChannelHandler.localInfo.setIpv6Multicast(); + peer.peerChannelHandler.localInfo.setAs4OctetCapability(); + peer.peerChannelHandler.localInfo.setAs4Number( + TestBgpPeerChannelHandler.PEER_AS4); + } + + // Initiate the connections + peer1.connect(connectToSocket); + peer2.connect(connectToSocket); + peer3.connect(connectToSocket); + + // + // Test the fields from the BGP OPEN message: + // BGP version, BGP ID + // + for (TestBgpPeer peer : peers) { + assertThat(peer.peerFrameDecoder.remoteInfo.bgpVersion(), + is(BgpConstants.BGP_VERSION)); + assertThat(peer.peerFrameDecoder.remoteInfo.bgpId(), + is(IP_LOOPBACK_ID)); + } + + // + // Test that the BgpSession instances have been created, + // and contain the appropriate BGP session information. + // + assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID)); + assertThat(bgpSessionManager.getBgpSessions(), hasSize(3)); + assertThat(bgpSession1, notNullValue()); + assertThat(bgpSession2, notNullValue()); + assertThat(bgpSession3, notNullValue()); + for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) { + BgpSessionInfo localInfo = bgpSession.localInfo(); + assertThat(localInfo.ipv4Unicast(), is(true)); + assertThat(localInfo.ipv4Multicast(), is(true)); + assertThat(localInfo.ipv6Unicast(), is(true)); + assertThat(localInfo.ipv6Multicast(), is(true)); + assertThat(localInfo.as4OctetCapability(), is(true)); + assertThat(localInfo.asNumber(), + is(TestBgpPeerChannelHandler.PEER_AS4)); + assertThat(localInfo.as4Number(), + is(TestBgpPeerChannelHandler.PEER_AS4)); + } + } + + /** + * Tests that the BGP UPDATE messages have been received and processed. + */ + @Test + public void testProcessedBgpUpdateMessages() throws InterruptedException { + ChannelBuffer message; + BgpRouteEntry bgpRouteEntry; + Collection<BgpRouteEntry> bgpRibIn1; + Collection<BgpRouteEntry> bgpRibIn2; + Collection<BgpRouteEntry> bgpRibIn3; + Collection<BgpRouteEntry> bgpRoutes; + + // Initiate the connections + peer1.connect(connectToSocket); + peer2.connect(connectToSocket); + peer3.connect(connectToSocket); + + // Prepare routes to add/delete + Collection<Ip4Prefix> addedRoutes = new LinkedList<>(); + Collection<Ip4Prefix> withdrawnRoutes = new LinkedList<>(); + + // + // Add and delete some routes + // + addedRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0")); + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + addedRoutes.add(Ip4Prefix.valueOf("30.0.0.0/16")); + addedRoutes.add(Ip4Prefix.valueOf("40.0.0.0/24")); + addedRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32")); + withdrawnRoutes.add(Ip4Prefix.valueOf("60.0.0.0/8")); + withdrawnRoutes.add(Ip4Prefix.valueOf("70.0.0.0/16")); + withdrawnRoutes.add(Ip4Prefix.valueOf("80.0.0.0/24")); + withdrawnRoutes.add(Ip4Prefix.valueOf("90.0.0.0/32")); + // Write the routes + message = peer1.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP1_ROUTER, + DEFAULT_LOCAL_PREF, + DEFAULT_MULTI_EXIT_DISC, + asPathLong, + addedRoutes, + withdrawnRoutes); + peer1.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn1 = waitForBgpRibIn(bgpSession1, 5); + assertThat(bgpRibIn1, hasSize(5)); + bgpRoutes = waitForBgpRoutes(5); + assertThat(bgpRoutes, hasSize(5)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("0.0.0.0/0"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("30.0.0.0/16"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("40.0.0.0/24"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("50.0.0.0/32"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + // + // Delete some routes + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + withdrawnRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0")); + withdrawnRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32")); + // Write the routes + message = peer1.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP1_ROUTER, + DEFAULT_LOCAL_PREF, + DEFAULT_MULTI_EXIT_DISC, + asPathLong, + addedRoutes, + withdrawnRoutes); + peer1.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn1 = waitForBgpRibIn(bgpSession1, 3); + assertThat(bgpRibIn1, hasSize(3)); + bgpRoutes = waitForBgpRoutes(3); + assertThat(bgpRoutes, hasSize(3)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("30.0.0.0/16"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("40.0.0.0/24"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + DEFAULT_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + + // Close the channels and test there are no routes + peer1.peerChannelHandler.closeChannel(); + peer2.peerChannelHandler.closeChannel(); + peer3.peerChannelHandler.closeChannel(); + bgpRoutes = waitForBgpRoutes(0); + assertThat(bgpRoutes, hasSize(0)); + } + + /** + * Tests the BGP route preference. + */ + @Test + public void testBgpRoutePreference() throws InterruptedException { + ChannelBuffer message; + BgpRouteEntry bgpRouteEntry; + Collection<BgpRouteEntry> bgpRibIn1; + Collection<BgpRouteEntry> bgpRibIn2; + Collection<BgpRouteEntry> bgpRibIn3; + Collection<BgpRouteEntry> bgpRoutes; + Collection<Ip4Prefix> addedRoutes = new LinkedList<>(); + Collection<Ip4Prefix> withdrawnRoutes = new LinkedList<>(); + + // Initiate the connections + peer1.connect(connectToSocket); + peer2.connect(connectToSocket); + peer3.connect(connectToSocket); + + // + // Setup the initial set of routes to Peer1 + // + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + addedRoutes.add(Ip4Prefix.valueOf("30.0.0.0/16")); + // Write the routes + message = peer1.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP1_ROUTER, + DEFAULT_LOCAL_PREF, + DEFAULT_MULTI_EXIT_DISC, + asPathLong, + addedRoutes, + withdrawnRoutes); + peer1.peerChannelHandler.savedCtx.getChannel().write(message); + bgpRoutes = waitForBgpRoutes(2); + assertThat(bgpRoutes, hasSize(2)); + + // + // Add a route entry to Peer2 with a better LOCAL_PREF + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + // Write the routes + message = peer2.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP2_ROUTER, + BETTER_LOCAL_PREF, + DEFAULT_MULTI_EXIT_DISC, + asPathLong, + addedRoutes, + withdrawnRoutes); + peer2.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn2 = waitForBgpRibIn(bgpSession2, 1); + assertThat(bgpRibIn2, hasSize(1)); + bgpRoutes = waitForBgpRoutes(2); + assertThat(bgpRoutes, hasSize(2)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession2, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP2_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathLong, + BETTER_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn2, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + // + // Add a route entry to Peer3 with a shorter AS path + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + // Write the routes + message = peer3.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP3_ROUTER, + BETTER_LOCAL_PREF, + DEFAULT_MULTI_EXIT_DISC, + asPathShort, + addedRoutes, + withdrawnRoutes); + peer3.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn3 = waitForBgpRibIn(bgpSession3, 1); + assertThat(bgpRibIn3, hasSize(1)); + bgpRoutes = waitForBgpRoutes(2); + assertThat(bgpRoutes, hasSize(2)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession3, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP3_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathShort, + BETTER_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); + assertThat(bgpRibIn3, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + // + // Cleanup in preparation for next test: delete old route entry from + // Peer2 + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + withdrawnRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + // Write the routes + message = peer2.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP2_ROUTER, + BETTER_LOCAL_PREF, + BETTER_MULTI_EXIT_DISC, + asPathShort, + addedRoutes, + withdrawnRoutes); + peer2.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn2 = waitForBgpRibIn(bgpSession2, 0); + assertThat(bgpRibIn2, hasSize(0)); + + // + // Add a route entry to Peer2 with a better MED + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + // Write the routes + message = peer2.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP2_ROUTER, + BETTER_LOCAL_PREF, + BETTER_MULTI_EXIT_DISC, + asPathShort, + addedRoutes, + withdrawnRoutes); + peer2.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn2 = waitForBgpRibIn(bgpSession2, 1); + assertThat(bgpRibIn2, hasSize(1)); + bgpRoutes = waitForBgpRoutes(2); + assertThat(bgpRoutes, hasSize(2)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession2, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP2_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathShort, + BETTER_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(BETTER_MULTI_EXIT_DISC); + assertThat(bgpRibIn2, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + // + // Add a route entry to Peer1 with a better (lower) BGP ID + // + addedRoutes = new LinkedList<>(); + withdrawnRoutes = new LinkedList<>(); + addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8")); + withdrawnRoutes.add(Ip4Prefix.valueOf("30.0.0.0/16")); + // Write the routes + message = peer1.peerChannelHandler.prepareBgpUpdate( + NEXT_HOP1_ROUTER, + BETTER_LOCAL_PREF, + BETTER_MULTI_EXIT_DISC, + asPathShort, + addedRoutes, + withdrawnRoutes); + peer1.peerChannelHandler.savedCtx.getChannel().write(message); + // + // Check that the routes have been received, processed and stored + // + bgpRibIn1 = waitForBgpRibIn(bgpSession1, 1); + assertThat(bgpRibIn1, hasSize(1)); + bgpRoutes = waitForBgpRoutes(1); + assertThat(bgpRoutes, hasSize(1)); + // + bgpRouteEntry = + new BgpRouteEntry(bgpSession1, + Ip4Prefix.valueOf("20.0.0.0/8"), + NEXT_HOP1_ROUTER, + (byte) BgpConstants.Update.Origin.IGP, + asPathShort, + BETTER_LOCAL_PREF); + bgpRouteEntry.setMultiExitDisc(BETTER_MULTI_EXIT_DISC); + assertThat(bgpRibIn1, hasBgpRouteEntry(bgpRouteEntry)); + assertThat(waitForBgpRoute(bgpRouteEntry), notNullValue()); + + + // Close the channels and test there are no routes + peer1.peerChannelHandler.closeChannel(); + peer2.peerChannelHandler.closeChannel(); + peer3.peerChannelHandler.closeChannel(); + bgpRoutes = waitForBgpRoutes(0); + assertThat(bgpRoutes, hasSize(0)); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java new file mode 100644 index 00000000..516bf578 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/PathSegmentTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.ArrayList; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +/** + * 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(1L); + segmentAsNumbers.add(2L); + segmentAsNumbers.add(3L); + 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=AS_SEQUENCE, 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(1L); + segmentAsNumbers.add(2L); + segmentAsNumbers.add(3L); + + // 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(1L); + segmentAsNumbers.add(22L); // Different + segmentAsNumbers.add(3L); + // + BgpRouteEntry.PathSegment pathSegment2 = + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers); + + assertThat(pathSegment1, Matchers.is(not(pathSegment2))); + } + + /** + * Tests object string representation. + */ + @Test + public void testToString() { + BgpRouteEntry.PathSegment pathSegment = generatePathSegment(); + + String expectedString = + "PathSegment{type=AS_SEQUENCE, segmentAsNumbers=[1, 2, 3]}"; + assertThat(pathSegment.toString(), is(expectedString)); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java new file mode 100644 index 00000000..41485e95 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerChannelHandler.java @@ -0,0 +1,207 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +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.Ip4Address; +import org.onlab.packet.Ip4Prefix; + +import java.util.Collection; + +/** + * Class for handling the remote BGP Peer session. + */ +class TestBgpPeerChannelHandler extends SimpleChannelHandler { + static final long PEER_AS = 65001; + static final long PEER_AS4 = 0x12345678; + static final int PEER_HOLDTIME = 120; // 120 seconds + + final BgpSessionInfo localInfo = new BgpSessionInfo(); + ChannelHandlerContext savedCtx; + + /** + * Constructor for given BGP ID. + * + * @param bgpId the BGP ID to use + */ + TestBgpPeerChannelHandler(Ip4Address bgpId) { + this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION); + this.localInfo.setBgpId(bgpId); + this.localInfo.setAsNumber(PEER_AS); + this.localInfo.setHoldtime(PEER_HOLDTIME); + } + + /** + * 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 = BgpOpen.prepareBgpOpen(localInfo); + ctx.getChannel().write(message); + + // Prepare and transmit BGP KEEPALIVE message + message = BgpKeepalive.prepareBgpKeepalive(); + ctx.getChannel().write(message); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent channelEvent) { + // Nothing to do + } + + /** + * Prepares BGP UPDATE message. + * + * @param nextHopRouter the next-hop router address for the routes to add + * @param localPref the local preference for the routes to use + * @param multiExitDisc the MED value + * @param asPath the AS path 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(Ip4Address nextHopRouter, + long localPref, + long multiExitDisc, + BgpRouteEntry.AsPath asPath, + Collection<Ip4Prefix> addedRoutes, + Collection<Ip4Prefix> 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: asPath + attrFlags = 0x40; // Transitive flag + pathAttributes.writeByte(attrFlags); + pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE); + ChannelBuffer encodedAsPath = encodeAsPath(asPath); + pathAttributes.writeByte(encodedAsPath.readableBytes()); // Data length + pathAttributes.writeBytes(encodedAsPath); + // 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 BgpMessage.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<Ip4Prefix> prefixes) { + ChannelBuffer message = + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); + + // Write each of the prefixes + for (Ip4Prefix prefix : prefixes) { + int prefixBitlen = prefix.prefixLength(); + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up + message.writeByte(prefixBitlen); + + Ip4Address address = prefix.address(); + long value = address.toInt() & 0xffffffffL; + for (int i = 0; i < Ip4Address.BYTE_LENGTH; i++) { + if (prefixBytelen-- == 0) { + break; + } + long nextByte = + (value >> ((Ip4Address.BYTE_LENGTH - i - 1) * 8)) & 0xff; + message.writeByte((int) nextByte); + } + } + + return message; + } + + /** + * Encodes an AS path. + * + * @param asPath the AS path to encode + * @return the buffer with the encoded AS path + */ + private ChannelBuffer encodeAsPath(BgpRouteEntry.AsPath asPath) { + ChannelBuffer message = + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); + + for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) { + message.writeByte(pathSegment.getType()); + message.writeByte(pathSegment.getSegmentAsNumbers().size()); + for (Long asNumber : pathSegment.getSegmentAsNumbers()) { + message.writeShort(asNumber.intValue()); + } + } + + return message; + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java new file mode 100644 index 00000000..b4ff40d2 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/TestBgpPeerFrameDecoder.java @@ -0,0 +1,175 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.bgp; + +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.Ip4Address; + +import java.util.concurrent.CountDownLatch; + +/** + * Class for handling the decoding of the BGP messages at the remote + * BGP peer session. + */ +class TestBgpPeerFrameDecoder extends FrameDecoder { + final BgpSessionInfo remoteInfo = new BgpSessionInfo(); + + 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 + // + remoteInfo.setBgpVersion(message.readUnsignedByte()); + remoteInfo.setAsNumber(message.readUnsignedShort()); + remoteInfo.setHoldtime(message.readUnsignedShort()); + remoteInfo.setBgpId(Ip4Address.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(); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/config/impl/HostToInterfaceAdaptorTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/config/impl/HostToInterfaceAdaptorTest.java new file mode 100644 index 00000000..0347fc5f --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/config/impl/HostToInterfaceAdaptorTest.java @@ -0,0 +1,210 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.config.impl; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.host.HostService; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.host.PortAddresses; +import org.onosproject.routing.config.Interface; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for the HostToInterfaceAdaptor class. + */ +public class HostToInterfaceAdaptorTest { + + private HostService hostService; + private HostToInterfaceAdaptor adaptor; + + private Set<PortAddresses> portAddresses; + private Map<ConnectPoint, Interface> interfaces; + + private static final ConnectPoint CP1 = new ConnectPoint( + DeviceId.deviceId("of:1"), PortNumber.portNumber(1)); + private static final ConnectPoint CP2 = new ConnectPoint( + DeviceId.deviceId("of:1"), PortNumber.portNumber(2)); + private static final ConnectPoint CP3 = new ConnectPoint( + DeviceId.deviceId("of:2"), PortNumber.portNumber(1)); + + private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint( + DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1)); + + @Before + public void setUp() throws Exception { + hostService = createMock(HostService.class); + + portAddresses = Sets.newHashSet(); + interfaces = Maps.newHashMap(); + + InterfaceIpAddress ia11 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.1.1"), + IpPrefix.valueOf("192.168.1.0/24")); + createPortAddressesAndInterface(CP1, + Sets.newHashSet(ia11), + MacAddress.valueOf("00:00:00:00:00:01"), + VlanId.NONE); + + // Two addresses in the same subnet + InterfaceIpAddress ia21 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.2.1"), + IpPrefix.valueOf("192.168.2.0/24")); + InterfaceIpAddress ia22 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.2.2"), + IpPrefix.valueOf("192.168.2.0/24")); + createPortAddressesAndInterface(CP2, + Sets.newHashSet(ia21, ia22), + MacAddress.valueOf("00:00:00:00:00:02"), + VlanId.vlanId((short) 4)); + + // Two addresses in different subnets + InterfaceIpAddress ia31 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.3.1"), + IpPrefix.valueOf("192.168.3.0/24")); + InterfaceIpAddress ia41 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.4.1"), + IpPrefix.valueOf("192.168.4.0/24")); + createPortAddressesAndInterface(CP3, + Sets.newHashSet(ia31, ia41), + MacAddress.valueOf("00:00:00:00:00:03"), + VlanId.NONE); + + expect(hostService.getAddressBindings()).andReturn(portAddresses).anyTimes(); + + replay(hostService); + + adaptor = new HostToInterfaceAdaptor(hostService); + } + + /** + * Creates both a PortAddresses and an Interface for the given inputs and + * places them in the correct global data stores. + * + * @param cp the connect point + * @param ipAddresses the set of interface IP addresses + * @param mac the MAC address + * @param vlan the VLAN ID + */ + private void createPortAddressesAndInterface( + ConnectPoint cp, Set<InterfaceIpAddress> ipAddresses, + MacAddress mac, VlanId vlan) { + PortAddresses pa = new PortAddresses(cp, ipAddresses, mac, vlan); + portAddresses.add(pa); + expect(hostService.getAddressBindingsForPort(cp)).andReturn( + Collections.singleton(pa)).anyTimes(); + + Interface intf = new Interface(cp, ipAddresses, mac, vlan); + interfaces.put(cp, intf); + } + + /** + * Tests {@link HostToInterfaceAdaptor#getInterfaces()}. + * Verifies that the set of interfaces returned matches what is expected + * based on the input PortAddresses data. + */ + @Test + public void testGetInterfaces() { + Set<Interface> adaptorIntfs = adaptor.getInterfaces(); + + assertEquals(3, adaptorIntfs.size()); + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP1))); + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP2))); + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP3))); + } + + /** + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)}. + * Verifies that the correct interface is returned for a given connect + * point. + */ + @Test + public void testGetInterface() { + assertEquals(this.interfaces.get(CP1), adaptor.getInterface(CP1)); + assertEquals(this.interfaces.get(CP2), adaptor.getInterface(CP2)); + assertEquals(this.interfaces.get(CP3), adaptor.getInterface(CP3)); + + // Try and get an interface for a connect point with no addresses + reset(hostService); + expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP)) + .andReturn(Collections.<PortAddresses>emptySet()).anyTimes(); + replay(hostService); + + assertNull(adaptor.getInterface(NON_EXISTENT_CP)); + } + + /** + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)} in the + * case that the input connect point is null. + * Verifies that a NullPointerException is thrown. + */ + @Test(expected = NullPointerException.class) + public void testGetInterfaceNull() { + ConnectPoint c = null; + adaptor.getInterface(c); + } + + /** + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)}. + * Verifies that the correct interface is returned based on the given IP + * address. + */ + @Test + public void testGetMatchingInterface() { + assertEquals(this.interfaces.get(CP1), + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.1.100"))); + assertEquals(this.interfaces.get(CP2), + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.2.100"))); + assertEquals(this.interfaces.get(CP3), + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.3.100"))); + assertEquals(this.interfaces.get(CP3), + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.4.100"))); + + // Try and match an address we don't have subnet configured for + assertNull(adaptor.getMatchingInterface(IpAddress.valueOf("1.1.1.1"))); + } + + /** + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)} in the + * case that the input IP address is null. + * Verifies that a NullPointerException is thrown. + */ + @Test(expected = NullPointerException.class) + public void testGetMatchingInterfaceNull() { + adaptor.getMatchingInterface(null); + } + +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java new file mode 100644 index 00000000..100c13d4 --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterAsyncArpTest.java @@ -0,0 +1,232 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.impl; + +import com.google.common.collect.Sets; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultHost; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.PortNumber; +import org.onosproject.net.host.HostEvent; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.routing.config.RoutingConfigurationService; +import org.onosproject.routing.impl.Router.InternalHostListener; +import org.onosproject.routing.BgpService; +import org.onosproject.routing.FibEntry; +import org.onosproject.routing.FibListener; +import org.onosproject.routing.FibUpdate; +import org.onosproject.routing.RouteEntry; +import org.onosproject.routing.RouteListener; +import org.onosproject.routing.RouteUpdate; + +import java.util.Collections; + +import static org.easymock.EasyMock.*; + +/** +* This class tests adding a route and updating a route. +* The HostService module answers the MAC address asynchronously. +*/ +public class RouterAsyncArpTest { + + private HostService hostService; + private FibListener fibListener; + private RoutingConfigurationService routingConfigurationService; + + private static final ConnectPoint SW1_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000001"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW2_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000002"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW3_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000003"), + PortNumber.portNumber(1)); + + private Router router; + private InternalHostListener internalHostListener; + + @Before + public void setUp() throws Exception { + hostService = createMock(HostService.class); + routingConfigurationService = + createMock(RoutingConfigurationService.class); + + BgpService bgpService = createMock(BgpService.class); + bgpService.start(anyObject(RouteListener.class)); + bgpService.stop(); + replay(bgpService); + + fibListener = createMock(FibListener.class); + + router = new Router(); + router.coreService = createNiceMock(CoreService.class); + router.hostService = hostService; + router.routingConfigurationService = routingConfigurationService; + router.bgpService = bgpService; + router.activate(); + + router.addFibListener(fibListener); + router.start(); + + internalHostListener = router.new InternalHostListener(); + } + + @After + public void tearDown() { + // Called during shutdown + reset(hostService); + hostService.removeListener(anyObject(HostListener.class)); + + router.stop(); + } + + /** + * Tests adding a route entry with asynchronous HostService replies. + */ + @Test + public void testRouteAdd() { + // Construct a route entry + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1"); + + RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp); + + // Host service will reply with no hosts when asked + reset(hostService); + expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn( + Collections.emptySet()).anyTimes(); + hostService.startMonitoringIp(IpAddress.valueOf("192.168.10.1")); + replay(hostService); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(false); + replay(routingConfigurationService); + + // Initially when we add the route, no FIB update will be sent + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry))); + + verify(fibListener); + + + // Now when we send the event, we expect the FIB update to be sent + reset(fibListener); + FibEntry fibEntry = new FibEntry(prefix, nextHopIp, + MacAddress.valueOf("00:00:00:00:00:01")); + + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList()); + replay(fibListener); + + Host host = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE, + new HostLocation( + SW1_ETH1.deviceId(), + SW1_ETH1.port(), 1), + Sets.newHashSet(IpAddress.valueOf("192.168.10.1"))); + + // Send in the host event + internalHostListener.event( + new HostEvent(HostEvent.Type.HOST_ADDED, host)); + + verify(fibListener); + } + + /** + * Tests updating a route entry with asynchronous HostService replies. + */ + @Test + public void testRouteUpdate() { + // Add a route + testRouteAdd(); + + // Construct a route entry + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + IpAddress nextHopIp = Ip4Address.valueOf("192.168.20.1"); + + RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp); + + // Host service will reply with no hosts when asked + reset(hostService); + expect(hostService.getHostsByIp(anyObject(IpAddress.class))).andReturn( + Collections.emptySet()).anyTimes(); + hostService.startMonitoringIp(IpAddress.valueOf("192.168.20.1")); + replay(hostService); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(false); + replay(routingConfigurationService); + + // Initially when we add the route, the DELETE FIB update will be sent + // but the UPDATE FIB update will come later when the MAC is resolved + reset(fibListener); + + fibListener.update(Collections.emptyList(), Collections.singletonList(new FibUpdate( + FibUpdate.Type.DELETE, new FibEntry(prefix, null, null)))); + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry))); + + verify(fibListener); + + + // Now when we send the event, we expect the FIB update to be sent + reset(fibListener); + FibEntry fibEntry = new FibEntry(prefix, nextHopIp, + MacAddress.valueOf("00:00:00:00:00:02")); + + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList()); + replay(fibListener); + + Host host = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE, + new HostLocation( + SW1_ETH1.deviceId(), + SW1_ETH1.port(), 1), + Sets.newHashSet(IpAddress.valueOf("192.168.20.1"))); + + // Send in the host event + internalHostListener.event( + new HostEvent(HostEvent.Type.HOST_ADDED, host)); + + verify(fibListener); + } +} diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java new file mode 100644 index 00000000..45bc309f --- /dev/null +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java @@ -0,0 +1,287 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing.impl; + +import com.google.common.collect.Sets; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultHost; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.PortNumber; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.routing.BgpService; +import org.onosproject.routing.FibEntry; +import org.onosproject.routing.FibListener; +import org.onosproject.routing.FibUpdate; +import org.onosproject.routing.RouteEntry; +import org.onosproject.routing.RouteListener; +import org.onosproject.routing.RouteUpdate; +import org.onosproject.routing.config.RoutingConfigurationService; + +import java.util.Collections; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * This class tests adding a route, updating a route, deleting a route, + * and adding a route whose next hop is the local BGP speaker. + * <p/> + * The HostService answers requests synchronously. + */ +public class RouterTest { + + private HostService hostService; + private RoutingConfigurationService routingConfigurationService; + + private FibListener fibListener; + + private static final ConnectPoint SW1_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000001"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW2_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000002"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW3_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000003"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW4_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000004"), + PortNumber.portNumber(1)); + + private Router router; + + @Before + public void setUp() throws Exception { + setUpHostService(); + routingConfigurationService = + createMock(RoutingConfigurationService.class); + + BgpService bgpService = createMock(BgpService.class); + bgpService.start(anyObject(RouteListener.class)); + bgpService.stop(); + replay(bgpService); + + fibListener = createMock(FibListener.class); + + router = new Router(); + router.coreService = createNiceMock(CoreService.class); + router.hostService = hostService; + router.routingConfigurationService = routingConfigurationService; + router.bgpService = bgpService; + router.activate(); + + router.addFibListener(fibListener); + router.start(); + } + + @After + public void tearDown() { + router.stop(); + } + + /** + * Sets up the host service with details of some hosts. + */ + private void setUpHostService() { + hostService = createMock(HostService.class); + + hostService.addListener(anyObject(HostListener.class)); + expectLastCall().anyTimes(); + + IpAddress host1Address = IpAddress.valueOf("192.168.10.1"); + Host host1 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE, + new HostLocation(SW1_ETH1, 1), + Sets.newHashSet(host1Address)); + + expect(hostService.getHostsByIp(host1Address)) + .andReturn(Sets.newHashSet(host1)).anyTimes(); + hostService.startMonitoringIp(host1Address); + expectLastCall().anyTimes(); + + + IpAddress host2Address = IpAddress.valueOf("192.168.20.1"); + Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE, + new HostLocation(SW2_ETH1, 1), + Sets.newHashSet(host2Address)); + + expect(hostService.getHostsByIp(host2Address)) + .andReturn(Sets.newHashSet(host2)).anyTimes(); + hostService.startMonitoringIp(host2Address); + expectLastCall().anyTimes(); + + // Next hop on a VLAN + IpAddress host3Address = IpAddress.valueOf("192.168.40.1"); + Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1), + new HostLocation(SW4_ETH1, 1), + Sets.newHashSet(host3Address)); + + expect(hostService.getHostsByIp(host3Address)) + .andReturn(Sets.newHashSet(host3)).anyTimes(); + hostService.startMonitoringIp(host3Address); + expectLastCall().anyTimes(); + + // Called during shutdown + hostService.removeListener(anyObject(HostListener.class)); + + replay(hostService); + } + + /** + * Tests adding a route entry. + */ + @Test + public void testRouteAdd() { + // Construct a route entry + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1"); + + RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp); + + // Expected FIB entry + FibEntry fibEntry = new FibEntry(prefix, nextHopIp, + MacAddress.valueOf("00:00:00:00:00:01")); + + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList()); + + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry))); + + verify(fibListener); + } + + /** + * Tests updating a route entry. + */ + @Test + public void testRouteUpdate() { + // Firstly add a route + testRouteAdd(); + + // Route entry with updated next hop for the original prefix + RouteEntry routeEntryUpdate = new RouteEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), + Ip4Address.valueOf("192.168.20.1")); + + // The old FIB entry will be withdrawn + FibEntry withdrawFibEntry = new FibEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), null, null); + + // A new FIB entry will be added + FibEntry updateFibEntry = new FibEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), + Ip4Address.valueOf("192.168.20.1"), + MacAddress.valueOf("00:00:00:00:00:02")); + + reset(fibListener); + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, updateFibEntry)), + Collections.singletonList(new FibUpdate( + FibUpdate.Type.DELETE, withdrawFibEntry))); + replay(fibListener); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(false); + replay(routingConfigurationService); + + router.processRouteUpdates(Collections.singletonList(new RouteUpdate( + RouteUpdate.Type.UPDATE, routeEntryUpdate))); + + verify(fibListener); + } + + /** + * Tests deleting a route entry. + */ + @Test + public void testRouteDelete() { + // Firstly add a route + testRouteAdd(); + + RouteEntry deleteRouteEntry = new RouteEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), + Ip4Address.valueOf("192.168.10.1")); + + FibEntry deleteFibEntry = new FibEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), null, null); + + reset(fibListener); + fibListener.update(Collections.emptyList(), Collections.singletonList( + new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry))); + + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry))); + + verify(fibListener); + } + + /** + * Tests adding a route whose next hop is the local BGP speaker. + */ + @Test + public void testLocalRouteAdd() { + // Construct a route entry, the next hop is the local BGP speaker + RouteEntry routeEntry = new RouteEntry( + Ip4Prefix.valueOf("1.1.1.0/24"), + Ip4Address.valueOf("0.0.0.0")); + + // No methods on the FIB listener should be called + replay(fibListener); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(true); + replay(routingConfigurationService); + + // Call the processRouteUpdates() method in Router class + RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE, + routeEntry); + router.processRouteUpdates(Collections.singletonList(routeUpdate)); + + // Verify + assertEquals(1, router.getRoutes4().size()); + assertTrue(router.getRoutes4().contains(routeEntry)); + verify(fibListener); + } +} |