blob: 5fdd6eb3175d15c943d96d6dc158f16a00edda01 [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
64 // The BGP Session Manager to test
65 private BgpSessionManager bgpSessionManager;
66
67 // Remote Peer state
68 private ClientBootstrap peerBootstrap;
69 private TestBgpPeerChannelHandler peerChannelHandler =
70 new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF);
71 private TestBgpPeerFrameDecoder peerFrameDecoder =
72 new TestBgpPeerFrameDecoder();
73
74 // The socket that the Remote Peer should connect to
75 private InetSocketAddress connectToSocket;
76
77 private final DummyRouteListener dummyRouteListener =
78 new DummyRouteListener();
79
80 /**
81 * Dummy implementation for the RouteListener interface.
82 */
83 private class DummyRouteListener implements RouteListener {
84 @Override
85 public void update(RouteUpdate routeUpdate) {
86 // Nothing to do
87 }
88 }
89
90 @Before
91 public void setUp() throws Exception {
92 //
93 // Setup the BGP Session Manager to test, and start listening for BGP
94 // connections.
95 //
96 bgpSessionManager = new BgpSessionManager(dummyRouteListener);
97 // NOTE: We use port 0 to bind on any available port
98 bgpSessionManager.startUp(0);
99
100 // Get the port number the BGP Session Manager is listening on
101 Channel serverChannel = TestUtils.getField(bgpSessionManager,
102 "serverChannel");
103 SocketAddress socketAddress = serverChannel.getLocalAddress();
104 InetSocketAddress inetSocketAddress =
105 (InetSocketAddress) socketAddress;
106
107 //
108 // Setup the BGP Peer, i.e., the "remote" BGP router that will
109 // initiate the BGP connection, send BGP UPDATE messages, etc.
110 //
111 ChannelFactory channelFactory =
112 new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
113 Executors.newCachedThreadPool());
114 ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
115 @Override
116 public ChannelPipeline getPipeline() throws Exception {
117 // Setup the transmitting pipeline
118 ChannelPipeline pipeline = Channels.pipeline();
119 pipeline.addLast("TestBgpPeerFrameDecoder",
120 peerFrameDecoder);
121 pipeline.addLast("TestBgpPeerChannelHandler",
122 peerChannelHandler);
123 return pipeline;
124 }
125 };
126
127 peerBootstrap = new ClientBootstrap(channelFactory);
128 peerBootstrap.setOption("child.keepAlive", true);
129 peerBootstrap.setOption("child.tcpNoDelay", true);
130 peerBootstrap.setPipelineFactory(pipelineFactory);
131
132 InetAddress connectToAddress = InetAddresses.forString("127.0.0.1");
133 connectToSocket = new InetSocketAddress(connectToAddress,
134 inetSocketAddress.getPort());
135 }
136
137 @After
138 public void tearDown() throws Exception {
139 bgpSessionManager.shutDown();
140 bgpSessionManager = null;
141 }
142
143 /**
144 * Gets BGP RIB-IN routes by waiting until they are received.
145 * <p/>
146 * NOTE: We keep checking once a second the number of received routes,
147 * up to 5 seconds.
148 *
149 * @param bgpSession the BGP session that is expected to receive the
150 * routes
151 * @param expectedRoutes the expected number of routes
152 * @return the BGP RIB-IN routes as received within the expected
153 * time interval
154 */
155 private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
156 long expectedRoutes)
157 throws InterruptedException {
158 Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
159
160 final int maxChecks = 5; // Max wait of 5 seconds
161 for (int i = 0; i < maxChecks; i++) {
162 if (bgpRibIn.size() == expectedRoutes) {
163 break;
164 }
165 Thread.sleep(1000);
166 bgpRibIn = bgpSession.getBgpRibIn();
167 }
168
169 return bgpRibIn;
170 }
171
172 /**
173 * Gets BGP merged routes by waiting until they are received.
174 * <p/>
175 * NOTE: We keep checking once a second the number of received routes,
176 * up to 5 seconds.
177 *
178 * @param expectedRoutes the expected number of routes
179 * @return the BGP Session Manager routes as received within the expected
180 * time interval
181 */
182 private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
183 throws InterruptedException {
184 Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
185
186 final int maxChecks = 5; // Max wait of 5 seconds
187 for (int i = 0; i < maxChecks; i++) {
188 if (bgpRoutes.size() == expectedRoutes) {
189 break;
190 }
191 Thread.sleep(1000);
192 bgpRoutes = bgpSessionManager.getBgpRoutes();
193 }
194
195 return bgpRoutes;
196 }
197
198 /**
199 * Tests that the BGP OPEN messages have been exchanged, followed by
200 * KEEPALIVE.
201 * <p>
202 * The BGP Peer opens the sessions and transmits OPEN Message, eventually
203 * followed by KEEPALIVE. The tested BGP listener should respond by
204 * OPEN Message, followed by KEEPALIVE.
205 *
206 * @throws TestUtilsException TestUtils error
207 */
208 @Test
209 public void testExchangedBgpOpenMessages()
210 throws InterruptedException, TestUtilsException {
211 // Initiate the connection
212 peerBootstrap.connect(connectToSocket);
213
214 // Wait until the OPEN message is received
215 peerFrameDecoder.receivedOpenMessageLatch.await(2000,
216 TimeUnit.MILLISECONDS);
217 // Wait until the KEEPALIVE message is received
218 peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
219 TimeUnit.MILLISECONDS);
220
221 //
222 // Test the fields from the BGP OPEN message:
223 // BGP version, AS number, BGP ID
224 //
225 assertThat(peerFrameDecoder.remoteBgpVersion,
226 is(BgpConstants.BGP_VERSION));
227 assertThat(peerFrameDecoder.remoteAs,
228 is(TestBgpPeerChannelHandler.PEER_AS));
229 assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID));
230
231 //
232 // Test that a BgpSession instance has been created
233 //
234 assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
235 assertThat(bgpSessionManager.getBgpSessions(), hasSize(1));
236 BgpSession bgpSession =
237 bgpSessionManager.getBgpSessions().iterator().next();
238 assertThat(bgpSession, notNullValue());
239 long sessionAs = TestUtils.getField(bgpSession, "localAs");
240 assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS));
241 }
242
243 /**
244 * Tests that the BGP UPDATE messages have been received and processed.
245 */
246 @Test
247 public void testProcessedBgpUpdateMessages() throws InterruptedException {
248 BgpSession bgpSession;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800249 Ip4Address nextHopRouter;
Jonathan Hart20d8e512014-10-16 11:05:52 -0700250 BgpRouteEntry bgpRouteEntry;
251 ChannelBuffer message;
252 Collection<BgpRouteEntry> bgpRibIn;
253 Collection<BgpRouteEntry> bgpRoutes;
254
255 // Initiate the connection
256 peerBootstrap.connect(connectToSocket);
257
258 // Wait until the OPEN message is received
259 peerFrameDecoder.receivedOpenMessageLatch.await(2000,
260 TimeUnit.MILLISECONDS);
261 // Wait until the KEEPALIVE message is received
262 peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
263 TimeUnit.MILLISECONDS);
264
265 // Get the BGP Session handler
266 bgpSession = bgpSessionManager.getBgpSessions().iterator().next();
267
268 // Prepare routes to add/delete
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800269 nextHopRouter = Ip4Address.valueOf("10.20.30.40");
270 Collection<Ip4Prefix> addedRoutes = new LinkedList<>();
271 Collection<Ip4Prefix> withdrawnRoutes = new LinkedList<>();
272 addedRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0"));
273 addedRoutes.add(Ip4Prefix.valueOf("20.0.0.0/8"));
274 addedRoutes.add(Ip4Prefix.valueOf("30.0.0.0/16"));
275 addedRoutes.add(Ip4Prefix.valueOf("40.0.0.0/24"));
276 addedRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32"));
277 withdrawnRoutes.add(Ip4Prefix.valueOf("60.0.0.0/8"));
278 withdrawnRoutes.add(Ip4Prefix.valueOf("70.0.0.0/16"));
279 withdrawnRoutes.add(Ip4Prefix.valueOf("80.0.0.0/24"));
280 withdrawnRoutes.add(Ip4Prefix.valueOf("90.0.0.0/32"));
Jonathan Hart20d8e512014-10-16 11:05:52 -0700281 // Write the routes
282 message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
283 addedRoutes,
284 withdrawnRoutes);
285 peerChannelHandler.savedCtx.getChannel().write(message);
286
287 // Check that the routes have been received, processed and stored
288 bgpRibIn = waitForBgpRibIn(bgpSession, 5);
289 assertThat(bgpRibIn, hasSize(5));
290 bgpRoutes = waitForBgpRoutes(5);
291 assertThat(bgpRoutes, hasSize(5));
292
293 // Setup the AS Path
294 ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
295 byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
296 ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
297 segmentAsNumbers1.add((long) 65010);
298 segmentAsNumbers1.add((long) 65020);
299 segmentAsNumbers1.add((long) 65030);
300 BgpRouteEntry.PathSegment pathSegment1 =
301 new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
302 pathSegments.add(pathSegment1);
303 //
304 byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
305 ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
306 segmentAsNumbers2.add((long) 65041);
307 segmentAsNumbers2.add((long) 65042);
308 segmentAsNumbers2.add((long) 65043);
309 BgpRouteEntry.PathSegment pathSegment2 =
310 new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
311 pathSegments.add(pathSegment2);
312 //
313 BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
314
315 //
316 bgpRouteEntry =
317 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800318 Ip4Prefix.valueOf("0.0.0.0/0"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700319 nextHopRouter,
320 (byte) BgpConstants.Update.Origin.IGP,
321 asPath,
322 DEFAULT_LOCAL_PREF);
323 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
324 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
325 //
326 bgpRouteEntry =
327 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800328 Ip4Prefix.valueOf("20.0.0.0/8"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700329 nextHopRouter,
330 (byte) BgpConstants.Update.Origin.IGP,
331 asPath,
332 DEFAULT_LOCAL_PREF);
333 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
334 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
335 //
336 bgpRouteEntry =
337 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800338 Ip4Prefix.valueOf("30.0.0.0/16"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700339 nextHopRouter,
340 (byte) BgpConstants.Update.Origin.IGP,
341 asPath,
342 DEFAULT_LOCAL_PREF);
343 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
344 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
345 //
346 bgpRouteEntry =
347 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800348 Ip4Prefix.valueOf("40.0.0.0/24"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700349 nextHopRouter,
350 (byte) BgpConstants.Update.Origin.IGP,
351 asPath,
352 DEFAULT_LOCAL_PREF);
353 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
354 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
355 //
356 bgpRouteEntry =
357 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800358 Ip4Prefix.valueOf("50.0.0.0/32"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700359 nextHopRouter,
360 (byte) BgpConstants.Update.Origin.IGP,
361 asPath,
362 DEFAULT_LOCAL_PREF);
363 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
364 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
365
366 // Delete some routes
367 addedRoutes = new LinkedList<>();
368 withdrawnRoutes = new LinkedList<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800369 withdrawnRoutes.add(Ip4Prefix.valueOf("0.0.0.0/0"));
370 withdrawnRoutes.add(Ip4Prefix.valueOf("50.0.0.0/32"));
Jonathan Hart20d8e512014-10-16 11:05:52 -0700371
372 // Write the routes
373 message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
374 addedRoutes,
375 withdrawnRoutes);
376 peerChannelHandler.savedCtx.getChannel().write(message);
377
378 // Check that the routes have been received, processed and stored
379 bgpRibIn = waitForBgpRibIn(bgpSession, 3);
380 assertThat(bgpRibIn, hasSize(3));
381 bgpRoutes = waitForBgpRoutes(3);
382 assertThat(bgpRoutes, hasSize(3));
383 //
384 bgpRouteEntry =
385 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800386 Ip4Prefix.valueOf("20.0.0.0/8"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700387 nextHopRouter,
388 (byte) BgpConstants.Update.Origin.IGP,
389 asPath,
390 DEFAULT_LOCAL_PREF);
391 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
392 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
393 //
394 bgpRouteEntry =
395 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800396 Ip4Prefix.valueOf("30.0.0.0/16"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700397 nextHopRouter,
398 (byte) BgpConstants.Update.Origin.IGP,
399 asPath,
400 DEFAULT_LOCAL_PREF);
401 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
402 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
403 //
404 bgpRouteEntry =
405 new BgpRouteEntry(bgpSession,
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800406 Ip4Prefix.valueOf("40.0.0.0/24"),
Jonathan Hart20d8e512014-10-16 11:05:52 -0700407 nextHopRouter,
408 (byte) BgpConstants.Update.Origin.IGP,
409 asPath,
410 DEFAULT_LOCAL_PREF);
411 bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
412 assertThat(bgpRibIn, hasItem(bgpRouteEntry));
413
414 // Close the channel and test there are no routes
415 peerChannelHandler.closeChannel();
416 bgpRoutes = waitForBgpRoutes(0);
417 assertThat(bgpRoutes, hasSize(0));
418 }
419}