Merge pull request #424 from jonohart/master

Code cleanup of SDNIP and ARP code
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java
index 01f886d..85f21ca 100644
--- a/src/main/java/net/floodlightcontroller/packet/IPv4.java
+++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java
@@ -31,6 +31,7 @@
  *
  */
 public class IPv4 extends BasePacket {
+	public static final int ADDRESS_LENGTH = 4;
     public static final byte PROTOCOL_ICMP = 0x1;
     public static final byte PROTOCOL_TCP = 0x6;
     public static final byte PROTOCOL_UDP = 0x11;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
index e98c3e8..fa11c17 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpPeer.java
@@ -7,24 +7,20 @@
 import com.google.common.net.InetAddresses;
 
 public class BgpPeer {
-	private String interfaceName;
-	private InetAddress ipAddress;
+	private final String interfaceName;
+	private final InetAddress ipAddress;
+	
+	public BgpPeer(@JsonProperty("interface") String interfaceName,
+				   @JsonProperty("ipAddress") String ipAddress) {
+		this.interfaceName = interfaceName;
+		this.ipAddress = InetAddresses.forString(ipAddress);
+	}
 	
 	public String getInterfaceName() {
 		return interfaceName;
 	}
-	
-	@JsonProperty("interface")
-	public void setInterfaceName(String interfaceName) {
-		this.interfaceName = interfaceName;
-	}
-	
+
 	public InetAddress getIpAddress() {
 		return ipAddress;
 	}
-	
-	@JsonProperty("ipAddress")
-	public void setIpAddress(String ipAddress) {
-		this.ipAddress = InetAddresses.forString(ipAddress);
-	}
 }
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 9e7bf61..960d001 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -76,7 +76,7 @@
 
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, 
 									ITopologyListener, IArpRequester,
-									IOFSwitchListener {
+									IOFSwitchListener, ILayer3InfoService {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
@@ -158,7 +158,6 @@
 							ldu.getDst(), ldu.getDstPort());
 					
 					if (activeLinks.contains(l)){
-						log.debug("Not found: {}", l);
 						it.remove();
 					}
 				}
@@ -263,7 +262,7 @@
 		
 		//TODO We'll initialise this here for now, but it should really be done as
 		//part of the controller core
-		proxyArp = new ProxyArpManager(floodlightProvider, topology);
+		proxyArp = new ProxyArpManager(floodlightProvider, topology, this);
 		
 		linkUpdates = new ArrayList<LDUpdate>();
 		ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -308,8 +307,6 @@
 		log.debug("Config file set to {}", configFilename);
 		
 		readGatewaysConfiguration(configFilename);
-		
-		proxyArp.setL3Mode(interfacePtrie, interfaces.values(), bgpdMacAddress);
 	}
 	
 	@Override
@@ -466,7 +463,7 @@
 				Path path = pushedPaths.get(dstIpAddress);
 				if (path == null) {
 					path = new Path(egressInterface, dstIpAddress);
-					setUpDataPath(path, MACAddress.valueOf(nextHopMacAddress));
+					calculateAndPushPath(path, MACAddress.valueOf(nextHopMacAddress));
 					pushedPaths.put(dstIpAddress, path);
 				}
 				
@@ -493,14 +490,7 @@
 		}
 		
 		//Add a flow to rewrite mac for this prefix to all other border switches
-		//for (Interface srcInterface : interfaces.values()) {
 		for (Interface srcInterface : srcInterfaces.values()) {
-			//if (srcInterface == egressInterface) {
-				//Don't push a flow for the switch where this peer is attached
-				//continue;
-			//}
-			
-			
 			DataPath shortestPath; 
 			if (topoRouteTopology == null) {
 				shortestPath = topoRouteService.getShortestPath(
@@ -572,7 +562,12 @@
             try {
 				sw.write(msglist, null);
 				sw.flush();
-				//Thread.sleep(0, 100000);
+				
+				/*
+				 * XXX Rate limit hack!
+				 * This should be solved properly by adding a rate limiting
+				 * layer on top of the switches if we know they need it.
+				 */
 				Thread.sleep(1);
 			} catch (IOException e) {
 				log.error("Failure writing flow mod", e);
@@ -603,7 +598,6 @@
 		
 		if (!bgpPeers.containsKey(ribEntry.getNextHop())) {
 			log.debug("Getting path for route with non-peer nexthop");
-			//Path path = prefixToPath.get(prefix);
 			Path path = prefixToPath.remove(prefix);
 			
 			if (path != null) {
@@ -611,7 +605,6 @@
 				//flows yet because we were waiting to resolve ARP
 			
 				path.decrementUsers();
-				log.debug("users {}, permanent {}", path.getUsers(), path.isPermanent());
 				if (path.getUsers() <= 0 && !path.isPermanent()) {
 					deletePath(path);
 					pushedPaths.remove(path.getDstIpAddress());
@@ -620,28 +613,37 @@
 		}
 	}
 	
-	private void deletePrefixFlows(Prefix prefix) {	
+	private void deletePrefixFlows(Prefix prefix) {
+		log.debug("Deleting flows for prefix {}", prefix);
+		
 		Collection<PushedFlowMod> pushedFlowMods 
 				= pushedFlows.removeAll(prefix);
 		
 		for (PushedFlowMod pfm : pushedFlowMods) {
-			log.debug("Pushing a DELETE flow mod to {}, matches prefix {} with mac-rewrite {}",
-					new Object[] {HexString.toHexString(pfm.getDpid()),
-					pfm.getFlowMod().getMatch().getNetworkDestination() + 
-					pfm.getFlowMod().getMatch().getNetworkDestinationMaskLen(),
-					HexString.toHexString(((OFActionDataLayerDestination)pfm.getFlowMod().getActions().get(0))
-							.getDataLayerAddress())});
+			if (log.isTraceEnabled()) {
+				log.trace("Pushing a DELETE flow mod to {}, matches prefix {} with mac-rewrite {}",
+						new Object[] {HexString.toHexString(pfm.getDpid()),
+						pfm.getFlowMod().getMatch().getNetworkDestination() + 
+						pfm.getFlowMod().getMatch().getNetworkDestinationMaskLen(),
+						HexString.toHexString(((OFActionDataLayerDestination)pfm.getFlowMod().getActions().get(0))
+								.getDataLayerAddress())});
+			}
 			
 			sendDeleteFlowMod(pfm.getFlowMod(), pfm.getDpid());
 		}
 	}
 	
 	private void deletePath(Path path) {
+		log.debug("Deleting flows for path to {}", 
+				path.getDstIpAddress().getHostAddress());
+		
 		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())
-			});
+			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());
 		}
@@ -703,14 +705,10 @@
 			}
 			
 			//If we know the MAC, lets go ahead and push the paths to this peer
-			setUpDataPath(path, MACAddress.valueOf(mac));
+			calculateAndPushPath(path, MACAddress.valueOf(mac));
 		}
 	}
 	
-	private void setUpDataPath(Path path, MACAddress dstMacAddress) {
-		calculateAndPushPath(path, dstMacAddress);
-	}
-	
 	private void calculateAndPushPath(Path path, MACAddress dstMacAddress) {
 		Interface dstInterface = path.getDstInterface();
 		
@@ -726,20 +724,18 @@
 			
 			DataPath shortestPath;
 			if (topoRouteTopology == null) {
-				log.debug("Using database topo");
 				shortestPath = topoRouteService.getShortestPath(
 						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
 			}
 			else {
-				log.debug("Using prepared topo");
 				shortestPath = topoRouteService.getTopoShortestPath(topoRouteTopology, 
 						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
 			}
 			
 			if (shortestPath == null){
-				log.debug("Shortest path between {} and {} not found",
+				log.warn("Shortest path between {} and {} not found",
 						srcInterface.getSwitchPort(), dstInterface.getSwitchPort());
-				return; // just quit here?
+				return;
 			}
 			
 			pushedFlows.addAll(installPath(shortestPath.flowEntries(), dstMacAddress));
@@ -766,6 +762,7 @@
         .setBufferId(OFPacketOut.BUFFER_ID_NONE)
         .setCookie(L2_FWD_COOKIE)
         .setCommand(OFFlowMod.OFPFC_ADD)
+        .setPriority(SDNIP_PRIORITY)
         .setActions(actions)
         .setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
         
@@ -989,7 +986,7 @@
 					}
 				}
 				else {
-					setUpDataPath(path, MACAddress.valueOf(macAddress));
+					calculateAndPushPath(path, MACAddress.valueOf(macAddress));
 					pushedPaths.put(path.getDstIpAddress(), path);
 				}
 			}
@@ -1057,7 +1054,7 @@
 	private void setupDefaultDropFlows() {
 		OFFlowMod fm = new OFFlowMod();
 		fm.setMatch(new OFMatch());
-		//No action means drop
+		fm.setActions(new ArrayList<OFAction>()); //No action means drop
 		
 		fm.setIdleTimeout((short)0)
         .setHardTimeout((short)0)
@@ -1067,14 +1064,52 @@
         .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)0x8942);
+		matchLLDP.setWildcards(matchLLDP.getWildcards() & ~ OFMatch.OFPFW_DL_TYPE);
+		fmLLDP.setMatch(matchLLDP);
+		
+		OFMatch matchBDDP = new OFMatch();
+		matchBDDP.setDataLayerType((short)0x88cc);
+		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);
+		
 		for (String strdpid : switches){
 			IOFSwitch sw = floodlightProvider.getSwitches().get(HexString.toLong(strdpid));
 			if (sw == null) {
 				log.debug("Couldn't find switch to push default deny flow");
 			}
 			else {
+				List<OFMessage> msgList = new ArrayList<OFMessage>();
+				msgList.add(fm);
+				msgList.add(fmLLDP);
+				msgList.add(fmBDDP);
 				try {
-					sw.write(fm, null);
+					sw.write(msgList, null);
 				} catch (IOException e) {
 					log.warn("Failure writing default deny flow to switch", e);
 				}
@@ -1214,20 +1249,54 @@
 	}
 
 	@Override
-	public void removedSwitch(IOFSwitch sw) {
-		// TODO Auto-generated method stub
-		
-	}
+	public void removedSwitch(IOFSwitch sw) {}
 
 	@Override
-	public void switchPortChanged(Long switchId) {
-		// TODO Auto-generated method stub
-		
-	}
+	public void switchPortChanged(Long switchId) {}
 
 	@Override
 	public String getName() {
-		// TODO Auto-generated method stub
-		return null;
+		return "BgpRoute";
+	}
+	
+	/*
+	 * ILayer3InfoService methods
+	 */
+	
+	@Override
+	public boolean isInterfaceAddress(InetAddress address) {
+		Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
+		return (intf != null && intf.getIpAddress().equals(address));
+	}
+	
+	@Override
+	public boolean inConnectedNetwork(InetAddress address) {
+		Interface intf = interfacePtrie.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 interfacePtrie.match(new Prefix(dstIpAddress.getAddress(), 32));
+	}
+	
+	@Override
+	public boolean hasLayer3Configuration() {
+		return !interfaces.isEmpty();
+	}
+	
+	@Override
+	public MACAddress getRouterMacAddress() {
+		return bgpdMacAddress;
 	}
 }
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 f058843..8403f71 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRouteResource.java
@@ -15,20 +15,6 @@
 
 	protected static Logger log = LoggerFactory.getLogger(BgpRouteResource.class);
 
-	private String addrToString(byte [] addr) {
-		String str = "";
-
-		for (int i = 0; i < 4; i++) {
-			int val = (addr[i] & 0xff);
-			str += val;
-			if (i != 3)
-				str += ".";
-		}
-
-		return str;
-	}
-
-	//@SuppressWarnings("unused")
 	@Get
 	public String get(String fmJson) {
 		String dest = (String) getRequestAttributes().get("dest");
@@ -38,45 +24,23 @@
 
 		if (dest != null) {
 			//TODO Needs to be changed to use the new RestClient.get().
-			
-			
-			//Prefix p;
-			//try {
-			//	p = new Prefix(dest, 32);
-			//} catch (UnknownHostException e) {
-			//if (p == null) {
-			//	return "[GET]: dest address format is wrong";
-			//}
 
 			// the dest here refers to router-id
 			//bgpdRestIp includes port number, such as 1.1.1.1:8080
 			String BGPdRestIp = bgpRoute.getBGPdRestIp();
 			String url="http://"+BGPdRestIp+"/wm/bgp/"+dest;
 
-			RestClient.get(url);
+			//Doesn't actually do anything with the response
+			RestClient.get(url); 
 			
 			output="Get rib from bgpd finished!\n";
 			return output;
 		} 
 		else {
-			//Ptree ptree = bgpRoute.getPtree();
 			IPatriciaTrie<RibEntry> ptree = bgpRoute.getPtree();
 			output += "{\n  \"rib\": [\n";
 			boolean printed = false;
 			
-			/*
-			for (PtreeNode node = ptree.begin(); node!= null; node = ptree.next(node)) {
-				if (node.rib == null) {
-					continue;
-				}
-				if (printed == true) {
-					output += ",\n";
-				}
-				output += "    {\"prefix\": \"" + addrToString(node.key) + "/" + node.keyBits +"\", ";
-				output += "\"nexthop\": \"" + addrToString(node.rib.nextHop.getAddress()) +"\"}";
-				printed = true;
-			}*/
-			
 			synchronized(ptree) {
 				Iterator<IPatriciaTrie.Entry<RibEntry>> it = ptree.iterator();
 				while (it.hasNext()) {
@@ -88,42 +52,22 @@
 					
 					output += "    {\"prefix\": \"" + entry.getPrefix() +"\", ";
 					output += "\"nexthop\": \"" + entry.getValue().getNextHop().getHostAddress() +"\"}";
-					//output += ",\n";
 					
 					printed = true;
 				}
 			}
 			
-			//output += "{\"router_id\": \"" + addrToString(node.rib.routerId.getAddress()) +"\"}\n";
 			output += "\n  ]\n}\n";
 		}
 		
 		return output;
 	}
 
-	//unused?
-	/*
-	public static ByteBuffer toByteBuffer(String value) throws UnsupportedEncodingException {
-		return ByteBuffer.wrap(value.getBytes("UTF-8"));
-	}
-	*/
-
-	//unused?
-	/*
-	public static String toString(ByteBuffer buffer) throws UnsupportedEncodingException {
-		byte[] bytes = new byte[buffer.remaining()];
-		buffer.get(bytes);
-		return new String(bytes, "UTF-8");
-	}
-	*/
-
 	@Post
 	public String store(String fmJson) {
 		IBgpRouteService bgpRoute = (IBgpRouteService) getContext().getAttributes().
 				get(IBgpRouteService.class.getCanonicalName());
 
-		//Ptree ptree = bgpRoute.getPtree();
-
 		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
 		String mask = (String) getRequestAttributes().get("mask");
@@ -151,18 +95,6 @@
 
 			bgpRoute.newRibUpdate(new RibUpdate(Operation.UPDATE, p, rib));
 			
-			/*
-			PtreeNode node = ptree.acquire(p.getAddress(), p.getPrefixLength());
-			
-			if (node.rib != null) {
-				node.rib = null;
-				ptree.delReference(node);
-			}
-			node.rib = rib;
-
-			bgpRoute.prefixAdded(node);
-			*/
-			
 			reply = "[POST: " + prefix + "/" + mask + ":" + nexthop + "]";
 			log.info(reply);
 		}
@@ -185,8 +117,6 @@
 		IBgpRouteService bgpRoute = (IBgpRouteService)getContext().getAttributes().
 				get(IBgpRouteService.class.getCanonicalName());
 
-		//Ptree ptree = bgpRoute.getPtree();
-
 		String routerId = (String) getRequestAttributes().get("routerid");
 		String prefix = (String) getRequestAttributes().get("prefix");
 		String mask = (String) getRequestAttributes().get("mask");
@@ -214,29 +144,6 @@
 			
 			bgpRoute.newRibUpdate(new RibUpdate(Operation.DELETE, p, r));
 			
-			/*
-			PtreeNode node = ptree.lookup(p.getAddress(), p.getPrefixLength());
-			
-			//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){
-				bgpRoute.prefixDeleted(node);
-			}
-
-			
-
-			if (node != null && node.rib != null) {
-				if (r.equals(node.rib)) {
-					node.rib = null;
-					ptree.delReference(node);					
-				}
-			}
-			*/
-			
 			reply =reply + "[DELE: " + prefix + "/" + mask + ":" + nextHop + "]";
 		}
 		else {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
deleted file mode 100644
index e893acf..0000000
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/GatewayRouter.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package net.onrc.onos.ofcontroller.bgproute;
-
-import net.floodlightcontroller.util.MACAddress;
-import net.onrc.onos.ofcontroller.util.Dpid;
-import net.onrc.onos.ofcontroller.util.IPv4;
-import net.onrc.onos.ofcontroller.util.Port;
-import net.onrc.onos.ofcontroller.util.SwitchPort;
-
-import org.codehaus.jackson.annotate.JsonProperty;
-import org.openflow.util.HexString;
-
-public class GatewayRouter {
-	private SwitchPort attachmentPoint = null;
-	private long dpid;
-	private short port;
-	private MACAddress routerMac;
-	private IPv4 routerIp;
-	private IPv4 myIpAddress;
-	
-	
-	public SwitchPort getAttachmentPoint() {
-		if (attachmentPoint == null){
-			attachmentPoint = new SwitchPort(new Dpid(dpid), new Port(port));
-		}
-		return attachmentPoint;
-	}
-	
-	public long getDpid() {
-		return dpid;
-	}
-
-	@JsonProperty("attachmentDpid")
-	public void setDpid(String dpid) {
-		this.dpid = HexString.toLong(dpid);
-	}
-
-	public short getPort() {
-		return port;
-	}
-
-	@JsonProperty("attachmentPort")
-	public void setPort(short port) {
-		this.port = port;
-	}
-
-	public MACAddress getRouterMac() {
-		return routerMac;
-	}
-	
-	@JsonProperty("macAddress")
-	public void setRouterMac(String routerMac) {
-		this.routerMac = MACAddress.valueOf(routerMac);;
-	}
-
-	public IPv4 getRouterIp() {
-		return routerIp;
-	}
-	
-	@JsonProperty("ipAddress")
-	public void setRouterIp(String routerIp) {
-		this.routerIp = new IPv4(routerIp);
-	}
-	
-	public IPv4 getMyIpAddress() {
-		return myIpAddress;
-	}
-	
-	@JsonProperty("myIpAddress")
-	public void setMyIpAddress(String myIpAddress) {
-		this.myIpAddress = new IPv4(myIpAddress);
-	}
-}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
new file mode 100644
index 0000000..00ddd68
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/ILayer3InfoService.java
@@ -0,0 +1,35 @@
+package net.onrc.onos.ofcontroller.bgproute;
+
+import java.net.InetAddress;
+
+import net.floodlightcontroller.util.MACAddress;
+
+/**
+ * Provides information about the layer 3 properties of the network.
+ * This is based on IP addresses configured on ports in the network.
+ *
+ */
+public interface ILayer3InfoService {
+	public boolean isInterfaceAddress(InetAddress address);
+	public boolean inConnectedNetwork(InetAddress address);
+	public boolean fromExternalNetwork(long inDpid, short inPort);
+	
+	/**
+	 * Retrieves the {@link Interface} object for the interface that packets
+	 * to dstIpAddress will be sent out of. Returns null if dstIpAddress is not
+	 * in a directly connected network, or if no interfaces are configured.
+	 * @param dstIpAddress Destination IP address that we want to match to
+	 * an outgoing interface
+	 * @return The {@link Interface} object if found, null if not
+	 */
+	public Interface getOutgoingInterface(InetAddress dstIpAddress);
+	
+	/**
+	 * Returns whether this controller has a layer 3 configuration 
+	 * (i.e. interfaces and IP addresses)
+	 * @return True if IP addresses are configured, false if not
+	 */
+	public boolean hasLayer3Configuration();
+	
+	public MACAddress getRouterMacAddress();
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
index 088c18e..48b60d8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/Interface.java
@@ -6,68 +6,57 @@
 import net.onrc.onos.ofcontroller.util.Port;
 import net.onrc.onos.ofcontroller.util.SwitchPort;
 
+import org.codehaus.jackson.annotate.JsonCreator;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.openflow.util.HexString;
 
 import com.google.common.net.InetAddresses;
 
 public class Interface {
-	private String name;
-	private SwitchPort switchPort = null;
-	private long dpid;
-	private short port;
-	private InetAddress ipAddress;
-	private int prefixLength;
+	private final String name;
+	private final SwitchPort switchPort;
+	private final long dpid;
+	private final short port;
+	private final InetAddress ipAddress;
+	private final int prefixLength;
+	
+	@JsonCreator
+	public Interface (@JsonProperty("name") String name,
+					  @JsonProperty("dpid") String dpid,
+					  @JsonProperty("port") short port,
+					  @JsonProperty("ipAddress") String ipAddress,
+					  @JsonProperty("prefixLength") int prefixLength) {
+		this.name = name;
+		this.dpid = HexString.toLong(dpid);
+		this.port = port;
+		this.ipAddress = InetAddresses.forString(ipAddress);
+		this.prefixLength = prefixLength;
+		this.switchPort = new SwitchPort(new Dpid(this.dpid), new Port(this.port));
+	}
 	
 	public String getName() {
 		return name;
 	}
 
-	@JsonProperty("name")
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public synchronized SwitchPort getSwitchPort() {
-		if (switchPort == null){
-			switchPort = new SwitchPort(new Dpid(dpid), new Port(port));
-		}
-		return switchPort;
+	public SwitchPort getSwitchPort() {
+		//TODO SwitchPort, Dpid and Port are mutable, but they could probably
+		//be made immutable which would prevent the need to copy
+		return new SwitchPort(new Dpid(dpid), new Port(port));
 	}
 	
 	public long getDpid() {
 		return dpid;
 	}
 
-	@JsonProperty("dpid")
-	public void setDpid(String dpid) {
-		this.dpid = HexString.toLong(dpid);
-	}
-
 	public short getPort() {
 		return port;
 	}
 
-	@JsonProperty("port")
-	public void setPort(short port) {
-		this.port = port;
-	}
-
 	public InetAddress getIpAddress() {
 		return ipAddress;
 	}
 
-	@JsonProperty("ipAddress")
-	public void setIpAddress(String ipAddress) {
-		this.ipAddress = InetAddresses.forString(ipAddress);
-	}
-
 	public int getPrefixLength() {
 		return prefixLength;
 	}
-
-	@JsonProperty("prefixLength")
-	public void setPrefixLength(int prefixLength) {
-		this.prefixLength = prefixLength;
-	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
index 56f4c04..fd9ba6f 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/PushedFlowMod.java
@@ -2,6 +2,13 @@
 
 import org.openflow.protocol.OFFlowMod;
 
+/**
+ * Wraps up a DPID and a OFFlowMod so we know how to delete
+ * the flow if we have to.
+ * 
+ * TODO This functionality should be handled by ONOS's flow layer in future.
+ *
+ */
 public class PushedFlowMod {
 	private long dpid;
 	private OFFlowMod flowMod;
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 8087471..1520c60 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibEntry.java
@@ -5,8 +5,8 @@
 import com.google.common.net.InetAddresses;
 
 public class RibEntry {
-	private InetAddress routerId;
-	private InetAddress nextHop;
+	private final InetAddress routerId;
+	private final InetAddress nextHop;
 	
 	public RibEntry(InetAddress routerId, InetAddress nextHop) {
 		this.routerId = routerId;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
index 7d4d7a5..d866304 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/RibUpdate.java
@@ -3,9 +3,9 @@
 public class RibUpdate {
 	public enum Operation {UPDATE, DELETE}; 
 	
-	private Operation operation;
-	private Prefix prefix;
-	private RibEntry ribEntry;
+	private final Operation operation;
+	private final Prefix prefix;
+	private final RibEntry ribEntry;
 	
 	public RibUpdate(Operation operation, Prefix prefix, RibEntry ribEntry) {
 		this.operation = operation;
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
index 3374fa7..b7c97f8 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/SwitchStorageImpl.java
@@ -227,6 +227,9 @@
         		IPortObject p = op.searchPort(dpid, port.getPortNumber());
         		if (p != null) {
             		log.error("SwitchStorage:addPort dpid:{} port:{} exists", dpid, port.getPortNumber());
+            		p.setState("ACTIVE");
+            		p.setPortState(port.getState());
+            		p.setDesc(port.getName());
             	} else {
             		p = op.newPort(dpid, port.getPortNumber());
             		p.setState("ACTIVE");
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index 493d58e..6e12f28 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -19,11 +19,11 @@
 import net.floodlightcontroller.core.IOFSwitch;
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
+import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.util.MACAddress;
-import net.onrc.onos.ofcontroller.bgproute.IPatriciaTrie;
+import net.onrc.onos.ofcontroller.bgproute.ILayer3InfoService;
 import net.onrc.onos.ofcontroller.bgproute.Interface;
-import net.onrc.onos.ofcontroller.bgproute.Prefix;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
@@ -40,32 +40,24 @@
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
 
-//TODO have L2 and also L3 mode, where it takes into account interface addresses
 //TODO REST API to inspect ARP table
 public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
-	private static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
+	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
 	private final long ARP_ENTRY_TIMEOUT = 600000; //ms (== 10 mins)
 	
 	private final long ARP_TIMER_PERIOD = 60000; //ms (== 1 min) 
 			
-	protected IFloodlightProviderService floodlightProvider;
-	protected ITopologyService topology;
+	private IFloodlightProviderService floodlightProvider;
+	private ITopologyService topology;
+	private ILayer3InfoService layer3;
 	
-	protected Map<InetAddress, ArpTableEntry> arpTable;
+	private Map<InetAddress, ArpTableEntry> arpTable;
 
-	protected SetMultimap<InetAddress, ArpRequest> arpRequests;
-	
-	public enum Mode {L2_MODE, L3_MODE}
-	
-	private Mode mode;
-	private IPatriciaTrie<Interface> interfacePtrie = null;
-	private Collection<Interface> interfaces = null;
-	private MACAddress routerMacAddress = null;
-	//private SwitchPort bgpdAttachmentPoint = null;
+	private SetMultimap<InetAddress, ArpRequest> arpRequests;
 	
 	private class ArpRequest {
-		private IArpRequester requester;
+		private final IArpRequester requester;
 		private boolean retry;
 		private long requestTime;
 		
@@ -91,36 +83,24 @@
 		}
 		
 		public void dispatchReply(InetAddress ipAddress, byte[] replyMacAddress) {
-			log.debug("Dispatching reply for {} to {}", ipAddress.getHostAddress(), 
-					requester);
 			requester.arpResponse(ipAddress, replyMacAddress);
 		}
 	}
 	
 	public ProxyArpManager(IFloodlightProviderService floodlightProvider,
-				ITopologyService topology){
+				ITopologyService topology, ILayer3InfoService layer3){
 		this.floodlightProvider = floodlightProvider;
 		this.topology = topology;
+		this.layer3 = layer3;
 		
 		arpTable = new HashMap<InetAddress, ArpTableEntry>();
 
 		arpRequests = Multimaps.synchronizedSetMultimap(
 				HashMultimap.<InetAddress, ArpRequest>create());
-		
-		mode = Mode.L2_MODE;
-	}
-	
-	public void setL3Mode(IPatriciaTrie<Interface> interfacePtrie, 
-			Collection<Interface> interfaces, MACAddress routerMacAddress) {
-		this.interfacePtrie = interfacePtrie;
-		this.interfaces = interfaces;
-		this.routerMacAddress = routerMacAddress;
-
-		mode = Mode.L3_MODE;
 	}
 	
 	public void startUp() {
-		Timer arpTimer = new Timer();
+		Timer arpTimer = new Timer("arp-processing");
 		arpTimer.scheduleAtFixedRate(new TimerTask() {
 			@Override
 			public void run() {
@@ -225,49 +205,31 @@
 	}
 	
 	protected void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp) {
-		log.trace("ARP request received for {}", 
-				bytesToStringAddr(arp.getTargetProtocolAddress()));
+		if (log.isTraceEnabled()) {
+			log.trace("ARP request received for {}", 
+					inetAddressToString(arp.getTargetProtocolAddress()));
+		}
 
 		InetAddress target;
-		InetAddress source;
 		try {
 			 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
-			 source = InetAddress.getByAddress(arp.getSenderProtocolAddress());
 		} catch (UnknownHostException e) {
 			log.debug("Invalid address in ARP request", e);
 			return;
 		}
-		
-		if (mode == Mode.L3_MODE) {
-			
-			//if (originatedOutsideNetwork(source)) {
-			if (originatedOutsideNetwork(sw.getId(), pi.getInPort())) {
-				//If the request came from outside our network, we only care if
-				//it was a request for one of our interfaces.
-				if (isInterfaceAddress(target)) {
-					log.trace("ARP request for our interface. Sending reply {} => {}",
-							target.getHostAddress(), routerMacAddress.toString());
-					sendArpReply(arp, sw.getId(), pi.getInPort(), routerMacAddress.toBytes());
-				}
-				return;
+
+		if (layer3.fromExternalNetwork(sw.getId(), pi.getInPort())) {
+			//If the request came from outside our network, we only care if
+			//it was a request for one of our interfaces.
+			if (layer3.isInterfaceAddress(target)) {
+				log.trace("ARP request for our interface. Sending reply {} => {}",
+						target.getHostAddress(), layer3.getRouterMacAddress());
+				
+				sendArpReply(arp, sw.getId(), pi.getInPort(), 
+						layer3.getRouterMacAddress().toBytes());
 			}
 			
-			/*
-			Interface intf = interfacePtrie.match(new Prefix(target.getAddress(), 32));
-			//if (intf != null && target.equals(intf.getIpAddress())) {
-			if (intf != null) {
-				if (target.equals(intf.getIpAddress())) {
-					//ARP request for one of our interfaces, we can reply straight away
-					sendArpReply(arp, sw.getId(), pi.getInPort(), routerMacAddress.toBytes());
-				}
-				// If we didn't enter the above if block, then we found a matching
-				// interface for the target IP but the request wasn't for us.
-				// This is someone else ARPing for a different host in the subnet.
-				// We shouldn't do anything in this case - if we let processing continue
-				// we'll end up erroneously re-broadcasting an ARP for someone else.
-				return;
-			}
-			*/
+			return;
 		}
 		
 		byte[] mac = lookupArpTable(arp.getTargetProtocolAddress());
@@ -280,25 +242,28 @@
 					new HostArpRequester(this, arp, sw.getId(), pi.getInPort()), false));
 						
 			//Flood the request out edge ports
-			//broadcastArpRequestOutEdge(pi.getPacketData(), sw.getId(), pi.getInPort());
 			sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
 		}
 		else {
 			//We know the address, so send a reply
-			log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
-					bytesToStringAddr(arp.getTargetProtocolAddress()),
-					MACAddress.valueOf(mac).toString(),
-					HexString.toHexString(sw.getId()), pi.getInPort()});
+			if (log.isTraceEnabled()) {
+				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+						inetAddressToString(arp.getTargetProtocolAddress()),
+						MACAddress.valueOf(mac).toString(),
+						HexString.toHexString(sw.getId()), pi.getInPort()});
+			}
 			
 			sendArpReply(arp, sw.getId(), pi.getInPort(), mac);
 		}
 	}
 	
 	protected void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
-		log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] { 
-				bytesToStringAddr(arp.getSenderProtocolAddress()),
-				HexString.toHexString(arp.getSenderHardwareAddress()),
-				HexString.toHexString(sw.getId()), pi.getInPort()});
+		if (log.isTraceEnabled()) {
+			log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] { 
+					inetAddressToString(arp.getSenderProtocolAddress()),
+					HexString.toHexString(arp.getSenderHardwareAddress()),
+					HexString.toHexString(sw.getId()), pi.getInPort()});
+		}
 		
 		updateArpTable(arp);
 		
@@ -307,6 +272,7 @@
 		try {
 			addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
 		} catch (UnknownHostException e) {
+			log.debug("Invalid address in ARP reply", e);
 			return;
 		}
 		
@@ -319,7 +285,6 @@
 			while (it.hasNext()) {
 				ArpRequest request = it.next();
 				it.remove();
-				//request.dispatchReply(addr, arp.getSenderHardwareAddress());
 				requestsToSend.add(request);
 			}
 		}
@@ -335,21 +300,22 @@
 		try {
 			addr = InetAddress.getByAddress(ipAddress);
 		} catch (UnknownHostException e) {
-			log.warn("Unable to create InetAddress", e);
+			log.debug("Unable to create InetAddress", e);
 			return null;
 		}
 		
 		ArpTableEntry arpEntry = arpTable.get(addr);
 		
 		if (arpEntry == null){
-			//log.debug("MAC for {} unknown", bytesToStringAddr(ipAddress));
 			return null;
 		}
 		
 		if (System.currentTimeMillis() - arpEntry.getTimeLastSeen() 
 				> ARP_ENTRY_TIMEOUT){
 			//Entry has timed out so we'll remove it and return null
-			log.debug("Timing out old ARP entry for {}", bytesToStringAddr(ipAddress));
+			log.trace("Removing expired ARP entry for {}", 
+					inetAddressToString(ipAddress));
+			
 			arpTable.remove(addr);
 			return null;
 		}
@@ -362,7 +328,7 @@
 		try {
 			addr = InetAddress.getByAddress(arp.getSenderProtocolAddress());
 		} catch (UnknownHostException e) {
-			log.warn("Unable to create InetAddress", e);
+			log.debug("Unable to create InetAddress", e);
 			return;
 		}
 		
@@ -380,10 +346,14 @@
 	}
 	
 	private void sendArpRequestForAddress(InetAddress ipAddress) {
-		//TODO what should the sender IP address be? Probably not 0.0.0.0
+		//TODO what should the sender IP address and MAC address be if no
+		//IP addresses are configured? Will there ever be a need to send
+		//ARP requests from the controller in that case?
+		//All-zero MAC address doesn't seem to work - hosts don't respond to it
+		
 		byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
 		byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
-		//byte[] bgpdMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
+		byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
 		byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff, 
 				(byte)0xff, (byte)0xff, (byte)0xff};
 		
@@ -392,56 +362,62 @@
 		arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
 			.setProtocolType(ARP.PROTO_TYPE_IP)
 			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
-			.setProtocolAddressLength((byte)4) //can't find the constant anywhere
+			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
 			.setOpCode(ARP.OP_REQUEST)
-			//.setSenderHardwareAddress(bgpdMac)
-			.setSenderHardwareAddress(routerMacAddress.toBytes())
-			//.setSenderProtocolAddress(zeroIpv4)
 			.setTargetHardwareAddress(zeroMac)
 			.setTargetProtocolAddress(ipAddress.getAddress());
 
+		MACAddress routerMacAddress = layer3.getRouterMacAddress();
+		//TODO hack for now as it's unclear what the MAC address should be
+		byte[] senderMacAddress = genericNonZeroMac;
+		if (routerMacAddress != null) {
+			senderMacAddress = routerMacAddress.toBytes();
+		}
+		arpRequest.setSenderHardwareAddress(senderMacAddress);
+		
 		byte[] senderIPAddress = zeroIpv4;
-		if (mode == Mode.L3_MODE) {
-			Interface intf = interfacePtrie.match(new Prefix(ipAddress.getAddress(), 32));
-			if (intf != null) {
-				senderIPAddress = intf.getIpAddress().getAddress();
-			}
+		Interface intf = layer3.getOutgoingInterface(ipAddress);
+		if (intf != null) {
+			senderIPAddress = intf.getIpAddress().getAddress();
 		}
 		
 		arpRequest.setSenderProtocolAddress(senderIPAddress);
 		
 		Ethernet eth = new Ethernet();
-		eth.setSourceMACAddress(routerMacAddress.toBytes())
+		eth.setSourceMACAddress(senderMacAddress)
 			.setDestinationMACAddress(broadcastMac)
 			.setEtherType(Ethernet.TYPE_ARP)
 			.setPayload(arpRequest);
 		
-		//broadcastArpRequestOutEdge(eth.serialize(), 0, OFPort.OFPP_NONE.getValue());
 		sendArpRequestToSwitches(ipAddress, eth.serialize());
 	}
 	
 	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
-		sendArpRequestToSwitches(dstAddress, arpRequest, 0, OFPort.OFPP_NONE.getValue());
+		sendArpRequestToSwitches(dstAddress, arpRequest, 
+				0, OFPort.OFPP_NONE.getValue());
 	}
+	
 	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
 			long inSwitch, short inPort) {
-		if (mode == Mode.L2_MODE) {
-			//log.debug("mode is l2");
-			broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
-		}
-		else if (mode == Mode.L3_MODE) {
-			//log.debug("mode is l3");
-			//TODO the case where it should be broadcast out all non-interface
-			//edge ports
-			Interface intf = interfacePtrie.match(new Prefix(dstAddress.getAddress(), 32));
+
+		if (layer3.hasLayer3Configuration()) {
+			Interface intf = layer3.getOutgoingInterface(dstAddress);
 			if (intf != null) {
 				sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
 			}
 			else {
-				log.debug("No interface found to send ARP request for {}", 
+				//TODO here it should be broadcast out all non-interface edge ports.
+				//I think we can assume that if it's not a request for an external 
+				//network, it's an ARP for a host in our own network. So we want to 
+				//send it out all edge ports that don't have an interface configured
+				//to ensure it reaches all hosts in our network.
+				log.debug("No interface found to send ARP request for {}",
 						dstAddress.getHostAddress());
 			}
 		}
+		else {
+			broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
+		}
 	}
 	
 	private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
@@ -471,7 +447,6 @@
 				}
 				
 				actions.add(new OFActionOutput(portNum));
-				//log.debug("Broadcasting out {}/{}", HexString.toHexString(sw.getId()), portNum);
 			}
 			
 			po.setActions(actions);
@@ -493,7 +468,10 @@
 	}
 	
 	private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
-		log.debug("Sending ARP request out {}/{}", HexString.toHexString(dpid), port);
+		if (log.isTraceEnabled()) {
+			log.trace("Sending ARP request out {}/{}", 
+					HexString.toHexString(dpid), port);
+		}
 		
 		OFPacketOut po = new OFPacketOut();
 		po.setInPort(OFPort.OFPP_NONE)
@@ -511,7 +489,7 @@
 		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
 		
 		if (sw == null) {
-			log.debug("Switch not found when sending ARP request");
+			log.warn("Switch not found when sending ARP request");
 			return;
 		}
 		
@@ -523,17 +501,32 @@
 		}
 	}
 	
+	private String inetAddressToString(byte[] bytes) {
+		try {
+			return InetAddress.getByAddress(bytes).getHostAddress();
+		} catch (UnknownHostException e) {
+			log.debug("Invalid IP address", e);
+			return "";
+		}
+	}
+	
+	/*
+	 * IProxyArpService methods
+	 */
+	
 	public void sendArpReply(ARP arpRequest, long dpid, short port, byte[] targetMac) {
-		log.trace("Sending reply {} => {} to {}", new Object[] {
-				bytesToStringAddr(arpRequest.getTargetProtocolAddress()),
-				HexString.toHexString(targetMac),
-				bytesToStringAddr(arpRequest.getSenderProtocolAddress())});
+		if (log.isTraceEnabled()) {
+			log.trace("Sending reply {} => {} to {}", new Object[] {
+					inetAddressToString(arpRequest.getTargetProtocolAddress()),
+					HexString.toHexString(targetMac),
+					inetAddressToString(arpRequest.getSenderProtocolAddress())});
+		}
 		
 		ARP arpReply = new ARP();
 		arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
 			.setProtocolType(ARP.PROTO_TYPE_IP)
 			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
-			.setProtocolAddressLength((byte)4) //can't find the constant anywhere
+			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
 			.setOpCode(ARP.OP_REPLY)
 			.setSenderHardwareAddress(targetMac)
 			.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
@@ -564,34 +557,19 @@
 		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
 		
 		if (sw == null) {
-			log.error("Switch {} not found when sending ARP reply", 
+			log.warn("Switch {} not found when sending ARP reply", 
 					HexString.toHexString(dpid));
 			return;
 		}
 		
 		try {
-			log.debug("Sending ARP reply to {}/{}", HexString.toHexString(sw.getId()), port);
 			sw.write(msgList, null);
 			sw.flush();
 		} catch (IOException e) {
-			log.warn("Failure writing packet out to switch", e);
+			log.error("Failure writing packet out to switch", e);
 		}
 	}
 
-	//TODO this should be put somewhere more central. I use it in BgpRoute as well.
-	//We need a HexString.toHexString() equivalent.
-	private String bytesToStringAddr(byte[] bytes) {
-		InetAddress addr;
-		try {
-			addr = InetAddress.getByAddress(bytes);
-		} catch (UnknownHostException e) {
-			log.warn(" ", e);
-			return "";
-		}
-		if (addr == null) return "";
-		else return addr.getHostAddress();
-	}
-	
 	@Override
 	public byte[] getMacAddress(InetAddress ipAddress) {
 		return lookupArpTable(ipAddress.getAddress());
@@ -601,57 +579,10 @@
 	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
 			boolean retry) {
 		arpRequests.put(ipAddress, new ArpRequest(requester, retry));
-		//storeRequester(ipAddress, requester, retry);
 		
 		//Sanity check to make sure we don't send a request for our own address
-		if (!isInterfaceAddress(ipAddress)) {
+		if (!layer3.isInterfaceAddress(ipAddress)) {
 			sendArpRequestForAddress(ipAddress);
 		}
 	}
-	
-	/*
-	 * TODO These methods might be more suited to some kind of L3 information service
-	 * that ProxyArpManager could query, rather than having the information 
-	 * embedded in ProxyArpManager. There may be many modules that need L3 information.
-	 */
-	
-	private boolean originatedOutsideNetwork(InetAddress source) {
-		Interface intf = interfacePtrie.match(new Prefix(source.getAddress(), 32));
-		if (intf != null) {
-			if (intf.getIpAddress().equals(source)) {
-				// This request must have been originated by us (the controller)
-				return false;
-			}
-			else {
-				// Source was in one of our interface subnets, but wasn't us.
-				// It must be external.
-				return true;
-			}
-		}
-		else {
-			// Source is not in one of our interface subnets. It's probably a host
-			// in our network as we should only receive ARPs broadcast by external
-			// hosts if they're in the same subnet.
-			return false;
-		}
-	}
-	
-	private boolean originatedOutsideNetwork(long inDpid, short inPort) {
-		for (Interface intf : interfaces) {
-			if (intf.getDpid() == inDpid && intf.getPort() == inPort) {
-				return true;
-			}
-		}
-		return false;
-	}
-	
-	private boolean isInterfaceAddress(InetAddress address) {
-		Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
-		return (intf != null && intf.getIpAddress().equals(address));
-	}
-	
-	private boolean inInterfaceSubnet(InetAddress address) {
-		Interface intf = interfacePtrie.match(new Prefix(address.getAddress(), 32));
-		return (intf != null && !intf.getIpAddress().equals(address));
-	}
 }