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