blob: 0352520477c807ccd2fbef149662d805fbb1f45c [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
Jonathan Hart41349e92015-02-09 14:14:02 -080034import java.net.InetAddress;
35import java.net.InetSocketAddress;
36import java.util.Collection;
37import java.util.Collections;
38import java.util.concurrent.ConcurrentHashMap;
39import java.util.concurrent.ConcurrentMap;
40import java.util.concurrent.TimeUnit;
41
Jonathan Hartab63aac2014-10-16 08:52:55 -070042/**
43 * Class for handling the BGP peer sessions.
44 * There is one instance per each BGP peer session.
45 */
46public class BgpSession extends SimpleChannelHandler {
47 private static final Logger log =
48 LoggerFactory.getLogger(BgpSession.class);
49
50 private final BgpSessionManager bgpSessionManager;
51
52 // Local flag to indicate the session is closed.
53 // It is used to avoid the Netty's asynchronous closing of a channel.
54 private boolean isClosed = false;
55
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080056 // BGP session info: local and remote
57 private final BgpSessionInfo localInfo; // BGP session local info
58 private final BgpSessionInfo remoteInfo; // BGP session remote info
Jonathan Hartab63aac2014-10-16 08:52:55 -070059
60 // Timers state
61 private Timer timer = new HashedWheelTimer();
62 private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
63 private volatile Timeout sessionTimeout; // Session timeout
64
65 // BGP RIB-IN routing entries from this peer
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080066 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRibIn4 =
67 new ConcurrentHashMap<>();
68 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRibIn6 =
Jonathan Hartab63aac2014-10-16 08:52:55 -070069 new ConcurrentHashMap<>();
70
71 /**
72 * Constructor for a given BGP Session Manager.
73 *
74 * @param bgpSessionManager the BGP Session Manager to use
75 */
76 BgpSession(BgpSessionManager bgpSessionManager) {
77 this.bgpSessionManager = bgpSessionManager;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080078 this.localInfo = new BgpSessionInfo();
79 this.remoteInfo = new BgpSessionInfo();
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080080
81 // NOTE: We support only BGP4
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080082 this.localInfo.setBgpVersion(BgpConstants.BGP_VERSION);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080083 }
84
85 /**
86 * Gets the BGP Session Manager.
87 *
88 * @return the BGP Session Manager
89 */
90 BgpSessionManager getBgpSessionManager() {
91 return bgpSessionManager;
Jonathan Hartab63aac2014-10-16 08:52:55 -070092 }
93
94 /**
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080095 * Gets the BGP Session local information.
96 *
97 * @return the BGP Session local information.
98 */
99 public BgpSessionInfo localInfo() {
100 return localInfo;
101 }
102
103 /**
104 * Gets the BGP Session remote information.
105 *
106 * @return the BGP Session remote information.
107 */
108 public BgpSessionInfo remoteInfo() {
109 return remoteInfo;
110 }
111
112 /**
113 * Gets the BGP Multiprotocol Extensions for the session.
114 *
115 * @return true if the BGP Multiprotocol Extensions are enabled for the
116 * session, otherwise false
117 */
118 public boolean mpExtensions() {
119 return remoteInfo.mpExtensions() && localInfo.mpExtensions();
120 }
121
122 /**
123 * Gets the BGP session 4 octet AS path capability.
124 *
125 * @return true when the BGP session is 4 octet AS path capable
126 */
127 public boolean isAs4OctetCapable() {
128 return remoteInfo.as4OctetCapability() &&
129 localInfo.as4OctetCapability();
130 }
131
132 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800133 * Gets the IPv4 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700134 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800135 * @return the IPv4 BGP RIB-IN routing entries
Jonathan Hartab63aac2014-10-16 08:52:55 -0700136 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800137 public Collection<BgpRouteEntry> getBgpRibIn4() {
138 return bgpRibIn4.values();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700139 }
140
141 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800142 * Gets the IPv6 BGP RIB-IN routing entries.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700143 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800144 * @return the IPv6 BGP RIB-IN routing entries
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800145 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800146 public Collection<BgpRouteEntry> getBgpRibIn6() {
147 return bgpRibIn6.values();
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800148 }
149
150 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800151 * Finds an IPv4 BGP routing entry for a prefix in the IPv4 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800152 *
153 * @param prefix the IPv4 prefix of the route to search for
154 * @return the IPv4 BGP routing entry if found, otherwise null
Jonathan Hartab63aac2014-10-16 08:52:55 -0700155 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800156 public BgpRouteEntry findBgpRoute(Ip4Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800157 return bgpRibIn4.get(prefix);
158 }
159
160 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800161 * Finds an IPv6 BGP routing entry for a prefix in the IPv6 BGP RIB-IN.
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800162 *
163 * @param prefix the IPv6 prefix of the route to search for
164 * @return the IPv6 BGP routing entry if found, otherwise null
165 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800166 public BgpRouteEntry findBgpRoute(Ip6Prefix prefix) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800167 return bgpRibIn6.get(prefix);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700168 }
169
170 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800171 * Finds a BGP routing entry for a prefix in the BGP RIB-IN. The prefix
172 * can be either IPv4 or IPv6.
173 *
174 * @param prefix the IP prefix of the route to search for
175 * @return the BGP routing entry if found, otherwise null
176 */
177 public BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700178 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800179 // IPv4 prefix
180 Ip4Prefix ip4Prefix = prefix.getIp4Prefix();
181 return bgpRibIn4.get(ip4Prefix);
182 }
183
184 // IPv6 prefix
185 Ip6Prefix ip6Prefix = prefix.getIp6Prefix();
186 return bgpRibIn6.get(ip6Prefix);
187 }
188
189 /**
190 * Adds a BGP route. The route can be either IPv4 or IPv6.
191 *
192 * @param bgpRouteEntry the BGP route entry to use
193 */
194 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700195 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800196 // IPv4 route
197 Ip4Prefix ip4Prefix = bgpRouteEntry.prefix().getIp4Prefix();
198 bgpRibIn4.put(ip4Prefix, bgpRouteEntry);
199 } else {
200 // IPv6 route
201 Ip6Prefix ip6Prefix = bgpRouteEntry.prefix().getIp6Prefix();
202 bgpRibIn6.put(ip6Prefix, bgpRouteEntry);
203 }
204 }
205
206 /**
207 * Removes an IPv4 BGP route for a prefix.
208 *
209 * @param prefix the prefix to use
210 * @return true if the route was found and removed, otherwise false
211 */
212 boolean removeBgpRoute(Ip4Prefix prefix) {
213 return (bgpRibIn4.remove(prefix) != null);
214 }
215
216 /**
217 * Removes an IPv6 BGP route for a prefix.
218 *
219 * @param prefix the prefix to use
220 * @return true if the route was found and removed, otherwise false
221 */
222 boolean removeBgpRoute(Ip6Prefix prefix) {
223 return (bgpRibIn6.remove(prefix) != null);
224 }
225
226 /**
227 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
228 *
229 * @param prefix the prefix to use
230 * @return true if the route was found and removed, otherwise false
231 */
232 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700233 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800234 return (bgpRibIn4.remove(prefix.getIp4Prefix()) != null); // IPv4
235 }
236 return (bgpRibIn6.remove(prefix.getIp6Prefix()) != null); // IPv6
237 }
238
239 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700240 * Tests whether the session is closed.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700241 * <p>
Jonathan Hartab63aac2014-10-16 08:52:55 -0700242 * NOTE: We use this method to avoid the Netty's asynchronous closing
243 * of a channel.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700244 * </p>
245 * @return true if the session is closed
Jonathan Hartab63aac2014-10-16 08:52:55 -0700246 */
247 boolean isClosed() {
248 return isClosed;
249 }
250
251 /**
Jonathan Hartff071a22014-12-03 12:10:00 -0800252 * Closes the session.
253 *
254 * @param ctx the Channel Handler Context
255 */
256 void closeSession(ChannelHandlerContext ctx) {
257 timer.stop();
258 closeChannel(ctx);
259 }
260
261 /**
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800262 * Closes the Netty channel.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700263 *
264 * @param ctx the Channel Handler Context
265 */
266 void closeChannel(ChannelHandlerContext ctx) {
267 isClosed = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700268 ctx.getChannel().close();
269 }
270
271 @Override
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800272 public void channelOpen(ChannelHandlerContext ctx,
273 ChannelStateEvent channelEvent) {
274 bgpSessionManager.addSessionChannel(channelEvent.getChannel());
275 }
276
277 @Override
278 public void channelClosed(ChannelHandlerContext ctx,
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800279 ChannelStateEvent channelEvent) {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800280 bgpSessionManager.removeSessionChannel(channelEvent.getChannel());
281 }
282
283 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700284 public void channelConnected(ChannelHandlerContext ctx,
285 ChannelStateEvent channelEvent) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800286 localInfo.setAddress(ctx.getChannel().getLocalAddress());
287 remoteInfo.setAddress(ctx.getChannel().getRemoteAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700288
289 // Assign the local and remote IPv4 addresses
290 InetAddress inetAddr;
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800291 if (localInfo.address() instanceof InetSocketAddress) {
292 inetAddr = ((InetSocketAddress) localInfo.address()).getAddress();
293 localInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700294 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800295 if (remoteInfo.address() instanceof InetSocketAddress) {
296 inetAddr = ((InetSocketAddress) remoteInfo.address()).getAddress();
297 remoteInfo.setIp4Address(Ip4Address.valueOf(inetAddr.getAddress()));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700298 }
299
300 log.debug("BGP Session Connected from {} on {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800301 remoteInfo.address(), localInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700302 if (!bgpSessionManager.peerConnected(this)) {
303 log.debug("Cannot setup BGP Session Connection from {}. Closing...",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800304 remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700305 ctx.getChannel().close();
306 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800307
308 //
309 // Assign the local BGP ID
310 // NOTE: This should be configuration-based
311 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800312 localInfo.setBgpId(bgpSessionManager.getMyBgpId());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700313 }
314
315 @Override
316 public void channelDisconnected(ChannelHandlerContext ctx,
317 ChannelStateEvent channelEvent) {
318 log.debug("BGP Session Disconnected from {} on {}",
319 ctx.getChannel().getRemoteAddress(),
320 ctx.getChannel().getLocalAddress());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800321 processChannelDisconnected();
322 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700323
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800324 @Override
325 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
326 log.debug("BGP Session Exception Caught from {} on {}: {}",
327 ctx.getChannel().getRemoteAddress(),
328 ctx.getChannel().getLocalAddress(),
329 e);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700330 log.debug("Exception:", e.getCause());
Pavlin Radoslavov0a714722014-12-03 13:44:08 -0800331 processChannelDisconnected();
332 }
333
334 /**
335 * Processes the channel being disconnected.
336 */
337 private void processChannelDisconnected() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700338 //
339 // Withdraw the routes advertised by this BGP peer
340 //
341 // NOTE: We must initialize the RIB-IN before propagating the withdraws
342 // for further processing. Otherwise, the BGP Decision Process
343 // will use those routes again.
344 //
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800345 Collection<BgpRouteEntry> deletedRoutes4 = bgpRibIn4.values();
346 Collection<BgpRouteEntry> deletedRoutes6 = bgpRibIn6.values();
347 bgpRibIn4 = new ConcurrentHashMap<>();
348 bgpRibIn6 = new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700349
350 // Push the updates to the BGP Merged RIB
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800351 BgpRouteSelector bgpRouteSelector =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700352 bgpSessionManager.getBgpRouteSelector();
353 Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700354 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes4);
355 bgpRouteSelector.routeUpdates(addedRoutes, deletedRoutes6);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700356
357 bgpSessionManager.peerDisconnected(this);
358 }
359
360 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700361 * Restarts the BGP KeepaliveTimer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800362 *
363 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700364 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800365 void restartKeepaliveTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800366 long localKeepaliveInterval = 0;
367
368 //
369 // Compute the local Keepalive interval
370 //
371 if (localInfo.holdtime() != 0) {
372 localKeepaliveInterval = Math.max(localInfo.holdtime() /
373 BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
374 BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
375 }
376
377 // Restart the Keepalive timer
Jonathan Hartab63aac2014-10-16 08:52:55 -0700378 if (localKeepaliveInterval == 0) {
379 return; // Nothing to do
380 }
381 keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
382 localKeepaliveInterval,
383 TimeUnit.SECONDS);
384 }
385
386 /**
387 * Task class for transmitting KEEPALIVE messages.
388 */
389 private final class TransmitKeepaliveTask implements TimerTask {
390 private final ChannelHandlerContext ctx;
391
392 /**
393 * Constructor for given Channel Handler Context.
394 *
395 * @param ctx the Channel Handler Context to use
396 */
397 TransmitKeepaliveTask(ChannelHandlerContext ctx) {
398 this.ctx = ctx;
399 }
400
401 @Override
402 public void run(Timeout timeout) throws Exception {
403 if (timeout.isCancelled()) {
404 return;
405 }
406 if (!ctx.getChannel().isOpen()) {
407 return;
408 }
409
410 // Transmit the KEEPALIVE
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800411 ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700412 ctx.getChannel().write(txMessage);
413
414 // Restart the KEEPALIVE timer
415 restartKeepaliveTimer(ctx);
416 }
417 }
418
419 /**
420 * Restarts the BGP Session Timeout Timer.
Charles M.C. Chanbc0e84d2015-01-13 23:53:09 +0800421 *
422 * @param ctx the Channel Handler Context to use
Jonathan Hartab63aac2014-10-16 08:52:55 -0700423 */
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800424 void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800425 if (remoteInfo.holdtime() == 0) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700426 return; // Nothing to do
427 }
428 if (sessionTimeout != null) {
429 sessionTimeout.cancel();
430 }
431 sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800432 remoteInfo.holdtime(),
Jonathan Hartab63aac2014-10-16 08:52:55 -0700433 TimeUnit.SECONDS);
434 }
435
436 /**
437 * Task class for BGP Session timeout.
438 */
439 private final class SessionTimeoutTask implements TimerTask {
440 private final ChannelHandlerContext ctx;
441
442 /**
443 * Constructor for given Channel Handler Context.
444 *
445 * @param ctx the Channel Handler Context to use
446 */
447 SessionTimeoutTask(ChannelHandlerContext ctx) {
448 this.ctx = ctx;
449 }
450
451 @Override
452 public void run(Timeout timeout) throws Exception {
453 if (timeout.isCancelled()) {
454 return;
455 }
456 if (!ctx.getChannel().isOpen()) {
457 return;
458 }
459
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800460 log.debug("BGP Session Timeout: peer {}", remoteInfo.address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700461 //
462 // ERROR: Invalid Optional Parameter Length field: Unspecific
463 //
464 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -0800465 int errorCode = BgpConstants.Notifications.HoldTimerExpired.ERROR_CODE;
466 int errorSubcode = BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700467 ChannelBuffer txMessage =
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800468 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
469 null);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700470 ctx.getChannel().write(txMessage);
471 closeChannel(ctx);
472 }
473 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700474}