Merge branch 'routeserver'
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index c36d4a5..278fb86 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -19,7 +19,6 @@
 
 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;
@@ -71,11 +70,11 @@
 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 BgpRoute implements IFloodlightModule, IBgpRouteService, 
-									ITopologyListener, IOFSwitchListener,
-									IArpRequester {
+									ITopologyListener, IArpRequester {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
@@ -87,8 +86,8 @@
 	
 	protected ProxyArpManager proxyArp;
 	
-	//protected static Ptree ptree;
-	protected IPatriciaTrie ptree;
+	protected IPatriciaTrie<RibEntry> ptree;
+	protected IPatriciaTrie<Interface> interfacePtrie;
 	protected BlockingQueue<RibUpdate> ribUpdates;
 	
 	protected String bgpdRestIp;
@@ -127,30 +126,15 @@
 	protected SingletonTask topologyChangeDetectorTask;
 	
 	protected SetMultimap<InetAddress, RibUpdate> prefixesWaitingOnArp;
-	protected SetMultimap<InetAddress, PathUpdate> pathsWaitingOnArp;
+	
+	protected Map<InetAddress, Path> pathsWaitingOnArp;
 	
 	protected ExecutorService bgpUpdatesExecutor;
 	
+	protected Map<InetAddress, Path> pushedPaths;
+	protected Map<Prefix, Path> prefixToPath;
 	protected Multimap<Prefix, PushedFlowMod> pushedFlows;
-	
-	private class PushedFlowMod {
-		private long dpid;
-		private OFFlowMod flowMod;
 		
-		public PushedFlowMod(long dpid, OFFlowMod flowMod) {
-			this.dpid = dpid;
-			this.flowMod = flowMod;
-		}
-		
-		public long getDpid() {
-			return dpid;
-		}
-		
-		public OFFlowMod getFlowMod() {
-			return flowMod;
-		}
-	}
-	
 	protected class TopologyChangeDetector implements Runnable {
 		@Override
 		public void run() {
@@ -219,6 +203,12 @@
 			log.error("Error reading JSON file", e);
 			System.exit(1);
 		}
+		
+		//Populate the interface Patricia Trie
+		for (Interface intf : interfaces.values()) {
+			Prefix prefix = new Prefix(intf.getIpAddress().getAddress(), intf.getPrefixLength());
+			interfacePtrie.put(prefix, intf);
+		}
 	}
 	
 	@Override
@@ -252,8 +242,8 @@
 	public void init(FloodlightModuleContext context)
 			throws FloodlightModuleException {
 	    
-	    //ptree = new Ptree(32);
-		ptree = new PatriciaTrie(32);
+		ptree = new PatriciaTrie<RibEntry>(32);
+		interfacePtrie = new PatriciaTrie<Interface>(32);
 	    
 	    ribUpdates = new LinkedBlockingQueue<RibUpdate>();
 	    	
@@ -273,11 +263,12 @@
 
 		topoRouteService = new TopoRouteService("");
 		
-		pathsWaitingOnArp = Multimaps.synchronizedSetMultimap(
-				HashMultimap.<InetAddress, PathUpdate>create());
+		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();
 		
 		bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
@@ -309,19 +300,25 @@
 		log.debug("Config file set to {}", configFilename);
 		
 		readGatewaysConfiguration(configFilename);
-		// Test.
-		//test();
+	}
+	
+	@Override
+	public void startUp(FloodlightModuleContext context) {
+		restApi.addRestletRoutable(new BgpRouteWebRoutable());
+		topology.addListener(this);
+		
+		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
+		
+		//Retrieve the RIB from BGPd during startup
+		retrieveRib();
 	}
 
-	//public Ptree getPtree() {
-	public IPatriciaTrie getPtree() {
+	public IPatriciaTrie<RibEntry> getPtree() {
 		return ptree;
 	}
 	
 	public void clearPtree() {
-		//ptree = null;
-		//ptree = new Ptree(32);
-		ptree = new PatriciaTrie(32);
+		ptree = new PatriciaTrie<RibEntry>(32);
 	}
 	
 	public String getBGPdRestIp() {
@@ -332,105 +329,6 @@
 		return routerId;
 	}
 	
-	// Return nexthop address as byte array.
-	/*
-	public RibEntry lookupRib(byte[] dest) {
-		if (ptree == null) {
-		    log.debug("lookupRib: ptree null");
-		    return null;
-		}
-		
-		PtreeNode node = ptree.match(dest, 32);
-		if (node == null) {
-            log.debug("lookupRib: ptree node null");
-			return null;
-		}
-		
-		if (node.rib == null) {
-            log.debug("lookupRib: ptree rib null");
-			return null;
-		}
-		
-		ptree.delReference(node);
-		
-		return node.rib;
-	}
-	*/
-	
-	/*
-	//TODO looks like this should be a unit test
-	@SuppressWarnings("unused")
-    private void test() throws UnknownHostException {
-		System.out.println("Here it is");
-		Prefix p = new Prefix("128.0.0.0", 8);
-		Prefix q = new Prefix("8.0.0.0", 8);
-		Prefix r = new Prefix("10.0.0.0", 24);
-		Prefix a = new Prefix("10.0.0.1", 32);
-	
-		ptree.acquire(p.getAddress(), p.getPrefixLength());
-		ptree.acquire(q.getAddress(), q.getPrefixLength());
-		ptree.acquire(r.getAddress(), r.getPrefixLength());
-	
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-	
-		PtreeNode n = ptree.match(a.getAddress(), a.getPrefixLength());
-		if (n != null) {
-			System.out.println("Matched prefix for 10.0.0.1:");
-			Prefix x = new Prefix(n.key, n.keyBits);
-			ptree.delReference(n);
-		}
-		
-		n = ptree.lookup(p.getAddress(), p.getPrefixLength());
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-		
-		n = ptree.lookup(q.getAddress(), q.getPrefixLength());
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-		
-		n = ptree.lookup(r.getAddress(), r.getPrefixLength());
-		if (n != null) {
-			ptree.delReference(n);
-			ptree.delReference(n);
-		}
-		System.out.println("Traverse start");
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)) {
-			Prefix p_result = new Prefix(node.key, node.keyBits);
-		}
-
-	}
-	*/
-	
-	//TODO once the Ptree is object oriented this can go
-	/*
-	private String getPrefixFromPtree(PtreeNode node){
-        InetAddress address = null;
-        try {
-			address = InetAddress.getByAddress(node.key);
-		} catch (UnknownHostException e1) {
-			//Should never happen is the reverse conversion has already been done
-			log.error("Malformed IP address");
-			return "";
-		}
-        return address.toString() + "/" + node.rib.masklen;
-	}
-	*/
-	
 	private void retrieveRib(){
 		String url = "http://" + bgpdRestIp + "/wm/bgp/" + routerId;
 		String response = RestClient.get(url);
@@ -469,21 +367,8 @@
 				continue;
 			}
 			
-			//PtreeNode node = ptree.acquire(p.getAddress(), p.getPrefixLength());
 			RibEntry rib = new RibEntry(router_id, nexthop);
-			
-			/*
-			if (node.rib != null) {
-				node.rib = null;
-				ptree.delReference(node);
-			}
-			
-			node.rib = rib;
-			*/
-			
-			//ptree.put(p, rib);
-			
-			//addPrefixFlows(p, rib);
+
 			try {
 				ribUpdates.put(new RibUpdate(Operation.UPDATE, p, rib));
 			} catch (InterruptedException e) {
@@ -497,8 +382,8 @@
 		try {
 			ribUpdates.put(update);
 		} catch (InterruptedException e) {
-			// TODO Auto-generated catch block
-			log.debug(" ", e);
+			log.debug("Interrupted while putting on ribUpdates queue", e);
+			Thread.currentThread().interrupt();
 		}
 	}
 	
@@ -507,140 +392,105 @@
 		
 		log.debug("Processing prefix add {}", prefix);
 		
-		//PtreeNode node = ptree.acquire(prefix.getAddress(), prefix.getPrefixLength());
 		RibEntry rib = ptree.put(prefix, update.getRibEntry());
 		
-		//if (node.rib != null) {
 		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
-			deletePrefixFlows(prefix);
-			
-			//Then remove the old nexthop from the Ptree
-			//node.rib = null;
-			//ptree.delReference(node);
+			_processDeletePrefix(prefix, rib);
 		}
 		
-		//Put the new nexthop in the Ptree
-		//node.rib = update.getRibEntry();
-
-		//Push flows for the new <prefix, nexthop>
-		addPrefixFlows(prefix, update.getRibEntry());
+		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;
+		}
+		
+		_processRibAdd(update);
 	}
 	
-	public synchronized void processRibDelete(RibUpdate update) {
+	private void _processRibAdd(RibUpdate update) {
 		Prefix prefix = update.getPrefix();
+		RibEntry rib = update.getRibEntry();
 		
-		//PtreeNode node = ptree.lookup(prefix.getAddress(), prefix.getPrefixLength());
+		InetAddress dstIpAddress = rib.getNextHop();
 		
-		/* 
-		 * Remove the flows from the switches before the rib is lost
-		 * Theory: we could get a delete for a prefix not in the Ptree.
-		 * This would result in a null node being returned. We could get a delete for
-		 * a node that's not actually there, but is a aggregate node. This would result
-		 * in a non-null node with a null rib. Only a non-null node with a non-null
-		 * rib is an actual prefix in the Ptree.
-		 */
-
-		/*
-		if (node != null && node.rib != null) {
-			if (update.getRibEntry().equals(node.rib)) {
-				node.rib = null;
-				ptree.delReference(node);
-				
-				deletePrefixFlows(update.getPrefix());
+		//See if we know the MAC address of the next hop
+		byte[] nextHopMacAddress = proxyArp.getMacAddress(rib.getNextHop());
+		
+		//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 = interfacePtrie.match(
+					new Prefix(dstIpAddress.getAddress(), 32));
+			if (egressInterface == null) {
+				log.warn("No outgoing interface found for {}", dstIpAddress.getHostAddress());
+				return;
 			}
 		}
-		*/
 		
-		if (ptree.remove(prefix, update.getRibEntry())) {
-			/*
-			 * Only delete flows if an entry was actually removed from the trie.
-			 * 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
-			 */
-			deletePrefixFlows(prefix);
+		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);
+					setUpDataPath(path, MACAddress.valueOf(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);
 		}
 	}
 	
-	//TODO compatibility layer, used by beginRouting()
-	/*public void prefixAdded(PtreeNode node) {
-		Prefix prefix = null;
-		try {
-			prefix = new Prefix(node.key, node.rib.masklen);
-		} catch (IllegalArgumentException e) {
-			log.error(" ", e);
-		}
+	private void addPrefixFlows(Prefix prefix, Interface egressInterface, byte[] nextHopMacAddress) {		
+		log.debug("Adding flows for prefix {} added, next hop mac {}",
+				prefix, HexString.toHexString(nextHopMacAddress));
 
-		addPrefixFlows(prefix, node.rib);
-	}*/
-
-	private void addPrefixFlows(Prefix prefix, RibEntry rib) {
-		if (!topologyReady){
-			return;
-		}
-		
-		//TODO before we do anything, we have to check that the RIB entry is still in the
-		//Ptree because it could have been removed while we were waiting for ARP.
-		//I think we'll have to make prefixAdded and prefixDelete atomic as well
-		//to protect against the prefix getting deleted while where trying to add it
-
-		log.debug("New prefix {} added, next hop {}", 
-				prefix, rib.getNextHop().getHostAddress());
-		
-		//TODO this is wrong, we shouldn't be dealing with BGP peers here.
-		//We need to figure out where the device is attached and what its
-		//mac address is by learning. 
-		//The next hop is not necessarily the peer, and the peer's attachment
-		//point is not necessarily the next hop's attachment point.
-		BgpPeer peer = bgpPeers.get(rib.getNextHop());
-		
-		if (peer == null){
-			//TODO local router isn't in peers list so this will get thrown
-			//Need to work out what to do about local prefixes with next hop 0.0.0.0.
-			
-			//The other scenario is this is a route server route. In that
-			//case the next hop is not in our configuration
-			log.error("Couldn't find next hop router in router {} in config",
-					rib.getNextHop().getHostAddress());
-			return; //just quit out here? This is probably a configuration error
-		}
-		
-		//Get MAC address for peer from the ARP module
-		//TODO separate out the 'ask for MAC' bit to another method
-		byte[] peerMacAddress = proxyArp.getMacAddress(peer.getIpAddress());
-		if (peerMacAddress == null) {
-			//A RibUpdate is still a nice way to package them up
-			prefixesWaitingOnArp.put(peer.getIpAddress(), 
-					new RibUpdate(Operation.UPDATE, prefix, rib));
-			proxyArp.sendArpRequest(peer.getIpAddress(), this, true);
-			return;
-		}
-		
-		Interface peerInterface = interfaces.get(peer.getInterfaceName());
-
-		//Add a flow to rewrite mac for this prefix to all border switches
+		//Add a flow to rewrite mac for this prefix to all other border switches
 		for (Interface srcInterface : interfaces.values()) {
-			if (srcInterface == peerInterface) {
+			if (srcInterface == egressInterface) {
 				//Don't push a flow for the switch where this peer is attached
 				continue;
 			}
 						
 			DataPath shortestPath = topoRouteService.getShortestPath(
 					srcInterface.getSwitchPort(),
-					peerInterface.getSwitchPort());
+					egressInterface.getSwitchPort());
 			
 			if (shortestPath == null){
 				log.debug("Shortest path between {} and {} not found",
 						srcInterface.getSwitchPort(),
-						peerInterface.getSwitchPort());
+						egressInterface.getSwitchPort());
 				return; // just quit here?
 			}
 			
 			//Set up the flow mod
-			OFFlowMod fm =
-	                (OFFlowMod) floodlightProvider.getOFMessageFactory()
-	                                              .getMessage(OFType.FLOW_MOD);
+			OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory()
+					.getMessage(OFType.FLOW_MOD);
 			
 	        fm.setIdleTimeout((short)0)
 	        .setHardTimeout((short)0)
@@ -656,30 +506,16 @@
 	        match.setDataLayerType(Ethernet.TYPE_IPv4);
 	        match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
 	        
-	        /*
-	        InetAddress address = null;
-	        try {
-	        	address = InetAddress.getByAddress(prefix.getAddress());
-			} catch (UnknownHostException e1) {
-				//Should never happen is the reverse conversion has already been done
-				log.error("Malformed IP address");
-				return;
-			}*/
-	        
-	        //match.setFromCIDR(address.getHostAddress() + "/" + 
-	        //		prefix.getPrefixLength(), OFMatch.STR_NW_DST);
 	        match.setFromCIDR(prefix.toString(), OFMatch.STR_NW_DST);
 	        fm.setMatch(match);
 	        
 	        //Set up MAC rewrite action
 	        OFActionDataLayerDestination macRewriteAction = new OFActionDataLayerDestination();
-	        //TODO the peer's mac address is not necessarily the next hop's...
-	        macRewriteAction.setDataLayerAddress(peerMacAddress);
+	        macRewriteAction.setDataLayerAddress(nextHopMacAddress);
 	        
 	        //Set up output action
 	        OFActionOutput outputAction = new OFActionOutput();
 	        outputAction.setMaxLength((short)0xffff);
-	        
 	        Port outputPort = shortestPath.flowEntries().get(0).outPort();
 	        outputAction.setPort(outputPort.value());
 	        
@@ -697,7 +533,6 @@
             	continue;
             }
             
-            //TODO if prefix Added/Deleted are synchronized this shouldn't have to be
             pushedFlows.put(prefix, new PushedFlowMod(sw.getId(), fm));
             
             List<OFMessage> msglist = new ArrayList<OFMessage>();
@@ -711,20 +546,41 @@
 		}
 	}
 	
-	//TODO test next-hop changes
-	//TODO check delete/add synchronization
+	public synchronized void processRibDelete(RibUpdate update) {
+		Prefix prefix = update.getPrefix();
 		
-	private void deletePrefixFlows(Prefix prefix) {
-		if (!topologyReady) {
-			return;
+		if (ptree.remove(prefix, update.getRibEntry())) {
+			/*
+			 * Only delete flows if an entry was actually removed from the trie.
+			 * 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
+			 */
+			_processDeletePrefix(prefix, update.getRibEntry());
 		}
+	}
+	
+	private void _processDeletePrefix(Prefix prefix, RibEntry ribEntry) {
+		deletePrefixFlows(prefix);
 		
-		log.debug("In deletePrefixFlows for {}", prefix);
-		
-		/*for (Map.Entry<Prefix, PushedFlowMod> entry : pushedFlows.entries()) {
-			log.debug("Pushed flow: {} => {}", entry.getKey(), entry.getValue());
-		}*/
-		
+		log.debug("Deleting {} to {}", prefix, ribEntry.getNextHop());
+		log.debug("is peer {}", bgpPeers.containsKey(ribEntry.getNextHop()));
+		if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
+			log.debug("Getting path for route with non-peer nexthop");
+			Path path = prefixToPath.get(prefix);
+			
+			if (path == null) {
+				log.error("No path found for non-peer path");
+			}
+			
+			path.decrementUsers();
+			log.debug("users {}, permanent {}", path.getUsers(), path.isPermanent());
+			if (path.getUsers() <= 0 && !path.isPermanent()) {
+				deletePath(path);
+			}
+		}
+	}
+	
+	private void deletePrefixFlows(Prefix prefix) {	
 		Collection<PushedFlowMod> pushedFlowMods 
 				= pushedFlows.removeAll(prefix);
 		
@@ -736,29 +592,45 @@
 					HexString.toHexString(((OFActionDataLayerDestination)pfm.getFlowMod().getActions().get(0))
 							.getDataLayerAddress())});
 			
-			OFFlowMod fm = pfm.getFlowMod();
-			
-			fm.setCommand(OFFlowMod.OFPFC_DELETE)
-			.setOutPort(OFPort.OFPP_NONE)
-			.setLengthU(OFFlowMod.MINIMUM_LENGTH);
-			
-			fm.getActions().clear();
-			
-			IOFSwitch sw = floodlightProvider.getSwitches().get(pfm.getDpid());
-			if (sw == null) {
-            	log.warn("Switch not found when pushing delete flow mod");
-            	continue;
-			}
-			
-			try {
-				sw.write(fm, null);
-				sw.flush();
-			} catch (IOException e) {
-				log.error("Failure writing flow mod", e);
-			}
+			sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
 		}
 	}
 	
+	private void deletePath(Path path) {
+		for (PushedFlowMod pfm : path.getFlowMods()) {
+			log.debug("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());
+		}
+	}
+	
+	private void sendDeleteFlowMod(OFFlowMod addFlowMod, long dpid) {
+		addFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT)
+		.setOutPort(OFPort.OFPP_NONE)
+		.setLengthU(OFFlowMod.MINIMUM_LENGTH);
+		
+		addFlowMod.getActions().clear();
+		
+		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+		if (sw == null) {
+        	log.warn("Switch not found when pushing delete flow mod");
+        	return;
+		}
+		
+		try {
+			sw.write(addFlowMod, null);
+			sw.flush();
+		} catch (IOException e) {
+			log.error("Failure writing flow mod", e);
+		}
+	}
+	
+	//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
@@ -773,25 +645,37 @@
 		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
 			byte[] mac = proxyArp.getMacAddress(peer.getIpAddress());
 			if (mac == null) {
 				log.debug("Don't know MAC for {}", peer.getIpAddress().getHostAddress());
 				//Put in the pending paths list first
-				pathsWaitingOnArp.put(peer.getIpAddress(),
-						new PathUpdate(peerInterface, peer.getIpAddress()));
+				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(peerInterface, MACAddress.valueOf(mac));
+			setUpDataPath(path, MACAddress.valueOf(mac));
 		}
 	}
 	
-	private void calculateAndPushPath(Interface dstInterface, MACAddress dstMacAddress) {
+	private void setUpDataPath(Path path, MACAddress dstMacAddress) {
+		calculateAndPushPath(path, dstMacAddress);
+	}
+	
+	private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
+		Interface dstInterface = path.getDstInterface();
+		
+		List<PushedFlowMod> pushedFlows = new ArrayList<PushedFlowMod>();
+		
 		for (Interface srcInterface : interfaces.values()) {
 			if (dstInterface.equals(srcInterface.getName())){
 				continue;
@@ -806,11 +690,15 @@
 				return; // just quit here?
 			}
 			
-			installPath(shortestPath.flowEntries(), dstMacAddress);
+			pushedFlows.addAll(installPath(shortestPath.flowEntries(), dstMacAddress));
 		}
+		
+		path.setFlowMods(pushedFlows);
 	}
 	
-	private void installPath(List<FlowEntry> flowEntries, MACAddress dstMacAddress){
+	private List<PushedFlowMod> installPath(List<FlowEntry> flowEntries, MACAddress dstMacAddress){
+		List<PushedFlowMod> flowMods = new ArrayList<PushedFlowMod>();
+		
 		//Set up the flow mod
 		OFFlowMod fm =
                 (OFFlowMod) floodlightProvider.getOFMessageFactory()
@@ -835,7 +723,6 @@
         	FlowEntry flowEntry = flowEntries.get(i);
            
             OFMatch match = new OFMatch();
-            //TODO Again using MAC address from configuration
             match.setDataLayerDestination(dstMacAddress.toBytes());
             match.setWildcards(match.getWildcards() & ~OFMatch.OFPFW_DL_DST);
             ((OFActionOutput) fm.getActions().get(0)).setPort(flowEntry.outPort().value());
@@ -849,6 +736,8 @@
             	continue;
             }
             
+            flowMods.add(new PushedFlowMod(sw.getId(), fm));
+            
             List<OFMessage> msglist = new ArrayList<OFMessage>();
             msglist.add(fm);
             try {
@@ -864,6 +753,8 @@
                 log.error("Failure cloning flow mod", e1);
             }
 		}
+        
+        return flowMods;
 	}
 	
 	private void setupBgpPaths(){
@@ -908,7 +799,6 @@
 	        
 	        //Common match fields
 	        forwardMatchSrc.setDataLayerType(Ethernet.TYPE_IPv4);
-	        //forwardMatch.setWildcards(forwardMatch.getWildcards() & ~OFMatch.OFPFW_DL_TYPE);
 	        forwardMatchSrc.setNetworkProtocol(IPv4.PROTOCOL_TCP);
 	        forwardMatchSrc.setTransportDestination(BGP_PORT);
 	        forwardMatchSrc.setWildcards(forwardMatchSrc.getWildcards() & ~OFMatch.OFPFW_IN_PORT
@@ -1024,30 +914,39 @@
 		log.debug("Received ARP response: {} => {}", ipAddress.getHostAddress(), 
 				MACAddress.valueOf(macAddress).toString());
 		
-		Set<PathUpdate> pathsToPush = pathsWaitingOnArp.removeAll(ipAddress);
-		
-		for (PathUpdate update : pathsToPush) {
-			log.debug("Pushing path to {} at {} on {}", new Object[] {
-					update.getDstIpAddress().getHostAddress(), 
-					MACAddress.valueOf(macAddress),
-					update.getDstInterface().getSwitchPort()});
-			calculateAndPushPath(update.getDstInterface(), 
-					MACAddress.valueOf(macAddress));
-		}
-		
-		Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
-		
 		/*
 		 * 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.valueOf(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.getDstInterface())) {
+					//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.getDstInterface()).setPermanent();
+					}
+				}
+				else {
+					setUpDataPath(path, MACAddress.valueOf(macAddress));
+					pushedPaths.put(path.getDstIpAddress(), path);
+				}
+			}
+			
+			Set<RibUpdate> prefixesToPush = prefixesWaitingOnArp.removeAll(ipAddress);
+			
 			for (RibUpdate update : prefixesToPush) {
 				//These will always be adds
 				
-				//addPrefixFlows(update.getPrefix(), update.getRibEntry());
-				//processRibAdd(update);
 				RibEntry rib = ptree.lookup(update.getPrefix()); 
 				if (rib != null && rib.equals(update.getRibEntry())) {
 					log.debug("Pushing prefix {} next hop {}", update.getPrefix(), 
@@ -1056,7 +955,7 @@
 					//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.
-					addPrefixFlows(update.getPrefix(), rib);
+					_processRibAdd(update);
 				} else {
 					log.debug("Received ARP response, but {},{} is no longer in ptree", 
 							update.getPrefix(), update.getRibEntry());
@@ -1070,32 +969,12 @@
 		setupBgpPaths();
 		setupFullMesh();
 		
-		//Traverse ptree and create flows for all routes
-		/*
-		for (PtreeNode node = ptree.begin(); node != null; node = ptree.next(node)){
-			if (node.rib != null){
-				prefixAdded(node);
-			}
-		}
-		*/
-		
-		/*
-		synchronized (ptree) {
-			Iterator<IPatriciaTrie.Entry> it = ptree.iterator();
-			while (it.hasNext()) {
-				IPatriciaTrie.Entry entry = it.next();
-				addPrefixFlows(entry.getPrefix(), entry.getRib());
-			}
-		}
-		*/
-		
 		bgpUpdatesExecutor.execute(new Runnable() {
 			@Override
 			public void run() {
 				doUpdatesThread();
 			}
 		});
-		
 	}
 	
 	private void checkSwitchesConnected(){
@@ -1131,8 +1010,6 @@
 	}
 	
 	private void checkStatus(){
-		log.debug("In checkStatus, swC {}, toRe {}", switchesConnected, topologyReady);
-		
 		if (!switchesConnected){
 			checkSwitchesConnected();
 		}
@@ -1144,19 +1021,7 @@
 			beginRouting();
 		}
 	}
-	
-	@Override
-	public void startUp(FloodlightModuleContext context) {
-		restApi.addRestletRoutable(new BgpRouteWebRoutable());
-		floodlightProvider.addOFSwitchListener(this);
-		topology.addListener(this);
-		
-		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, proxyArp);
-		
-		//Retrieve the RIB from BGPd during startup
-		retrieveRib();
-	}
-	
+
 	private void doUpdatesThread() {
 		boolean interrupted = false;
 		try {
@@ -1172,8 +1037,10 @@
 						break;
 					}
 				} catch (InterruptedException e) {
-					log.debug("interrupted", e);
+					log.debug("Interrupted while taking from updates queue", e);
 					interrupted = true;
+				} catch (Exception e) {
+					log.debug("exception", e);
 				}
 			}
 		} finally {
@@ -1206,23 +1073,4 @@
 			topologyChangeDetectorTask.reschedule(TOPO_DETECTION_WAIT, TimeUnit.SECONDS);
 		}
 	}
-
-	//TODO determine whether we need to listen for switch joins
-	@Override
-	public void addedSwitch(IOFSwitch sw) {
-		//checkStatus();
-	}
-
-	@Override
-	public void removedSwitch(IOFSwitch sw) {
-		// TODO Auto-generated method stub	
-	}
-
-	@Override
-	public void switchPortChanged(Long switchId) {}
-
-	@Override
-	public String getName() {
-		return "BgpRoute";
-	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
index c9b3265..f058843 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
@@ -60,7 +60,7 @@
 		} 
 		else {
 			//Ptree ptree = bgpRoute.getPtree();
-			IPatriciaTrie ptree = bgpRoute.getPtree();
+			IPatriciaTrie<RibEntry> ptree = bgpRoute.getPtree();
 			output += "{\n  \"rib\": [\n";
 			boolean printed = false;
 			
@@ -78,16 +78,16 @@
 			}*/
 			
 			synchronized(ptree) {
-				Iterator<IPatriciaTrie.Entry> it = ptree.iterator();
+				Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptree.iterator();
 				while (it.hasNext()) {
-					IPatriciaTrie.Entry entry = it.next();
+					IPatriciaTrie.Entry<RibEntry> entry = it.next();
 					
 					if (printed == true) {
 						output += ",\n";
 					}
 					
 					output += "    {\"prefix\": \"" + entry.getPrefix() +"\", ";
-					output += "\"nexthop\": \"" + entry.getRib().getNextHop().getHostAddress() +"\"}";
+					output += "\"nexthop\": \"" + entry.getValue().getNextHop().getHostAddress() +"\"}";
 					//output += ",\n";
 					
 					printed = true;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
index ba912ce..954976c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IBgpRouteService.java
@@ -7,7 +7,7 @@
 	//public RibEntry lookupRib(byte[] dest);
 
 	//public Ptree getPtree();
-	public IPatriciaTrie getPtree();
+	public IPatriciaTrie<RibEntry> getPtree();
 
 	public String getBGPdRestIp();
 
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
index 7fd7382..1fb0716 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/IPatriciaTrie.java
@@ -2,19 +2,19 @@
 
 import java.util.Iterator;
 
-public interface IPatriciaTrie {
-	public RibEntry put(Prefix p, RibEntry r);
+public interface IPatriciaTrie<V> {
+	public V put(Prefix prefix, V value);
 	
-	public RibEntry lookup(Prefix p);
+	public V lookup(Prefix prefix);
 	
-	public RibEntry match(Prefix p);
+	public V match(Prefix prefix);
 	
-	public boolean remove(Prefix p, RibEntry r);
+	public boolean remove(Prefix prefix, V value);
 	
-	public Iterator<Entry> iterator();
+	public Iterator<Entry<V>> iterator();
 	
-	interface Entry {
+	interface Entry<V> {
 		public Prefix getPrefix();
-		public RibEntry getRib();
+		public V getValue();
 	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
new file mode 100644
index 0000000..5cf4b09
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Path.java
@@ -0,0 +1,61 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * A path is always assumed to be from all other interfaces (external-facing
+ * switchports) to the destination interface.
+ */
+
+public class Path {
+
+	private Interface dstInterface;
+	private InetAddress dstIpAddress;
+	private int numUsers = 0;
+	
+	private List<PushedFlowMod> flowMods = null;
+	private boolean permanent = false;
+	
+	public Path(Interface dstInterface, InetAddress dstIpAddress) {
+		this.dstInterface = dstInterface;
+		this.dstIpAddress = dstIpAddress;
+	}
+
+	public Interface getDstInterface() {
+		return dstInterface;
+	}
+
+	public InetAddress getDstIpAddress() {
+		return dstIpAddress;
+	}
+	
+	public void incrementUsers() {
+		numUsers++;
+	}
+	
+	public void decrementUsers() {
+		numUsers--;
+	}
+	
+	public int getUsers() {
+		return numUsers;
+	}
+	
+	public List<PushedFlowMod> getFlowMods() {
+		return Collections.unmodifiableList(flowMods);
+	}
+	
+	public void setFlowMods(List<PushedFlowMod> flowMods) {
+		this.flowMods = flowMods;
+	}
+	
+	public boolean isPermanent() {
+		return permanent;
+	}
+	
+	public void setPermanent() {
+		permanent = true;
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java
deleted file mode 100644
index 1d2a47b..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PathUpdate.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.onrc.onos.ofcontroller.bgproute;
-
-import java.net.InetAddress;
-
-/*
- * A path is always assumed to be from all other interfaces (external-facing
- * switchports) to the destination interface.
- */
-
-public class PathUpdate {
-
-	private Interface dstInterface;
-	private InetAddress dstIpAddress;
-	
-	public PathUpdate(Interface dstInterface, InetAddress dstIpAddress) {
-		this.dstInterface = dstInterface;
-		this.dstIpAddress = dstIpAddress;
-	}
-
-	public Interface getDstInterface() {
-		return dstInterface;
-	}
-
-	public InetAddress getDstIpAddress() {
-		return dstIpAddress;
-	}
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
index 86bc8cf..89dfb30 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrie.java
@@ -3,7 +3,7 @@
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-public class PatriciaTrie implements IPatriciaTrie{
+public class PatriciaTrie<V> implements IPatriciaTrie<V> {
 	private final byte maskBits[] = {(byte)0x00, (byte)0x80, (byte)0xc0, (byte)0xe0, (byte)0xf0, 
 												 (byte)0xf8, (byte)0xfc, (byte)0xfe, (byte)0xff};
 	
@@ -15,14 +15,15 @@
 		this.maxPrefixLength = maxPrefixLength;
 	}
 
-	public synchronized RibEntry put(Prefix p, RibEntry r) {
-		if (p.getPrefixLength() > maxPrefixLength) {
+	@Override
+	public synchronized V put(Prefix prefix, V value) {
+		if (prefix.getPrefixLength() > maxPrefixLength) {
 			throw new IllegalArgumentException(String.format(
 					"Prefix length %d is greater than max prefix length %d", 
-					p.getPrefixLength(), maxPrefixLength));
+					prefix.getPrefixLength(), maxPrefixLength));
 		}
 		
-		if (p == null || r == null) {
+		if (prefix == null || value == null) {
 			throw new NullPointerException();
 		}
 		
@@ -30,23 +31,23 @@
 		Node match = null;
 		
 		while (node != null
-				&& node.prefix.getPrefixLength() <= p.getPrefixLength()
-				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
-		    if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+		    if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
 		    	/*
 		    	 * Prefix is already in tree. This may be an aggregate node, in which case
 		    	 * we are inserting a new prefix, or it could be an actual node, in which 
 		    	 * case we are inserting a new nexthop for the prefix and should return
 		    	 * the old nexthop.
 		    	 */
-		    	RibEntry oldRib = node.rib;
-		    	node.rib = r;
-		    	return oldRib;
+		    	V oldValue = node.value;
+		    	node.value = value;
+		    	return oldValue;
 			}
 
 			match = node;
 			
-			if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
 				node = node.right;
 			} else {
 				node = node.left;
@@ -56,7 +57,9 @@
 		Node add = null;
 		
 		if (node == null) {
-			add = new Node(p, r);
+			//add = new Node(p, r);
+			add = new Node(prefix);
+			add.value = value;
 			
 			if (match != null) {
 				node_link(match, add);
@@ -64,7 +67,7 @@
 				top = add;
 			}
 		} else {
-			add = node_common(node, p.getAddress(), p.getPrefixLength());
+			add = node_common(node, prefix.getAddress(), prefix.getPrefixLength());
 			if (add == null) {
 				//I think this is -ENOMEM?
 				//return null;
@@ -77,14 +80,16 @@
 			}
 			node_link(add, node);
 			
-			if (add.prefix.getPrefixLength() != p.getPrefixLength()) {
+			if (add.prefix.getPrefixLength() != prefix.getPrefixLength()) {
 				match = add;
 				
-				add = new Node(p, r);
+				//add = new Node(p, r);
+				add = new Node(prefix);
+				add.value = value;
 				node_link(match, add);
 			}
 			else {
-				add.rib = r;
+				add.value = value;
 			}
 		}
 		
@@ -94,10 +99,9 @@
 	}
 	
 	/*exact match*/
-	public synchronized RibEntry lookup(Prefix p) {
-		//TODO
-		
-		if (p.getPrefixLength() > maxPrefixLength) {
+	@Override
+	public synchronized V lookup(Prefix prefix) {
+		if (prefix.getPrefixLength() > maxPrefixLength) {
 			return null;
 		}
 		
@@ -120,28 +124,36 @@
 		}
 		*/
 		
-		Node node = findNode(p);
+		Node node = findNode(prefix);
 		
-		return node == null ? null : node.rib;
+		return node == null ? null : node.value;
 	}
 	
 	/*closest containing prefix*/
-	public synchronized RibEntry match(Prefix p) {
+	@Override
+	public synchronized V match(Prefix prefix) {
 		//TODO
-		return null;
+		if (prefix.getPrefixLength() > maxPrefixLength) {
+			return null;
+		}
+		
+		Node closestNode = findClosestNode(prefix);
+		
+		return closestNode == null ? null : closestNode.value;
 	}
 	
-	public synchronized boolean remove(Prefix p, RibEntry r) {
+	@Override
+	public synchronized boolean remove(Prefix prefix, V value) {
 		Node child;
 		Node parent;
 		
-		if (p == null || r == null) {
+		if (prefix == null || value == null) {
 			return false;
 		}
 		
-		Node node = findNode(p);
+		Node node = findNode(prefix);
 		
-		if (node == null || node.rib == null || !node.rib.equals(r)) {
+		if (node == null || node.isAggregate() || !node.value.equals(value)) {
 			//Given <prefix, nexthop> mapping is not in the tree
 			return false;
 		}
@@ -151,7 +163,7 @@
 			//In the future, maybe we should re-evaluate what the aggregate prefix should be?
 			//It shouldn't necessarily stay the same.
 			//More complicated if the above prefix is also aggregate.
-			node.rib = null;
+			node.value = null;
 			return true;
 		}
 		
@@ -192,22 +204,23 @@
 		return true;
 	}
 	
-	public Iterator<Entry> iterator() {
+	@Override
+	public Iterator<Entry<V>> iterator() {
 		return new PatriciaTrieIterator(top);
 	}
 	
-	private Node findNode(Prefix p) {
+	private Node findNode(Prefix prefix) {
 		Node node = top;
 		
 		while (node != null
-				&& node.prefix.getPrefixLength() <= p.getPrefixLength()
-				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), p.getAddress(), p.getPrefixLength()) == true) {
-			if (node.prefix.getPrefixLength() == p.getPrefixLength()) {
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+			if (node.prefix.getPrefixLength() == prefix.getPrefixLength()) {
 				//return addReference(node);
 				return node;
 			}
 			
-			if (bit_check(p.getAddress(), node.prefix.getPrefixLength()) == true) {
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
 				node = node.right;
 			} else {
 				node = node.left;
@@ -217,6 +230,27 @@
 		return null;
 	}
 	
+	private Node findClosestNode(Prefix prefix) {
+		Node node = top;
+		Node match = null;
+		
+		while (node != null
+				&& node.prefix.getPrefixLength() <= prefix.getPrefixLength()
+				&& key_match(node.prefix.getAddress(), node.prefix.getPrefixLength(), prefix.getAddress(), prefix.getPrefixLength()) == true) {
+			if (!node.isAggregate()) {
+				match = node;
+			}
+			
+			if (bit_check(prefix.getAddress(), node.prefix.getPrefixLength()) == true) {
+				node = node.right;
+			} else {
+				node = node.left;
+			}
+		}
+		
+		return match;
+	}
+	
 	/*
 	 * Receives a 1-based bit index
 	 * Returns a 1-based byte index
@@ -325,7 +359,8 @@
 		if (boundary != 0)
 			newPrefix[j] = (byte)(node.prefix.getAddress()[j] & maskBits[common_len % 8]);
 		
-		return new Node(new Prefix(newPrefix, common_len), null);
+		//return new Node(new Prefix(newPrefix, common_len), null);
+		return new Node(new Prefix(newPrefix, common_len));
 		//return add;
 	}
 	
@@ -334,26 +369,33 @@
 		public Node left = null;
 		public Node right = null;
 		
-		public Prefix prefix;
-		public RibEntry rib;
+		public final Prefix prefix;
+		public V value;
 		
-		public Node(Prefix p, RibEntry r) {
+		//public Node(Prefix p, RibEntry r) {
+		//	this.prefix = p;
+		//	this.rib = r;
+		//}
+		public Node(Prefix p) {
 			this.prefix = p;
-			this.rib = r;
 		}
 		
-		public Entry getEntry() {
-			return new PatriciaTrieEntry(prefix, rib);
+		public boolean isAggregate() {
+			return value == null;
+		}
+				
+		public Entry<V> getEntry() {
+			return new PatriciaTrieEntry(prefix, value);
 		}
 	}
 	
-	private class PatriciaTrieEntry implements Entry {
+	private class PatriciaTrieEntry implements Entry<V> {
 		private Prefix prefix;
-		private RibEntry rib;
+		private V value;
 		
-		public PatriciaTrieEntry(Prefix prefix, RibEntry rib) {
+		public PatriciaTrieEntry(Prefix prefix, V value) {
 			this.prefix = prefix;
-			this.rib = rib;
+			this.value = value;
 		}
 		
 		@Override
@@ -362,12 +404,12 @@
 		}
 		
 		@Override
-		public RibEntry getRib() {
-			return rib;
+		public V getValue() {
+			return value;
 		}
 	}
 	
-	private class PatriciaTrieIterator implements Iterator<Entry> {
+	private class PatriciaTrieIterator implements Iterator<Entry<V>> {
 		
 		private Node current;
 		private boolean started = false;
@@ -376,7 +418,7 @@
 			current = start;
 			
 			//If the start is an aggregate node fast forward to find the next valid node
-			if (current != null && current.rib == null) {
+			if (current != null && current.isAggregate()) {
 				current = findNext(current);
 			}
 		}
@@ -395,7 +437,7 @@
 		}
 
 		@Override
-		public Entry next() {
+		public Entry<V> next() {
 			if (current == null) {
 				throw new NoSuchElementException();
 			}
@@ -452,9 +494,9 @@
 				return null;
 			}
 			
-			//If the node doesn't have a rib, it's not an actual node, it's an artifically
+			//If the node doesn't have a value, it's not an actual node, it's an artifically
 			//inserted aggregate node. We don't want to return these to the user.
-			if (next.rib == null) {
+			if (next.isAggregate()) {
 				return findNext(next);
 			}
 			
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
index 21dd45c..05ce0a4 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Prefix.java
@@ -4,6 +4,8 @@
 import java.net.UnknownHostException;
 import java.util.Arrays;
 
+import com.google.common.net.InetAddresses;
+
 public class Prefix {
 	private final int MAX_BYTES = 4;
 	
@@ -31,11 +33,7 @@
 
 	public Prefix(String strAddress, int prefixLength) {
 		byte[] addr = null;
-		try {
-			addr = InetAddress.getByName(strAddress).getAddress();
-		} catch (UnknownHostException e) {
-			throw new IllegalArgumentException("Invalid IP inetAddress argument");
-		}
+		addr = InetAddresses.forString(strAddress).getAddress();
 				
 		if (addr == null || addr.length != MAX_BYTES || 
 				prefixLength < 0 || prefixLength > MAX_BYTES * Byte.SIZE) {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
new file mode 100644
index 0000000..56f4c04
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
@@ -0,0 +1,25 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import org.openflow.protocol.OFFlowMod;
+
+public class PushedFlowMod {
+	private long dpid;
+	private OFFlowMod flowMod;
+	
+	public PushedFlowMod(long dpid, OFFlowMod flowMod) {
+		this.dpid = dpid;
+		try {
+			this.flowMod = flowMod.clone();
+		} catch (CloneNotSupportedException e) {
+			this.flowMod = flowMod;
+		}
+	}
+	
+	public long getDpid() {
+		return dpid;
+	}
+	
+	public OFFlowMod getFlowMod() {
+		return flowMod;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
index c27f962..8087471 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
@@ -1,7 +1,8 @@
 package net.onrc.onos.ofcontroller.bgproute;
 
 import java.net.InetAddress;
-import java.net.UnknownHostException;
+
+import com.google.common.net.InetAddresses;
 
 public class RibEntry {
 	private InetAddress routerId;
@@ -13,12 +14,8 @@
 	}
 	
 	public RibEntry(String routerId, String nextHop) {
-		try {
-			this.routerId = InetAddress.getByName(routerId);
-			this.nextHop = InetAddress.getByName(nextHop);
-		} catch (UnknownHostException e) {
-			throw new IllegalArgumentException("Invalid address format");
-		}
+		this.routerId = InetAddresses.forString(routerId);
+		this.nextHop = InetAddresses.forString(nextHop);
 	}
 	
 	public InetAddress getNextHop() {
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
index 1af0416..571d37d 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PatriciaTrieTest.java
@@ -4,7 +4,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import java.util.HashMap;
 import java.util.Iterator;
@@ -12,18 +11,17 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class PatriciaTrieTest {
 
-	IPatriciaTrie ptrie;
+	IPatriciaTrie<RibEntry> ptrie;
 	Prefix[] prefixes;
 	Map<Prefix, RibEntry> mappings;
 	
 	@Before
 	public void setUp() throws Exception {
-		ptrie = new PatriciaTrie(32);
+		ptrie = new PatriciaTrie<RibEntry>(32);
 		mappings = new HashMap<Prefix, RibEntry>();
 		
 		prefixes = new Prefix[] {
@@ -50,7 +48,7 @@
 
 	@Test
 	public void testPut() {
-		IPatriciaTrie ptrie = new PatriciaTrie(32);
+		IPatriciaTrie<RibEntry> ptrie = new PatriciaTrie<RibEntry>(32);
 		
 		Prefix p1 = new Prefix("192.168.240.0", 20);
 		RibEntry r1 = new RibEntry("192.168.10.101", "192.168.60.2");
@@ -107,10 +105,27 @@
 		assertTrue(retval.equals(r2));
 	}
 
-	@Ignore
+	//@Ignore
 	@Test
 	public void testMatch() {
-		fail("Not yet implemented");
+		Prefix p1 = new Prefix("192.168.10.30", 32);
+		Prefix p2 = new Prefix("192.168.10.30", 31);
+		Prefix p3 = new Prefix("192.168.8.241", 32);
+		Prefix p4 = new Prefix("1.0.0.0", 32);
+		Prefix p5 = new Prefix("192.168.8.0", 22);
+		Prefix p6 = new Prefix("192.168.8.0", 21);
+		
+		assertTrue(ptrie.match(p1).equals(mappings.get(prefixes[0])));
+		assertTrue(ptrie.match(p2).equals(mappings.get(prefixes[0])));
+		assertTrue(ptrie.match(p3).equals(mappings.get(prefixes[1])));
+		assertNull(ptrie.match(p4));
+		assertTrue(ptrie.match(p5).equals(mappings.get(prefixes[2])));
+		//System.out.println(ptrie.match(p6).getNextHop().getHostAddress());
+		assertTrue(ptrie.match(p6).equals(mappings.get(prefixes[8])));
+		
+		
+		//TODO more extensive tests
+		//fail("Not yet implemented");
 	}
 
 	@Test
@@ -169,19 +184,19 @@
 	public void testIterator() {		
 		int[] order = new int[] {7, 5, 3, 8, 2, 1, 0, 4, 6};
 		
-		Iterator<IPatriciaTrie.Entry> it = ptrie.iterator();
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptrie.iterator();
 		int i = 0;
 		assertTrue(it.hasNext());
 		while (it.hasNext()) {
-			IPatriciaTrie.Entry entry = it.next();
+			IPatriciaTrie.Entry<RibEntry> entry = it.next();
 			assertTrue(entry.getPrefix().equals(prefixes[order[i]]));
 			i++;
 		}
 		assertFalse(it.hasNext());
 		assertTrue(i == order.length);
 		
-		IPatriciaTrie pt = new PatriciaTrie(32);
-		Iterator<IPatriciaTrie.Entry> it2 = pt.iterator();
+		IPatriciaTrie<RibEntry> pt = new PatriciaTrie<RibEntry>(32);
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it2 = pt.iterator();
 		assertFalse(it2.hasNext());
 		it.next(); //throws NoSuchElementException
 	}
diff --git a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
index 6af9d30..1135407 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/bgproute/PtreeTest.java
@@ -25,14 +25,14 @@
 	private Logger log = LoggerFactory.getLogger(PtreeTest.class);
 	
 	private Ptree ptree;
-	private PatriciaTrie ooptrie;
+	private PatriciaTrie<RibEntry> ooptrie;
 	
 	private Map<String, byte[]> byteAddresses;
 
 	@Before
 	public void setUp() throws Exception {
 		ptree = new Ptree(32);
-		ooptrie = new PatriciaTrie(32);
+		ooptrie = new PatriciaTrie<RibEntry>(32);
 			
 		String[] strPrefixes = {
 			"192.168.10.0/24",
@@ -176,6 +176,7 @@
 		fail("Not yet implemented");
 	}
 	
+	@Ignore
 	@Test
 	public void testMisc() {
 		int bitIndex = -1;
@@ -197,10 +198,10 @@
 	
 	@Test
 	public void testIteration() {
-		Iterator<IPatriciaTrie.Entry> it = ooptrie.iterator();
+		Iterator<IPatriciaTrie.Entry<RibEntry>> it = ooptrie.iterator();
 		
 		while (it.hasNext()) {
-			IPatriciaTrie.Entry entry = it.next();
+			IPatriciaTrie.Entry<RibEntry> entry = it.next();
 			log.debug("PatriciaTrie prefix {} \t {}", entry.getPrefix(), entry.getPrefix().printAsBits());
 		}