blob: 7f47a02da8361e0fa83d935ba1d0b15cd1aaec30 [file] [log] [blame]
Satish Ke107e662015-09-21 19:00:17 +05301/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.bgp.controller.impl;
18
Shashikanth VH6de20d32015-10-09 12:04:13 +053019import java.io.IOException;
Vidyashree Ramaee293252015-11-18 17:00:11 +053020import java.net.InetAddress;
Shashikanth VH6de20d32015-10-09 12:04:13 +053021import java.net.InetSocketAddress;
22import java.net.SocketAddress;
Vidyashree Ramaee293252015-11-18 17:00:11 +053023import java.net.UnknownHostException;
Shashikanth VH6de20d32015-10-09 12:04:13 +053024import java.nio.channels.ClosedChannelException;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053025import java.util.Collections;
Shashikanth VH6de20d32015-10-09 12:04:13 +053026import java.util.Date;
27import java.util.List;
Vidyashree Ramaee293252015-11-18 17:00:11 +053028import java.util.LinkedList;
29import java.util.ListIterator;
Shashikanth VH6de20d32015-10-09 12:04:13 +053030import java.util.concurrent.RejectedExecutionException;
31
Vidyashree Ramaee293252015-11-18 17:00:11 +053032import org.jboss.netty.buffer.ChannelBuffer;
33import org.jboss.netty.buffer.ChannelBuffers;
Shashikanth VH6de20d32015-10-09 12:04:13 +053034import org.jboss.netty.channel.Channel;
35import org.jboss.netty.channel.ChannelHandlerContext;
36import org.jboss.netty.channel.ChannelStateEvent;
37import org.jboss.netty.channel.ExceptionEvent;
38import org.jboss.netty.channel.MessageEvent;
Satish Ke107e662015-09-21 19:00:17 +053039import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
Shashikanth VH6de20d32015-10-09 12:04:13 +053040import org.jboss.netty.handler.timeout.ReadTimeoutException;
41import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053042import org.onlab.packet.Ip4Address;
Shashikanth VH6de20d32015-10-09 12:04:13 +053043import org.onlab.packet.IpAddress;
44import org.onosproject.bgp.controller.BGPCfg;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053045import org.onosproject.bgp.controller.BGPController;
Shashikanth VH6de20d32015-10-09 12:04:13 +053046import org.onosproject.bgp.controller.BGPId;
47import org.onosproject.bgp.controller.BGPPeer;
48import org.onosproject.bgp.controller.BGPPeerCfg;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053049import org.onosproject.bgp.controller.impl.BGPControllerImpl.BGPPeerManagerImpl;
Shashikanth VH6de20d32015-10-09 12:04:13 +053050import org.onosproject.bgpio.exceptions.BGPParseException;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053051import org.onosproject.bgpio.protocol.BGPFactory;
Shashikanth VH6de20d32015-10-09 12:04:13 +053052import org.onosproject.bgpio.protocol.BGPMessage;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053053import org.onosproject.bgpio.protocol.BGPOpenMsg;
Shashikanth VH6de20d32015-10-09 12:04:13 +053054import org.onosproject.bgpio.protocol.BGPType;
55import org.onosproject.bgpio.protocol.BGPVersion;
Vidyashree Ramaee293252015-11-18 17:00:11 +053056import org.onosproject.bgpio.types.BGPErrorType;
57import org.onosproject.bgpio.types.BGPValueType;
58import org.onosproject.bgpio.types.FourOctetAsNumCapabilityTlv;
59import org.onosproject.bgpio.types.MultiProtocolExtnCapabilityTlv;
Shashikanth VH6de20d32015-10-09 12:04:13 +053060import org.slf4j.Logger;
61import org.slf4j.LoggerFactory;
Satish Ke107e662015-09-21 19:00:17 +053062
63/**
64 * Channel handler deals with the bgp peer connection and dispatches messages from peer to the appropriate locations.
65 */
66class BGPChannelHandler extends IdleStateAwareChannelHandler {
67
Shashikanth VH6de20d32015-10-09 12:04:13 +053068 private static final Logger log = LoggerFactory.getLogger(BGPChannelHandler.class);
Vidyashree Ramaee293252015-11-18 17:00:11 +053069 static final int BGP_MIN_HOLDTIME = 3;
Shashikanth VH6de20d32015-10-09 12:04:13 +053070 static final int BGP_MAX_KEEPALIVE_INTERVAL = 3;
71 private BGPPeer bgpPeer;
72 private BGPId thisbgpId;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053073 private Channel channel;
Shashikanth VH6de20d32015-10-09 12:04:13 +053074 private BGPKeepAliveTimer keepAliveTimer = null;
75 private short peerHoldTime = 0;
76 private short negotiatedHoldTime = 0;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053077 private long peerAsNum;
Shashikanth VH6de20d32015-10-09 12:04:13 +053078 private int peerIdentifier;
79 private BGPPacketStatsImpl bgpPacketStats;
80 static final int MAX_WRONG_COUNT_PACKET = 5;
Vidyashree Ramaee293252015-11-18 17:00:11 +053081 static final byte MULTI_PROTOCOL_EXTN_CAPA_TYPE = 1;
82 static final byte FOUR_OCTET_AS_NUM_CAPA_TYPE = 65;
83 static final int AS_TRANS = 23456;
84 static final int MAX_AS2_NUM = 65535;
85 static final short AFI = 16388;
86 static final byte RES = 0;
87 static final byte SAFI = 71;
Shashikanth VH6de20d32015-10-09 12:04:13 +053088
89 // State needs to be volatile because the HandshakeTimeoutHandler
90 // needs to check if the handshake is complete
91 private volatile ChannelState state;
92
93 // When a bgp peer with a ip addresss is found (i.e we already have a
94 // connected peer with the same ip), the new peer is immediately
95 // disconnected. At that point netty callsback channelDisconnected() which
Shashikanth VH9f8afb42015-11-04 18:00:30 +053096 // proceeds to cleaup peer state - we need to ensure that it does not
97 // cleanup
Shashikanth VH6de20d32015-10-09 12:04:13 +053098 // peer state for the older (still connected) peer
99 private volatile Boolean duplicateBGPIdFound;
100 // Indicates the bgp version used by this bgp peer
101 protected BGPVersion bgpVersion;
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530102 private BGPController bgpController;
103 protected BGPFactory factory4;
104 private boolean isIbgpSession;
105 private BgpSessionInfoImpl sessionInfo;
106 private BGPPeerManagerImpl peerManager;
Shashikanth VH6de20d32015-10-09 12:04:13 +0530107 private InetSocketAddress inetAddress;
108 private IpAddress ipAddress;
109 private SocketAddress address;
110 private String peerAddr;
111 private BGPCfg bgpconfig;
112
Satish Ke107e662015-09-21 19:00:17 +0530113 /**
114 * Create a new unconnected BGPChannelHandler.
115 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530116 * @param bgpController bgp controller
Satish Ke107e662015-09-21 19:00:17 +0530117 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530118 BGPChannelHandler(BGPController bgpController) {
119 this.bgpController = bgpController;
120 this.peerManager = (BGPPeerManagerImpl) bgpController.peerManager();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530121 this.state = ChannelState.IDLE;
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530122 this.factory4 = Controller.getBGPMessageFactory4();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530123 this.duplicateBGPIdFound = Boolean.FALSE;
124 this.bgpPacketStats = new BGPPacketStatsImpl();
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530125 this.bgpconfig = bgpController.getConfig();
Satish Ke107e662015-09-21 19:00:17 +0530126 }
Shashikanth VH6de20d32015-10-09 12:04:13 +0530127
128 // To disconnect peer session.
129 public void disconnectPeer() {
130 bgpPeer.disconnectPeer();
131 }
132
133 // *************************
134 // Channel State Machine
135 // *************************
136
137 /**
138 * The state machine for handling the peer/channel state. All state transitions should happen from within the state
139 * machine (and not from other parts of the code)
140 */
141 enum ChannelState {
142 /**
143 * Initial state before channel is connected.
144 */
145 IDLE(false) {
146
147 },
148
149 OPENSENT(false) {
150 @Override
151 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
152 log.debug("message received in OPENSENT state");
153 // check for OPEN message
154 if (m.getType() != BGPType.OPEN) {
155 // When the message type is not keep alive message increment the wrong packet statistics
156 h.processUnknownMsg();
157 log.debug("Message is not OPEN message");
158 } else {
159 log.debug("Sending keep alive message in OPENSENT state");
160 h.bgpPacketStats.addInPacket();
161
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530162 BGPOpenMsg pOpenmsg = (BGPOpenMsg) m;
163 h.peerIdentifier = pOpenmsg.getBgpId();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530164
165 // validate capabilities and open msg
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530166 if (h.openMsgValidation(h, pOpenmsg)) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530167 log.debug("Sending handshake OPEN message");
168
169 /*
170 * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the
171 * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time
172 * received in the OPEN message
173 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530174 h.peerHoldTime = pOpenmsg.getHoldTime();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530175 if (h.peerHoldTime < h.bgpconfig.getHoldTime()) {
176 h.channel.getPipeline().replace("holdTime",
177 "holdTime",
178 new ReadTimeoutHandler(BGPPipelineFactory.TIMER,
179 h.peerHoldTime));
180 }
181
182 log.info("Hold Time : " + h.peerHoldTime);
183
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530184 // update AS number
185 h.peerAsNum = pOpenmsg.getAsNumber();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530186 }
187
188 // Send keepalive message to peer.
189 h.sendKeepAliveMessage();
190 h.bgpPacketStats.addOutPacket();
191 h.setState(OPENCONFIRM);
192 h.bgpconfig.setPeerConnState(h.peerAddr, BGPPeerCfg.State.OPENCONFIRM);
193 }
194 }
195 },
196
197 OPENWAIT(false) {
198 @Override
199 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
200 log.debug("Message received in OPEN WAIT State");
201
202 // check for open message
203 if (m.getType() != BGPType.OPEN) {
204 // When the message type is not open message increment the wrong packet statistics
205 h.processUnknownMsg();
206 log.debug("Message is not OPEN message");
207 } else {
208 h.bgpPacketStats.addInPacket();
209
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530210 BGPOpenMsg pOpenmsg = (BGPOpenMsg) m;
211 h.peerIdentifier = pOpenmsg.getBgpId();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530212
213 // Validate open message
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530214 if (h.openMsgValidation(h, pOpenmsg)) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530215 log.debug("Sending handshake OPEN message");
216
217 /*
218 * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the
219 * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time
220 * received in the OPEN message
221 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530222 h.peerHoldTime = pOpenmsg.getHoldTime();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530223 if (h.peerHoldTime < h.bgpconfig.getHoldTime()) {
224 h.channel.getPipeline().replace("holdTime",
225 "holdTime",
226 new ReadTimeoutHandler(BGPPipelineFactory.TIMER,
227 h.peerHoldTime));
228 }
229
230 log.debug("Hold Time : " + h.peerHoldTime);
231
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530232 // update AS number
233 h.peerAsNum = pOpenmsg.getAsNumber();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530234
235 h.sendHandshakeOpenMessage();
236 h.bgpPacketStats.addOutPacket();
237 h.setState(OPENCONFIRM);
238 }
239 }
240 }
241 },
242
243 OPENCONFIRM(false) {
244 @Override
245 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
246 log.debug("Message received in OPENCONFIRM state");
247 // check for keep alive message
248 if (m.getType() != BGPType.KEEP_ALIVE) {
249 // When the message type is not keep alive message handle the wrong packet
250 h.processUnknownMsg();
251 log.debug("Message is not KEEPALIVE message");
252 } else {
253
254 // Set the peer connected status
255 h.bgpPacketStats.addInPacket();
256 log.debug("Sending keep alive message in OPENCONFIRM state");
257
258 final InetSocketAddress inetAddress = (InetSocketAddress) h.address;
259 h.thisbgpId = BGPId.bgpId(IpAddress.valueOf(inetAddress.getAddress()));
260
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530261 // set session parameters
262 h.negotiatedHoldTime = (h.peerHoldTime < h.bgpconfig.getHoldTime()) ? h.peerHoldTime
263 : h.bgpconfig.getHoldTime();
264 h.sessionInfo = new BgpSessionInfoImpl(h.thisbgpId, h.bgpVersion, h.peerAsNum, h.peerHoldTime,
265 h.peerIdentifier, h.negotiatedHoldTime, h.isIbgpSession);
266
267 h.bgpPeer = h.peerManager.getBGPPeerInstance(h.bgpController, h.sessionInfo, h.bgpPacketStats);
268 // set the status of bgp as connected
Shashikanth VH6de20d32015-10-09 12:04:13 +0530269 h.bgpPeer.setConnected(true);
270 h.bgpPeer.setChannel(h.channel);
271
Shashikanth VH6de20d32015-10-09 12:04:13 +0530272 /*
273 * RFC 4271, When an OPEN message is received, sends a KEEPALIVE message, If the negotiated hold
274 * time value is zero, then the HoldTimer and KeepaliveTimer are not started. A reasonable maximum
275 * time between KEEPALIVE messages would be one third of the Hold Time interval.
276 */
277 h.sendKeepAliveMessage();
278
279 if (h.negotiatedHoldTime != 0) {
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530280 h.keepAliveTimer = new BGPKeepAliveTimer(h,
281 (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL));
Shashikanth VH6de20d32015-10-09 12:04:13 +0530282 }
283
284 h.bgpPacketStats.addOutPacket();
285
286 // set the state handshake completion.
287 h.setHandshakeComplete(true);
288
289 if (!h.peerManager.addConnectedPeer(h.thisbgpId, h.bgpPeer)) {
290 /*
291 * RFC 4271, Section 6.8, Based on the value of the BGP identifier, a convention is established
292 * for detecting which BGP connection is to be preserved when a collision occurs. The convention
293 * is to compare the BGP Identifiers of the peers involved in the collision and to retain only
294 * the connection initiated by the BGP speaker with the higher-valued BGP Identifier..
295 */
296 // TODO: Connection collision handling.
297 disconnectDuplicate(h);
298 } else {
299 h.setState(ESTABLISHED);
300 h.bgpconfig.setPeerConnState(h.peerAddr, BGPPeerCfg.State.ESTABLISHED);
301 }
302 }
303 }
304 },
305
306 ESTABLISHED(true) {
307 @Override
308 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
309 log.debug("Message received in established state " + m.getType());
310 // dispatch the message
311 h.dispatchMessage(m);
312 }
313 };
314
315 private boolean handshakeComplete;
316
317 ChannelState(boolean handshakeComplete) {
318 this.handshakeComplete = handshakeComplete;
319 }
320
321 /**
322 * Is this a state in which the handshake has completed?
323 *
324 * @return true if the handshake is complete
325 */
326 public boolean isHandshakeComplete() {
327 return this.handshakeComplete;
328 }
329
330 /**
331 * Disconnect duplicate peer connection.
332 *
333 * @param h channel handler
334 */
335 protected void disconnectDuplicate(BGPChannelHandler h) {
336 log.error("Duplicated BGP IP or incompleted cleanup - " + "" + "disconnecting channel {}",
337 h.getPeerInfoString());
338 h.duplicateBGPIdFound = Boolean.TRUE;
339 h.channel.disconnect();
340 }
341
342 // set handshake completion status
343 public void setHandshakeComplete(boolean handshakeComplete) {
344 this.handshakeComplete = handshakeComplete;
345 }
346
347 void processBGPMessage(BGPChannelHandler bgpChannelHandler, BGPMessage pm)
348 throws IOException, BGPParseException {
349 // TODO Auto-generated method stub
350 log.debug("BGP message stub");
351 }
352
353 }
354
355 // *************************
356 // Channel handler methods
357 // *************************
358
359 @Override
360 public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
361
362 channel = e.getChannel();
363 log.info("BGP connected from {}", channel.getRemoteAddress());
364
365 address = channel.getRemoteAddress();
366 if (!(address instanceof InetSocketAddress)) {
367 throw new IOException("Invalid peer connection.");
368 }
369
370 // Connection should establish only if local ip and Autonomous system number is configured.
371 if (bgpconfig.getState() != BGPCfg.State.IP_AS_CONFIGURED) {
372 channel.close();
373 log.info("BGP local AS and router ID not configured");
374 return;
375 }
376
377 inetAddress = (InetSocketAddress) address;
378 ipAddress = IpAddress.valueOf(inetAddress.getAddress());
379 peerAddr = ipAddress.toString();
380
381 // if peer is not configured disconnect session
382 if (!bgpconfig.isPeerConfigured(peerAddr)) {
383 log.debug("Peer is not configured {}", peerAddr);
384 channel.close();
385 return;
386 }
387
388 // if connection is already established close channel
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530389 if (peerManager.isPeerConnected(BGPId.bgpId(IpAddress.valueOf(peerAddr)))) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530390 log.debug("Duplicate connection received, peer {}", peerAddr);
391 channel.close();
392 return;
393 }
394
395 if (null != channel.getPipeline().get("PassiveHandler")) {
396 log.info("BGP handle connection request from peer");
397 // Wait for open message from bgp peer
398 setState(ChannelState.OPENWAIT);
399 } else if (null != channel.getPipeline().get("ActiveHandler")) {
400 log.info("BGP handle connection response from peer");
401
402 sendHandshakeOpenMessage();
403 bgpPacketStats.addOutPacket();
404 setState(ChannelState.OPENSENT);
405 bgpconfig.setPeerConnState(peerAddr, BGPPeerCfg.State.OPENSENT);
406 }
407 }
408
409 @Override
410 public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
411
412 channel = e.getChannel();
413 log.info("BGP disconnected callback for bgp:{}. Cleaning up ...", getPeerInfoString());
414
415 address = channel.getRemoteAddress();
416 if (!(address instanceof InetSocketAddress)) {
417 throw new IOException("Invalid peer connection.");
418 }
419
420 inetAddress = (InetSocketAddress) address;
421 ipAddress = IpAddress.valueOf(inetAddress.getAddress());
422 peerAddr = ipAddress.toString();
423
424 if (thisbgpId != null) {
425 if (!duplicateBGPIdFound) {
426 // if the disconnected peer (on this ChannelHandler)
427 // was not one with a duplicate, it is safe to remove all
428 // state for it at the controller. Notice that if the disconnected
429 // peer was a duplicate-ip, calling the method below would clear
430 // all state for the original peer (with the same ip),
431 // which we obviously don't want.
432 log.debug("{}:removal called", getPeerInfoString());
433 if (bgpPeer != null) {
434 peerManager.removeConnectedPeer(thisbgpId);
435 }
436 } else {
437 // A duplicate was disconnected on this ChannelHandler,
438 // this is the same peer reconnecting, but the original state was
439 // not cleaned up - XXX check liveness of original ChannelHandler
440 log.debug("{}:duplicate found", getPeerInfoString());
441 duplicateBGPIdFound = Boolean.FALSE;
442 }
443
444 if (null != keepAliveTimer) {
445 keepAliveTimer.getKeepAliveTimer().cancel();
446 }
447 } else {
448 log.warn("No bgp ip in channelHandler registered for " + "disconnected peer {}", getPeerInfoString());
449 }
450 }
451
452 @Override
453 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
454
455 log.info("[exceptionCaught]: " + e.toString());
456
457 if (e.getCause() instanceof ReadTimeoutException) {
458 if ((ChannelState.OPENWAIT == state) || (ChannelState.OPENSENT == state)) {
459
460 // When ReadTimeout timer is expired in OPENWAIT/OPENSENT state, it is considered
461 // TODO: Send notification
462 channel.close();
463 state = ChannelState.IDLE;
464 return;
465 } else if (ChannelState.OPENCONFIRM == state) {
466
467 // When ReadTimeout timer is expired in OPENCONFIRM state.
468 // TODO: Send Notification
469 channel.close();
470 state = ChannelState.IDLE;
471 return;
472 }
473 } else if (e.getCause() instanceof ClosedChannelException) {
474 log.debug("Channel for bgp {} already closed", getPeerInfoString());
475 } else if (e.getCause() instanceof IOException) {
476 log.error("Disconnecting peer {} due to IO Error: {}", getPeerInfoString(), e.getCause().getMessage());
477 if (log.isDebugEnabled()) {
478 // still print stack trace if debug is enabled
479 log.debug("StackTrace for previous Exception: ", e.getCause());
480 }
481 channel.close();
482 } else if (e.getCause() instanceof BGPParseException) {
483 // TODO: SEND NOTIFICATION
484 log.debug("BGP Parse Exception: ", e.getCause());
485 } else if (e.getCause() instanceof RejectedExecutionException) {
486 log.warn("Could not process message: queue full");
487 } else {
488 log.error("Error while processing message from peer " + getPeerInfoString() + "state " + this.state);
489 channel.close();
490 }
491 }
492
493 @Override
494 public String toString() {
495 return getPeerInfoString();
496 }
497
498 @Override
499 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
500 if (e.getMessage() instanceof List) {
501 @SuppressWarnings("Unchecked")
502 List<BGPMessage> msglist = (List<BGPMessage>) e.getMessage();
503 for (BGPMessage pm : msglist) {
504 // Do the actual packet processing
505 state.processBGPMessage(this, pm);
506 }
507 } else {
508 state.processBGPMessage(this, (BGPMessage) e.getMessage());
509 }
510 }
511
512 // *************************
513 // Channel utility methods
514 // *************************
515 /**
516 * Set handshake status.
517 *
518 * @param handshakeComplete handshake complete status
519 */
520 public void setHandshakeComplete(boolean handshakeComplete) {
521 this.state.setHandshakeComplete(handshakeComplete);
522 }
523
524 /**
525 * Is this a state in which the handshake has completed?
526 *
527 * @return true if the handshake is complete
528 */
529 public boolean isHandshakeComplete() {
530 return state.isHandshakeComplete();
531 }
532
533 /**
534 * To handle the BGP message.
535 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530536 * @param m bgp message
537 * @throws BGPParseException throw exception
Shashikanth VH6de20d32015-10-09 12:04:13 +0530538 */
539 private void dispatchMessage(BGPMessage m) throws BGPParseException {
540 bgpPacketStats.addInPacket();
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530541 bgpController.processBGPPacket(thisbgpId, m);
Shashikanth VH6de20d32015-10-09 12:04:13 +0530542 }
543
544 /**
545 * Return a string describing this peer based on the already available information (ip address and/or remote
546 * socket).
547 *
548 * @return display string
549 */
550 private String getPeerInfoString() {
551 if (bgpPeer != null) {
552 return bgpPeer.toString();
553 }
554 String channelString;
555 if (channel == null || channel.getRemoteAddress() == null) {
556 channelString = "?";
557 } else {
558 channelString = channel.getRemoteAddress().toString();
559 }
560 String bgpIpString;
561 // TODO: implement functionality to get bgp id string
562 bgpIpString = "?";
563 return String.format("[%s BGP-IP[%s]]", channelString, bgpIpString);
564 }
565
566 /**
567 * Update the channels state. Only called from the state machine. TODO: enforce restricted state transitions
568 *
569 * @param state
570 */
571 private void setState(ChannelState state) {
572 this.state = state;
573 }
574
575 /**
576 * get packet statistics.
577 *
578 * @return packet statistics
579 */
580 public BGPPacketStatsImpl getBgpPacketStats() {
581 return bgpPacketStats;
582 }
583
584 /**
585 * Send handshake open message to the peer.
586 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530587 * @throws IOException, BGPParseException
Shashikanth VH6de20d32015-10-09 12:04:13 +0530588 */
589 private void sendHandshakeOpenMessage() throws IOException, BGPParseException {
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530590 int bgpId;
591
592 bgpId = Ip4Address.valueOf(bgpconfig.getRouterId()).toInt();
593 BGPMessage msg = factory4.openMessageBuilder().setAsNumber((short) bgpconfig.getAsNumber())
594 .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId).build();
595 log.debug("Sending open message to {}", channel.getRemoteAddress());
596 channel.write(Collections.singletonList(msg));
Shashikanth VH6de20d32015-10-09 12:04:13 +0530597
598 }
599
600 /**
601 * Send keep alive message.
602 *
603 * @throws IOException when channel is disconnected
604 * @throws BGPParseException while building keep alive message
605 */
606 synchronized void sendKeepAliveMessage() throws IOException, BGPParseException {
607
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530608 BGPMessage msg = factory4.keepaliveMessageBuilder().build();
609 log.debug("Sending keepalive message to {}", channel.getRemoteAddress());
610 channel.write(Collections.singletonList(msg));
Shashikanth VH6de20d32015-10-09 12:04:13 +0530611 }
612
613 /**
614 * Send notification and close channel with peer.
615 */
616 private void sendErrNotificationAndCloseChannel() {
617 // TODO: send notification
618 channel.close();
619 }
620
621 /**
622 * Process unknown BGP message received.
623 *
624 * @throws BGPParseException when received invalid message
625 */
626 public void processUnknownMsg() throws BGPParseException {
627 log.debug("UNKNOWN message received");
628 Date now = null;
629 if (bgpPacketStats.wrongPacketCount() == 0) {
630 now = new Date();
631 bgpPacketStats.setTime(now.getTime());
632 bgpPacketStats.addWrongPacket();
633 sendErrNotificationAndCloseChannel();
634 }
635 if (bgpPacketStats.wrongPacketCount() > 1) {
636 Date lastest = new Date();
637 bgpPacketStats.addWrongPacket();
638 // converting to seconds
639 if (((lastest.getTime() - bgpPacketStats.getTime()) / 1000) > 60) {
640 now = lastest;
641 bgpPacketStats.setTime(now.getTime());
642 bgpPacketStats.resetWrongPacket();
643 bgpPacketStats.addWrongPacket();
644 } else if (((int) (lastest.getTime() - now.getTime()) / 1000) < 60) {
645 if (MAX_WRONG_COUNT_PACKET <= bgpPacketStats.wrongPacketCount()) {
646 // reset once wrong packet count reaches MAX_WRONG_COUNT_PACKET
647 bgpPacketStats.resetWrongPacket();
648 // max wrong packets received send error message and close the session
649 sendErrNotificationAndCloseChannel();
650 }
651 }
652 }
653 }
654
655 /**
Vidyashree Ramaee293252015-11-18 17:00:11 +0530656 * BGP open message validation.
Shashikanth VH6de20d32015-10-09 12:04:13 +0530657 *
658 * @param h channel handler
Vidyashree Ramaee293252015-11-18 17:00:11 +0530659 * @param openMsg open message
660 * @return true if valid message, otherwise false
661 * @throws BGPParseException throw exception
Shashikanth VH6de20d32015-10-09 12:04:13 +0530662 */
Vidyashree Ramaee293252015-11-18 17:00:11 +0530663 public boolean openMsgValidation(BGPChannelHandler h, BGPOpenMsg openMsg) throws BGPParseException {
664 boolean result;
665
666 // Validate BGP ID
667 result = bgpIdValidation(openMsg);
668 if (!result) {
669 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_BGP_IDENTIFIER, null);
670 }
671
672 // Validate AS number
673 result = asNumberValidation(h, openMsg);
674 if (!result) {
675 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
676 }
677
678 // Validate hold timer
679 if ((openMsg.getHoldTime() != 0) && (openMsg.getHoldTime() < BGP_MIN_HOLDTIME)) {
680 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.UNACCEPTABLE_HOLD_TIME, null);
681 }
682
683 // Validate capabilities
684 result = capabilityValidation(h, openMsg);
685 return result;
686 }
687
688 /**
689 * Capability Validation.
690 *
691 * @param h channel handler
692 * @param openmsg open message
693 * @return success or failure
694 * @throws BGPParseException
695 */
696 private boolean capabilityValidation(BGPChannelHandler h, BGPOpenMsg openmsg) throws BGPParseException {
697 log.debug("capabilityValidation");
698
699 boolean isMultiProtocolcapabilityExists = false;
700 boolean isFourOctetCapabilityExits = false;
701 int capAsNum = 0;
702
703 List<BGPValueType> capabilityTlv = openmsg.getCapabilityTlv();
704 ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator();
705 List<BGPValueType> unSupportedCapabilityTlv = new LinkedList<>();
706 ListIterator<BGPValueType> unSupportedCaplistIterator = unSupportedCapabilityTlv.listIterator();
707 BGPValueType tempTlv;
708 boolean isLargeAsCapabilityCfg = h.bgpconfig.getLargeASCapability();
709 boolean isLsCapabilityCfg = h.bgpconfig.getLsCapability();
710
711 while (listIterator.hasNext()) {
712 BGPValueType tlv = listIterator.next();
713 if (tlv.getType() == MULTI_PROTOCOL_EXTN_CAPA_TYPE) {
714 isMultiProtocolcapabilityExists = true;
715 }
716 if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) {
717 isFourOctetCapabilityExits = true;
718 capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt();
719 }
720 }
721
722 if (isFourOctetCapabilityExits) {
723 if (capAsNum > MAX_AS2_NUM) {
724 if (openmsg.getAsNumber() != AS_TRANS) {
725 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
726 }
727 } else {
728 if (capAsNum != openmsg.getAsNumber()) {
729 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
730 }
731 }
732 }
733
734 if ((isLsCapabilityCfg)) {
735 if (!isMultiProtocolcapabilityExists) {
736 tempTlv = new MultiProtocolExtnCapabilityTlv(AFI, RES, SAFI);
737 unSupportedCapabilityTlv.add(tempTlv);
738 }
739 }
740
741 if ((isLargeAsCapabilityCfg)) {
742 if (!isFourOctetCapabilityExits) {
743 tempTlv = new FourOctetAsNumCapabilityTlv(h.bgpconfig.getAsNumber());
744 unSupportedCapabilityTlv.add(tempTlv);
745 }
746 }
747
748 if (unSupportedCaplistIterator.hasNext()) {
749 ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
750 while (unSupportedCaplistIterator.hasNext()) {
751 BGPValueType tlv = unSupportedCaplistIterator.next();
752 tlv.write(buffer);
753 }
754 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR,
755 BGPErrorType.UNSUPPORTED_CAPABILITY, buffer);
756 } else {
757 return true;
758 }
759 }
760
761 /**
762 * AS Number Validation.
763 *
764 * @param h channel Handler
765 * @param openMsg open message
766 * @return true or false
767 */
768 private boolean asNumberValidation(BGPChannelHandler h, BGPOpenMsg openMsg) {
769 log.debug("AS Num validation");
770
771 int capAsNum = 0;
772 boolean isFourOctetCapabilityExits = false;
773
774 BGPPeerCfg peerCfg = h.bgpconfig.displayPeers(peerAddr);
775 List<BGPValueType> capabilityTlv = openMsg.getCapabilityTlv();
776 ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator();
777
778 while (listIterator.hasNext()) {
779 BGPValueType tlv = listIterator.next();
780 if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) {
781 isFourOctetCapabilityExits = true;
782 capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt();
783 }
784 }
785
786 if (peerCfg.getAsNumber() > MAX_AS2_NUM) {
787 if (openMsg.getAsNumber() != AS_TRANS) {
788 return false;
789 }
790
791 if (!isFourOctetCapabilityExits) {
792 return false;
793 }
794
795 if (peerCfg.getAsNumber() != capAsNum) {
796 return false;
797 }
798
799 isIbgpSession = peerCfg.getIsIBgp();
800 if (isIbgpSession) {
801 // IBGP - AS number should be same for Peer and local if it is IBGP
802 if (h.bgpconfig.getAsNumber() != capAsNum) {
803 return false;
804 }
805 }
806 } else {
807
808 if (openMsg.getAsNumber() != peerCfg.getAsNumber()) {
809 return false;
810 }
811
812 if (isFourOctetCapabilityExits) {
813 if (capAsNum != peerCfg.getAsNumber()) {
814 return false;
815 }
816 }
817
818 isIbgpSession = peerCfg.getIsIBgp();
819 if (isIbgpSession) {
820 // IBGP - AS number should be same for Peer and local if it is IBGP
821 if (openMsg.getAsNumber() != h.bgpconfig.getAsNumber()) {
822 return false;
823 }
824 }
825 }
826 return true;
827 }
828
829 /**
830 * Validates BGP ID.
831 *
832 * @param openMsg open message
833 * @return true or false
834 */
835 private boolean bgpIdValidation(BGPOpenMsg openMsg) {
836 String openMsgBgpId = Ip4Address.valueOf(openMsg.getBgpId()).toString();
837
838 InetAddress ipAddress;
839 try {
840 ipAddress = InetAddress.getByName(openMsgBgpId);
841 if (ipAddress.isMulticastAddress()) {
842 return false;
843 }
844 } catch (UnknownHostException e) {
845 log.debug("InetAddress convertion failed");
846 }
Shashikanth VH6de20d32015-10-09 12:04:13 +0530847 return true;
848 }
849}