Jonathan Hart

Ported BGP tests from old codebase

...@@ -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.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 +}