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