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