blob: f16aeb411683fbde9784fce4b7ff72855ece49cc [file] [log] [blame]
tejeshwer degala3fe1ed52016-04-22 17:04:01 +05301/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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.isis.controller.impl;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import org.jboss.netty.bootstrap.ClientBootstrap;
20import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor;
21import org.jboss.netty.channel.ChannelFuture;
sunish vk4b5ce002016-05-09 20:18:35 +053022import org.jboss.netty.channel.ChannelFutureListener;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053023import org.jboss.netty.channel.ChannelPipelineFactory;
24import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory;
25import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
26import org.onlab.packet.Ip4Address;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.TpPort;
29import org.onosproject.isis.controller.IsisInterface;
30import org.onosproject.isis.controller.IsisNetworkType;
31import org.onosproject.isis.controller.IsisProcess;
32import org.onosproject.isis.controller.IsisRouterType;
33import org.onosproject.isis.io.util.IsisConstants;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
37import java.net.InetAddress;
38import java.net.InetSocketAddress;
sunish vk4b5ce002016-05-09 20:18:35 +053039import java.net.NetworkInterface;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053040import java.net.UnknownHostException;
41import java.util.ArrayList;
sunish vk4b5ce002016-05-09 20:18:35 +053042import java.util.Enumeration;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053043import java.util.List;
44import java.util.concurrent.Executors;
sunish vk4b5ce002016-05-09 20:18:35 +053045import java.util.concurrent.ScheduledExecutorService;
46import java.util.concurrent.TimeUnit;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053047
48import static org.onlab.util.Tools.groupedThreads;
49
50/**
51 * Representation of an ISIS controller.
52 */
53public class Controller {
54 protected static final int BUFFER_SIZE = 4 * 1024 * 1024;
55 private static final Logger log = LoggerFactory.getLogger(Controller.class);
sunish vk4b5ce002016-05-09 20:18:35 +053056 private static final int RETRY_INTERVAL = 4;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053057 private final int peerWorkerThreads = 16;
sunish vk4b5ce002016-05-09 20:18:35 +053058 byte[] configPacket = null;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053059 private List<IsisProcess> processes = null;
60 private IsisChannelHandler isisChannelHandler;
61 private NioClientSocketChannelFactory peerExecFactory;
62 private ClientBootstrap peerBootstrap = null;
63 private TpPort isisPort = TpPort.tpPort(IsisConstants.SPORT);
sunish vk4b5ce002016-05-09 20:18:35 +053064 private ScheduledExecutorService connectExecutor = null;
65 private int connectRetryCounter = 0;
66 private int connectRetryTime;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053067
68 /**
69 * Deactivates ISIS controller.
70 */
71 public void isisDeactivate() {
72 peerExecFactory.shutdown();
73 }
74
75 /**
76 * Updates the processes configuration.
77 *
78 * @param jsonNode json node instance
79 * @throws Exception might throws parse exception
80 */
81 public void updateConfig(JsonNode jsonNode) throws Exception {
82 log.debug("Controller::UpdateConfig called");
sunish vk4b5ce002016-05-09 20:18:35 +053083 configPacket = new byte[IsisConstants.CONFIG_LENGTH];
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053084 byte numberOfInterface = 0; // number of interfaces to configure
85
86 configPacket[0] = (byte) 0xFF; // its a conf packet - identifier
87 List<IsisProcess> isisProcesses = getConfig(jsonNode);
tejeshwer degala3fe1ed52016-04-22 17:04:01 +053088 for (IsisProcess isisProcess : isisProcesses) {
89 log.debug("IsisProcessDetails : " + isisProcess);
90 for (IsisInterface isisInterface : isisProcess.isisInterfaceList()) {
91 DefaultIsisInterface isisInterfaceImpl = (DefaultIsisInterface) isisInterface;
92 log.debug("IsisInterfaceDetails : " + isisInterface);
93 numberOfInterface++;
94 configPacket[2 * numberOfInterface] = (byte) isisInterfaceImpl.interfaceIndex();
95 if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
96 isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L1.value()) {
97 configPacket[(2 * numberOfInterface) + 1] = (byte) 0;
98 } else if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
99 isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L2.value()) {
100 configPacket[(2 * numberOfInterface) + 1] = (byte) 1;
101 } else if (isisInterface.networkType() == IsisNetworkType.P2P) {
102 configPacket[(2 * numberOfInterface) + 1] = (byte) 2;
103 } else if (isisInterface.networkType() == IsisNetworkType.BROADCAST &&
104 isisInterfaceImpl.reservedPacketCircuitType() == IsisRouterType.L1L2.value()) {
105 configPacket[(2 * numberOfInterface) + 1] = (byte) 3;
106 }
107 }
108 }
109 configPacket[1] = numberOfInterface;
110 //First time configuration
111 if (processes == null) {
sunish vk4b5ce002016-05-09 20:18:35 +0530112 if (isisProcesses.size() > 0) {
113 processes = isisProcesses;
114 connectPeer();
115 //Initializing the interface map in channel handler
116 if (isisChannelHandler != null) {
117 isisChannelHandler.initializeInterfaceMap();
118 }
119 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530120 } else {
121 isisChannelHandler.updateInterfaceMap(isisProcesses);
sunish vk4b5ce002016-05-09 20:18:35 +0530122 //Send the config packet
123 isisChannelHandler.sentConfigPacket(configPacket);
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530124 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530125 }
126
127 /**
128 * Initializes the netty client channel connection.
129 */
130 private void initConnection() {
131 if (peerBootstrap != null) {
132 return;
133 }
134 peerBootstrap = createPeerBootStrap();
135
136 peerBootstrap.setOption("reuseAddress", true);
137 peerBootstrap.setOption("tcpNoDelay", true);
138 peerBootstrap.setOption("keepAlive", true);
139 peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE);
140 peerBootstrap.setOption("receiveBufferSizePredictorFactory",
141 new FixedReceiveBufferSizePredictorFactory(
142 Controller.BUFFER_SIZE));
143 peerBootstrap.setOption("receiveBufferSizePredictor",
144 new AdaptiveReceiveBufferSizePredictor(64, 1024, 65536));
145 peerBootstrap.setOption("child.keepAlive", true);
146 peerBootstrap.setOption("child.tcpNoDelay", true);
147 peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE);
148 peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE);
149 peerBootstrap.setOption("child.receiveBufferSizePredictorFactory",
150 new FixedReceiveBufferSizePredictorFactory(
151 Controller.BUFFER_SIZE));
152 peerBootstrap.setOption("child.reuseAddress", true);
153
154 isisChannelHandler = new IsisChannelHandler(this, processes);
155 ChannelPipelineFactory pfact = new IsisPipelineFactory(isisChannelHandler);
156 peerBootstrap.setPipelineFactory(pfact);
157 ChannelFuture connection = peerBootstrap.connect(new InetSocketAddress(IsisConstants.SHOST, isisPort.toInt()));
158 }
159
160 /**
161 * Creates peer boot strap.
162 *
163 * @return client bootstrap instance
164 */
165 private ClientBootstrap createPeerBootStrap() {
166
167 if (peerWorkerThreads == 0) {
168 peerExecFactory = new NioClientSocketChannelFactory(
169 Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
170 Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")));
171 return new ClientBootstrap(peerExecFactory);
172 } else {
173 peerExecFactory = new NioClientSocketChannelFactory(
174 Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
175 Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")),
176 peerWorkerThreads);
177 return new ClientBootstrap(peerExecFactory);
178 }
179 }
180
181 /**
182 * Gets all configured processes.
183 *
184 * @return all configured processes
185 */
186 public List<IsisProcess> getAllConfiguredProcesses() {
187 return processes;
188 }
189
190 /**
191 * Gets the list of processes configured.
192 *
193 * @param json posted json
194 * @return list of processes configured
195 */
196 private List<IsisProcess> getConfig(JsonNode json) throws Exception {
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530197 List<IsisProcess> isisProcessesList = new ArrayList<>();
198 JsonNode jsonNodes = json;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530199 if (jsonNodes == null) {
200 return isisProcessesList;
201 }
202 jsonNodes.forEach(jsonNode -> {
203 List<IsisInterface> interfaceList = new ArrayList<>();
204 for (JsonNode jsonNode1 : jsonNode.path(IsisConstants.INTERFACE)) {
205 IsisInterface isisInterface = new DefaultIsisInterface();
sunish vk4b5ce002016-05-09 20:18:35 +0530206 String index = jsonNode1.path(IsisConstants.INTERFACEINDEX).asText();
207 if (isPrimitive(index)) {
208 int input = Integer.parseInt(index);
209 if (input < 1 || input > 255) {
210 log.debug("Wrong interface index: {}", index);
211 continue;
212 }
213 isisInterface.setInterfaceIndex(Integer.parseInt(index));
214 } else {
215 log.debug("Wrong interface index {}", index);
216 continue;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530217 }
sunish vk4b5ce002016-05-09 20:18:35 +0530218 Ip4Address ipAddress = getInterfaceIp(isisInterface.interfaceIndex());
219 if (ipAddress != null && !ipAddress.equals(IsisConstants.DEFAULTIP)) {
220 isisInterface.setInterfaceIpAddress(ipAddress);
221 } else {
222 log.debug("Wrong interface index {}. No matching interface in system.", index);
223 continue;
224 }
225 MacAddress macAddress = getInterfaceMac(isisInterface.interfaceIndex());
226 if (macAddress != null) {
227 isisInterface.setInterfaceMacAddress(macAddress);
228 } else {
229 log.debug("Wrong interface index {}. No matching interface in system.", index);
230 continue;
231 }
232 String mask = getInterfaceMask(isisInterface.interfaceIndex());
233 if (mask != null) {
234 try {
235 isisInterface.setNetworkMask(InetAddress.getByName(mask).getAddress());
236 } catch (UnknownHostException e) {
237 log.debug("Wrong interface index {}. Error while getting network mask.", index);
238 }
239 } else {
240 log.debug("Wrong interface index {}. Error while getting network mask.", index);
241 continue;
242 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530243 isisInterface.setIntermediateSystemName(jsonNode1
244 .path(IsisConstants.INTERMEDIATESYSTEMNAME)
245 .asText());
sunish vk4b5ce002016-05-09 20:18:35 +0530246 String systemId = jsonNode1.path(IsisConstants.SYSTEMID).asText();
247 if (isValidSystemId(systemId)) {
248 isisInterface.setSystemId(systemId);
249 } else {
250 log.debug("Wrong systemId: {} for interface index {}.", systemId, index);
251 continue;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530252 }
sunish vk4b5ce002016-05-09 20:18:35 +0530253 String circuitType = jsonNode1.path(IsisConstants.RESERVEDPACKETCIRCUITTYPE).asText();
254 if (isPrimitive(circuitType)) {
255 int input = Integer.parseInt(circuitType);
256 if (input < 1 || input > 3) {
257 log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
258 continue;
259 }
260 isisInterface.setReservedPacketCircuitType(input);
261 } else {
262 log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
263 continue;
264 }
265 String networkType = jsonNode1.path(IsisConstants.NETWORKTYPE).asText();
266 if (isPrimitive(networkType)) {
267 int input = Integer.parseInt(networkType);
268 if (input < 1 || input > 2) {
269 log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
270 continue;
271 }
272 isisInterface.setNetworkType(IsisNetworkType.get(input));
273 } else {
274 log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
275 continue;
276 }
277 String areaAddress = jsonNode1.path(IsisConstants.AREAADDRESS).asText();
278 if (isPrimitive(areaAddress)) {
279 if (areaAddress.length() > 7) {
280 log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
281 continue;
282 }
283 isisInterface.setAreaAddress(areaAddress);
284 } else {
285 log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
286 continue;
287 }
288 String circuitId = jsonNode1.path(IsisConstants.CIRCUITID).asText();
289 if (isPrimitive(circuitId)) {
290 int input = Integer.parseInt(circuitId);
291 if (input < 1) {
292 log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
293 continue;
294 }
295 isisInterface.setCircuitId(circuitId);
296 } else {
297 log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
298 continue;
299 }
300 String holdingTime = jsonNode1.path(IsisConstants.HOLDINGTIME).asText();
301 if (isPrimitive(holdingTime)) {
302 int input = Integer.parseInt(holdingTime);
303 if (input < 1 || input > 255) {
304 log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
305 continue;
306 }
307 isisInterface.setHoldingTime(input);
308 } else {
309 log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
310 continue;
311 }
312 String helloInterval = jsonNode1.path(IsisConstants.HELLOINTERVAL).asText();
313 if (isPrimitive(helloInterval)) {
314 int interval = Integer.parseInt(helloInterval);
315 if (interval > 0 && interval <= 255) {
316 isisInterface.setHelloInterval(interval);
317 } else {
318 log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
319 continue;
320 }
321 } else {
322 log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
323 continue;
324 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530325 interfaceList.add(isisInterface);
326 }
sunish vk4b5ce002016-05-09 20:18:35 +0530327 if (interfaceList.size() > 0) {
328 IsisProcess process = new DefaultIsisProcess();
329 process.setProcessId(jsonNode.path(IsisConstants.PROCESSESID).asText());
330 process.setIsisInterfaceList(interfaceList);
331 isisProcessesList.add(process);
332 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530333 });
334
335 return isisProcessesList;
336 }
sunish vk4b5ce002016-05-09 20:18:35 +0530337
338 /**
339 * Returns interface MAC by index.
340 *
341 * @param interfaceIndex interface index
342 * @return interface IP by index
343 */
344 private MacAddress getInterfaceMac(int interfaceIndex) {
345 MacAddress macAddress = null;
346 try {
347 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
348 macAddress = MacAddress.valueOf(networkInterface.getHardwareAddress());
349 } catch (Exception e) {
350 log.debug("Error while getting Interface IP by index");
351 return macAddress;
352 }
353
354 return macAddress;
355 }
356
357 /**
358 * Returns interface IP by index.
359 *
360 * @param interfaceIndex interface index
361 * @return interface IP by index
362 */
363 private Ip4Address getInterfaceIp(int interfaceIndex) {
364 Ip4Address ipAddress = null;
365 try {
366 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
367 Enumeration ipAddresses = networkInterface.getInetAddresses();
368 while (ipAddresses.hasMoreElements()) {
369 InetAddress address = (InetAddress) ipAddresses.nextElement();
370 if (!address.isLinkLocalAddress()) {
371 ipAddress = Ip4Address.valueOf(address.getAddress());
372 break;
373 }
374 }
375 } catch (Exception e) {
376 log.debug("Error while getting Interface IP by index");
377 return IsisConstants.DEFAULTIP;
378 }
379 return ipAddress;
380 }
381
382 /**
383 * Returns interface MAC by index.
384 *
385 * @param interfaceIndex interface index
386 * @return interface IP by index
387 */
388 private String getInterfaceMask(int interfaceIndex) {
389 String subnetMask = null;
390 try {
391 Ip4Address ipAddress = getInterfaceIp(interfaceIndex);
392 NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
393 InetAddress.getByName(ipAddress.toString()));
394 Enumeration ipAddresses = networkInterface.getInetAddresses();
395 int index = 0;
396 while (ipAddresses.hasMoreElements()) {
397 InetAddress address = (InetAddress) ipAddresses.nextElement();
398 if (!address.isLinkLocalAddress()) {
399 break;
400 }
401 index++;
402 }
403 int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength();
404 int shft = 0xffffffff << (32 - prfLen);
405 int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff;
406 int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff;
407 int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff;
408 int oct4 = ((byte) (shft & 0x000000ff)) & 0xff;
409 subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4;
410 } catch (Exception e) {
411 log.debug("Error while getting Interface network mask by index");
412 return subnetMask;
413 }
414 return subnetMask;
415 }
416
417 /**
418 * Checks if primitive or not.
419 *
420 * @param value input value
421 * @return true if number else false
422 */
423 private boolean isPrimitive(String value) {
424 boolean status = true;
425 value = value.trim();
426 if (value.length() < 1) {
427 return false;
428 }
429 for (int i = 0; i < value.length(); i++) {
430 char c = value.charAt(i);
431 if (!Character.isDigit(c)) {
432 status = false;
433 break;
434 }
435 }
436
437 return status;
438 }
439
440 /**
441 * Checks if system id is valid or not.
442 *
443 * @param value input value
444 * @return true if valid else false
445 */
446 private boolean isValidSystemId(String value) {
447 value = value.trim();
448 boolean status = true;
449 if (value.length() != 14) {
450 return false;
451 }
452 for (int i = 0; i < value.length(); i++) {
453 char c = value.charAt(i);
454 if (!Character.isDigit(c)) {
455 if (!((i == 4 || i == 9) && c == '.')) {
456 status = false;
457 break;
458 }
459 }
460 }
461
462 return status;
463 }
464
465 /**
466 * Disconnects the executor.
467 */
468 public void disconnectExecutor() {
469 if (connectExecutor != null) {
470 connectExecutor.shutdown();
471 connectExecutor = null;
472 }
473 }
474
475 /**
476 * Connects to peer.
477 */
478 public void connectPeer() {
479 scheduleConnectionRetry(this.connectRetryTime);
480 }
481
482 /**
483 * Retry connection with exponential back-off mechanism.
484 *
485 * @param retryDelay retry delay
486 */
487 private void scheduleConnectionRetry(long retryDelay) {
488 if (this.connectExecutor == null) {
489 this.connectExecutor = Executors.newSingleThreadScheduledExecutor();
490 }
491 this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES);
492 }
493
494 /**
495 * Implements ISIS connection and manages connection to peer with back-off mechanism in case of failure.
496 */
497 class ConnectionRetry implements Runnable {
498 @Override
499 public void run() {
500 log.debug("Connect to peer {}", IsisConstants.SHOST);
501 initConnection();
502 InetSocketAddress connectToSocket = new InetSocketAddress(IsisConstants.SHOST, isisPort.toInt());
503 try {
504 peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() {
505 @Override
506 public void operationComplete(ChannelFuture future) throws Exception {
507 if (!future.isSuccess()) {
508 connectRetryCounter++;
509 log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter,
510 IsisConstants.SHOST);
511 /*
512 * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4
513 * mins.
514 */
515 if (connectRetryTime < RETRY_INTERVAL) {
516 connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1;
517 }
518 scheduleConnectionRetry(connectRetryTime);
519 } else {
520 connectRetryCounter++;
521 log.info("Connected to remote host {}, Connect Counter {}", IsisConstants.SHOST,
522 connectRetryCounter);
523 disconnectExecutor();
524 isisChannelHandler.initializeInterfaceMap();
525 //Send the config packet
526 isisChannelHandler.sentConfigPacket(configPacket);
527 return;
528 }
529 }
530 });
531 } catch (Exception e) {
532 log.info("Connect peer exception : " + e.toString());
533 disconnectExecutor();
534 }
535 }
536 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530537}