blob: f9d2a20acf52fc549b6ed73f63550e8e39dd3514 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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 Hart41349e92015-02-09 14:14:02 -080016package org.onosproject.routing.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070017
Jonathan Hartab63aac2014-10-16 08:52:55 -070018import org.jboss.netty.buffer.ChannelBuffer;
Jonathan Hartab63aac2014-10-16 08:52:55 -070019import org.jboss.netty.channel.ChannelHandlerContext;
20import org.jboss.netty.channel.ChannelStateEvent;
Pavlin Radoslavov0a714722014-12-03 13:44:08 -080021import org.jboss.netty.channel.ExceptionEvent;
Jonathan Hartab63aac2014-10-16 08:52:55 -070022import org.jboss.netty.channel.SimpleChannelHandler;
23import org.jboss.netty.util.HashedWheelTimer;
24import org.jboss.netty.util.Timeout;
25import org.jboss.netty.util.Timer;
26import org.jboss.netty.util.TimerTask;
Jonathan Harte9ef4f32014-12-04 17:49:22 -080027import org.onlab.packet.Ip4Address;
28import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080029import org.onlab.packet.Ip6Prefix;
Jonathan Hart41349e92015-02-09 14:14:02 -080030import org.onlab.packet.IpPrefix;
Jonathan Hartab63aac2014-10-16 08:52:55 -070031import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070034import static org.onlab.util.Tools.groupedThreads;
35
Jonathan Hart41349e92015-02-09 14:14:02 -080036import java.net.InetAddress;
37import java.net.InetSocketAddress;
38import java.util.Collection;
39import java.util.Collections;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.ConcurrentMap;
42import java.util.concurrent.TimeUnit;
43
Jonathan Hartab63aac2014-10-16 08:52:55 -070044/**
45 * Class for handling the BGP peer sessions.
46 * There is one instance per each BGP peer session.
47 */
48public class BgpSession extends SimpleChannelHandler {
49 private static final Logger log =
50 LoggerFactory.getLogger(BgpSession.class);
51
52 private final BgpSessionManager bgpSessionManager;
53
54 // Local flag to indicate the session is closed.
55 // It is used to avoid the Netty's asynchronous closing of a channel.
56 private boolean isClosed = false;
57
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080058 // BGP session info: local and remote
59 private final BgpSessionInfo localInfo; // BGP session local info
60 private final BgpSessionInfo remoteInfo; // BGP session remote info
Jonathan Hartab63aac2014-10-16 08:52:55 -070061
62 // Timers state
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070063 private Timer timer = new HashedWheelTimer(groupedThreads("BgpSession", "timer-%d", log));
Jonathan Hartab63aac2014-10-16 08:52:55 -070064 private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
65 private volatile Timeout sessionTimeout; // Session timeout
66
67 // BGP RIB-IN routing entries from this peer
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080068 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRibIn4 =
69 new ConcurrentHashMap<>();
70 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRibIn6 =
Jonathan Hartab63aac2014-10-16 08:52:55 -070071 new ConcurrentHashMap<>();
72
73 /**
74 * Constructor for a given BGP Session Manager.
75 *
76 * @param bgpSessionManager the BGP Session Manager to use
77 */
78 BgpSession(BgpSessionManager bgpSessionManager) {
79 this.bgpSessionManager = bgpSessionManager;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080080 this.localInfo = new BgpSessionInfo();
81 this.remoteInfo = new BgpSessionInfo();
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080082
83 // NOTE: We support only BGP4
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080084 this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080085 }
86
87 /**
88 * Gets the BGP Session Manager.
89 *
90 * @return the BGP Session Manager
91 */
92 BgpSessionManager getBgpSessionManager() {
93 return bgpSessionManager;
Jonathan Hartab63aac2014-10-16 08:52:55 -070094 }
95
96 /**
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080097 * Gets the BGP Session local information.
98 *
99 * @return the BGP Session local information.
100 */
101 public BgpSessionInfo localInfo() {
102 return localInfo;
103 }
104
105 /**
106 * Gets the BGP Session remote information.
107 *
108 * @return the BGP Session remote information.
109 */
110 public BgpSessionInfo remoteInfo() {
111 return remoteInfo;
112 }
113
114 /**
115 * Gets the BGP Multiprotocol Extensions for the session.
116 *
117 * @return true if the BGP Multiprotocol Extensions are enabled for the
118 * session, otherwise false
119 */
120 public boolean mpExtensions() {
121 return remoteInfo.mpExtensions() && localInfo.mpExtensions();
122 }
123
124 /**
125 * Gets the BGP session 4 octet AS path capability.
126 *
127 * @return true when the BGP session is 4 octet AS path capable
128 */
129 public boolean isAs4OctetCapable() {
130 return remoteInfo.as4OctetCapability() &&
131 localInfo.as4OctetCapability();
132 }
133
134 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800135 * Gets the IPv4 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700136 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800137 * @return the IPv4 BGP RIB-IN routing entries
Jonathan Hartab63aac2014-10-16 08:52:55 -0700138 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800139 public Collection<BgpRouteEntry> getBgpRibIn4() {
140 return bgpRibIn4.values();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700141 }
142
143 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800144 * Gets the IPv6 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700145 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800146 * @return the IPv6 BGP RIB-IN routing entries
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800147 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800148 public Collection<BgpRouteEntry> getBgpRibIn6() {
149 return bgpRibIn6.values();
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800150 }
151
152 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800153 * Finds an IPv4 BGP routing entry for a prefix in the IPv4 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800154 *
155 * @param prefix the IPv4 prefix of the route to search for
156 * @return the IPv4 BGP routing entry if found, otherwise null
Jonathan Hartab63aac2014-10-16 08:52:55 -0700157 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800158 public BgpRouteEntry findBgpRoute(Ip4Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800159 return bgpRibIn4.get(prefix);
160 }
161
162 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800163 * Finds an IPv6 BGP routing entry for a prefix in the IPv6 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800164 *
165 * @param prefix the IPv6 prefix of the route to search for
166 * @return the IPv6 BGP routing entry if found, otherwise null
167 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800168 public BgpRouteEntry findBgpRoute(Ip6Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800169 return bgpRibIn6.get(prefix);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700170 }
171
172 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800173 * Finds a BGP routing entry for a prefix in the BGP RIB-IN. The prefix
174 * can be either IPv4 or IPv6.
175 *
176 * @param prefix the IP prefix of the route to search for
177 * @return the BGP routing entry if found, otherwise null
178 */
179 public BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700180 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800181 // IPv4 prefix
182 Ip4Prefix ip4Prefix = prefix.getIp4Prefix();
183 return bgpRibIn4.get(ip4Prefix);
184 }
185
186 // IPv6 prefix
187 Ip6Prefix ip6Prefix = prefix.getIp6Prefix();
188 return bgpRibIn6.get(ip6Prefix);
189 }
190
191 /**
192 * Adds a BGP route. The route can be either IPv4 or IPv6.
193 *
194 * @param bgpRouteEntry the BGP route entry to use
195 */
196 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700197 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800198 // IPv4 route
199 Ip4Prefix ip4Prefix = bgpRouteEntry.prefix().getIp4Prefix();
200 bgpRibIn4.put(ip4Prefix, bgpRouteEntry);
201 } else {
202 // IPv6 route
203 Ip6Prefix ip6Prefix = bgpRouteEntry.prefix().getIp6Prefix();
204 bgpRibIn6.put(ip6Prefix, bgpRouteEntry);
205 }
206 }
207
208 /**
209 * Removes an IPv4 BGP route for a prefix.
210 *
211 * @param prefix the prefix to use
212 * @return true if the route was found and removed, otherwise false
213 */
214 boolean removeBgpRoute(Ip4Prefix prefix) {
215 return (bgpRibIn4.remove(prefix) != null);
216 }
217
218 /**
219 * Removes an IPv6 BGP route for a prefix.
220 *
221 * @param prefix the prefix to use
222 * @return true if the route was found and removed, otherwise false
223 */
224 boolean removeBgpRoute(Ip6Prefix prefix) {
225 return (bgpRibIn6.remove(prefix) != null);
226 }
227
228 /**
229 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
230 *
231 * @param prefix the prefix to use
232 * @return true if the route was found and removed, otherwise false
233 */
234 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700235 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800236 return (bgpRibIn4.remove(prefix.getIp4Prefix()) != null); // IPv4
237 }
238 return (bgpRibIn6.remove(prefix.getIp6Prefix()) != null); // IPv6
239 }
240
241 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700242 * Tests whether the session is closed.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700243 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700244 * NOTE: We use this method to avoid the Netty's asynchronous closing
245 * of a channel.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700246 * </p>
247 * @return true if the session is closed
Jonathan Hartab63aac2014-10-16 08:52:55 -0700248 */
249 boolean isClosed() {
250 return isClosed;
251 }
252
253 /**
Jonathan Hartff071a22014-12-03 12:10:00 -0800254 * Closes the session.
255 *
256 * @param ctx the Channel Handler Context
257 */
258 void closeSession(ChannelHandlerContext ctx) {
259 timer.stop();
260 closeChannel(ctx);
261 }
262
263 /**
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800264 * Closes the Netty channel.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700265 *
266 * @param ctx the Channel Handler Context
267 */
268 void closeChannel(ChannelHandlerContext ctx) {
269 isClosed = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700270 ctx.getChannel().close();
271 }
272
273 @Override
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800274 public void channelOpen(ChannelHandlerContext ctx,
275 ChannelStateEvent channelEvent) {
276 bgpSessionManager.addSessionChannel(channelEvent.getChannel());
277 }
278
279 @Override
280 public void channelClosed(ChannelHandlerContext ctx,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800281 ChannelStateEvent channelEvent) {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800282 bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
283 }
284
285 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700286 public void channelConnected(ChannelHandlerContext ctx,
287 ChannelStateEvent channelEvent) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800288 localInfo.setAddress(ctx.getChannel().getLocalAddress());
289 remoteInfo.setAddress(ctx.getChannel().getRemoteAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700290
291 // Assign the local and remote IPv4 addresses
292 InetAddress inetAddr;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800293 if (localInfo.address() instanceof InetSocketAddress) {
294 inetAddr = ((InetSocketAddress) localInfo.address()).getAddress();
295 localInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700296 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800297 if (remoteInfo.address() instanceof InetSocketAddress) {
298 inetAddr = ((InetSocketAddress) remoteInfo.address()).getAddress();
299 remoteInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700300 }
301
302 log.debug("BGP Session Connected from {} on {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800303 remoteInfo.address(), localInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700304 if (!bgpSessionManager.peerConnected(this)) {
305 log.debug("Cannot setup BGP Session Connection from {}. Closing...",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800306 remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700307 ctx.getChannel().close();
308 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800309
310 //
311 // Assign the local BGP ID
312 // NOTE: This should be configuration-based
313 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800314 localInfo.setBgpId(bgpSessionManager.getMyBgpId());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700315 }
316
317 @Override
318 public void channelDisconnected(ChannelHandlerContext ctx,
319 ChannelStateEvent channelEvent) {
320 log.debug("BGP Session Disconnected from {} on {}",
321 ctx.getChannel().getRemoteAddress(),
322 ctx.getChannel().getLocalAddress());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800323 processChannelDisconnected();
324 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700325
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800326 @Override
327 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
328 log.debug("BGP Session Exception Caught from {} on {}: {}",
329 ctx.getChannel().getRemoteAddress(),
330 ctx.getChannel().getLocalAddress(),
331 e);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700332 log.debug("Exception:", e.getCause());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800333 processChannelDisconnected();
334 }
335
336 /**
337 * Processes the channel being disconnected.
338 */
339 private void processChannelDisconnected() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700340 //
341 // Withdraw the routes advertised by this BGP peer
342 //
343 // NOTE: We must initialize the RIB-IN before propagating the withdraws
344 // for further processing. Otherwise, the BGP Decision Process
345 // will use those routes again.
346 //
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800347 Collection<BgpRouteEntry> deletedRoutes4 = bgpRibIn4.values();
348 Collection<BgpRouteEntry> deletedRoutes6 = bgpRibIn6.values();
349 bgpRibIn4 = new ConcurrentHashMap<>();
350 bgpRibIn6 = new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700351
352 // Push the updates to the BGP Merged RIB
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800353 BgpRouteSelector bgpRouteSelector =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700354 bgpSessionManager.getBgpRouteSelector();
355 Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700356 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes4);
357 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes6);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700358
359 bgpSessionManager.peerDisconnected(this);
360 }
361
362 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700363 * Restarts the BGP KeepaliveTimer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800364 *
365 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700366 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800367 void restartKeepaliveTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800368 long localKeepaliveInterval = 0;
369
370 //
371 // Compute the local Keepalive interval
372 //
373 if (localInfo.holdtime() != 0) {
374 localKeepaliveInterval = Math.max(localInfo.holdtime() /
375 BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
376 BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
377 }
378
379 // Restart the Keepalive timer
Jonathan Hartab63aac2014-10-16 08:52:55 -0700380 if (localKeepaliveInterval == 0) {
381 return; // Nothing to do
382 }
383 keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
384 localKeepaliveInterval,
385 TimeUnit.SECONDS);
386 }
387
388 /**
389 * Task class for transmitting KEEPALIVE messages.
390 */
391 private final class TransmitKeepaliveTask implements TimerTask {
392 private final ChannelHandlerContext ctx;
393
394 /**
395 * Constructor for given Channel Handler Context.
396 *
397 * @param ctx the Channel Handler Context to use
398 */
399 TransmitKeepaliveTask(ChannelHandlerContext ctx) {
400 this.ctx = ctx;
401 }
402
403 @Override
404 public void run(Timeout timeout) throws Exception {
405 if (timeout.isCancelled()) {
406 return;
407 }
408 if (!ctx.getChannel().isOpen()) {
409 return;
410 }
411
412 // Transmit the KEEPALIVE
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800413 ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700414 ctx.getChannel().write(txMessage);
415
416 // Restart the KEEPALIVE timer
417 restartKeepaliveTimer(ctx);
418 }
419 }
420
421 /**
422 * Restarts the BGP Session Timeout Timer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800423 *
424 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700425 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800426 void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800427 if (remoteInfo.holdtime() == 0) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700428 return; // Nothing to do
429 }
430 if (sessionTimeout != null) {
431 sessionTimeout.cancel();
432 }
433 sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800434 remoteInfo.holdtime(),
Jonathan Hartab63aac2014-10-16 08:52:55 -0700435 TimeUnit.SECONDS);
436 }
437
438 /**
439 * Task class for BGP Session timeout.
440 */
441 private final class SessionTimeoutTask implements TimerTask {
442 private final ChannelHandlerContext ctx;
443
444 /**
445 * Constructor for given Channel Handler Context.
446 *
447 * @param ctx the Channel Handler Context to use
448 */
449 SessionTimeoutTask(ChannelHandlerContext ctx) {
450 this.ctx = ctx;
451 }
452
453 @Override
454 public void run(Timeout timeout) throws Exception {
455 if (timeout.isCancelled()) {
456 return;
457 }
458 if (!ctx.getChannel().isOpen()) {
459 return;
460 }
461
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800462 log.debug("BGP Session Timeout: peer {}", remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700463 //
464 // ERROR: Invalid Optional Parameter Length field: Unspecific
465 //
466 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -0800467 int errorCode = BgpConstants.Notifications.HoldTimerExpired.ERROR_CODE;
468 int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700469 ChannelBuffer txMessage =
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800470 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
471 null);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700472 ctx.getChannel().write(txMessage);
473 closeChannel(ctx);
474 }
475 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700476}