| /* |
| * Copyright 2014 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.onlab.onos.sdnip.bgp; |
| |
| import static org.hamcrest.Matchers.hasItem; |
| import static org.hamcrest.Matchers.hasSize; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.notNullValue; |
| import static org.junit.Assert.assertThat; |
| |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.SocketAddress; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.LinkedList; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.jboss.netty.bootstrap.ClientBootstrap; |
| import org.jboss.netty.buffer.ChannelBuffer; |
| import org.jboss.netty.channel.Channel; |
| import org.jboss.netty.channel.ChannelFactory; |
| import org.jboss.netty.channel.ChannelPipeline; |
| import org.jboss.netty.channel.ChannelPipelineFactory; |
| import org.jboss.netty.channel.Channels; |
| import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.onlab.junit.TestUtils; |
| import org.onlab.junit.TestUtils.TestUtilsException; |
| import org.onlab.onos.sdnip.RouteListener; |
| import org.onlab.onos.sdnip.RouteUpdate; |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.packet.Ip4Prefix; |
| |
| import com.google.common.net.InetAddresses; |
| |
| /** |
| * 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 long DEFAULT_LOCAL_PREF = 10; |
| private static final long DEFAULT_MULTI_EXIT_DISC = 20; |
| |
| // The BGP Session Manager to test |
| private BgpSessionManager bgpSessionManager; |
| |
| // Remote Peer state |
| private ClientBootstrap peerBootstrap; |
| private TestBgpPeerChannelHandler peerChannelHandler = |
| new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF); |
| private TestBgpPeerFrameDecoder peerFrameDecoder = |
| new TestBgpPeerFrameDecoder(); |
| |
| // The socket that the Remote Peer should connect to |
| private InetSocketAddress connectToSocket; |
| |
| private final DummyRouteListener dummyRouteListener = |
| new DummyRouteListener(); |
| |
| /** |
| * Dummy implementation for the RouteListener interface. |
| */ |
| private class DummyRouteListener implements RouteListener { |
| @Override |
| public void update(RouteUpdate routeUpdate) { |
| // Nothing to do |
| } |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| // |
| // Setup the BGP Session Manager to test, and start listening for BGP |
| // connections. |
| // |
| bgpSessionManager = new BgpSessionManager(dummyRouteListener); |
| // NOTE: We use port 0 to bind on any available port |
| bgpSessionManager.startUp(0); |
| |
| // Get the port number the BGP Session Manager is listening on |
| Channel serverChannel = TestUtils.getField(bgpSessionManager, |
| "serverChannel"); |
| SocketAddress socketAddress = serverChannel.getLocalAddress(); |
| InetSocketAddress inetSocketAddress = |
| (InetSocketAddress) socketAddress; |
| |
| // |
| // Setup the BGP Peer, i.e., the "remote" BGP router that will |
| // initiate the BGP connection, send BGP UPDATE messages, etc. |
| // |
| ChannelFactory channelFactory = |
| new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), |
| Executors.newCachedThreadPool()); |
| ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() { |
| @Override |
| public ChannelPipeline getPipeline() throws Exception { |
| // Setup the transmitting pipeline |
| ChannelPipeline pipeline = Channels.pipeline(); |
| pipeline.addLast("TestBgpPeerFrameDecoder", |
| peerFrameDecoder); |
| pipeline.addLast("TestBgpPeerChannelHandler", |
| peerChannelHandler); |
| return pipeline; |
| } |
| }; |
| |
| peerBootstrap = new ClientBootstrap(channelFactory); |
| peerBootstrap.setOption("child.keepAlive", true); |
| peerBootstrap.setOption("child.tcpNoDelay", true); |
| peerBootstrap.setPipelineFactory(pipelineFactory); |
| |
| InetAddress connectToAddress = InetAddresses.forString("127.0.0.1"); |
| connectToSocket = new InetSocketAddress(connectToAddress, |
| inetSocketAddress.getPort()); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| bgpSessionManager.shutDown(); |
| bgpSessionManager = null; |
| } |
| |
| /** |
| * Gets BGP RIB-IN routes by waiting until they are received. |
| * <p/> |
| * NOTE: We keep checking once a second the number of received routes, |
| * up to 5 seconds. |
| * |
| * @param bgpSession the BGP session that is expected to receive the |
| * routes |
| * @param expectedRoutes the expected number of routes |
| * @return the BGP RIB-IN routes as received within the expected |
| * time interval |
| */ |
| private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession, |
| long expectedRoutes) |
| throws InterruptedException { |
| Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn(); |
| |
| final int maxChecks = 5; // Max wait of 5 seconds |
| for (int i = 0; i < maxChecks; i++) { |
| if (bgpRibIn.size() == expectedRoutes) { |
| break; |
| } |
| Thread.sleep(1000); |
| bgpRibIn = bgpSession.getBgpRibIn(); |
| } |
| |
| return bgpRibIn; |
| } |
| |
| /** |
| * Gets BGP merged routes by waiting until they are received. |
| * <p/> |
| * NOTE: We keep checking once a second the number of received routes, |
| * up to 5 seconds. |
| * |
| * @param expectedRoutes the expected number of routes |
| * @return the BGP Session Manager routes as received within the expected |
| * time interval |
| */ |
| private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes) |
| throws InterruptedException { |
| Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes(); |
| |
| final int maxChecks = 5; // Max wait of 5 seconds |
| for (int i = 0; i < maxChecks; i++) { |
| if (bgpRoutes.size() == expectedRoutes) { |
| break; |
| } |
| Thread.sleep(1000); |
| bgpRoutes = bgpSessionManager.getBgpRoutes(); |
| } |
| |
| return bgpRoutes; |
| } |
| |
| /** |
| * Tests that the BGP OPEN messages have been exchanged, followed by |
| * KEEPALIVE. |
| * <p> |
| * The BGP Peer opens the sessions and transmits OPEN Message, eventually |
| * followed by KEEPALIVE. The tested BGP listener should respond by |
| * OPEN Message, followed by KEEPALIVE. |
| * |
| * @throws TestUtilsException TestUtils error |
| */ |
| @Test |
| public void testExchangedBgpOpenMessages() |
| throws InterruptedException, TestUtilsException { |
| // Initiate the connection |
| peerBootstrap.connect(connectToSocket); |
| |
| // Wait until the OPEN message is received |
| peerFrameDecoder.receivedOpenMessageLatch.await(2000, |
| TimeUnit.MILLISECONDS); |
| // Wait until the KEEPALIVE message is received |
| peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000, |
| TimeUnit.MILLISECONDS); |
| |
| // |
| // Test the fields from the BGP OPEN message: |
| // BGP version, AS number, BGP ID |
| // |
| assertThat(peerFrameDecoder.remoteBgpVersion, |
| is(BgpConstants.BGP_VERSION)); |
| assertThat(peerFrameDecoder.remoteAs, |
| is(TestBgpPeerChannelHandler.PEER_AS)); |
| assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID)); |
| |
| // |
| // Test that a BgpSession instance has been created |
| // |
| assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID)); |
| assertThat(bgpSessionManager.getBgpSessions(), hasSize(1)); |
| BgpSession bgpSession = |
| bgpSessionManager.getBgpSessions().iterator().next(); |
| assertThat(bgpSession, notNullValue()); |
| long sessionAs = TestUtils.getField(bgpSession, "localAs"); |
| assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS)); |
| } |
| |
| /** |
| * Tests that the BGP UPDATE messages have been received and processed. |
| */ |
| @Test |
| public void testProcessedBgpUpdateMessages() throws InterruptedException { |
| BgpSession bgpSession; |
| Ip4Address nextHopRouter; |
| BgpRouteEntry bgpRouteEntry; |
| ChannelBuffer message; |
| Collection<BgpRouteEntry> bgpRibIn; |
| Collection<BgpRouteEntry> bgpRoutes; |
| |
| // Initiate the connection |
| peerBootstrap.connect(connectToSocket); |
| |
| // Wait until the OPEN message is received |
| peerFrameDecoder.receivedOpenMessageLatch.await(2000, |
| TimeUnit.MILLISECONDS); |
| // Wait until the KEEPALIVE message is received |
| peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000, |
| TimeUnit.MILLISECONDS); |
| |
| // Get the BGP Session handler |
| bgpSession = bgpSessionManager.getBgpSessions().iterator().next(); |
| |
| // Prepare routes to add/delete |
| nextHopRouter = Ip4Address.valueOf("10.20.30.40"); |
| Collection<Ip4Prefix> addedRoutes = new LinkedList<>(); |
| Collection<Ip4Prefix> withdrawnRoutes = new LinkedList<>(); |
| 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 = peerChannelHandler.prepareBgpUpdate(nextHopRouter, |
| addedRoutes, |
| withdrawnRoutes); |
| peerChannelHandler.savedCtx.getChannel().write(message); |
| |
| // Check that the routes have been received, processed and stored |
| bgpRibIn = waitForBgpRibIn(bgpSession, 5); |
| assertThat(bgpRibIn, hasSize(5)); |
| bgpRoutes = waitForBgpRoutes(5); |
| assertThat(bgpRoutes, hasSize(5)); |
| |
| // Setup the AS Path |
| ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); |
| byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE; |
| ArrayList<Long> segmentAsNumbers1 = new ArrayList<>(); |
| segmentAsNumbers1.add((long) 65010); |
| segmentAsNumbers1.add((long) 65020); |
| segmentAsNumbers1.add((long) 65030); |
| BgpRouteEntry.PathSegment pathSegment1 = |
| new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1); |
| pathSegments.add(pathSegment1); |
| // |
| byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET; |
| ArrayList<Long> segmentAsNumbers2 = new ArrayList<>(); |
| segmentAsNumbers2.add((long) 65041); |
| segmentAsNumbers2.add((long) 65042); |
| segmentAsNumbers2.add((long) 65043); |
| BgpRouteEntry.PathSegment pathSegment2 = |
| new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2); |
| pathSegments.add(pathSegment2); |
| // |
| BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments); |
| |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("0.0.0.0/0"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("20.0.0.0/8"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("30.0.0.0/16"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("40.0.0.0/24"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("50.0.0.0/32"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| |
| // Delete some routes |
| addedRoutes = new LinkedList<>(); |
| withdrawnRoutes = new LinkedList<>(); |
| withdrawnRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0")); |
| withdrawnRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32")); |
| |
| // Write the routes |
| message = peerChannelHandler.prepareBgpUpdate(nextHopRouter, |
| addedRoutes, |
| withdrawnRoutes); |
| peerChannelHandler.savedCtx.getChannel().write(message); |
| |
| // Check that the routes have been received, processed and stored |
| bgpRibIn = waitForBgpRibIn(bgpSession, 3); |
| assertThat(bgpRibIn, hasSize(3)); |
| bgpRoutes = waitForBgpRoutes(3); |
| assertThat(bgpRoutes, hasSize(3)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("20.0.0.0/8"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("30.0.0.0/16"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| // |
| bgpRouteEntry = |
| new BgpRouteEntry(bgpSession, |
| Ip4Prefix.valueOf("40.0.0.0/24"), |
| nextHopRouter, |
| (byte) BgpConstants.Update.Origin.IGP, |
| asPath, |
| DEFAULT_LOCAL_PREF); |
| bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC); |
| assertThat(bgpRibIn, hasItem(bgpRouteEntry)); |
| |
| // Close the channel and test there are no routes |
| peerChannelHandler.closeChannel(); |
| bgpRoutes = waitForBgpRoutes(0); |
| assertThat(bgpRoutes, hasSize(0)); |
| } |
| } |