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