blob: b0b99008c037739a7f1d9419b0ad1675669c573b [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
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 Hartf4bd0482017-01-27 15:11:18 -080016
Jonathan Hart41349e92015-02-09 14:14:02 -080017package org.onosproject.routing.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070018
Jonathan Hartab63aac2014-10-16 08:52:55 -070019import org.jboss.netty.buffer.ChannelBuffer;
Jonathan Hartab63aac2014-10-16 08:52:55 -070020import org.jboss.netty.channel.ChannelHandlerContext;
21import org.jboss.netty.channel.ChannelStateEvent;
Pavlin Radoslavov0a714722014-12-03 13:44:08 -080022import org.jboss.netty.channel.ExceptionEvent;
Jonathan Hartab63aac2014-10-16 08:52:55 -070023import org.jboss.netty.channel.SimpleChannelHandler;
24import org.jboss.netty.util.HashedWheelTimer;
25import org.jboss.netty.util.Timeout;
26import org.jboss.netty.util.Timer;
27import org.jboss.netty.util.TimerTask;
Jonathan Harte9ef4f32014-12-04 17:49:22 -080028import org.onlab.packet.Ip4Address;
29import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080030import org.onlab.packet.Ip6Prefix;
Jonathan Hart41349e92015-02-09 14:14:02 -080031import org.onlab.packet.IpPrefix;
Jonathan Hartab63aac2014-10-16 08:52:55 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070035import static org.onlab.util.Tools.groupedThreads;
36
Jonathan Hart41349e92015-02-09 14:14:02 -080037import java.net.InetAddress;
38import java.net.InetSocketAddress;
39import java.util.Collection;
40import java.util.Collections;
41import java.util.concurrent.ConcurrentHashMap;
42import java.util.concurrent.ConcurrentMap;
43import java.util.concurrent.TimeUnit;
44
Jonathan Hartab63aac2014-10-16 08:52:55 -070045/**
46 * Class for handling the BGP peer sessions.
47 * There is one instance per each BGP peer session.
48 */
49public class BgpSession extends SimpleChannelHandler {
50 private static final Logger log =
51 LoggerFactory.getLogger(BgpSession.class);
52
53 private final BgpSessionManager bgpSessionManager;
54
55 // Local flag to indicate the session is closed.
56 // It is used to avoid the Netty's asynchronous closing of a channel.
57 private boolean isClosed = false;
58
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080059 // BGP session info: local and remote
60 private final BgpSessionInfo localInfo; // BGP session local info
61 private final BgpSessionInfo remoteInfo; // BGP session remote info
Jonathan Hartab63aac2014-10-16 08:52:55 -070062
63 // Timers state
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070064 private Timer timer = new HashedWheelTimer(groupedThreads("BgpSession", "timer-%d", log));
Jonathan Hartab63aac2014-10-16 08:52:55 -070065 private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
66 private volatile Timeout sessionTimeout; // Session timeout
67
68 // BGP RIB-IN routing entries from this peer
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080069 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRibIn4 =
70 new ConcurrentHashMap<>();
71 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRibIn6 =
Jonathan Hartab63aac2014-10-16 08:52:55 -070072 new ConcurrentHashMap<>();
73
74 /**
75 * Constructor for a given BGP Session Manager.
76 *
77 * @param bgpSessionManager the BGP Session Manager to use
78 */
79 BgpSession(BgpSessionManager bgpSessionManager) {
80 this.bgpSessionManager = bgpSessionManager;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080081 this.localInfo = new BgpSessionInfo();
82 this.remoteInfo = new BgpSessionInfo();
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080083
84 // NOTE: We support only BGP4
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080085 this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080086 }
87
88 /**
89 * Gets the BGP Session Manager.
90 *
91 * @return the BGP Session Manager
92 */
93 BgpSessionManager getBgpSessionManager() {
94 return bgpSessionManager;
Jonathan Hartab63aac2014-10-16 08:52:55 -070095 }
96
97 /**
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080098 * Gets the BGP Session local information.
99 *
100 * @return the BGP Session local information.
101 */
102 public BgpSessionInfo localInfo() {
103 return localInfo;
104 }
105
106 /**
107 * Gets the BGP Session remote information.
108 *
109 * @return the BGP Session remote information.
110 */
111 public BgpSessionInfo remoteInfo() {
112 return remoteInfo;
113 }
114
115 /**
116 * Gets the BGP Multiprotocol Extensions for the session.
117 *
118 * @return true if the BGP Multiprotocol Extensions are enabled for the
119 * session, otherwise false
120 */
121 public boolean mpExtensions() {
122 return remoteInfo.mpExtensions() && localInfo.mpExtensions();
123 }
124
125 /**
126 * Gets the BGP session 4 octet AS path capability.
127 *
128 * @return true when the BGP session is 4 octet AS path capable
129 */
130 public boolean isAs4OctetCapable() {
131 return remoteInfo.as4OctetCapability() &&
132 localInfo.as4OctetCapability();
133 }
134
135 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800136 * Gets the IPv4 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700137 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800138 * @return the IPv4 BGP RIB-IN routing entries
Jonathan Hartab63aac2014-10-16 08:52:55 -0700139 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800140 public Collection<BgpRouteEntry> getBgpRibIn4() {
141 return bgpRibIn4.values();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700142 }
143
144 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800145 * Gets the IPv6 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700146 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800147 * @return the IPv6 BGP RIB-IN routing entries
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800148 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800149 public Collection<BgpRouteEntry> getBgpRibIn6() {
150 return bgpRibIn6.values();
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800151 }
152
153 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800154 * Finds an IPv4 BGP routing entry for a prefix in the IPv4 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800155 *
156 * @param prefix the IPv4 prefix of the route to search for
157 * @return the IPv4 BGP routing entry if found, otherwise null
Jonathan Hartab63aac2014-10-16 08:52:55 -0700158 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800159 public BgpRouteEntry findBgpRoute(Ip4Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800160 return bgpRibIn4.get(prefix);
161 }
162
163 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800164 * Finds an IPv6 BGP routing entry for a prefix in the IPv6 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800165 *
166 * @param prefix the IPv6 prefix of the route to search for
167 * @return the IPv6 BGP routing entry if found, otherwise null
168 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800169 public BgpRouteEntry findBgpRoute(Ip6Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800170 return bgpRibIn6.get(prefix);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700171 }
172
173 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800174 * Finds a BGP routing entry for a prefix in the BGP RIB-IN. The prefix
175 * can be either IPv4 or IPv6.
176 *
177 * @param prefix the IP prefix of the route to search for
178 * @return the BGP routing entry if found, otherwise null
179 */
180 public BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700181 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800182 // IPv4 prefix
183 Ip4Prefix ip4Prefix = prefix.getIp4Prefix();
184 return bgpRibIn4.get(ip4Prefix);
185 }
186
187 // IPv6 prefix
188 Ip6Prefix ip6Prefix = prefix.getIp6Prefix();
189 return bgpRibIn6.get(ip6Prefix);
190 }
191
192 /**
193 * Adds a BGP route. The route can be either IPv4 or IPv6.
194 *
195 * @param bgpRouteEntry the BGP route entry to use
196 */
197 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700198 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800199 // IPv4 route
200 Ip4Prefix ip4Prefix = bgpRouteEntry.prefix().getIp4Prefix();
201 bgpRibIn4.put(ip4Prefix, bgpRouteEntry);
202 } else {
203 // IPv6 route
204 Ip6Prefix ip6Prefix = bgpRouteEntry.prefix().getIp6Prefix();
205 bgpRibIn6.put(ip6Prefix, bgpRouteEntry);
206 }
207 }
208
209 /**
210 * Removes an IPv4 BGP route for a prefix.
211 *
212 * @param prefix the prefix to use
213 * @return true if the route was found and removed, otherwise false
214 */
215 boolean removeBgpRoute(Ip4Prefix prefix) {
216 return (bgpRibIn4.remove(prefix) != null);
217 }
218
219 /**
220 * Removes an IPv6 BGP route for a prefix.
221 *
222 * @param prefix the prefix to use
223 * @return true if the route was found and removed, otherwise false
224 */
225 boolean removeBgpRoute(Ip6Prefix prefix) {
226 return (bgpRibIn6.remove(prefix) != null);
227 }
228
229 /**
230 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
231 *
232 * @param prefix the prefix to use
233 * @return true if the route was found and removed, otherwise false
234 */
235 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700236 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800237 return (bgpRibIn4.remove(prefix.getIp4Prefix()) != null); // IPv4
238 }
239 return (bgpRibIn6.remove(prefix.getIp6Prefix()) != null); // IPv6
240 }
241
242 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700243 * Tests whether the session is closed.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700244 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700245 * NOTE: We use this method to avoid the Netty's asynchronous closing
246 * of a channel.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700247 * </p>
248 * @return true if the session is closed
Jonathan Hartab63aac2014-10-16 08:52:55 -0700249 */
250 boolean isClosed() {
251 return isClosed;
252 }
253
254 /**
Jonathan Hartff071a22014-12-03 12:10:00 -0800255 * Closes the session.
256 *
257 * @param ctx the Channel Handler Context
258 */
259 void closeSession(ChannelHandlerContext ctx) {
260 timer.stop();
261 closeChannel(ctx);
262 }
263
264 /**
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800265 * Closes the Netty channel.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700266 *
267 * @param ctx the Channel Handler Context
268 */
269 void closeChannel(ChannelHandlerContext ctx) {
270 isClosed = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700271 ctx.getChannel().close();
272 }
273
274 @Override
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800275 public void channelOpen(ChannelHandlerContext ctx,
276 ChannelStateEvent channelEvent) {
277 bgpSessionManager.addSessionChannel(channelEvent.getChannel());
278 }
279
280 @Override
281 public void channelClosed(ChannelHandlerContext ctx,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800282 ChannelStateEvent channelEvent) {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800283 bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
284 }
285
286 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700287 public void channelConnected(ChannelHandlerContext ctx,
288 ChannelStateEvent channelEvent) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800289 localInfo.setAddress(ctx.getChannel().getLocalAddress());
290 remoteInfo.setAddress(ctx.getChannel().getRemoteAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700291
292 // Assign the local and remote IPv4 addresses
293 InetAddress inetAddr;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800294 if (localInfo.address() instanceof InetSocketAddress) {
295 inetAddr = ((InetSocketAddress) localInfo.address()).getAddress();
296 localInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700297 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800298 if (remoteInfo.address() instanceof InetSocketAddress) {
299 inetAddr = ((InetSocketAddress) remoteInfo.address()).getAddress();
300 remoteInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700301 }
302
303 log.debug("BGP Session Connected from {} on {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800304 remoteInfo.address(), localInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700305 if (!bgpSessionManager.peerConnected(this)) {
306 log.debug("Cannot setup BGP Session Connection from {}. Closing...",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800307 remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700308 ctx.getChannel().close();
309 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800310
311 //
312 // Assign the local BGP ID
313 // NOTE: This should be configuration-based
314 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800315 localInfo.setBgpId(bgpSessionManager.getMyBgpId());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700316 }
317
318 @Override
319 public void channelDisconnected(ChannelHandlerContext ctx,
320 ChannelStateEvent channelEvent) {
321 log.debug("BGP Session Disconnected from {} on {}",
322 ctx.getChannel().getRemoteAddress(),
323 ctx.getChannel().getLocalAddress());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800324 processChannelDisconnected();
325 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700326
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800327 @Override
328 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
329 log.debug("BGP Session Exception Caught from {} on {}: {}",
330 ctx.getChannel().getRemoteAddress(),
331 ctx.getChannel().getLocalAddress(),
332 e);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700333 log.debug("Exception:", e.getCause());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800334 processChannelDisconnected();
335 }
336
337 /**
338 * Processes the channel being disconnected.
339 */
340 private void processChannelDisconnected() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700341 //
342 // Withdraw the routes advertised by this BGP peer
343 //
344 // NOTE: We must initialize the RIB-IN before propagating the withdraws
345 // for further processing. Otherwise, the BGP Decision Process
346 // will use those routes again.
347 //
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800348 Collection<BgpRouteEntry> deletedRoutes4 = bgpRibIn4.values();
349 Collection<BgpRouteEntry> deletedRoutes6 = bgpRibIn6.values();
350 bgpRibIn4 = new ConcurrentHashMap<>();
351 bgpRibIn6 = new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700352
353 // Push the updates to the BGP Merged RIB
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800354 BgpRouteSelector bgpRouteSelector =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700355 bgpSessionManager.getBgpRouteSelector();
356 Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700357 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes4);
358 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes6);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700359
360 bgpSessionManager.peerDisconnected(this);
361 }
362
363 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700364 * Restarts the BGP KeepaliveTimer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800365 *
366 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700367 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800368 void restartKeepaliveTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800369 long localKeepaliveInterval = 0;
370
371 //
372 // Compute the local Keepalive interval
373 //
374 if (localInfo.holdtime() != 0) {
375 localKeepaliveInterval = Math.max(localInfo.holdtime() /
376 BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
377 BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
378 }
379
380 // Restart the Keepalive timer
Jonathan Hartab63aac2014-10-16 08:52:55 -0700381 if (localKeepaliveInterval == 0) {
382 return; // Nothing to do
383 }
384 keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
385 localKeepaliveInterval,
386 TimeUnit.SECONDS);
387 }
388
389 /**
390 * Task class for transmitting KEEPALIVE messages.
391 */
392 private final class TransmitKeepaliveTask implements TimerTask {
393 private final ChannelHandlerContext ctx;
394
395 /**
396 * Constructor for given Channel Handler Context.
397 *
398 * @param ctx the Channel Handler Context to use
399 */
400 TransmitKeepaliveTask(ChannelHandlerContext ctx) {
401 this.ctx = ctx;
402 }
403
404 @Override
405 public void run(Timeout timeout) throws Exception {
406 if (timeout.isCancelled()) {
407 return;
408 }
409 if (!ctx.getChannel().isOpen()) {
410 return;
411 }
412
413 // Transmit the KEEPALIVE
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800414 ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700415 ctx.getChannel().write(txMessage);
416
417 // Restart the KEEPALIVE timer
418 restartKeepaliveTimer(ctx);
419 }
420 }
421
422 /**
423 * Restarts the BGP Session Timeout Timer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800424 *
425 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700426 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800427 void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800428 if (remoteInfo.holdtime() == 0) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700429 return; // Nothing to do
430 }
431 if (sessionTimeout != null) {
432 sessionTimeout.cancel();
433 }
434 sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800435 remoteInfo.holdtime(),
Jonathan Hartab63aac2014-10-16 08:52:55 -0700436 TimeUnit.SECONDS);
437 }
438
439 /**
440 * Task class for BGP Session timeout.
441 */
442 private final class SessionTimeoutTask implements TimerTask {
443 private final ChannelHandlerContext ctx;
444
445 /**
446 * Constructor for given Channel Handler Context.
447 *
448 * @param ctx the Channel Handler Context to use
449 */
450 SessionTimeoutTask(ChannelHandlerContext ctx) {
451 this.ctx = ctx;
452 }
453
454 @Override
455 public void run(Timeout timeout) throws Exception {
456 if (timeout.isCancelled()) {
457 return;
458 }
459 if (!ctx.getChannel().isOpen()) {
460 return;
461 }
462
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800463 log.debug("BGP Session Timeout: peer {}", remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700464 //
465 // ERROR: Invalid Optional Parameter Length field: Unspecific
466 //
467 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -0800468 int errorCode = BgpConstants.Notifications.HoldTimerExpired.ERROR_CODE;
469 int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700470 ChannelBuffer txMessage =
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800471 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
472 null);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700473 ctx.getChannel().write(txMessage);
474 closeChannel(ctx);
475 }
476 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700477}