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.Date;
20 import java.util.Dictionary;
21 import java.util.EnumSet;
22 import java.util.HashSet;
25 import java.util.Timer;
26 import java.util.TimerTask;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.Future;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
36 import org.apache.felix.dm.Component;
37 import org.opendaylight.controller.clustering.services.CacheConfigException;
38 import org.opendaylight.controller.clustering.services.CacheExistException;
39 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
40 import org.opendaylight.controller.clustering.services.IClusterServices;
41 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
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.IfIptoHost;
47 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
48 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
49 import org.opendaylight.controller.sal.utils.GlobalConstants;
50 import org.opendaylight.controller.sal.utils.IObjectReader;
51 import org.opendaylight.controller.sal.utils.ObjectReader;
52 import org.opendaylight.controller.sal.utils.ObjectWriter;
53 import org.opendaylight.controller.sal.utils.Status;
54 import org.opendaylight.controller.sal.utils.StatusCode;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
59 * Static Routing feature provides the bridge between SDN and Non-SDN networks.
61 public class StaticRoutingImplementation implements IfNewHostNotify,
62 IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware {
63 private static Logger log = LoggerFactory
64 .getLogger(StaticRoutingImplementation.class);
65 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
66 private static final String SAVE = "Save";
67 ConcurrentMap<String, StaticRoute> staticRoutes;
68 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
69 private IfIptoHost hostTracker;
70 private Timer gatewayProbeTimer;
71 private String staticRoutesFileName = null;
72 private IClusterContainerServices clusterContainerService = null;
73 private Set<IStaticRoutingAware> staticRoutingAware = Collections
74 .synchronizedSet(new HashSet<IStaticRoutingAware>());
75 private ExecutorService executor;
77 void setStaticRoutingAware(IStaticRoutingAware s) {
78 if (this.staticRoutingAware != null) {
79 this.staticRoutingAware.add(s);
83 void unsetStaticRoutingAware(IStaticRoutingAware s) {
84 if (this.staticRoutingAware != null) {
85 this.staticRoutingAware.remove(s);
89 public void setHostTracker(IfIptoHost hostTracker) {
90 log.debug("Setting HostTracker");
91 this.hostTracker = hostTracker;
94 public void unsetHostTracker(IfIptoHost hostTracker) {
95 if (this.hostTracker == hostTracker) {
96 this.hostTracker = null;
101 public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
102 return staticRouteConfigs;
105 public void setStaticRouteConfigs(
106 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs) {
107 this.staticRouteConfigs = staticRouteConfigs;
111 public Object readObject(ObjectInputStream ois)
112 throws FileNotFoundException, IOException, ClassNotFoundException {
113 // Perform the class deserialization locally, from inside the package
114 // where the class is defined
115 return ois.readObject();
118 @SuppressWarnings("unchecked")
119 private void loadConfiguration() {
120 ObjectReader objReader = new ObjectReader();
121 ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader
122 .read(this, staticRoutesFileName);
124 if (confList == null) {
128 for (StaticRouteConfig conf : confList.values()) {
129 addStaticRoute(conf);
134 private Status saveConfig() {
135 return saveConfigInternal();
138 public Status saveConfigInternal() {
140 ObjectWriter objWriter = new ObjectWriter();
142 status = objWriter.write(
143 new ConcurrentHashMap<String, StaticRouteConfig>(
144 staticRouteConfigs), staticRoutesFileName);
146 if (status.isSuccess()) {
149 return new Status(StatusCode.INTERNALERROR, "Save failed");
153 @SuppressWarnings("deprecation")
154 private void allocateCaches() {
155 if (this.clusterContainerService == null) {
157 .info("un-initialized clusterContainerService, can't create cache");
162 clusterContainerService.createCache(
163 "forwarding.staticrouting.routes", EnumSet
164 .of(IClusterServices.cacheMode.TRANSACTIONAL));
165 clusterContainerService.createCache(
166 "forwarding.staticrouting.configs", EnumSet
167 .of(IClusterServices.cacheMode.TRANSACTIONAL));
168 } catch (CacheExistException cee) {
170 .error("\nCache already exists - destroy and recreate if needed");
171 } catch (CacheConfigException cce) {
172 log.error("\nCache configuration invalid - check cache mode");
176 @SuppressWarnings({ "unchecked", "deprecation" })
177 private void retrieveCaches() {
178 if (this.clusterContainerService == null) {
180 .info("un-initialized clusterContainerService, can't retrieve cache");
184 staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
185 .getCache("forwarding.staticrouting.routes");
186 if (staticRoutes == null) {
187 log.error("\nFailed to get rulesDB handle");
190 staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
191 .getCache("forwarding.staticrouting.configs");
192 if (staticRouteConfigs == null) {
193 log.error("\nFailed to get rulesDB handle");
197 private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
198 if (this.staticRoutingAware != null) {
199 log.info("Invoking StaticRoutingAware listeners");
200 synchronized (this.staticRoutingAware) {
201 for (IStaticRoutingAware ra : this.staticRoutingAware) {
203 ra.staticRouteUpdate(s, update);
204 } catch (Exception e) {
212 private class NotifyStaticRouteWorker implements Callable<Object> {
213 private StaticRoute staticRoute;
214 private boolean added;
216 public NotifyStaticRouteWorker(StaticRoute s, boolean update) {
217 this.staticRoute = s;
222 public Object call() throws Exception {
224 || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
225 notifyStaticRouteUpdate(staticRoute, added);
227 InetAddress nh = staticRoute.getNextHopAddress();
228 HostNodeConnector host = hostTracker.hostQuery(nh);
230 log.debug("Next hop {} is not present, try to discover it", nh.getHostAddress());
231 Future<HostNodeConnector> future = hostTracker.discoverHost(nh);
232 if (future != null) {
235 } catch (Exception e) {
241 log.debug("Next hop {} is found", nh.getHostAddress());
242 staticRoute.setHost(host);
243 notifyStaticRouteUpdate(staticRoute, added);
245 log.debug("Next hop {} is still not present, try again later", nh.getHostAddress());
252 private void checkAndUpdateListeners(StaticRoute staticRoute, boolean added) {
253 NotifyStaticRouteWorker worker = new NotifyStaticRouteWorker(staticRoute, added);
255 executor.submit(worker);
256 } catch (Exception e) {
257 log.error("got Exception ", e);
261 private void notifyHostUpdate(HostNodeConnector host, boolean added) {
265 for (StaticRoute s : staticRoutes.values()) {
266 if (s.getType() == StaticRoute.NextHopType.SWITCHPORT) {
269 if (s.getNextHopAddress().equals(host.getNetworkAddress())) {
275 notifyStaticRouteUpdate(s, added);
281 public void notifyHTClient(HostNodeConnector host) {
282 notifyHostUpdate(host, true);
286 public void notifyHTClientHostRemoved(HostNodeConnector host) {
287 notifyHostUpdate(host, false);
290 public boolean isIPv4AddressValid(String cidr) {
295 String values[] = cidr.split("/");
296 Pattern ipv4Pattern = Pattern
297 .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
298 Matcher mm = ipv4Pattern.matcher(values[0]);
300 log.debug("IPv4 source address {} is not valid", cidr);
303 if (values.length >= 2) {
304 int prefix = Integer.valueOf(values[1]);
305 if ((prefix < 0) || (prefix > 32)) {
306 log.debug("prefix {} is not valid", prefix);
313 public static short getUnsignedByte(ByteBuffer bb, int position) {
314 return ((short) (bb.get(position) & (short) 0xff));
317 public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
318 for (int i = 0; i < buf1.array().length; i++) {
319 if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
321 } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
330 public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
331 ByteBuffer bblongestPrefix = null;
333 bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0")
335 } catch (Exception e) {
339 if (staticRoutes == null) {
343 StaticRoute longestPrefixRoute = null;
344 for (StaticRoute s : staticRoutes.values()) {
345 InetAddress prefix = s.longestPrefixMatch(ipAddress);
346 if ((prefix != null) && (prefix instanceof Inet4Address)) {
347 ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
348 if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
349 bblongestPrefix = bbtmp;
350 longestPrefixRoute = s;
354 return longestPrefixRoute;
358 public Status addStaticRoute(StaticRouteConfig config) {
359 Status status = config.isValid();
360 if (!status.isSuccess()) {
363 if (staticRouteConfigs.get(config.getName()) != null) {
364 return new Status(StatusCode.CONFLICT,
365 "A valid Static Route configuration with this name " +
366 "already exists. Please use a different name");
370 StaticRoute sRoute = new StaticRoute(config);
372 for (Map.Entry<String, StaticRoute> entry : staticRoutes.entrySet()) {
373 if (entry.getValue().compareTo(sRoute) == 0) {
374 return new Status(StatusCode.CONFLICT,
375 "This conflicts with an existing Static Route " +
376 "Configuration. Please check the configuration " +
380 staticRoutes.put(config.getName(), sRoute);
382 // Update config databse
383 staticRouteConfigs.put(config.getName(), config);
386 checkAndUpdateListeners(sRoute, true);
391 public Status removeStaticRoute(String name) {
392 staticRouteConfigs.remove(name);
393 StaticRoute sRoute = staticRoutes.remove(name);
394 if (sRoute != null) {
395 checkAndUpdateListeners(sRoute, false);
396 return new Status(StatusCode.SUCCESS, null);
398 return new Status(StatusCode.NOTFOUND,
399 "Static Route with name " + name + " is not found");
402 void setClusterContainerService(IClusterContainerServices s) {
403 log.debug("Cluster Service set");
404 this.clusterContainerService = s;
407 void unsetClusterContainerService(IClusterContainerServices s) {
408 if (this.clusterContainerService == s) {
409 log.debug("Cluster Service removed!");
410 this.clusterContainerService = null;
415 * Function called by the dependency manager when all the required
416 * dependencies are satisfied
419 void init(Component c) {
420 String containerName = null;
421 Dictionary props = c.getServiceProperties();
423 containerName = (String) props.get("containerName");
425 // In the Global instance case the containerName is empty
429 staticRoutesFileName = ROOT + "staticRouting_" + containerName
432 log.debug("forwarding.staticrouting starting on container {}",
434 //staticRoutes = new ConcurrentHashMap<String, StaticRoute>();
437 this.executor = Executors.newFixedThreadPool(1);
438 if (staticRouteConfigs.isEmpty()) {
443 * Slow probe to identify any gateway that might have silently appeared
444 * after the Static Routing Configuration.
446 gatewayProbeTimer = new Timer();
447 gatewayProbeTimer.schedule(new TimerTask() {
450 for (StaticRoute s : staticRoutes.values()) {
451 if ((s.getType() == StaticRoute.NextHopType.IPADDRESS)
452 && s.getHost() == null) {
453 checkAndUpdateListeners(s, true);
457 }, 60 * 1000, 60 * 1000);
461 * Function called by the dependency manager when at least one
462 * dependency become unsatisfied or when the component is shutting
463 * down because for example bundle is being stopped.
467 log.debug("Destroy all the Static Routing Rules given we are "
470 gatewayProbeTimer.cancel();
472 // Clear the listener so to be ready in next life
473 this.staticRoutingAware.clear();
477 * Function called by dependency manager after "init ()" is called
478 * and after the services provided by the class are registered in
479 * the service registry
486 * Function called by the dependency manager before the services
487 * exported by the component are unregistered, this will be
488 * followed by a "destroy ()" calls
496 public Status saveConfiguration() {
497 return this.saveConfig();