blob: 7cd7785a14ec9a2ff0af14632fa0dc9b2eee2de4 [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();
sunish vk4b5ce002016-05-09 20:18:35 +0530115 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530116 } else {
117 isisChannelHandler.updateInterfaceMap(isisProcesses);
sunish vk4b5ce002016-05-09 20:18:35 +0530118 //Send the config packet
119 isisChannelHandler.sentConfigPacket(configPacket);
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530120 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530121 }
122
123 /**
124 * Initializes the netty client channel connection.
125 */
126 private void initConnection() {
127 if (peerBootstrap != null) {
128 return;
129 }
130 peerBootstrap = createPeerBootStrap();
131
132 peerBootstrap.setOption("reuseAddress", true);
133 peerBootstrap.setOption("tcpNoDelay", true);
134 peerBootstrap.setOption("keepAlive", true);
135 peerBootstrap.setOption("receiveBufferSize", Controller.BUFFER_SIZE);
136 peerBootstrap.setOption("receiveBufferSizePredictorFactory",
137 new FixedReceiveBufferSizePredictorFactory(
138 Controller.BUFFER_SIZE));
139 peerBootstrap.setOption("receiveBufferSizePredictor",
140 new AdaptiveReceiveBufferSizePredictor(64, 1024, 65536));
141 peerBootstrap.setOption("child.keepAlive", true);
142 peerBootstrap.setOption("child.tcpNoDelay", true);
143 peerBootstrap.setOption("child.sendBufferSize", Controller.BUFFER_SIZE);
144 peerBootstrap.setOption("child.receiveBufferSize", Controller.BUFFER_SIZE);
145 peerBootstrap.setOption("child.receiveBufferSizePredictorFactory",
146 new FixedReceiveBufferSizePredictorFactory(
147 Controller.BUFFER_SIZE));
148 peerBootstrap.setOption("child.reuseAddress", true);
149
150 isisChannelHandler = new IsisChannelHandler(this, processes);
151 ChannelPipelineFactory pfact = new IsisPipelineFactory(isisChannelHandler);
152 peerBootstrap.setPipelineFactory(pfact);
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530153 }
154
155 /**
156 * Creates peer boot strap.
157 *
158 * @return client bootstrap instance
159 */
160 private ClientBootstrap createPeerBootStrap() {
161
162 if (peerWorkerThreads == 0) {
163 peerExecFactory = new NioClientSocketChannelFactory(
164 Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
165 Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")));
166 return new ClientBootstrap(peerExecFactory);
167 } else {
168 peerExecFactory = new NioClientSocketChannelFactory(
169 Executors.newCachedThreadPool(groupedThreads("onos/isis", "boss-%d")),
170 Executors.newCachedThreadPool(groupedThreads("onos/isis", "worker-%d")),
171 peerWorkerThreads);
172 return new ClientBootstrap(peerExecFactory);
173 }
174 }
175
176 /**
177 * Gets all configured processes.
178 *
179 * @return all configured processes
180 */
181 public List<IsisProcess> getAllConfiguredProcesses() {
182 return processes;
183 }
184
185 /**
186 * Gets the list of processes configured.
187 *
188 * @param json posted json
189 * @return list of processes configured
190 */
191 private List<IsisProcess> getConfig(JsonNode json) throws Exception {
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530192 List<IsisProcess> isisProcessesList = new ArrayList<>();
193 JsonNode jsonNodes = json;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530194 if (jsonNodes == null) {
195 return isisProcessesList;
196 }
197 jsonNodes.forEach(jsonNode -> {
198 List<IsisInterface> interfaceList = new ArrayList<>();
199 for (JsonNode jsonNode1 : jsonNode.path(IsisConstants.INTERFACE)) {
200 IsisInterface isisInterface = new DefaultIsisInterface();
sunish vk4b5ce002016-05-09 20:18:35 +0530201 String index = jsonNode1.path(IsisConstants.INTERFACEINDEX).asText();
202 if (isPrimitive(index)) {
203 int input = Integer.parseInt(index);
204 if (input < 1 || input > 255) {
205 log.debug("Wrong interface index: {}", index);
206 continue;
207 }
208 isisInterface.setInterfaceIndex(Integer.parseInt(index));
209 } else {
210 log.debug("Wrong interface index {}", index);
211 continue;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530212 }
sunish vk4b5ce002016-05-09 20:18:35 +0530213 Ip4Address ipAddress = getInterfaceIp(isisInterface.interfaceIndex());
214 if (ipAddress != null && !ipAddress.equals(IsisConstants.DEFAULTIP)) {
215 isisInterface.setInterfaceIpAddress(ipAddress);
216 } else {
217 log.debug("Wrong interface index {}. No matching interface in system.", index);
218 continue;
219 }
220 MacAddress macAddress = getInterfaceMac(isisInterface.interfaceIndex());
221 if (macAddress != null) {
222 isisInterface.setInterfaceMacAddress(macAddress);
223 } else {
224 log.debug("Wrong interface index {}. No matching interface in system.", index);
225 continue;
226 }
227 String mask = getInterfaceMask(isisInterface.interfaceIndex());
228 if (mask != null) {
229 try {
230 isisInterface.setNetworkMask(InetAddress.getByName(mask).getAddress());
231 } catch (UnknownHostException e) {
232 log.debug("Wrong interface index {}. Error while getting network mask.", index);
233 }
234 } else {
235 log.debug("Wrong interface index {}. Error while getting network mask.", index);
236 continue;
237 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530238 isisInterface.setIntermediateSystemName(jsonNode1
239 .path(IsisConstants.INTERMEDIATESYSTEMNAME)
240 .asText());
sunish vk4b5ce002016-05-09 20:18:35 +0530241 String systemId = jsonNode1.path(IsisConstants.SYSTEMID).asText();
242 if (isValidSystemId(systemId)) {
243 isisInterface.setSystemId(systemId);
244 } else {
245 log.debug("Wrong systemId: {} for interface index {}.", systemId, index);
246 continue;
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530247 }
sunish vk4b5ce002016-05-09 20:18:35 +0530248 String circuitType = jsonNode1.path(IsisConstants.RESERVEDPACKETCIRCUITTYPE).asText();
249 if (isPrimitive(circuitType)) {
250 int input = Integer.parseInt(circuitType);
251 if (input < 1 || input > 3) {
252 log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
253 continue;
254 }
255 isisInterface.setReservedPacketCircuitType(input);
256 } else {
257 log.debug("Wrong ReservedPacketCircuitType: {} for interface index {}.", circuitType, index);
258 continue;
259 }
260 String networkType = jsonNode1.path(IsisConstants.NETWORKTYPE).asText();
261 if (isPrimitive(networkType)) {
262 int input = Integer.parseInt(networkType);
263 if (input < 1 || input > 2) {
264 log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
265 continue;
266 }
267 isisInterface.setNetworkType(IsisNetworkType.get(input));
268 } else {
269 log.debug("Wrong networkType: {} for interface index {}.", networkType, index);
270 continue;
271 }
272 String areaAddress = jsonNode1.path(IsisConstants.AREAADDRESS).asText();
273 if (isPrimitive(areaAddress)) {
274 if (areaAddress.length() > 7) {
275 log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
276 continue;
277 }
278 isisInterface.setAreaAddress(areaAddress);
279 } else {
280 log.debug("Wrong areaAddress: {} for interface index {}.", areaAddress, index);
281 continue;
282 }
283 String circuitId = jsonNode1.path(IsisConstants.CIRCUITID).asText();
284 if (isPrimitive(circuitId)) {
285 int input = Integer.parseInt(circuitId);
286 if (input < 1) {
287 log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
288 continue;
289 }
290 isisInterface.setCircuitId(circuitId);
291 } else {
292 log.debug("Wrong circuitId: {} for interface index {}.", circuitId, index);
293 continue;
294 }
295 String holdingTime = jsonNode1.path(IsisConstants.HOLDINGTIME).asText();
296 if (isPrimitive(holdingTime)) {
297 int input = Integer.parseInt(holdingTime);
298 if (input < 1 || input > 255) {
299 log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
300 continue;
301 }
302 isisInterface.setHoldingTime(input);
303 } else {
304 log.debug("Wrong holdingTime: {} for interface index {}.", holdingTime, index);
305 continue;
306 }
307 String helloInterval = jsonNode1.path(IsisConstants.HELLOINTERVAL).asText();
308 if (isPrimitive(helloInterval)) {
309 int interval = Integer.parseInt(helloInterval);
310 if (interval > 0 && interval <= 255) {
311 isisInterface.setHelloInterval(interval);
312 } else {
313 log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
314 continue;
315 }
316 } else {
317 log.debug("Wrong hello interval: {} for interface index {}.", helloInterval, index);
318 continue;
319 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530320 interfaceList.add(isisInterface);
321 }
sunish vk4b5ce002016-05-09 20:18:35 +0530322 if (interfaceList.size() > 0) {
323 IsisProcess process = new DefaultIsisProcess();
324 process.setProcessId(jsonNode.path(IsisConstants.PROCESSESID).asText());
325 process.setIsisInterfaceList(interfaceList);
326 isisProcessesList.add(process);
327 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530328 });
329
330 return isisProcessesList;
331 }
sunish vk4b5ce002016-05-09 20:18:35 +0530332
333 /**
334 * Returns interface MAC by index.
335 *
336 * @param interfaceIndex interface index
337 * @return interface IP by index
338 */
339 private MacAddress getInterfaceMac(int interfaceIndex) {
340 MacAddress macAddress = null;
341 try {
342 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
343 macAddress = MacAddress.valueOf(networkInterface.getHardwareAddress());
344 } catch (Exception e) {
345 log.debug("Error while getting Interface IP by index");
346 return macAddress;
347 }
348
349 return macAddress;
350 }
351
352 /**
353 * Returns interface IP by index.
354 *
355 * @param interfaceIndex interface index
356 * @return interface IP by index
357 */
358 private Ip4Address getInterfaceIp(int interfaceIndex) {
359 Ip4Address ipAddress = null;
360 try {
361 NetworkInterface networkInterface = NetworkInterface.getByIndex(interfaceIndex);
362 Enumeration ipAddresses = networkInterface.getInetAddresses();
363 while (ipAddresses.hasMoreElements()) {
364 InetAddress address = (InetAddress) ipAddresses.nextElement();
365 if (!address.isLinkLocalAddress()) {
366 ipAddress = Ip4Address.valueOf(address.getAddress());
367 break;
368 }
369 }
370 } catch (Exception e) {
371 log.debug("Error while getting Interface IP by index");
372 return IsisConstants.DEFAULTIP;
373 }
374 return ipAddress;
375 }
376
377 /**
378 * Returns interface MAC by index.
379 *
380 * @param interfaceIndex interface index
381 * @return interface IP by index
382 */
383 private String getInterfaceMask(int interfaceIndex) {
384 String subnetMask = null;
385 try {
386 Ip4Address ipAddress = getInterfaceIp(interfaceIndex);
387 NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
388 InetAddress.getByName(ipAddress.toString()));
389 Enumeration ipAddresses = networkInterface.getInetAddresses();
390 int index = 0;
391 while (ipAddresses.hasMoreElements()) {
392 InetAddress address = (InetAddress) ipAddresses.nextElement();
393 if (!address.isLinkLocalAddress()) {
394 break;
395 }
396 index++;
397 }
398 int prfLen = networkInterface.getInterfaceAddresses().get(index).getNetworkPrefixLength();
399 int shft = 0xffffffff << (32 - prfLen);
400 int oct1 = ((byte) ((shft & 0xff000000) >> 24)) & 0xff;
401 int oct2 = ((byte) ((shft & 0x00ff0000) >> 16)) & 0xff;
402 int oct3 = ((byte) ((shft & 0x0000ff00) >> 8)) & 0xff;
403 int oct4 = ((byte) (shft & 0x000000ff)) & 0xff;
404 subnetMask = oct1 + "." + oct2 + "." + oct3 + "." + oct4;
405 } catch (Exception e) {
406 log.debug("Error while getting Interface network mask by index");
407 return subnetMask;
408 }
409 return subnetMask;
410 }
411
412 /**
413 * Checks if primitive or not.
414 *
415 * @param value input value
416 * @return true if number else false
417 */
418 private boolean isPrimitive(String value) {
419 boolean status = true;
420 value = value.trim();
421 if (value.length() < 1) {
422 return false;
423 }
424 for (int i = 0; i < value.length(); i++) {
425 char c = value.charAt(i);
426 if (!Character.isDigit(c)) {
427 status = false;
428 break;
429 }
430 }
431
432 return status;
433 }
434
435 /**
436 * Checks if system id is valid or not.
437 *
438 * @param value input value
439 * @return true if valid else false
440 */
441 private boolean isValidSystemId(String value) {
442 value = value.trim();
443 boolean status = true;
444 if (value.length() != 14) {
445 return false;
446 }
447 for (int i = 0; i < value.length(); i++) {
448 char c = value.charAt(i);
449 if (!Character.isDigit(c)) {
450 if (!((i == 4 || i == 9) && c == '.')) {
451 status = false;
452 break;
453 }
454 }
455 }
456
457 return status;
458 }
459
460 /**
461 * Disconnects the executor.
462 */
463 public void disconnectExecutor() {
464 if (connectExecutor != null) {
465 connectExecutor.shutdown();
466 connectExecutor = null;
467 }
468 }
469
470 /**
471 * Connects to peer.
472 */
473 public void connectPeer() {
474 scheduleConnectionRetry(this.connectRetryTime);
475 }
476
477 /**
478 * Retry connection with exponential back-off mechanism.
479 *
480 * @param retryDelay retry delay
481 */
482 private void scheduleConnectionRetry(long retryDelay) {
483 if (this.connectExecutor == null) {
484 this.connectExecutor = Executors.newSingleThreadScheduledExecutor();
485 }
486 this.connectExecutor.schedule(new ConnectionRetry(), retryDelay, TimeUnit.MINUTES);
487 }
488
489 /**
490 * Implements ISIS connection and manages connection to peer with back-off mechanism in case of failure.
491 */
492 class ConnectionRetry implements Runnable {
493 @Override
494 public void run() {
495 log.debug("Connect to peer {}", IsisConstants.SHOST);
496 initConnection();
sunish vkc3824e82016-05-11 19:38:24 +0530497 isisChannelHandler.sentConfigPacket(configPacket);
sunish vk4b5ce002016-05-09 20:18:35 +0530498 InetSocketAddress connectToSocket = new InetSocketAddress(IsisConstants.SHOST, isisPort.toInt());
499 try {
500 peerBootstrap.connect(connectToSocket).addListener(new ChannelFutureListener() {
501 @Override
502 public void operationComplete(ChannelFuture future) throws Exception {
503 if (!future.isSuccess()) {
504 connectRetryCounter++;
505 log.error("Connection failed, ConnectRetryCounter {} remote host {}", connectRetryCounter,
506 IsisConstants.SHOST);
507 /*
508 * Reconnect to peer on failure is exponential till 4 mins, later on retry after every 4
509 * mins.
510 */
511 if (connectRetryTime < RETRY_INTERVAL) {
512 connectRetryTime = (connectRetryTime != 0) ? connectRetryTime * 2 : 1;
513 }
514 scheduleConnectionRetry(connectRetryTime);
515 } else {
sunish vkc3824e82016-05-11 19:38:24 +0530516 //Send the config packet
517 isisChannelHandler.sentConfigPacket(configPacket);
sunish vk4b5ce002016-05-09 20:18:35 +0530518 connectRetryCounter++;
519 log.info("Connected to remote host {}, Connect Counter {}", IsisConstants.SHOST,
520 connectRetryCounter);
521 disconnectExecutor();
sunish vkc3824e82016-05-11 19:38:24 +0530522
sunish vk4b5ce002016-05-09 20:18:35 +0530523 return;
524 }
525 }
526 });
527 } catch (Exception e) {
528 log.info("Connect peer exception : " + e.toString());
529 disconnectExecutor();
530 }
531 }
532 }
tejeshwer degala3fe1ed52016-04-22 17:04:01 +0530533}