| /* |
| * 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.ospf.controller.impl; |
| |
| import org.jboss.netty.bootstrap.ClientBootstrap; |
| import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor; |
| import org.jboss.netty.channel.ChannelFuture; |
| import org.jboss.netty.channel.ChannelFutureListener; |
| import org.jboss.netty.channel.ChannelPipelineFactory; |
| import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory; |
| import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.packet.TpPort; |
| import org.onosproject.net.driver.DriverService; |
| import org.onosproject.ospf.controller.OspfAgent; |
| import org.onosproject.ospf.controller.OspfArea; |
| import org.onosproject.ospf.controller.OspfInterface; |
| import org.onosproject.ospf.controller.OspfLinkTed; |
| import org.onosproject.ospf.controller.OspfProcess; |
| import org.onosproject.ospf.controller.OspfRouter; |
| import org.onosproject.ospf.protocol.util.OspfUtil; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.net.NetworkInterface; |
| import java.util.Enumeration; |
| import java.util.List; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| import static org.onlab.util.Tools.groupedThreads; |
| |
| /** |
| * Representation of an OSPF controller. |
| */ |
| public class Controller { |
| protected static final int BUFFER_SIZE = 4 * 1024 * 1024; |
| private static final Logger log = LoggerFactory.getLogger(Controller.class); |
| private static final int RETRY_INTERVAL = 4; |
| private final int peerWorkerThreads = 16; |
| protected long systemStartTime; |
| byte[] configPacket = null; |
| private List<OspfProcess> processes = null; |
| private OspfInterfaceChannelHandler ospfChannelHandler; |
| private NioClientSocketChannelFactory peerExecFactory; |
| private ClientBootstrap peerBootstrap = null; |
| private TpPort ospfPort = TpPort.tpPort(OspfUtil.SPORT); |
| private ScheduledExecutorService connectExecutor = null; |
| private int connectRetryCounter = 0; |
| private int connectRetryTime; |
| private DriverService driverService; |
| private OspfAgent agent; |
| |
| /** |
| * Deactivates OSPF controller. |
| */ |
| public void ospfDeactivate() { |
| peerExecFactory.shutdown(); |
| } |
| |
| /** |
| * Updates the processes configuration. |
| * |
| * @param ospfProcesses list of OSPF process instances |
| * @throws Exception might throws parse exception |
| */ |
| public void updateConfig(List<OspfProcess> ospfProcesses) throws Exception { |
| log.debug("Controller::UpdateConfig called"); |
| configPacket = new byte[OspfUtil.CONFIG_LENGTH]; |
| byte numberOfInterface = 0; // number of interfaces to configure |
| configPacket[0] = (byte) 0xFF; // its a conf packet - identifier |
| for (OspfProcess ospfProcess : ospfProcesses) { |
| log.debug("OspfProcessDetails : " + ospfProcess); |
| for (OspfArea ospfArea : ospfProcess.areas()) { |
| for (OspfInterface ospfInterface : ospfArea.ospfInterfaceList()) { |
| log.debug("OspfInterfaceDetails : " + ospfInterface); |
| numberOfInterface++; |
| configPacket[2 * numberOfInterface] = (byte) ospfInterface.interfaceIndex(); |
| configPacket[(2 * numberOfInterface) + 1] = (byte) 4; |
| } |
| } |
| } |
| configPacket[1] = numberOfInterface; |
| //First time configuration |
| if (processes == null) { |
| if (ospfProcesses.size() > 0) { |
| processes = ospfProcesses; |
| connectPeer(); |
| } |
| } else { |
| ospfChannelHandler.updateInterfaceMap(ospfProcesses); |
| //Send the config packet |
| ospfChannelHandler.sentConfigPacket(configPacket); |
| } |
| } |
| |
| /** |
| * Initializes the netty client channel connection. |
| */ |
| private void initConnection() { |
| if (peerBootstrap != null) { |
| return; |
| } |
| peerBootstrap = createPeerBootStrap(); |
| |
| peerBootstrap.setOption("reuseAddress", true); |
| peerBootstrap.setOption("tcpNoDelay", true); |
| peerBootstrap.setOption("keepAlive", true); |
| peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE); |
| peerBootstrap.setOption("receiveBufferSizePredictorFactory", |
| new FixedReceiveBufferSizePredictorFactory( |
| Controller.BUFFER_SIZE)); |
| peerBootstrap.setOption("receiveBufferSizePredictor", |
| new AdaptiveReceiveBufferSizePredictor(64, 4096, 65536)); |
| peerBootstrap.setOption("child.keepAlive", true); |
| peerBootstrap.setOption("child.tcpNoDelay", true); |
| peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE); |
| peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE); |
| peerBootstrap.setOption("child.receiveBufferSizePredictorFactory", |
| new FixedReceiveBufferSizePredictorFactory( |
| Controller.BUFFER_SIZE)); |
| peerBootstrap.setOption("child.reuseAddress", true); |
| |
| ospfChannelHandler = new OspfInterfaceChannelHandler(this, processes); |
| ChannelPipelineFactory pfact = new OspfPipelineFactory(ospfChannelHandler); |
| peerBootstrap.setPipelineFactory(pfact); |
| } |
| |
| /** |
| * Creates peer boot strap. |
| * |
| * @return client bootstrap instance |
| */ |
| private ClientBootstrap createPeerBootStrap() { |
| |
| if (peerWorkerThreads == 0) { |
| peerExecFactory = new NioClientSocketChannelFactory( |
| Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")), |
| Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d"))); |
| return new ClientBootstrap(peerExecFactory); |
| } else { |
| peerExecFactory = new NioClientSocketChannelFactory( |
| Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")), |
| Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d")), |
| peerWorkerThreads); |
| return new ClientBootstrap(peerExecFactory); |
| } |
| } |
| |
| /** |
| * Gets all configured processes. |
| * |
| * @return all configured processes |
| */ |
| public List<OspfProcess> getAllConfiguredProcesses() { |
| return processes; |
| } |
| |
| /** |
| * Adds device details. |
| * |
| * @param ospfRouter OSPF router instance |
| */ |
| public void addDeviceDetails(OspfRouter ospfRouter) { |
| agent.addConnectedRouter(ospfRouter); |
| } |
| |
| /** |
| * Removes device details. |
| * |
| * @param ospfRouter OSPF router instance |
| */ |
| public void removeDeviceDetails(OspfRouter ospfRouter) { |
| agent.removeConnectedRouter(ospfRouter); |
| } |
| |
| /** |
| * Adds link details. |
| * |
| * @param ospfRouter OSPF router instance |
| * @param ospfLinkTed OSPF link ted instance |
| */ |
| public void addLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) { |
| agent.addLink(ospfRouter, ospfLinkTed); |
| } |
| |
| /** |
| * Removes link details. |
| * |
| * @param ospfRouter OSPF router instance |
| * @param ospfLinkTed OSPF link ted instance |
| */ |
| public void removeLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) { |
| agent.deleteLink(ospfRouter, ospfLinkTed); |
| } |
| |
| /** |
| * Initializes internal data structures. |
| */ |
| public void init() { |
| this.systemStartTime = System.currentTimeMillis(); |
| } |
| |
| /** |
| * Starts the controller. |
| * |
| * @param ag OSPF agent instance |
| * @param driverService driver service instance |
| */ |
| public void start(OspfAgent ag, DriverService driverService) { |
| log.info("Starting OSPF controller...!!!"); |
| this.agent = ag; |
| this.driverService = driverService; |
| this.init(); |
| } |
| |
| /** |
| * Stops the Controller. |
| */ |
| public void stop() { |
| log.info("Stopping OSPF controller...!!!"); |
| ospfDeactivate(); |
| processes.clear(); |
| } |
| |
| /** |
| * Returns interface IP by index. |
| * |
| * @param interfaceIndex interface index |
| * @return interface IP by index |
| */ |
| private Ip4Address getInterfaceIp(int interfaceIndex) { |
| Ip4Address ipAddress = null; |
| try { |
| NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex); |
| Enumeration ipAddresses = networkInterface.getInetAddresses(); |
| while (ipAddresses.hasMoreElements()) { |
| InetAddress address = (InetAddress) ipAddresses.nextElement(); |
| if (!address.isLinkLocalAddress()) { |
| ipAddress = Ip4Address.valueOf(address.getAddress()); |
| break; |
| } |
| } |
| } catch (Exception e) { |
| log.debug("Error while getting Interface IP by index"); |
| return OspfUtil.DEFAULTIP; |
| } |
| |
| return ipAddress; |
| } |
| |
| /** |
| * Returns interface mask by index. |
| * |
| * @param interfaceIndex interface index |
| * @return interface IP by index |
| */ |
| private String getInterfaceMask(int interfaceIndex) { |
| String subnetMask = null; |
| try { |
| Ip4Address ipAddress = getInterfaceIp(interfaceIndex); |
| NetworkInterface networkInterface = NetworkInterface.getByInetAddress( |
| InetAddress.getByName(ipAddress.toString())); |
| Enumeration ipAddresses = networkInterface.getInetAddresses(); |
| int index = 0; |
| while (ipAddresses.hasMoreElements()) { |
| InetAddress address = (InetAddress) ipAddresses.nextElement(); |
| if (!address.isLinkLocalAddress()) { |
| break; |
| } |
| index++; |
| } |
| int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength(); |
| int shft = 0xffffffff << (32 - prfLen); |
| int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff; |
| int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff; |
| int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff; |
| int oct4 = ((byte) (shft & 0x000000ff)) & 0xff; |
| subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4; |
| } catch (Exception e) { |
| log.debug("Error while getting Interface network mask by index"); |
| return subnetMask; |
| } |
| |
| return subnetMask; |
| } |
| |
| /** |
| * Disconnects the executor. |
| */ |
| public void disconnectExecutor() { |
| if (connectExecutor != null) { |
| connectExecutor.shutdown(); |
| connectExecutor = null; |
| } |
| } |
| |
| /** |
| * Connects to peer. |
| */ |
| public void connectPeer() { |
| scheduleConnectionRetry(this.connectRetryTime); |
| } |
| |
| /** |
| * Retry connection with exponential back-off mechanism. |
| * |
| * @param retryDelay retry delay |
| */ |
| private void scheduleConnectionRetry(long retryDelay) { |
| if (this.connectExecutor == null) { |
| this.connectExecutor = Executors.newSingleThreadScheduledExecutor(); |
| } |
| this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES); |
| } |
| |
| /** |
| * Implements OSPF connection and manages connection to peer with back-off mechanism in case of failure. |
| */ |
| class ConnectionRetry implements Runnable { |
| @Override |
| public void run() { |
| log.debug("Connect to peer {}", OspfUtil.SHOST); |
| initConnection(); |
| ospfChannelHandler.sentConfigPacket(configPacket); |
| InetSocketAddress connectToSocket = new InetSocketAddress(OspfUtil.SHOST, ospfPort.toInt()); |
| try { |
| peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() { |
| @Override |
| public void operationComplete(ChannelFuture future) throws Exception { |
| if (!future.isSuccess()) { |
| connectRetryCounter++; |
| log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter, |
| OspfUtil.SHOST); |
| /* |
| * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4 |
| * mins. |
| */ |
| if (connectRetryTime < RETRY_INTERVAL) { |
| connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1; |
| } |
| scheduleConnectionRetry(connectRetryTime); |
| } else { |
| //Send the config packet |
| ospfChannelHandler.sentConfigPacket(configPacket); |
| connectRetryCounter++; |
| log.info("Connected to remote host {}, Connect Counter {}", OspfUtil.SHOST, |
| connectRetryCounter); |
| disconnectExecutor(); |
| |
| return; |
| } |
| } |
| }); |
| } catch (Exception e) { |
| log.info("Connect peer exception : " + e.toString()); |
| disconnectExecutor(); |
| } |
| } |
| } |
| } |