blob: d9b3774be61a2d7cfe0a203ad0e5bf6fef8ba120 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Jonathan Hart20d8e512014-10-16 11:05:52 -070016package org.onlab.onos.sdnip.bgp;
17
18import static org.hamcrest.Matchers.hasItem;
19import static org.hamcrest.Matchers.hasSize;
20import static org.hamcrest.Matchers.is;
21import static org.hamcrest.Matchers.notNullValue;
22import static org.junit.Assert.assertThat;
23
24import java.net.InetAddress;
25import java.net.InetSocketAddress;
26import java.net.SocketAddress;
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.LinkedList;
30import java.util.concurrent.Executors;
31import java.util.concurrent.TimeUnit;
32
33import org.jboss.netty.bootstrap.ClientBootstrap;
34import org.jboss.netty.buffer.ChannelBuffer;
35import org.jboss.netty.channel.Channel;
36import org.jboss.netty.channel.ChannelFactory;
37import org.jboss.netty.channel.ChannelPipeline;
38import org.jboss.netty.channel.ChannelPipelineFactory;
39import org.jboss.netty.channel.Channels;
40import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
41import org.junit.After;
42import org.junit.Before;
43import org.junit.Test;
Pavlin Radoslavovd26f57a2014-10-23 17:19:45 -070044import org.onlab.junit.TestUtils;
45import org.onlab.junit.TestUtils.TestUtilsException;
Jonathan Hart20d8e512014-10-16 11:05:52 -070046import org.onlab.onos.sdnip.RouteListener;
47import org.onlab.onos.sdnip.RouteUpdate;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080048import org.onlab.packet.Ip4Address;
49import org.onlab.packet.Ip4Prefix;
Jonathan Hart20d8e512014-10-16 11:05:52 -070050
51import com.google.common.net.InetAddresses;
52
53/**
54 * Unit tests for the BgpSessionManager class.
55 */
56public class BgpSessionManagerTest {
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080057 private static final Ip4Address IP_LOOPBACK_ID =
58 Ip4Address.valueOf("127.0.0.1");
59 private static final Ip4Address BGP_PEER1_ID =
60 Ip4Address.valueOf("10.0.0.1");
Jonathan Hart20d8e512014-10-16 11:05:52 -070061 private static final long DEFAULT_LOCAL_PREF = 10;
62 private static final long DEFAULT_MULTI_EXIT_DISC = 20;
63
Pavlin Radoslavov23c05692014-12-02 13:18:10 -080064 // Timeout waiting for a message to be received
65 private static final int MESSAGE_TIMEOUT_MS = 5000; // 5s
66
Jonathan Hart20d8e512014-10-16 11:05:52 -070067 // The BGP Session Manager to test
68 private BgpSessionManager bgpSessionManager;
69
70 // Remote Peer state
71 private ClientBootstrap peerBootstrap;
72 private TestBgpPeerChannelHandler peerChannelHandler =
73 new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF);
74 private TestBgpPeerFrameDecoder peerFrameDecoder =
75 new TestBgpPeerFrameDecoder();
76
77 // The socket that the Remote Peer should connect to
78 private InetSocketAddress connectToSocket;
79
80 private final DummyRouteListener dummyRouteListener =
81 new DummyRouteListener();
82
83 /**
84 * Dummy implementation for the RouteListener interface.
85 */
86 private class DummyRouteListener implements RouteListener {
87 @Override
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -080088 public void update(Collection<RouteUpdate> routeUpdate) {
Jonathan Hart20d8e512014-10-16 11:05:52 -070089 // Nothing to do
90 }
91 }
92
93 @Before
94 public void setUp() throws Exception {
95 //
96 // Setup the BGP Session Manager to test, and start listening for BGP
97 // connections.
98 //
99 bgpSessionManager = new BgpSessionManager(dummyRouteListener);
100 // NOTE: We use port 0 to bind on any available port
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800101 bgpSessionManager.start(0);
Jonathan Hart20d8e512014-10-16 11:05:52 -0700102
103 // Get the port number the BGP Session Manager is listening on
104 Channel serverChannel = TestUtils.getField(bgpSessionManager,
105 "serverChannel");
106 SocketAddress socketAddress = serverChannel.getLocalAddress();
107 InetSocketAddress inetSocketAddress =
108 (InetSocketAddress) socketAddress;
109
110 //
111 // Setup the BGP Peer, i.e., the "remote" BGP router that will
112 // initiate the BGP connection, send BGP UPDATE messages, etc.
113 //
114 ChannelFactory channelFactory =
115 new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
116 Executors.newCachedThreadPool());
117 ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
118 @Override
119 public ChannelPipeline getPipeline() throws Exception {
120 // Setup the transmitting pipeline
121 ChannelPipeline pipeline = Channels.pipeline();
122 pipeline.addLast("TestBgpPeerFrameDecoder",
123 peerFrameDecoder);
124 pipeline.addLast("TestBgpPeerChannelHandler",
125 peerChannelHandler);
126 return pipeline;
127 }
128 };
129
130 peerBootstrap = new ClientBootstrap(channelFactory);
131 peerBootstrap.setOption("child.keepAlive", true);
132 peerBootstrap.setOption("child.tcpNoDelay", true);
133 peerBootstrap.setPipelineFactory(pipelineFactory);
134
135 InetAddress connectToAddress = InetAddresses.forString("127.0.0.1");
136 connectToSocket = new InetSocketAddress(connectToAddress,
137 inetSocketAddress.getPort());
138 }
139
140 @After
141 public void tearDown() throws Exception {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800142 bgpSessionManager.stop();
Jonathan Hart20d8e512014-10-16 11:05:52 -0700143 bgpSessionManager = null;
144 }
145
146 /**
147 * Gets BGP RIB-IN routes by waiting until they are received.
148 * <p/>
149 * NOTE: We keep checking once a second the number of received routes,
150 * up to 5 seconds.
151 *
152 * @param bgpSession the BGP session that is expected to receive the
153 * routes
154 * @param expectedRoutes the expected number of routes
155 * @return the BGP RIB-IN routes as received within the expected
156 * time interval
157 */
158 private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
159 long expectedRoutes)
160 throws InterruptedException {
161 Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
162
163 final int maxChecks = 5; // Max wait of 5 seconds
164 for (int i = 0; i < maxChecks; i++) {
165 if (bgpRibIn.size() == expectedRoutes) {
166 break;
167 }
168 Thread.sleep(1000);
169 bgpRibIn = bgpSession.getBgpRibIn();
170 }
171
172 return bgpRibIn;
173 }
174
175 /**
176 * Gets BGP merged routes by waiting until they are received.
177 * <p/>
178 * NOTE: We keep checking once a second the number of received routes,
179 * up to 5 seconds.
180 *
181 * @param expectedRoutes the expected number of routes
182 * @return the BGP Session Manager routes as received within the expected
183 * time interval
184 */
185 private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
186 throws InterruptedException {
187 Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
188
189 final int maxChecks = 5; // Max wait of 5 seconds
190 for (int i = 0; i < maxChecks; i++) {
191 if (bgpRoutes.size() == expectedRoutes) {
192 break;
193 }
194 Thread.sleep(1000);
195 bgpRoutes = bgpSessionManager.getBgpRoutes();
196 }
197
198 return bgpRoutes;
199 }
200
201 /**
202 * Tests that the BGP OPEN messages have been exchanged, followed by
203 * KEEPALIVE.
204 * <p>
205 * The BGP Peer opens the sessions and transmits OPEN Message, eventually
206 * followed by KEEPALIVE. The tested BGP listener should respond by
207 * OPEN Message, followed by KEEPALIVE.
208 *
209 * @throws TestUtilsException TestUtils error
210 */
211 @Test
212 public void testExchangedBgpOpenMessages()
213 throws InterruptedException, TestUtilsException {
214 // Initiate the connection
215 peerBootstrap.connect(connectToSocket);
216
217 // Wait until the OPEN message is received
Pavlin Radoslavov23c05692014-12-02 13:18:10 -0800218 peerFrameDecoder.receivedOpenMessageLatch.await(MESSAGE_TIMEOUT_MS,
Jonathan Hart20d8e512014-10-16 11:05:52 -0700219 TimeUnit.MILLISECONDS);
220 // Wait until the KEEPALIVE message is received
Pavlin Radoslavov23c05692014-12-02 13:18:10 -0800221 peerFrameDecoder.receivedKeepaliveMessageLatch.await(MESSAGE_TIMEOUT_MS,
Jonathan Hart20d8e512014-10-16 11:05:52 -0700222 TimeUnit.MILLISECONDS);
223
224 //
225 // Test the fields from the BGP OPEN message:
226 // BGP version, AS number, BGP ID
227 //
228 assertThat(peerFrameDecoder.remoteBgpVersion,
229 is(BgpConstants.BGP_VERSION));
230 assertThat(peerFrameDecoder.remoteAs,
231 is(TestBgpPeerChannelHandler.PEER_AS));
232 assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID));
233
234 //
235 // Test that a BgpSession instance has been created
236 //
237 assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
238 assertThat(bgpSessionManager.getBgpSessions(), hasSize(1));
239 BgpSession bgpSession =
240 bgpSessionManager.getBgpSessions().iterator().next();
241 assertThat(bgpSession, notNullValue());
242 long sessionAs = TestUtils.getField(bgpSession, "localAs");
243 assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS));
244 }
245
246 /**
247 * Tests that the BGP UPDATE messages have been received and processed.
248 */
249 @Test
250 public void testProcessedBgpUpdateMessages() throws InterruptedException {
251 BgpSession bgpSession;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800252 Ip4Address nextHopRouter;
Jonathan Hart20d8e512014-10-16 11:05:52 -0700253 BgpRouteEntry bgpRouteEntry;
254 ChannelBuffer message;
255 Collection<BgpRouteEntry> bgpRibIn;
256 Collection<BgpRouteEntry> bgpRoutes;
257
258 // Initiate the connection
259 peerBootstrap.connect(connectToSocket);
260
261 // Wait until the OPEN message is received
Pavlin Radoslavov23c05692014-12-02 13:18:10 -0800262 peerFrameDecoder.receivedOpenMessageLatch.await(MESSAGE_TIMEOUT_MS,
Jonathan Hart20d8e512014-10-16 11:05:52 -0700263 TimeUnit.MILLISECONDS);
264 // Wait until the KEEPALIVE message is received
Pavlin Radoslavov23c05692014-12-02 13:18:10 -0800265 peerFrameDecoder.receivedKeepaliveMessageLatch.await(MESSAGE_TIMEOUT_MS,
Jonathan Hart20d8e512014-10-16 11:05:52 -0700266 TimeUnit.MILLISECONDS);
267
268 // Get the BGP Session handler
269 bgpSession = bgpSessionManager.getBgpSessions().iterator().next();
270
271 // Prepare routes to add/delete
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800272 nextHopRouter = Ip4Address.valueOf("10.20.30.40");
273 Collection<Ip4Prefix> addedRoutes = new LinkedList<>();
274 Collection<Ip4Prefix> withdrawnRoutes = new LinkedList<>();
275 addedRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0"));
276 addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8"));
277 addedRoutes.add(Ip4Prefix.valueOf("30.0.0.0/16"));
278 addedRoutes.add(Ip4Prefix.valueOf("40.0.0.0/24"));
279 addedRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32"));
280 withdrawnRoutes.add(Ip4Prefix.valueOf("60.0.0.0/8"));
281 withdrawnRoutes.add(Ip4Prefix.valueOf("70.0.0.0/16"));
282 withdrawnRoutes.add(Ip4Prefix.valueOf("80.0.0.0/24"));
283 withdrawnRoutes.add(Ip4Prefix.valueOf("90.0.0.0/32"));
Jonathan Hart20d8e512014-10-16 11:05:52 -0700284 // Write the routes
285 message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
286 addedRoutes,
287 withdrawnRoutes);
288 peerChannelHandler.savedCtx.getChannel().write(message);
289
290 // Check that the routes have been received, processed and stored
291 bgpRibIn = waitForBgpRibIn(bgpSession, 5);
292 assertThat(bgpRibIn, hasSize(5));
293 bgpRoutes = waitForBgpRoutes(5);
294 assertThat(bgpRoutes, hasSize(5));
295
296 // Setup the AS Path
297 ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
298 byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
299 ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
300 segmentAsNumbers1.add((long) 65010);
301 segmentAsNumbers1.add((long) 65020);
302 segmentAsNumbers1.add((long) 65030);
303 BgpRouteEntry.PathSegment pathSegment1 =
304 new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
305 pathSegments.add(pathSegment1);
306 //
307 byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
308 ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
309 segmentAsNumbers2.add((long) 65041);
310 segmentAsNumbers2.add((long) 65042);
311 segmentAsNumbers2.add((long) 65043);
312 BgpRouteEntry.PathSegment pathSegment2 =
313 new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
314 pathSegments.add(pathSegment2);
315 //
316 BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
317
318 //
319 bgpRouteEntry =
320 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800321 Ip4Prefix.valueOf("0.0.0.0/0"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700322 nextHopRouter,
323 (byte) BgpConstants.Update.Origin.IGP,
324 asPath,
325 DEFAULT_LOCAL_PREF);
326 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
327 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
328 //
329 bgpRouteEntry =
330 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800331 Ip4Prefix.valueOf("20.0.0.0/8"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700332 nextHopRouter,
333 (byte) BgpConstants.Update.Origin.IGP,
334 asPath,
335 DEFAULT_LOCAL_PREF);
336 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
337 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
338 //
339 bgpRouteEntry =
340 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800341 Ip4Prefix.valueOf("30.0.0.0/16"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700342 nextHopRouter,
343 (byte) BgpConstants.Update.Origin.IGP,
344 asPath,
345 DEFAULT_LOCAL_PREF);
346 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
347 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
348 //
349 bgpRouteEntry =
350 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800351 Ip4Prefix.valueOf("40.0.0.0/24"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700352 nextHopRouter,
353 (byte) BgpConstants.Update.Origin.IGP,
354 asPath,
355 DEFAULT_LOCAL_PREF);
356 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
357 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
358 //
359 bgpRouteEntry =
360 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800361 Ip4Prefix.valueOf("50.0.0.0/32"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700362 nextHopRouter,
363 (byte) BgpConstants.Update.Origin.IGP,
364 asPath,
365 DEFAULT_LOCAL_PREF);
366 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
367 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
368
369 // Delete some routes
370 addedRoutes = new LinkedList<>();
371 withdrawnRoutes = new LinkedList<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800372 withdrawnRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0"));
373 withdrawnRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32"));
Jonathan Hart20d8e512014-10-16 11:05:52 -0700374
375 // Write the routes
376 message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
377 addedRoutes,
378 withdrawnRoutes);
379 peerChannelHandler.savedCtx.getChannel().write(message);
380
381 // Check that the routes have been received, processed and stored
382 bgpRibIn = waitForBgpRibIn(bgpSession, 3);
383 assertThat(bgpRibIn, hasSize(3));
384 bgpRoutes = waitForBgpRoutes(3);
385 assertThat(bgpRoutes, hasSize(3));
386 //
387 bgpRouteEntry =
388 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800389 Ip4Prefix.valueOf("20.0.0.0/8"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700390 nextHopRouter,
391 (byte) BgpConstants.Update.Origin.IGP,
392 asPath,
393 DEFAULT_LOCAL_PREF);
394 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
395 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
396 //
397 bgpRouteEntry =
398 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800399 Ip4Prefix.valueOf("30.0.0.0/16"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700400 nextHopRouter,
401 (byte) BgpConstants.Update.Origin.IGP,
402 asPath,
403 DEFAULT_LOCAL_PREF);
404 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
405 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
406 //
407 bgpRouteEntry =
408 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800409 Ip4Prefix.valueOf("40.0.0.0/24"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700410 nextHopRouter,
411 (byte) BgpConstants.Update.Origin.IGP,
412 asPath,
413 DEFAULT_LOCAL_PREF);
414 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
415 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
416
417 // Close the channel and test there are no routes
418 peerChannelHandler.closeChannel();
419 bgpRoutes = waitForBgpRoutes(0);
420 assertThat(bgpRoutes, hasSize(0));
421 }
422}