| /* |
| * Copyright 2016-present 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.isis.controller.impl; |
| |
| 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.onlab.packet.Ip4Address; |
| import org.onosproject.isis.controller.IsisInterface; |
| import org.onosproject.isis.controller.IsisLsdb; |
| import org.onosproject.isis.controller.IsisMessage; |
| import org.onosproject.isis.controller.IsisProcess; |
| import org.onosproject.isis.controller.impl.lsdb.DefaultIsisLsdb; |
| import org.onosproject.isis.exceptions.IsisParseException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.nio.channels.ClosedChannelException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.RejectedExecutionException; |
| import java.util.concurrent.ScheduledExecutorService; |
| |
| /** |
| * Channel handler deals with the ISIS channel connection. |
| * Also it dispatches messages to the appropriate handlers for processing. |
| */ |
| public class IsisChannelHandler extends IdleStateAwareChannelHandler { |
| |
| private static final Logger log = LoggerFactory.getLogger(IsisChannelHandler.class); |
| private static Map<Integer, Object> isisDb = null; |
| private Channel channel = null; |
| private Controller controller; |
| private List<IsisProcess> processes = null; |
| private List<ScheduledExecutorService> executorList = new ArrayList<>(); |
| private byte[] configPacket = null; |
| private Map<Integer, IsisInterface> isisInterfaceMap = new ConcurrentHashMap<>(); |
| private IsisLsdb isisLsdb = new DefaultIsisLsdb(); |
| private List<Ip4Address> interfaceIps = new ArrayList<>(); |
| |
| /** |
| * Creates an instance of ISIS channel handler. |
| * |
| * @param controller controller instance |
| * @param processes list of configured processes |
| */ |
| public IsisChannelHandler(Controller controller, List<IsisProcess> processes) { |
| this.controller = controller; |
| this.processes = processes; |
| ((DefaultIsisLsdb) isisLsdb).setController(this.controller); |
| ((DefaultIsisLsdb) isisLsdb).setIsisInterface(isisInterfaceList()); |
| } |
| |
| private List<IsisInterface> isisInterfaceList() { |
| List<IsisInterface> isisInterfaceList = new ArrayList<>(); |
| for (Integer key : isisInterfaceMap.keySet()) { |
| isisInterfaceList.add(isisInterfaceMap.get(key)); |
| } |
| return isisInterfaceList; |
| } |
| |
| /** |
| * Initializes the interface map with interface details. |
| */ |
| public void initializeInterfaceMap() { |
| for (IsisProcess process : processes) { |
| for (IsisInterface isisInterface : process.isisInterfaceList()) { |
| IsisInterface anInterface = isisInterfaceMap.get(isisInterface.interfaceIndex()); |
| if (anInterface == null) { |
| isisInterfaceMap.put(isisInterface.interfaceIndex(), isisInterface); |
| interfaceIps.add(isisInterface.interfaceIpAddress()); |
| } |
| } |
| } |
| //Initializes the interface with all interface ip details - for ls pdu generation |
| initializeInterfaceIpList(); |
| } |
| |
| /** |
| * Updates the interface map with interface details. |
| * |
| * @param isisProcesses updated process instances |
| */ |
| public void updateInterfaceMap(List<IsisProcess> isisProcesses) { |
| for (IsisProcess isisUpdatedProcess : isisProcesses) { |
| for (IsisInterface isisUpdatedInterface : isisUpdatedProcess.isisInterfaceList()) { |
| IsisInterface isisInterface = isisInterfaceMap.get(isisUpdatedInterface.interfaceIndex()); |
| if (isisInterface == null) { |
| isisInterfaceMap.put(isisUpdatedInterface.interfaceIndex(), isisUpdatedInterface); |
| interfaceIps.add(isisUpdatedInterface.interfaceIpAddress()); |
| } else { |
| if (isisInterface.intermediateSystemName() != isisUpdatedInterface.intermediateSystemName()) { |
| isisInterface.setIntermediateSystemName(isisUpdatedInterface.intermediateSystemName()); |
| } |
| if (isisInterface.reservedPacketCircuitType() != isisUpdatedInterface.reservedPacketCircuitType()) { |
| isisInterface.setReservedPacketCircuitType(isisUpdatedInterface.reservedPacketCircuitType()); |
| isisInterface.removeNeighbors(); |
| } |
| if (isisInterface.circuitId() != isisUpdatedInterface.circuitId()) { |
| isisInterface.setCircuitId(isisUpdatedInterface.circuitId()); |
| } |
| if (isisInterface.networkType() != isisUpdatedInterface.networkType()) { |
| isisInterface.setNetworkType(isisUpdatedInterface.networkType()); |
| isisInterface.removeNeighbors(); |
| } |
| if (isisInterface.areaAddress() != isisUpdatedInterface.areaAddress()) { |
| isisInterface.setAreaAddress(isisUpdatedInterface.areaAddress()); |
| } |
| if (isisInterface.holdingTime() != isisUpdatedInterface.holdingTime()) { |
| isisInterface.setHoldingTime(isisUpdatedInterface.holdingTime()); |
| } |
| if (isisInterface.helloInterval() != isisUpdatedInterface.helloInterval()) { |
| isisInterface.setHelloInterval(isisUpdatedInterface.helloInterval()); |
| isisInterface.stopHelloSender(); |
| isisInterface.startHelloSender(channel); |
| } |
| |
| isisInterfaceMap.put(isisInterface.interfaceIndex(), isisInterface); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Initializes the interface with all interface ip details. |
| */ |
| public void initializeInterfaceIpList() { |
| for (IsisProcess process : processes) { |
| for (IsisInterface isisInterface : process.isisInterfaceList()) { |
| ((DefaultIsisInterface) isisInterface).setAllConfiguredInterfaceIps(interfaceIps); |
| } |
| } |
| } |
| |
| /** |
| * Initialize channel, start hello sender and initialize LSDB. |
| */ |
| private void initialize() { |
| log.debug("IsisChannelHandler initialize..!!!"); |
| if (configPacket != null) { |
| log.debug("IsisChannelHandler initialize -> sentConfig packet of length ::" |
| + configPacket.length); |
| sentConfigPacket(configPacket); |
| } |
| initializeInterfaceMap(); |
| //start the hello timer |
| startHelloSender(); |
| //Initialize Database |
| isisLsdb.initializeDb(); |
| } |
| |
| @Override |
| public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent evt) throws Exception { |
| log.info("ISIS channelConnected from {}", evt.getChannel().getRemoteAddress()); |
| this.channel = evt.getChannel(); |
| initialize(); |
| } |
| |
| @Override |
| public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent evt) { |
| log.debug("IsisChannelHandler::channelDisconnected...!!!"); |
| if (controller != null) { |
| controller.connectPeer(); |
| stopHelloSender(); |
| } |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { |
| if (e.getCause() instanceof ReadTimeoutException) { |
| log.debug("Disconnecting device {} due to read timeout", e.getChannel().getRemoteAddress()); |
| return; |
| } else if (e.getCause() instanceof ClosedChannelException) { |
| log.debug("Channel for ISIS {} already closed", e.getChannel().getRemoteAddress()); |
| } else if (e.getCause() instanceof IOException) { |
| log.debug("Disconnecting ISIS {} due to IO Error: {}", e.getChannel().getRemoteAddress(), |
| e.getCause().getMessage()); |
| } else if (e.getCause() instanceof IsisParseException) { |
| IsisParseException errMsg = (IsisParseException) e.getCause(); |
| byte errorCode = errMsg.errorCode(); |
| byte errorSubCode = errMsg.errorSubCode(); |
| log.debug("Error while parsing message from ISIS {}, ErrorCode {}", |
| e.getChannel().getRemoteAddress(), errorCode); |
| } else if (e.getCause() instanceof RejectedExecutionException) { |
| log.debug("Could not process message: queue full"); |
| } else { |
| log.debug("Error while processing message from ISIS {}, {}", |
| e.getChannel().getRemoteAddress(), e.getCause().getMessage()); |
| } |
| } |
| |
| @Override |
| public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { |
| log.debug("IsisChannelHandler::messageReceived...!!!"); |
| Object message = e.getMessage(); |
| if (message instanceof List) { |
| List<IsisMessage> isisMessageList = (List<IsisMessage>) message; |
| log.debug("IsisChannelHandler::List of IsisMessages Size {}", isisMessageList.size()); |
| if (isisMessageList != null) { |
| for (IsisMessage isisMessage : isisMessageList) { |
| processIsisMessage(isisMessage, ctx); |
| } |
| } else { |
| log.debug("IsisChannelHandler::IsisMessages Null List...!!"); |
| } |
| } |
| if (message instanceof IsisMessage) { |
| IsisMessage isisMessage = (IsisMessage) message; |
| log.debug("IsisChannelHandler::IsisMessages received...!!"); |
| processIsisMessage(isisMessage, ctx); |
| } |
| } |
| |
| /** |
| * When an ISIS message received it is handed over to this method. |
| * Based on the type of the ISIS message received it will be handed over |
| * to corresponding message handler methods. |
| * |
| * @param isisMessage received ISIS message |
| * @param ctx channel handler context instance. |
| * @throws Exception might throws exception |
| */ |
| public void processIsisMessage(IsisMessage isisMessage, ChannelHandlerContext ctx) throws Exception { |
| log.debug("IsisChannelHandler::processIsisMessage...!!!"); |
| int interfaceIndex = isisMessage.interfaceIndex(); |
| IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| isisInterface.processIsisMessage(isisMessage, isisLsdb, channel); |
| } |
| |
| /** |
| * Starts the hello timer which sends hello packet every configured seconds. |
| */ |
| public void startHelloSender() { |
| log.debug("IsisController::startHelloSender"); |
| Set<Integer> interfaceIndexes = isisInterfaceMap.keySet(); |
| for (Integer interfaceIndex : interfaceIndexes) { |
| IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| isisInterface.startHelloSender(channel); |
| } |
| } |
| |
| /** |
| * Stops the hello timer. |
| */ |
| public void stopHelloSender() { |
| log.debug("ISISChannelHandler::stopHelloTimer "); |
| log.debug("IsisController::startHelloSender"); |
| Set<Integer> interfaceIndexes = isisInterfaceMap.keySet(); |
| for (Integer interfaceIndex : interfaceIndexes) { |
| IsisInterface isisInterface = isisInterfaceMap.get(interfaceIndex); |
| isisInterface.stopHelloSender(); |
| } |
| } |
| |
| /** |
| * Sends the interface configuration packet to server. |
| * |
| * @param configPacket interface configuration |
| */ |
| public void sentConfigPacket(byte[] configPacket) { |
| if (channel != null && channel.isConnected() && channel.isOpen()) { |
| channel.write(configPacket); |
| log.debug("IsisChannelHandler sentConfigPacket packet sent..!!!"); |
| } else { |
| log.debug("IsisChannelHandler sentConfigPacket channel not connected - re try..!!!"); |
| this.configPacket = configPacket; |
| } |
| } |
| } |