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.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.Future;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
33 import org.apache.felix.dm.Component;
34 import org.opendaylight.controller.clustering.services.CacheConfigException;
35 import org.opendaylight.controller.clustering.services.CacheExistException;
36 import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
37 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
38 import org.opendaylight.controller.clustering.services.IClusterServices;
39 import org.opendaylight.controller.configuration.IConfigurationContainerAware;
40 import org.opendaylight.controller.forwarding.staticrouting.IForwardingStaticRouting;
41 import org.opendaylight.controller.forwarding.staticrouting.IStaticRoutingAware;
42 import org.opendaylight.controller.forwarding.staticrouting.StaticRoute;
43 import org.opendaylight.controller.forwarding.staticrouting.StaticRouteConfig;
44 import org.opendaylight.controller.hosttracker.IfIptoHost;
45 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
46 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
47 import org.opendaylight.controller.sal.utils.StatusCode;
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.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Static Routing feature provides the bridge between SDN and Non-SDN networks.
62 public class StaticRoutingImplementation implements IfNewHostNotify,
63 IForwardingStaticRouting, IObjectReader, IConfigurationContainerAware,
64 ICacheUpdateAware<Long, String> {
65 private static Logger log = LoggerFactory
66 .getLogger(StaticRoutingImplementation.class);
67 private static String ROOT = GlobalConstants.STARTUPHOME.toString();
68 private static final String SAVE = "Save";
69 ConcurrentMap<String, StaticRoute> staticRoutes;
70 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs;
71 private IfIptoHost hostTracker;
72 private Timer gatewayProbeTimer;
73 private String staticRoutesFileName = null;
74 private Map<Long, String> configSaveEvent;
75 private IClusterContainerServices clusterContainerService = null;
76 private Set<IStaticRoutingAware> staticRoutingAware = Collections
77 .synchronizedSet(new HashSet<IStaticRoutingAware>());
79 void setStaticRoutingAware(IStaticRoutingAware s) {
80 if (this.staticRoutingAware != null) {
81 this.staticRoutingAware.add(s);
85 void unsetStaticRoutingAware(IStaticRoutingAware s) {
86 if (this.staticRoutingAware != null) {
87 this.staticRoutingAware.remove(s);
91 public void setHostTracker(IfIptoHost hostTracker) {
92 log.debug("Setting HostTracker");
93 this.hostTracker = hostTracker;
96 public void unsetHostTracker(IfIptoHost hostTracker) {
97 if (this.hostTracker == hostTracker) {
98 this.hostTracker = null;
102 public ConcurrentMap<String, StaticRouteConfig> getStaticRouteConfigs() {
103 return staticRouteConfigs;
106 public void setStaticRouteConfigs(
107 ConcurrentMap<String, StaticRouteConfig> staticRouteConfigs) {
108 this.staticRouteConfigs = staticRouteConfigs;
112 public Object readObject(ObjectInputStream ois)
113 throws FileNotFoundException, IOException, ClassNotFoundException {
114 // Perform the class deserialization locally, from inside the package
115 // where the class is defined
116 return ois.readObject();
119 @SuppressWarnings("unchecked")
120 private void loadConfiguration() {
121 ObjectReader objReader = new ObjectReader();
122 ConcurrentMap<String, StaticRouteConfig> confList = (ConcurrentMap<String, StaticRouteConfig>) objReader
123 .read(this, staticRoutesFileName);
125 if (confList == null) {
129 for (StaticRouteConfig conf : confList.values()) {
130 addStaticRoute(conf);
135 private Status saveConfig() {
136 // Publish the save config event to the cluster nodes
137 configSaveEvent.put(new Date().getTime(), SAVE);
138 return saveConfigInternal();
141 public Status saveConfigInternal() {
143 ObjectWriter objWriter = new ObjectWriter();
145 status = objWriter.write(
146 new ConcurrentHashMap<String, StaticRouteConfig>(
147 staticRouteConfigs), staticRoutesFileName);
149 if (status.isSuccess()) {
152 return new Status(StatusCode.INTERNALERROR, "Save failed");
156 @SuppressWarnings("deprecation")
157 private void allocateCaches() {
158 if (this.clusterContainerService == null) {
160 .info("un-initialized clusterContainerService, can't create cache");
165 clusterContainerService.createCache(
166 "forwarding.staticrouting.routes", EnumSet
167 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
168 clusterContainerService.createCache(
169 "forwarding.staticrouting.configs", EnumSet
170 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
171 clusterContainerService.createCache(
172 "forwarding.staticrouting.configSaveEvent", EnumSet
173 .of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
175 } catch (CacheExistException cee) {
177 .error("\nCache already exists - destroy and recreate if needed");
178 } catch (CacheConfigException cce) {
179 log.error("\nCache configuration invalid - check cache mode");
183 @SuppressWarnings({ "unchecked", "deprecation" })
184 private void retrieveCaches() {
185 if (this.clusterContainerService == null) {
187 .info("un-initialized clusterContainerService, can't retrieve cache");
191 staticRoutes = (ConcurrentMap<String, StaticRoute>) clusterContainerService
192 .getCache("forwarding.staticrouting.routes");
193 if (staticRoutes == null) {
194 log.error("\nFailed to get rulesDB handle");
197 staticRouteConfigs = (ConcurrentMap<String, StaticRouteConfig>) clusterContainerService
198 .getCache("forwarding.staticrouting.configs");
199 if (staticRouteConfigs == null) {
200 log.error("\nFailed to get rulesDB handle");
202 configSaveEvent = (ConcurrentMap<Long, String>) clusterContainerService
203 .getCache("forwarding.staticrouting.configSaveEvent");
204 if (configSaveEvent == null) {
205 log.error("\nFailed to get cache for configSaveEvent");
209 @SuppressWarnings("deprecation")
210 private void destroyCaches() {
211 if (this.clusterContainerService == null) {
213 .info("un-initialized clusterContainerService, can't destroy cache");
217 clusterContainerService.destroyCache("forwarding.staticrouting.routes");
218 clusterContainerService
219 .destroyCache("forwarding.staticrouting.configs");
220 clusterContainerService
221 .destroyCache("forwarding.staticrouting.configSaveEvent");
226 public void entryCreated(Long key, String cacheName, boolean local) {
230 public void entryUpdated(Long key, String new_value, String cacheName,
231 boolean originLocal) {
232 saveConfigInternal();
236 public void entryDeleted(Long key, String cacheName, boolean originLocal) {
239 private void notifyStaticRouteUpdate(StaticRoute s, boolean update) {
240 if (this.staticRoutingAware != null) {
241 log.info("Invoking StaticRoutingAware listeners");
242 synchronized (this.staticRoutingAware) {
243 for (IStaticRoutingAware ra : this.staticRoutingAware) {
245 ra.staticRouteUpdate(s, update);
246 } catch (Exception e) {
254 private class NotifyStaticRouteThread extends Thread {
255 private StaticRoute staticRoute;
256 private boolean added;
258 public NotifyStaticRouteThread(StaticRoute s, boolean update) {
259 this.staticRoute = s;
265 || (staticRoute.getType() == StaticRoute.NextHopType.SWITCHPORT)) {
266 notifyStaticRouteUpdate(staticRoute, added);
268 HostNodeConnector host = hostTracker.hostQuery(staticRoute
269 .getNextHopAddress());
271 Future<HostNodeConnector> future = hostTracker
272 .discoverHost(staticRoute.getNextHopAddress());
273 if (future != null) {
276 } catch (Exception e) {
282 staticRoute.setHost(host);
283 notifyStaticRouteUpdate(staticRoute, added);
289 private void checkAndUpdateListeners(StaticRoute staticRoute, boolean added) {
290 new NotifyStaticRouteThread(staticRoute, added).start();
293 private void notifyHostUpdate(HostNodeConnector host, boolean added) {
296 for (StaticRoute s : staticRoutes.values()) {
297 if (s.getType() == StaticRoute.NextHopType.SWITCHPORT)
299 if (s.getNextHopAddress().equals(host.getNetworkAddress())) {
305 notifyStaticRouteUpdate(s, added);
311 public void notifyHTClient(HostNodeConnector host) {
312 notifyHostUpdate(host, true);
316 public void notifyHTClientHostRemoved(HostNodeConnector host) {
317 notifyHostUpdate(host, false);
320 public boolean isIPv4AddressValid(String cidr) {
324 String values[] = cidr.split("/");
325 Pattern ipv4Pattern = Pattern
326 .compile("(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
327 Matcher mm = ipv4Pattern.matcher(values[0]);
329 log.debug("IPv4 source address {} is not valid", cidr);
332 if (values.length >= 2) {
333 int prefix = Integer.valueOf(values[1]);
334 if ((prefix < 0) || (prefix > 32)) {
335 log.debug("prefix {} is not valid", prefix);
342 public static short getUnsignedByte(ByteBuffer bb, int position) {
343 return ((short) (bb.get(position) & (short) 0xff));
346 public static int compareByteBuffers(ByteBuffer buf1, ByteBuffer buf2) {
347 for (int i = 0; i < buf1.array().length; i++) {
348 if (getUnsignedByte(buf1, i) > getUnsignedByte(buf2, i)) {
350 } else if (getUnsignedByte(buf1, i) < getUnsignedByte(buf2, i)) {
358 public StaticRoute getBestMatchStaticRoute(InetAddress ipAddress) {
359 ByteBuffer bblongestPrefix = null;
361 bblongestPrefix = ByteBuffer.wrap(InetAddress.getByName("0.0.0.0")
363 } catch (Exception e) {
367 if (staticRoutes == null) {
371 StaticRoute longestPrefixRoute = null;
372 for (StaticRoute s : staticRoutes.values()) {
373 InetAddress prefix = s.longestPrefixMatch(ipAddress);
374 if ((prefix != null) && (prefix instanceof Inet4Address)) {
375 ByteBuffer bbtmp = ByteBuffer.wrap(prefix.getAddress());
376 if (compareByteBuffers(bbtmp, bblongestPrefix) > 0) {
377 bblongestPrefix = bbtmp;
378 longestPrefixRoute = s;
382 return longestPrefixRoute;
385 public Status addStaticRoute(StaticRouteConfig config) {
388 status = config.isValid();
389 if (!status.isSuccess()) {
392 if (staticRouteConfigs.get(config.getName()) != null) {
393 return new Status(StatusCode.CONFLICT,
394 "A valid Static Route configuration with this name " +
395 "already exists. Please use a different name");
397 for (StaticRouteConfig s : staticRouteConfigs.values()) {
398 if (s.equals(config)) {
399 return new Status(StatusCode.CONFLICT,
400 "This conflicts with an existing Static Route " +
401 "Configuration. Please check the configuration " +
406 staticRouteConfigs.put(config.getName(), config);
407 StaticRoute sRoute = new StaticRoute(config);
408 staticRoutes.put(config.getName(), sRoute);
409 checkAndUpdateListeners(sRoute, true);
413 public Status removeStaticRoute(String name) {
414 staticRouteConfigs.remove(name);
415 StaticRoute sRoute = staticRoutes.remove(name);
416 if (sRoute != null) {
417 checkAndUpdateListeners(sRoute, false);
418 return new Status(StatusCode.SUCCESS, null);
420 return new Status(StatusCode.NOTFOUND,
421 "Static Route with name " + name + " is not found");
424 void setClusterContainerService(IClusterContainerServices s) {
425 log.debug("Cluster Service set");
426 this.clusterContainerService = s;
429 void unsetClusterContainerService(IClusterContainerServices s) {
430 if (this.clusterContainerService == s) {
431 log.debug("Cluster Service removed!");
432 this.clusterContainerService = null;
437 * Function called by the dependency manager when all the required
438 * dependencies are satisfied
441 void init(Component c) {
442 String containerName = null;
443 Dictionary props = c.getServiceProperties();
445 containerName = (String) props.get("containerName");
447 // In the Global instance case the containerName is empty
451 staticRoutesFileName = ROOT + "staticRouting_" + containerName
454 log.debug("forwarding.staticrouting starting on container {}",
456 //staticRoutes = new ConcurrentHashMap<String, StaticRoute>();
460 if (staticRouteConfigs.isEmpty())
464 * Slow probe to identify any gateway that might have silently appeared
465 * after the Static Routing Configuration.
467 gatewayProbeTimer = new Timer();
468 gatewayProbeTimer.schedule(new TimerTask() {
471 for (StaticRoute s : staticRoutes.values()) {
472 if ((s.getType() == StaticRoute.NextHopType.IPADDRESS)
473 && s.getHost() == null) {
474 checkAndUpdateListeners(s, true);
478 }, 60 * 1000, 60 * 1000);
482 * Function called by the dependency manager when at least one
483 * dependency become unsatisfied or when the component is shutting
484 * down because for example bundle is being stopped.
488 log.debug("Destroy all the Static Routing Rules given we are "
492 gatewayProbeTimer.cancel();
494 // Clear the listener so to be ready in next life
495 this.staticRoutingAware.clear();
499 * Function called by dependency manager after "init ()" is called
500 * and after the services provided by the class are registered in
501 * the service registry
508 * Function called by the dependency manager before the services
509 * exported by the component are unregistered, this will be
510 * followed by a "destroy ()" calls
517 public Status saveConfiguration() {
518 return this.saveConfig();