Renamed SDN-IP packages and classes.

The code use to use the name 'BgpRoute' in a number of places, which is not
descriptive and doesn't map to how we talk about SDN-IP (we always call it
SDN-IP in all other documents/presentations).

Details of changes are as follows:

net.onrc.onos.apps.bgproute -> net.onrc.onos.apps.sdnip
    BgpRoute.java -> SdnIp.java
    IBgpRouteService.java -> ISdnIpService.java

created new package for web classes: net.onrc.onos.apps.sdnip.web
    BgpRouteResource.java -> IncomingRequestResource.java
    BgpRouteResourceSynch.java -> OutgoingRequestResource.java
    BgpRouteWebRoutable.java -> SdnIpWebRoutable.java

Change-Id: Ie6b1cbe4e95736d4cbd53b9f4def7cc3e0b46132
diff --git a/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
new file mode 100644
index 0000000..4e4e6fa
--- /dev/null
+++ b/src/main/java/net/onrc/onos/apps/sdnip/SdnIp.java
@@ -0,0 +1,1406 @@
+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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.IOFSwitchListener;
+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.core.util.SingletonTask;
+import net.floodlightcontroller.restserver.IRestApiService;
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.apps.proxyarp.IArpRequester;
+import net.onrc.onos.apps.proxyarp.IProxyArpService;
+import net.onrc.onos.apps.sdnip.RibUpdate.Operation;
+import net.onrc.onos.apps.sdnip.web.SdnIpWebRoutable;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscovery.LDUpdate;
+import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
+import net.onrc.onos.core.main.config.IConfigInfoService;
+import net.onrc.onos.core.packet.Ethernet;
+import net.onrc.onos.core.packet.IPv4;
+import net.onrc.onos.core.util.CallerId;
+import net.onrc.onos.core.util.DataPath;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.FlowEntryAction;
+import net.onrc.onos.core.util.FlowEntryActions;
+import net.onrc.onos.core.util.FlowEntryMatch;
+import net.onrc.onos.core.util.FlowId;
+import net.onrc.onos.core.util.FlowPath;
+import net.onrc.onos.core.util.FlowPathFlags;
+import net.onrc.onos.core.util.FlowPathType;
+import net.onrc.onos.core.util.FlowPathUserState;
+import net.onrc.onos.core.util.IPv4Net;
+import net.onrc.onos.core.util.Port;
+import net.onrc.onos.core.util.SwitchPort;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import net.sf.json.JSONSerializer;
+
+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.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFPacketOut;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.net.InetAddresses;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+public class SdnIp implements IFloodlightModule, ISdnIpService,
+        IArpRequester,
+        IOFSwitchListener, IConfigInfoService {
+
+    private static final Logger log = LoggerFactory.getLogger(SdnIp.class);
+
+    private IFloodlightProviderService floodlightProvider;
+    private ILinkDiscoveryService linkDiscoveryService;
+    private IRestApiService restApi;
+    private IProxyArpService proxyArp;
+
+    private IPatriciaTree<RibEntry> ptree;
+    private IPatriciaTree<Interface> interfacePtree;
+    private BlockingQueue<RibUpdate> ribUpdates;
+
+    private String bgpdRestIp;
+    private String routerId;
+    private static final String DEFAULT_CONFIG_FILENAME = "config.json";
+    private String currentConfigFilename = DEFAULT_CONFIG_FILENAME;
+
+    private static final short ARP_PRIORITY = 20;
+
+    // The fields below are unused after the move to FlowManager.
+    // Remove them if no longer needed.
+    /*
+    // We need to identify our flows somehow, in lieu of an OS-wide mechanism
+    // to hand out cookie IDs to prevent conflicts.
+    private static final long APP_COOKIE = 0xa0000000000000L;
+    // Cookie for flows that do L2 forwarding within SDN domain to egress routers
+    private static final long L2_FWD_COOKIE = APP_COOKIE + 1;
+    // Cookie for flows in ingress switches that rewrite the MAC address
+    private static final long MAC_RW_COOKIE = APP_COOKIE + 2;
+    // Cookie for flows that setup BGP paths
+    private static final long BGP_COOKIE = APP_COOKIE + 3;
+    // Forwarding uses priority 0, and the mac rewrite entries in ingress switches
+    // need to be higher priority than this otherwise the rewrite may not get done
+    private static final short SDNIP_PRIORITY = 10;
+    */
+
+    private static final short BGP_PORT = 179;
+
+    private static final int TOPO_DETECTION_WAIT = 2; // seconds
+
+    // Configuration stuff
+    private List<String> switches;
+    private Map<String, Interface> interfaces;
+    private Map<InetAddress, BgpPeer> bgpPeers;
+    private SwitchPort bgpdAttachmentPoint;
+    private MACAddress bgpdMacAddress;
+    private short vlan;
+
+    // True when all switches have connected
+    private volatile boolean switchesConnected = false;
+    // True when we have a full mesh of shortest paths between gateways
+    private volatile boolean topologyReady = false;
+
+    private List<LDUpdate> linkUpdates;
+    private SingletonTask topologyChangeDetectorTask;
+
+    private SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
+
+    private Map<InetAddress, Path> pathsWaitingOnArp;
+
+    private ExecutorService bgpUpdatesExecutor;
+
+    private Map<InetAddress, Path> pushedPaths;
+    private Map<Prefix, Path> prefixToPath;
+    //  private Multimap<Prefix, PushedFlowMod> pushedFlows;
+    private Multimap<Prefix, FlowId> pushedFlowIds;
+
+    private FlowCache flowCache;
+
+    // TODO: Fix for the new Topology Network Graph
+    // private volatile Topology topology = null;
+
+    private class TopologyChangeDetector implements Runnable {
+        @Override
+        public void run() {
+            log.debug("Running topology change detection task");
+            // TODO: Fix the code below after topoLinkService was removed
+            /*
+            synchronized (linkUpdates) {
+
+                ITopoLinkService topoLinkService = new TopoLinkServiceImpl();
+
+                List<Link> activeLinks = topoLinkService.getActiveLinks();
+
+                Iterator<LDUpdate> it = linkUpdates.iterator();
+                while (it.hasNext()){
+                    LDUpdate ldu = it.next();
+                    Link l = new Link(ldu.getSrc(), ldu.getSrcPort(),
+                            ldu.getDst(), ldu.getDstPort());
+
+                    if (activeLinks.contains(l)){
+                        it.remove();
+                    }
+                }
+            }
+            */
+
+            if (!topologyReady) {
+                if (linkUpdates.isEmpty()) {
+                    // All updates have been seen in network map.
+                    // We can check if topology is ready
+                    log.debug("No known changes outstanding. Checking topology now");
+                    checkStatus();
+                } else {
+                    // We know of some link updates that haven't propagated to the database yet
+                    log.debug("Some changes not found in network map - {} links missing", linkUpdates.size());
+                    topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+                }
+            }
+        }
+    }
+
+    private void readConfiguration(String configFilename) {
+        File gatewaysFile = new File(configFilename);
+        ObjectMapper mapper = new ObjectMapper();
+
+        try {
+            Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
+
+            switches = config.getSwitches();
+            interfaces = new HashMap<String, Interface>();
+            for (Interface intf : config.getInterfaces()) {
+                interfaces.put(intf.getName(), intf);
+            }
+            bgpPeers = new HashMap<InetAddress, BgpPeer>();
+            for (BgpPeer peer : config.getPeers()) {
+                bgpPeers.put(peer.getIpAddress(), peer);
+            }
+
+            bgpdAttachmentPoint = new SwitchPort(
+                    new Dpid(config.getBgpdAttachmentDpid()),
+                    new Port(config.getBgpdAttachmentPort()));
+
+            bgpdMacAddress = config.getBgpdMacAddress();
+            vlan = config.getVlan();
+        } catch (JsonParseException e) {
+            log.error("Error in JSON file", e);
+            throw new ConfigurationRuntimeException("Error in JSON file", e);
+        } catch (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 Patricia Tree
+        for (Interface intf : interfaces.values()) {
+            Prefix prefix = new Prefix(intf.getIpAddress().getAddress(), intf.getPrefixLength());
+            interfacePtree.put(prefix, intf);
+        }
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l
+                = new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(ISdnIpService.class);
+        l.add(IConfigInfoService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+        Map<Class<? extends IFloodlightService>, IFloodlightService> m
+                = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+        m.put(ISdnIpService.class, this);
+        m.put(IConfigInfoService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l
+                = new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IFloodlightProviderService.class);
+        l.add(IRestApiService.class);
+        return l;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context)
+            throws FloodlightModuleException {
+
+        ptree = new PatriciaTree<RibEntry>(32);
+        interfacePtree = new PatriciaTree<Interface>(32);
+
+        ribUpdates = new LinkedBlockingQueue<RibUpdate>();
+
+        // Register floodlight provider and REST handler.
+        floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
+        linkDiscoveryService = context.getServiceImpl(ILinkDiscoveryService.class);
+        restApi = context.getServiceImpl(IRestApiService.class);
+        proxyArp = context.getServiceImpl(IProxyArpService.class);
+
+        linkUpdates = new ArrayList<LDUpdate>();
+        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+        topologyChangeDetectorTask = new SingletonTask(executor, new TopologyChangeDetector());
+
+        pathsWaitingOnArp = new HashMap<InetAddress, Path>();
+        prefixesWaitingOnArp = Multimaps.synchronizedSetMultimap(
+                HashMultimap.<InetAddress, RibUpdate>create());
+
+        pushedPaths = new HashMap<InetAddress, Path>();
+        prefixToPath = new HashMap<Prefix, Path>();
+//              pushedFlows = HashMultimap.<Prefix, PushedFlowMod>create();
+        pushedFlowIds = HashMultimap.<Prefix, FlowId>create();
+
+        flowCache = new FlowCache(floodlightProvider);
+
+        bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
+                new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
+
+        // Read in config values
+        bgpdRestIp = context.getConfigParams(this).get("BgpdRestIp");
+        if (bgpdRestIp == null) {
+            log.error("BgpdRestIp property not found in config file");
+            throw new ConfigurationRuntimeException(
+                    "BgpdRestIp property not found in config file");
+        } else {
+            log.info("BgpdRestIp set to {}", bgpdRestIp);
+        }
+
+        routerId = context.getConfigParams(this).get("RouterId");
+        if (routerId == null) {
+            log.error("RouterId property not found in config file");
+            throw new ConfigurationRuntimeException(
+                    "RouterId property not found in config file");
+        } else {
+            log.info("RouterId set to {}", routerId);
+        }
+
+        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) {
+        restApi.addRestletRoutable(new SdnIpWebRoutable());
+        floodlightProvider.addOFSwitchListener(this);
+
+        // Retrieve the RIB from BGPd during startup
+        retrieveRib();
+    }
+
+    @Override
+    public IPatriciaTree<RibEntry> getPtree() {
+        return ptree;
+    }
+
+    @Override
+    public void clearPtree() {
+        ptree = new PatriciaTree<RibEntry>(32);
+    }
+
+    @Override
+    public String getBgpdRestIp() {
+        return bgpdRestIp;
+    }
+
+    @Override
+    public String getRouterId() {
+        return routerId;
+    }
+
+    private void retrieveRib() {
+        String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
+        String response = RestClient.get(url);
+
+        if ("".equals(response)) {
+            return;
+        }
+
+        response = response.replaceAll("\"", "'");
+        JSONObject jsonObj = (JSONObject) JSONSerializer.toJSON(response);
+        JSONArray ribArray = jsonObj.getJSONArray("rib");
+        String inboundRouterId = jsonObj.getString("router-id");
+
+        int size = ribArray.size();
+
+        log.info("Retrived RIB of {} entries from BGPd", size);
+
+        for (int j = 0; j < size; j++) {
+            JSONObject ribEntry = ribArray.getJSONObject(j);
+            String prefix = ribEntry.getString("prefix");
+            String nexthop = ribEntry.getString("nexthop");
+
+            // Insert each rib entry into the local rib
+            String[] substring = prefix.split("/");
+            String prefix1 = substring[0];
+            String mask1 = substring[1];
+
+            Prefix p;
+            try {
+                p = new Prefix(prefix1, Integer.valueOf(mask1));
+            } catch (NumberFormatException e) {
+                log.warn("Wrong mask format in RIB JSON: {}", mask1);
+                continue;
+            } catch (IllegalArgumentException e1) {
+                log.warn("Wrong prefix format in RIB JSON: {}", prefix1);
+                continue;
+            }
+
+            RibEntry rib = new RibEntry(inboundRouterId, nexthop);
+
+            try {
+                ribUpdates.put(new RibUpdate(Operation.UPDATE, p, rib));
+            } catch (InterruptedException e) {
+                log.debug("Interrupted while pushing onto update queue");
+            }
+        }
+    }
+
+    @Override
+    public void newRibUpdate(RibUpdate update) {
+        try {
+            ribUpdates.put(update);
+        } catch (InterruptedException e) {
+            log.debug("Interrupted while putting on ribUpdates queue", e);
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public void processRibAdd(RibUpdate update) {
+        synchronized (this) {
+            Prefix prefix = update.getPrefix();
+
+            log.debug("Processing prefix add {}", prefix);
+
+            RibEntry rib = ptree.put(prefix, update.getRibEntry());
+
+            if (rib != null && !rib.equals(update.getRibEntry())) {
+                // There was an existing nexthop for this prefix. This update supersedes that,
+                // so we need to remove the old flows for this prefix from the switches
+                executeDeletePrefix(prefix, rib);
+            }
+
+            if (update.getRibEntry().getNextHop().equals(
+                    InetAddresses.forString("0.0.0.0"))) {
+                // Route originated by SDN domain
+                // We don't handle these at the moment
+                log.debug("Own route {} to {}", prefix,
+                        update.getRibEntry().getNextHop().getHostAddress());
+                return;
+            }
+
+            executeRibAdd(update);
+        }
+    }
+
+    private void executeRibAdd(RibUpdate update) {
+        // TODO: Fix the code below. Note that "deviceStorage" was removed.
+
+        /*
+        Prefix prefix = update.getPrefix();
+        RibEntry rib = update.getRibEntry();
+
+        InetAddress dstIpAddress = rib.getNextHop();
+        MACAddress nextHopMacAddress = null;
+
+        // See if we know the MAC address of the next hop
+        // TODO if we do not treat the next hop as a device in the future, we need to update this
+        IDeviceObject nextHopDevice =
+                deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(dstIpAddress));
+
+        if (nextHopDevice == null){
+            log.debug("NextHopDevice for IP: {} is null", dstIpAddress);
+            prefixesWaitingOnArp.put(dstIpAddress,
+                    new RibUpdate(Operation.UPDATE, prefix, rib));
+            proxyArp.sendArpRequest(dstIpAddress, this, true);
+            return;
+
+        }
+        nextHopMacAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
+
+        // Find the attachment point (egress interface) of the next hop
+        Interface egressInterface = null;
+        if (bgpPeers.containsKey(dstIpAddress)) {
+            //Route to a peer
+            log.debug("Route to peer {}", dstIpAddress);
+            BgpPeer peer = bgpPeers.get(dstIpAddress);
+            egressInterface = interfaces.get(peer.getInterfaceName());
+        } else {
+            //Route to non-peer
+            log.debug("Route to non-peer {}", dstIpAddress);
+            egressInterface = interfacePtree.match(
+                    new Prefix(dstIpAddress.getAddress(), 32));
+            if (egressInterface == null) {
+                log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+                return;
+            }
+        }
+
+        if (nextHopMacAddress == null) {
+            prefixesWaitingOnArp.put(dstIpAddress,
+                    new RibUpdate(Operation.UPDATE, prefix, rib));
+            proxyArp.sendArpRequest(dstIpAddress, this, true);
+            return;
+        } else {
+            if (!bgpPeers.containsKey(dstIpAddress)) {
+                //If the prefix is for a non-peer we need to ensure there's a path,
+                //and push one if there isn't.
+                Path path = pushedPaths.get(dstIpAddress);
+                if (path == null) {
+                    path = new Path(egressInterface, dstIpAddress);
+                    calculateAndPushPath(path, nextHopMacAddress);
+                    pushedPaths.put(dstIpAddress, path);
+                }
+
+                path.incrementUsers();
+                prefixToPath.put(prefix, path);
+            }
+
+            //For all prefixes we need to add the first-hop mac-rewriting flows
+            addPrefixFlows(prefix, egressInterface, nextHopMacAddress);
+        }
+        */
+    }
+
+    /**
+     * Add a flow to match dst-IP prefix and rewrite MAC for one IP prefix
+     * to all other border switches.
+     */
+    private void addPrefixFlows(Prefix prefix, Interface egressInterface,
+                                MACAddress nextHopMacAddress) {
+        log.debug("Adding flows for prefix {}, next hop mac {}",
+                prefix, nextHopMacAddress);
+
+        FlowPath flowPath = new FlowPath();
+        flowPath.setInstallerId(new CallerId("SDNIP"));
+
+        // Set flowPath FlowPathType and FlowPathUserState
+        flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
+        flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+
+        // Insert dst-ip prefix based forwarding and MAC rewrite flow entry
+        // only to the first-host switches
+        FlowPathFlags flowPathFlags = new FlowPathFlags();
+        flowPathFlags.setFlags(FlowPathFlags.KEEP_ONLY_FIRST_HOP_ENTRY);
+        flowPath.setFlowPathFlags(flowPathFlags);
+
+        // Create the DataPath object: dstSwitchPort
+        SwitchPort dstPort =
+            new SwitchPort(new Dpid(egressInterface.getDpid()),
+                           new Port(egressInterface.getPort()));
+
+        // We only need one flow mod per switch, so pick one interface on each switch
+        Map<Long, Interface> srcInterfaces = new HashMap<Long, Interface>();
+        for (Interface intf : interfaces.values()) {
+            if (!srcInterfaces.containsKey(intf.getDpid())
+                    && !intf.equals(egressInterface)) {
+                srcInterfaces.put(intf.getDpid(), intf);
+            }
+        }
+        for (Interface srcInterface : srcInterfaces.values()) {
+
+            if (egressInterface.equals(srcInterface)) {
+                continue;
+            }
+
+            // Create flowPath FlowId
+            flowPath.setFlowId(new FlowId());
+
+            // Create DataPath object: srcSwitchPort
+            SwitchPort srcPort =
+                new SwitchPort(new Dpid(srcInterface.getDpid()),
+                               new Port(srcInterface.getPort()));
+
+            DataPath dataPath = new DataPath();
+            dataPath.setSrcPort(srcPort);
+            dataPath.setDstPort(dstPort);
+            flowPath.setDataPath(dataPath);
+
+            // Create flow path matching condition(s): IPv4 Prefix
+            FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+            flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
+            IPv4Net dstIPv4Net = new IPv4Net(prefix.toString());
+            flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            /*
+             * Create the Flow Entry Action(s): dst-MAC rewrite action
+             */
+            FlowEntryActions flowEntryActions = new FlowEntryActions();
+            FlowEntryAction flowEntryAction1 = new FlowEntryAction();
+            flowEntryAction1.setActionSetEthernetDstAddr(nextHopMacAddress);
+            // flowEntryAction1.actionSetEthernetDstAddr(nextHopMacAddress);
+            flowEntryActions.addAction(flowEntryAction1);
+            flowPath.setFlowEntryActions(flowEntryActions);
+
+            // Flow Path installation, only to first hop switches
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+                log.error("Failed to install flow path to the first hop for " +
+                        "prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
+                        nextHopMacAddress);
+            }
+            else {
+                log.debug("Successfully installed flow path to the first hop " +
+                        "for prefix: {}, nextHopMacAddress: {}", prefix.getAddress(),
+                        nextHopMacAddress);
+
+                pushedFlowIds.put(prefix, flowPath.flowId());
+            }
+            */
+        }
+    }
+
+    public void processRibDelete(RibUpdate update) {
+        synchronized (this) {
+            Prefix prefix = update.getPrefix();
+
+            if (ptree.remove(prefix, update.getRibEntry())) {
+                /*
+                 * Only delete flows if an entry was actually removed from the tree.
+                 * If no entry was removed, the <prefix, nexthop> wasn't there so
+                 * it's probably already been removed and we don't need to do anything
+                 */
+                executeDeletePrefix(prefix, update.getRibEntry());
+            }
+        }
+    }
+
+    private void executeDeletePrefix(Prefix prefix, RibEntry ribEntry) {
+        deletePrefixFlows(prefix);
+
+        log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
+
+        if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
+            log.debug("Getting path for route with non-peer nexthop");
+            Path path = prefixToPath.remove(prefix);
+
+            if (path != null) {
+                //path could be null if we added to the Ptree but didn't push
+                //flows yet because we were waiting to resolve ARP
+
+                path.decrementUsers();
+                if (path.getUsers() <= 0 && !path.isPermanent()) {
+                    deletePath(path);
+                    pushedPaths.remove(path.getDstIpAddress());
+                }
+            }
+        }
+    }
+
+    // TODO have not tested this module
+    private void deletePrefixFlows(Prefix prefix) {
+        log.debug("Deleting flows for prefix {}", prefix);
+
+        //
+        // TODO: Delete the flow by using the new Path Intent framework
+        // NOTE: During the refactoring of the code below, if obtaining
+        // the values of the removed flowIds is needed, the first
+        // removeAll() statement should be replaced with the second removeAll()
+        // statement.
+        //
+        pushedFlowIds.removeAll(prefix);
+        /*
+        Collection<FlowId> flowIds = pushedFlowIds.removeAll(prefix);
+        for (FlowId flowId : flowIds) {
+            if (log.isTraceEnabled()) {
+                //Trace the flow status by flowPath in the switch before deleting it
+                log.trace("Pushing a DELETE flow mod to flowPath : {}",
+                        flowManagerService.getFlow(flowId).toString());
+            }
+
+            if( flowManagerService.deleteFlow(flowId))
+            {
+                log.debug("Successfully deleted FlowId: {}",flowId);
+            }
+            else
+            {
+                log.debug("Failed to delete FlowId: {}",flowId);
+            }
+        }
+        */
+    }
+
+    // TODO need to record the path and then delete here
+    private void deletePath(Path path) {
+        log.debug("Deleting flows for path to {}",
+                path.getDstIpAddress().getHostAddress());
+
+        // TODO need update
+        /*for (PushedFlowMod pfm : path.getFlowMods()) {
+            if (log.isTraceEnabled()) {
+                log.trace("Pushing a DELETE flow mod to {}, dst MAC {}",
+                        new Object[] {HexString.toHexString(pfm.getDpid()),
+                        HexString.toHexString(pfm.getFlowMod().getMatch().getDataLayerDestination())
+                });
+            }
+
+            sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
+        }*/
+    }
+
+
+    //TODO test next-hop changes
+    //TODO check delete/add synchronization
+
+    /**
+     * On startup, we need to calculate a full mesh of paths between all gateway
+     * switches.
+     */
+    private void setupFullMesh() {
+        // TODO: Fix the code below. Note that "deviceStorage" was removed.
+
+        /*
+
+        //For each border router, calculate and install a path from every other
+        //border switch to said border router. However, don't install the entry
+        //in to the first hop switch, as we need to install an entry to rewrite
+        //for each prefix received. This will be done later when prefixes have
+        //actually been received.
+
+        for (BgpPeer peer : bgpPeers.values()) {
+            Interface peerInterface = interfaces.get(peer.getInterfaceName());
+
+            //We know there's not already a Path here pushed, because this is
+            //called before all other routing
+            Path path = new Path(peerInterface, peer.getIpAddress());
+            path.setPermanent();
+
+            //See if we know the MAC address of the peer. If not we can't
+            //do anything until we learn it
+            MACAddress macAddress = null;
+            IDeviceObject nextHopDevice =
+                    deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(peer.getIpAddress()));
+
+            if(nextHopDevice == null){
+                log.debug("There is no DeviceObject for {}", peer.getIpAddress().getHostAddress());
+                //Put in the pending paths list first
+                pathsWaitingOnArp.put(peer.getIpAddress(), path);
+                proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
+                continue;
+            }
+
+            macAddress = MACAddress.valueOf(nextHopDevice.getMACAddress());
+
+            if (macAddress == null) {
+                log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
+                //Put in the pending paths list first
+                pathsWaitingOnArp.put(peer.getIpAddress(), path);
+                proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
+                continue;
+            }
+
+            //If we know the MAC, lets go ahead and push the paths to this peer
+            calculateAndPushPath(path, macAddress);
+        }
+        */
+    }
+
+    private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
+        Interface dstInterface = path.getDstInterface();
+
+        log.debug("Setting up path to {}, {}", path.getDstIpAddress().getHostAddress(),
+                dstMacAddress);
+
+        FlowPath flowPath = new FlowPath();
+
+        flowPath.setInstallerId(new CallerId("SDNIP"));
+
+        // Set flowPath FlowPathType and FlowPathUserState
+        flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
+        flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+
+        // Insert the dest-mac based forwarding flow entry to the non-first-hop switches
+        FlowPathFlags flowPathFlags = new FlowPathFlags();
+        flowPathFlags.setFlags(FlowPathFlags.DISCARD_FIRST_HOP_ENTRY);
+        flowPath.setFlowPathFlags(flowPathFlags);
+
+        // Create the DataPath object: dstSwitchPort
+        SwitchPort dstPort =
+            new SwitchPort(new Dpid(dstInterface.getDpid()),
+                           new Port(dstInterface.getPort()));
+
+        for (Interface srcInterface : interfaces.values()) {
+
+            if (dstInterface.equals(srcInterface)) {
+                continue;
+            }
+
+            // Create flowPath FlowId
+            flowPath.setFlowId(new FlowId());
+
+            // Create the DataPath object: srcSwitchPort
+            SwitchPort srcPort =
+                new SwitchPort(new Dpid(srcInterface.getDpid()),
+                               new Port(srcInterface.getPort()));
+
+            DataPath dataPath = new DataPath();
+            dataPath.setSrcPort(srcPort);
+            dataPath.setDstPort(dstPort);
+            flowPath.setDataPath(dataPath);
+
+            // Create the Flow Path Match condition(s)
+            FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+            flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
+            flowEntryMatch.enableDstMac(dstMacAddress);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            // NOTE: No need to add ACTION_OUTPUT. It is implied when creating
+            // Shortest Path Flow, and is always the last action for the Flow Entries
+            log.debug("FlowPath of MAC based forwarding: {}", flowPath.toString());
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+                log.error("Failed to set up MAC based forwarding path to {}, {}",
+                        path.getDstIpAddress().getHostAddress(),dstMacAddress);
+            }
+            else {
+                log.debug("Successfully set up MAC based forwarding path to {}, {}",
+                        path.getDstIpAddress().getHostAddress(),dstMacAddress);
+            }
+            */
+        }
+    }
+
+    /**
+     * Proactively install all BGP traffic paths from BGP host attachment point
+     * in SDN network to all the virtual gateways to BGP peers in other networks.
+     */
+    private void setupBgpPaths() {
+
+        for (BgpPeer bgpPeer : bgpPeers.values()) {
+
+            FlowPath flowPath = new FlowPath();
+            flowPath.setInstallerId(new CallerId("SDNIP"));
+
+            // Set flowPath FlowPathType and FlowPathUserState
+            flowPath.setFlowPathType(FlowPathType.FP_TYPE_SHORTEST_PATH);
+            flowPath.setFlowPathUserState(FlowPathUserState.FP_USER_ADD);
+
+            // Install flow paths between BGPd and its peers
+            // There is no need to set the FlowPathFlags
+            flowPath.setFlowPathFlags(new FlowPathFlags(0));
+
+            Interface peerInterface = interfaces.get(bgpPeer.getInterfaceName());
+
+            // Create the Flow Path Match condition(s)
+            FlowEntryMatch flowEntryMatch = new FlowEntryMatch();
+            flowEntryMatch.enableEthernetFrameType(Ethernet.TYPE_IPV4);
+
+            // Match both source address and dest address
+            IPv4Net dstIPv4Net = new IPv4Net(bgpPeer.getIpAddress().getHostAddress() + "/32");
+            flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
+
+            IPv4Net srcIPv4Net = new IPv4Net(peerInterface.getIpAddress().getHostAddress() + "/32");
+            flowEntryMatch.enableSrcIPv4Net(srcIPv4Net);
+
+            // Match TCP protocol
+            flowEntryMatch.enableIpProto(IPv4.PROTOCOL_TCP);
+
+            // Match destination TCP port
+            flowEntryMatch.enableDstTcpUdpPort(BGP_PORT);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            /**
+             * Create the DataPath: BGP -> BGP peer
+             */
+            // Flow path for src-TCP-port
+            DataPath dataPath = new DataPath();
+
+            SwitchPort srcPort =
+                new SwitchPort(bgpdAttachmentPoint.dpid(),
+                               bgpdAttachmentPoint.port());
+            dataPath.setSrcPort(srcPort);
+
+            SwitchPort dstPort =
+                new SwitchPort(new Dpid(peerInterface.getDpid()),
+                               new Port(peerInterface.getSwitchPort().port()));
+            dataPath.setDstPort(dstPort);
+
+            flowPath.setDataPath(dataPath);
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+                log.error("Failed to set up path BGP -> peer {}"+"; dst-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully set up path BGP -> peer {}"+"; dst-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+
+            // Disable dst-TCP-port, and set src-TCP-port
+            flowEntryMatch.disableDstTcpUdpPort();
+            flowEntryMatch.enableSrcTcpUdpPort(BGP_PORT);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            // Create a new FlowId
+            flowPath.setFlowId(new FlowId());
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+                log.error("Failed to set up path BGP -> Peer {}" + "; src-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully set up path BGP -> Peer {}" + "; src-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+
+            /**
+             * Create the DataPath: BGP <-BGP peer
+             */
+            // Reversed BGP flow path for src-TCP-port
+            flowPath.setFlowId(new FlowId());
+
+            DataPath reverseDataPath = new DataPath();
+
+            SwitchPort reverseDstPort =
+                new SwitchPort(bgpdAttachmentPoint.dpid(),
+                               bgpdAttachmentPoint.port());
+            reverseDataPath.setDstPort(reverseDstPort);
+
+            SwitchPort reverseSrcPort =
+                new SwitchPort(new Dpid(peerInterface.getDpid()),
+                               new Port(peerInterface.getSwitchPort().port()));
+            reverseDataPath.setSrcPort(reverseSrcPort);
+            flowPath.setDataPath(reverseDataPath);
+
+            // Reverse the dst IP and src IP addresses
+            flowEntryMatch.enableDstIPv4Net(srcIPv4Net);
+            flowEntryMatch.enableSrcIPv4Net(dstIPv4Net);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            log.debug("Reversed BGP FlowPath: {}", flowPath.toString());
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+
+                log.error("Failed to set up path BGP <- Peer {}" + "; src-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully set up path BGP <- Peer {}" + "; src-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+
+            // Reversed BGP flow path for dst-TCP-port
+            flowPath.setFlowId(new FlowId());
+
+            // Disable src-TCP-port, and set the dst-TCP-port
+            flowEntryMatch.disableSrcTcpUdpPort();
+            flowEntryMatch.enableDstTcpUdpPort(BGP_PORT);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            log.debug("Reversed BGP FlowPath: {}", flowPath.toString());
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+                log.error("Failed to setting up path BGP <- Peer {}" + "; dst-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully setting up path BGP <- Peer {}" + "; dst-TCP-port:179",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+
+            /**
+             * ICMP paths between BGPd and its peers
+             */
+            // match ICMP protocol BGP <- Peer
+            flowPath.setFlowId(new FlowId());
+
+            flowEntryMatch.enableIpProto(IPv4.PROTOCOL_ICMP);
+            flowEntryMatch.disableSrcTcpUdpPort();
+            flowEntryMatch.disableDstTcpUdpPort();
+
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            flowPath.setDataPath(reverseDataPath);
+
+            log.debug("Reversed ICMP FlowPath: {}", flowPath.toString());
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+
+                log.error("Failed to set up ICMP path BGP <- Peer {}",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully set up ICMP path BGP <- Peer {}",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+
+            // match ICMP protocol BGP -> Peer
+            flowPath.setFlowId(new FlowId());
+
+            flowEntryMatch.enableDstIPv4Net(dstIPv4Net);
+            flowEntryMatch.enableSrcIPv4Net(srcIPv4Net);
+            flowPath.setFlowEntryMatch(flowEntryMatch);
+
+            flowPath.setDataPath(dataPath);
+
+            log.debug("ICMP flowPath: {}", flowPath.toString());
+
+            // TODO: Add the flow by using the new Path Intent framework
+            /*
+            if (flowManagerService.addFlow(flowPath) == null) {
+
+                log.error("Failed to set up ICMP path BGP -> Peer {}",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            else {
+                log.debug("Successfully set up ICMP path BGP -> Peer {}",
+                        bgpPeer.getIpAddress().getHostAddress());
+            }
+            */
+        }
+    }
+
+    @Override
+    public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
+        log.debug("Received ARP response: {} => {}",
+                ipAddress.getHostAddress(), macAddress);
+
+        /*
+         * We synchronize on this to prevent changes to the ptree while we're pushing
+         * flows to the switches. If the ptree changes, the ptree and switches
+         * could get out of sync.
+         */
+        synchronized (this) {
+            Path path = pathsWaitingOnArp.remove(ipAddress);
+
+            if (path != null) {
+                log.debug("Pushing path to {} at {} on {}", new Object[]{
+                        path.getDstIpAddress().getHostAddress(), macAddress,
+                        path.getDstInterface().getSwitchPort()});
+                // These paths should always be to BGP peers. Paths to non-peers are
+                // handled once the first prefix is ready to push
+                if (pushedPaths.containsKey(path.getDstIpAddress())) {
+                    // A path already got pushed to this endpoint while we were waiting
+                    // for ARP. We'll copy over the permanent attribute if it is set on this path.
+                    if (path.isPermanent()) {
+                        pushedPaths.get(path.getDstIpAddress()).setPermanent();
+                    }
+                } else {
+                    calculateAndPushPath(path, macAddress);
+                    pushedPaths.put(path.getDstIpAddress(), path);
+                }
+            }
+
+            Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+
+            for (RibUpdate update : prefixesToPush) {
+                // These will always be adds
+
+                RibEntry rib = ptree.lookup(update.getPrefix());
+                if (rib != null && rib.equals(update.getRibEntry())) {
+                    log.debug("Pushing prefix {} next hop {}", update.getPrefix(),
+                            rib.getNextHop().getHostAddress());
+                    // We only push prefix flows if the prefix is still in the ptree
+                    // and the next hop is the same as our update. The prefix could
+                    // have been removed while we were waiting for the ARP, or the
+                    // next hop could have changed.
+                    executeRibAdd(update);
+                } else {
+                    log.debug("Received ARP response, but {},{} is no longer in ptree",
+                            update.getPrefix(), update.getRibEntry());
+                }
+            }
+        }
+    }
+
+    // TODO wait the priority module of the flow Manager
+    private void setupArpFlows() {
+        OFMatch match = new OFMatch();
+        match.setDataLayerType(Ethernet.TYPE_ARP);
+        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+
+        OFFlowMod fm = new OFFlowMod();
+        fm.setMatch(match);
+
+        OFActionOutput action = new OFActionOutput();
+        action.setPort(OFPort.OFPP_CONTROLLER.getValue());
+        action.setMaxLength((short) 0xffff);
+        List<OFAction> actions = new ArrayList<OFAction>(1);
+        actions.add(action);
+        fm.setActions(actions);
+
+        fm.setIdleTimeout((short) 0)
+                .setHardTimeout((short) 0)
+                .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+                .setCookie(0)
+                .setCommand(OFFlowMod.OFPFC_ADD)
+                .setPriority(ARP_PRIORITY)
+                .setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+
+        for (String strdpid : switches) {
+            flowCache.write(HexString.toLong(strdpid), fm);
+        }
+    }
+
+    // TODO need update, waiting for the priority feature from flow Manager
+    private void setupDefaultDropFlows() {
+        OFFlowMod fm = new OFFlowMod();
+        fm.setMatch(new OFMatch());
+        fm.setActions(new ArrayList<OFAction>()); // No action means drop
+
+        fm.setIdleTimeout((short) 0)
+                .setHardTimeout((short) 0)
+                .setBufferId(OFPacketOut.BUFFER_ID_NONE)
+                .setCookie(0)
+                .setCommand(OFFlowMod.OFPFC_ADD)
+                .setPriority((short) 0)
+                .setLengthU(OFFlowMod.MINIMUM_LENGTH);
+
+        OFFlowMod fmLLDP;
+        OFFlowMod fmBDDP;
+        try {
+            fmLLDP = fm.clone();
+            fmBDDP = fm.clone();
+        } catch (CloneNotSupportedException e1) {
+            log.error("Error cloning flow mod", e1);
+            return;
+        }
+
+        OFMatch matchLLDP = new OFMatch();
+        matchLLDP.setDataLayerType((short) 0x88cc);
+        matchLLDP.setWildcards(matchLLDP.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+        fmLLDP.setMatch(matchLLDP);
+
+        OFMatch matchBDDP = new OFMatch();
+        matchBDDP.setDataLayerType((short) 0x8942);
+        matchBDDP.setWildcards(matchBDDP.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
+        fmBDDP.setMatch(matchBDDP);
+
+        OFActionOutput action = new OFActionOutput();
+        action.setPort(OFPort.OFPP_CONTROLLER.getValue());
+        action.setMaxLength((short) 0xffff);
+        List<OFAction> actions = new ArrayList<OFAction>(1);
+        actions.add(action);
+
+        fmLLDP.setActions(actions);
+        fmBDDP.setActions(actions);
+
+        fmLLDP.setPriority(ARP_PRIORITY);
+        fmLLDP.setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+        fmBDDP.setPriority(ARP_PRIORITY);
+        fmBDDP.setLengthU(OFFlowMod.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH);
+
+        List<OFFlowMod> flowModList = new ArrayList<OFFlowMod>(3);
+        flowModList.add(fm);
+        flowModList.add(fmLLDP);
+        flowModList.add(fmBDDP);
+
+        for (String strdpid : switches) {
+            flowCache.write(HexString.toLong(strdpid), flowModList);
+        }
+    }
+
+    private void beginRouting() {
+        log.debug("Topology is now ready, beginning routing function");
+        // TODO: Fix for the new Topology Network Graph
+        // topology = topologyNetService.newDatabaseTopology();
+
+        // Wait Pavlin's API. We need the following functions.
+        /*setupArpFlows();
+        setupDefaultDropFlows();*/
+
+        setupBgpPaths();
+        setupFullMesh();
+
+        //Suppress link discovery on external-facing router ports
+        for (Interface intf : interfaces.values()) {
+            linkDiscoveryService.addToSuppressLLDPs(intf.getDpid(), intf.getPort());
+        }
+
+        bgpUpdatesExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                doUpdatesThread();
+            }
+        });
+    }
+
+    // Before inserting the paths for BGP traffic, we should check whether
+    // all the switches in the configuration file are discovered by ONOS
+    private void checkSwitchesConnected() {
+        // TODO: Fix the code below after topoSwitchSerice was removed
+        /*
+        for (String dpid : switches) {
+
+            Iterator<ISwitchObject> activeSwitches = topoSwitchService.
+                    getActiveSwitches().iterator();
+            while(activeSwitches.hasNext())
+            {
+                ISwitchObject switchObject = activeSwitches.next();
+                if (switchObject.getDPID().equals(dpid)) {
+                    break;
+                }
+                if(activeSwitches.hasNext() == false) {
+                    log.debug("Not all switches are here yet");
+                    return;
+                }
+            }
+        }
+        switchesConnected = true;
+        */
+    }
+
+    // Actually we only need to go half way round to verify full mesh connectivity
+    private void checkTopologyReady() {
+        for (Interface dstInterface : interfaces.values()) {
+            for (Interface srcInterface : interfaces.values()) {
+                if (dstInterface.equals(srcInterface)) {
+                    continue;
+                }
+
+                // TODO: Fix for the new Topology Network Graph
+                /*
+                DataPath shortestPath = topologyNetService.getDatabaseShortestPath(
+                        srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+
+                if (shortestPath == null){
+                    log.debug("Shortest path between {} and {} not found",
+                            srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
+                    return;
+                }
+                */
+            }
+        }
+        topologyReady = true;
+    }
+
+    private void checkStatus() {
+        if (!switchesConnected) {
+            checkSwitchesConnected();
+        }
+        boolean oldTopologyReadyStatus = topologyReady;
+        if (switchesConnected && !topologyReady) {
+            checkTopologyReady();
+        }
+        if (!oldTopologyReadyStatus && topologyReady) {
+            beginRouting();
+        }
+    }
+
+    private void doUpdatesThread() {
+        boolean interrupted = false;
+        try {
+            while (true) {
+                try {
+                    RibUpdate update = ribUpdates.take();
+                    switch (update.getOperation()) {
+                        case UPDATE:
+                            if (validateUpdate(update)) {
+                                processRibAdd(update);
+                            } else {
+                                log.debug("Rib UPDATE out of order: {} via {}",
+                                        update.getPrefix(), update.getRibEntry().getNextHop());
+                            }
+                            break;
+                        case DELETE:
+                            if (validateUpdate(update)) {
+                                processRibDelete(update);
+                            } else {
+                                log.debug("Rib DELETE out of order: {} via {}",
+                                        update.getPrefix(), update.getRibEntry().getNextHop());
+                            }
+                            break;
+                        default:
+                            log.error("Unknown operation {}", update.getOperation());
+                            break;
+                    }
+                } catch (InterruptedException e) {
+                    log.debug("Interrupted while taking from updates queue", e);
+                    interrupted = true;
+                } catch (Exception e) {
+                    log.debug("exception", e);
+                }
+            }
+        } finally {
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    private boolean validateUpdate(RibUpdate update) {
+        RibEntry newEntry = update.getRibEntry();
+        RibEntry oldEntry = ptree.lookup(update.getPrefix());
+
+        //If there is no existing entry we must assume this is the most recent
+        //update. However this might not always be the case as we might have a
+        //POST then DELETE reordering.
+        //if (oldEntry == null || !newEntry.getNextHop().equals(oldEntry.getNextHop())) {
+        if (oldEntry == null) {
+            return true;
+        }
+
+        // This handles the case where routes are gathered in the initial
+        // request because they don't have sequence number info
+        if (newEntry.getSysUpTime() == -1 && newEntry.getSequenceNum() == -1) {
+            return true;
+        }
+
+        if (newEntry.getSysUpTime() > oldEntry.getSysUpTime()) {
+            return true;
+        }
+
+        return newEntry.getSysUpTime() == oldEntry.getSysUpTime() &&
+               newEntry.getSequenceNum() > oldEntry.getSequenceNum();
+    }
+
+    // The code below should be reimplemented after removal of Floodlight's
+    // ITopologyService API. It should be implemented on top of network graph
+    // notifications. (It was pretty hacky anyway...)
+    /*
+    @Override
+    public void topologyChanged() {
+        if (topologyReady) {
+            return;
+        }
+
+        boolean refreshNeeded = false;
+        for (LDUpdate ldu : topologyService.getLastLinkUpdates()){
+            if (!ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_UPDATED)){
+                //We don't need to recalculate anything for just link updates
+                //They happen very frequently
+                refreshNeeded = true;
+            }
+
+            log.debug("Topo change {}", ldu.getOperation());
+
+            if (ldu.getOperation().equals(ILinkDiscovery.UpdateOperation.LINK_ADDED)){
+                synchronized (linkUpdates) {
+                    linkUpdates.add(ldu);
+                }
+            }
+        }
+
+        if (refreshNeeded && !topologyReady){
+            topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
+        }
+    }
+    */
+
+    @Override
+    public void addedSwitch(IOFSwitch sw) {
+        if (!topologyReady) {
+            sw.clearAllFlowMods();
+        }
+
+        flowCache.switchConnected(sw);
+    }
+
+    @Override
+    public void removedSwitch(IOFSwitch sw) {
+        // Not used
+    }
+
+    @Override
+    public void switchPortChanged(Long switchId) {
+        // Not used
+    }
+
+    @Override
+    public String getName() {
+        return "SdnIp";
+    }
+
+    /*
+     * IConfigInfoService methods
+     */
+
+    @Override
+    public boolean isInterfaceAddress(InetAddress address) {
+        Interface intf = interfacePtree.match(new Prefix(address.getAddress(), 32));
+        return (intf != null && intf.getIpAddress().equals(address));
+    }
+
+    @Override
+    public boolean inConnectedNetwork(InetAddress address) {
+        Interface intf = interfacePtree.match(new Prefix(address.getAddress(), 32));
+        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 interfacePtree.match(new Prefix(dstIpAddress.getAddress(), 32));
+    }
+
+    @Override
+    public boolean hasLayer3Configuration() {
+        return !interfaces.isEmpty();
+    }
+
+    @Override
+    public MACAddress getRouterMacAddress() {
+        return bgpdMacAddress;
+    }
+
+    @Override
+    public short getVlan() {
+        return vlan;
+    }
+}