blob: 0b39bb746d2c8336a3c4595e2912e30ee77afa32 [file] [log] [blame]
Jonathan Hartab63aac2014-10-16 08:52:55 -07001package org.onlab.onos.sdnip.bgp;
2
3import java.net.InetAddress;
4import java.net.InetSocketAddress;
5import java.net.SocketAddress;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.HashMap;
10import java.util.Map;
11import java.util.concurrent.ConcurrentHashMap;
12import java.util.concurrent.ConcurrentMap;
13import java.util.concurrent.TimeUnit;
14
15import org.apache.commons.lang3.tuple.Pair;
16import org.jboss.netty.buffer.ChannelBuffer;
17import org.jboss.netty.buffer.ChannelBuffers;
18import org.jboss.netty.channel.ChannelHandlerContext;
19import org.jboss.netty.channel.ChannelStateEvent;
20import org.jboss.netty.channel.SimpleChannelHandler;
21import org.jboss.netty.util.HashedWheelTimer;
22import org.jboss.netty.util.Timeout;
23import org.jboss.netty.util.Timer;
24import org.jboss.netty.util.TimerTask;
25import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications;
26import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired;
27import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
28import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
29import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
35/**
36 * Class for handling the BGP peer sessions.
37 * There is one instance per each BGP peer session.
38 */
39public 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 =
Jonathan Hartbcae7bd2014-10-16 10:24:41 -07001229 IpPrefix.valueOf(IpAddress.valueOf((int) address).toInt(),
Jonathan Hartab63aac2014-10-16 08:52:55 -07001230 (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);
Jonathan Hartbcae7bd2014-10-16 10:24:41 -07001603 message.writeInt(bgpSessionManager.getMyBgpId().toInt());
Jonathan Hartab63aac2014-10-16 08:52:55 -07001604 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}