blob: 3816806397b0c4a1a41db9827ebcfb91216e83bf [file] [log] [blame]
Kalyankumar Asangi27728f22016-02-17 15:46:28 +05301/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Kalyankumar Asangi27728f22016-02-17 15:46:28 +05303 *
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 */
16package org.onosproject.ospf.controller.impl;
17
sunishvkf7c56552016-07-18 16:02:39 +053018import org.jboss.netty.bootstrap.ClientBootstrap;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053019import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor;
sunishvkf7c56552016-07-18 16:02:39 +053020import org.jboss.netty.channel.ChannelFuture;
21import org.jboss.netty.channel.ChannelFutureListener;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053022import org.jboss.netty.channel.ChannelPipelineFactory;
23import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory;
sunishvkf7c56552016-07-18 16:02:39 +053024import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
25import org.onlab.packet.Ip4Address;
26import org.onlab.packet.TpPort;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053027import org.onosproject.net.driver.DriverService;
28import org.onosproject.ospf.controller.OspfAgent;
29import org.onosproject.ospf.controller.OspfArea;
30import org.onosproject.ospf.controller.OspfInterface;
31import org.onosproject.ospf.controller.OspfLinkTed;
32import org.onosproject.ospf.controller.OspfProcess;
33import org.onosproject.ospf.controller.OspfRouter;
sunishvkf7c56552016-07-18 16:02:39 +053034import org.onosproject.ospf.protocol.util.OspfUtil;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053035import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38import java.net.InetAddress;
39import java.net.InetSocketAddress;
40import java.net.NetworkInterface;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053041import java.util.Enumeration;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053042import java.util.List;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053043import java.util.concurrent.Executors;
sunishvkf7c56552016-07-18 16:02:39 +053044import java.util.concurrent.ScheduledExecutorService;
45import java.util.concurrent.TimeUnit;
46
47import static org.onlab.util.Tools.groupedThreads;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053048
49/**
sunishvkf7c56552016-07-18 16:02:39 +053050 * Representation of an OSPF controller.
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053051 */
52public class Controller {
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053053 protected static final int BUFFER_SIZE = 4 * 1024 * 1024;
sunishvkf7c56552016-07-18 16:02:39 +053054 private static final Logger log = LoggerFactory.getLogger(Controller.class);
55 private static final int RETRY_INTERVAL = 4;
56 private final int peerWorkerThreads = 16;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053057 protected long systemStartTime;
sunishvkf7c56552016-07-18 16:02:39 +053058 byte[] configPacket = null;
59 private List<OspfProcess> processes = null;
60 private OspfInterfaceChannelHandler ospfChannelHandler;
61 private NioClientSocketChannelFactory peerExecFactory;
62 private ClientBootstrap peerBootstrap = null;
sangyun-han53ee58a2017-01-05 16:34:02 +090063 private TpPort ospfPort = TpPort.tpPort(OspfUtil.SPORT);
sunishvkf7c56552016-07-18 16:02:39 +053064 private ScheduledExecutorService connectExecutor = null;
65 private int connectRetryCounter = 0;
66 private int connectRetryTime;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +053067 private DriverService driverService;
68 private OspfAgent agent;
sunishvkf7c56552016-07-18 16:02:39 +053069
70 /**
71 * Deactivates OSPF controller.
72 */
sangyun-han53ee58a2017-01-05 16:34:02 +090073 public void ospfDeactivate() {
sunishvkf7c56552016-07-18 16:02:39 +053074 peerExecFactory.shutdown();
75 }
76
77 /**
78 * Updates the processes configuration.
79 *
80 * @param ospfProcesses list of OSPF process instances
81 * @throws Exception might throws parse exception
82 */
83 public void updateConfig(List<OspfProcess> ospfProcesses) throws Exception {
84 log.debug("Controller::UpdateConfig called");
85 configPacket = new byte[OspfUtil.CONFIG_LENGTH];
86 byte numberOfInterface = 0; // number of interfaces to configure
87 configPacket[0] = (byte) 0xFF; // its a conf packet - identifier
88 for (OspfProcess ospfProcess : ospfProcesses) {
89 log.debug("OspfProcessDetails : " + ospfProcess);
90 for (OspfArea ospfArea : ospfProcess.areas()) {
91 for (OspfInterface ospfInterface : ospfArea.ospfInterfaceList()) {
92 log.debug("OspfInterfaceDetails : " + ospfInterface);
93 numberOfInterface++;
94 configPacket[2 * numberOfInterface] = (byte) ospfInterface.interfaceIndex();
95 configPacket[(2 * numberOfInterface) + 1] = (byte) 4;
96 }
97 }
98 }
99 configPacket[1] = numberOfInterface;
100 //First time configuration
101 if (processes == null) {
102 if (ospfProcesses.size() > 0) {
103 processes = ospfProcesses;
104 connectPeer();
105 }
106 } else {
107 ospfChannelHandler.updateInterfaceMap(ospfProcesses);
108 //Send the config packet
109 ospfChannelHandler.sentConfigPacket(configPacket);
110 }
111 }
112
113 /**
114 * Initializes the netty client channel connection.
115 */
116 private void initConnection() {
117 if (peerBootstrap != null) {
118 return;
119 }
120 peerBootstrap = createPeerBootStrap();
121
122 peerBootstrap.setOption("reuseAddress", true);
123 peerBootstrap.setOption("tcpNoDelay", true);
124 peerBootstrap.setOption("keepAlive", true);
125 peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE);
126 peerBootstrap.setOption("receiveBufferSizePredictorFactory",
127 new FixedReceiveBufferSizePredictorFactory(
128 Controller.BUFFER_SIZE));
129 peerBootstrap.setOption("receiveBufferSizePredictor",
130 new AdaptiveReceiveBufferSizePredictor(64, 4096, 65536));
131 peerBootstrap.setOption("child.keepAlive", true);
132 peerBootstrap.setOption("child.tcpNoDelay", true);
133 peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE);
134 peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE);
135 peerBootstrap.setOption("child.receiveBufferSizePredictorFactory",
136 new FixedReceiveBufferSizePredictorFactory(
137 Controller.BUFFER_SIZE));
138 peerBootstrap.setOption("child.reuseAddress", true);
139
140 ospfChannelHandler = new OspfInterfaceChannelHandler(this, processes);
141 ChannelPipelineFactory pfact = new OspfPipelineFactory(ospfChannelHandler);
142 peerBootstrap.setPipelineFactory(pfact);
143 }
144
145 /**
146 * Creates peer boot strap.
147 *
148 * @return client bootstrap instance
149 */
150 private ClientBootstrap createPeerBootStrap() {
151
152 if (peerWorkerThreads == 0) {
153 peerExecFactory = new NioClientSocketChannelFactory(
sangyun-han53ee58a2017-01-05 16:34:02 +0900154 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")),
155 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d")));
sunishvkf7c56552016-07-18 16:02:39 +0530156 return new ClientBootstrap(peerExecFactory);
157 } else {
158 peerExecFactory = new NioClientSocketChannelFactory(
sangyun-han53ee58a2017-01-05 16:34:02 +0900159 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")),
160 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d")),
sunishvkf7c56552016-07-18 16:02:39 +0530161 peerWorkerThreads);
162 return new ClientBootstrap(peerExecFactory);
163 }
164 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530165
166 /**
167 * Gets all configured processes.
168 *
169 * @return all configured processes
170 */
171 public List<OspfProcess> getAllConfiguredProcesses() {
172 return processes;
173 }
174
175 /**
176 * Adds device details.
177 *
178 * @param ospfRouter OSPF router instance
179 */
180 public void addDeviceDetails(OspfRouter ospfRouter) {
181 agent.addConnectedRouter(ospfRouter);
182 }
183
184 /**
185 * Removes device details.
186 *
187 * @param ospfRouter OSPF router instance
188 */
189 public void removeDeviceDetails(OspfRouter ospfRouter) {
190 agent.removeConnectedRouter(ospfRouter);
191 }
192
193 /**
194 * Adds link details.
195 *
196 * @param ospfRouter OSPF router instance
197 * @param ospfLinkTed OSPF link ted instance
198 */
199 public void addLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
200 agent.addLink(ospfRouter, ospfLinkTed);
201 }
202
203 /**
204 * Removes link details.
205 *
sunishvkf7c56552016-07-18 16:02:39 +0530206 * @param ospfRouter OSPF router instance
207 * @param ospfLinkTed OSPF link ted instance
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530208 */
sunishvkf7c56552016-07-18 16:02:39 +0530209 public void removeLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
210 agent.deleteLink(ospfRouter, ospfLinkTed);
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530211 }
212
213 /**
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530214 * Initializes internal data structures.
215 */
216 public void init() {
217 this.systemStartTime = System.currentTimeMillis();
218 }
219
220 /**
221 * Starts the controller.
222 *
223 * @param ag OSPF agent instance
224 * @param driverService driver service instance
225 */
226 public void start(OspfAgent ag, DriverService driverService) {
sunishvkf7c56552016-07-18 16:02:39 +0530227 log.info("Starting OSPF controller...!!!");
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530228 this.agent = ag;
229 this.driverService = driverService;
230 this.init();
231 }
232
233 /**
234 * Stops the Controller.
235 */
236 public void stop() {
sunishvkf7c56552016-07-18 16:02:39 +0530237 log.info("Stopping OSPF controller...!!!");
sangyun-han53ee58a2017-01-05 16:34:02 +0900238 ospfDeactivate();
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530239 processes.clear();
240 }
241
242 /**
sunishvkf7c56552016-07-18 16:02:39 +0530243 * Returns interface IP by index.
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530244 *
sunishvkf7c56552016-07-18 16:02:39 +0530245 * @param interfaceIndex interface index
246 * @return interface IP by index
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530247 */
sunishvkf7c56552016-07-18 16:02:39 +0530248 private Ip4Address getInterfaceIp(int interfaceIndex) {
249 Ip4Address ipAddress = null;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530250 try {
sunishvkf7c56552016-07-18 16:02:39 +0530251 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
252 Enumeration ipAddresses = networkInterface.getInetAddresses();
253 while (ipAddresses.hasMoreElements()) {
254 InetAddress address = (InetAddress) ipAddresses.nextElement();
255 if (!address.isLinkLocalAddress()) {
256 ipAddress = Ip4Address.valueOf(address.getAddress());
257 break;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530258 }
259 }
sunishvkf7c56552016-07-18 16:02:39 +0530260 } catch (Exception e) {
261 log.debug("Error while getting Interface IP by index");
262 return OspfUtil.DEFAULTIP;
263 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530264
sunishvkf7c56552016-07-18 16:02:39 +0530265 return ipAddress;
266 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530267
sunishvkf7c56552016-07-18 16:02:39 +0530268 /**
269 * Returns interface mask by index.
270 *
271 * @param interfaceIndex interface index
272 * @return interface IP by index
273 */
274 private String getInterfaceMask(int interfaceIndex) {
275 String subnetMask = null;
276 try {
277 Ip4Address ipAddress = getInterfaceIp(interfaceIndex);
278 NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
279 InetAddress.getByName(ipAddress.toString()));
280 Enumeration ipAddresses = networkInterface.getInetAddresses();
281 int index = 0;
282 while (ipAddresses.hasMoreElements()) {
283 InetAddress address = (InetAddress) ipAddresses.nextElement();
284 if (!address.isLinkLocalAddress()) {
285 break;
286 }
287 index++;
288 }
289 int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength();
290 int shft = 0xffffffff << (32 - prfLen);
291 int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff;
292 int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff;
293 int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff;
294 int oct4 = ((byte) (shft & 0x000000ff)) & 0xff;
295 subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4;
296 } catch (Exception e) {
297 log.debug("Error while getting Interface network mask by index");
298 return subnetMask;
299 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530300
sunishvkf7c56552016-07-18 16:02:39 +0530301 return subnetMask;
302 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530303
sunishvkf7c56552016-07-18 16:02:39 +0530304 /**
305 * Disconnects the executor.
306 */
307 public void disconnectExecutor() {
308 if (connectExecutor != null) {
309 connectExecutor.shutdown();
310 connectExecutor = null;
311 }
312 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530313
sunishvkf7c56552016-07-18 16:02:39 +0530314 /**
315 * Connects to peer.
316 */
317 public void connectPeer() {
318 scheduleConnectionRetry(this.connectRetryTime);
319 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530320
sunishvkf7c56552016-07-18 16:02:39 +0530321 /**
322 * Retry connection with exponential back-off mechanism.
323 *
324 * @param retryDelay retry delay
325 */
326 private void scheduleConnectionRetry(long retryDelay) {
327 if (this.connectExecutor == null) {
328 this.connectExecutor = Executors.newSingleThreadScheduledExecutor();
329 }
330 this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES);
331 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530332
sunishvkf7c56552016-07-18 16:02:39 +0530333 /**
sangyun-han53ee58a2017-01-05 16:34:02 +0900334 * Implements OSPF connection and manages connection to peer with back-off mechanism in case of failure.
sunishvkf7c56552016-07-18 16:02:39 +0530335 */
336 class ConnectionRetry implements Runnable {
337 @Override
338 public void run() {
339 log.debug("Connect to peer {}", OspfUtil.SHOST);
340 initConnection();
341 ospfChannelHandler.sentConfigPacket(configPacket);
sangyun-han53ee58a2017-01-05 16:34:02 +0900342 InetSocketAddress connectToSocket = new InetSocketAddress(OspfUtil.SHOST, ospfPort.toInt());
sunishvkf7c56552016-07-18 16:02:39 +0530343 try {
344 peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() {
345 @Override
346 public void operationComplete(ChannelFuture future) throws Exception {
347 if (!future.isSuccess()) {
348 connectRetryCounter++;
349 log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter,
350 OspfUtil.SHOST);
351 /*
352 * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4
353 * mins.
354 */
355 if (connectRetryTime < RETRY_INTERVAL) {
356 connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530357 }
sunishvkf7c56552016-07-18 16:02:39 +0530358 scheduleConnectionRetry(connectRetryTime);
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530359 } else {
sunishvkf7c56552016-07-18 16:02:39 +0530360 //Send the config packet
361 ospfChannelHandler.sentConfigPacket(configPacket);
362 connectRetryCounter++;
363 log.info("Connected to remote host {}, Connect Counter {}", OspfUtil.SHOST,
364 connectRetryCounter);
365 disconnectExecutor();
366
367 return;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530368 }
369 }
sunishvkf7c56552016-07-18 16:02:39 +0530370 });
371 } catch (Exception e) {
372 log.info("Connect peer exception : " + e.toString());
373 disconnectExecutor();
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530374 }
375 }
376 }
377}