Jonathan Hart

Port the BGP implementation of SDN-IP.

...@@ -126,6 +126,8 @@ public class Router implements RouteListener { ...@@ -126,6 +126,8 @@ public class Router implements RouteListener {
126 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor( 126 bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
127 new ThreadFactoryBuilder() 127 new ThreadFactoryBuilder()
128 .setNameFormat("bgp-intents-synchronizer-%d").build()); 128 .setNameFormat("bgp-intents-synchronizer-%d").build());
129 +
130 + this.hostService.addListener(new InternalHostListener());
129 } 131 }
130 132
131 /** 133 /**
......
...@@ -10,6 +10,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -10,6 +10,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
10 import org.onlab.onos.net.host.HostService; 10 import org.onlab.onos.net.host.HostService;
11 import org.onlab.onos.net.intent.IntentService; 11 import org.onlab.onos.net.intent.IntentService;
12 import org.onlab.onos.sdnip.RouteUpdate.Type; 12 import org.onlab.onos.sdnip.RouteUpdate.Type;
13 +import org.onlab.onos.sdnip.bgp.BgpSessionManager;
13 import org.onlab.onos.sdnip.config.SdnIpConfigReader; 14 import org.onlab.onos.sdnip.config.SdnIpConfigReader;
14 import org.onlab.packet.IpAddress; 15 import org.onlab.packet.IpAddress;
15 import org.onlab.packet.IpPrefix; 16 import org.onlab.packet.IpPrefix;
...@@ -32,6 +33,7 @@ public class SdnIp { ...@@ -32,6 +33,7 @@ public class SdnIp {
32 private SdnIpConfigReader config; 33 private SdnIpConfigReader config;
33 private PeerConnectivity peerConnectivity; 34 private PeerConnectivity peerConnectivity;
34 private Router router; 35 private Router router;
36 + private BgpSessionManager bgpSessionManager;
35 37
36 @Activate 38 @Activate
37 protected void activate() { 39 protected void activate() {
...@@ -48,6 +50,9 @@ public class SdnIp { ...@@ -48,6 +50,9 @@ public class SdnIp {
48 router = new Router(intentService, hostService, config, interfaceService); 50 router = new Router(intentService, hostService, config, interfaceService);
49 router.start(); 51 router.start();
50 52
53 + bgpSessionManager = new BgpSessionManager(router);
54 + bgpSessionManager.startUp(2000); // TODO
55 +
51 // TODO need to disable link discovery on external ports 56 // TODO need to disable link discovery on external ports
52 57
53 router.update(new RouteUpdate(Type.UPDATE, new RouteEntry( 58 router.update(new RouteUpdate(Type.UPDATE, new RouteEntry(
......
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +/**
4 + * BGP related constants.
5 + */
6 +public final class BgpConstants {
7 + /**
8 + * Default constructor.
9 + * <p>
10 + * The constructor is private to prevent creating an instance of
11 + * this utility class.
12 + */
13 + private BgpConstants() {
14 + }
15 +
16 + /** BGP port number (RFC 4271). */
17 + public static final int BGP_PORT = 179;
18 +
19 + /** BGP version. */
20 + public static final int BGP_VERSION = 4;
21 +
22 + /** BGP OPEN message type. */
23 + public static final int BGP_TYPE_OPEN = 1;
24 +
25 + /** BGP UPDATE message type. */
26 + public static final int BGP_TYPE_UPDATE = 2;
27 +
28 + /** BGP NOTIFICATION message type. */
29 + public static final int BGP_TYPE_NOTIFICATION = 3;
30 +
31 + /** BGP KEEPALIVE message type. */
32 + public static final int BGP_TYPE_KEEPALIVE = 4;
33 +
34 + /** BGP Header Marker field length. */
35 + public static final int BGP_HEADER_MARKER_LENGTH = 16;
36 +
37 + /** BGP Header length. */
38 + public static final int BGP_HEADER_LENGTH = 19;
39 +
40 + /** BGP message maximum length. */
41 + public static final int BGP_MESSAGE_MAX_LENGTH = 4096;
42 +
43 + /** BGP OPEN message minimum length (BGP Header included). */
44 + public static final int BGP_OPEN_MIN_LENGTH = 29;
45 +
46 + /** BGP UPDATE message minimum length (BGP Header included). */
47 + public static final int BGP_UPDATE_MIN_LENGTH = 23;
48 +
49 + /** BGP NOTIFICATION message minimum length (BGP Header included). */
50 + public static final int BGP_NOTIFICATION_MIN_LENGTH = 21;
51 +
52 + /** BGP KEEPALIVE message expected length (BGP Header included). */
53 + public static final int BGP_KEEPALIVE_EXPECTED_LENGTH = 19;
54 +
55 + /** BGP KEEPALIVE messages transmitted per Hold interval. */
56 + public static final int BGP_KEEPALIVE_PER_HOLD_INTERVAL = 3;
57 +
58 + /** BGP KEEPALIVE messages minimum Holdtime (in seconds). */
59 + public static final int BGP_KEEPALIVE_MIN_HOLDTIME = 3;
60 +
61 + /** BGP KEEPALIVE messages minimum transmission interval (in seconds). */
62 + public static final int BGP_KEEPALIVE_MIN_INTERVAL = 1;
63 +
64 + /** BGP AS 0 (zero) value. See draft-ietf-idr-as0-06.txt Internet Draft. */
65 + public static final long BGP_AS_0 = 0;
66 +
67 + /**
68 + * BGP UPDATE related constants.
69 + */
70 + public static final class Update {
71 + /**
72 + * Default constructor.
73 + * <p>
74 + * The constructor is private to prevent creating an instance of
75 + * this utility class.
76 + */
77 + private Update() {
78 + }
79 +
80 + /**
81 + * BGP UPDATE: ORIGIN related constants.
82 + */
83 + public static final class Origin {
84 + /**
85 + * Default constructor.
86 + * <p>
87 + * The constructor is private to prevent creating an instance of
88 + * this utility class.
89 + */
90 + private Origin() {
91 + }
92 +
93 + /** BGP UPDATE Attributes Type Code ORIGIN. */
94 + public static final int TYPE = 1;
95 +
96 + /** BGP UPDATE Attributes Type Code ORIGIN length. */
97 + public static final int LENGTH = 1;
98 +
99 + /** BGP UPDATE ORIGIN: IGP. */
100 + public static final int IGP = 0;
101 +
102 + /** BGP UPDATE ORIGIN: EGP. */
103 + public static final int EGP = 1;
104 +
105 + /** BGP UPDATE ORIGIN: INCOMPLETE. */
106 + public static final int INCOMPLETE = 2;
107 + }
108 +
109 + /**
110 + * BGP UPDATE: AS_PATH related constants.
111 + */
112 + public static final class AsPath {
113 + /**
114 + * Default constructor.
115 + * <p>
116 + * The constructor is private to prevent creating an instance of
117 + * this utility class.
118 + */
119 + private AsPath() {
120 + }
121 +
122 + /** BGP UPDATE Attributes Type Code AS_PATH. */
123 + public static final int TYPE = 2;
124 +
125 + /** BGP UPDATE AS_PATH Type: AS_SET. */
126 + public static final int AS_SET = 1;
127 +
128 + /** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
129 + public static final int AS_SEQUENCE = 2;
130 + }
131 +
132 + /**
133 + * BGP UPDATE: NEXT_HOP related constants.
134 + */
135 + public static final class NextHop {
136 + /**
137 + * Default constructor.
138 + * <p>
139 + * The constructor is private to prevent creating an instance of
140 + * this utility class.
141 + */
142 + private NextHop() {
143 + }
144 +
145 + /** BGP UPDATE Attributes Type Code NEXT_HOP. */
146 + public static final int TYPE = 3;
147 +
148 + /** BGP UPDATE Attributes Type Code NEXT_HOP length. */
149 + public static final int LENGTH = 4;
150 + }
151 +
152 + /**
153 + * BGP UPDATE: MULTI_EXIT_DISC related constants.
154 + */
155 + public static final class MultiExitDisc {
156 + /**
157 + * Default constructor.
158 + * <p>
159 + * The constructor is private to prevent creating an instance of
160 + * this utility class.
161 + */
162 + private MultiExitDisc() {
163 + }
164 +
165 + /** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC. */
166 + public static final int TYPE = 4;
167 +
168 + /** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC length. */
169 + public static final int LENGTH = 4;
170 +
171 + /** BGP UPDATE Attributes lowest MULTI_EXIT_DISC value. */
172 + public static final int LOWEST_MULTI_EXIT_DISC = 0;
173 + }
174 +
175 + /**
176 + * BGP UPDATE: LOCAL_PREF related constants.
177 + */
178 + public static final class LocalPref {
179 + /**
180 + * Default constructor.
181 + * <p>
182 + * The constructor is private to prevent creating an instance of
183 + * this utility class.
184 + */
185 + private LocalPref() {
186 + }
187 +
188 + /** BGP UPDATE Attributes Type Code LOCAL_PREF. */
189 + public static final int TYPE = 5;
190 +
191 + /** BGP UPDATE Attributes Type Code LOCAL_PREF length. */
192 + public static final int LENGTH = 4;
193 + }
194 +
195 + /**
196 + * BGP UPDATE: ATOMIC_AGGREGATE related constants.
197 + */
198 + public static final class AtomicAggregate {
199 + /**
200 + * Default constructor.
201 + * <p>
202 + * The constructor is private to prevent creating an instance of
203 + * this utility class.
204 + */
205 + private AtomicAggregate() {
206 + }
207 +
208 + /** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE. */
209 + public static final int TYPE = 6;
210 +
211 + /** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE length. */
212 + public static final int LENGTH = 0;
213 + }
214 +
215 + /**
216 + * BGP UPDATE: AGGREGATOR related constants.
217 + */
218 + public static final class Aggregator {
219 + /**
220 + * Default constructor.
221 + * <p>
222 + * The constructor is private to prevent creating an instance of
223 + * this utility class.
224 + */
225 + private Aggregator() {
226 + }
227 +
228 + /** BGP UPDATE Attributes Type Code AGGREGATOR. */
229 + public static final int TYPE = 7;
230 +
231 + /** BGP UPDATE Attributes Type Code AGGREGATOR length. */
232 + public static final int LENGTH = 6;
233 + }
234 + }
235 +
236 + /**
237 + * BGP NOTIFICATION related constants.
238 + */
239 + public static final class Notifications {
240 + /**
241 + * Default constructor.
242 + * <p>
243 + * The constructor is private to prevent creating an instance of
244 + * this utility class.
245 + */
246 + private Notifications() {
247 + }
248 +
249 + /**
250 + * BGP NOTIFICATION: Message Header Error constants.
251 + */
252 + public static final class MessageHeaderError {
253 + /**
254 + * Default constructor.
255 + * <p>
256 + * The constructor is private to prevent creating an instance of
257 + * this utility class.
258 + */
259 + private MessageHeaderError() {
260 + }
261 +
262 + /** Message Header Error code. */
263 + public static final int ERROR_CODE = 1;
264 +
265 + /** Message Header Error subcode: Connection Not Synchronized. */
266 + public static final int CONNECTION_NOT_SYNCHRONIZED = 1;
267 +
268 + /** Message Header Error subcode: Bad Message Length. */
269 + public static final int BAD_MESSAGE_LENGTH = 2;
270 +
271 + /** Message Header Error subcode: Bad Message Type. */
272 + public static final int BAD_MESSAGE_TYPE = 3;
273 + }
274 +
275 + /**
276 + * BGP NOTIFICATION: OPEN Message Error constants.
277 + */
278 + public static final class OpenMessageError {
279 + /**
280 + * Default constructor.
281 + * <p>
282 + * The constructor is private to prevent creating an instance of
283 + * this utility class.
284 + */
285 + private OpenMessageError() {
286 + }
287 +
288 + /** OPEN Message Error code. */
289 + public static final int ERROR_CODE = 2;
290 +
291 + /** OPEN Message Error subcode: Unsupported Version Number. */
292 + public static final int UNSUPPORTED_VERSION_NUMBER = 1;
293 +
294 + /** OPEN Message Error subcode: Bad PEER AS. */
295 + public static final int BAD_PEER_AS = 2;
296 +
297 + /** OPEN Message Error subcode: Unacceptable Hold Time. */
298 + public static final int UNACCEPTABLE_HOLD_TIME = 6;
299 + }
300 +
301 + /**
302 + * BGP NOTIFICATION: UPDATE Message Error constants.
303 + */
304 + public static final class UpdateMessageError {
305 + /**
306 + * Default constructor.
307 + * <p>
308 + * The constructor is private to prevent creating an instance of
309 + * this utility class.
310 + */
311 + private UpdateMessageError() {
312 + }
313 +
314 + /** UPDATE Message Error code. */
315 + public static final int ERROR_CODE = 3;
316 +
317 + /** UPDATE Message Error subcode: Malformed Attribute List. */
318 + public static final int MALFORMED_ATTRIBUTE_LIST = 1;
319 +
320 + /** UPDATE Message Error subcode: Unrecognized Well-known Attribute. */
321 + public static final int UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2;
322 +
323 + /** UPDATE Message Error subcode: Missing Well-known Attribute. */
324 + public static final int MISSING_WELL_KNOWN_ATTRIBUTE = 3;
325 +
326 + /** UPDATE Message Error subcode: Attribute Flags Error. */
327 + public static final int ATTRIBUTE_FLAGS_ERROR = 4;
328 +
329 + /** UPDATE Message Error subcode: Attribute Length Error. */
330 + public static final int ATTRIBUTE_LENGTH_ERROR = 5;
331 +
332 + /** UPDATE Message Error subcode: Invalid ORIGIN Attribute. */
333 + public static final int INVALID_ORIGIN_ATTRIBUTE = 6;
334 +
335 + /** UPDATE Message Error subcode: Invalid NEXT_HOP Attribute. */
336 + public static final int INVALID_NEXT_HOP_ATTRIBUTE = 8;
337 +
338 + /** UPDATE Message Error subcode: Optional Attribute Error. Unused. */
339 + public static final int OPTIONAL_ATTRIBUTE_ERROR = 9;
340 +
341 + /** UPDATE Message Error subcode: Invalid Network Field. */
342 + public static final int INVALID_NETWORK_FIELD = 10;
343 +
344 + /** UPDATE Message Error subcode: Malformed AS_PATH. */
345 + public static final int MALFORMED_AS_PATH = 11;
346 + }
347 +
348 + /**
349 + * BGP NOTIFICATION: Hold Timer Expired constants.
350 + */
351 + public static final class HoldTimerExpired {
352 + /**
353 + * Default constructor.
354 + * <p>
355 + * The constructor is private to prevent creating an instance of
356 + * this utility class.
357 + */
358 + private HoldTimerExpired() {
359 + }
360 +
361 + /** Hold Timer Expired code. */
362 + public static final int ERROR_CODE = 4;
363 + }
364 +
365 + /** BGP NOTIFICATION message Error subcode: Unspecific. */
366 + public static final int ERROR_SUBCODE_UNSPECIFIC = 0;
367 + }
368 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import org.jboss.netty.buffer.ChannelBuffer;
4 +import org.jboss.netty.buffer.ChannelBuffers;
5 +import org.jboss.netty.channel.Channel;
6 +import org.jboss.netty.channel.ChannelHandlerContext;
7 +import org.jboss.netty.handler.codec.frame.FrameDecoder;
8 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
9 +import org.slf4j.Logger;
10 +import org.slf4j.LoggerFactory;
11 +
12 +/**
13 + * Class for handling the decoding of the BGP messages.
14 + */
15 +class BgpFrameDecoder extends FrameDecoder {
16 + private static final Logger log =
17 + LoggerFactory.getLogger(BgpFrameDecoder.class);
18 +
19 + private final BgpSession bgpSession;
20 +
21 + /**
22 + * Constructor for a given BGP Session.
23 + *
24 + * @param bgpSession the BGP session state to use.
25 + */
26 + BgpFrameDecoder(BgpSession bgpSession) {
27 + this.bgpSession = bgpSession;
28 + }
29 +
30 + @Override
31 + protected Object decode(ChannelHandlerContext ctx,
32 + Channel channel,
33 + ChannelBuffer buf) throws Exception {
34 + //
35 + // NOTE: If we close the channel during the decoding, we might still
36 + // see some incoming messages while the channel closing is completed.
37 + //
38 + if (bgpSession.isClosed()) {
39 + return null;
40 + }
41 +
42 + log.trace("BGP Peer: decode(): remoteAddr = {} localAddr = {} " +
43 + "messageSize = {}",
44 + ctx.getChannel().getRemoteAddress(),
45 + ctx.getChannel().getLocalAddress(),
46 + buf.readableBytes());
47 +
48 + // Test for minimum length of the BGP message
49 + if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
50 + // No enough data received
51 + return null;
52 + }
53 +
54 + //
55 + // Mark the current buffer position in case we haven't received
56 + // the whole message.
57 + //
58 + buf.markReaderIndex();
59 +
60 + //
61 + // Read and check the BGP message Marker field: it must be all ones
62 + // (See RFC 4271, Section 4.1)
63 + //
64 + byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
65 + buf.readBytes(marker);
66 + for (int i = 0; i < marker.length; i++) {
67 + if (marker[i] != (byte) 0xff) {
68 + log.debug("BGP RX Error: invalid marker {} at position {}",
69 + marker[i], i);
70 + //
71 + // ERROR: Connection Not Synchronized
72 + //
73 + // Send NOTIFICATION and close the connection
74 + int errorCode = MessageHeaderError.ERROR_CODE;
75 + int errorSubcode =
76 + MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
77 + ChannelBuffer txMessage =
78 + bgpSession.prepareBgpNotification(errorCode, errorSubcode,
79 + null);
80 + ctx.getChannel().write(txMessage);
81 + bgpSession.closeChannel(ctx);
82 + return null;
83 + }
84 + }
85 +
86 + //
87 + // Read and check the BGP message Length field
88 + //
89 + int length = buf.readUnsignedShort();
90 + if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
91 + (length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
92 + log.debug("BGP RX Error: invalid Length field {}. " +
93 + "Must be between {} and {}",
94 + length,
95 + BgpConstants.BGP_HEADER_LENGTH,
96 + BgpConstants.BGP_MESSAGE_MAX_LENGTH);
97 + //
98 + // ERROR: Bad Message Length
99 + //
100 + // Send NOTIFICATION and close the connection
101 + ChannelBuffer txMessage =
102 + bgpSession.prepareBgpNotificationBadMessageLength(length);
103 + ctx.getChannel().write(txMessage);
104 + bgpSession.closeChannel(ctx);
105 + return null;
106 + }
107 +
108 + //
109 + // Test whether the rest of the message is received:
110 + // So far we have read the Marker (16 octets) and the
111 + // Length (2 octets) fields.
112 + //
113 + int remainingMessageLen =
114 + length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
115 + if (buf.readableBytes() < remainingMessageLen) {
116 + // No enough data received
117 + buf.resetReaderIndex();
118 + return null;
119 + }
120 +
121 + //
122 + // Read the BGP message Type field, and process based on that type
123 + //
124 + int type = buf.readUnsignedByte();
125 + remainingMessageLen--; // Adjust after reading the type
126 + ChannelBuffer message = buf.readBytes(remainingMessageLen);
127 +
128 + //
129 + // Process the remaining of the message based on the message type
130 + //
131 + switch (type) {
132 + case BgpConstants.BGP_TYPE_OPEN:
133 + bgpSession.processBgpOpen(ctx, message);
134 + break;
135 + case BgpConstants.BGP_TYPE_UPDATE:
136 + bgpSession.processBgpUpdate(ctx, message);
137 + break;
138 + case BgpConstants.BGP_TYPE_NOTIFICATION:
139 + bgpSession.processBgpNotification(ctx, message);
140 + break;
141 + case BgpConstants.BGP_TYPE_KEEPALIVE:
142 + bgpSession.processBgpKeepalive(ctx, message);
143 + break;
144 + default:
145 + //
146 + // ERROR: Bad Message Type
147 + //
148 + // Send NOTIFICATION and close the connection
149 + int errorCode = MessageHeaderError.ERROR_CODE;
150 + int errorSubcode = MessageHeaderError.BAD_MESSAGE_TYPE;
151 + ChannelBuffer data = ChannelBuffers.buffer(1);
152 + data.writeByte(type);
153 + ChannelBuffer txMessage =
154 + bgpSession.prepareBgpNotification(errorCode, errorSubcode,
155 + data);
156 + ctx.getChannel().write(txMessage);
157 + bgpSession.closeChannel(ctx);
158 + return null;
159 + }
160 + return null;
161 + }
162 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.ArrayList;
6 +import java.util.Objects;
7 +
8 +import org.onlab.onos.sdnip.RouteEntry;
9 +import org.onlab.packet.IpAddress;
10 +import org.onlab.packet.IpPrefix;
11 +
12 +import com.google.common.base.MoreObjects;
13 +
14 +/**
15 + * Represents a route in BGP.
16 + */
17 +public class BgpRouteEntry extends RouteEntry {
18 + private final BgpSession bgpSession; // The BGP Session the route was
19 + // received on
20 + private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
21 + private final AsPath asPath; // The AS Path
22 + private final long localPref; // The local preference for the route
23 + private long multiExitDisc =
24 + BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
25 +
26 + /**
27 + * Class constructor.
28 + *
29 + * @param bgpSession the BGP Session the route was received on
30 + * @param prefix the prefix of the route
31 + * @param nextHop the next hop of the route
32 + * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
33 + * @param asPath the AS path
34 + * @param localPref the route local preference
35 + */
36 + public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
37 + IpAddress nextHop, byte origin,
38 + BgpRouteEntry.AsPath asPath, long localPref) {
39 + super(prefix, nextHop);
40 + this.bgpSession = checkNotNull(bgpSession);
41 + this.origin = origin;
42 + this.asPath = checkNotNull(asPath);
43 + this.localPref = localPref;
44 + }
45 +
46 + /**
47 + * Gets the BGP Session the route was received on.
48 + *
49 + * @return the BGP Session the route was received on
50 + */
51 + public BgpSession getBgpSession() {
52 + return bgpSession;
53 + }
54 +
55 + /**
56 + * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
57 + *
58 + * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
59 + */
60 + public byte getOrigin() {
61 + return origin;
62 + }
63 +
64 + /**
65 + * Gets the route AS path.
66 + *
67 + * @return the route AS path
68 + */
69 + public BgpRouteEntry.AsPath getAsPath() {
70 + return asPath;
71 + }
72 +
73 + /**
74 + * Gets the route local preference.
75 + *
76 + * @return the route local preference
77 + */
78 + public long getLocalPref() {
79 + return localPref;
80 + }
81 +
82 + /**
83 + * Gets the route MED (Multi-Exit Discriminator).
84 + *
85 + * @return the route MED (Multi-Exit Discriminator)
86 + */
87 + public long getMultiExitDisc() {
88 + return multiExitDisc;
89 + }
90 +
91 + /**
92 + * Sets the route MED (Multi-Exit Discriminator).
93 + *
94 + * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
95 + */
96 + void setMultiExitDisc(long multiExitDisc) {
97 + this.multiExitDisc = multiExitDisc;
98 + }
99 +
100 + /**
101 + * Tests whether the route is originated from the local AS.
102 + * <p/>
103 + * The route is considered originated from the local AS if the AS Path
104 + * is empty or if it begins with an AS_SET.
105 + *
106 + * @return true if the route is originated from the local AS, otherwise
107 + * false
108 + */
109 + boolean isLocalRoute() {
110 + if (asPath.getPathSegments().isEmpty()) {
111 + return true;
112 + }
113 + PathSegment firstPathSegment = asPath.getPathSegments().get(0);
114 + if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
115 + return true;
116 + }
117 + return false;
118 + }
119 +
120 + /**
121 + * Gets the BGP Neighbor AS number the route was received from.
122 + * <p/>
123 + * If the router is originated from the local AS, the return value is
124 + * zero (BGP_AS_0).
125 + *
126 + * @return the BGP Neighbor AS number the route was received from.
127 + */
128 + long getNeighborAs() {
129 + if (isLocalRoute()) {
130 + return BgpConstants.BGP_AS_0;
131 + }
132 + PathSegment firstPathSegment = asPath.getPathSegments().get(0);
133 + if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
134 + // TODO: Shouldn't happen. Should check during the parsing.
135 + return BgpConstants.BGP_AS_0;
136 + }
137 + return firstPathSegment.getSegmentAsNumbers().get(0);
138 + }
139 +
140 + /**
141 + * Tests whether the AS Path contains a loop.
142 + * <p/>
143 + * The test is done by comparing whether the AS Path contains the
144 + * local AS number.
145 + *
146 + * @param localAsNumber the local AS number to compare against
147 + * @return true if the AS Path contains a loop, otherwise false
148 + */
149 + boolean hasAsPathLoop(long localAsNumber) {
150 + for (PathSegment pathSegment : asPath.getPathSegments()) {
151 + for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
152 + if (asNumber.equals(localAsNumber)) {
153 + return true;
154 + }
155 + }
156 + }
157 + return false;
158 + }
159 +
160 + /**
161 + * Compares this BGP route against another BGP route by using the
162 + * BGP Decision Process.
163 + * <p/>
164 + * NOTE: The comparison needs to be performed only on routes that have
165 + * same IP Prefix.
166 + *
167 + * @param other the BGP route to compare against
168 + * @return true if this BGP route is better than the other BGP route
169 + * or same, otherwise false
170 + */
171 + boolean isBetterThan(BgpRouteEntry other) {
172 + if (this == other) {
173 + return true; // Return true if same route
174 + }
175 +
176 + // Compare the LOCAL_PREF values: larger is better
177 + if (getLocalPref() != other.getLocalPref()) {
178 + return (getLocalPref() > other.getLocalPref());
179 + }
180 +
181 + // Compare the AS number in the path: smaller is better
182 + if (getAsPath().getAsPathLength() !=
183 + other.getAsPath().getAsPathLength()) {
184 + return getAsPath().getAsPathLength() <
185 + other.getAsPath().getAsPathLength();
186 + }
187 +
188 + // Compare the Origin number: lower is better
189 + if (getOrigin() != other.getOrigin()) {
190 + return (getOrigin() < other.getOrigin());
191 + }
192 +
193 + // Compare the MED if the neighbor AS is same: larger is better
194 + medLabel: {
195 + boolean thisIsLocalRoute = isLocalRoute();
196 + if (thisIsLocalRoute != other.isLocalRoute()) {
197 + break medLabel; // AS number is different
198 + }
199 + if (!thisIsLocalRoute) {
200 + long thisNeighborAs = getNeighborAs();
201 + if (thisNeighborAs != other.getNeighborAs()) {
202 + break medLabel; // AS number is different
203 + }
204 + if (thisNeighborAs == BgpConstants.BGP_AS_0) {
205 + break medLabel; // Invalid AS number
206 + }
207 + }
208 +
209 + // Compare the MED
210 + if (getMultiExitDisc() != other.getMultiExitDisc()) {
211 + return (getMultiExitDisc() > other.getMultiExitDisc());
212 + }
213 + }
214 +
215 + // Compare the peer BGP ID: lower is better
216 + IpAddress peerBgpId = getBgpSession().getRemoteBgpId();
217 + IpAddress otherPeerBgpId = other.getBgpSession().getRemoteBgpId();
218 + if (!peerBgpId.equals(otherPeerBgpId)) {
219 + return (peerBgpId.compareTo(otherPeerBgpId) < 0);
220 + }
221 +
222 + // Compare the peer BGP address: lower is better
223 + IpAddress peerAddress = getBgpSession().getRemoteIp4Address();
224 + IpAddress otherPeerAddress =
225 + other.getBgpSession().getRemoteIp4Address();
226 + if (!peerAddress.equals(otherPeerAddress)) {
227 + return (peerAddress.compareTo(otherPeerAddress) < 0);
228 + }
229 +
230 + return true; // Routes are same. Shouldn't happen?
231 + }
232 +
233 + /**
234 + * A class to represent AS Path Segment.
235 + */
236 + public static class PathSegment {
237 + private final byte type; // Segment type: AS_SET, AS_SEQUENCE
238 + private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
239 +
240 + /**
241 + * Constructor.
242 + *
243 + * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
244 + * @param segmentAsNumbers the Segment AS numbers
245 + */
246 + PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
247 + this.type = type;
248 + this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
249 + }
250 +
251 + /**
252 + * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
253 + *
254 + * @return the Path Segment Type: AS_SET, AS_SEQUENCE
255 + */
256 + public byte getType() {
257 + return type;
258 + }
259 +
260 + /**
261 + * Gets the Path Segment AS Numbers.
262 + *
263 + * @return the Path Segment AS Numbers
264 + */
265 + public ArrayList<Long> getSegmentAsNumbers() {
266 + return segmentAsNumbers;
267 + }
268 +
269 + @Override
270 + public boolean equals(Object other) {
271 + if (this == other) {
272 + return true;
273 + }
274 +
275 + if (!(other instanceof PathSegment)) {
276 + return false;
277 + }
278 +
279 + PathSegment otherPathSegment = (PathSegment) other;
280 + return Objects.equals(this.type, otherPathSegment.type) &&
281 + Objects.equals(this.segmentAsNumbers,
282 + otherPathSegment.segmentAsNumbers);
283 + }
284 +
285 + @Override
286 + public int hashCode() {
287 + return Objects.hash(type, segmentAsNumbers);
288 + }
289 +
290 + @Override
291 + public String toString() {
292 + return MoreObjects.toStringHelper(getClass())
293 + .add("type", this.type)
294 + .add("segmentAsNumbers", this.segmentAsNumbers)
295 + .toString();
296 + }
297 + }
298 +
299 + /**
300 + * A class to represent AS Path.
301 + */
302 + public static class AsPath {
303 + private final ArrayList<PathSegment> pathSegments;
304 + private final int asPathLength; // Precomputed AS Path Length
305 +
306 + /**
307 + * Constructor.
308 + *
309 + * @param pathSegments the Path Segments of the Path
310 + */
311 + AsPath(ArrayList<PathSegment> pathSegments) {
312 + this.pathSegments = checkNotNull(pathSegments);
313 +
314 + //
315 + // Precompute the AS Path Length:
316 + // - AS_SET counts as 1
317 + //
318 + int pl = 0;
319 + for (PathSegment pathSegment : pathSegments) {
320 + if (pathSegment.getType() ==
321 + BgpConstants.Update.AsPath.AS_SET) {
322 + pl++;
323 + continue;
324 + }
325 + pl += pathSegment.getSegmentAsNumbers().size();
326 + }
327 + asPathLength = pl;
328 + }
329 +
330 + /**
331 + * Gets the AS Path Segments.
332 + *
333 + * @return the AS Path Segments
334 + */
335 + public ArrayList<PathSegment> getPathSegments() {
336 + return pathSegments;
337 + }
338 +
339 + /**
340 + * Gets the AS Path Length as considered by the BGP Decision Process.
341 + *
342 + * @return the AS Path Length as considered by the BGP Decision Process
343 + */
344 + int getAsPathLength() {
345 + return asPathLength;
346 + }
347 +
348 + @Override
349 + public boolean equals(Object other) {
350 + if (this == other) {
351 + return true;
352 + }
353 +
354 + if (!(other instanceof AsPath)) {
355 + return false;
356 + }
357 +
358 + AsPath otherAsPath = (AsPath) other;
359 + return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
360 + }
361 +
362 + @Override
363 + public int hashCode() {
364 + return Objects.hash(pathSegments);
365 + }
366 +
367 + @Override
368 + public String toString() {
369 + return MoreObjects.toStringHelper(getClass())
370 + .add("pathSegments", this.pathSegments)
371 + .toString();
372 + }
373 + }
374 +
375 + /**
376 + * Compares whether two objects are equal.
377 + * <p/>
378 + * NOTE: The bgpSession field is excluded from the comparison.
379 + *
380 + * @return true if the two objects are equal, otherwise false.
381 + */
382 + @Override
383 + public boolean equals(Object other) {
384 + if (this == other) {
385 + return true;
386 + }
387 +
388 + //
389 + // NOTE: Subclasses are considered as change of identity, hence
390 + // equals() will return false if the class type doesn't match.
391 + //
392 + if (other == null || getClass() != other.getClass()) {
393 + return false;
394 + }
395 +
396 + if (!super.equals(other)) {
397 + return false;
398 + }
399 +
400 + // NOTE: The bgpSession field is excluded from the comparison
401 + BgpRouteEntry otherRoute = (BgpRouteEntry) other;
402 + return (this.origin == otherRoute.origin) &&
403 + Objects.equals(this.asPath, otherRoute.asPath) &&
404 + (this.localPref == otherRoute.localPref) &&
405 + (this.multiExitDisc == otherRoute.multiExitDisc);
406 + }
407 +
408 + /**
409 + * Computes the hash code.
410 + * <p/>
411 + * NOTE: We return the base class hash code to avoid expensive computation
412 + *
413 + * @return the object hash code
414 + */
415 + @Override
416 + public int hashCode() {
417 + return super.hashCode();
418 + }
419 +
420 + @Override
421 + public String toString() {
422 + return MoreObjects.toStringHelper(getClass())
423 + .add("prefix", prefix())
424 + .add("nextHop", nextHop())
425 + .add("bgpId", bgpSession.getRemoteBgpId())
426 + .add("origin", origin)
427 + .add("asPath", asPath)
428 + .add("localPref", localPref)
429 + .add("multiExitDisc", multiExitDisc)
430 + .toString();
431 + }
432 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.net.InetAddress;
4 +import java.net.InetSocketAddress;
5 +import java.net.SocketAddress;
6 +import java.util.ArrayList;
7 +import java.util.Collection;
8 +import java.util.Collections;
9 +import java.util.HashMap;
10 +import java.util.Map;
11 +import java.util.concurrent.ConcurrentHashMap;
12 +import java.util.concurrent.ConcurrentMap;
13 +import java.util.concurrent.TimeUnit;
14 +
15 +import org.apache.commons.lang3.tuple.Pair;
16 +import org.jboss.netty.buffer.ChannelBuffer;
17 +import org.jboss.netty.buffer.ChannelBuffers;
18 +import org.jboss.netty.channel.ChannelHandlerContext;
19 +import org.jboss.netty.channel.ChannelStateEvent;
20 +import org.jboss.netty.channel.SimpleChannelHandler;
21 +import org.jboss.netty.util.HashedWheelTimer;
22 +import org.jboss.netty.util.Timeout;
23 +import org.jboss.netty.util.Timer;
24 +import org.jboss.netty.util.TimerTask;
25 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications;
26 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired;
27 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
28 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
29 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
30 +import org.onlab.packet.IpAddress;
31 +import org.onlab.packet.IpPrefix;
32 +import org.slf4j.Logger;
33 +import org.slf4j.LoggerFactory;
34 +
35 +/**
36 + * Class for handling the BGP peer sessions.
37 + * There is one instance per each BGP peer session.
38 + */
39 +public class BgpSession extends SimpleChannelHandler {
40 + private static final Logger log =
41 + LoggerFactory.getLogger(BgpSession.class);
42 +
43 + private final BgpSessionManager bgpSessionManager;
44 +
45 + // Local flag to indicate the session is closed.
46 + // It is used to avoid the Netty's asynchronous closing of a channel.
47 + private boolean isClosed = false;
48 +
49 + private SocketAddress remoteAddress; // Peer IP addr/port
50 + private IpAddress remoteIp4Address; // Peer IPv4 address
51 + private int remoteBgpVersion; // 1 octet
52 + private long remoteAs; // 2 octets
53 + private long remoteHoldtime; // 2 octets
54 + private IpAddress remoteBgpId; // 4 octets -> IPv4 address
55 + //
56 + private SocketAddress localAddress; // Local IP addr/port
57 + private IpAddress localIp4Address; // Local IPv4 address
58 + private int localBgpVersion; // 1 octet
59 + private long localAs; // 2 octets
60 + private long localHoldtime; // 2 octets
61 + private IpAddress localBgpId; // 4 octets -> IPv4 address
62 + //
63 + private long localKeepaliveInterval; // Keepalive interval
64 +
65 + // Timers state
66 + private Timer timer = new HashedWheelTimer();
67 + private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
68 + private volatile Timeout sessionTimeout; // Session timeout
69 +
70 + // BGP RIB-IN routing entries from this peer
71 + private ConcurrentMap<IpPrefix, BgpRouteEntry> bgpRibIn =
72 + new ConcurrentHashMap<>();
73 +
74 + /**
75 + * Constructor for a given BGP Session Manager.
76 + *
77 + * @param bgpSessionManager the BGP Session Manager to use
78 + */
79 + BgpSession(BgpSessionManager bgpSessionManager) {
80 + this.bgpSessionManager = bgpSessionManager;
81 + }
82 +
83 + /**
84 + * Gets the BGP RIB-IN routing entries.
85 + *
86 + * @return the BGP RIB-IN routing entries
87 + */
88 + public Collection<BgpRouteEntry> getBgpRibIn() {
89 + return bgpRibIn.values();
90 + }
91 +
92 + /**
93 + * Finds a BGP routing entry in the BGP RIB-IN.
94 + *
95 + * @param prefix the prefix of the route to search for
96 + * @return the BGP routing entry if found, otherwise null
97 + */
98 + public BgpRouteEntry findBgpRouteEntry(IpPrefix prefix) {
99 + return bgpRibIn.get(prefix);
100 + }
101 +
102 + /**
103 + * Gets the BGP session remote address.
104 + *
105 + * @return the BGP session remote address
106 + */
107 + public SocketAddress getRemoteAddress() {
108 + return remoteAddress;
109 + }
110 +
111 + /**
112 + * Gets the BGP session remote IPv4 address.
113 + *
114 + * @return the BGP session remote IPv4 address
115 + */
116 + public IpAddress getRemoteIp4Address() {
117 + return remoteIp4Address;
118 + }
119 +
120 + /**
121 + * Gets the BGP session remote BGP version.
122 + *
123 + * @return the BGP session remote BGP version
124 + */
125 + public int getRemoteBgpVersion() {
126 + return remoteBgpVersion;
127 + }
128 +
129 + /**
130 + * Gets the BGP session remote AS number.
131 + *
132 + * @return the BGP session remote AS number
133 + */
134 + public long getRemoteAs() {
135 + return remoteAs;
136 + }
137 +
138 + /**
139 + * Gets the BGP session remote Holdtime.
140 + *
141 + * @return the BGP session remote Holdtime
142 + */
143 + public long getRemoteHoldtime() {
144 + return remoteHoldtime;
145 + }
146 +
147 + /**
148 + * Gets the BGP session remote BGP Identifier as an IPv4 address.
149 + *
150 + * @return the BGP session remote BGP Identifier as an IPv4 address
151 + */
152 + public IpAddress getRemoteBgpId() {
153 + return remoteBgpId;
154 + }
155 +
156 + /**
157 + * Gets the BGP session local address.
158 + *
159 + * @return the BGP session local address
160 + */
161 + public SocketAddress getLocalAddress() {
162 + return localAddress;
163 + }
164 +
165 + /**
166 + * Gets the BGP session local BGP version.
167 + *
168 + * @return the BGP session local BGP version
169 + */
170 + public int getLocalBgpVersion() {
171 + return localBgpVersion;
172 + }
173 +
174 + /**
175 + * Gets the BGP session local AS number.
176 + *
177 + * @return the BGP session local AS number
178 + */
179 + public long getLocalAs() {
180 + return localAs;
181 + }
182 +
183 + /**
184 + * Gets the BGP session local Holdtime.
185 + *
186 + * @return the BGP session local Holdtime
187 + */
188 + public long getLocalHoldtime() {
189 + return localHoldtime;
190 + }
191 +
192 + /**
193 + * Gets the BGP session local BGP Identifier as an IPv4 address.
194 + *
195 + * @return the BGP session local BGP Identifier as an IPv4 address
196 + */
197 + public IpAddress getLocalBgpId() {
198 + return localBgpId;
199 + }
200 +
201 + /**
202 + * Tests whether the session is closed.
203 + * <p/>
204 + * NOTE: We use this method to avoid the Netty's asynchronous closing
205 + * of a channel.
206 + *
207 + * @param return true if the session is closed
208 + */
209 + boolean isClosed() {
210 + return isClosed;
211 + }
212 +
213 + /**
214 + * Closes the channel.
215 + *
216 + * @param ctx the Channel Handler Context
217 + */
218 + void closeChannel(ChannelHandlerContext ctx) {
219 + isClosed = true;
220 + timer.stop();
221 + ctx.getChannel().close();
222 + }
223 +
224 + @Override
225 + public void channelConnected(ChannelHandlerContext ctx,
226 + ChannelStateEvent channelEvent) {
227 + localAddress = ctx.getChannel().getLocalAddress();
228 + remoteAddress = ctx.getChannel().getRemoteAddress();
229 +
230 + // Assign the local and remote IPv4 addresses
231 + InetAddress inetAddr;
232 + if (localAddress instanceof InetSocketAddress) {
233 + inetAddr = ((InetSocketAddress) localAddress).getAddress();
234 + localIp4Address = IpAddress.valueOf(inetAddr.getAddress());
235 + }
236 + if (remoteAddress instanceof InetSocketAddress) {
237 + inetAddr = ((InetSocketAddress) remoteAddress).getAddress();
238 + remoteIp4Address = IpAddress.valueOf(inetAddr.getAddress());
239 + }
240 +
241 + log.debug("BGP Session Connected from {} on {}",
242 + remoteAddress, localAddress);
243 + if (!bgpSessionManager.peerConnected(this)) {
244 + log.debug("Cannot setup BGP Session Connection from {}. Closing...",
245 + remoteAddress);
246 + ctx.getChannel().close();
247 + }
248 + }
249 +
250 + @Override
251 + public void channelDisconnected(ChannelHandlerContext ctx,
252 + ChannelStateEvent channelEvent) {
253 + log.debug("BGP Session Disconnected from {} on {}",
254 + ctx.getChannel().getRemoteAddress(),
255 + ctx.getChannel().getLocalAddress());
256 +
257 + //
258 + // Withdraw the routes advertised by this BGP peer
259 + //
260 + // NOTE: We must initialize the RIB-IN before propagating the withdraws
261 + // for further processing. Otherwise, the BGP Decision Process
262 + // will use those routes again.
263 + //
264 + Collection<BgpRouteEntry> deletedRoutes = bgpRibIn.values();
265 + bgpRibIn = new ConcurrentHashMap<>();
266 +
267 + // Push the updates to the BGP Merged RIB
268 + BgpSessionManager.BgpRouteSelector bgpRouteSelector =
269 + bgpSessionManager.getBgpRouteSelector();
270 + Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
271 + bgpRouteSelector.routeUpdates(this, addedRoutes, deletedRoutes);
272 +
273 + bgpSessionManager.peerDisconnected(this);
274 + }
275 +
276 + /**
277 + * Processes BGP OPEN message.
278 + *
279 + * @param ctx the Channel Handler Context
280 + * @param message the message to process
281 + */
282 + void processBgpOpen(ChannelHandlerContext ctx, ChannelBuffer message) {
283 + int minLength =
284 + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
285 + if (message.readableBytes() < minLength) {
286 + log.debug("BGP RX OPEN Error from {}: " +
287 + "Message length {} too short. Must be at least {}",
288 + remoteAddress, message.readableBytes(), minLength);
289 + //
290 + // ERROR: Bad Message Length
291 + //
292 + // Send NOTIFICATION and close the connection
293 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
294 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
295 + ctx.getChannel().write(txMessage);
296 + closeChannel(ctx);
297 + return;
298 + }
299 +
300 + //
301 + // Parse the OPEN message
302 + //
303 + // Remote BGP version
304 + remoteBgpVersion = message.readUnsignedByte();
305 + if (remoteBgpVersion != BgpConstants.BGP_VERSION) {
306 + log.debug("BGP RX OPEN Error from {}: " +
307 + "Unsupported BGP version {}. Should be {}",
308 + remoteAddress, remoteBgpVersion,
309 + BgpConstants.BGP_VERSION);
310 + //
311 + // ERROR: Unsupported Version Number
312 + //
313 + // Send NOTIFICATION and close the connection
314 + int errorCode = OpenMessageError.ERROR_CODE;
315 + int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER;
316 + ChannelBuffer data = ChannelBuffers.buffer(2);
317 + data.writeShort(BgpConstants.BGP_VERSION);
318 + ChannelBuffer txMessage =
319 + prepareBgpNotification(errorCode, errorSubcode, data);
320 + ctx.getChannel().write(txMessage);
321 + closeChannel(ctx);
322 + return;
323 + }
324 +
325 + // Remote AS number
326 + remoteAs = message.readUnsignedShort();
327 + //
328 + // Verify that the AS number is same for all other BGP Sessions
329 + // NOTE: This check applies only for our use-case where all BGP
330 + // sessions are iBGP.
331 + //
332 + for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) {
333 + if (remoteAs != bgpSession.getRemoteAs()) {
334 + log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
335 + "Expected {}",
336 + remoteAddress, remoteAs, bgpSession.getRemoteAs());
337 + //
338 + // ERROR: Bad Peer AS
339 + //
340 + // Send NOTIFICATION and close the connection
341 + int errorCode = OpenMessageError.ERROR_CODE;
342 + int errorSubcode = OpenMessageError.BAD_PEER_AS;
343 + ChannelBuffer txMessage =
344 + prepareBgpNotification(errorCode, errorSubcode, null);
345 + ctx.getChannel().write(txMessage);
346 + closeChannel(ctx);
347 + return;
348 + }
349 + }
350 +
351 + // Remote Hold Time
352 + remoteHoldtime = message.readUnsignedShort();
353 + if ((remoteHoldtime != 0) &&
354 + (remoteHoldtime < BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME)) {
355 + log.debug("BGP RX OPEN Error from {}: " +
356 + "Unacceptable Hold Time field {}. " +
357 + "Should be 0 or at least {}",
358 + remoteAddress, remoteHoldtime,
359 + BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME);
360 + //
361 + // ERROR: Unacceptable Hold Time
362 + //
363 + // Send NOTIFICATION and close the connection
364 + int errorCode = OpenMessageError.ERROR_CODE;
365 + int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME;
366 + ChannelBuffer txMessage =
367 + prepareBgpNotification(errorCode, errorSubcode, null);
368 + ctx.getChannel().write(txMessage);
369 + closeChannel(ctx);
370 + return;
371 + }
372 +
373 + // Remote BGP Identifier
374 + remoteBgpId = IpAddress.valueOf((int) message.readUnsignedInt());
375 +
376 + // Optional Parameters
377 + int optParamLen = message.readUnsignedByte();
378 + if (message.readableBytes() < optParamLen) {
379 + log.debug("BGP RX OPEN Error from {}: " +
380 + "Invalid Optional Parameter Length field {}. " +
381 + "Remaining Optional Parameters {}",
382 + remoteAddress, optParamLen, message.readableBytes());
383 + //
384 + // ERROR: Invalid Optional Parameter Length field: Unspecific
385 + //
386 + // Send NOTIFICATION and close the connection
387 + int errorCode = OpenMessageError.ERROR_CODE;
388 + int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
389 + ChannelBuffer txMessage =
390 + prepareBgpNotification(errorCode, errorSubcode, null);
391 + ctx.getChannel().write(txMessage);
392 + closeChannel(ctx);
393 + return;
394 + }
395 + // TODO: Parse the optional parameters (if needed)
396 + message.readBytes(optParamLen); // NOTE: data ignored
397 +
398 + //
399 + // Copy some of the remote peer's state/setup to the local setup:
400 + // - BGP version
401 + // - AS number (NOTE: the peer setup is always iBGP)
402 + // - Holdtime
403 + // Also, assign the local BGP ID based on the local setup
404 + //
405 + localBgpVersion = remoteBgpVersion;
406 + localAs = remoteAs;
407 + localHoldtime = remoteHoldtime;
408 + localBgpId = bgpSessionManager.getMyBgpId();
409 +
410 + // Set the Keepalive interval
411 + if (localHoldtime == 0) {
412 + localKeepaliveInterval = 0;
413 + } else {
414 + localKeepaliveInterval = Math.max(localHoldtime /
415 + BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
416 + BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
417 + }
418 +
419 + log.debug("BGP RX OPEN message from {}: " +
420 + "BGPv{} AS {} BGP-ID {} Holdtime {}",
421 + remoteAddress, remoteBgpVersion, remoteAs,
422 + remoteBgpId, remoteHoldtime);
423 +
424 + // Send my OPEN followed by KEEPALIVE
425 + ChannelBuffer txMessage = prepareBgpOpen();
426 + ctx.getChannel().write(txMessage);
427 + //
428 + txMessage = prepareBgpKeepalive();
429 + ctx.getChannel().write(txMessage);
430 +
431 + // Start the KEEPALIVE timer
432 + restartKeepaliveTimer(ctx);
433 +
434 + // Start the Session Timeout timer
435 + restartSessionTimeoutTimer(ctx);
436 + }
437 +
438 + /**
439 + * Processes BGP UPDATE message.
440 + *
441 + * @param ctx the Channel Handler Context
442 + * @param message the message to process
443 + */
444 + void processBgpUpdate(ChannelHandlerContext ctx, ChannelBuffer message) {
445 + Collection<BgpRouteEntry> addedRoutes = null;
446 + Map<IpPrefix, BgpRouteEntry> deletedRoutes = new HashMap<>();
447 +
448 + int minLength =
449 + BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
450 + if (message.readableBytes() < minLength) {
451 + log.debug("BGP RX UPDATE Error from {}: " +
452 + "Message length {} too short. Must be at least {}",
453 + remoteAddress, message.readableBytes(), minLength);
454 + //
455 + // ERROR: Bad Message Length
456 + //
457 + // Send NOTIFICATION and close the connection
458 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
459 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
460 + ctx.getChannel().write(txMessage);
461 + closeChannel(ctx);
462 + return;
463 + }
464 +
465 + log.debug("BGP RX UPDATE message from {}", remoteAddress);
466 +
467 + //
468 + // Parse the UPDATE message
469 + //
470 +
471 + //
472 + // Parse the Withdrawn Routes
473 + //
474 + int withdrawnRoutesLength = message.readUnsignedShort();
475 + if (withdrawnRoutesLength > message.readableBytes()) {
476 + // ERROR: Malformed Attribute List
477 + actionsBgpUpdateMalformedAttributeList(ctx);
478 + return;
479 + }
480 + Collection<IpPrefix> withdrawnPrefixes = null;
481 + try {
482 + withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength,
483 + message);
484 + } catch (BgpParseException e) {
485 + // ERROR: Invalid Network Field
486 + log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
487 + remoteBgpId, e);
488 + actionsBgpUpdateInvalidNetworkField(ctx);
489 + return;
490 + }
491 + for (IpPrefix prefix : withdrawnPrefixes) {
492 + log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
493 + remoteAddress, prefix);
494 + BgpRouteEntry bgpRouteEntry = bgpRibIn.get(prefix);
495 + if (bgpRouteEntry != null) {
496 + deletedRoutes.put(prefix, bgpRouteEntry);
497 + }
498 + }
499 +
500 + //
501 + // Parse the Path Attributes
502 + //
503 + try {
504 + addedRoutes = parsePathAttributes(ctx, message);
505 + } catch (BgpParseException e) {
506 + log.debug("Exception parsing Path Attributes from BGP peer {}: ",
507 + remoteBgpId, e);
508 + // NOTE: The session was already closed, so nothing else to do
509 + return;
510 + }
511 + // Ignore WITHDRAWN routes that are ADDED
512 + for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
513 + deletedRoutes.remove(bgpRouteEntry.prefix());
514 + }
515 +
516 + // Update the BGP RIB-IN
517 + for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) {
518 + bgpRibIn.remove(bgpRouteEntry.prefix());
519 + }
520 + for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
521 + bgpRibIn.put(bgpRouteEntry.prefix(), bgpRouteEntry);
522 + }
523 +
524 + // Push the updates to the BGP Merged RIB
525 + BgpSessionManager.BgpRouteSelector bgpRouteSelector =
526 + bgpSessionManager.getBgpRouteSelector();
527 + bgpRouteSelector.routeUpdates(this, addedRoutes,
528 + deletedRoutes.values());
529 +
530 + // Start the Session Timeout timer
531 + restartSessionTimeoutTimer(ctx);
532 + }
533 +
534 + /**
535 + * Parse BGP Path Attributes from the BGP UPDATE message.
536 + *
537 + * @param ctx the Channel Handler Context
538 + * @param message the message to parse
539 + * @return a collection of the result BGP Route Entries
540 + * @throws BgpParseException
541 + */
542 + private Collection<BgpRouteEntry> parsePathAttributes(
543 + ChannelHandlerContext ctx,
544 + ChannelBuffer message)
545 + throws BgpParseException {
546 + Map<IpPrefix, BgpRouteEntry> addedRoutes = new HashMap<>();
547 +
548 + //
549 + // Parsed values
550 + //
551 + Short origin = -1; // Mandatory
552 + BgpRouteEntry.AsPath asPath = null; // Mandatory
553 + IpAddress nextHop = null; // Mandatory
554 + long multiExitDisc = // Optional
555 + BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
556 + Long localPref = null; // Mandatory
557 + Long aggregatorAsNumber = null; // Optional: unused
558 + IpAddress aggregatorIpAddress = null; // Optional: unused
559 +
560 + //
561 + // Get and verify the Path Attributes Length
562 + //
563 + int pathAttributeLength = message.readUnsignedShort();
564 + if (pathAttributeLength > message.readableBytes()) {
565 + // ERROR: Malformed Attribute List
566 + actionsBgpUpdateMalformedAttributeList(ctx);
567 + String errorMsg = "Malformed Attribute List";
568 + throw new BgpParseException(errorMsg);
569 + }
570 + if (pathAttributeLength == 0) {
571 + return addedRoutes.values();
572 + }
573 +
574 + //
575 + // Parse the Path Attributes
576 + //
577 + int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
578 + while (message.readerIndex() < pathAttributeEnd) {
579 + int attrFlags = message.readUnsignedByte();
580 + if (message.readerIndex() >= pathAttributeEnd) {
581 + // ERROR: Malformed Attribute List
582 + actionsBgpUpdateMalformedAttributeList(ctx);
583 + String errorMsg = "Malformed Attribute List";
584 + throw new BgpParseException(errorMsg);
585 + }
586 + int attrTypeCode = message.readUnsignedByte();
587 +
588 + // The Attribute Flags
589 + boolean optionalBit = ((0x80 & attrFlags) != 0);
590 + boolean transitiveBit = ((0x40 & attrFlags) != 0);
591 + boolean partialBit = ((0x20 & attrFlags) != 0);
592 + boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
593 +
594 + // The Attribute Length
595 + int attrLen = 0;
596 + int attrLenOctets = 1;
597 + if (extendedLengthBit) {
598 + attrLenOctets = 2;
599 + }
600 + if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
601 + // ERROR: Malformed Attribute List
602 + actionsBgpUpdateMalformedAttributeList(ctx);
603 + String errorMsg = "Malformed Attribute List";
604 + throw new BgpParseException(errorMsg);
605 + }
606 + if (extendedLengthBit) {
607 + attrLen = message.readUnsignedShort();
608 + } else {
609 + attrLen = message.readUnsignedByte();
610 + }
611 + if (message.readerIndex() + attrLen > pathAttributeEnd) {
612 + // ERROR: Malformed Attribute List
613 + actionsBgpUpdateMalformedAttributeList(ctx);
614 + String errorMsg = "Malformed Attribute List";
615 + throw new BgpParseException(errorMsg);
616 + }
617 +
618 + //
619 + // Verify the Attribute Flags
620 + //
621 + verifyBgpUpdateAttributeFlags(ctx, attrTypeCode, attrLen,
622 + attrFlags, message);
623 +
624 + //
625 + // Extract the Attribute Value based on the Attribute Type Code
626 + //
627 + switch (attrTypeCode) {
628 +
629 + case BgpConstants.Update.Origin.TYPE:
630 + // Attribute Type Code ORIGIN
631 + origin = parseAttributeTypeOrigin(ctx, attrTypeCode, attrLen,
632 + attrFlags, message);
633 + break;
634 +
635 + case BgpConstants.Update.AsPath.TYPE:
636 + // Attribute Type Code AS_PATH
637 + asPath = parseAttributeTypeAsPath(ctx, attrTypeCode, attrLen,
638 + attrFlags, message);
639 + break;
640 +
641 + case BgpConstants.Update.NextHop.TYPE:
642 + // Attribute Type Code NEXT_HOP
643 + nextHop = parseAttributeTypeNextHop(ctx, attrTypeCode, attrLen,
644 + attrFlags, message);
645 + break;
646 +
647 + case BgpConstants.Update.MultiExitDisc.TYPE:
648 + // Attribute Type Code MULTI_EXIT_DISC
649 + multiExitDisc =
650 + parseAttributeTypeMultiExitDisc(ctx, attrTypeCode, attrLen,
651 + attrFlags, message);
652 + break;
653 +
654 + case BgpConstants.Update.LocalPref.TYPE:
655 + // Attribute Type Code LOCAL_PREF
656 + localPref =
657 + parseAttributeTypeLocalPref(ctx, attrTypeCode, attrLen,
658 + attrFlags, message);
659 + break;
660 +
661 + case BgpConstants.Update.AtomicAggregate.TYPE:
662 + // Attribute Type Code ATOMIC_AGGREGATE
663 + parseAttributeTypeAtomicAggregate(ctx, attrTypeCode, attrLen,
664 + attrFlags, message);
665 + // Nothing to do: this attribute is primarily informational
666 + break;
667 +
668 + case BgpConstants.Update.Aggregator.TYPE:
669 + // Attribute Type Code AGGREGATOR
670 + Pair<Long, IpAddress> aggregator =
671 + parseAttributeTypeAggregator(ctx, attrTypeCode, attrLen,
672 + attrFlags, message);
673 + aggregatorAsNumber = aggregator.getLeft();
674 + aggregatorIpAddress = aggregator.getRight();
675 + break;
676 +
677 + default:
678 + // TODO: Parse any new Attribute Types if needed
679 + if (!optionalBit) {
680 + // ERROR: Unrecognized Well-known Attribute
681 + actionsBgpUpdateUnrecognizedWellKnownAttribute(
682 + ctx, attrTypeCode, attrLen, attrFlags, message);
683 + String errorMsg = "Unrecognized Well-known Attribute: " +
684 + attrTypeCode;
685 + throw new BgpParseException(errorMsg);
686 + }
687 +
688 + // Skip the data from the unrecognized attribute
689 + log.debug("BGP RX UPDATE message from {}: " +
690 + "Unrecognized Attribute Type {}",
691 + remoteAddress, attrTypeCode);
692 + message.skipBytes(attrLen);
693 + break;
694 + }
695 + }
696 +
697 + //
698 + // Verify the Well-known Attributes
699 + //
700 + verifyBgpUpdateWellKnownAttributes(ctx, origin, asPath, nextHop,
701 + localPref);
702 +
703 + //
704 + // Parse the NLRI (Network Layer Reachability Information)
705 + //
706 + Collection<IpPrefix> addedPrefixes = null;
707 + int nlriLength = message.readableBytes();
708 + try {
709 + addedPrefixes = parsePackedPrefixes(nlriLength, message);
710 + } catch (BgpParseException e) {
711 + // ERROR: Invalid Network Field
712 + log.debug("Exception parsing NLRI from BGP peer {}: ",
713 + remoteBgpId, e);
714 + actionsBgpUpdateInvalidNetworkField(ctx);
715 + // Rethrow the exception
716 + throw e;
717 + }
718 +
719 + // Generate the added routes
720 + for (IpPrefix prefix : addedPrefixes) {
721 + BgpRouteEntry bgpRouteEntry =
722 + new BgpRouteEntry(this, prefix, nextHop,
723 + origin.byteValue(), asPath, localPref);
724 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
725 + if (bgpRouteEntry.hasAsPathLoop(localAs)) {
726 + log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
727 + "nextHop {}: contains AS Path loop",
728 + remoteAddress, prefix, nextHop);
729 + continue;
730 + } else {
731 + log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
732 + remoteAddress, prefix, nextHop);
733 + }
734 + addedRoutes.put(prefix, bgpRouteEntry);
735 + }
736 +
737 + return addedRoutes.values();
738 + }
739 +
740 + /**
741 + * Verifies BGP UPDATE Well-known Attributes.
742 + *
743 + * @param ctx the Channel Handler Context
744 + * @param origin the ORIGIN well-known mandatory attribute
745 + * @param asPath the AS_PATH well-known mandatory attribute
746 + * @param nextHop the NEXT_HOP well-known mandatory attribute
747 + * @param localPref the LOCAL_PREF required attribute
748 + * @throws BgpParseException
749 + */
750 + private void verifyBgpUpdateWellKnownAttributes(
751 + ChannelHandlerContext ctx,
752 + Short origin,
753 + BgpRouteEntry.AsPath asPath,
754 + IpAddress nextHop,
755 + Long localPref)
756 + throws BgpParseException {
757 + //
758 + // Check for Missing Well-known Attributes
759 + //
760 + if ((origin == null) || (origin == -1)) {
761 + // Missing Attribute Type Code ORIGIN
762 + int type = BgpConstants.Update.Origin.TYPE;
763 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
764 + String errorMsg = "Missing Well-known Attribute: ORIGIN";
765 + throw new BgpParseException(errorMsg);
766 + }
767 + if (asPath == null) {
768 + // Missing Attribute Type Code AS_PATH
769 + int type = BgpConstants.Update.AsPath.TYPE;
770 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
771 + String errorMsg = "Missing Well-known Attribute: AS_PATH";
772 + throw new BgpParseException(errorMsg);
773 + }
774 + if (nextHop == null) {
775 + // Missing Attribute Type Code NEXT_HOP
776 + int type = BgpConstants.Update.NextHop.TYPE;
777 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
778 + String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
779 + throw new BgpParseException(errorMsg);
780 + }
781 + if (localPref == null) {
782 + // Missing Attribute Type Code LOCAL_PREF
783 + // NOTE: Required for iBGP
784 + int type = BgpConstants.Update.LocalPref.TYPE;
785 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
786 + String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
787 + throw new BgpParseException(errorMsg);
788 + }
789 + }
790 +
791 + /**
792 + * Verifies the BGP UPDATE Attribute Flags.
793 + *
794 + * @param ctx the Channel Handler Context
795 + * @param attrTypeCode the attribute type code
796 + * @param attrLen the attribute length (in octets)
797 + * @param attrFlags the attribute flags
798 + * @param message the message to parse
799 + * @throws BgpParseException
800 + */
801 + private void verifyBgpUpdateAttributeFlags(
802 + ChannelHandlerContext ctx,
803 + int attrTypeCode,
804 + int attrLen,
805 + int attrFlags,
806 + ChannelBuffer message)
807 + throws BgpParseException {
808 +
809 + //
810 + // Assign the Attribute Type Name and the Well-known flag
811 + //
812 + String typeName = "UNKNOWN";
813 + boolean isWellKnown = false;
814 + switch (attrTypeCode) {
815 + case BgpConstants.Update.Origin.TYPE:
816 + isWellKnown = true;
817 + typeName = "ORIGIN";
818 + break;
819 + case BgpConstants.Update.AsPath.TYPE:
820 + isWellKnown = true;
821 + typeName = "AS_PATH";
822 + break;
823 + case BgpConstants.Update.NextHop.TYPE:
824 + isWellKnown = true;
825 + typeName = "NEXT_HOP";
826 + break;
827 + case BgpConstants.Update.MultiExitDisc.TYPE:
828 + isWellKnown = false;
829 + typeName = "MULTI_EXIT_DISC";
830 + break;
831 + case BgpConstants.Update.LocalPref.TYPE:
832 + isWellKnown = true;
833 + typeName = "LOCAL_PREF";
834 + break;
835 + case BgpConstants.Update.AtomicAggregate.TYPE:
836 + isWellKnown = true;
837 + typeName = "ATOMIC_AGGREGATE";
838 + break;
839 + case BgpConstants.Update.Aggregator.TYPE:
840 + isWellKnown = false;
841 + typeName = "AGGREGATOR";
842 + break;
843 + default:
844 + isWellKnown = false;
845 + typeName = "UNKNOWN(" + attrTypeCode + ")";
846 + break;
847 + }
848 +
849 + //
850 + // Verify the Attribute Flags
851 + //
852 + boolean optionalBit = ((0x80 & attrFlags) != 0);
853 + boolean transitiveBit = ((0x40 & attrFlags) != 0);
854 + boolean partialBit = ((0x20 & attrFlags) != 0);
855 + if ((isWellKnown && optionalBit) ||
856 + (isWellKnown && (!transitiveBit)) ||
857 + (isWellKnown && partialBit) ||
858 + (optionalBit && (!transitiveBit) && partialBit)) {
859 + //
860 + // ERROR: The Optional bit cannot be set for Well-known attributes
861 + // ERROR: The Transtive bit MUST be 1 for well-known attributes
862 + // ERROR: The Partial bit MUST be 0 for well-known attributes
863 + // ERROR: The Partial bit MUST be 0 for optional non-transitive
864 + // attributes
865 + //
866 + actionsBgpUpdateAttributeFlagsError(
867 + ctx, attrTypeCode, attrLen, attrFlags, message);
868 + String errorMsg = "Attribute Flags Error for " + typeName + ": " +
869 + attrFlags;
870 + throw new BgpParseException(errorMsg);
871 + }
872 + }
873 +
874 + /**
875 + * Parses BGP UPDATE Attribute Type ORIGIN.
876 + *
877 + * @param ctx the Channel Handler Context
878 + * @param attrTypeCode the attribute type code
879 + * @param attrLen the attribute length (in octets)
880 + * @param attrFlags the attribute flags
881 + * @param message the message to parse
882 + * @return the parsed ORIGIN value
883 + * @throws BgpParseException
884 + */
885 + private short parseAttributeTypeOrigin(
886 + ChannelHandlerContext ctx,
887 + int attrTypeCode,
888 + int attrLen,
889 + int attrFlags,
890 + ChannelBuffer message)
891 + throws BgpParseException {
892 +
893 + // Check the Attribute Length
894 + if (attrLen != BgpConstants.Update.Origin.LENGTH) {
895 + // ERROR: Attribute Length Error
896 + actionsBgpUpdateAttributeLengthError(
897 + ctx, attrTypeCode, attrLen, attrFlags, message);
898 + String errorMsg = "Attribute Length Error";
899 + throw new BgpParseException(errorMsg);
900 + }
901 +
902 + message.markReaderIndex();
903 + short origin = message.readUnsignedByte();
904 + switch (origin) {
905 + case BgpConstants.Update.Origin.IGP:
906 + // FALLTHROUGH
907 + case BgpConstants.Update.Origin.EGP:
908 + // FALLTHROUGH
909 + case BgpConstants.Update.Origin.INCOMPLETE:
910 + break;
911 + default:
912 + // ERROR: Invalid ORIGIN Attribute
913 + message.resetReaderIndex();
914 + actionsBgpUpdateInvalidOriginAttribute(
915 + ctx, attrTypeCode, attrLen, attrFlags, message, origin);
916 + String errorMsg = "Invalid ORIGIN Attribute: " + origin;
917 + throw new BgpParseException(errorMsg);
918 + }
919 +
920 + return origin;
921 + }
922 +
923 + /**
924 + * Parses BGP UPDATE Attribute AS Path.
925 + *
926 + * @param ctx the Channel Handler Context
927 + * @param attrTypeCode the attribute type code
928 + * @param attrLen the attribute length (in octets)
929 + * @param attrFlags the attribute flags
930 + * @param message the message to parse
931 + * @return the parsed AS Path
932 + * @throws BgpParseException
933 + */
934 + private BgpRouteEntry.AsPath parseAttributeTypeAsPath(
935 + ChannelHandlerContext ctx,
936 + int attrTypeCode,
937 + int attrLen,
938 + int attrFlags,
939 + ChannelBuffer message)
940 + throws BgpParseException {
941 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
942 +
943 + //
944 + // Parse the message
945 + //
946 + while (attrLen > 0) {
947 + if (attrLen < 2) {
948 + // ERROR: Malformed AS_PATH
949 + actionsBgpUpdateMalformedAsPath(ctx);
950 + String errorMsg = "Malformed AS Path";
951 + throw new BgpParseException(errorMsg);
952 + }
953 + // Get the Path Segment Type and Length (in number of ASes)
954 + short pathSegmentType = message.readUnsignedByte();
955 + short pathSegmentLength = message.readUnsignedByte();
956 + attrLen -= 2;
957 +
958 + // Verify the Path Segment Type
959 + switch (pathSegmentType) {
960 + case BgpConstants.Update.AsPath.AS_SET:
961 + // FALLTHROUGH
962 + case BgpConstants.Update.AsPath.AS_SEQUENCE:
963 + break;
964 + default:
965 + // ERROR: Invalid Path Segment Type
966 + //
967 + // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
968 + // for "Invalid Path Segment Type", hence we return
969 + // the error as "Malformed AS_PATH".
970 + //
971 + actionsBgpUpdateMalformedAsPath(ctx);
972 + String errorMsg =
973 + "Invalid AS Path Segment Type: " + pathSegmentType;
974 + throw new BgpParseException(errorMsg);
975 + }
976 +
977 + // Parse the AS numbers
978 + if (2 * pathSegmentLength > attrLen) {
979 + // ERROR: Malformed AS_PATH
980 + actionsBgpUpdateMalformedAsPath(ctx);
981 + String errorMsg = "Malformed AS Path";
982 + throw new BgpParseException(errorMsg);
983 + }
984 + attrLen -= (2 * pathSegmentLength);
985 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
986 + while (pathSegmentLength-- > 0) {
987 + long asNumber = message.readUnsignedShort();
988 + segmentAsNumbers.add(asNumber);
989 + }
990 +
991 + BgpRouteEntry.PathSegment pathSegment =
992 + new BgpRouteEntry.PathSegment((byte) pathSegmentType,
993 + segmentAsNumbers);
994 + pathSegments.add(pathSegment);
995 + }
996 +
997 + return new BgpRouteEntry.AsPath(pathSegments);
998 + }
999 +
1000 + /**
1001 + * Parses BGP UPDATE Attribute Type NEXT_HOP.
1002 + *
1003 + * @param ctx the Channel Handler Context
1004 + * @param attrTypeCode the attribute type code
1005 + * @param attrLen the attribute length (in octets)
1006 + * @param attrFlags the attribute flags
1007 + * @param message the message to parse
1008 + * @return the parsed NEXT_HOP value
1009 + * @throws BgpParseException
1010 + */
1011 + private IpAddress parseAttributeTypeNextHop(
1012 + ChannelHandlerContext ctx,
1013 + int attrTypeCode,
1014 + int attrLen,
1015 + int attrFlags,
1016 + ChannelBuffer message)
1017 + throws BgpParseException {
1018 +
1019 + // Check the Attribute Length
1020 + if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
1021 + // ERROR: Attribute Length Error
1022 + actionsBgpUpdateAttributeLengthError(
1023 + ctx, attrTypeCode, attrLen, attrFlags, message);
1024 + String errorMsg = "Attribute Length Error";
1025 + throw new BgpParseException(errorMsg);
1026 + }
1027 +
1028 + message.markReaderIndex();
1029 + long address = message.readUnsignedInt();
1030 + IpAddress nextHopAddress = IpAddress.valueOf((int) address);
1031 + //
1032 + // Check whether the NEXT_HOP IP address is semantically correct.
1033 + // As per RFC 4271, Section 6.3:
1034 + //
1035 + // a) It MUST NOT be the IP address of the receiving speaker
1036 + // b) In the case of an EBGP ....
1037 + //
1038 + // Here we check only (a), because (b) doesn't apply for us: all our
1039 + // peers are iBGP.
1040 + //
1041 + if (nextHopAddress.equals(localIp4Address)) {
1042 + // ERROR: Invalid NEXT_HOP Attribute
1043 + message.resetReaderIndex();
1044 + actionsBgpUpdateInvalidNextHopAttribute(
1045 + ctx, attrTypeCode, attrLen, attrFlags, message,
1046 + nextHopAddress);
1047 + String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
1048 + throw new BgpParseException(errorMsg);
1049 + }
1050 +
1051 + return nextHopAddress;
1052 + }
1053 +
1054 + /**
1055 + * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
1056 + *
1057 + * @param ctx the Channel Handler Context
1058 + * @param attrTypeCode the attribute type code
1059 + * @param attrLen the attribute length (in octets)
1060 + * @param attrFlags the attribute flags
1061 + * @param message the message to parse
1062 + * @return the parsed MULTI_EXIT_DISC value
1063 + * @throws BgpParseException
1064 + */
1065 + private long parseAttributeTypeMultiExitDisc(
1066 + ChannelHandlerContext ctx,
1067 + int attrTypeCode,
1068 + int attrLen,
1069 + int attrFlags,
1070 + ChannelBuffer message)
1071 + throws BgpParseException {
1072 +
1073 + // Check the Attribute Length
1074 + if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
1075 + // ERROR: Attribute Length Error
1076 + actionsBgpUpdateAttributeLengthError(
1077 + ctx, attrTypeCode, attrLen, attrFlags, message);
1078 + String errorMsg = "Attribute Length Error";
1079 + throw new BgpParseException(errorMsg);
1080 + }
1081 +
1082 + long multiExitDisc = message.readUnsignedInt();
1083 + return multiExitDisc;
1084 + }
1085 +
1086 + /**
1087 + * Parses BGP UPDATE Attribute Type LOCAL_PREF.
1088 + *
1089 + * @param ctx the Channel Handler Context
1090 + * @param attrTypeCode the attribute type code
1091 + * @param attrLen the attribute length (in octets)
1092 + * @param attrFlags the attribute flags
1093 + * @param message the message to parse
1094 + * @return the parsed LOCAL_PREF value
1095 + * @throws BgpParseException
1096 + */
1097 + private long parseAttributeTypeLocalPref(
1098 + ChannelHandlerContext ctx,
1099 + int attrTypeCode,
1100 + int attrLen,
1101 + int attrFlags,
1102 + ChannelBuffer message)
1103 + throws BgpParseException {
1104 +
1105 + // Check the Attribute Length
1106 + if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
1107 + // ERROR: Attribute Length Error
1108 + actionsBgpUpdateAttributeLengthError(
1109 + ctx, attrTypeCode, attrLen, attrFlags, message);
1110 + String errorMsg = "Attribute Length Error";
1111 + throw new BgpParseException(errorMsg);
1112 + }
1113 +
1114 + long localPref = message.readUnsignedInt();
1115 + return localPref;
1116 + }
1117 +
1118 + /**
1119 + * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
1120 + *
1121 + * @param ctx the Channel Handler Context
1122 + * @param attrTypeCode the attribute type code
1123 + * @param attrLen the attribute length (in octets)
1124 + * @param attrFlags the attribute flags
1125 + * @param message the message to parse
1126 + * @throws BgpParseException
1127 + */
1128 + private void parseAttributeTypeAtomicAggregate(
1129 + ChannelHandlerContext ctx,
1130 + int attrTypeCode,
1131 + int attrLen,
1132 + int attrFlags,
1133 + ChannelBuffer message)
1134 + throws BgpParseException {
1135 +
1136 + // Check the Attribute Length
1137 + if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
1138 + // ERROR: Attribute Length Error
1139 + actionsBgpUpdateAttributeLengthError(
1140 + ctx, attrTypeCode, attrLen, attrFlags, message);
1141 + String errorMsg = "Attribute Length Error";
1142 + throw new BgpParseException(errorMsg);
1143 + }
1144 +
1145 + // Nothing to do: this attribute is primarily informational
1146 + }
1147 +
1148 + /**
1149 + * Parses BGP UPDATE Attribute Type AGGREGATOR.
1150 + *
1151 + * @param ctx the Channel Handler Context
1152 + * @param attrTypeCode the attribute type code
1153 + * @param attrLen the attribute length (in octets)
1154 + * @param attrFlags the attribute flags
1155 + * @param message the message to parse
1156 + * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
1157 + * @throws BgpParseException
1158 + */
1159 + private Pair<Long, IpAddress> parseAttributeTypeAggregator(
1160 + ChannelHandlerContext ctx,
1161 + int attrTypeCode,
1162 + int attrLen,
1163 + int attrFlags,
1164 + ChannelBuffer message)
1165 + throws BgpParseException {
1166 +
1167 + // Check the Attribute Length
1168 + if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
1169 + // ERROR: Attribute Length Error
1170 + actionsBgpUpdateAttributeLengthError(
1171 + ctx, attrTypeCode, attrLen, attrFlags, message);
1172 + String errorMsg = "Attribute Length Error";
1173 + throw new BgpParseException(errorMsg);
1174 + }
1175 +
1176 + // The AGGREGATOR AS number
1177 + long aggregatorAsNumber = message.readUnsignedShort();
1178 + // The AGGREGATOR IP address
1179 + long aggregatorAddress = message.readUnsignedInt();
1180 + IpAddress aggregatorIpAddress =
1181 + IpAddress.valueOf((int) aggregatorAddress);
1182 +
1183 + Pair<Long, IpAddress> aggregator = Pair.of(aggregatorAsNumber,
1184 + aggregatorIpAddress);
1185 + return aggregator;
1186 + }
1187 +
1188 + /**
1189 + * Parses a message that contains encoded IPv4 network prefixes.
1190 + * <p>
1191 + * The IPv4 prefixes are encoded in the form:
1192 + * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
1193 + * and Prefix is the IPv4 prefix (padded with trailing bits to the end
1194 + * of an octet).
1195 + *
1196 + * @param totalLength the total length of the data to parse
1197 + * @param message the message with data to parse
1198 + * @return a collection of parsed IPv4 network prefixes
1199 + * @throws BgpParseException
1200 + */
1201 + private Collection<IpPrefix> parsePackedPrefixes(int totalLength,
1202 + ChannelBuffer message)
1203 + throws BgpParseException {
1204 + Collection<IpPrefix> result = new ArrayList<>();
1205 +
1206 + if (totalLength == 0) {
1207 + return result;
1208 + }
1209 +
1210 + // Parse the data
1211 + int dataEnd = message.readerIndex() + totalLength;
1212 + while (message.readerIndex() < dataEnd) {
1213 + int prefixBitlen = message.readUnsignedByte();
1214 + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
1215 + if (message.readerIndex() + prefixBytelen > dataEnd) {
1216 + String errorMsg = "Malformed Network Prefixes";
1217 + throw new BgpParseException(errorMsg);
1218 + }
1219 +
1220 + long address = 0;
1221 + long extraShift = (4 - prefixBytelen) * 8;
1222 + while (prefixBytelen > 0) {
1223 + address <<= 8;
1224 + address |= message.readUnsignedByte();
1225 + prefixBytelen--;
1226 + }
1227 + address <<= extraShift;
1228 + IpPrefix prefix =
1229 + IpPrefix.valueOf(IpAddress.valueOf((int) address).toRealInt(),
1230 + (short) prefixBitlen);
1231 + result.add(prefix);
1232 + }
1233 +
1234 + return result;
1235 + }
1236 +
1237 + /**
1238 + * Applies the appropriate actions after detecting BGP UPDATE
1239 + * Invalid Network Field Error: send NOTIFICATION and close the channel.
1240 + *
1241 + * @param ctx the Channel Handler Context
1242 + */
1243 + private void actionsBgpUpdateInvalidNetworkField(
1244 + ChannelHandlerContext ctx) {
1245 + log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
1246 + remoteAddress);
1247 +
1248 + //
1249 + // ERROR: Invalid Network Field
1250 + //
1251 + // Send NOTIFICATION and close the connection
1252 + int errorCode = UpdateMessageError.ERROR_CODE;
1253 + int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
1254 + ChannelBuffer txMessage =
1255 + prepareBgpNotification(errorCode, errorSubcode, null);
1256 + ctx.getChannel().write(txMessage);
1257 + closeChannel(ctx);
1258 + }
1259 +
1260 + /**
1261 + * Applies the appropriate actions after detecting BGP UPDATE
1262 + * Malformed Attribute List Error: send NOTIFICATION and close the channel.
1263 + *
1264 + * @param ctx the Channel Handler Context
1265 + */
1266 + private void actionsBgpUpdateMalformedAttributeList(
1267 + ChannelHandlerContext ctx) {
1268 + log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
1269 + remoteAddress);
1270 +
1271 + //
1272 + // ERROR: Malformed Attribute List
1273 + //
1274 + // Send NOTIFICATION and close the connection
1275 + int errorCode = UpdateMessageError.ERROR_CODE;
1276 + int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
1277 + ChannelBuffer txMessage =
1278 + prepareBgpNotification(errorCode, errorSubcode, null);
1279 + ctx.getChannel().write(txMessage);
1280 + closeChannel(ctx);
1281 + }
1282 +
1283 + /**
1284 + * Applies the appropriate actions after detecting BGP UPDATE
1285 + * Missing Well-known Attribute Error: send NOTIFICATION and close the
1286 + * channel.
1287 + *
1288 + * @param ctx the Channel Handler Context
1289 + * @param missingAttrTypeCode the missing attribute type code
1290 + */
1291 + private void actionsBgpUpdateMissingWellKnownAttribute(
1292 + ChannelHandlerContext ctx,
1293 + int missingAttrTypeCode) {
1294 + log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
1295 + remoteAddress, missingAttrTypeCode);
1296 +
1297 + //
1298 + // ERROR: Missing Well-known Attribute
1299 + //
1300 + // Send NOTIFICATION and close the connection
1301 + int errorCode = UpdateMessageError.ERROR_CODE;
1302 + int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
1303 + ChannelBuffer data = ChannelBuffers.buffer(1);
1304 + data.writeByte(missingAttrTypeCode);
1305 + ChannelBuffer txMessage =
1306 + prepareBgpNotification(errorCode, errorSubcode, data);
1307 + ctx.getChannel().write(txMessage);
1308 + closeChannel(ctx);
1309 + }
1310 +
1311 + /**
1312 + * Applies the appropriate actions after detecting BGP UPDATE
1313 + * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
1314 + *
1315 + * @param ctx the Channel Handler Context
1316 + * @param attrTypeCode the attribute type code
1317 + * @param attrLen the attribute length (in octets)
1318 + * @param attrFlags the attribute flags
1319 + * @param message the message with the data
1320 + * @param origin the ORIGIN attribute value
1321 + */
1322 + private void actionsBgpUpdateInvalidOriginAttribute(
1323 + ChannelHandlerContext ctx,
1324 + int attrTypeCode,
1325 + int attrLen,
1326 + int attrFlags,
1327 + ChannelBuffer message,
1328 + short origin) {
1329 + log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
1330 + remoteAddress);
1331 +
1332 + //
1333 + // ERROR: Invalid ORIGIN Attribute
1334 + //
1335 + // Send NOTIFICATION and close the connection
1336 + int errorCode = UpdateMessageError.ERROR_CODE;
1337 + int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
1338 + ChannelBuffer data =
1339 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1340 + attrFlags, message);
1341 + ChannelBuffer txMessage =
1342 + prepareBgpNotification(errorCode, errorSubcode, data);
1343 + ctx.getChannel().write(txMessage);
1344 + closeChannel(ctx);
1345 + }
1346 +
1347 + /**
1348 + * Applies the appropriate actions after detecting BGP UPDATE
1349 + * Attribute Flags Error: send NOTIFICATION and close the channel.
1350 + *
1351 + * @param ctx the Channel Handler Context
1352 + * @param attrTypeCode the attribute type code
1353 + * @param attrLen the attribute length (in octets)
1354 + * @param attrFlags the attribute flags
1355 + * @param message the message with the data
1356 + */
1357 + private void actionsBgpUpdateAttributeFlagsError(
1358 + ChannelHandlerContext ctx,
1359 + int attrTypeCode,
1360 + int attrLen,
1361 + int attrFlags,
1362 + ChannelBuffer message) {
1363 + log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
1364 + remoteAddress);
1365 +
1366 + //
1367 + // ERROR: Attribute Flags Error
1368 + //
1369 + // Send NOTIFICATION and close the connection
1370 + int errorCode = UpdateMessageError.ERROR_CODE;
1371 + int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
1372 + ChannelBuffer data =
1373 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1374 + attrFlags, message);
1375 + ChannelBuffer txMessage =
1376 + prepareBgpNotification(errorCode, errorSubcode, data);
1377 + ctx.getChannel().write(txMessage);
1378 + closeChannel(ctx);
1379 + }
1380 +
1381 + /**
1382 + * Applies the appropriate actions after detecting BGP UPDATE
1383 + * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
1384 + * channel.
1385 + *
1386 + * @param ctx the Channel Handler Context
1387 + * @param attrTypeCode the attribute type code
1388 + * @param attrLen the attribute length (in octets)
1389 + * @param attrFlags the attribute flags
1390 + * @param message the message with the data
1391 + * @param nextHop the NEXT_HOP attribute value
1392 + */
1393 + private void actionsBgpUpdateInvalidNextHopAttribute(
1394 + ChannelHandlerContext ctx,
1395 + int attrTypeCode,
1396 + int attrLen,
1397 + int attrFlags,
1398 + ChannelBuffer message,
1399 + IpAddress nextHop) {
1400 + log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
1401 + remoteAddress, nextHop);
1402 +
1403 + //
1404 + // ERROR: Invalid ORIGIN Attribute
1405 + //
1406 + // Send NOTIFICATION and close the connection
1407 + int errorCode = UpdateMessageError.ERROR_CODE;
1408 + int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
1409 + ChannelBuffer data =
1410 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1411 + attrFlags, message);
1412 + ChannelBuffer txMessage =
1413 + prepareBgpNotification(errorCode, errorSubcode, data);
1414 + ctx.getChannel().write(txMessage);
1415 + closeChannel(ctx);
1416 + }
1417 +
1418 + /**
1419 + * Applies the appropriate actions after detecting BGP UPDATE
1420 + * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
1421 + * the channel.
1422 + *
1423 + * @param ctx the Channel Handler Context
1424 + * @param attrTypeCode the attribute type code
1425 + * @param attrLen the attribute length (in octets)
1426 + * @param attrFlags the attribute flags
1427 + * @param message the message with the data
1428 + */
1429 + private void actionsBgpUpdateUnrecognizedWellKnownAttribute(
1430 + ChannelHandlerContext ctx,
1431 + int attrTypeCode,
1432 + int attrLen,
1433 + int attrFlags,
1434 + ChannelBuffer message) {
1435 + log.debug("BGP RX UPDATE Error from {}: " +
1436 + "Unrecognized Well-known Attribute Error: {}",
1437 + remoteAddress, attrTypeCode);
1438 +
1439 + //
1440 + // ERROR: Unrecognized Well-known Attribute
1441 + //
1442 + // Send NOTIFICATION and close the connection
1443 + int errorCode = UpdateMessageError.ERROR_CODE;
1444 + int errorSubcode =
1445 + UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
1446 + ChannelBuffer data =
1447 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1448 + attrFlags, message);
1449 + ChannelBuffer txMessage =
1450 + prepareBgpNotification(errorCode, errorSubcode, data);
1451 + ctx.getChannel().write(txMessage);
1452 + closeChannel(ctx);
1453 + }
1454 +
1455 + /**
1456 + * Applies the appropriate actions after detecting BGP UPDATE
1457 + * Attribute Length Error: send NOTIFICATION and close the channel.
1458 + *
1459 + * @param ctx the Channel Handler Context
1460 + * @param attrTypeCode the attribute type code
1461 + * @param attrLen the attribute length (in octets)
1462 + * @param attrFlags the attribute flags
1463 + * @param message the message with the data
1464 + */
1465 + private void actionsBgpUpdateAttributeLengthError(
1466 + ChannelHandlerContext ctx,
1467 + int attrTypeCode,
1468 + int attrLen,
1469 + int attrFlags,
1470 + ChannelBuffer message) {
1471 + log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
1472 + remoteAddress);
1473 +
1474 + //
1475 + // ERROR: Attribute Length Error
1476 + //
1477 + // Send NOTIFICATION and close the connection
1478 + int errorCode = UpdateMessageError.ERROR_CODE;
1479 + int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
1480 + ChannelBuffer data =
1481 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1482 + attrFlags, message);
1483 + ChannelBuffer txMessage =
1484 + prepareBgpNotification(errorCode, errorSubcode, data);
1485 + ctx.getChannel().write(txMessage);
1486 + closeChannel(ctx);
1487 + }
1488 +
1489 + /**
1490 + * Applies the appropriate actions after detecting BGP UPDATE
1491 + * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
1492 + *
1493 + * @param ctx the Channel Handler Context
1494 + */
1495 + private void actionsBgpUpdateMalformedAsPath(
1496 + ChannelHandlerContext ctx) {
1497 + log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
1498 + remoteAddress);
1499 +
1500 + //
1501 + // ERROR: Malformed AS_PATH
1502 + //
1503 + // Send NOTIFICATION and close the connection
1504 + int errorCode = UpdateMessageError.ERROR_CODE;
1505 + int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
1506 + ChannelBuffer txMessage =
1507 + prepareBgpNotification(errorCode, errorSubcode, null);
1508 + ctx.getChannel().write(txMessage);
1509 + closeChannel(ctx);
1510 + }
1511 +
1512 + /**
1513 + * Processes BGP NOTIFICATION message.
1514 + *
1515 + * @param ctx the Channel Handler Context
1516 + * @param message the message to process
1517 + */
1518 + void processBgpNotification(ChannelHandlerContext ctx,
1519 + ChannelBuffer message) {
1520 + int minLength =
1521 + BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
1522 + if (message.readableBytes() < minLength) {
1523 + log.debug("BGP RX NOTIFICATION Error from {}: " +
1524 + "Message length {} too short. Must be at least {}",
1525 + remoteAddress, message.readableBytes(), minLength);
1526 + //
1527 + // ERROR: Bad Message Length
1528 + //
1529 + // NOTE: We do NOT send NOTIFICATION in response to a notification
1530 + return;
1531 + }
1532 +
1533 + //
1534 + // Parse the NOTIFICATION message
1535 + //
1536 + int errorCode = message.readUnsignedByte();
1537 + int errorSubcode = message.readUnsignedByte();
1538 + int dataLength = message.readableBytes();
1539 +
1540 + log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " +
1541 + "Error Subcode {} Data Length {}",
1542 + remoteAddress, errorCode, errorSubcode, dataLength);
1543 +
1544 + //
1545 + // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to
1546 + // close the connection.
1547 + //
1548 +
1549 + // Start the Session Timeout timer
1550 + restartSessionTimeoutTimer(ctx);
1551 + }
1552 +
1553 + /**
1554 + * Processes BGP KEEPALIVE message.
1555 + *
1556 + * @param ctx the Channel Handler Context
1557 + * @param message the message to process
1558 + */
1559 + void processBgpKeepalive(ChannelHandlerContext ctx,
1560 + ChannelBuffer message) {
1561 + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
1562 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
1563 + log.debug("BGP RX KEEPALIVE Error from {}: " +
1564 + "Invalid total message length {}. Expected {}",
1565 + remoteAddress,
1566 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH,
1567 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH);
1568 + //
1569 + // ERROR: Bad Message Length
1570 + //
1571 + // Send NOTIFICATION and close the connection
1572 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
1573 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
1574 + ctx.getChannel().write(txMessage);
1575 + closeChannel(ctx);
1576 + return;
1577 + }
1578 +
1579 + //
1580 + // Parse the KEEPALIVE message: nothing to do
1581 + //
1582 + log.debug("BGP RX KEEPALIVE message from {}", remoteAddress);
1583 +
1584 + // Start the Session Timeout timer
1585 + restartSessionTimeoutTimer(ctx);
1586 + }
1587 +
1588 + /**
1589 + * Prepares BGP OPEN message.
1590 + *
1591 + * @return the message to transmit (BGP header included)
1592 + */
1593 + private ChannelBuffer prepareBgpOpen() {
1594 + ChannelBuffer message =
1595 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1596 +
1597 + //
1598 + // Prepare the OPEN message payload
1599 + //
1600 + message.writeByte(localBgpVersion);
1601 + message.writeShort((int) localAs);
1602 + message.writeShort((int) localHoldtime);
1603 + message.writeInt(bgpSessionManager.getMyBgpId().toRealInt());
1604 + message.writeByte(0); // No Optional Parameters
1605 + return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
1606 + }
1607 +
1608 + /**
1609 + * Prepares BGP KEEPALIVE message.
1610 + *
1611 + * @return the message to transmit (BGP header included)
1612 + */
1613 + private ChannelBuffer prepareBgpKeepalive() {
1614 + ChannelBuffer message =
1615 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1616 +
1617 + //
1618 + // Prepare the KEEPALIVE message payload: nothing to do
1619 + //
1620 + return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
1621 + }
1622 +
1623 + /**
1624 + * Prepares BGP NOTIFICATION message.
1625 + *
1626 + * @param errorCode the BGP NOTIFICATION Error Code
1627 + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
1628 + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
1629 + * @param payload the BGP NOTIFICATION Data if applicable, otherwise null
1630 + * @return the message to transmit (BGP header included)
1631 + */
1632 + ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
1633 + ChannelBuffer data) {
1634 + ChannelBuffer message =
1635 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1636 +
1637 + //
1638 + // Prepare the NOTIFICATION message payload
1639 + //
1640 + message.writeByte(errorCode);
1641 + message.writeByte(errorSubcode);
1642 + if (data != null) {
1643 + message.writeBytes(data);
1644 + }
1645 + return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
1646 + }
1647 +
1648 + /**
1649 + * Prepares BGP NOTIFICATION message: Bad Message Length.
1650 + *
1651 + * @param length the erroneous Length field
1652 + * @return the message to transmit (BGP header included)
1653 + */
1654 + ChannelBuffer prepareBgpNotificationBadMessageLength(int length) {
1655 + int errorCode = MessageHeaderError.ERROR_CODE;
1656 + int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH;
1657 + ChannelBuffer data = ChannelBuffers.buffer(2);
1658 + data.writeShort(length);
1659 +
1660 + return prepareBgpNotification(errorCode, errorSubcode, data);
1661 + }
1662 +
1663 + /**
1664 + * Prepares BGP UPDATE Notification data payload.
1665 + *
1666 + * @param attrTypeCode the attribute type code
1667 + * @param attrLen the attribute length (in octets)
1668 + * @param attrFlags the attribute flags
1669 + * @param message the message with the data
1670 + * @return the buffer with the data payload for the BGP UPDATE Notification
1671 + */
1672 + private ChannelBuffer prepareBgpUpdateNotificationDataPayload(
1673 + int attrTypeCode,
1674 + int attrLen,
1675 + int attrFlags,
1676 + ChannelBuffer message) {
1677 + // Compute the attribute length field octets
1678 + boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
1679 + int attrLenOctets = 1;
1680 + if (extendedLengthBit) {
1681 + attrLenOctets = 2;
1682 + }
1683 + ChannelBuffer data =
1684 + ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
1685 + data.writeByte(attrTypeCode);
1686 + if (extendedLengthBit) {
1687 + data.writeShort(attrLen);
1688 + } else {
1689 + data.writeByte(attrLen);
1690 + }
1691 + data.writeBytes(message, attrLen);
1692 + return data;
1693 + }
1694 +
1695 + /**
1696 + * Prepares BGP message.
1697 + *
1698 + * @param type the BGP message type
1699 + * @param payload the message payload to transmit (BGP header excluded)
1700 + * @return the message to transmit (BGP header included)
1701 + */
1702 + private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
1703 + ChannelBuffer message =
1704 + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
1705 + payload.readableBytes());
1706 +
1707 + // Write the marker
1708 + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
1709 + message.writeByte(0xff);
1710 + }
1711 +
1712 + // Write the rest of the BGP header
1713 + message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
1714 + payload.readableBytes());
1715 + message.writeByte(type);
1716 +
1717 + // Write the payload
1718 + message.writeBytes(payload);
1719 + return message;
1720 + }
1721 +
1722 + /**
1723 + * Restarts the BGP KeepaliveTimer.
1724 + */
1725 + private void restartKeepaliveTimer(ChannelHandlerContext ctx) {
1726 + if (localKeepaliveInterval == 0) {
1727 + return; // Nothing to do
1728 + }
1729 + keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
1730 + localKeepaliveInterval,
1731 + TimeUnit.SECONDS);
1732 + }
1733 +
1734 + /**
1735 + * Task class for transmitting KEEPALIVE messages.
1736 + */
1737 + private final class TransmitKeepaliveTask implements TimerTask {
1738 + private final ChannelHandlerContext ctx;
1739 +
1740 + /**
1741 + * Constructor for given Channel Handler Context.
1742 + *
1743 + * @param ctx the Channel Handler Context to use
1744 + */
1745 + TransmitKeepaliveTask(ChannelHandlerContext ctx) {
1746 + this.ctx = ctx;
1747 + }
1748 +
1749 + @Override
1750 + public void run(Timeout timeout) throws Exception {
1751 + if (timeout.isCancelled()) {
1752 + return;
1753 + }
1754 + if (!ctx.getChannel().isOpen()) {
1755 + return;
1756 + }
1757 +
1758 + // Transmit the KEEPALIVE
1759 + ChannelBuffer txMessage = prepareBgpKeepalive();
1760 + ctx.getChannel().write(txMessage);
1761 +
1762 + // Restart the KEEPALIVE timer
1763 + restartKeepaliveTimer(ctx);
1764 + }
1765 + }
1766 +
1767 + /**
1768 + * Restarts the BGP Session Timeout Timer.
1769 + */
1770 + private void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
1771 + if (remoteHoldtime == 0) {
1772 + return; // Nothing to do
1773 + }
1774 + if (sessionTimeout != null) {
1775 + sessionTimeout.cancel();
1776 + }
1777 + sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
1778 + remoteHoldtime,
1779 + TimeUnit.SECONDS);
1780 + }
1781 +
1782 + /**
1783 + * Task class for BGP Session timeout.
1784 + */
1785 + private final class SessionTimeoutTask implements TimerTask {
1786 + private final ChannelHandlerContext ctx;
1787 +
1788 + /**
1789 + * Constructor for given Channel Handler Context.
1790 + *
1791 + * @param ctx the Channel Handler Context to use
1792 + */
1793 + SessionTimeoutTask(ChannelHandlerContext ctx) {
1794 + this.ctx = ctx;
1795 + }
1796 +
1797 + @Override
1798 + public void run(Timeout timeout) throws Exception {
1799 + if (timeout.isCancelled()) {
1800 + return;
1801 + }
1802 + if (!ctx.getChannel().isOpen()) {
1803 + return;
1804 + }
1805 +
1806 + log.debug("BGP Session Timeout: peer {}", remoteAddress);
1807 + //
1808 + // ERROR: Invalid Optional Parameter Length field: Unspecific
1809 + //
1810 + // Send NOTIFICATION and close the connection
1811 + int errorCode = HoldTimerExpired.ERROR_CODE;
1812 + int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
1813 + ChannelBuffer txMessage =
1814 + prepareBgpNotification(errorCode, errorSubcode, null);
1815 + ctx.getChannel().write(txMessage);
1816 + closeChannel(ctx);
1817 + }
1818 + }
1819 +
1820 + /**
1821 + * An exception indicating a parsing error of the BGP message.
1822 + */
1823 + private static class BgpParseException extends Exception {
1824 + /**
1825 + * Default constructor.
1826 + */
1827 + public BgpParseException() {
1828 + super();
1829 + }
1830 +
1831 + /**
1832 + * Constructor for a specific exception details message.
1833 + *
1834 + * @param message the message with the exception details
1835 + */
1836 + public BgpParseException(String message) {
1837 + super(message);
1838 + }
1839 + }
1840 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.net.InetAddress;
6 +import java.net.InetSocketAddress;
7 +import java.net.SocketAddress;
8 +import java.util.Collection;
9 +import java.util.concurrent.ConcurrentHashMap;
10 +import java.util.concurrent.ConcurrentMap;
11 +import java.util.concurrent.Executors;
12 +
13 +import org.jboss.netty.bootstrap.ServerBootstrap;
14 +import org.jboss.netty.channel.Channel;
15 +import org.jboss.netty.channel.ChannelException;
16 +import org.jboss.netty.channel.ChannelFactory;
17 +import org.jboss.netty.channel.ChannelPipeline;
18 +import org.jboss.netty.channel.ChannelPipelineFactory;
19 +import org.jboss.netty.channel.Channels;
20 +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
21 +import org.onlab.onos.sdnip.RouteListener;
22 +import org.onlab.onos.sdnip.RouteUpdate;
23 +import org.onlab.packet.IpAddress;
24 +import org.onlab.packet.IpPrefix;
25 +import org.slf4j.Logger;
26 +import org.slf4j.LoggerFactory;
27 +
28 +/**
29 + * BGP Session Manager class.
30 + */
31 +public class BgpSessionManager {
32 + private static final Logger log =
33 + LoggerFactory.getLogger(BgpSessionManager.class);
34 + private Channel serverChannel; // Listener for incoming BGP connections
35 + private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
36 + new ConcurrentHashMap<>();
37 + private IpAddress myBgpId; // Same BGP ID for all peers
38 +
39 + private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector();
40 + private ConcurrentMap<IpPrefix, BgpRouteEntry> bgpRoutes =
41 + new ConcurrentHashMap<>();
42 +
43 + private final RouteListener routeListener;
44 +
45 + /**
46 + * Constructor for given route listener.
47 + *
48 + * @param routeListener the route listener to use
49 + */
50 + public BgpSessionManager(RouteListener routeListener) {
51 + this.routeListener = checkNotNull(routeListener);
52 + }
53 +
54 + /**
55 + * Gets the BGP sessions.
56 + *
57 + * @return the BGP sessions
58 + */
59 + public Collection<BgpSession> getBgpSessions() {
60 + return bgpSessions.values();
61 + }
62 +
63 + /**
64 + * Gets the BGP routes.
65 + *
66 + * @return the BGP routes
67 + */
68 + public Collection<BgpRouteEntry> getBgpRoutes() {
69 + return bgpRoutes.values();
70 + }
71 +
72 + /**
73 + * Processes the connection from a BGP peer.
74 + *
75 + * @param bgpSession the BGP session for the peer
76 + * @return true if the connection can be established, otherwise false
77 + */
78 + boolean peerConnected(BgpSession bgpSession) {
79 +
80 + // Test whether there is already a session from the same remote
81 + if (bgpSessions.get(bgpSession.getRemoteAddress()) != null) {
82 + return false; // Duplicate BGP session
83 + }
84 + bgpSessions.put(bgpSession.getRemoteAddress(), bgpSession);
85 +
86 + //
87 + // If the first connection, set my BGP ID to the local address
88 + // of the socket.
89 + //
90 + if (bgpSession.getLocalAddress() instanceof InetSocketAddress) {
91 + InetAddress inetAddr =
92 + ((InetSocketAddress) bgpSession.getLocalAddress()).getAddress();
93 + IpAddress ip4Address = IpAddress.valueOf(inetAddr.getAddress());
94 + updateMyBgpId(ip4Address);
95 + }
96 + return true;
97 + }
98 +
99 + /**
100 + * Processes the disconnection from a BGP peer.
101 + *
102 + * @param bgpSession the BGP session for the peer
103 + */
104 + void peerDisconnected(BgpSession bgpSession) {
105 + bgpSessions.remove(bgpSession.getRemoteAddress());
106 + }
107 +
108 + /**
109 + * Conditionally updates the local BGP ID if it wasn't set already.
110 + * <p/>
111 + * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
112 + *
113 + * @param ip4Address the IPv4 address to use as BGP ID
114 + */
115 + private synchronized void updateMyBgpId(IpAddress ip4Address) {
116 + if (myBgpId == null) {
117 + myBgpId = ip4Address;
118 + log.debug("BGP: My BGP ID is {}", myBgpId);
119 + }
120 + }
121 +
122 + /**
123 + * Gets the local BGP Identifier as an IPv4 address.
124 + *
125 + * @return the local BGP Identifier as an IPv4 address
126 + */
127 + IpAddress getMyBgpId() {
128 + return myBgpId;
129 + }
130 +
131 + /**
132 + * Gets the BGP Route Selector.
133 + *
134 + * @return the BGP Route Selector
135 + */
136 + BgpRouteSelector getBgpRouteSelector() {
137 + return bgpRouteSelector;
138 + }
139 +
140 + /**
141 + * Starts up BGP Session Manager operation.
142 + *
143 + * @param listenPortNumber the port number to listen on. By default
144 + * it should be BgpConstants.BGP_PORT (179)
145 + */
146 + public void startUp(int listenPortNumber) {
147 + log.debug("BGP Session Manager startUp()");
148 +
149 + ChannelFactory channelFactory =
150 + new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
151 + Executors.newCachedThreadPool());
152 + ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
153 + @Override
154 + public ChannelPipeline getPipeline() throws Exception {
155 + // Allocate a new session per connection
156 + BgpSession bgpSessionHandler =
157 + new BgpSession(BgpSessionManager.this);
158 + BgpFrameDecoder bgpFrameDecoder =
159 + new BgpFrameDecoder(bgpSessionHandler);
160 +
161 + // Setup the processing pipeline
162 + ChannelPipeline pipeline = Channels.pipeline();
163 + pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
164 + pipeline.addLast("BgpSession", bgpSessionHandler);
165 + return pipeline;
166 + }
167 + };
168 + InetSocketAddress listenAddress =
169 + new InetSocketAddress(listenPortNumber);
170 +
171 + ServerBootstrap serverBootstrap = new ServerBootstrap(channelFactory);
172 + // serverBootstrap.setOptions("reuseAddr", true);
173 + serverBootstrap.setOption("child.keepAlive", true);
174 + serverBootstrap.setOption("child.tcpNoDelay", true);
175 + serverBootstrap.setPipelineFactory(pipelineFactory);
176 + try {
177 + serverChannel = serverBootstrap.bind(listenAddress);
178 + } catch (ChannelException e) {
179 + log.debug("Exception binding to BGP port {}: ",
180 + listenAddress.getPort(), e);
181 + }
182 + }
183 +
184 + /**
185 + * Shuts down the BGP Session Manager operation.
186 + */
187 + public void shutDown() {
188 + // TODO: Complete the implementation: remove routes, etc.
189 + if (serverChannel != null) {
190 + serverChannel.close();
191 + }
192 + }
193 +
194 + /**
195 + * Class to receive and process the BGP routes from each BGP Session/Peer.
196 + */
197 + class BgpRouteSelector {
198 + /**
199 + * Processes route entry updates: added/updated and deleted route
200 + * entries.
201 + *
202 + * @param bgpSession the BGP session the route entry updates were
203 + * received on
204 + * @param addedBgpRouteEntries the added/updated route entries to
205 + * process
206 + * @param deletedBgpRouteEntries the deleted route entries to process
207 + */
208 + synchronized void routeUpdates(BgpSession bgpSession,
209 + Collection<BgpRouteEntry> addedBgpRouteEntries,
210 + Collection<BgpRouteEntry> deletedBgpRouteEntries) {
211 + //
212 + // TODO: Merge the updates from different BGP Peers,
213 + // by choosing the best route.
214 + //
215 +
216 + // Process the deleted route entries
217 + for (BgpRouteEntry bgpRouteEntry : deletedBgpRouteEntries) {
218 + processDeletedRoute(bgpSession, bgpRouteEntry);
219 + }
220 +
221 + // Process the added/updated route entries
222 + for (BgpRouteEntry bgpRouteEntry : addedBgpRouteEntries) {
223 + processAddedRoute(bgpSession, bgpRouteEntry);
224 + }
225 + }
226 +
227 + /**
228 + * Processes an added/updated route entry.
229 + *
230 + * @param bgpSession the BGP session the route entry update was
231 + * received on
232 + * @param bgpRouteEntry the added/updated route entry
233 + */
234 + private void processAddedRoute(BgpSession bgpSession,
235 + BgpRouteEntry bgpRouteEntry) {
236 + RouteUpdate routeUpdate;
237 + BgpRouteEntry bestBgpRouteEntry =
238 + bgpRoutes.get(bgpRouteEntry.prefix());
239 +
240 + //
241 + // Install the new route entry if it is better than the
242 + // current best route.
243 + //
244 + if ((bestBgpRouteEntry == null) ||
245 + bgpRouteEntry.isBetterThan(bestBgpRouteEntry)) {
246 + bgpRoutes.put(bgpRouteEntry.prefix(), bgpRouteEntry);
247 + routeUpdate =
248 + new RouteUpdate(RouteUpdate.Type.UPDATE, bgpRouteEntry);
249 + // Forward the result route updates to the Route Listener
250 + routeListener.update(routeUpdate);
251 + return;
252 + }
253 +
254 + //
255 + // If the route entry arrived on the same BGP Session as
256 + // the current best route, then elect the next best route
257 + // and install it.
258 + //
259 + if (bestBgpRouteEntry.getBgpSession() !=
260 + bgpRouteEntry.getBgpSession()) {
261 + return;
262 + }
263 +
264 + // Find the next best route
265 + bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
266 + if (bestBgpRouteEntry == null) {
267 + //
268 + // TODO: Shouldn't happen. Install the new route as a
269 + // pre-caution.
270 + //
271 + log.debug("BGP next best route for prefix {} is missing. " +
272 + "Adding the route that is currently processed.",
273 + bgpRouteEntry.prefix());
274 + bestBgpRouteEntry = bgpRouteEntry;
275 + }
276 + // Install the next best route
277 + bgpRoutes.put(bestBgpRouteEntry.prefix(), bestBgpRouteEntry);
278 + routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
279 + bestBgpRouteEntry);
280 + // Forward the result route updates to the Route Listener
281 + routeListener.update(routeUpdate);
282 + }
283 +
284 + /**
285 + * Processes a deleted route entry.
286 + *
287 + * @param bgpSession the BGP session the route entry update was
288 + * received on
289 + * @param bgpRouteEntry the deleted route entry
290 + */
291 + private void processDeletedRoute(BgpSession bgpSession,
292 + BgpRouteEntry bgpRouteEntry) {
293 + RouteUpdate routeUpdate;
294 + BgpRouteEntry bestBgpRouteEntry =
295 + bgpRoutes.get(bgpRouteEntry.prefix());
296 +
297 + //
298 + // Remove the route entry only if it was the best one.
299 + // Install the the next best route if it exists.
300 + //
301 + // NOTE: We intentionally use "==" instead of method equals(),
302 + // because we need to check whether this is same object.
303 + //
304 + if (bgpRouteEntry != bestBgpRouteEntry) {
305 + return; // Nothing to do
306 + }
307 +
308 + //
309 + // Find the next best route
310 + //
311 + bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
312 + if (bestBgpRouteEntry != null) {
313 + // Install the next best route
314 + bgpRoutes.put(bestBgpRouteEntry.prefix(),
315 + bestBgpRouteEntry);
316 + routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
317 + bestBgpRouteEntry);
318 + // Forward the result route updates to the Route Listener
319 + routeListener.update(routeUpdate);
320 + return;
321 + }
322 +
323 + //
324 + // No route found. Remove the route entry
325 + //
326 + bgpRoutes.remove(bgpRouteEntry.prefix());
327 + routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
328 + bgpRouteEntry);
329 + // Forward the result route updates to the Route Listener
330 + routeListener.update(routeUpdate);
331 + }
332 +
333 + /**
334 + * Finds the best route entry among all BGP Sessions.
335 + *
336 + * @param prefix the prefix of the route
337 + * @return the best route if found, otherwise null
338 + */
339 + private BgpRouteEntry findBestBgpRoute(IpPrefix prefix) {
340 + BgpRouteEntry bestRoute = null;
341 +
342 + // Iterate across all BGP Sessions and select the best route
343 + for (BgpSession bgpSession : bgpSessions.values()) {
344 + BgpRouteEntry route = bgpSession.findBgpRouteEntry(prefix);
345 + if (route == null) {
346 + continue;
347 + }
348 + if ((bestRoute == null) || route.isBetterThan(bestRoute)) {
349 + bestRoute = route;
350 + }
351 + }
352 + return bestRoute;
353 + }
354 + }
355 +}
1 +/**
2 + * Implementation of the BGP protocol.
3 + */
4 +package org.onlab.onos.sdnip.bgp;
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,13 +2,15 @@ package org.onlab.packet; ...@@ -2,13 +2,15 @@ package org.onlab.packet;
2 2
3 import java.util.Arrays; 3 import java.util.Arrays;
4 4
5 +
6 +
5 /** 7 /**
6 * A class representing an IPv4 address. 8 * A class representing an IPv4 address.
7 * <p/> 9 * <p/>
8 * TODO this class is a clone of IpPrefix and still needs to be modified to 10 * TODO this class is a clone of IpPrefix and still needs to be modified to
9 * look more like an IpAddress. 11 * look more like an IpAddress.
10 */ 12 */
11 -public final class IpAddress { 13 +public final class IpAddress implements Comparable<IpAddress> {
12 14
13 // TODO a comparator for netmasks? E.g. for sorting by prefix match order. 15 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
14 16
...@@ -289,6 +291,13 @@ public final class IpAddress { ...@@ -289,6 +291,13 @@ public final class IpAddress {
289 } 291 }
290 292
291 @Override 293 @Override
294 + public int compareTo(IpAddress o) {
295 + Long lv = ((long) this.toRealInt()) & 0xffffffffL;
296 + Long rv = ((long) o.toRealInt()) & 0xffffffffL;
297 + return lv.compareTo(rv);
298 + }
299 +
300 + @Override
292 public int hashCode() { 301 public int hashCode() {
293 final int prime = 31; 302 final int prime = 31;
294 int result = 1; 303 int result = 1;
......