3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.forwarding.staticrouting.internal;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.ObjectInputStream;
15 import java.net.Inet4Address;
16 import java.net.InetAddress;
17 import java.nio.ByteBuffer;
18 import java.util.Collections;
19 import java.util.Dictionary;
20 import java.util.EnumSet;
21 import java.util.HashSet;
24 import java.util.Timer;
25 import java.util.TimerTask;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
35 import org.apache.felix.dm.Component;
36 import org.opendaylight.controller.clustering.services.CacheConfigException;
37 import org.opendaylight.controller.clustering.services.CacheExistException;
38 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
39 import org.opendaylight.controller.clustering.services.IClusterServices;
40 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
41 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
42 import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
43 import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
44 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
45 import org.opendaylight.controller.hosttracker.IfIptoHost;
46 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
47 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
48 import org.opendaylight.controller.sal.utils.GlobalConstants;
49 import org.opendaylight.controller.sal.utils.IObjectReader;
50 import org.opendaylight.controller.sal.utils.ObjectReader;
51 import org.opendaylight.controller.sal.utils.ObjectWriter;
52 import org.opendaylight.controller.sal.utils.Status;
53 import org.opendaylight.controller.sal.utils.StatusCode;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * Static Routing feature provides the bridge between SDN and Non-SDN networks.
60 public class StaticRoutingImplementation implements IfNewHostNotify,
61 IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware {
62 private static Logger log = LoggerFactory
63 .getLogger(StaticRoutingImplementation.class);
64 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
65 ConcurrentMap<String, StaticRoute> staticRoutes;
66 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
67 private IfIptoHost hostTracker;
68 private Timer gatewayProbeTimer;
69 private String staticRoutesFileName = null;
70 private IClusterContainerServices clusterContainerService = null;
71 private Set<IStaticRoutingAware> staticRoutingAware = Collections
72 .synchronizedSet(new HashSet<IStaticRoutingAware>());
73 private ExecutorService executor;
75 void setStaticRoutingAware(IStaticRoutingAware s) {
76 if (this.staticRoutingAware != null) {
77 this.staticRoutingAware.add(s);
81 void unsetStaticRoutingAware(IStaticRoutingAware s) {
82 if (this.staticRoutingAware != null) {
83 this.staticRoutingAware.remove(s);
87 public void setHostTracker(IfIptoHost hostTracker) {
88 log.debug("Setting HostTracker");
89 this.hostTracker = hostTracker;
92 public void unsetHostTracker(IfIptoHost hostTracker) {
93 if (this.hostTracker == hostTracker) {
94 this.hostTracker = null;
99 public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
100 return staticRouteConfigs;
104 public Object readObject(ObjectInputStream ois)
105 throws FileNotFoundException, IOException, ClassNotFoundException {
106 // Perform the class deserialization locally, from inside the package
107 // where the class is defined
108 return ois.readObject();
111 @SuppressWarnings("unchecked")
112 private void loadConfiguration() {
113 ObjectReader objReader = new ObjectReader();
114 ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader
115 .read(this, staticRoutesFileName);
117 if (confList == null) {
121 for (StaticRouteConfig conf : confList.values()) {
122 addStaticRoute(conf);
127 private Status saveConfig() {
128 return saveConfigInternal();
131 public Status saveConfigInternal() {
133 ObjectWriter objWriter = new ObjectWriter();
135 status = objWriter.write(
136 new ConcurrentHashMap<String, StaticRouteConfig>(
137 staticRouteConfigs), staticRoutesFileName);
139 if (status.isSuccess()) {
142 return new Status(StatusCode.INTERNALERROR, "Save failed");
146 @SuppressWarnings("deprecation")
147 private void allocateCaches() {
148 if (this.clusterContainerService == null) {
150 .info("un-initialized clusterContainerService, can't create cache");
155 clusterContainerService.createCache(
156 "forwarding.staticrouting.routes", EnumSet
157 .of(IClusterServices.cacheMode.TRANSACTIONAL));
158 clusterContainerService.createCache(
159 "forwarding.staticrouting.configs", EnumSet
160 .of(IClusterServices.cacheMode.TRANSACTIONAL));
161 } catch (CacheExistException cee) {
163 .error("\nCache already exists - destroy and recreate if needed");
164 } catch (CacheConfigException cce) {
165 log.error("\nCache configuration invalid - check cache mode");
169 @SuppressWarnings({ "unchecked", "deprecation" })
170 private void retrieveCaches() {
171 if (this.clusterContainerService == null) {
173 .info("un-initialized clusterContainerService, can't retrieve cache");
177 staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
178 .getCache("forwarding.staticrouting.routes");
179 if (staticRoutes == null) {
180 log.error("\nFailed to get rulesDB handle");
183 staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
184 .getCache("forwarding.staticrouting.configs");
185 if (staticRouteConfigs == null) {
186 log.error("\nFailed to get rulesDB handle");
190 private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
191 if (this.staticRoutingAware != null) {
192 log.info("Invoking StaticRoutingAware listeners");
193 synchronized (this.staticRoutingAware) {
194 for (IStaticRoutingAware ra : this.staticRoutingAware) {
196 ra.staticRouteUpdate(s, update);
197 } catch (Exception e) {
205 private class NotifyStaticRouteWorker implements Callable<Object> {
208 private StaticRoute staticRoute;
209 private boolean added;
211 public NotifyStaticRouteWorker(String name, StaticRoute s, boolean update) {
213 this.staticRoute = s;
218 public Object call() throws Exception {
220 || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
221 notifyStaticRouteUpdate(staticRoute, added);
223 InetAddress nh = staticRoute.getNextHopAddress();
224 HostNodeConnector host = hostTracker.hostQuery(nh);
226 log.debug("Next hop {} is not present, try to discover it", nh.getHostAddress());
227 Future<HostNodeConnector> future = hostTracker.discoverHost(nh);
228 if (future != null) {
231 } catch (InterruptedException ioe) {
232 log.trace("Thread interrupted {}", ioe);
233 } catch (Exception e) {
239 log.debug("Next hop {} is found", nh.getHostAddress());
240 staticRoute.setHost(host);
241 // static route object has changed
242 // put the changed object back in the cache
244 staticRoutes.put(name, staticRoute);
245 notifyStaticRouteUpdate(staticRoute, added);
247 log.debug("Next hop {} is still not present, try again later", nh.getHostAddress());
254 private void checkAndUpdateListeners(String name, StaticRoute staticRoute, boolean added) {
255 NotifyStaticRouteWorker worker = new NotifyStaticRouteWorker(name, staticRoute, added);
257 executor.submit(worker);
258 } catch (Exception e) {
259 log.error("got Exception ", e);
263 private void notifyHostUpdate(HostNodeConnector host, boolean added) {
267 for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
268 StaticRoute route = s.getValue();
269 if (route.getType() == StaticRoute.NextHopType.SWITCHPORT) {
272 if (route.getNextHopAddress().equals(host.getNetworkAddress())) {
278 // static route object has changed
279 // put the changed object back in the cache
281 staticRoutes.put(s.getKey(), route);
282 notifyStaticRouteUpdate(route, added);
288 public void notifyHTClient(HostNodeConnector host) {
289 notifyHostUpdate(host, true);
293 public void notifyHTClientHostRemoved(HostNodeConnector host) {
294 notifyHostUpdate(host, false);
297 public boolean isIPv4AddressValid(String cidr) {
302 String values[] = cidr.split("/");
303 Pattern ipv4Pattern = Pattern
304 .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
305 Matcher mm = ipv4Pattern.matcher(values[0]);
307 log.debug("IPv4 source address {} is not valid", cidr);
310 if (values.length >= 2) {
311 int prefix = Integer.valueOf(values[1]);
312 if ((prefix < 0) || (prefix > 32)) {
313 log.debug("prefix {} is not valid", prefix);
320 public static short getUnsignedByte(ByteBuffer bb, int position) {
321 return ((short) (bb.get(position) & (short) 0xff));
324 public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
325 for (int i = 0; i < buf1.array().length; i++) {
326 if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
328 } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
337 public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
338 ByteBuffer bblongestPrefix = null;
340 bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0")
342 } catch (Exception e) {
346 if (staticRoutes == null) {
350 StaticRoute longestPrefixRoute = null;
351 for (StaticRoute s : staticRoutes.values()) {
352 InetAddress prefix = s.longestPrefixMatch(ipAddress);
353 if ((prefix != null) && (prefix instanceof Inet4Address)) {
354 ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
355 if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
356 bblongestPrefix = bbtmp;
357 longestPrefixRoute = s;
361 return longestPrefixRoute;
365 public Status addStaticRoute(StaticRouteConfig config) {
366 Status status = config.isValid();
367 if (!status.isSuccess()) {
370 if (staticRouteConfigs.get(config.getName()) != null) {
371 return new Status(StatusCode.CONFLICT,
372 "A valid Static Route configuration with this name " +
373 "already exists. Please use a different name");
377 StaticRoute sRoute = new StaticRoute(config);
379 for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
380 if (entry.getValue().compareTo(sRoute) == 0) {
381 return new Status(StatusCode.CONFLICT,
382 "This conflicts with an existing Static Route " +
383 "Configuration. Please check the configuration " +
387 staticRoutes.put(config.getName(), sRoute);
389 // Update config databse
390 staticRouteConfigs.put(config.getName(), config);
393 checkAndUpdateListeners(config.getName(), sRoute, true);
398 public Status removeStaticRoute(String name) {
399 staticRouteConfigs.remove(name);
400 StaticRoute sRoute = staticRoutes.remove(name);
401 if (sRoute != null) {
402 checkAndUpdateListeners(name, sRoute, false);
403 return new Status(StatusCode.SUCCESS, null);
405 return new Status(StatusCode.NOTFOUND,
406 "Static Route with name " + name + " is not found");
409 void setClusterContainerService(IClusterContainerServices s) {
410 log.debug("Cluster Service set");
411 this.clusterContainerService = s;
414 void unsetClusterContainerService(IClusterContainerServices s) {
415 if (this.clusterContainerService == s) {
416 log.debug("Cluster Service removed!");
417 this.clusterContainerService = null;
422 * Function called by the dependency manager when all the required
423 * dependencies are satisfied
426 void init(Component c) {
427 String containerName = null;
428 Dictionary props = c.getServiceProperties();
430 containerName = (String) props.get("containerName");
432 // In the Global instance case the containerName is empty
436 staticRoutesFileName = ROOT + "staticRouting_" + containerName
439 log.debug("forwarding.staticrouting starting on container {}",
443 this.executor = Executors.newFixedThreadPool(1);
444 if (staticRouteConfigs.isEmpty()) {
449 * Slow probe to identify any gateway that might have silently appeared
450 * after the Static Routing Configuration.
452 gatewayProbeTimer = new Timer();
453 gatewayProbeTimer.schedule(new TimerTask() {
456 for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
457 StaticRoute route = s.getValue();
458 if ((route.getType() == StaticRoute.NextHopType.IPADDRESS)
459 && route.getHost() == null) {
460 checkAndUpdateListeners(s.getKey(), route, true);
464 }, 60 * 1000, 60 * 1000);
468 * Function called by the dependency manager when at least one
469 * dependency become unsatisfied or when the component is shutting
470 * down because for example bundle is being stopped.
474 log.debug("Destroy all the Static Routing Rules given we are "
477 gatewayProbeTimer.cancel();
479 // Clear the listener so to be ready in next life
480 this.staticRoutingAware.clear();
484 * Function called by dependency manager after "init ()" is called
485 * and after the services provided by the class are registered in
486 * the service registry
493 * Function called by the dependency manager before the services
494 * exported by the component are unregistered, this will be
495 * followed by a "destroy ()" calls
503 public Status saveConfiguration() {
504 return this.saveConfig();