initial impl of proxy arp

Change-Id: I131667e8051e88c27f5fa020b580be57fee358ea
diff --git a/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
new file mode 100644
index 0000000..e6fe43b
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/proxyarp/ProxyArpService.java
@@ -0,0 +1,29 @@
+package org.onlab.onos.net.proxyarp;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+
+/**
+ * Service for processing arp requests on behalf of applications.
+ */
+public interface ProxyArpService {
+
+    /**
+     * Returns whether this particular ip address is known to the system.
+     *
+     * @param addr
+     *            a ip address
+     * @return true if know, false otherwise
+     */
+    boolean known(IpPrefix addr);
+
+    /**
+     * Sends a reply for a given request. If the host is not known then the arp
+     * will be flooded at all edge ports.
+     *
+     * @param request
+     *            an arp request
+     */
+    void reply(Ethernet request);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/proxyarp/package-info.java b/core/api/src/main/java/org/onlab/onos/net/proxyarp/package-info.java
new file mode 100644
index 0000000..4917c6e
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/proxyarp/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Base abstractions related to the proxy arp service.
+ */
+package org.onlab.onos.net.proxyarp;
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index fe146aa..1d90fd3 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -45,9 +45,9 @@
  */
 @Component(immediate = true)
 @Service
-public class DeviceManager
-extends AbstractProviderRegistry<DeviceProvider, DeviceProviderService>
-implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
+public class DeviceManager extends
+    AbstractProviderRegistry<DeviceProvider, DeviceProviderService> implements
+        DeviceService, DeviceAdminService, DeviceProviderRegistry {
 
     private static final String DEVICE_ID_NULL = "Device ID cannot be null";
     private static final String PORT_NUMBER_NULL = "Port number cannot be null";
@@ -57,8 +57,7 @@
 
     private final Logger log = getLogger(getClass());
 
-    protected final AbstractListenerRegistry<DeviceEvent, DeviceListener>
-    listenerRegistry = new AbstractListenerRegistry<>();
+    protected final AbstractListenerRegistry<DeviceEvent, DeviceListener> listenerRegistry = new AbstractListenerRegistry<>();
 
     private final DeviceStoreDelegate delegate = new InternalStoreDelegate();
 
@@ -169,28 +168,31 @@
     }
 
     @Override
-    protected DeviceProviderService createProviderService(DeviceProvider provider) {
+    protected DeviceProviderService createProviderService(
+            DeviceProvider provider) {
         return new InternalDeviceProviderService(provider);
     }
 
     // Personalized device provider service issued to the supplied provider.
-    private class InternalDeviceProviderService
-    extends AbstractProviderService<DeviceProvider>
-    implements DeviceProviderService {
+    private class InternalDeviceProviderService extends
+    AbstractProviderService<DeviceProvider> implements
+    DeviceProviderService {
 
         InternalDeviceProviderService(DeviceProvider provider) {
             super(provider);
         }
 
         @Override
-        public void deviceConnected(DeviceId deviceId, DeviceDescription deviceDescription) {
+        public void deviceConnected(DeviceId deviceId,
+                DeviceDescription deviceDescription) {
             checkNotNull(deviceId, DEVICE_ID_NULL);
             checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
             checkValidity();
             DeviceEvent event = store.createOrUpdateDevice(provider().id(),
                     deviceId, deviceDescription);
 
-            // If there was a change of any kind, trigger role selection process.
+            // If there was a change of any kind, trigger role selection
+            // process.
             if (event != null) {
                 log.info("Device {} connected", deviceId);
                 mastershipService.requestRoleFor(deviceId);
@@ -212,25 +214,30 @@
         }
 
         @Override
-        public void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions) {
+        public void updatePorts(DeviceId deviceId,
+                List<PortDescription> portDescriptions) {
             checkNotNull(deviceId, DEVICE_ID_NULL);
-            checkNotNull(portDescriptions, "Port descriptions list cannot be null");
+            checkNotNull(portDescriptions,
+                    "Port descriptions list cannot be null");
             checkValidity();
-            List<DeviceEvent> events = store.updatePorts(deviceId, portDescriptions);
+            List<DeviceEvent> events = store.updatePorts(deviceId,
+                    portDescriptions);
             for (DeviceEvent event : events) {
                 post(event);
             }
         }
 
         @Override
-        public void portStatusChanged(DeviceId deviceId, PortDescription portDescription) {
+        public void portStatusChanged(DeviceId deviceId,
+                PortDescription portDescription) {
             checkNotNull(deviceId, DEVICE_ID_NULL);
             checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
             checkValidity();
-            DeviceEvent event = store.updatePortStatus(deviceId, portDescription);
+            DeviceEvent event = store.updatePortStatus(deviceId,
+                    portDescription);
             if (event != null) {
-                log.info("Device {} port {} status changed", deviceId,
-                        event.port().number());
+                log.info("Device {} port {} status changed", deviceId, event
+                        .port().number());
                 post(event);
             }
         }
@@ -238,8 +245,8 @@
         @Override
         public void unableToAssertRole(DeviceId deviceId, MastershipRole role) {
             // FIXME: implement response to this notification
-            log.warn("Failed to assert role [{}] onto Device {}",
-                    role, deviceId);
+            log.warn("Failed to assert role [{}] onto Device {}", role,
+                    deviceId);
         }
     }
 
@@ -255,7 +262,8 @@
         @Override
         public void event(MastershipEvent event) {
             if (event.master().equals(clusterService.getLocalNode().id())) {
-                MastershipTerm term = mastershipService.requestTermService().getMastershipTerm(event.subject());
+                MastershipTerm term = mastershipService.requestTermService()
+                        .getMastershipTerm(event.subject());
                 clockService.setMastershipTerm(event.subject(), term);
                 applyRole(event.subject(), MastershipRole.MASTER);
             } else {
diff --git a/core/net/src/main/java/org/onlab/onos/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/proxyarp/impl/ProxyArpManager.java
new file mode 100644
index 0000000..f267f68
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/proxyarp/impl/ProxyArpManager.java
@@ -0,0 +1,100 @@
+package org.onlab.onos.proxyarp.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.Host;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.packet.DefaultOutboundPacket;
+import org.onlab.onos.net.packet.PacketService;
+import org.onlab.onos.net.proxyarp.ProxyArpService;
+import org.onlab.onos.net.topology.TopologyService;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.VlanId;
+
+public class ProxyArpManager implements ProxyArpService {
+
+    private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
+    private static final String REQUEST_NULL = "Arp request cannot be null.";
+    private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
+    private static final String NOT_ARP_REQUEST = "ARP is not a request.";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected TopologyService topologyService;
+
+    @Override
+    public boolean known(IpPrefix addr) {
+        checkNotNull(MAC_ADDR_NULL, addr);
+        Set<Host> hosts = hostService.getHostsByIp(addr);
+        return !hosts.isEmpty();
+    }
+
+    @Override
+    public void reply(Ethernet request) {
+        checkNotNull(REQUEST_NULL, request);
+        checkArgument(request.getEtherType() == Ethernet.TYPE_ARP,
+                REQUEST_NOT_ARP);
+        ARP arp = (ARP) request.getPayload();
+        checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
+
+        VlanId vlan = VlanId.vlanId(request.getVlanID());
+        Set<Host> hosts = hostService.getHostsByIp(IpPrefix.valueOf(arp
+                .getTargetProtocolAddress()));
+
+        Host h = null;
+        for (Host host : hosts) {
+            if (host.vlan().equals(vlan)) {
+                h = host;
+                break;
+            }
+        }
+
+        if (h == null) {
+            flood(request);
+            return;
+        }
+
+        Ethernet arpReply = buildArpReply(h, request);
+        // TODO: check send status with host service.
+        TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+        builder.setOutput(h.location().port());
+        packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
+                builder.build(), ByteBuffer.wrap(arpReply.serialize())));
+    }
+
+    private void flood(Ethernet request) {
+        // TODO: flood on all edge ports.
+    }
+
+    private Ethernet buildArpReply(Host h, Ethernet request) {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(request.getSourceMACAddress());
+        eth.setSourceMACAddress(h.mac().getAddress());
+        eth.setEtherType(Ethernet.TYPE_ARP);
+        ARP arp = new ARP();
+        arp.setOpCode(ARP.OP_REPLY);
+        arp.setSenderHardwareAddress(h.mac().getAddress());
+        arp.setTargetHardwareAddress(request.getSourceMACAddress());
+
+        arp.setTargetProtocolAddress(((ARP) request.getPayload())
+                .getSenderProtocolAddress());
+        arp.setSenderProtocolAddress(h.ipAddresses().iterator().next().toInt());
+        eth.setPayload(arp);
+        return eth;
+    }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/proxyarp/impl/package-info.java b/core/net/src/main/java/org/onlab/onos/proxyarp/impl/package-info.java
new file mode 100644
index 0000000..a9ad72a
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/proxyarp/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Core subsystem for responding to arp requests.
+ */
+package org.onlab.onos.proxyarp.impl;
\ No newline at end of file
diff --git a/tools/test/bin/onos-start-network b/tools/test/bin/onos-start-network
new file mode 100755
index 0000000..c8245ab
--- /dev/null
+++ b/tools/test/bin/onos-start-network
@@ -0,0 +1,17 @@
+#!/bin/bash
+#-------------------------------------------------------------------------------
+# Verifies connectivity to each node in ONOS cell.
+#-------------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+SSHCMD="ssh -o PasswordAuthentication=no"
+SCPCMD="scp -q -o PasswordAuthentication=no"
+
+echo "Copying topology files to mininet vm."
+$SSHCMD -n $ONOS_USER@$OCN mkdir -p topos
+$SCPCMD $ONOS_ROOT/tools/test/topos/* $ONOS_USER@$OCN:topos/
+
+echo "Starting Network."
+$SSHCMD -t $ONOS_USER@$OCN sudo python topos/sol.py $(env | sort | egrep "OC[0-9]+" | cut -d= -f2)