Thomas Vachuska

Merge remote-tracking branch 'origin/master'

...@@ -42,6 +42,16 @@ ...@@ -42,6 +42,16 @@
42 <artifactId>onlab-thirdparty</artifactId> 42 <artifactId>onlab-thirdparty</artifactId>
43 </dependency> 43 </dependency>
44 44
45 + <dependency>
46 + <groupId>org.onlab.onos</groupId>
47 + <artifactId>onlab-misc</artifactId>
48 + </dependency>
49 +
50 + <dependency>
51 + <groupId>org.easymock</groupId>
52 + <artifactId>easymock</artifactId>
53 + <scope>test</scope>
54 + </dependency>
45 </dependencies> 55 </dependencies>
46 56
47 </project> 57 </project>
......
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import java.util.ArrayList;
8 +
9 +import org.junit.Test;
10 +
11 +/**
12 + * Unit tests for the BgpRouteEntry.AsPath class.
13 + */
14 +public class AsPathTest {
15 + /**
16 + * Generates an AS Path.
17 + *
18 + * @return a generated AS Path
19 + */
20 + private BgpRouteEntry.AsPath generateAsPath() {
21 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
22 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
23 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
24 + segmentAsNumbers1.add((long) 1);
25 + segmentAsNumbers1.add((long) 2);
26 + segmentAsNumbers1.add((long) 3);
27 + BgpRouteEntry.PathSegment pathSegment1 =
28 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
29 + pathSegments.add(pathSegment1);
30 + //
31 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
32 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
33 + segmentAsNumbers2.add((long) 4);
34 + segmentAsNumbers2.add((long) 5);
35 + segmentAsNumbers2.add((long) 6);
36 + BgpRouteEntry.PathSegment pathSegment2 =
37 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
38 + pathSegments.add(pathSegment2);
39 + //
40 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
41 +
42 + return asPath;
43 + }
44 +
45 + /**
46 + * Tests valid class constructor.
47 + */
48 + @Test
49 + public void testConstructor() {
50 + BgpRouteEntry.AsPath asPath = generateAsPath();
51 +
52 + String expectedString =
53 + "AsPath{pathSegments=" +
54 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
55 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
56 + assertThat(asPath.toString(), is(expectedString));
57 + }
58 +
59 + /**
60 + * Tests invalid class constructor for null Path Segments.
61 + */
62 + @Test(expected = NullPointerException.class)
63 + public void testInvalidConstructorNullPathSegments() {
64 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = null;
65 + new BgpRouteEntry.AsPath(pathSegments);
66 + }
67 +
68 + /**
69 + * Tests getting the fields of an AS Path.
70 + */
71 + @Test
72 + public void testGetFields() {
73 + // Create the fields to compare against
74 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
75 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
76 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
77 + segmentAsNumbers1.add((long) 1);
78 + segmentAsNumbers1.add((long) 2);
79 + segmentAsNumbers1.add((long) 3);
80 + BgpRouteEntry.PathSegment pathSegment1 =
81 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
82 + pathSegments.add(pathSegment1);
83 + //
84 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
85 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
86 + segmentAsNumbers2.add((long) 4);
87 + segmentAsNumbers2.add((long) 5);
88 + segmentAsNumbers2.add((long) 6);
89 + BgpRouteEntry.PathSegment pathSegment2 =
90 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
91 + pathSegments.add(pathSegment2);
92 +
93 + // Generate the entry to test
94 + BgpRouteEntry.AsPath asPath = generateAsPath();
95 +
96 + assertThat(asPath.getPathSegments(), is(pathSegments));
97 + }
98 +
99 + /**
100 + * Tests getting the AS Path Length.
101 + */
102 + @Test
103 + public void testGetAsPathLength() {
104 + BgpRouteEntry.AsPath asPath = generateAsPath();
105 + assertThat(asPath.getAsPathLength(), is(4));
106 +
107 + // Create an empty AS Path
108 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
109 + asPath = new BgpRouteEntry.AsPath(pathSegments);
110 + assertThat(asPath.getAsPathLength(), is(0));
111 + }
112 +
113 + /**
114 + * Tests equality of {@link BgpRouteEntry.AsPath}.
115 + */
116 + @Test
117 + public void testEquality() {
118 + BgpRouteEntry.AsPath asPath1 = generateAsPath();
119 + BgpRouteEntry.AsPath asPath2 = generateAsPath();
120 +
121 + assertThat(asPath1, is(asPath2));
122 + }
123 +
124 + /**
125 + * Tests non-equality of {@link BgpRouteEntry.AsPath}.
126 + */
127 + @Test
128 + public void testNonEquality() {
129 + BgpRouteEntry.AsPath asPath1 = generateAsPath();
130 +
131 + // Setup AS Path 2
132 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
133 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
134 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
135 + segmentAsNumbers1.add((long) 1);
136 + segmentAsNumbers1.add((long) 2);
137 + segmentAsNumbers1.add((long) 3);
138 + BgpRouteEntry.PathSegment pathSegment1 =
139 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
140 + pathSegments.add(pathSegment1);
141 + //
142 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
143 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
144 + segmentAsNumbers2.add((long) 4);
145 + segmentAsNumbers2.add((long) 55); // Different
146 + segmentAsNumbers2.add((long) 6);
147 + BgpRouteEntry.PathSegment pathSegment2 =
148 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
149 + pathSegments.add(pathSegment2);
150 + //
151 + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
152 +
153 + assertThat(asPath1, is(not(asPath2)));
154 + }
155 +
156 + /**
157 + * Tests object string representation.
158 + */
159 + @Test
160 + public void testToString() {
161 + BgpRouteEntry.AsPath asPath = generateAsPath();
162 +
163 + String expectedString =
164 + "AsPath{pathSegments=" +
165 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
166 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
167 + assertThat(asPath.toString(), is(expectedString));
168 + }
169 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.easymock.EasyMock.createMock;
4 +import static org.easymock.EasyMock.expect;
5 +import static org.easymock.EasyMock.replay;
6 +import static org.hamcrest.Matchers.is;
7 +import static org.hamcrest.Matchers.not;
8 +import static org.junit.Assert.assertThat;
9 +
10 +import java.util.ArrayList;
11 +
12 +import org.junit.Before;
13 +import org.junit.Test;
14 +import org.onlab.packet.IpAddress;
15 +import org.onlab.packet.IpPrefix;
16 +
17 +/**
18 + * Unit tests for the BgpRouteEntry class.
19 + */
20 +public class BgpRouteEntryTest {
21 + private BgpSession bgpSession;
22 + private static final IpAddress BGP_SESSION_BGP_ID =
23 + IpAddress.valueOf("10.0.0.1");
24 + private static final IpAddress BGP_SESSION_IP_ADDRESS =
25 + IpAddress.valueOf("20.0.0.1");
26 +
27 + private BgpSession bgpSession2;
28 + private static final IpAddress BGP_SESSION_BGP_ID2 =
29 + IpAddress.valueOf("10.0.0.2");
30 + private static final IpAddress BGP_SESSION_IP_ADDRESS2 =
31 + IpAddress.valueOf("20.0.0.1");
32 +
33 + private BgpSession bgpSession3;
34 + private static final IpAddress BGP_SESSION_BGP_ID3 =
35 + IpAddress.valueOf("10.0.0.1");
36 + private static final IpAddress BGP_SESSION_IP_ADDRESS3 =
37 + IpAddress.valueOf("20.0.0.2");
38 +
39 + @Before
40 + public void setUp() throws Exception {
41 + // Mock objects for testing
42 + bgpSession = createMock(BgpSession.class);
43 + bgpSession2 = createMock(BgpSession.class);
44 + bgpSession3 = createMock(BgpSession.class);
45 +
46 + // Setup the BGP Sessions
47 + expect(bgpSession.getRemoteBgpId())
48 + .andReturn(BGP_SESSION_BGP_ID).anyTimes();
49 + expect(bgpSession.getRemoteIp4Address())
50 + .andReturn(BGP_SESSION_IP_ADDRESS).anyTimes();
51 + //
52 + expect(bgpSession2.getRemoteBgpId())
53 + .andReturn(BGP_SESSION_BGP_ID2).anyTimes();
54 + expect(bgpSession2.getRemoteIp4Address())
55 + .andReturn(BGP_SESSION_IP_ADDRESS2).anyTimes();
56 + //
57 + expect(bgpSession3.getRemoteBgpId())
58 + .andReturn(BGP_SESSION_BGP_ID3).anyTimes();
59 + expect(bgpSession3.getRemoteIp4Address())
60 + .andReturn(BGP_SESSION_IP_ADDRESS3).anyTimes();
61 +
62 + replay(bgpSession);
63 + replay(bgpSession2);
64 + replay(bgpSession3);
65 + }
66 +
67 + /**
68 + * Generates a BGP Route Entry.
69 + *
70 + * @return a generated BGP Route Entry
71 + */
72 + private BgpRouteEntry generateBgpRouteEntry() {
73 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
74 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
75 + byte origin = BgpConstants.Update.Origin.IGP;
76 + // Setup the AS Path
77 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
78 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
79 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
80 + segmentAsNumbers1.add((long) 1);
81 + segmentAsNumbers1.add((long) 2);
82 + segmentAsNumbers1.add((long) 3);
83 + BgpRouteEntry.PathSegment pathSegment1 =
84 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
85 + pathSegments.add(pathSegment1);
86 + //
87 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
88 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
89 + segmentAsNumbers2.add((long) 4);
90 + segmentAsNumbers2.add((long) 5);
91 + segmentAsNumbers2.add((long) 6);
92 + BgpRouteEntry.PathSegment pathSegment2 =
93 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
94 + pathSegments.add(pathSegment2);
95 + //
96 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
97 + //
98 + long localPref = 100;
99 + long multiExitDisc = 20;
100 +
101 + BgpRouteEntry bgpRouteEntry =
102 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
103 + localPref);
104 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
105 +
106 + return bgpRouteEntry;
107 + }
108 +
109 + /**
110 + * Tests valid class constructor.
111 + */
112 + @Test
113 + public void testConstructor() {
114 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
115 +
116 + String expectedString =
117 + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
118 + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
119 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
120 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
121 + "localPref=100, multiExitDisc=20}";
122 + assertThat(bgpRouteEntry.toString(), is(expectedString));
123 + }
124 +
125 + /**
126 + * Tests invalid class constructor for null BGP Session.
127 + */
128 + @Test(expected = NullPointerException.class)
129 + public void testInvalidConstructorNullBgpSession() {
130 + BgpSession bgpSessionNull = null;
131 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
132 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
133 + byte origin = BgpConstants.Update.Origin.IGP;
134 + // Setup the AS Path
135 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
136 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
137 + //
138 + long localPref = 100;
139 +
140 + new BgpRouteEntry(bgpSessionNull, prefix, nextHop, origin, asPath,
141 + localPref);
142 + }
143 +
144 + /**
145 + * Tests invalid class constructor for null AS Path.
146 + */
147 + @Test(expected = NullPointerException.class)
148 + public void testInvalidConstructorNullAsPath() {
149 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
150 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
151 + byte origin = BgpConstants.Update.Origin.IGP;
152 + BgpRouteEntry.AsPath asPath = null;
153 + long localPref = 100;
154 +
155 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
156 + localPref);
157 + }
158 +
159 + /**
160 + * Tests getting the fields of a BGP route entry.
161 + */
162 + @Test
163 + public void testGetFields() {
164 + // Create the fields to compare against
165 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
166 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
167 + byte origin = BgpConstants.Update.Origin.IGP;
168 + // Setup the AS Path
169 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
170 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
171 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
172 + segmentAsNumbers1.add((long) 1);
173 + segmentAsNumbers1.add((long) 2);
174 + segmentAsNumbers1.add((long) 3);
175 + BgpRouteEntry.PathSegment pathSegment1 =
176 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
177 + pathSegments.add(pathSegment1);
178 + //
179 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
180 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
181 + segmentAsNumbers2.add((long) 4);
182 + segmentAsNumbers2.add((long) 5);
183 + segmentAsNumbers2.add((long) 6);
184 + BgpRouteEntry.PathSegment pathSegment2 =
185 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
186 + pathSegments.add(pathSegment2);
187 + //
188 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
189 + //
190 + long localPref = 100;
191 + long multiExitDisc = 20;
192 +
193 + // Generate the entry to test
194 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
195 +
196 + assertThat(bgpRouteEntry.prefix(), is(prefix));
197 + assertThat(bgpRouteEntry.nextHop(), is(nextHop));
198 + assertThat(bgpRouteEntry.getBgpSession(), is(bgpSession));
199 + assertThat(bgpRouteEntry.getOrigin(), is(origin));
200 + assertThat(bgpRouteEntry.getAsPath(), is(asPath));
201 + assertThat(bgpRouteEntry.getLocalPref(), is(localPref));
202 + assertThat(bgpRouteEntry.getMultiExitDisc(), is(multiExitDisc));
203 + }
204 +
205 + /**
206 + * Tests whether a BGP route entry is a local route.
207 + */
208 + @Test
209 + public void testIsLocalRoute() {
210 + //
211 + // Test non-local route
212 + //
213 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
214 + assertThat(bgpRouteEntry.isLocalRoute(), is(false));
215 +
216 + //
217 + // Test local route with AS Path that begins with AS_SET
218 + //
219 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
220 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
221 + byte origin = BgpConstants.Update.Origin.IGP;
222 + // Setup the AS Path
223 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
224 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SET;
225 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
226 + segmentAsNumbers1.add((long) 1);
227 + segmentAsNumbers1.add((long) 2);
228 + segmentAsNumbers1.add((long) 3);
229 + BgpRouteEntry.PathSegment pathSegment1 =
230 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
231 + pathSegments.add(pathSegment1);
232 + //
233 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
234 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
235 + segmentAsNumbers2.add((long) 4);
236 + segmentAsNumbers2.add((long) 5);
237 + segmentAsNumbers2.add((long) 6);
238 + BgpRouteEntry.PathSegment pathSegment2 =
239 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
240 + pathSegments.add(pathSegment2);
241 + //
242 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
243 + //
244 + long localPref = 100;
245 + long multiExitDisc = 20;
246 + //
247 + bgpRouteEntry =
248 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
249 + localPref);
250 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
251 + assertThat(bgpRouteEntry.isLocalRoute(), is(true));
252 +
253 + //
254 + // Test local route with empty AS Path
255 + //
256 + pathSegments = new ArrayList<>();
257 + asPath = new BgpRouteEntry.AsPath(pathSegments);
258 + bgpRouteEntry =
259 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
260 + localPref);
261 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
262 + assertThat(bgpRouteEntry.isLocalRoute(), is(true));
263 + }
264 +
265 + /**
266 + * Tests getting the BGP Neighbor AS number for a route.
267 + */
268 + @Test
269 + public void testGetNeighborAs() {
270 + //
271 + // Get neighbor AS for non-local route
272 + //
273 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
274 + assertThat(bgpRouteEntry.getNeighborAs(), is((long) 1));
275 +
276 + //
277 + // Get neighbor AS for a local route
278 + //
279 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
280 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
281 + byte origin = BgpConstants.Update.Origin.IGP;
282 + // Setup the AS Path
283 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
284 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
285 + //
286 + long localPref = 100;
287 + long multiExitDisc = 20;
288 + //
289 + bgpRouteEntry =
290 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
291 + localPref);
292 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
293 + assertThat(bgpRouteEntry.getNeighborAs(), is(BgpConstants.BGP_AS_0));
294 + }
295 +
296 + /**
297 + * Tests whether a BGP route entry has AS Path loop.
298 + */
299 + @Test
300 + public void testHasAsPathLoop() {
301 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
302 +
303 + // Test for loops: test each AS number in the interval [1, 6]
304 + for (int i = 1; i <= 6; i++) {
305 + assertThat(bgpRouteEntry.hasAsPathLoop(i), is(true));
306 + }
307 +
308 + // Test for non-loops
309 + assertThat(bgpRouteEntry.hasAsPathLoop(500), is(false));
310 + }
311 +
312 + /**
313 + * Tests the BGP Decision Process comparison of BGP routes.
314 + */
315 + @Test
316 + public void testBgpDecisionProcessComparison() {
317 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
318 + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
319 +
320 + //
321 + // Compare two routes that are same
322 + //
323 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
324 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
325 +
326 + //
327 + // Compare two routes with different LOCAL_PREF
328 + //
329 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
330 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
331 + byte origin = BgpConstants.Update.Origin.IGP;
332 + // Setup the AS Path
333 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
334 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
335 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
336 + segmentAsNumbers1.add((long) 1);
337 + segmentAsNumbers1.add((long) 2);
338 + segmentAsNumbers1.add((long) 3);
339 + BgpRouteEntry.PathSegment pathSegment1 =
340 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
341 + pathSegments.add(pathSegment1);
342 + //
343 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
344 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
345 + segmentAsNumbers2.add((long) 4);
346 + segmentAsNumbers2.add((long) 5);
347 + segmentAsNumbers2.add((long) 6);
348 + BgpRouteEntry.PathSegment pathSegment2 =
349 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
350 + pathSegments.add(pathSegment2);
351 + //
352 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
353 + //
354 + long localPref = 50; // Different
355 + long multiExitDisc = 20;
356 + bgpRouteEntry2 =
357 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
358 + localPref);
359 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
360 + //
361 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
362 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
363 + localPref = bgpRouteEntry1.getLocalPref(); // Restore
364 +
365 + //
366 + // Compare two routes with different AS_PATH length
367 + //
368 + ArrayList<BgpRouteEntry.PathSegment> pathSegments2 = new ArrayList<>();
369 + pathSegments2.add(pathSegment1);
370 + // Different AS Path
371 + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments2);
372 + bgpRouteEntry2 =
373 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath2,
374 + localPref);
375 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
376 + //
377 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(false));
378 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
379 +
380 + //
381 + // Compare two routes with different ORIGIN
382 + //
383 + origin = BgpConstants.Update.Origin.EGP; // Different
384 + bgpRouteEntry2 =
385 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
386 + localPref);
387 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
388 + //
389 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
390 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
391 + origin = bgpRouteEntry1.getOrigin(); // Restore
392 +
393 + //
394 + // Compare two routes with different MULTI_EXIT_DISC
395 + //
396 + multiExitDisc = 10; // Different
397 + bgpRouteEntry2 =
398 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
399 + localPref);
400 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
401 + //
402 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
403 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
404 + multiExitDisc = bgpRouteEntry1.getMultiExitDisc(); // Restore
405 +
406 + //
407 + // Compare two routes with different BGP ID
408 + //
409 + bgpRouteEntry2 =
410 + new BgpRouteEntry(bgpSession2, prefix, nextHop, origin, asPath,
411 + localPref);
412 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
413 + //
414 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
415 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
416 +
417 + //
418 + // Compare two routes with different BGP address
419 + //
420 + bgpRouteEntry2 =
421 + new BgpRouteEntry(bgpSession3, prefix, nextHop, origin, asPath,
422 + localPref);
423 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
424 + //
425 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
426 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
427 + }
428 +
429 + /**
430 + * Tests equality of {@link BgpRouteEntry}.
431 + */
432 + @Test
433 + public void testEquality() {
434 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
435 + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
436 +
437 + assertThat(bgpRouteEntry1, is(bgpRouteEntry2));
438 + }
439 +
440 + /**
441 + * Tests non-equality of {@link BgpRouteEntry}.
442 + */
443 + @Test
444 + public void testNonEquality() {
445 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
446 +
447 + // Setup BGP Route 2
448 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
449 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
450 + byte origin = BgpConstants.Update.Origin.IGP;
451 + // Setup the AS Path
452 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
453 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
454 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
455 + segmentAsNumbers1.add((long) 1);
456 + segmentAsNumbers1.add((long) 2);
457 + segmentAsNumbers1.add((long) 3);
458 + BgpRouteEntry.PathSegment pathSegment1 =
459 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
460 + pathSegments.add(pathSegment1);
461 + //
462 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
463 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
464 + segmentAsNumbers2.add((long) 4);
465 + segmentAsNumbers2.add((long) 5);
466 + segmentAsNumbers2.add((long) 6);
467 + BgpRouteEntry.PathSegment pathSegment2 =
468 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
469 + pathSegments.add(pathSegment2);
470 + //
471 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
472 + //
473 + long localPref = 500; // Different
474 + long multiExitDisc = 20;
475 + BgpRouteEntry bgpRouteEntry2 =
476 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
477 + localPref);
478 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
479 +
480 + assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2)));
481 + }
482 +
483 + /**
484 + * Tests object string representation.
485 + */
486 + @Test
487 + public void testToString() {
488 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
489 +
490 + String expectedString =
491 + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
492 + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
493 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
494 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
495 + "localPref=100, multiExitDisc=20}";
496 + assertThat(bgpRouteEntry.toString(), is(expectedString));
497 + }
498 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.hasItem;
4 +import static org.hamcrest.Matchers.hasSize;
5 +import static org.hamcrest.Matchers.is;
6 +import static org.hamcrest.Matchers.notNullValue;
7 +import static org.junit.Assert.assertThat;
8 +
9 +import java.net.InetAddress;
10 +import java.net.InetSocketAddress;
11 +import java.net.SocketAddress;
12 +import java.util.ArrayList;
13 +import java.util.Collection;
14 +import java.util.LinkedList;
15 +import java.util.concurrent.Executors;
16 +import java.util.concurrent.TimeUnit;
17 +
18 +import org.jboss.netty.bootstrap.ClientBootstrap;
19 +import org.jboss.netty.buffer.ChannelBuffer;
20 +import org.jboss.netty.channel.Channel;
21 +import org.jboss.netty.channel.ChannelFactory;
22 +import org.jboss.netty.channel.ChannelPipeline;
23 +import org.jboss.netty.channel.ChannelPipelineFactory;
24 +import org.jboss.netty.channel.Channels;
25 +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
26 +import org.junit.After;
27 +import org.junit.Before;
28 +import org.junit.Test;
29 +import org.onlab.onos.sdnip.RouteListener;
30 +import org.onlab.onos.sdnip.RouteUpdate;
31 +import org.onlab.packet.IpAddress;
32 +import org.onlab.packet.IpPrefix;
33 +import org.onlab.util.TestUtils;
34 +import org.onlab.util.TestUtils.TestUtilsException;
35 +
36 +import com.google.common.net.InetAddresses;
37 +
38 +/**
39 + * Unit tests for the BgpSessionManager class.
40 + */
41 +public 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 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import java.util.ArrayList;
8 +
9 +import org.junit.Test;
10 +
11 +/**
12 + * Unit tests for the BgpRouteEntry.PathSegment class.
13 + */
14 +public class PathSegmentTest {
15 + /**
16 + * Generates a Path Segment.
17 + *
18 + * @return a generated PathSegment
19 + */
20 + private BgpRouteEntry.PathSegment generatePathSegment() {
21 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
22 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
23 + segmentAsNumbers.add((long) 1);
24 + segmentAsNumbers.add((long) 2);
25 + segmentAsNumbers.add((long) 3);
26 + BgpRouteEntry.PathSegment pathSegment =
27 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
28 +
29 + return pathSegment;
30 + }
31 +
32 + /**
33 + * Tests valid class constructor.
34 + */
35 + @Test
36 + public void testConstructor() {
37 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
38 +
39 + String expectedString =
40 + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
41 + assertThat(pathSegment.toString(), is(expectedString));
42 + }
43 +
44 + /**
45 + * Tests invalid class constructor for null Segment AS Numbers.
46 + */
47 + @Test(expected = NullPointerException.class)
48 + public void testInvalidConstructorNullSegmentAsNumbers() {
49 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
50 + ArrayList<Long> segmentAsNumbers = null;
51 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
52 + }
53 +
54 + /**
55 + * Tests getting the fields of a Path Segment.
56 + */
57 + @Test
58 + public void testGetFields() {
59 + // Create the fields to compare against
60 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
61 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
62 + segmentAsNumbers.add((long) 1);
63 + segmentAsNumbers.add((long) 2);
64 + segmentAsNumbers.add((long) 3);
65 +
66 + // Generate the entry to test
67 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
68 +
69 + assertThat(pathSegment.getType(), is(pathSegmentType));
70 + assertThat(pathSegment.getSegmentAsNumbers(), is(segmentAsNumbers));
71 + }
72 +
73 + /**
74 + * Tests equality of {@link BgpRouteEntry.PathSegment}.
75 + */
76 + @Test
77 + public void testEquality() {
78 + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
79 + BgpRouteEntry.PathSegment pathSegment2 = generatePathSegment();
80 +
81 + assertThat(pathSegment1, is(pathSegment2));
82 + }
83 +
84 + /**
85 + * Tests non-equality of {@link BgpRouteEntry.PathSegment}.
86 + */
87 + @Test
88 + public void testNonEquality() {
89 + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
90 +
91 + // Setup Path Segment 2
92 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
93 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
94 + segmentAsNumbers.add((long) 1);
95 + segmentAsNumbers.add((long) 22); // Different
96 + segmentAsNumbers.add((long) 3);
97 + //
98 + BgpRouteEntry.PathSegment pathSegment2 =
99 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
100 +
101 + assertThat(pathSegment1, is(not(pathSegment2)));
102 + }
103 +
104 + /**
105 + * Tests object string representation.
106 + */
107 + @Test
108 + public void testToString() {
109 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
110 +
111 + String expectedString =
112 + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
113 + assertThat(pathSegment.toString(), is(expectedString));
114 + }
115 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.util.Collection;
4 +
5 +import org.jboss.netty.buffer.ChannelBuffer;
6 +import org.jboss.netty.buffer.ChannelBuffers;
7 +import org.jboss.netty.channel.ChannelHandlerContext;
8 +import org.jboss.netty.channel.ChannelStateEvent;
9 +import org.jboss.netty.channel.SimpleChannelHandler;
10 +import org.onlab.packet.IpAddress;
11 +import org.onlab.packet.IpPrefix;
12 +
13 +/**
14 + * Class for handling the remote BGP Peer session.
15 + */
16 +class TestBgpPeerChannelHandler extends SimpleChannelHandler {
17 + static final long PEER_AS = 65001;
18 + static final int PEER_HOLDTIME = 120; // 120 seconds
19 + final IpAddress bgpId; // The BGP ID
20 + final long localPref; // Local preference for routes
21 + final long multiExitDisc = 20; // MED value
22 +
23 + ChannelHandlerContext savedCtx;
24 +
25 + /**
26 + * Constructor for given BGP ID.
27 + *
28 + * @param bgpId the BGP ID to use
29 + * @param localPref the local preference for the routes to use
30 + */
31 + TestBgpPeerChannelHandler(IpAddress bgpId,
32 + long localPref) {
33 + this.bgpId = bgpId;
34 + this.localPref = localPref;
35 + }
36 +
37 + /**
38 + * Closes the channel.
39 + */
40 + void closeChannel() {
41 + savedCtx.getChannel().close();
42 + }
43 +
44 + @Override
45 + public void channelConnected(ChannelHandlerContext ctx,
46 + ChannelStateEvent channelEvent) {
47 + this.savedCtx = ctx;
48 + // Prepare and transmit BGP OPEN message
49 + ChannelBuffer message = prepareBgpOpen();
50 + ctx.getChannel().write(message);
51 +
52 + // Prepare and transmit BGP KEEPALIVE message
53 + message = prepareBgpKeepalive();
54 + ctx.getChannel().write(message);
55 + }
56 +
57 + @Override
58 + public void channelDisconnected(ChannelHandlerContext ctx,
59 + ChannelStateEvent channelEvent) {
60 + // Nothing to do
61 + }
62 +
63 + /**
64 + * Prepares BGP OPEN message.
65 + *
66 + * @return the message to transmit (BGP header included)
67 + */
68 + ChannelBuffer prepareBgpOpen() {
69 + ChannelBuffer message =
70 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
71 + message.writeByte(BgpConstants.BGP_VERSION);
72 + message.writeShort((int) PEER_AS);
73 + message.writeShort(PEER_HOLDTIME);
74 + message.writeInt(bgpId.toInt());
75 + message.writeByte(0); // No Optional Parameters
76 + return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
77 + }
78 +
79 + /**
80 + * Prepares BGP UPDATE message.
81 + *
82 + * @param nextHopRouter the next-hop router address for the routes to add
83 + * @param addedRoutes the routes to add
84 + * @param withdrawnRoutes the routes to withdraw
85 + * @return the message to transmit (BGP header included)
86 + */
87 + ChannelBuffer prepareBgpUpdate(IpAddress nextHopRouter,
88 + Collection<IpPrefix> addedRoutes,
89 + Collection<IpPrefix> withdrawnRoutes) {
90 + int attrFlags;
91 + ChannelBuffer message =
92 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
93 + ChannelBuffer pathAttributes =
94 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
95 +
96 + // Encode the Withdrawn Routes
97 + ChannelBuffer encodedPrefixes = encodePackedPrefixes(withdrawnRoutes);
98 + message.writeShort(encodedPrefixes.readableBytes());
99 + message.writeBytes(encodedPrefixes);
100 +
101 + // Encode the Path Attributes
102 + // ORIGIN: IGP
103 + attrFlags = 0x40; // Transitive flag
104 + pathAttributes.writeByte(attrFlags);
105 + pathAttributes.writeByte(BgpConstants.Update.Origin.TYPE);
106 + pathAttributes.writeByte(1); // Data length
107 + pathAttributes.writeByte(BgpConstants.Update.Origin.IGP);
108 + // AS_PATH: Two Path Segments of 3 ASes each
109 + attrFlags = 0x40; // Transitive flag
110 + pathAttributes.writeByte(attrFlags);
111 + pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE);
112 + pathAttributes.writeByte(16); // Data length
113 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
114 + pathAttributes.writeByte(pathSegmentType1);
115 + pathAttributes.writeByte(3); // Three ASes
116 + pathAttributes.writeShort(65010); // AS=65010
117 + pathAttributes.writeShort(65020); // AS=65020
118 + pathAttributes.writeShort(65030); // AS=65030
119 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
120 + pathAttributes.writeByte(pathSegmentType2);
121 + pathAttributes.writeByte(3); // Three ASes
122 + pathAttributes.writeShort(65041); // AS=65041
123 + pathAttributes.writeShort(65042); // AS=65042
124 + pathAttributes.writeShort(65043); // AS=65043
125 + // NEXT_HOP: nextHopRouter
126 + attrFlags = 0x40; // Transitive flag
127 + pathAttributes.writeByte(attrFlags);
128 + pathAttributes.writeByte(BgpConstants.Update.NextHop.TYPE);
129 + pathAttributes.writeByte(4); // Data length
130 + pathAttributes.writeInt(nextHopRouter.toInt()); // Next-hop router
131 + // LOCAL_PREF: localPref
132 + attrFlags = 0x40; // Transitive flag
133 + pathAttributes.writeByte(attrFlags);
134 + pathAttributes.writeByte(BgpConstants.Update.LocalPref.TYPE);
135 + pathAttributes.writeByte(4); // Data length
136 + pathAttributes.writeInt((int) localPref); // Preference value
137 + // MULTI_EXIT_DISC: multiExitDisc
138 + attrFlags = 0x80; // Optional
139 + // Non-Transitive flag
140 + pathAttributes.writeByte(attrFlags);
141 + pathAttributes.writeByte(BgpConstants.Update.MultiExitDisc.TYPE);
142 + pathAttributes.writeByte(4); // Data length
143 + pathAttributes.writeInt((int) multiExitDisc); // Preference value
144 + // The NLRI prefixes
145 + encodedPrefixes = encodePackedPrefixes(addedRoutes);
146 +
147 + // Write the Path Attributes, beginning with its length
148 + message.writeShort(pathAttributes.readableBytes());
149 + message.writeBytes(pathAttributes);
150 + message.writeBytes(encodedPrefixes);
151 +
152 + return prepareBgpMessage(BgpConstants.BGP_TYPE_UPDATE, message);
153 + }
154 +
155 + /**
156 + * Encodes a collection of IPv4 network prefixes in a packed format.
157 + * <p>
158 + * The IPv4 prefixes are encoded in the form:
159 + * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
160 + * and Prefix is the IPv4 prefix (padded with trailing bits to the end
161 + * of an octet).
162 + *
163 + * @param prefixes the prefixes to encode
164 + * @return the buffer with the encoded prefixes
165 + */
166 + private ChannelBuffer encodePackedPrefixes(Collection<IpPrefix> prefixes) {
167 + ChannelBuffer message =
168 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
169 +
170 + // Write each of the prefixes
171 + for (IpPrefix prefix : prefixes) {
172 + int prefixBitlen = prefix.prefixLength();
173 + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
174 + message.writeByte(prefixBitlen);
175 +
176 + IpAddress address = prefix.toIpAddress();
177 + long value = address.toInt() & 0xffffffffL;
178 + for (int i = 0; i < IpAddress.INET_LEN; i++) {
179 + if (prefixBytelen-- == 0) {
180 + break;
181 + }
182 + long nextByte =
183 + (value >> ((IpAddress.INET_LEN - i - 1) * 8)) & 0xff;
184 + message.writeByte((int) nextByte);
185 + }
186 + }
187 +
188 + return message;
189 + }
190 +
191 + /**
192 + * Prepares BGP KEEPALIVE message.
193 + *
194 + * @return the message to transmit (BGP header included)
195 + */
196 + ChannelBuffer prepareBgpKeepalive() {
197 + ChannelBuffer message =
198 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
199 + return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
200 + }
201 +
202 + /**
203 + * Prepares BGP NOTIFICATION message.
204 + *
205 + * @param errorCode the BGP NOTIFICATION Error Code
206 + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
207 + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
208 + * @param payload the BGP NOTIFICATION Data if applicable, otherwise null
209 + * @return the message to transmit (BGP header included)
210 + */
211 + ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
212 + ChannelBuffer data) {
213 + ChannelBuffer message =
214 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
215 + // Prepare the NOTIFICATION message payload
216 + message.writeByte(errorCode);
217 + message.writeByte(errorSubcode);
218 + if (data != null) {
219 + message.writeBytes(data);
220 + }
221 + return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
222 + }
223 +
224 + /**
225 + * Prepares BGP message.
226 + *
227 + * @param type the BGP message type
228 + * @param payload the message payload to transmit (BGP header excluded)
229 + * @return the message to transmit (BGP header included)
230 + */
231 + private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
232 + ChannelBuffer message =
233 + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
234 + payload.readableBytes());
235 +
236 + // Write the marker
237 + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
238 + message.writeByte(0xff);
239 + }
240 +
241 + // Write the rest of the BGP header
242 + message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
243 + payload.readableBytes());
244 + message.writeByte(type);
245 +
246 + // Write the payload
247 + message.writeBytes(payload);
248 + return message;
249 + }
250 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.util.concurrent.CountDownLatch;
4 +
5 +import org.jboss.netty.buffer.ChannelBuffer;
6 +import org.jboss.netty.channel.Channel;
7 +import org.jboss.netty.channel.ChannelHandlerContext;
8 +import org.jboss.netty.handler.codec.frame.FrameDecoder;
9 +import org.onlab.packet.IpAddress;
10 +
11 +/**
12 + * Class for handling the decoding of the BGP messages at the remote
13 + * BGP peer session.
14 + */
15 +class TestBgpPeerFrameDecoder extends FrameDecoder {
16 + int remoteBgpVersion; // 1 octet
17 + long remoteAs; // 2 octets
18 + long remoteHoldtime; // 2 octets
19 + IpAddress remoteBgpIdentifier; // 4 octets -> IPv4 address
20 +
21 + final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1);
22 + final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1);
23 +
24 + @Override
25 + protected Object decode(ChannelHandlerContext ctx,
26 + Channel channel,
27 + ChannelBuffer buf) throws Exception {
28 + // Test for minimum length of the BGP message
29 + if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
30 + // No enough data received
31 + return null;
32 + }
33 +
34 + //
35 + // Mark the current buffer position in case we haven't received
36 + // the whole message.
37 + //
38 + buf.markReaderIndex();
39 +
40 + //
41 + // Read and check the BGP message Marker field: it must be all ones
42 + //
43 + byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
44 + buf.readBytes(marker);
45 + for (int i = 0; i < marker.length; i++) {
46 + if (marker[i] != (byte) 0xff) {
47 + // ERROR: Connection Not Synchronized. Close the channel.
48 + ctx.getChannel().close();
49 + return null;
50 + }
51 + }
52 +
53 + //
54 + // Read and check the BGP message Length field
55 + //
56 + int length = buf.readUnsignedShort();
57 + if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
58 + (length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
59 + // ERROR: Bad Message Length. Close the channel.
60 + ctx.getChannel().close();
61 + return null;
62 + }
63 +
64 + //
65 + // Test whether the rest of the message is received:
66 + // So far we have read the Marker (16 octets) and the
67 + // Length (2 octets) fields.
68 + //
69 + int remainingMessageLen =
70 + length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
71 + if (buf.readableBytes() < remainingMessageLen) {
72 + // No enough data received
73 + buf.resetReaderIndex();
74 + return null;
75 + }
76 +
77 + //
78 + // Read the BGP message Type field, and process based on that type
79 + //
80 + int type = buf.readUnsignedByte();
81 + remainingMessageLen--; // Adjust after reading the type
82 + ChannelBuffer message = buf.readBytes(remainingMessageLen);
83 +
84 + //
85 + // Process the remaining of the message based on the message type
86 + //
87 + switch (type) {
88 + case BgpConstants.BGP_TYPE_OPEN:
89 + processBgpOpen(ctx, message);
90 + break;
91 + case BgpConstants.BGP_TYPE_UPDATE:
92 + // NOTE: Not used as part of the test, because ONOS does not
93 + // originate UPDATE messages.
94 + break;
95 + case BgpConstants.BGP_TYPE_NOTIFICATION:
96 + // NOTE: Not used as part of the testing (yet)
97 + break;
98 + case BgpConstants.BGP_TYPE_KEEPALIVE:
99 + processBgpKeepalive(ctx, message);
100 + break;
101 + default:
102 + // ERROR: Bad Message Type. Close the channel.
103 + ctx.getChannel().close();
104 + return null;
105 + }
106 +
107 + return null;
108 + }
109 +
110 + /**
111 + * Processes BGP OPEN message.
112 + *
113 + * @param ctx the Channel Handler Context.
114 + * @param message the message to process.
115 + */
116 + private void processBgpOpen(ChannelHandlerContext ctx,
117 + ChannelBuffer message) {
118 + int minLength =
119 + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
120 + if (message.readableBytes() < minLength) {
121 + // ERROR: Bad Message Length. Close the channel.
122 + ctx.getChannel().close();
123 + return;
124 + }
125 +
126 + //
127 + // Parse the OPEN message
128 + //
129 + remoteBgpVersion = message.readUnsignedByte();
130 + remoteAs = message.readUnsignedShort();
131 + remoteHoldtime = message.readUnsignedShort();
132 + remoteBgpIdentifier = IpAddress.valueOf((int) message.readUnsignedInt());
133 + // Optional Parameters
134 + int optParamLen = message.readUnsignedByte();
135 + if (message.readableBytes() < optParamLen) {
136 + // ERROR: Bad Message Length. Close the channel.
137 + ctx.getChannel().close();
138 + return;
139 + }
140 + message.readBytes(optParamLen); // NOTE: data ignored
141 +
142 + // BGP OPEN message successfully received
143 + receivedOpenMessageLatch.countDown();
144 + }
145 +
146 + /**
147 + * Processes BGP KEEPALIVE message.
148 + *
149 + * @param ctx the Channel Handler Context.
150 + * @param message the message to process.
151 + */
152 + private void processBgpKeepalive(ChannelHandlerContext ctx,
153 + ChannelBuffer message) {
154 + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
155 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
156 + // ERROR: Bad Message Length. Close the channel.
157 + ctx.getChannel().close();
158 + return;
159 + }
160 + // BGP KEEPALIVE message successfully received
161 + receivedKeepaliveMessageLatch.countDown();
162 + }
163 +}
1 +package org.onlab.util;
2 +
3 +import java.lang.reflect.Constructor;
4 +import java.lang.reflect.Field;
5 +import java.lang.reflect.InvocationTargetException;
6 +import java.lang.reflect.Method;
7 +
8 +
9 +/**
10 + * Utilities for testing.
11 + */
12 +public final class TestUtils {
13 +
14 + /**
15 + * Sets the field, bypassing scope restriction.
16 + *
17 + * @param subject Object where the field belongs
18 + * @param fieldName name of the field to set
19 + * @param value value to set to the field.
20 + * @param <T> subject type
21 + * @param <U> value type
22 + * @throws TestUtilsException if there are reflection errors while setting
23 + * the field
24 + */
25 + public static <T, U> void setField(T subject, String fieldName, U value)
26 + throws TestUtilsException {
27 + @SuppressWarnings("unchecked")
28 + Class<T> clazz = (Class<T>) subject.getClass();
29 + try {
30 + Field field = clazz.getDeclaredField(fieldName);
31 + field.setAccessible(true);
32 + field.set(subject, value);
33 + } catch (NoSuchFieldException | SecurityException |
34 + IllegalArgumentException | IllegalAccessException e) {
35 + throw new TestUtilsException("setField failed", e);
36 + }
37 + }
38 +
39 + /**
40 + * Gets the field, bypassing scope restriction.
41 + *
42 + * @param subject Object where the field belongs
43 + * @param fieldName name of the field to get
44 + * @return value of the field.
45 + * @param <T> subject type
46 + * @param <U> field value type
47 + * @throws TestUtilsException if there are reflection errors while getting
48 + * the field
49 + */
50 + public static <T, U> U getField(T subject, String fieldName)
51 + throws TestUtilsException {
52 + try {
53 + @SuppressWarnings("unchecked")
54 + Class<T> clazz = (Class<T>) subject.getClass();
55 + Field field = clazz.getDeclaredField(fieldName);
56 + field.setAccessible(true);
57 +
58 + @SuppressWarnings("unchecked")
59 + U result = (U) field.get(subject);
60 + return result;
61 + } catch (NoSuchFieldException | SecurityException |
62 + IllegalArgumentException | IllegalAccessException e) {
63 + throw new TestUtilsException("getField failed", e);
64 + }
65 + }
66 +
67 + /**
68 + * Calls the method, bypassing scope restriction.
69 + *
70 + * @param subject Object where the method belongs
71 + * @param methodName name of the method to call
72 + * @param paramTypes formal parameter type array
73 + * @param args arguments
74 + * @return return value or null if void
75 + * @param <T> subject type
76 + * @param <U> return value type
77 + * @throws TestUtilsException if there are reflection errors while calling
78 + * the method
79 + */
80 + public static <T, U> U callMethod(T subject, String methodName,
81 + Class<?>[] paramTypes, Object...args) throws TestUtilsException {
82 +
83 + try {
84 + @SuppressWarnings("unchecked")
85 + Class<T> clazz = (Class<T>) subject.getClass();
86 + final Method method;
87 + if (paramTypes == null || paramTypes.length == 0) {
88 + method = clazz.getDeclaredMethod(methodName);
89 + } else {
90 + method = clazz.getDeclaredMethod(methodName, paramTypes);
91 + }
92 + method.setAccessible(true);
93 +
94 + @SuppressWarnings("unchecked")
95 + U result = (U) method.invoke(subject, args);
96 + return result;
97 + } catch (NoSuchMethodException | SecurityException |
98 + IllegalAccessException | IllegalArgumentException |
99 + InvocationTargetException e) {
100 + throw new TestUtilsException("callMethod failed", e);
101 + }
102 + }
103 +
104 + /**
105 + * Calls the method, bypassing scope restriction.
106 + *
107 + * @param subject Object where the method belongs
108 + * @param methodName name of the method to call
109 + * @param paramType formal parameter type
110 + * @param arg argument
111 + * @return return value or null if void
112 + * @param <T> subject type
113 + * @param <U> return value type
114 + * @throws TestUtilsException if there are reflection errors while calling
115 + * the method
116 + */
117 + public static <T, U> U callMethod(T subject, String methodName,
118 + Class<?> paramType, Object arg) throws TestUtilsException {
119 + return callMethod(subject, methodName, new Class<?>[]{paramType}, arg);
120 + }
121 +
122 + /**
123 + * Triggers an allocation of an object of type <T> and forces a call to
124 + * the private constructor.
125 + *
126 + * @param constructor Constructor to call
127 + * @param <T> type of the object to create
128 + * @return created object of type <T>
129 + * @throws TestUtilsException if there are reflection errors while calling
130 + * the constructor
131 + */
132 + public static <T> T callConstructor(Constructor<T> constructor)
133 + throws TestUtilsException {
134 + try {
135 + constructor.setAccessible(true);
136 + return constructor.newInstance();
137 + } catch (InstantiationException | IllegalAccessException |
138 + InvocationTargetException error) {
139 + throw new TestUtilsException("callConstructor failed", error);
140 + }
141 + }
142 +
143 + /**
144 + * Avoid instantiation.
145 + */
146 + private TestUtils() {}
147 +
148 + /**
149 + * Exception that can be thrown if problems are encountered while executing
150 + * the utility method. These are usually problems accessing fields/methods
151 + * through reflection. The original exception can be found by examining the
152 + * cause.
153 + */
154 + public static class TestUtilsException extends Exception {
155 +
156 + private static final long serialVersionUID = 1L;
157 +
158 + /**
159 + * Constructs a new exception with the specified detail message and
160 + * cause.
161 + *
162 + * @param message the detail message
163 + * @param cause the original cause of this exception
164 + */
165 + public TestUtilsException(String message, Throwable cause) {
166 + super(message, cause);
167 + }
168 + }
169 +}
1 +package org.onlab.util;
2 +
3 +import static org.junit.Assert.assertArrayEquals;
4 +import static org.junit.Assert.assertEquals;
5 +import static org.junit.Assert.assertNull;
6 +
7 +import org.junit.Before;
8 +import org.junit.Test;
9 +import org.onlab.util.TestUtils.TestUtilsException;
10 +
11 +/**
12 + * Test and usage examples for TestUtils.
13 + */
14 +public class TestUtilsTest {
15 +
16 + /**
17 + * Test data.
18 + */
19 + private static final class TestClass {
20 +
21 + @SuppressWarnings("unused")
22 + private int privateField = 42;
23 +
24 + @SuppressWarnings("unused")
25 + protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE
26 +
27 + /**
28 + * Protected method with multiple argument.
29 + *
30 + * @param x simply returns
31 + * @param y not used
32 + * @return x
33 + */
34 + @SuppressWarnings("unused")
35 + private int privateMethod(Number x, Long y) {
36 + return x.intValue();
37 + }
38 +
39 + /**
40 + * Protected method with no argument.
41 + *
42 + * @return int
43 + */
44 + @SuppressWarnings("unused")
45 + protected int protectedMethod() {
46 + return 42;
47 + }
48 +
49 + /**
50 + * Method returning array.
51 + *
52 + * @param ary random array
53 + * @return ary
54 + */
55 + @SuppressWarnings("unused")
56 + private int[] arrayReturnMethod(int[] ary) {
57 + return ary;
58 + }
59 +
60 + /**
61 + * Method without return value.
62 + *
63 + * @param s ignored
64 + */
65 + @SuppressWarnings("unused")
66 + private void voidMethod(String s) {
67 + System.out.println(s);
68 + }
69 + }
70 +
71 + private TestClass test;
72 +
73 + /**
74 + * Sets up the test fixture.
75 + */
76 + @Before
77 + public void setUp() {
78 + test = new TestClass();
79 + }
80 +
81 + /**
82 + * Example to access private field.
83 + *
84 + * @throws TestUtilsException TestUtils error
85 + */
86 + @Test
87 + public void testSetGetPrivateField() throws TestUtilsException {
88 +
89 + assertEquals(42, TestUtils.getField(test, "privateField"));
90 + TestUtils.setField(test, "privateField", 0xDEAD);
91 + assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
92 + }
93 +
94 + /**
95 + * Example to access protected field.
96 + *
97 + * @throws TestUtilsException TestUtils error
98 + */
99 + @Test
100 + public void testSetGetProtectedField() throws TestUtilsException {
101 +
102 + assertEquals(2501, TestUtils.getField(test, "protectedField"));
103 + TestUtils.setField(test, "protectedField", 0xBEEF);
104 + assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
105 + }
106 +
107 + /**
108 + * Example to call private method and multiple parameters.
109 + * <p/>
110 + * It also illustrates that paramTypes must match declared type,
111 + * not the runtime types of arguments.
112 + *
113 + * @throws TestUtilsException TestUtils error
114 + */
115 + @Test
116 + public void testCallPrivateMethod() throws TestUtilsException {
117 +
118 + int result = TestUtils.callMethod(test, "privateMethod",
119 + new Class<?>[] {Number.class, Long.class},
120 + Long.valueOf(42), Long.valueOf(32));
121 + assertEquals(42, result);
122 + }
123 +
124 + /**
125 + * Example to call protected method and no parameters.
126 + *
127 + * @throws TestUtilsException TestUtils error
128 + */
129 + @Test
130 + public void testCallProtectedMethod() throws TestUtilsException {
131 +
132 + int result = TestUtils.callMethod(test, "protectedMethod",
133 + new Class<?>[] {});
134 + assertEquals(42, result);
135 + }
136 +
137 + /**
138 + * Example to call method returning array.
139 + * <p/>
140 + * Note: It is not required to receive as Object.
141 + * Following is just verifying it is not Boxed arrays.
142 + *
143 + * @throws TestUtilsException TestUtils error
144 + */
145 + @Test
146 + public void testCallArrayReturnMethod() throws TestUtilsException {
147 +
148 + int[] array = {1, 2, 3};
149 + Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod",
150 + new Class<?>[] {int[].class}, array);
151 + assertEquals(int[].class, aryResult.getClass());
152 + assertArrayEquals(array, (int[]) aryResult);
153 + }
154 +
155 +
156 + /**
157 + * Example to call void returning method.
158 + * <p/>
159 + * Note: Return value will be null for void methods.
160 + *
161 + * @throws TestUtilsException TestUtils error
162 + */
163 + @Test
164 + public void testCallVoidReturnMethod() throws TestUtilsException {
165 +
166 + Object voidResult = TestUtils.callMethod(test, "voidMethod",
167 + String.class, "foobar");
168 + assertNull(voidResult);
169 + }
170 +}