blob: f21c311c0816a5e894394b33208112aeec28d65b [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.List;
Vidyashree Ramaee293252015-11-18 17:00:11 +053027import java.util.LinkedList;
28import java.util.ListIterator;
Shashikanth VH6de20d32015-10-09 12:04:13 +053029import java.util.concurrent.RejectedExecutionException;
30
Vidyashree Ramaee293252015-11-18 17:00:11 +053031import org.jboss.netty.buffer.ChannelBuffer;
32import org.jboss.netty.buffer.ChannelBuffers;
Shashikanth VH6de20d32015-10-09 12:04:13 +053033import org.jboss.netty.channel.Channel;
34import org.jboss.netty.channel.ChannelHandlerContext;
35import org.jboss.netty.channel.ChannelStateEvent;
36import org.jboss.netty.channel.ExceptionEvent;
37import org.jboss.netty.channel.MessageEvent;
Satish Ke107e662015-09-21 19:00:17 +053038import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
Shashikanth VH6de20d32015-10-09 12:04:13 +053039import org.jboss.netty.handler.timeout.ReadTimeoutException;
40import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053041import org.onlab.packet.Ip4Address;
Shashikanth VH6de20d32015-10-09 12:04:13 +053042import org.onlab.packet.IpAddress;
43import org.onosproject.bgp.controller.BGPCfg;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053044import org.onosproject.bgp.controller.BGPController;
Shashikanth VH6de20d32015-10-09 12:04:13 +053045import org.onosproject.bgp.controller.BGPId;
46import org.onosproject.bgp.controller.BGPPeer;
47import org.onosproject.bgp.controller.BGPPeerCfg;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053048import org.onosproject.bgp.controller.impl.BGPControllerImpl.BGPPeerManagerImpl;
Shashikanth VH6de20d32015-10-09 12:04:13 +053049import org.onosproject.bgpio.exceptions.BGPParseException;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053050import org.onosproject.bgpio.protocol.BGPFactory;
Shashikanth VH6de20d32015-10-09 12:04:13 +053051import org.onosproject.bgpio.protocol.BGPMessage;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053052import org.onosproject.bgpio.protocol.BGPOpenMsg;
Shashikanth VH6de20d32015-10-09 12:04:13 +053053import org.onosproject.bgpio.protocol.BGPType;
54import org.onosproject.bgpio.protocol.BGPVersion;
Vidyashree Ramaee293252015-11-18 17:00:11 +053055import org.onosproject.bgpio.types.BGPErrorType;
56import org.onosproject.bgpio.types.BGPValueType;
57import org.onosproject.bgpio.types.FourOctetAsNumCapabilityTlv;
58import org.onosproject.bgpio.types.MultiProtocolExtnCapabilityTlv;
Shashikanth VH6de20d32015-10-09 12:04:13 +053059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
Satish Ke107e662015-09-21 19:00:17 +053061
62/**
63 * Channel handler deals with the bgp peer connection and dispatches messages from peer to the appropriate locations.
64 */
65class BGPChannelHandler extends IdleStateAwareChannelHandler {
66
Shashikanth VH6de20d32015-10-09 12:04:13 +053067 private static final Logger log = LoggerFactory.getLogger(BGPChannelHandler.class);
Vidyashree Ramaee293252015-11-18 17:00:11 +053068 static final int BGP_MIN_HOLDTIME = 3;
Shashikanth VH6de20d32015-10-09 12:04:13 +053069 static final int BGP_MAX_KEEPALIVE_INTERVAL = 3;
70 private BGPPeer bgpPeer;
71 private BGPId thisbgpId;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053072 private Channel channel;
Shashikanth VH6de20d32015-10-09 12:04:13 +053073 private BGPKeepAliveTimer keepAliveTimer = null;
74 private short peerHoldTime = 0;
75 private short negotiatedHoldTime = 0;
Shashikanth VH9f8afb42015-11-04 18:00:30 +053076 private long peerAsNum;
Shashikanth VH6de20d32015-10-09 12:04:13 +053077 private int peerIdentifier;
78 private BGPPacketStatsImpl bgpPacketStats;
79 static final int MAX_WRONG_COUNT_PACKET = 5;
Vidyashree Ramaee293252015-11-18 17:00:11 +053080 static final byte MULTI_PROTOCOL_EXTN_CAPA_TYPE = 1;
81 static final byte FOUR_OCTET_AS_NUM_CAPA_TYPE = 65;
82 static final int AS_TRANS = 23456;
83 static final int MAX_AS2_NUM = 65535;
84 static final short AFI = 16388;
85 static final byte RES = 0;
86 static final byte SAFI = 71;
Shashikanth VH6de20d32015-10-09 12:04:13 +053087
88 // State needs to be volatile because the HandshakeTimeoutHandler
89 // needs to check if the handshake is complete
90 private volatile ChannelState state;
91
92 // When a bgp peer with a ip addresss is found (i.e we already have a
93 // connected peer with the same ip), the new peer is immediately
94 // disconnected. At that point netty callsback channelDisconnected() which
Shashikanth VH9f8afb42015-11-04 18:00:30 +053095 // proceeds to cleaup peer state - we need to ensure that it does not
96 // cleanup
Shashikanth VH6de20d32015-10-09 12:04:13 +053097 // peer state for the older (still connected) peer
98 private volatile Boolean duplicateBGPIdFound;
99 // Indicates the bgp version used by this bgp peer
100 protected BGPVersion bgpVersion;
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530101 private BGPController bgpController;
102 protected BGPFactory factory4;
103 private boolean isIbgpSession;
104 private BgpSessionInfoImpl sessionInfo;
105 private BGPPeerManagerImpl peerManager;
Shashikanth VH6de20d32015-10-09 12:04:13 +0530106 private InetSocketAddress inetAddress;
107 private IpAddress ipAddress;
108 private SocketAddress address;
109 private String peerAddr;
110 private BGPCfg bgpconfig;
111
Satish Ke107e662015-09-21 19:00:17 +0530112 /**
113 * Create a new unconnected BGPChannelHandler.
114 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530115 * @param bgpController bgp controller
Satish Ke107e662015-09-21 19:00:17 +0530116 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530117 BGPChannelHandler(BGPController bgpController) {
118 this.bgpController = bgpController;
119 this.peerManager = (BGPPeerManagerImpl) bgpController.peerManager();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530120 this.state = ChannelState.IDLE;
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530121 this.factory4 = Controller.getBGPMessageFactory4();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530122 this.duplicateBGPIdFound = Boolean.FALSE;
123 this.bgpPacketStats = new BGPPacketStatsImpl();
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530124 this.bgpconfig = bgpController.getConfig();
Satish Ke107e662015-09-21 19:00:17 +0530125 }
Shashikanth VH6de20d32015-10-09 12:04:13 +0530126
127 // To disconnect peer session.
128 public void disconnectPeer() {
129 bgpPeer.disconnectPeer();
130 }
131
132 // *************************
133 // Channel State Machine
134 // *************************
135
136 /**
137 * The state machine for handling the peer/channel state. All state transitions should happen from within the state
138 * machine (and not from other parts of the code)
139 */
140 enum ChannelState {
141 /**
142 * Initial state before channel is connected.
143 */
144 IDLE(false) {
145
146 },
147
148 OPENSENT(false) {
149 @Override
150 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
151 log.debug("message received in OPENSENT state");
152 // check for OPEN message
153 if (m.getType() != BGPType.OPEN) {
154 // When the message type is not keep alive message increment the wrong packet statistics
Shashikanth VHdae80402015-11-20 14:20:33 +0530155 h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
156 BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE, m.getType()
157 .getType());
Shashikanth VH6de20d32015-10-09 12:04:13 +0530158 log.debug("Message is not OPEN message");
159 } else {
160 log.debug("Sending keep alive message in OPENSENT state");
161 h.bgpPacketStats.addInPacket();
162
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530163 BGPOpenMsg pOpenmsg = (BGPOpenMsg) m;
164 h.peerIdentifier = pOpenmsg.getBgpId();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530165
166 // validate capabilities and open msg
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530167 if (h.openMsgValidation(h, pOpenmsg)) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530168 log.debug("Sending handshake OPEN message");
169
170 /*
171 * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the
172 * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time
173 * received in the OPEN message
174 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530175 h.peerHoldTime = pOpenmsg.getHoldTime();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530176 if (h.peerHoldTime < h.bgpconfig.getHoldTime()) {
177 h.channel.getPipeline().replace("holdTime",
178 "holdTime",
179 new ReadTimeoutHandler(BGPPipelineFactory.TIMER,
180 h.peerHoldTime));
181 }
182
183 log.info("Hold Time : " + h.peerHoldTime);
184
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530185 // update AS number
186 h.peerAsNum = pOpenmsg.getAsNumber();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530187 }
188
189 // Send keepalive message to peer.
190 h.sendKeepAliveMessage();
191 h.bgpPacketStats.addOutPacket();
192 h.setState(OPENCONFIRM);
193 h.bgpconfig.setPeerConnState(h.peerAddr, BGPPeerCfg.State.OPENCONFIRM);
194 }
195 }
196 },
197
198 OPENWAIT(false) {
199 @Override
200 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
201 log.debug("Message received in OPEN WAIT State");
202
203 // check for open message
204 if (m.getType() != BGPType.OPEN) {
205 // When the message type is not open message increment the wrong packet statistics
Shashikanth VHdae80402015-11-20 14:20:33 +0530206 h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR, BGPErrorType.UNSPECIFIED_ERROR, m
207 .getType().getType());
Shashikanth VH6de20d32015-10-09 12:04:13 +0530208 log.debug("Message is not OPEN message");
209 } else {
210 h.bgpPacketStats.addInPacket();
211
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530212 BGPOpenMsg pOpenmsg = (BGPOpenMsg) m;
213 h.peerIdentifier = pOpenmsg.getBgpId();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530214
215 // Validate open message
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530216 if (h.openMsgValidation(h, pOpenmsg)) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530217 log.debug("Sending handshake OPEN message");
218
219 /*
220 * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the
221 * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time
222 * received in the OPEN message
223 */
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530224 h.peerHoldTime = pOpenmsg.getHoldTime();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530225 if (h.peerHoldTime < h.bgpconfig.getHoldTime()) {
226 h.channel.getPipeline().replace("holdTime",
227 "holdTime",
228 new ReadTimeoutHandler(BGPPipelineFactory.TIMER,
229 h.peerHoldTime));
230 }
231
232 log.debug("Hold Time : " + h.peerHoldTime);
233
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530234 // update AS number
235 h.peerAsNum = pOpenmsg.getAsNumber();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530236
237 h.sendHandshakeOpenMessage();
238 h.bgpPacketStats.addOutPacket();
239 h.setState(OPENCONFIRM);
240 }
241 }
242 }
243 },
244
245 OPENCONFIRM(false) {
246 @Override
247 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
248 log.debug("Message received in OPENCONFIRM state");
249 // check for keep alive message
250 if (m.getType() != BGPType.KEEP_ALIVE) {
251 // When the message type is not keep alive message handle the wrong packet
Shashikanth VHdae80402015-11-20 14:20:33 +0530252 h.processUnknownMsg(BGPErrorType.FINITE_STATE_MACHINE_ERROR,
253 BGPErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE, m.getType()
254 .getType());
Shashikanth VH6de20d32015-10-09 12:04:13 +0530255 log.debug("Message is not KEEPALIVE message");
256 } else {
257
258 // Set the peer connected status
259 h.bgpPacketStats.addInPacket();
260 log.debug("Sending keep alive message in OPENCONFIRM state");
261
262 final InetSocketAddress inetAddress = (InetSocketAddress) h.address;
263 h.thisbgpId = BGPId.bgpId(IpAddress.valueOf(inetAddress.getAddress()));
264
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530265 // set session parameters
266 h.negotiatedHoldTime = (h.peerHoldTime < h.bgpconfig.getHoldTime()) ? h.peerHoldTime
267 : h.bgpconfig.getHoldTime();
268 h.sessionInfo = new BgpSessionInfoImpl(h.thisbgpId, h.bgpVersion, h.peerAsNum, h.peerHoldTime,
269 h.peerIdentifier, h.negotiatedHoldTime, h.isIbgpSession);
270
271 h.bgpPeer = h.peerManager.getBGPPeerInstance(h.bgpController, h.sessionInfo, h.bgpPacketStats);
272 // set the status of bgp as connected
Shashikanth VH6de20d32015-10-09 12:04:13 +0530273 h.bgpPeer.setConnected(true);
274 h.bgpPeer.setChannel(h.channel);
275
Shashikanth VH6de20d32015-10-09 12:04:13 +0530276 /*
277 * RFC 4271, When an OPEN message is received, sends a KEEPALIVE message, If the negotiated hold
278 * time value is zero, then the HoldTimer and KeepaliveTimer are not started. A reasonable maximum
279 * time between KEEPALIVE messages would be one third of the Hold Time interval.
280 */
Shashikanth VH6de20d32015-10-09 12:04:13 +0530281
282 if (h.negotiatedHoldTime != 0) {
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530283 h.keepAliveTimer = new BGPKeepAliveTimer(h,
284 (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL));
Shashikanth VHdae80402015-11-20 14:20:33 +0530285 } else {
286 h.sendKeepAliveMessage();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530287 }
288
289 h.bgpPacketStats.addOutPacket();
290
291 // set the state handshake completion.
292 h.setHandshakeComplete(true);
293
294 if (!h.peerManager.addConnectedPeer(h.thisbgpId, h.bgpPeer)) {
295 /*
296 * RFC 4271, Section 6.8, Based on the value of the BGP identifier, a convention is established
297 * for detecting which BGP connection is to be preserved when a collision occurs. The convention
298 * is to compare the BGP Identifiers of the peers involved in the collision and to retain only
299 * the connection initiated by the BGP speaker with the higher-valued BGP Identifier..
300 */
301 // TODO: Connection collision handling.
302 disconnectDuplicate(h);
303 } else {
304 h.setState(ESTABLISHED);
305 h.bgpconfig.setPeerConnState(h.peerAddr, BGPPeerCfg.State.ESTABLISHED);
306 }
307 }
308 }
309 },
310
311 ESTABLISHED(true) {
312 @Override
313 void processBGPMessage(BGPChannelHandler h, BGPMessage m) throws IOException, BGPParseException {
314 log.debug("Message received in established state " + m.getType());
315 // dispatch the message
316 h.dispatchMessage(m);
317 }
318 };
319
320 private boolean handshakeComplete;
321
322 ChannelState(boolean handshakeComplete) {
323 this.handshakeComplete = handshakeComplete;
324 }
325
326 /**
327 * Is this a state in which the handshake has completed?
328 *
329 * @return true if the handshake is complete
330 */
331 public boolean isHandshakeComplete() {
332 return this.handshakeComplete;
333 }
334
335 /**
336 * Disconnect duplicate peer connection.
337 *
338 * @param h channel handler
339 */
340 protected void disconnectDuplicate(BGPChannelHandler h) {
341 log.error("Duplicated BGP IP or incompleted cleanup - " + "" + "disconnecting channel {}",
342 h.getPeerInfoString());
343 h.duplicateBGPIdFound = Boolean.TRUE;
344 h.channel.disconnect();
345 }
346
347 // set handshake completion status
348 public void setHandshakeComplete(boolean handshakeComplete) {
349 this.handshakeComplete = handshakeComplete;
350 }
351
352 void processBGPMessage(BGPChannelHandler bgpChannelHandler, BGPMessage pm)
353 throws IOException, BGPParseException {
354 // TODO Auto-generated method stub
355 log.debug("BGP message stub");
356 }
357
358 }
359
360 // *************************
361 // Channel handler methods
362 // *************************
363
364 @Override
365 public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
366
367 channel = e.getChannel();
368 log.info("BGP connected from {}", channel.getRemoteAddress());
369
370 address = channel.getRemoteAddress();
371 if (!(address instanceof InetSocketAddress)) {
372 throw new IOException("Invalid peer connection.");
373 }
374
375 // Connection should establish only if local ip and Autonomous system number is configured.
376 if (bgpconfig.getState() != BGPCfg.State.IP_AS_CONFIGURED) {
377 channel.close();
378 log.info("BGP local AS and router ID not configured");
379 return;
380 }
381
382 inetAddress = (InetSocketAddress) address;
Shashikanth VHdae80402015-11-20 14:20:33 +0530383 peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530384
385 // if peer is not configured disconnect session
386 if (!bgpconfig.isPeerConfigured(peerAddr)) {
387 log.debug("Peer is not configured {}", peerAddr);
388 channel.close();
389 return;
390 }
391
392 // if connection is already established close channel
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530393 if (peerManager.isPeerConnected(BGPId.bgpId(IpAddress.valueOf(peerAddr)))) {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530394 log.debug("Duplicate connection received, peer {}", peerAddr);
395 channel.close();
396 return;
397 }
398
399 if (null != channel.getPipeline().get("PassiveHandler")) {
400 log.info("BGP handle connection request from peer");
401 // Wait for open message from bgp peer
402 setState(ChannelState.OPENWAIT);
403 } else if (null != channel.getPipeline().get("ActiveHandler")) {
404 log.info("BGP handle connection response from peer");
405
406 sendHandshakeOpenMessage();
407 bgpPacketStats.addOutPacket();
408 setState(ChannelState.OPENSENT);
409 bgpconfig.setPeerConnState(peerAddr, BGPPeerCfg.State.OPENSENT);
410 }
411 }
412
413 @Override
414 public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
415
416 channel = e.getChannel();
417 log.info("BGP disconnected callback for bgp:{}. Cleaning up ...", getPeerInfoString());
418
419 address = channel.getRemoteAddress();
420 if (!(address instanceof InetSocketAddress)) {
421 throw new IOException("Invalid peer connection.");
422 }
423
424 inetAddress = (InetSocketAddress) address;
Shashikanth VHdae80402015-11-20 14:20:33 +0530425 peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530426
427 if (thisbgpId != null) {
428 if (!duplicateBGPIdFound) {
429 // if the disconnected peer (on this ChannelHandler)
430 // was not one with a duplicate, it is safe to remove all
431 // state for it at the controller. Notice that if the disconnected
432 // peer was a duplicate-ip, calling the method below would clear
433 // all state for the original peer (with the same ip),
434 // which we obviously don't want.
435 log.debug("{}:removal called", getPeerInfoString());
436 if (bgpPeer != null) {
437 peerManager.removeConnectedPeer(thisbgpId);
438 }
439 } else {
440 // A duplicate was disconnected on this ChannelHandler,
441 // this is the same peer reconnecting, but the original state was
442 // not cleaned up - XXX check liveness of original ChannelHandler
443 log.debug("{}:duplicate found", getPeerInfoString());
444 duplicateBGPIdFound = Boolean.FALSE;
445 }
446
447 if (null != keepAliveTimer) {
448 keepAliveTimer.getKeepAliveTimer().cancel();
449 }
450 } else {
451 log.warn("No bgp ip in channelHandler registered for " + "disconnected peer {}", getPeerInfoString());
452 }
453 }
454
455 @Override
456 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
457
458 log.info("[exceptionCaught]: " + e.toString());
459
460 if (e.getCause() instanceof ReadTimeoutException) {
461 if ((ChannelState.OPENWAIT == state) || (ChannelState.OPENSENT == state)) {
462
463 // When ReadTimeout timer is expired in OPENWAIT/OPENSENT state, it is considered
Shashikanth VHdae80402015-11-20 14:20:33 +0530464 sendNotification(BGPErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null);
Shashikanth VH6de20d32015-10-09 12:04:13 +0530465 channel.close();
466 state = ChannelState.IDLE;
467 return;
468 } else if (ChannelState.OPENCONFIRM == state) {
469
470 // When ReadTimeout timer is expired in OPENCONFIRM state.
Shashikanth VHdae80402015-11-20 14:20:33 +0530471 sendNotification(BGPErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null);
Shashikanth VH6de20d32015-10-09 12:04:13 +0530472 channel.close();
473 state = ChannelState.IDLE;
474 return;
475 }
476 } else if (e.getCause() instanceof ClosedChannelException) {
477 log.debug("Channel for bgp {} already closed", getPeerInfoString());
478 } else if (e.getCause() instanceof IOException) {
479 log.error("Disconnecting peer {} due to IO Error: {}", getPeerInfoString(), e.getCause().getMessage());
480 if (log.isDebugEnabled()) {
481 // still print stack trace if debug is enabled
482 log.debug("StackTrace for previous Exception: ", e.getCause());
483 }
484 channel.close();
485 } else if (e.getCause() instanceof BGPParseException) {
Shashikanth VHdae80402015-11-20 14:20:33 +0530486 byte[] data = new byte[] {};
487 BGPParseException errMsg = (BGPParseException) e.getCause();
488 byte errorCode = errMsg.getErrorCode();
489 byte errorSubCode = errMsg.getErrorSubCode();
490 ChannelBuffer tempCb = errMsg.getData();
491 if (tempCb != null) {
492 int dataLength = tempCb.capacity();
493 data = new byte[dataLength];
494 tempCb.readBytes(data, 0, dataLength);
495 }
496 sendNotification(errorCode, errorSubCode, data);
Shashikanth VH6de20d32015-10-09 12:04:13 +0530497 } else if (e.getCause() instanceof RejectedExecutionException) {
498 log.warn("Could not process message: queue full");
499 } else {
500 log.error("Error while processing message from peer " + getPeerInfoString() + "state " + this.state);
501 channel.close();
502 }
503 }
504
505 @Override
506 public String toString() {
507 return getPeerInfoString();
508 }
509
510 @Override
511 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
512 if (e.getMessage() instanceof List) {
513 @SuppressWarnings("Unchecked")
514 List<BGPMessage> msglist = (List<BGPMessage>) e.getMessage();
515 for (BGPMessage pm : msglist) {
516 // Do the actual packet processing
517 state.processBGPMessage(this, pm);
518 }
519 } else {
520 state.processBGPMessage(this, (BGPMessage) e.getMessage());
521 }
522 }
523
524 // *************************
525 // Channel utility methods
526 // *************************
527 /**
528 * Set handshake status.
529 *
530 * @param handshakeComplete handshake complete status
531 */
532 public void setHandshakeComplete(boolean handshakeComplete) {
533 this.state.setHandshakeComplete(handshakeComplete);
534 }
535
536 /**
537 * Is this a state in which the handshake has completed?
538 *
539 * @return true if the handshake is complete
540 */
541 public boolean isHandshakeComplete() {
542 return state.isHandshakeComplete();
543 }
544
545 /**
546 * To handle the BGP message.
547 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530548 * @param m bgp message
549 * @throws BGPParseException throw exception
Shashikanth VH6de20d32015-10-09 12:04:13 +0530550 */
551 private void dispatchMessage(BGPMessage m) throws BGPParseException {
552 bgpPacketStats.addInPacket();
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530553 bgpController.processBGPPacket(thisbgpId, m);
Shashikanth VH6de20d32015-10-09 12:04:13 +0530554 }
555
556 /**
557 * Return a string describing this peer based on the already available information (ip address and/or remote
558 * socket).
559 *
560 * @return display string
561 */
562 private String getPeerInfoString() {
563 if (bgpPeer != null) {
564 return bgpPeer.toString();
565 }
566 String channelString;
567 if (channel == null || channel.getRemoteAddress() == null) {
568 channelString = "?";
569 } else {
570 channelString = channel.getRemoteAddress().toString();
571 }
572 String bgpIpString;
573 // TODO: implement functionality to get bgp id string
574 bgpIpString = "?";
575 return String.format("[%s BGP-IP[%s]]", channelString, bgpIpString);
576 }
577
578 /**
579 * Update the channels state. Only called from the state machine. TODO: enforce restricted state transitions
580 *
581 * @param state
582 */
583 private void setState(ChannelState state) {
584 this.state = state;
585 }
586
587 /**
588 * get packet statistics.
589 *
590 * @return packet statistics
591 */
592 public BGPPacketStatsImpl getBgpPacketStats() {
593 return bgpPacketStats;
594 }
595
596 /**
597 * Send handshake open message to the peer.
598 *
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530599 * @throws IOException, BGPParseException
Shashikanth VH6de20d32015-10-09 12:04:13 +0530600 */
601 private void sendHandshakeOpenMessage() throws IOException, BGPParseException {
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530602 int bgpId;
603
604 bgpId = Ip4Address.valueOf(bgpconfig.getRouterId()).toInt();
605 BGPMessage msg = factory4.openMessageBuilder().setAsNumber((short) bgpconfig.getAsNumber())
Shashikanth VHdae80402015-11-20 14:20:33 +0530606 .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId)
607 .setLsCapabilityTlv(bgpconfig.getLsCapability())
608 .setLargeAsCapabilityTlv(bgpconfig.getLargeASCapability())
609 .build();
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530610 log.debug("Sending open message to {}", channel.getRemoteAddress());
611 channel.write(Collections.singletonList(msg));
Shashikanth VH6de20d32015-10-09 12:04:13 +0530612
613 }
614
615 /**
Shashikanth VHdae80402015-11-20 14:20:33 +0530616 * Send notification message to peer.
617 *
618 * @param errorCode error code send in notification
619 * @param errorSubCode sub error code send in notification
620 * @param data data to send in notification
621 * @throws IOException, BGPParseException while building message
622 */
623 private void sendNotification(byte errorCode, byte errorSubCode, byte[] data)
624 throws IOException, BGPParseException {
625 BGPMessage msg = factory4.notificationMessageBuilder().setErrorCode(errorCode).setErrorSubCode(errorSubCode)
626 .setData(data).build();
627 log.debug("Sending notification message to {}", channel.getRemoteAddress());
628 channel.write(Collections.singletonList(msg));
629 }
630
631 /**
Shashikanth VH6de20d32015-10-09 12:04:13 +0530632 * Send keep alive message.
633 *
634 * @throws IOException when channel is disconnected
635 * @throws BGPParseException while building keep alive message
636 */
637 synchronized void sendKeepAliveMessage() throws IOException, BGPParseException {
638
Shashikanth VH9f8afb42015-11-04 18:00:30 +0530639 BGPMessage msg = factory4.keepaliveMessageBuilder().build();
640 log.debug("Sending keepalive message to {}", channel.getRemoteAddress());
641 channel.write(Collections.singletonList(msg));
Shashikanth VH6de20d32015-10-09 12:04:13 +0530642 }
643
644 /**
Shashikanth VH6de20d32015-10-09 12:04:13 +0530645 * Process unknown BGP message received.
646 *
Shashikanth VHdae80402015-11-20 14:20:33 +0530647 * @param errorCode error code
648 * @param errorSubCode error sub code
649 * @param data message type
650 * @throws BGPParseException while processing error messsage
651 * @throws IOException while processing error message
Shashikanth VH6de20d32015-10-09 12:04:13 +0530652 */
Shashikanth VHdae80402015-11-20 14:20:33 +0530653 public void processUnknownMsg(byte errorCode, byte errorSubCode, byte data) throws BGPParseException, IOException {
Shashikanth VH6de20d32015-10-09 12:04:13 +0530654 log.debug("UNKNOWN message received");
Shashikanth VHdae80402015-11-20 14:20:33 +0530655 byte[] byteArray = new byte[1];
656 byteArray[0] = data;
657 sendNotification(errorCode, errorSubCode, byteArray);
658 channel.close();
Shashikanth VH6de20d32015-10-09 12:04:13 +0530659 }
660
661 /**
Vidyashree Ramaee293252015-11-18 17:00:11 +0530662 * BGP open message validation.
Shashikanth VH6de20d32015-10-09 12:04:13 +0530663 *
664 * @param h channel handler
Vidyashree Ramaee293252015-11-18 17:00:11 +0530665 * @param openMsg open message
666 * @return true if valid message, otherwise false
667 * @throws BGPParseException throw exception
Shashikanth VH6de20d32015-10-09 12:04:13 +0530668 */
Vidyashree Ramaee293252015-11-18 17:00:11 +0530669 public boolean openMsgValidation(BGPChannelHandler h, BGPOpenMsg openMsg) throws BGPParseException {
670 boolean result;
671
672 // Validate BGP ID
673 result = bgpIdValidation(openMsg);
674 if (!result) {
675 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_BGP_IDENTIFIER, null);
676 }
677
678 // Validate AS number
679 result = asNumberValidation(h, openMsg);
680 if (!result) {
681 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
682 }
683
684 // Validate hold timer
685 if ((openMsg.getHoldTime() != 0) && (openMsg.getHoldTime() < BGP_MIN_HOLDTIME)) {
686 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.UNACCEPTABLE_HOLD_TIME, null);
687 }
688
689 // Validate capabilities
690 result = capabilityValidation(h, openMsg);
691 return result;
692 }
693
694 /**
695 * Capability Validation.
696 *
697 * @param h channel handler
698 * @param openmsg open message
699 * @return success or failure
700 * @throws BGPParseException
701 */
702 private boolean capabilityValidation(BGPChannelHandler h, BGPOpenMsg openmsg) throws BGPParseException {
703 log.debug("capabilityValidation");
704
705 boolean isMultiProtocolcapabilityExists = false;
706 boolean isFourOctetCapabilityExits = false;
707 int capAsNum = 0;
708
709 List<BGPValueType> capabilityTlv = openmsg.getCapabilityTlv();
710 ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator();
711 List<BGPValueType> unSupportedCapabilityTlv = new LinkedList<>();
712 ListIterator<BGPValueType> unSupportedCaplistIterator = unSupportedCapabilityTlv.listIterator();
713 BGPValueType tempTlv;
714 boolean isLargeAsCapabilityCfg = h.bgpconfig.getLargeASCapability();
715 boolean isLsCapabilityCfg = h.bgpconfig.getLsCapability();
716
717 while (listIterator.hasNext()) {
718 BGPValueType tlv = listIterator.next();
719 if (tlv.getType() == MULTI_PROTOCOL_EXTN_CAPA_TYPE) {
720 isMultiProtocolcapabilityExists = true;
721 }
722 if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) {
723 isFourOctetCapabilityExits = true;
724 capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt();
725 }
726 }
727
728 if (isFourOctetCapabilityExits) {
729 if (capAsNum > MAX_AS2_NUM) {
730 if (openmsg.getAsNumber() != AS_TRANS) {
731 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
732 }
733 } else {
734 if (capAsNum != openmsg.getAsNumber()) {
735 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_PEER_AS, null);
736 }
737 }
738 }
739
740 if ((isLsCapabilityCfg)) {
741 if (!isMultiProtocolcapabilityExists) {
742 tempTlv = new MultiProtocolExtnCapabilityTlv(AFI, RES, SAFI);
743 unSupportedCapabilityTlv.add(tempTlv);
744 }
745 }
746
747 if ((isLargeAsCapabilityCfg)) {
748 if (!isFourOctetCapabilityExits) {
749 tempTlv = new FourOctetAsNumCapabilityTlv(h.bgpconfig.getAsNumber());
750 unSupportedCapabilityTlv.add(tempTlv);
751 }
752 }
753
754 if (unSupportedCaplistIterator.hasNext()) {
755 ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
756 while (unSupportedCaplistIterator.hasNext()) {
757 BGPValueType tlv = unSupportedCaplistIterator.next();
758 tlv.write(buffer);
759 }
760 throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR,
761 BGPErrorType.UNSUPPORTED_CAPABILITY, buffer);
762 } else {
763 return true;
764 }
765 }
766
767 /**
768 * AS Number Validation.
769 *
770 * @param h channel Handler
771 * @param openMsg open message
772 * @return true or false
773 */
774 private boolean asNumberValidation(BGPChannelHandler h, BGPOpenMsg openMsg) {
775 log.debug("AS Num validation");
776
777 int capAsNum = 0;
778 boolean isFourOctetCapabilityExits = false;
779
780 BGPPeerCfg peerCfg = h.bgpconfig.displayPeers(peerAddr);
781 List<BGPValueType> capabilityTlv = openMsg.getCapabilityTlv();
782 ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator();
783
784 while (listIterator.hasNext()) {
785 BGPValueType tlv = listIterator.next();
786 if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) {
787 isFourOctetCapabilityExits = true;
788 capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt();
789 }
790 }
791
792 if (peerCfg.getAsNumber() > MAX_AS2_NUM) {
793 if (openMsg.getAsNumber() != AS_TRANS) {
794 return false;
795 }
796
797 if (!isFourOctetCapabilityExits) {
798 return false;
799 }
800
801 if (peerCfg.getAsNumber() != capAsNum) {
802 return false;
803 }
804
805 isIbgpSession = peerCfg.getIsIBgp();
806 if (isIbgpSession) {
807 // IBGP - AS number should be same for Peer and local if it is IBGP
808 if (h.bgpconfig.getAsNumber() != capAsNum) {
809 return false;
810 }
811 }
812 } else {
813
814 if (openMsg.getAsNumber() != peerCfg.getAsNumber()) {
815 return false;
816 }
817
818 if (isFourOctetCapabilityExits) {
819 if (capAsNum != peerCfg.getAsNumber()) {
820 return false;
821 }
822 }
823
824 isIbgpSession = peerCfg.getIsIBgp();
825 if (isIbgpSession) {
826 // IBGP - AS number should be same for Peer and local if it is IBGP
827 if (openMsg.getAsNumber() != h.bgpconfig.getAsNumber()) {
828 return false;
829 }
830 }
831 }
832 return true;
833 }
834
835 /**
836 * Validates BGP ID.
837 *
838 * @param openMsg open message
839 * @return true or false
840 */
841 private boolean bgpIdValidation(BGPOpenMsg openMsg) {
842 String openMsgBgpId = Ip4Address.valueOf(openMsg.getBgpId()).toString();
843
844 InetAddress ipAddress;
845 try {
846 ipAddress = InetAddress.getByName(openMsgBgpId);
847 if (ipAddress.isMulticastAddress()) {
848 return false;
849 }
850 } catch (UnknownHostException e) {
851 log.debug("InetAddress convertion failed");
852 }
Shashikanth VH6de20d32015-10-09 12:04:13 +0530853 return true;
854 }
855}