Merge remote-tracking branch 'upstream/master'
diff --git a/src/main/java/net/floodlightcontroller/util/MACAddress.java b/src/main/java/net/floodlightcontroller/util/MACAddress.java
index b77d4cc..b143bda 100644
--- a/src/main/java/net/floodlightcontroller/util/MACAddress.java
+++ b/src/main/java/net/floodlightcontroller/util/MACAddress.java
@@ -1,5 +1,6 @@
 package net.floodlightcontroller.util;
 
+import java.io.Serializable;
 import java.util.Arrays;
 
 import net.onrc.onos.ofcontroller.util.serializers.MACAddressDeserializer;
@@ -15,7 +16,8 @@
  */
 @JsonDeserialize(using=MACAddressDeserializer.class)
 @JsonSerialize(using=MACAddressSerializer.class)
-public class MACAddress {
+public class MACAddress implements Serializable{
+    private static final long serialVersionUID = 10000L;
     public static final int MAC_ADDRESS_LENGTH = 6;
     private byte[] address = new byte[MAC_ADDRESS_LENGTH];
 
diff --git a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
index 034fe25..0f03d77 100644
--- a/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
+++ b/src/main/java/net/onrc/onos/datagrid/IDatagridService.java
@@ -170,5 +170,5 @@
      * Send an ARP request to other ONOS instances
      * @param arpRequest The request packet to send
      */
-    public void sendArpRequest(ArpMessage arpMessage);
+    public void sendArpRequest(ArpMessage arpMessage);  
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
index be495b9..13b9182 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
@@ -8,6 +8,7 @@
 	public IDeviceObject addDevice(IDevice device);
 	public IDeviceObject updateDevice(IDevice device);
 	public void removeDevice(IDevice device);
+	public void removeDevice(IDeviceObject deviceObject);
 	public IDeviceObject getDeviceByMac(String mac);
 	public IDeviceObject getDeviceByIP(int ipv4Address);
 	public void changeDeviceAttachments(IDevice device);
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpMessage.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpMessage.java
index 5f22ca2..ee8f23d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpMessage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpMessage.java
@@ -2,6 +2,7 @@
 
 import java.io.Serializable;
 import java.net.InetAddress;
+import net.floodlightcontroller.util.MACAddress;
 
 public class ArpMessage implements Serializable {
 
@@ -14,6 +15,14 @@
 	private final InetAddress forAddress;
 	private final byte[] packetData;
 	
+	//ARP reply message needs MAC info
+	private final MACAddress mac;
+	//only send the ARP request message to the device attachment needs the attachment switch and port. 
+	private final long outSwitch; 
+	private final short outPort;
+	
+
+
 	public enum Type {
 		REQUEST,
 		REPLY
@@ -24,14 +33,41 @@
 		this.type = type;
 		this.forAddress = address;
 		this.packetData = eth;
+		this.mac = null;
+		this.outSwitch = -1;
+		this.outPort = -1;
 	}
 	
 	private ArpMessage(Type type, InetAddress address) {
 		this.type = type;
 		this.forAddress = address;
 		this.packetData = null;
+		this.mac = null;
+		this.outSwitch = -1;
+		this.outPort = -1;
+		
+	}
+	// the ARP reply message with MAC
+	private ArpMessage(Type type, InetAddress address, MACAddress mac) {
+		this.type = type;
+		this.forAddress = address;
+		this.packetData = null;
+		this.mac = mac;
+		this.outSwitch = -1;
+		this.outPort = -1;
 	}
 	
+	// construct ARP request message with attachment switch and port
+	private ArpMessage(Type type, InetAddress address, byte[] arpRequest,
+			long outSwitch, short outPort) {
+		this.type = type;
+		this.forAddress = address;
+		this.packetData = arpRequest; 	
+		this.mac = null;
+		this.outSwitch = outSwitch; 
+		this.outPort = outPort;	
+	}
+
 	public static ArpMessage newRequest(InetAddress forAddress, byte[] arpRequest) {
 		return new ArpMessage(Type.REQUEST, forAddress, arpRequest);
 	}
@@ -39,6 +75,16 @@
 	public static ArpMessage newReply(InetAddress forAddress) {
 		return new ArpMessage(Type.REPLY, forAddress);
 	}
+	//ARP reply message with MAC
+	public static ArpMessage newReply(InetAddress forAddress, MACAddress mac) {
+		return new ArpMessage(Type.REPLY, forAddress, mac);
+
+	}
+	//ARP reqsuest message with attachment switch and port
+	public static ArpMessage newRequest(InetAddress forAddress, byte[] arpRequest, long outSwitch, short outPort ) {
+		return new ArpMessage(Type.REQUEST, forAddress, arpRequest, outSwitch, outPort);
+
+	}
 
 	public Type getType() {
 		return type;
@@ -51,4 +97,16 @@
 	public byte[] getPacket() {
 		return packetData;
 	}
+	public MACAddress getMAC() {
+		return mac;
+	}
+
+	public long getOutSwitch() {
+		return outSwitch;
+	}
+
+	public short getOutPort() {
+		return outPort;
+	}
+
 }
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 415d697..c53e8e1 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -32,6 +32,7 @@
 import net.onrc.onos.ofcontroller.core.IDeviceStorage;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoSwitchService;
 import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
 import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
@@ -60,9 +61,9 @@
 										IArpEventHandler, IFloodlightModule {
 	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
-	private final long ARP_TIMER_PERIOD = 60000; //ms (== 1 min) 
-	
-	private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
+	private final long ARP_TIMER_PERIOD = 100; //ms  
+
+	private static final int ARP_REQUEST_TIMEOUT = 500; //ms
 			
 	private IFloodlightProviderService floodlightProvider;
 	private ITopologyService topology;
@@ -221,6 +222,15 @@
 					log.debug("Cleaning expired ARP request for {}", 
 							entry.getKey().getHostAddress());
 		
+					//if he ARP Request is expired and then delete the device
+					IDeviceObject targetDevice = 
+							deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
+					
+					if(targetDevice!=null)
+					{deviceStorage.removeDevice(targetDevice);
+					 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice.toString());
+					}				
+					
 					it.remove();
 					
 					if (request.shouldRetry()) {
@@ -277,6 +287,7 @@
 		if (eth.getEtherType() == Ethernet.TYPE_ARP){
 			ARP arp = (ARP) eth.getPayload();	
 			if (arp.getOpCode() == ARP.OP_REQUEST) {
+				log.debug("receive ARP request");
 				//TODO check what the DeviceManager does about propagating
 				//or swallowing ARPs. We want to go after DeviceManager in the
 				//chain but we really need it to CONTINUE ARP packets so we can
@@ -284,7 +295,9 @@
 				handleArpRequest(sw, pi, arp, eth);
 			}
 			else if (arp.getOpCode() == ARP.OP_REPLY) {
-				//handleArpReply(sw, pi, arp);
+					log.debug("receive ARP reply");
+					handleArpReply(sw, pi, arp);
+					sendToOtherNodesReply(eth, pi);
 			}
 		}
 		
@@ -301,7 +314,7 @@
 
 		InetAddress target;
 		try {
-			 target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+			target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
 		} catch (UnknownHostException e) {
 			log.debug("Invalid address in ARP request", e);
 			return;
@@ -313,66 +326,72 @@
 			if (configService.isInterfaceAddress(target)) {
 				log.trace("ARP request for our interface. Sending reply {} => {}",
 						target.getHostAddress(), configService.getRouterMacAddress());
-				
+
 				sendArpReply(arp, sw.getId(), pi.getInPort(), 
 						configService.getRouterMacAddress());
 			}
-			
+
 			return;
 		}
 		
 		//MACAddress macAddress = arpCache.lookup(target);
-		
+				
 		IDeviceObject targetDevice = 
 				deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
-		
 		log.debug("targetDevice: {}", targetDevice);
-		
+
+		arpRequests.put(target, new ArpRequest(
+				new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+
 		if (targetDevice != null) {
-			// We have the device in our database, so send a reply
+			// Even the device in our database is not null, we do not reply to the request directly, but to check whether the device is still valid
 			MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
-			
+
 			if (log.isTraceEnabled()) {
-				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
+				log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}", new Object [] {
 						inetAddressToString(arp.getTargetProtocolAddress()),
 						macAddress.toString(),
 						HexString.toHexString(sw.getId()), pi.getInPort()});
 			}
-			
-			sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
-		}
-		else {
+
+			// sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+
+			log.trace("Checking the device info from DB is still valid or not");
+			Iterable<IPortObject> outPorts=targetDevice.getAttachedPorts();	
+
+			if(!outPorts.iterator().hasNext()){
+				log.debug("outPort : null");
+				sendToOtherNodes(eth, pi);
+			}else{
+
+				for (IPortObject portObject : outPorts) {
+					long outSwitch=0;
+					short outPort=0;   
+
+
+					if (!portObject.getLinkedPorts().iterator().hasNext()) {
+						outPort=portObject.getNumber();					
+						log.debug("outPort:{} ", outPort);
+					}   
+
+					Iterable<ISwitchObject>  outSwitches= targetDevice.getSwitch(); 
+
+					for (ISwitchObject outswitch : outSwitches) {
+
+						outSwitch= HexString.toLong(outswitch.getDPID());
+						log.debug("outSwitch.DPID:{}; outPort: {}", outswitch.getDPID(), outPort );
+						sendToOtherNodes( eth, pi, outSwitch, outPort);
+					}
+				}
+			}
+
+		}else {
+			log.debug("The Device info in DB is {} for IP {}", targetDevice, inetAddressToString(arp.getTargetProtocolAddress()));
+
 			// We don't know the device so broadcast the request out
-			// the edge of the network
-			
-			//Record where the request came from so we know where to send the reply
-			arpRequests.put(target, new ArpRequest(
-					new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
-			
 			sendToOtherNodes(eth, pi);
 		}
-		
-		/*if (macAddress == null){
-			//MAC address is not in our ARP cache.
-			
-			//Record where the request came from so we know where to send the reply
-			//arpRequests.put(target, new ArpRequest(
-					//new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
-						
-			//Flood the request out edge ports
-			//sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
-		}
-		else {
-			//We know the address, so send a reply
-			if (log.isTraceEnabled()) {
-				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
-						inetAddressToString(arp.getTargetProtocolAddress()),
-						macAddress.toString(),
-						HexString.toHexString(sw.getId()), pi.getInPort()});
-			}
-			
-			sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
-		}*/
+ 
 	}
 	
 	@SuppressWarnings("unused")
@@ -394,7 +413,7 @@
 		
 		MACAddress senderMacAddress = MACAddress.valueOf(arp.getSenderHardwareAddress());
 		
-		arpCache.update(senderIpAddress, senderMacAddress);
+		//arpCache.update(senderIpAddress, senderMacAddress);
 		
 		//See if anyone's waiting for this ARP reply
 		Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
@@ -514,6 +533,51 @@
 		
 		datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize()));
 	}
+	//hazelcast to other ONOS instances to send the ARP packet out on outPort of outSwitch
+	private void sendToOtherNodes(Ethernet eth, OFPacketIn pi, long outSwitch, short outPort) {
+		ARP arp = (ARP) eth.getPayload();
+		
+		if (log.isTraceEnabled()) {
+				log.trace("Sending ARP request for {} to other ONOS instances with outSwitch {} ",
+				inetAddressToString(arp.getTargetProtocolAddress()), String.valueOf(outSwitch));
+			
+		}
+		
+		InetAddress targetAddress;
+		try {
+				targetAddress = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+		} catch (UnknownHostException e) {
+				log.error("Unknown host", e);
+				return;
+		}
+		
+		datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize(), outSwitch, outPort)); 
+		//datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize()));
+		
+		
+	}
+	private void sendToOtherNodesReply(Ethernet eth, OFPacketIn pi) {
+		ARP arp = (ARP) eth.getPayload();
+		
+		if (log.isTraceEnabled()) {
+				log.trace("Sending ARP reply for {} to other ONOS instances",
+				inetAddressToString(arp.getSenderProtocolAddress()));
+		}
+		
+		InetAddress targetAddress;		
+		MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
+		
+		try {
+				targetAddress = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+		} catch (UnknownHostException e) {
+				log.error("Unknown host", e);
+				return;
+		}
+		
+		datagrid.sendArpRequest(ArpMessage.newReply(targetAddress,mac));
+		//datagrid.sendArpReply(ArpMessage.newRequest(targetAddress, eth.serialize()));
+	
+	}
 	
 	private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
 		for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
@@ -604,7 +668,7 @@
 			}
 		}
 		
-		log.debug("Broadcast ARP request for to: {}", switchPorts);
+		log.debug("Broadcast ARP request to: {}", switchPorts);
 	}
 	
 	private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
@@ -744,23 +808,26 @@
 	
 	@Override
 	public void arpRequestNotification(ArpMessage arpMessage) {
-		//log.debug("Received ARP notification from other instances");
-		
+		log.debug("Received ARP notification from other instances");
+
 		switch (arpMessage.getType()){
 		case REQUEST:
-			log.debug("Received ARP request notification for {}", 
-					arpMessage.getAddress());
-			broadcastArpRequestOutMyEdge(arpMessage.getPacket());
+			if(arpMessage.getOutSwitch() == -1 || arpMessage.getOutPort() == -1){	
+				broadcastArpRequestOutMyEdge(arpMessage.getPacket());					
+			}else{					
+				sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
+				log.debug("OutSwitch in ARP request message is: {}; OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
+			}
 			break;
 		case REPLY:
 			log.debug("Received ARP reply notification for {}",
 					arpMessage.getAddress());
-			sendArpReplyToWaitingRequesters(arpMessage.getAddress());
+			sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
 			break;
 		}
 	}
 	
-	private void sendArpReplyToWaitingRequesters(InetAddress address) {
+	private void sendArpReplyToWaitingRequesters(InetAddress address, MACAddress mac) {
 		log.debug("Sending ARP reply for {} to requesters", 
 				address.getHostAddress());
 		
@@ -778,13 +845,13 @@
 			}
 		}
 		
-		IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
+		/*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
 				InetAddresses.coerceToInteger(address));
 		
 		MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
 		
 		log.debug("Found {} at {} in network map", 
-				address.getHostAddress(), mac);
+				address.getHostAddress(), mac);*/
 		
 		//Don't hold an ARP lock while dispatching requests
 		for (ArpRequest request : requestsToSend) {