New module SdnIpConfigReader useful for SDN-IP. It provides configuration to ARP Proxy for nodes not running SDN-IP.

Change-Id: I8e65d774bf0722897898fc2e3aec0c2a29cb566f
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/SdnIpConfigReader.java b/src/main/java/net/onrc/onos/apps/sdnip/SdnIpConfigReader.java
new file mode 100644
index 0000000..f508f5d
--- /dev/null
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIpConfigReader.java
@@ -0,0 +1,201 @@
+package net.onrc.onos.apps.sdnip;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.module.IFloodlightModule;
+import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.main.config.IConfigInfoService;
+import net.onrc.onos.core.util.SwitchPort;
+
+import org.apache.commons.configuration.ConfigurationRuntimeException;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
+import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
+import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
+
+/**
+ * SDN-IP Config Reader provides IConfigInfoService
+ * by reading from an SDN-IP configuration file.
+ * It must be enabled on the nodes within the cluster
+ * not running SDN-IP.
+ * <p/>
+ * TODO: As a long term solution, a module providing
+ * general network configuration to ONOS nodes should be used.
+ */
+public class SdnIpConfigReader implements IFloodlightModule, IConfigInfoService {
+
+    private static final Logger log = LoggerFactory.getLogger(SdnIpConfigReader.class);
+
+    private static final String DEFAULT_CONFIG_FILENAME = "config.json";
+    private String currentConfigFilename = DEFAULT_CONFIG_FILENAME;
+    private Map<String, Interface> interfaces;
+    private Map<InetAddress, BgpPeer> bgpPeers;
+    private MACAddress bgpdMacAddress;
+    private short vlan;
+    private InvertedRadixTree<Interface> interfaceRoutes;
+    private Set<SwitchPort> externalNetworkSwitchPorts;
+
+    /**
+     * Reads the info contained in the configuration file.
+     *
+     * @param configFilename The name of configuration file for SDN-IP application.
+     */
+    private void readConfiguration(String configFilename) {
+        File gatewaysFile = new File(configFilename);
+        ObjectMapper mapper = new ObjectMapper();
+
+        try {
+            Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
+            interfaces = new HashMap<>();
+            for (Interface intf : config.getInterfaces()) {
+                interfaces.put(intf.getName(), intf);
+                externalNetworkSwitchPorts.add(new SwitchPort(intf.getDpid(),
+                        intf.getPort()));
+            }
+            bgpPeers = new HashMap<>();
+            for (BgpPeer peer : config.getPeers()) {
+                bgpPeers.put(peer.getIpAddress(), peer);
+            }
+            bgpdMacAddress = config.getBgpdMacAddress();
+            vlan = config.getVlan();
+        } catch (JsonParseException | JsonMappingException e) {
+            log.error("Error in JSON file", e);
+            throw new ConfigurationRuntimeException("Error in JSON file", e);
+        } catch (IOException e) {
+            log.error("Error reading JSON file", e);
+            throw new ConfigurationRuntimeException("Error in JSON file", e);
+        }
+
+        // Populate the interface InvertedRadixTree
+        for (Interface intf : interfaces.values()) {
+            Prefix prefix = new Prefix(intf.getIpAddress().getAddress(),
+                    intf.getPrefixLength());
+            interfaceRoutes.put(prefix.toBinaryString(), intf);
+        }
+    }
+
+    /**
+     * To find the Interface which has longest matchable IP prefix (sub-network
+     *  prefix) to next hop IP address.
+     *
+     * @param address the IP address of next hop router
+     * @return Interface the Interface which has longest matchable IP prefix
+     */
+    private Interface longestInterfacePrefixMatch(InetAddress address) {
+        Prefix prefixToSearchFor = new Prefix(address.getAddress(),
+                Prefix.MAX_PREFIX_LENGTH);
+        Iterator<Interface> it =
+                interfaceRoutes.getValuesForKeysPrefixing(
+                        prefixToSearchFor.toBinaryString()).iterator();
+        Interface intf = null;
+        // Find the last prefix, which will be the longest prefix
+        while (it.hasNext()) {
+            intf = it.next();
+        }
+
+        return intf;
+    }
+
+    @Override
+    public boolean isInterfaceAddress(InetAddress address) {
+        Interface intf = longestInterfacePrefixMatch(address);
+        return (intf != null && intf.getIpAddress().equals(address));
+    }
+
+    @Override
+    public boolean inConnectedNetwork(InetAddress address) {
+        Interface intf = longestInterfacePrefixMatch(address);
+        return (intf != null && !intf.getIpAddress().equals(address));
+    }
+
+    @Override
+    public boolean fromExternalNetwork(long inDpid, short inPort) {
+        for (Interface intf : interfaces.values()) {
+            if (intf.getDpid() == inDpid && intf.getPort() == inPort) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Interface getOutgoingInterface(InetAddress dstIpAddress) {
+        return longestInterfacePrefixMatch(dstIpAddress);
+    }
+
+    @Override
+    public boolean hasLayer3Configuration() {
+        return !interfaces.isEmpty();
+    }
+
+    @Override
+    public MACAddress getRouterMacAddress() {
+        return bgpdMacAddress;
+    }
+
+    @Override
+    public short getVlan() {
+        return vlan;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l = new ArrayList<>();
+        l.add(IConfigInfoService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+        Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<>();
+        m.put(IConfigInfoService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        return null;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+        interfaceRoutes = new ConcurrentInvertedRadixTree<>(
+                new DefaultByteArrayNodeFactory());
+        externalNetworkSwitchPorts = new HashSet<SwitchPort>();
+        // Reading config values
+        String configFilenameParameter = context.getConfigParams(this).get("configfile");
+        if (configFilenameParameter != null) {
+            currentConfigFilename = configFilenameParameter;
+        }
+        log.debug("Config file set to {}", currentConfigFilename);
+
+        readConfiguration(currentConfigFilename);
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) throws FloodlightModuleException {
+    }
+
+    @Override
+    public Set<SwitchPort> getExternalSwitchPorts() {
+        return Collections.unmodifiableSet(externalNetworkSwitchPorts);
+    }
+}