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