Implemented HostMonitor for probing IP addresses to detect hosts
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
index ecdb177..f27cd78 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
@@ -1,30 +1,67 @@
 package org.onlab.onos.net.host.impl;
 
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.jboss.netty.util.Timeout;
 import org.jboss.netty.util.TimerTask;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.Port;
 import org.onlab.onos.net.device.DeviceService;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.flow.instructions.Instruction;
+import org.onlab.onos.net.flow.instructions.Instructions;
 import org.onlab.onos.net.host.HostProvider;
 import org.onlab.onos.net.host.HostService;
-import org.onlab.onos.net.packet.PacketProvider;
+import org.onlab.onos.net.host.HostStore;
+import org.onlab.onos.net.host.PortAddresses;
+import org.onlab.onos.net.packet.DefaultOutboundPacket;
+import org.onlab.onos.net.packet.OutboundPacket;
+import org.onlab.onos.net.packet.PacketService;
 import org.onlab.onos.net.topology.TopologyService;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
 import org.onlab.util.Timer;
 
+/**
+ * Monitors hosts on the dataplane to detect changes in host data.
+ * <p/>
+ * The HostMonitor can monitor hosts that have already been detected for
+ * changes. At an application's request, it can also monitor and actively
+ * probe for hosts that have not yet been detected (specified by IP address).
+ */
 public class HostMonitor implements TimerTask {
 
+    private static final byte[] DEFAULT_MAC_ADDRESS =
+            MacAddress.valueOf("00:00:00:00:00:01").getAddress();
+
+    private static final byte[] ZERO_MAC_ADDRESS =
+            MacAddress.valueOf("00:00:00:00:00:00").getAddress();
+
+    // TODO put on Ethernet
+    private static final byte[] BROADCAST_MAC =
+            MacAddress.valueOf("ff:ff:ff:ff:ff:ff").getAddress();
+
     private final HostService hostService;
     private final TopologyService topologyService;
     private final DeviceService deviceService;
     private final HostProvider hostProvider;
-    private final PacketProvider packetProvider;
+    private final PacketService packetService;
+    private final HostStore hostStore;
 
-    private final Set<IpPrefix> monitoredAddresses;
+    private final Set<IpAddress> monitoredAddresses;
 
     private final long probeRate;
 
@@ -32,12 +69,14 @@
 
     public HostMonitor(HostService hostService, TopologyService topologyService,
                        DeviceService deviceService,
-                       HostProvider hostProvider, PacketProvider packetProvider) {
+                       HostProvider hostProvider, PacketService packetService,
+                       HostStore hostStore) {
         this.hostService = hostService;
         this.topologyService = topologyService;
         this.deviceService = deviceService;
         this.hostProvider = hostProvider;
-        this.packetProvider = packetProvider;
+        this.packetService = packetService;
+        this.hostStore = hostStore;
 
         monitoredAddresses = new HashSet<>();
 
@@ -46,11 +85,11 @@
         timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
     }
 
-    public void addMonitoringFor(IpPrefix ip) {
+    public void addMonitoringFor(IpAddress ip) {
         monitoredAddresses.add(ip);
     }
 
-    public void stopMonitoring(IpPrefix ip) {
+    public void stopMonitoring(IpAddress ip) {
         monitoredAddresses.remove(ip);
     }
 
@@ -60,8 +99,8 @@
 
     @Override
     public void run(Timeout timeout) throws Exception {
-        for (IpPrefix ip : monitoredAddresses) {
-            Set<Host> hosts = hostService.getHostsByIp(ip);
+        for (IpAddress ip : monitoredAddresses) {
+            Set<Host> hosts = Collections.emptySet(); //TODO hostService.getHostsByIp(ip);
 
             if (hosts.isEmpty()) {
                 sendArpRequest(ip);
@@ -80,28 +119,70 @@
      *
      * @param targetIp IP address to ARP for
      */
-    private void sendArpRequest(IpPrefix targetIp) {
-        // emit ARP packet out appropriate ports
+    private void sendArpRequest(IpAddress targetIp) {
 
-        // if ip in one of the configured (external) subnets
-        //   sent out that port
-        // else (ip isn't in any configured subnet)
-        //   send out all non-external edge ports
-
-        /*for (Device device : deviceService.getDevices()) {
+        // Find ports with an IP address in the target's subnet and sent ARP
+        // probes out those ports.
+        for (Device device : deviceService.getDevices()) {
             for (Port port : deviceService.getPorts(device.id())) {
-                for (IpPrefix ip : port.ipAddresses()) {
-                    if (ip.contains(targetIp)) {
-                        sendProbe(port, targetIp);
-                        continue;
-                    }
+                ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+                PortAddresses addresses = hostStore.getAddressBindingsForPort(cp);
+
+                if (addresses.ip().contains(targetIp)) {
+                    sendProbe(device.id(), port, addresses, targetIp);
                 }
             }
-        }*/
+        }
 
+        // TODO case where no address was found.
+        // Broadcast out internal edge ports?
     }
 
-    private void sendProbe(Port port, IpPrefix targetIp) {
+    private void sendProbe(DeviceId deviceId, Port port, PortAddresses portAddresses,
+            IpAddress targetIp) {
+        Ethernet arpPacket = createArpFor(targetIp, portAddresses);
 
+        List<Instruction> instructions = new ArrayList<>();
+        instructions.add(Instructions.createOutput(port.number()));
+
+        TrafficTreatment treatment =
+                new DefaultTrafficTreatment.Builder()
+                .add(Instructions.createOutput(port.number()))
+                .build();
+
+        OutboundPacket outboundPacket =
+                new DefaultOutboundPacket(deviceId, treatment,
+                        ByteBuffer.wrap(arpPacket.serialize()));
+
+        packetService.emit(outboundPacket);
+    }
+
+    private Ethernet createArpFor(IpAddress targetIp, PortAddresses portAddresses) {
+
+        ARP arp = new ARP();
+        arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
+           .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+           .setProtocolType(ARP.PROTO_TYPE_IP)
+           .setProtocolAddressLength((byte) IpPrefix.INET_LEN);
+
+        byte[] sourceMacAddress;
+        if (portAddresses.mac() == null) {
+            sourceMacAddress = DEFAULT_MAC_ADDRESS;
+        } else {
+            sourceMacAddress = portAddresses.mac().getAddress();
+        }
+
+        arp.setSenderHardwareAddress(sourceMacAddress)
+           .setSenderProtocolAddress(portAddresses.ip().toOctets())
+           .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+           .setTargetProtocolAddress(targetIp.toOctets());
+
+        Ethernet ethernet = new Ethernet();
+        ethernet.setEtherType(Ethernet.TYPE_ARP)
+                .setDestinationMACAddress(BROADCAST_MAC)
+                .setSourceMACAddress(sourceMacAddress)
+                .setPayload(arp);
+
+        return ethernet;
     }
 }