diff options
Diffstat (limited to 'framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java')
-rw-r--r-- | framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java | 895 |
1 files changed, 895 insertions, 0 deletions
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)); + } +} |