blob: 8e8e5f31e0db6c8db28e4508ba7c30ded5b4e4c8 [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
sunishvkf7c56552016-07-18 16:02:39 +053081 */
Ray Milkey019fba42018-01-31 14:07:47 -080082 public void updateConfig(List<OspfProcess> ospfProcesses) {
sunishvkf7c56552016-07-18 16:02:39 +053083 log.debug("Controller::UpdateConfig called");
84 configPacket = new byte[OspfUtil.CONFIG_LENGTH];
85 byte numberOfInterface = 0; // number of interfaces to configure
86 configPacket[0] = (byte) 0xFF; // its a conf packet - identifier
87 for (OspfProcess ospfProcess : ospfProcesses) {
88 log.debug("OspfProcessDetails : " + ospfProcess);
89 for (OspfArea ospfArea : ospfProcess.areas()) {
90 for (OspfInterface ospfInterface : ospfArea.ospfInterfaceList()) {
91 log.debug("OspfInterfaceDetails : " + ospfInterface);
92 numberOfInterface++;
93 configPacket[2 * numberOfInterface] = (byte) ospfInterface.interfaceIndex();
94 configPacket[(2 * numberOfInterface) + 1] = (byte) 4;
95 }
96 }
97 }
98 configPacket[1] = numberOfInterface;
99 //First time configuration
100 if (processes == null) {
101 if (ospfProcesses.size() > 0) {
102 processes = ospfProcesses;
103 connectPeer();
104 }
105 } else {
106 ospfChannelHandler.updateInterfaceMap(ospfProcesses);
107 //Send the config packet
108 ospfChannelHandler.sentConfigPacket(configPacket);
109 }
110 }
111
112 /**
113 * Initializes the netty client channel connection.
114 */
115 private void initConnection() {
116 if (peerBootstrap != null) {
117 return;
118 }
119 peerBootstrap = createPeerBootStrap();
120
121 peerBootstrap.setOption("reuseAddress", true);
122 peerBootstrap.setOption("tcpNoDelay", true);
123 peerBootstrap.setOption("keepAlive", true);
124 peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE);
125 peerBootstrap.setOption("receiveBufferSizePredictorFactory",
126 new FixedReceiveBufferSizePredictorFactory(
127 Controller.BUFFER_SIZE));
128 peerBootstrap.setOption("receiveBufferSizePredictor",
129 new AdaptiveReceiveBufferSizePredictor(64, 4096, 65536));
130 peerBootstrap.setOption("child.keepAlive", true);
131 peerBootstrap.setOption("child.tcpNoDelay", true);
132 peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE);
133 peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE);
134 peerBootstrap.setOption("child.receiveBufferSizePredictorFactory",
135 new FixedReceiveBufferSizePredictorFactory(
136 Controller.BUFFER_SIZE));
137 peerBootstrap.setOption("child.reuseAddress", true);
138
139 ospfChannelHandler = new OspfInterfaceChannelHandler(this, processes);
140 ChannelPipelineFactory pfact = new OspfPipelineFactory(ospfChannelHandler);
141 peerBootstrap.setPipelineFactory(pfact);
142 }
143
144 /**
145 * Creates peer boot strap.
146 *
147 * @return client bootstrap instance
148 */
149 private ClientBootstrap createPeerBootStrap() {
150
151 if (peerWorkerThreads == 0) {
152 peerExecFactory = new NioClientSocketChannelFactory(
sangyun-han53ee58a2017-01-05 16:34:02 +0900153 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")),
154 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d")));
sunishvkf7c56552016-07-18 16:02:39 +0530155 return new ClientBootstrap(peerExecFactory);
156 } else {
157 peerExecFactory = new NioClientSocketChannelFactory(
sangyun-han53ee58a2017-01-05 16:34:02 +0900158 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "boss-%d")),
159 Executors.newCachedThreadPool(groupedThreads("onos/ospf", "worker-%d")),
sunishvkf7c56552016-07-18 16:02:39 +0530160 peerWorkerThreads);
161 return new ClientBootstrap(peerExecFactory);
162 }
163 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530164
165 /**
166 * Gets all configured processes.
167 *
168 * @return all configured processes
169 */
170 public List<OspfProcess> getAllConfiguredProcesses() {
171 return processes;
172 }
173
174 /**
175 * Adds device details.
176 *
177 * @param ospfRouter OSPF router instance
178 */
179 public void addDeviceDetails(OspfRouter ospfRouter) {
180 agent.addConnectedRouter(ospfRouter);
181 }
182
183 /**
184 * Removes device details.
185 *
186 * @param ospfRouter OSPF router instance
187 */
188 public void removeDeviceDetails(OspfRouter ospfRouter) {
189 agent.removeConnectedRouter(ospfRouter);
190 }
191
192 /**
193 * Adds link details.
194 *
195 * @param ospfRouter OSPF router instance
196 * @param ospfLinkTed OSPF link ted instance
197 */
198 public void addLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
199 agent.addLink(ospfRouter, ospfLinkTed);
200 }
201
202 /**
203 * Removes link details.
204 *
sunishvkf7c56552016-07-18 16:02:39 +0530205 * @param ospfRouter OSPF router instance
206 * @param ospfLinkTed OSPF link ted instance
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530207 */
sunishvkf7c56552016-07-18 16:02:39 +0530208 public void removeLinkDetails(OspfRouter ospfRouter, OspfLinkTed ospfLinkTed) {
209 agent.deleteLink(ospfRouter, ospfLinkTed);
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530210 }
211
212 /**
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530213 * Initializes internal data structures.
214 */
215 public void init() {
216 this.systemStartTime = System.currentTimeMillis();
217 }
218
219 /**
220 * Starts the controller.
221 *
222 * @param ag OSPF agent instance
223 * @param driverService driver service instance
224 */
225 public void start(OspfAgent ag, DriverService driverService) {
sunishvkf7c56552016-07-18 16:02:39 +0530226 log.info("Starting OSPF controller...!!!");
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530227 this.agent = ag;
228 this.driverService = driverService;
229 this.init();
230 }
231
232 /**
233 * Stops the Controller.
234 */
235 public void stop() {
sunishvkf7c56552016-07-18 16:02:39 +0530236 log.info("Stopping OSPF controller...!!!");
sangyun-han53ee58a2017-01-05 16:34:02 +0900237 ospfDeactivate();
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530238 processes.clear();
239 }
240
241 /**
sunishvkf7c56552016-07-18 16:02:39 +0530242 * Returns interface IP by index.
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530243 *
sunishvkf7c56552016-07-18 16:02:39 +0530244 * @param interfaceIndex interface index
245 * @return interface IP by index
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530246 */
sunishvkf7c56552016-07-18 16:02:39 +0530247 private Ip4Address getInterfaceIp(int interfaceIndex) {
248 Ip4Address ipAddress = null;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530249 try {
sunishvkf7c56552016-07-18 16:02:39 +0530250 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
251 Enumeration ipAddresses = networkInterface.getInetAddresses();
252 while (ipAddresses.hasMoreElements()) {
253 InetAddress address = (InetAddress) ipAddresses.nextElement();
254 if (!address.isLinkLocalAddress()) {
255 ipAddress = Ip4Address.valueOf(address.getAddress());
256 break;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530257 }
258 }
sunishvkf7c56552016-07-18 16:02:39 +0530259 } catch (Exception e) {
260 log.debug("Error while getting Interface IP by index");
261 return OspfUtil.DEFAULTIP;
262 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530263
sunishvkf7c56552016-07-18 16:02:39 +0530264 return ipAddress;
265 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530266
sunishvkf7c56552016-07-18 16:02:39 +0530267 /**
268 * Returns interface mask by index.
269 *
270 * @param interfaceIndex interface index
271 * @return interface IP by index
272 */
273 private String getInterfaceMask(int interfaceIndex) {
274 String subnetMask = null;
275 try {
276 Ip4Address ipAddress = getInterfaceIp(interfaceIndex);
277 NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
278 InetAddress.getByName(ipAddress.toString()));
279 Enumeration ipAddresses = networkInterface.getInetAddresses();
280 int index = 0;
281 while (ipAddresses.hasMoreElements()) {
282 InetAddress address = (InetAddress) ipAddresses.nextElement();
283 if (!address.isLinkLocalAddress()) {
284 break;
285 }
286 index++;
287 }
288 int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength();
289 int shft = 0xffffffff << (32 - prfLen);
290 int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff;
291 int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff;
292 int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff;
293 int oct4 = ((byte) (shft & 0x000000ff)) & 0xff;
294 subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4;
295 } catch (Exception e) {
296 log.debug("Error while getting Interface network mask by index");
297 return subnetMask;
298 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530299
sunishvkf7c56552016-07-18 16:02:39 +0530300 return subnetMask;
301 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530302
sunishvkf7c56552016-07-18 16:02:39 +0530303 /**
304 * Disconnects the executor.
305 */
306 public void disconnectExecutor() {
307 if (connectExecutor != null) {
308 connectExecutor.shutdown();
309 connectExecutor = null;
310 }
311 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530312
sunishvkf7c56552016-07-18 16:02:39 +0530313 /**
314 * Connects to peer.
315 */
316 public void connectPeer() {
317 scheduleConnectionRetry(this.connectRetryTime);
318 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530319
sunishvkf7c56552016-07-18 16:02:39 +0530320 /**
321 * Retry connection with exponential back-off mechanism.
322 *
323 * @param retryDelay retry delay
324 */
325 private void scheduleConnectionRetry(long retryDelay) {
326 if (this.connectExecutor == null) {
327 this.connectExecutor = Executors.newSingleThreadScheduledExecutor();
328 }
329 this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES);
330 }
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530331
sunishvkf7c56552016-07-18 16:02:39 +0530332 /**
sangyun-han53ee58a2017-01-05 16:34:02 +0900333 * Implements OSPF connection and manages connection to peer with back-off mechanism in case of failure.
sunishvkf7c56552016-07-18 16:02:39 +0530334 */
335 class ConnectionRetry implements Runnable {
336 @Override
337 public void run() {
338 log.debug("Connect to peer {}", OspfUtil.SHOST);
339 initConnection();
340 ospfChannelHandler.sentConfigPacket(configPacket);
sangyun-han53ee58a2017-01-05 16:34:02 +0900341 InetSocketAddress connectToSocket = new InetSocketAddress(OspfUtil.SHOST, ospfPort.toInt());
sunishvkf7c56552016-07-18 16:02:39 +0530342 try {
343 peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() {
344 @Override
Ray Milkey019fba42018-01-31 14:07:47 -0800345 public void operationComplete(ChannelFuture future) {
sunishvkf7c56552016-07-18 16:02:39 +0530346 if (!future.isSuccess()) {
347 connectRetryCounter++;
348 log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter,
349 OspfUtil.SHOST);
350 /*
351 * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4
352 * mins.
353 */
354 if (connectRetryTime < RETRY_INTERVAL) {
355 connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530356 }
sunishvkf7c56552016-07-18 16:02:39 +0530357 scheduleConnectionRetry(connectRetryTime);
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530358 } else {
sunishvkf7c56552016-07-18 16:02:39 +0530359 //Send the config packet
360 ospfChannelHandler.sentConfigPacket(configPacket);
361 connectRetryCounter++;
362 log.info("Connected to remote host {}, Connect Counter {}", OspfUtil.SHOST,
363 connectRetryCounter);
364 disconnectExecutor();
365
366 return;
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530367 }
368 }
sunishvkf7c56552016-07-18 16:02:39 +0530369 });
370 } catch (Exception e) {
371 log.info("Connect peer exception : " + e.toString());
372 disconnectExecutor();
Kalyankumar Asangi27728f22016-02-17 15:46:28 +0530373 }
374 }
375 }
376}