2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.forwarding.staticrouting.internal;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.net.Inet4Address;
15 import java.net.InetAddress;
16 import java.nio.ByteBuffer;
17 import java.util.ArrayList;
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.ConcurrentMap;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.Future;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
34 import org.apache.felix.dm.Component;
35 import org.opendaylight.controller.clustering.services.CacheConfigException;
36 import org.opendaylight.controller.clustering.services.CacheExistException;
37 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
38 import org.opendaylight.controller.clustering.services.IClusterServices;
39 import org.opendaylight.controller.configuration.ConfigurationObject;
40 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
41 import org.opendaylight.controller.configuration.IConfigurationContainerService;
42 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
43 import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
44 import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
45 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
46 import org.opendaylight.controller.hosttracker.HostIdFactory;
47 import org.opendaylight.controller.hosttracker.IHostId;
48 import org.opendaylight.controller.hosttracker.IfIptoHost;
49 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
50 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
51 import org.opendaylight.controller.sal.utils.IObjectReader;
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, IForwardingStaticRouting, IObjectReader,
61 IConfigurationContainerAware {
62 private static Logger log = LoggerFactory.getLogger(StaticRoutingImplementation.class);
63 private static final String STATIC_ROUTES_FILE_NAME = "staticRouting.conf";
64 ConcurrentMap<String, StaticRoute> staticRoutes;
65 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
66 private IfIptoHost hostTracker;
67 private Timer gatewayProbeTimer;
68 private IClusterContainerServices clusterContainerService = null;
69 private IConfigurationContainerService configurationService;
70 private Set<IStaticRoutingAware> staticRoutingAware = Collections
71 .synchronizedSet(new HashSet<IStaticRoutingAware>());
72 private ExecutorService executor;
74 void setStaticRoutingAware(IStaticRoutingAware s) {
75 if (this.staticRoutingAware != null) {
76 this.staticRoutingAware.add(s);
80 void unsetStaticRoutingAware(IStaticRoutingAware s) {
81 if (this.staticRoutingAware != null) {
82 this.staticRoutingAware.remove(s);
86 public void setHostTracker(IfIptoHost hostTracker) {
87 log.debug("Setting HostTracker");
88 this.hostTracker = hostTracker;
91 public void unsetHostTracker(IfIptoHost hostTracker) {
92 if (this.hostTracker == hostTracker) {
93 this.hostTracker = null;
97 public void setConfigurationContainerService(IConfigurationContainerService service) {
98 log.trace("Got configuration service set request {}", service);
99 this.configurationService = service;
102 public void unsetConfigurationContainerService(IConfigurationContainerService service) {
103 log.trace("Got configuration service UNset request");
104 this.configurationService = null;
108 public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
109 return staticRouteConfigs;
113 public Object readObject(ObjectInputStream ois) throws FileNotFoundException, IOException, ClassNotFoundException {
114 // Perform the class deserialization locally, from inside the package
115 // where the class is defined
116 return ois.readObject();
120 private void loadConfiguration() {
121 for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, STATIC_ROUTES_FILE_NAME)) {
122 addStaticRoute((StaticRouteConfig) conf);
126 private Status saveConfig() {
127 return saveConfigInternal();
130 public Status saveConfigInternal() {
131 Status status = configurationService.persistConfiguration(
132 new ArrayList<ConfigurationObject>(staticRouteConfigs.values()), STATIC_ROUTES_FILE_NAME);
134 if (status.isSuccess()) {
137 return new Status(StatusCode.INTERNALERROR, "Save failed");
141 private void allocateCaches() {
142 if (this.clusterContainerService == null) {
143 log.trace("un-initialized clusterContainerService, can't create cache");
148 clusterContainerService.createCache("forwarding.staticrouting.routes",
149 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
150 clusterContainerService.createCache("forwarding.staticrouting.configs",
151 EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
152 } catch (CacheExistException cee) {
153 log.error("\nCache already exists - destroy and recreate if needed");
154 } catch (CacheConfigException cce) {
155 log.error("\nCache configuration invalid - check cache mode");
159 @SuppressWarnings({ "unchecked" })
160 private void retrieveCaches() {
161 if (this.clusterContainerService == null) {
162 log.warn("un-initialized clusterContainerService, can't retrieve cache");
166 staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
167 .getCache("forwarding.staticrouting.routes");
168 if (staticRoutes == null) {
169 log.error("\nFailed to get rulesDB handle");
172 staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
173 .getCache("forwarding.staticrouting.configs");
174 if (staticRouteConfigs == null) {
175 log.error("\nFailed to get rulesDB handle");
179 private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
180 if (this.staticRoutingAware != null) {
181 log.trace("Invoking StaticRoutingAware listeners");
182 synchronized (this.staticRoutingAware) {
183 for (IStaticRoutingAware ra : this.staticRoutingAware) {
185 ra.staticRouteUpdate(s, update);
186 } catch (Exception e) {
194 private class NotifyStaticRouteWorker implements Callable<Object> {
197 private StaticRoute staticRoute;
198 private boolean added;
200 public NotifyStaticRouteWorker(String name, StaticRoute s, boolean update) {
202 this.staticRoute = s;
207 public Object call() throws Exception {
208 if (!added || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
209 notifyStaticRouteUpdate(staticRoute, added);
211 InetAddress nh = staticRoute.getNextHopAddress();
212 // HostTracker hosts db key scheme implementation
213 IHostId id = HostIdFactory.create(nh, null);
214 HostNodeConnector host = hostTracker.hostQuery(id);
216 log.debug("Next hop {} is not present, try to discover it", nh.getHostAddress());
217 Future<HostNodeConnector> future = hostTracker.discoverHost(id);
218 if (future != null) {
221 } catch (InterruptedException ioe) {
222 log.trace("Thread interrupted {}", ioe);
223 } catch (Exception e) {
229 log.debug("Next hop {} is found", nh.getHostAddress());
230 staticRoute.setHost(host);
231 // static route object has changed
232 // put the changed object back in the cache
234 staticRoutes.put(name, staticRoute);
235 notifyStaticRouteUpdate(staticRoute, added);
237 log.debug("Next hop {} is still not present, try again later", nh.getHostAddress());
244 private void checkAndUpdateListeners(String name, StaticRoute staticRoute, boolean added) {
245 NotifyStaticRouteWorker worker = new NotifyStaticRouteWorker(name, staticRoute, added);
247 executor.submit(worker);
248 } catch (Exception e) {
249 log.error("got Exception ", e);
253 private void notifyHostUpdate(HostNodeConnector host, boolean added) {
257 for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
258 StaticRoute route = s.getValue();
259 if (route.getType() == StaticRoute.NextHopType.SWITCHPORT) {
262 if (route.getNextHopAddress().equals(host.getNetworkAddress())) {
268 // static route object has changed
269 // put the changed object back in the cache
271 staticRoutes.put(s.getKey(), route);
272 notifyStaticRouteUpdate(route, added);
278 public void notifyHTClient(HostNodeConnector host) {
279 notifyHostUpdate(host, true);
283 public void notifyHTClientHostRemoved(HostNodeConnector host) {
284 notifyHostUpdate(host, false);
287 public boolean isIPv4AddressValid(String cidr) {
292 String values[] = cidr.split("/");
293 Pattern ipv4Pattern = Pattern
294 .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
295 Matcher mm = ipv4Pattern.matcher(values[0]);
297 log.debug("IPv4 source address {} is not valid", cidr);
300 if (values.length >= 2) {
301 int prefix = Integer.valueOf(values[1]);
302 if ((prefix < 0) || (prefix > 32)) {
303 log.debug("prefix {} is not valid", prefix);
310 public static short getUnsignedByte(ByteBuffer bb, int position) {
311 return ((short) (bb.get(position) & (short) 0xff));
314 public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
315 for (int i = 0; i < buf1.array().length; i++) {
316 if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
318 } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
327 public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
328 ByteBuffer bblongestPrefix = null;
330 bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0").getAddress());
331 } catch (Exception e) {
335 if (staticRoutes == null) {
339 StaticRoute longestPrefixRoute = null;
340 for (StaticRoute s : staticRoutes.values()) {
341 InetAddress prefix = s.longestPrefixMatch(ipAddress);
342 if ((prefix != null) && (prefix instanceof Inet4Address)) {
343 ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
344 if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
345 bblongestPrefix = bbtmp;
346 longestPrefixRoute = s;
350 return longestPrefixRoute;
354 public Status addStaticRoute(StaticRouteConfig config) {
355 Status status = config.isValid();
356 if (!status.isSuccess()) {
359 if (staticRouteConfigs.get(config.getName()) != null) {
360 return new Status(StatusCode.CONFLICT, "A valid Static Route configuration with this name "
361 + "already exists. Please use a different name");
365 StaticRoute sRoute = new StaticRoute(config);
367 for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
368 if (entry.getValue().compareTo(sRoute) == 0) {
369 return new Status(StatusCode.CONFLICT, "This conflicts with an existing Static Route "
370 + "Configuration. Please check the configuration " + "and try again");
373 staticRoutes.put(config.getName(), sRoute);
375 // Update config databse
376 staticRouteConfigs.put(config.getName(), config);
379 checkAndUpdateListeners(config.getName(), sRoute, true);
384 public Status removeStaticRoute(String name) {
385 staticRouteConfigs.remove(name);
386 StaticRoute sRoute = staticRoutes.remove(name);
387 if (sRoute != null) {
388 checkAndUpdateListeners(name, sRoute, false);
389 return new Status(StatusCode.SUCCESS, null);
391 return new Status(StatusCode.NOTFOUND, "Static Route with name " + name + " is not found");
394 void setClusterContainerService(IClusterContainerServices s) {
395 log.debug("Cluster Service set");
396 this.clusterContainerService = s;
399 void unsetClusterContainerService(IClusterContainerServices s) {
400 if (this.clusterContainerService == s) {
401 log.debug("Cluster Service removed!");
402 this.clusterContainerService = null;
407 * Function called by the dependency manager when all the required
408 * dependencies are satisfied
411 void init(Component c) {
412 String containerName = null;
413 Dictionary props = c.getServiceProperties();
415 containerName = (String) props.get("containerName");
417 // In the Global instance case the containerName is empty
421 log.debug("forwarding.staticrouting starting on container {}", containerName);
424 this.executor = Executors.newFixedThreadPool(1);
428 * Slow probe to identify any gateway that might have silently appeared
429 * after the Static Routing Configuration.
431 gatewayProbeTimer = new Timer();
432 gatewayProbeTimer.schedule(new TimerTask() {
435 for (Map.Entry<String, StaticRoute> s : staticRoutes.entrySet()) {
436 StaticRoute route = s.getValue();
437 if ((route.getType() == StaticRoute.NextHopType.IPADDRESS) && route.getHost() == null) {
438 checkAndUpdateListeners(s.getKey(), route, true);
442 }, 60 * 1000, 60 * 1000);
447 * Function called by the dependency manager when at least one dependency
448 * become unsatisfied or when the component is shutting down because for
449 * example bundle is being stopped.
453 log.debug("Destroy all the Static Routing Rules given we are " + "shutting down");
455 gatewayProbeTimer.cancel();
457 // Clear the listener so to be ready in next life
458 this.staticRoutingAware.clear();
462 * Function called by dependency manager after "init ()" is called and after
463 * the services provided by the class are registered in the service registry
470 * Function called by the dependency manager before the services exported by
471 * the component are unregistered, this will be followed by a "destroy ()"
480 public Status saveConfiguration() {
481 return this.saveConfig();