CORD-592 Added DHCP classless static route option for service dependency

And moved config registration code to cordvtnmanager

Change-Id: I2657ca21659fa1abfb81799a922524ca9c52ead6
diff --git a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
index cbdde5f..6b26daf 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
@@ -22,6 +22,7 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
 
 /**
  * Representation of a service network which holds service specific information,
@@ -91,6 +92,19 @@
                 .findAny().isPresent();
     }
 
+    /**
+     * Returns if the given network is the provider of this network with
+     * bidirectional access type.
+     *
+     * @param netId network id
+     * @return true if the given network is a bidrectional provider
+     */
+    public boolean isBidirectionalProvider(NetworkId netId) {
+        return providers.stream().filter(p -> Objects.equals(p.id(), netId))
+                .filter(p -> p.type() == BIDIRECTIONAL)
+                .findAny().isPresent();
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
index ba5a4af..607e3b0 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
@@ -16,6 +16,7 @@
 package org.opencord.cordvtn.impl;
 
 import com.google.common.collect.Lists;
+import com.google.common.primitives.Bytes;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -27,6 +28,7 @@
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
@@ -56,7 +58,11 @@
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static org.onlab.packet.DHCP.DHCPOptionCode.*;
@@ -76,8 +82,9 @@
 
     private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
     private static final byte PACKET_TTL = (byte) 127;
-    // TODO add MTU option code to ONOS DHCP implementation and remove this
+    // TODO add MTU, static route option codes to ONOS DHCP and remove here
     private static final byte DHCP_OPTION_MTU = (byte) 26;
+    private static final byte DHCP_OPTION_CLASSLESS_STATIC_ROUTE = (byte) 121;
     private static final byte[] DHCP_DATA_LEASE_INFINITE =
             ByteBuffer.allocate(4).putInt(-1).array();
     private static final byte[] DHCP_DATA_MTU_DEFAULT =
@@ -117,13 +124,12 @@
 
     @Deactivate
     protected void deactivate() {
+        configRegistry.removeListener(configListener);
         packetService.removeProcessor(packetProcessor);
         cancelPackets();
         log.info("Stopped");
     }
 
-    // TODO implement public method forceRenew
-
     private void requestPackets() {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -341,6 +347,7 @@
             option.setData(broadcast.toOctets());
             options.add(option);
 
+            // domain server
             option = new DHCPOption();
             option.setCode(OptionCode_DomainServer.getValue());
             option.setLength((byte) 4);
@@ -363,7 +370,15 @@
                 options.add(option);
             }
 
-            // TODO add host route option if network has service dependency
+            // classless static routes
+            byte[] data = getClasslessStaticRoutesData(vtnNet);
+            if (data.length >= 5) {
+                option = new DHCPOption();
+                option.setCode(DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
+                option.setLength((byte) data.length);
+                option.setData(data);
+                options.add(option);
+            }
 
             // end option
             option = new DHCPOption();
@@ -374,6 +389,45 @@
             dhcpReply.setOptions(options);
             return dhcpReply;
         }
+
+        private byte[] getClasslessStaticRoutesData(VtnNetwork vtnNet) {
+            List<Byte> result = Lists.newArrayList();
+            List<Byte> router = Bytes.asList(vtnNet.serviceIp().toOctets());
+
+            // static routes for the providers
+            Set<VtnNetwork> providers = vtnNet.providers().stream()
+                    .map(provider -> cordVtnService.vtnNetwork(provider.id()))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+
+            providers.stream().forEach(provider -> {
+                result.add((byte) provider.subnet().prefixLength());
+                result.addAll(getSignificantOctets(provider.subnet()));
+                result.addAll(router);
+            });
+
+            // static routes for the bidirectional subscribers
+            Set<VtnNetwork> subscribers = cordVtnService.vtnNetworks().stream()
+                    .filter(net -> net.isBidirectionalProvider(vtnNet.id()))
+                    .collect(Collectors.toSet());
+
+            subscribers.stream().forEach(subscriber -> {
+                result.add((byte) subscriber.subnet().prefixLength());
+                result.addAll(getSignificantOctets(subscriber.subnet()));
+                result.addAll(router);
+            });
+
+            return Bytes.toArray(result);
+        }
+
+        private List<Byte> getSignificantOctets(IpPrefix ipPrefix) {
+            int numOfOctets = ipPrefix.prefixLength() / 8;
+            if (ipPrefix.prefixLength() % 8 != 0) {
+                numOfOctets += 1;
+            }
+            byte[] result = Arrays.copyOfRange(ipPrefix.address().toOctets(), 0, numOfOctets);
+            return Bytes.asList(result);
+        }
     }
 
     private void readConfiguration() {
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
index e93eaf6..f3dd1f4 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
@@ -27,7 +27,10 @@
 import org.onosproject.event.ListenerRegistry;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
 import org.onosproject.net.host.HostService;
 import org.opencord.cordvtn.api.Constants;
 import org.opencord.cordvtn.api.config.CordVtnConfig;
@@ -108,6 +111,9 @@
     protected NetworkConfigService configService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -116,18 +122,31 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnStore store;
 
+    // TODO add cordvtn config service and move this
+    private static final Class<CordVtnConfig> CONFIG_CLASS = CordVtnConfig.class;
+    private final ConfigFactory configFactory =
+            new ConfigFactory<ApplicationId, CordVtnConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "cordvtn") {
+                @Override
+                public CordVtnConfig createConfig() {
+                    return new CordVtnConfig();
+                }
+            };
+
     private final CordVtnStoreDelegate delegate = new InternalCordVtnStoreDelegate();
     private ApplicationId appId;
 
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
+        configRegistry.registerConfigFactory(configFactory);
         store.setDelegate(delegate);
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
+        configRegistry.unregisterConfigFactory(configFactory);
         store.unsetDelegate(delegate);
         log.info("Stopped");
     }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
index c2a89b9..f7f9c2d 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -29,36 +29,26 @@
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.behaviour.BridgeDescription;
-import org.onosproject.net.behaviour.DefaultBridgeDescription;
-import org.onosproject.net.behaviour.InterfaceConfig;
-import org.onosproject.net.behaviour.TunnelEndPoints;
-import org.onosproject.net.behaviour.TunnelKeys;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.opencord.cordvtn.api.node.ConnectionHandler;
-import org.opencord.cordvtn.api.config.CordVtnConfig;
-import org.opencord.cordvtn.api.node.CordVtnNode;
-import org.opencord.cordvtn.api.node.CordVtnNodeState;
-import org.opencord.cordvtn.api.instance.InstanceService;
-import org.opencord.cordvtn.api.node.NetworkAddress;
-import org.opencord.cordvtn.api.node.SshAccessInfo;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
 import org.onosproject.net.behaviour.BridgeName;
 import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.behaviour.DefaultBridgeDescription;
 import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.InterfaceConfig;
 import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoints;
+import org.onosproject.net.behaviour.TunnelKeys;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.device.DeviceAdminService;
 import org.onosproject.net.device.DeviceEvent;
@@ -75,6 +65,13 @@
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.Versioned;
+import org.opencord.cordvtn.api.config.CordVtnConfig;
+import org.opencord.cordvtn.api.instance.InstanceService;
+import org.opencord.cordvtn.api.node.ConnectionHandler;
+import org.opencord.cordvtn.api.node.CordVtnNode;
+import org.opencord.cordvtn.api.node.CordVtnNodeState;
+import org.opencord.cordvtn.api.node.NetworkAddress;
+import org.opencord.cordvtn.api.node.SshAccessInfo;
 import org.slf4j.Logger;
 
 import java.util.List;
@@ -118,9 +115,6 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigService configService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -150,16 +144,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
 
-    private static final Class<CordVtnConfig> CONFIG_CLASS = CordVtnConfig.class;
-    private final ConfigFactory configFactory =
-            new ConfigFactory<ApplicationId, CordVtnConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "cordvtn") {
-                @Override
-                public CordVtnConfig createConfig() {
-                    return new CordVtnConfig();
-                }
-            };
-
     private final ExecutorService eventExecutor =
             newSingleThreadExecutor(groupedThreads("onos/cordvtn-node", "event-handler", log));
 
@@ -230,7 +214,6 @@
         appId = coreService.registerApplication(CORDVTN_APP_ID);
         leadershipService.runForLeadership(appId.name());
         localNodeId = clusterService.getLocalNode().id();
-        configRegistry.registerConfigFactory(configFactory);
 
         nodeStore = storageService.<String, CordVtnNode>consistentMapBuilder()
                 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
@@ -254,7 +237,6 @@
         nodeStore.removeListener(nodeStoreListener);
 
         leadershipService.withdraw(appId.name());
-        configRegistry.unregisterConfigFactory(configFactory);
         eventExecutor.shutdown();
 
         log.info("Stopped");
@@ -850,7 +832,7 @@
             return;
         }
 
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
             return;
@@ -859,7 +841,7 @@
     }
 
     private void readControllers() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
             return;