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