From 76dc892491948adae5e5e62cf94448967e8d865b Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Sun, 6 Dec 2015 07:15:03 -0800 Subject: Fixes bad POM file with ONOS commit 8c68536972f63069c263635c9d9f4f31d7f3e9a2 Change-Id: I7adb5a2d3738d53dbc41db7577768b0e7ced5450 Signed-off-by: Ashlee Young --- .../bgp/controller/impl/BgpChannelHandler.java | 896 +++++++++++++++++++++ 1 file changed, 896 insertions(+) create mode 100755 framework/src/onos/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpChannelHandler.java (limited to 'framework/src/onos/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpChannelHandler.java') diff --git a/framework/src/onos/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpChannelHandler.java b/framework/src/onos/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpChannelHandler.java new file mode 100755 index 00000000..0c55eadc --- /dev/null +++ b/framework/src/onos/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpChannelHandler.java @@ -0,0 +1,896 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller.impl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.channels.ClosedChannelException; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.RejectedExecutionException; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; +import org.jboss.netty.handler.timeout.ReadTimeoutException; +import org.jboss.netty.handler.timeout.ReadTimeoutHandler; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onosproject.bgp.controller.BgpCfg; +import org.onosproject.bgp.controller.BgpController; +import org.onosproject.bgp.controller.BgpId; +import org.onosproject.bgp.controller.BgpPeer; +import org.onosproject.bgp.controller.BgpPeerCfg; +import org.onosproject.bgp.controller.impl.BgpControllerImpl.BgpPeerManagerImpl; +import org.onosproject.bgpio.exceptions.BgpParseException; +import org.onosproject.bgpio.protocol.BgpFactory; +import org.onosproject.bgpio.protocol.BgpMessage; +import org.onosproject.bgpio.protocol.BgpOpenMsg; +import org.onosproject.bgpio.protocol.BgpType; +import org.onosproject.bgpio.protocol.BgpVersion; +import org.onosproject.bgpio.types.BgpErrorType; +import org.onosproject.bgpio.types.BgpValueType; +import org.onosproject.bgpio.types.FourOctetAsNumCapabilityTlv; +import org.onosproject.bgpio.types.MultiProtocolExtnCapabilityTlv; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel handler deals with the bgp peer connection and dispatches messages from peer to the appropriate locations. + */ +class BgpChannelHandler extends IdleStateAwareChannelHandler { + + private static final Logger log = LoggerFactory.getLogger(BgpChannelHandler.class); + static final int BGP_MIN_HOLDTIME = 3; + static final int BGP_MAX_KEEPALIVE_INTERVAL = 3; + private BgpPeer bgpPeer; + private BgpId thisbgpId; + private Channel channel; + private BgpKeepAliveTimer keepAliveTimer = null; + private short peerHoldTime = 0; + private short negotiatedHoldTime = 0; + private long peerAsNum; + private int peerIdentifier; + private BgpPacketStatsImpl bgpPacketStats; + static final int MAX_WRONG_COUNT_PACKET = 5; + static final byte MULTI_PROTOCOL_EXTN_CAPA_TYPE = 1; + static final byte FOUR_OCTET_AS_NUM_CAPA_TYPE = 65; + static final int AS_TRANS = 23456; + static final int MAX_AS2_NUM = 65535; + static final short AFI = 16388; + static final byte RES = 0; + static final byte SAFI = 71; + + // State needs to be volatile because the HandshakeTimeoutHandler + // needs to check if the handshake is complete + private volatile ChannelState state; + + // When a bgp peer with a ip addresss is found (i.e we already have a + // connected peer with the same ip), the new peer is immediately + // disconnected. At that point netty callsback channelDisconnected() which + // proceeds to cleaup peer state - we need to ensure that it does not + // cleanup + // peer state for the older (still connected) peer + private volatile Boolean duplicateBGPIdFound; + // Indicates the bgp version used by this bgp peer + protected BgpVersion bgpVersion; + private BgpController bgpController; + protected BgpFactory factory4; + private boolean isIbgpSession; + private BgpSessionInfoImpl sessionInfo; + private BgpPeerManagerImpl peerManager; + private InetSocketAddress inetAddress; + private IpAddress ipAddress; + private SocketAddress address; + private String peerAddr; + private BgpCfg bgpconfig; + + /** + * Create a new unconnected BGPChannelHandler. + * + * @param bgpController bgp controller + */ + BgpChannelHandler(BgpController bgpController) { + this.bgpController = bgpController; + this.peerManager = (BgpPeerManagerImpl) bgpController.peerManager(); + this.state = ChannelState.IDLE; + this.factory4 = Controller.getBgpMessageFactory4(); + this.duplicateBGPIdFound = Boolean.FALSE; + this.bgpPacketStats = new BgpPacketStatsImpl(); + this.bgpconfig = bgpController.getConfig(); + } + + // To disconnect peer session. + public void disconnectPeer() { + bgpPeer.disconnectPeer(); + } + + // ************************* + // Channel State Machine + // ************************* + + /** + * The state machine for handling the peer/channel state. All state transitions should happen from within the state + * machine (and not from other parts of the code) + */ + enum ChannelState { + /** + * Initial state before channel is connected. + */ + IDLE(false) { + + }, + + OPENSENT(false) { + @Override + void processBgpMessage(BgpChannelHandler h, BgpMessage m) throws IOException, BgpParseException { + log.debug("message received in OPENSENT state"); + // check for OPEN message + if (m.getType() != BgpType.OPEN) { + // When the message type is not keep alive message increment the wrong packet statistics + h.processUnknownMsg(BgpErrorType.FINITE_STATE_MACHINE_ERROR, + BgpErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENSENT_STATE, + m.getType().getType()); + log.debug("Message is not OPEN message"); + } else { + log.debug("Sending keep alive message in OPENSENT state"); + h.bgpPacketStats.addInPacket(); + + BgpOpenMsg pOpenmsg = (BgpOpenMsg) m; + h.peerIdentifier = pOpenmsg.getBgpId(); + + // validate capabilities and open msg + if (h.openMsgValidation(h, pOpenmsg)) { + if (h.connectionCollisionDetection(BgpPeerCfg.State.OPENCONFIRM, + h.peerIdentifier, h.peerAddr)) { + h.channel.close(); + return; + } + log.debug("Sending handshake OPEN message"); + + /* + * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the + * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time + * received in the OPEN message + */ + h.peerHoldTime = pOpenmsg.getHoldTime(); + if (h.peerHoldTime < h.bgpconfig.getHoldTime()) { + h.channel.getPipeline().replace("holdTime", + "holdTime", + new ReadTimeoutHandler(BgpPipelineFactory.TIMER, + h.peerHoldTime)); + } + + log.info("Hold Time : " + h.peerHoldTime); + + // update AS number + h.peerAsNum = pOpenmsg.getAsNumber(); + } + + // Send keepalive message to peer. + h.sendKeepAliveMessage(); + h.bgpPacketStats.addOutPacket(); + h.setState(OPENCONFIRM); + h.bgpconfig.setPeerConnState(h.peerAddr, BgpPeerCfg.State.OPENCONFIRM); + } + } + }, + + OPENWAIT(false) { + @Override + void processBgpMessage(BgpChannelHandler h, BgpMessage m) throws IOException, BgpParseException { + log.debug("Message received in OPEN WAIT State"); + + // check for open message + if (m.getType() != BgpType.OPEN) { + // When the message type is not open message increment the wrong packet statistics + h.processUnknownMsg(BgpErrorType.FINITE_STATE_MACHINE_ERROR, BgpErrorType.UNSPECIFIED_ERROR, + m.getType().getType()); + log.debug("Message is not OPEN message"); + } else { + h.bgpPacketStats.addInPacket(); + + BgpOpenMsg pOpenmsg = (BgpOpenMsg) m; + h.peerIdentifier = pOpenmsg.getBgpId(); + + // Validate open message + if (h.openMsgValidation(h, pOpenmsg)) { + if (h.connectionCollisionDetection(BgpPeerCfg.State.OPENSENT, + h.peerIdentifier, h.peerAddr)) { + h.channel.close(); + return; + } + log.debug("Sending handshake OPEN message"); + + /* + * RFC 4271, section 4.2: Upon receipt of an OPEN message, a BGP speaker MUST calculate the + * value of the Hold Timer by using the smaller of its configured Hold Time and the Hold Time + * received in the OPEN message + */ + h.peerHoldTime = pOpenmsg.getHoldTime(); + if (h.peerHoldTime < h.bgpconfig.getHoldTime()) { + h.channel.getPipeline().replace("holdTime", + "holdTime", + new ReadTimeoutHandler(BgpPipelineFactory.TIMER, + h.peerHoldTime)); + } + + log.debug("Hold Time : " + h.peerHoldTime); + + // update AS number + h.peerAsNum = pOpenmsg.getAsNumber(); + + h.sendHandshakeOpenMessage(); + h.bgpPacketStats.addOutPacket(); + h.setState(OPENCONFIRM); + h.bgpconfig.setPeerConnState(h.peerAddr, BgpPeerCfg.State.OPENCONFIRM); + } + } + } + }, + + OPENCONFIRM(false) { + @Override + void processBgpMessage(BgpChannelHandler h, BgpMessage m) throws IOException, BgpParseException { + log.debug("Message received in OPENCONFIRM state"); + // check for keep alive message + if (m.getType() != BgpType.KEEP_ALIVE) { + // When the message type is not keep alive message handle the wrong packet + h.processUnknownMsg(BgpErrorType.FINITE_STATE_MACHINE_ERROR, + BgpErrorType.RECEIVE_UNEXPECTED_MESSAGE_IN_OPENCONFIRM_STATE, + m.getType().getType()); + log.debug("Message is not KEEPALIVE message"); + } else { + + // Set the peer connected status + h.bgpPacketStats.addInPacket(); + log.debug("Sending keep alive message in OPENCONFIRM state"); + + final InetSocketAddress inetAddress = (InetSocketAddress) h.address; + h.thisbgpId = BgpId.bgpId(IpAddress.valueOf(inetAddress.getAddress())); + + // set session parameters + h.negotiatedHoldTime = (h.peerHoldTime < h.bgpconfig.getHoldTime()) ? h.peerHoldTime + : h.bgpconfig.getHoldTime(); + h.sessionInfo = new BgpSessionInfoImpl(h.thisbgpId, h.bgpVersion, h.peerAsNum, h.peerHoldTime, + h.peerIdentifier, h.negotiatedHoldTime, h.isIbgpSession); + + h.bgpPeer = h.peerManager.getBgpPeerInstance(h.bgpController, h.sessionInfo, h.bgpPacketStats); + // set the status of bgp as connected + h.bgpPeer.setConnected(true); + h.bgpPeer.setChannel(h.channel); + + /* + * RFC 4271, When an OPEN message is received, sends a KEEPALIVE message, If the negotiated hold + * time value is zero, then the HoldTimer and KeepaliveTimer are not started. A reasonable maximum + * time between KEEPALIVE messages would be one third of the Hold Time interval. + */ + + if (h.negotiatedHoldTime != 0) { + h.keepAliveTimer = new BgpKeepAliveTimer(h, + (h.negotiatedHoldTime / BGP_MAX_KEEPALIVE_INTERVAL)); + } else { + h.sendKeepAliveMessage(); + } + + h.bgpPacketStats.addOutPacket(); + + // set the state handshake completion. + h.setHandshakeComplete(true); + + if (!h.peerManager.addConnectedPeer(h.thisbgpId, h.bgpPeer)) { + disconnectDuplicate(h); + } else { + h.setState(ESTABLISHED); + h.bgpconfig.setPeerConnState(h.peerAddr, BgpPeerCfg.State.ESTABLISHED); + } + } + } + }, + + ESTABLISHED(true) { + @Override + void processBgpMessage(BgpChannelHandler h, BgpMessage m) throws IOException, BgpParseException { + log.debug("Message received in established state " + m.getType()); + // dispatch the message + h.dispatchMessage(m); + } + }; + + private boolean handshakeComplete; + + ChannelState(boolean handshakeComplete) { + this.handshakeComplete = handshakeComplete; + } + + /** + * Is this a state in which the handshake has completed? + * + * @return true if the handshake is complete + */ + public boolean isHandshakeComplete() { + return this.handshakeComplete; + } + + /** + * Disconnect duplicate peer connection. + * + * @param h channel handler + */ + protected void disconnectDuplicate(BgpChannelHandler h) { + log.error("Duplicated BGP IP or incompleted cleanup - " + "" + "disconnecting channel {}", + h.getPeerInfoString()); + h.duplicateBGPIdFound = Boolean.TRUE; + h.channel.disconnect(); + } + + // set handshake completion status + public void setHandshakeComplete(boolean handshakeComplete) { + this.handshakeComplete = handshakeComplete; + } + + void processBgpMessage(BgpChannelHandler bgpChannelHandler, BgpMessage pm) + throws IOException, BgpParseException { + // TODO Auto-generated method stub + log.debug("BGP message stub"); + } + + } + + // ************************* + // Channel handler methods + // ************************* + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + + channel = e.getChannel(); + log.info("BGP connected from {}", channel.getRemoteAddress()); + + address = channel.getRemoteAddress(); + if (!(address instanceof InetSocketAddress)) { + throw new IOException("Invalid peer connection."); + } + + + inetAddress = (InetSocketAddress) address; + peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString(); + + + // if connection is already established close channel + if (peerManager.isPeerConnected(BgpId.bgpId(IpAddress.valueOf(peerAddr)))) { + log.debug("Duplicate connection received, peer {}", peerAddr); + channel.close(); + return; + } + + if (null != channel.getPipeline().get("PassiveHandler")) { + log.info("BGP handle connection request from peer"); + // Wait for open message from bgp peer + setState(ChannelState.OPENWAIT); + } else if (null != channel.getPipeline().get("ActiveHandler")) { + log.info("BGP handle connection response from peer"); + + sendHandshakeOpenMessage(); + bgpPacketStats.addOutPacket(); + setState(ChannelState.OPENSENT); + bgpconfig.setPeerConnState(peerAddr, BgpPeerCfg.State.OPENSENT); + } + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + + channel = e.getChannel(); + log.info("BGP disconnected callback for bgp:{}. Cleaning up ...", getPeerInfoString()); + + address = channel.getRemoteAddress(); + if (!(address instanceof InetSocketAddress)) { + throw new IOException("Invalid peer connection."); + } + + inetAddress = (InetSocketAddress) address; + peerAddr = IpAddress.valueOf(inetAddress.getAddress()).toString(); + + if (thisbgpId != null) { + if (!duplicateBGPIdFound) { + // if the disconnected peer (on this ChannelHandler) + // was not one with a duplicate, it is safe to remove all + // state for it at the controller. Notice that if the disconnected + // peer was a duplicate-ip, calling the method below would clear + // all state for the original peer (with the same ip), + // which we obviously don't want. + log.debug("{}:removal called", getPeerInfoString()); + if (bgpPeer != null) { + BgpPeerImpl peer = (BgpPeerImpl) bgpPeer; + peerManager.removeConnectedPeer(thisbgpId); + peer.updateLocalRIBOnPeerDisconnect(); + } + + // Retry connection if connection is lost to bgp speaker/peer + if ((channel != null) && (null != channel.getPipeline().get("ActiveHandler"))) { + BgpConnectPeerImpl connectPeer; + BgpPeerCfg.State peerCfgState; + + peerCfgState = bgpconfig.getPeerConnState(peerAddr); + // on session disconnect using configuration, do not retry + if (!peerCfgState.equals(BgpPeerCfg.State.IDLE)) { + log.debug("Connection reset by peer, retry, STATE:{}", peerCfgState); + BgpPeerConfig peerConfig = (BgpPeerConfig) bgpconfig.displayPeers(peerAddr); + + bgpconfig.setPeerConnState(peerAddr, BgpPeerCfg.State.IDLE); + connectPeer = new BgpConnectPeerImpl(bgpController, peerAddr, Controller.getBgpPortNum()); + peerConfig.setConnectPeer(connectPeer); + } + } else { + bgpconfig.setPeerConnState(peerAddr, BgpPeerCfg.State.IDLE); + } + } else { + // A duplicate was disconnected on this ChannelHandler, + // this is the same peer reconnecting, but the original state was + // not cleaned up - XXX check liveness of original ChannelHandler + log.debug("{}:duplicate found", getPeerInfoString()); + duplicateBGPIdFound = Boolean.FALSE; + } + + if (null != keepAliveTimer) { + keepAliveTimer.getKeepAliveTimer().cancel(); + } + } else { + bgpconfig.setPeerConnState(peerAddr, BgpPeerCfg.State.IDLE); + log.warn("No bgp ip in channelHandler registered for " + "disconnected peer {}", getPeerInfoString()); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + + log.info("[exceptionCaught]: " + e.toString()); + + if (e.getCause() instanceof ReadTimeoutException) { + if ((ChannelState.OPENWAIT == state) || (ChannelState.OPENSENT == state)) { + + // When ReadTimeout timer is expired in OPENWAIT/OPENSENT state, it is considered + sendNotification(BgpErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null); + channel.close(); + state = ChannelState.IDLE; + return; + } else if (ChannelState.OPENCONFIRM == state) { + + // When ReadTimeout timer is expired in OPENCONFIRM state. + sendNotification(BgpErrorType.HOLD_TIMER_EXPIRED, (byte) 0, null); + channel.close(); + state = ChannelState.IDLE; + return; + } + } else if (e.getCause() instanceof ClosedChannelException) { + log.debug("Channel for bgp {} already closed", getPeerInfoString()); + } else if (e.getCause() instanceof IOException) { + log.error("Disconnecting peer {} due to IO Error: {}", getPeerInfoString(), e.getCause().getMessage()); + if (log.isDebugEnabled()) { + // still print stack trace if debug is enabled + log.debug("StackTrace for previous Exception: ", e.getCause()); + } + channel.close(); + } else if (e.getCause() instanceof BgpParseException) { + byte[] data = new byte[] {}; + BgpParseException errMsg = (BgpParseException) e.getCause(); + byte errorCode = errMsg.getErrorCode(); + byte errorSubCode = errMsg.getErrorSubCode(); + ChannelBuffer tempCb = errMsg.getData(); + if (tempCb != null) { + int dataLength = tempCb.capacity(); + data = new byte[dataLength]; + tempCb.readBytes(data, 0, dataLength); + } + sendNotification(errorCode, errorSubCode, data); + } else if (e.getCause() instanceof RejectedExecutionException) { + log.warn("Could not process message: queue full"); + } else { + log.error("Error while processing message from peer " + getPeerInfoString() + "state " + this.state); + channel.close(); + } + } + + @Override + public String toString() { + return getPeerInfoString(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + if (e.getMessage() instanceof List) { + @SuppressWarnings("Unchecked") + List msglist = (List) e.getMessage(); + for (BgpMessage pm : msglist) { + // Do the actual packet processing + state.processBgpMessage(this, pm); + } + } else { + state.processBgpMessage(this, (BgpMessage) e.getMessage()); + } + } + + /** + * Check for connection collision. + * + * @param state connection state + * @param peerIdentifier BGP peer identifier + * @param peerAddr BGP peer address + * @return true if bgp spreakers initiated connection + * @throws BgpParseException on error while procession collision detection + * @throws IOException on error while procession collision detection + */ + public boolean connectionCollisionDetection(BgpPeerCfg.State state, int peerIdentifier, String peerAddr) + throws IOException, BgpParseException { + /* + * RFC 4271, Section 6.8, Based on the value of the BGP identifier, a convention is established for detecting + * which BGP connection is to be preserved when a collision occurs. The convention is to compare the BGP + * Identifiers of the peers involved in the collision and to retain only the connection initiated by the BGP + * speaker with the higher-valued BGP Identifier.. + */ + BgpPeerCfg.State currentState = bgpconfig.getPeerConnState(peerAddr); + if (currentState.equals(state)) { + if (((Ip4Address.valueOf(bgpconfig.getRouterId())).compareTo(Ip4Address.valueOf(peerIdentifier))) > 0) { + // send notification + sendNotification(BgpErrorType.CEASE, BgpErrorType.CONNECTION_COLLISION_RESOLUTION, null); + log.debug("Connection collision detected, local id: {}, peer id: {}, peer state:{}, in state:{}", + (Ip4Address.valueOf(bgpconfig.getRouterId())), (Ip4Address.valueOf(peerIdentifier)), + currentState, state); + return true; + } + } + + return false; + } + + // ************************* + // Channel utility methods + // ************************* + /** + * Set handshake status. + * + * @param handshakeComplete handshake complete status + */ + public void setHandshakeComplete(boolean handshakeComplete) { + this.state.setHandshakeComplete(handshakeComplete); + } + + /** + * Is this a state in which the handshake has completed? + * + * @return true if the handshake is complete + */ + public boolean isHandshakeComplete() { + return state.isHandshakeComplete(); + } + + /** + * To handle the BGP message. + * + * @param m bgp message + * @throws BgpParseException throw exception + */ + private void dispatchMessage(BgpMessage m) throws BgpParseException { + bgpPacketStats.addInPacket(); + bgpController.processBGPPacket(thisbgpId, m); + } + + /** + * Return a string describing this peer based on the already available information (ip address and/or remote + * socket). + * + * @return display string + */ + private String getPeerInfoString() { + if (bgpPeer != null) { + return bgpPeer.toString(); + } + String channelString; + if (channel == null || channel.getRemoteAddress() == null) { + channelString = "?"; + } else { + channelString = channel.getRemoteAddress().toString(); + } + String bgpIpString; + // TODO: implement functionality to get bgp id string + bgpIpString = "?"; + return String.format("[%s BGP-IP[%s]]", channelString, bgpIpString); + } + + /** + * Update the channels state. Only called from the state machine. TODO: enforce restricted state transitions + * + * @param state + */ + private void setState(ChannelState state) { + this.state = state; + } + + /** + * get packet statistics. + * + * @return packet statistics + */ + public BgpPacketStatsImpl getBgpPacketStats() { + return bgpPacketStats; + } + + /** + * Send handshake open message to the peer. + * + * @throws IOException, BgpParseException + */ + private void sendHandshakeOpenMessage() throws IOException, BgpParseException { + int bgpId; + + InetSocketAddress localAddress = (InetSocketAddress) channel.getLocalAddress(); + + bgpId = Ip4Address.valueOf(IpAddress.valueOf(localAddress.getAddress()).toString()).toInt(); + BgpMessage msg = factory4.openMessageBuilder().setAsNumber((short) peerAsNum) + .setHoldTime(bgpconfig.getHoldTime()).setBgpId(bgpId).setLsCapabilityTlv(bgpconfig.getLsCapability()) + .setLargeAsCapabilityTlv(bgpconfig.getLargeASCapability()).build(); + log.debug("Sending open message to {}", channel.getRemoteAddress()); + channel.write(Collections.singletonList(msg)); + + } + + /** + * Send notification message to peer. + * + * @param errorCode error code send in notification + * @param errorSubCode sub error code send in notification + * @param data data to send in notification + * @throws IOException, BgpParseException while building message + */ + private void sendNotification(byte errorCode, byte errorSubCode, byte[] data) + throws IOException, BgpParseException { + BgpMessage msg = factory4.notificationMessageBuilder().setErrorCode(errorCode) + .setErrorSubCode(errorSubCode).setData(data).build(); + log.debug("Sending notification message to {}", channel.getRemoteAddress()); + channel.write(Collections.singletonList(msg)); + } + + /** + * Send keep alive message. + * + * @throws IOException when channel is disconnected + * @throws BgpParseException while building keep alive message + */ + synchronized void sendKeepAliveMessage() throws IOException, BgpParseException { + + BgpMessage msg = factory4.keepaliveMessageBuilder().build(); + log.debug("Sending keepalive message to {}", channel.getRemoteAddress()); + channel.write(Collections.singletonList(msg)); + } + + /** + * Process unknown BGP message received. + * + * @param errorCode error code + * @param errorSubCode error sub code + * @param data message type + * @throws BgpParseException while processing error messsage + * @throws IOException while processing error message + */ + public void processUnknownMsg(byte errorCode, byte errorSubCode, byte data) throws BgpParseException, IOException { + log.debug("UNKNOWN message received"); + byte[] byteArray = new byte[1]; + byteArray[0] = data; + sendNotification(errorCode, errorSubCode, byteArray); + channel.close(); + } + + /** + * BGP open message validation. + * + * @param h channel handler + * @param openMsg open message + * @return true if valid message, otherwise false + * @throws BgpParseException throw exception + */ + public boolean openMsgValidation(BgpChannelHandler h, BgpOpenMsg openMsg) throws BgpParseException { + boolean result; + + // Validate BGP ID + result = bgpIdValidation(openMsg); + if (!result) { + throw new BgpParseException(BgpErrorType.OPEN_MESSAGE_ERROR, BgpErrorType.BAD_BGP_IDENTIFIER, null); + } + + + // Validate hold timer + if ((openMsg.getHoldTime() != 0) && (openMsg.getHoldTime() < BGP_MIN_HOLDTIME)) { + throw new BgpParseException(BgpErrorType.OPEN_MESSAGE_ERROR, BgpErrorType.UNACCEPTABLE_HOLD_TIME, null); + } + + // Validate capabilities + result = capabilityValidation(h, openMsg); + return result; + } + + /** + * Capability Validation. + * + * @param h channel handler + * @param openmsg open message + * @return success or failure + * @throws BgpParseException + */ + private boolean capabilityValidation(BgpChannelHandler h, BgpOpenMsg openmsg) throws BgpParseException { + log.debug("capabilityValidation"); + + boolean isMultiProtocolcapabilityExists = false; + boolean isFourOctetCapabilityExits = false; + int capAsNum = 0; + + List capabilityTlv = openmsg.getCapabilityTlv(); + ListIterator listIterator = capabilityTlv.listIterator(); + List unSupportedCapabilityTlv = new LinkedList<>(); + ListIterator unSupportedCaplistIterator = unSupportedCapabilityTlv.listIterator(); + BgpValueType tempTlv; + boolean isLargeAsCapabilityCfg = h.bgpconfig.getLargeASCapability(); + boolean isLsCapabilityCfg = h.bgpconfig.getLsCapability(); + + while (listIterator.hasNext()) { + BgpValueType tlv = listIterator.next(); + if (tlv.getType() == MULTI_PROTOCOL_EXTN_CAPA_TYPE) { + isMultiProtocolcapabilityExists = true; + } + if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) { + isFourOctetCapabilityExits = true; + capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt(); + } + } + + if (isFourOctetCapabilityExits) { + if (capAsNum > MAX_AS2_NUM) { + if (openmsg.getAsNumber() != AS_TRANS) { + throw new BgpParseException(BgpErrorType.OPEN_MESSAGE_ERROR, BgpErrorType.BAD_PEER_AS, null); + } + } else { + if (capAsNum != openmsg.getAsNumber()) { + throw new BgpParseException(BgpErrorType.OPEN_MESSAGE_ERROR, BgpErrorType.BAD_PEER_AS, null); + } + } + } + + if ((isLsCapabilityCfg)) { + if (!isMultiProtocolcapabilityExists) { + tempTlv = new MultiProtocolExtnCapabilityTlv(AFI, RES, SAFI); + unSupportedCapabilityTlv.add(tempTlv); + } + } + + if ((isLargeAsCapabilityCfg)) { + if (!isFourOctetCapabilityExits) { + tempTlv = new FourOctetAsNumCapabilityTlv(h.bgpconfig.getAsNumber()); + unSupportedCapabilityTlv.add(tempTlv); + } + } + + if (unSupportedCaplistIterator.hasNext()) { + ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); + while (unSupportedCaplistIterator.hasNext()) { + BgpValueType tlv = unSupportedCaplistIterator.next(); + tlv.write(buffer); + } + throw new BgpParseException(BgpErrorType.OPEN_MESSAGE_ERROR, BgpErrorType.UNSUPPORTED_CAPABILITY, buffer); + } else { + return true; + } + } + + /** + * AS Number Validation. + * + * @param h channel Handler + * @param openMsg open message + * @return true or false + */ + private boolean asNumberValidation(BgpChannelHandler h, BgpOpenMsg openMsg) { + log.debug("AS Num validation"); + + int capAsNum = 0; + boolean isFourOctetCapabilityExits = false; + + BgpPeerCfg peerCfg = h.bgpconfig.displayPeers(peerAddr); + List capabilityTlv = openMsg.getCapabilityTlv(); + ListIterator listIterator = capabilityTlv.listIterator(); + + while (listIterator.hasNext()) { + BgpValueType tlv = listIterator.next(); + if (tlv.getType() == FOUR_OCTET_AS_NUM_CAPA_TYPE) { + isFourOctetCapabilityExits = true; + capAsNum = ((FourOctetAsNumCapabilityTlv) tlv).getInt(); + } + } + + if (peerCfg.getAsNumber() > MAX_AS2_NUM) { + if (openMsg.getAsNumber() != AS_TRANS) { + return false; + } + + if (!isFourOctetCapabilityExits) { + return false; + } + + if (peerCfg.getAsNumber() != capAsNum) { + return false; + } + + isIbgpSession = peerCfg.getIsIBgp(); + if (isIbgpSession) { + // IBGP - AS number should be same for Peer and local if it is IBGP + if (h.bgpconfig.getAsNumber() != capAsNum) { + return false; + } + } + } else { + + if (openMsg.getAsNumber() != peerCfg.getAsNumber()) { + return false; + } + + if (isFourOctetCapabilityExits) { + if (capAsNum != peerCfg.getAsNumber()) { + return false; + } + } + + isIbgpSession = peerCfg.getIsIBgp(); + if (isIbgpSession) { + // IBGP - AS number should be same for Peer and local if it is IBGP + if (openMsg.getAsNumber() != h.bgpconfig.getAsNumber()) { + return false; + } + } + } + return true; + } + + /** + * Validates BGP ID. + * + * @param openMsg open message + * @return true or false + */ + private boolean bgpIdValidation(BgpOpenMsg openMsg) { + String openMsgBgpId = Ip4Address.valueOf(openMsg.getBgpId()).toString(); + + InetAddress ipAddress; + try { + ipAddress = InetAddress.getByName(openMsgBgpId); + if (ipAddress.isMulticastAddress()) { + return false; + } + } catch (UnknownHostException e) { + log.debug("InetAddress convertion failed"); + } + return true; + } +} -- cgit 1.2.3-korg