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); } }