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