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