Merge "Fixing reactive forwarding not to forward LLDP & BDDP."
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
index 3a93b30..509c6ac 100644
--- 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
@@ -18,7 +18,7 @@
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.packet.PacketContext;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
+import org.onlab.packet.Ip4Address;
 
 /**
  * Service for processing arp requests on behalf of applications.
@@ -27,12 +27,12 @@
 public interface ProxyArpService {
 
     /**
-     * Returns whether this particular ip address is known to the system.
+     * Returns whether this particular IPv4 address is known to the system.
      *
-     * @param addr a ip address
+     * @param addr an IPv4 address
      * @return true if know, false otherwise
      */
-    boolean known(IpAddress addr);
+    boolean known(Ip4Address addr);
 
     /**
      * Sends a reply for a given request. If the host is not known then the arp
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 49528d0..c0a3f16 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -56,6 +56,7 @@
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.slf4j.Logger;
@@ -113,7 +114,7 @@
     }
 
     @Override
-    public boolean known(IpAddress addr) {
+    public boolean known(Ip4Address addr) {
         checkNotNull(addr, MAC_ADDR_NULL);
         Set<Host> hosts = hostService.getHostsByIp(addr);
         return !hosts.isEmpty();
@@ -131,9 +132,8 @@
         // If the request came from outside the network, only reply if it was
         // for one of our external addresses.
         if (isOutsidePort(inPort)) {
-            IpAddress target =
-                IpAddress.valueOf(IpAddress.Version.INET,
-                                  arp.getTargetProtocolAddress());
+            Ip4Address target =
+                Ip4Address.valueOf(arp.getTargetProtocolAddress());
             Set<PortAddresses> addressSet =
                 hostService.getAddressBindingsForPort(inPort);
 
@@ -141,7 +141,7 @@
                 for (InterfaceIpAddress ia : addresses.ipAddresses()) {
                     if (ia.ipAddress().equals(target)) {
                         Ethernet arpReply =
-                            buildArpReply(ia.ipAddress(), addresses.mac(), eth);
+                            buildArpReply(target, addresses.mac(), eth);
                         sendTo(arpReply, inPort);
                     }
                 }
@@ -151,9 +151,8 @@
             // If the source address matches one of our external addresses
             // it could be a request from an internal host to an external
             // address. Forward it over to the correct port.
-            IpAddress source =
-                IpAddress.valueOf(IpAddress.Version.INET,
-                                  arp.getSenderProtocolAddress());
+            Ip4Address source =
+                Ip4Address.valueOf(arp.getSenderProtocolAddress());
             PortAddresses sourceAddresses = findPortInSubnet(source);
             if (sourceAddresses != null) {
                 for (InterfaceIpAddress ia : sourceAddresses.ipAddresses()) {
@@ -168,9 +167,8 @@
         // Continue with normal proxy ARP case
 
         VlanId vlan = VlanId.vlanId(eth.getVlanID());
-        Set<Host> hosts =
-            hostService.getHostsByIp(IpAddress.valueOf(IpAddress.Version.INET,
-                                        arp.getTargetProtocolAddress()));
+        Set<Host> hosts = hostService.getHostsByIp(
+                        Ip4Address.valueOf(arp.getTargetProtocolAddress()));
 
         Host dst = null;
         Host src = hostService.getHost(HostId.hostId(eth.getSourceMAC(),
@@ -188,11 +186,19 @@
             return;
         }
 
-        // TODO find the correct IP address
-        IpAddress ipAddress = dst.ipAddresses().iterator().next();
-        Ethernet arpReply = buildArpReply(ipAddress, dst.mac(), eth);
-        // TODO: check send status with host service.
-        sendTo(arpReply, src.location());
+        //
+        // TODO find the correct IP address.
+        // Right now we use the first IPv4 address that is found.
+        //
+        for (IpAddress ipAddress : dst.ipAddresses()) {
+            Ip4Address ip4Address = ipAddress.getIp4Address();
+            if (ip4Address != null) {
+                Ethernet arpReply = buildArpReply(ip4Address, dst.mac(), eth);
+                // TODO: check send status with host service.
+                sendTo(arpReply, src.location());
+                break;
+            }
+        }
     }
 
     /**
@@ -223,7 +229,7 @@
      * @param target the target address to find a matching port for
      * @return a PortAddresses object if one was found, otherwise null
      */
-    private PortAddresses findPortInSubnet(IpAddress target) {
+    private PortAddresses findPortInSubnet(Ip4Address target) {
         for (PortAddresses addresses : hostService.getAddressBindings()) {
             for (InterfaceIpAddress ia : addresses.ipAddresses()) {
                 if (ia.subnetAddress().contains(target)) {
@@ -358,7 +364,7 @@
      * @param request the ARP request we got
      * @return an Ethernet frame containing the ARP reply
      */
-    private Ethernet buildArpReply(IpAddress srcIp, MacAddress srcMac,
+    private Ethernet buildArpReply(Ip4Address srcIp, MacAddress srcMac,
             Ethernet request) {
 
         Ethernet eth = new Ethernet();
@@ -372,7 +378,7 @@
         arp.setProtocolType(ARP.PROTO_TYPE_IP);
         arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
 
-        arp.setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH);
+        arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
         arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
         arp.setSenderHardwareAddress(srcMac.toBytes());
         arp.setTargetHardwareAddress(request.getSourceMACAddress());
diff --git a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
index 8beac4a..bfb659e 100644
--- a/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
+++ b/core/net/src/test/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManagerTest.java
@@ -57,8 +57,8 @@
 import org.onlab.onos.net.provider.ProviderId;
 import org.onlab.packet.ARP;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
@@ -74,8 +74,8 @@
     private static final int NUM_ADDRESS_PORTS = NUM_DEVICES / 2;
     private static final int NUM_FLOOD_PORTS = 3;
 
-    private static final IpAddress IP1 = IpAddress.valueOf("192.168.1.1");
-    private static final IpAddress IP2 = IpAddress.valueOf("192.168.1.2");
+    private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1");
+    private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2");
 
     private static final ProviderId PID = new ProviderId("of", "foo");
 
@@ -204,10 +204,12 @@
 
         for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) {
             ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1);
-            IpPrefix prefix1 = IpPrefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
-            IpAddress addr1 = IpAddress.valueOf("10.0." + (2 * i - 1) + ".1");
-            IpPrefix prefix2 = IpPrefix.valueOf("10.0." + (2 * i) + ".0/24");
-            IpAddress addr2 = IpAddress.valueOf("10.0." + (2 * i) + ".1");
+            Ip4Prefix prefix1 =
+                Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24");
+            Ip4Address addr1 =
+                Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1");
+            Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24");
+            Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1");
             InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1);
             InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
             PortAddresses pa1 =
@@ -235,7 +237,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#known(IpAddress)} in the case where the
+     * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
      * IP address is not known.
      * Verifies the method returns false.
      */
@@ -248,7 +250,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#known(IpAddress)} in the case where the
+     * Tests {@link ProxyArpManager#known(Ip4Address)} in the case where the
      * IP address is known.
      * Verifies the method returns true.
      */
@@ -344,9 +346,9 @@
 
     @Test
     public void testReplyToRequestForUs() {
-        IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
-        IpAddress ourFirstIp = IpAddress.valueOf("10.0.1.1");
-        IpAddress ourSecondIp = IpAddress.valueOf("10.0.2.1");
+        Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
+        Ip4Address ourFirstIp = Ip4Address.valueOf("10.0.1.1");
+        Ip4Address ourSecondIp = Ip4Address.valueOf("10.0.2.1");
         MacAddress firstMac = MacAddress.valueOf(1L);
         MacAddress secondMac = MacAddress.valueOf(2L);
 
@@ -379,11 +381,11 @@
     public void testReplyExternalPortBadRequest() {
         replay(hostService); // no further host service expectations
 
-        IpAddress theirIp = IpAddress.valueOf("10.0.1.254");
+        Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254");
 
         // Request for a valid external IP address but coming in the wrong port
         Ethernet arpRequest = buildArp(ARP.OP_REQUEST, MAC1, null, theirIp,
-                IpAddress.valueOf("10.0.3.1"));
+                Ip4Address.valueOf("10.0.3.1"));
         proxyArp.reply(arpRequest, LOC1);
         assertEquals(0, packetService.packets.size());
 
@@ -398,9 +400,9 @@
     public void testReplyToRequestFromUs() {
         replay(hostService); // no further host service expectations
 
-        IpAddress ourIp = IpAddress.valueOf("10.0.1.1");
+        Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1");
         MacAddress ourMac = MacAddress.valueOf(1L);
-        IpAddress theirIp = IpAddress.valueOf("10.0.1.100");
+        Ip4Address theirIp = Ip4Address.valueOf("10.0.1.100");
 
         // This is a request from something inside our network (like a BGP
         // daemon) to an external host.
@@ -523,7 +525,7 @@
      * @return the ARP packet
      */
     private Ethernet buildArp(short opcode, MacAddress srcMac, MacAddress dstMac,
-            IpAddress srcIp, IpAddress dstIp) {
+            Ip4Address srcIp, Ip4Address dstIp) {
         Ethernet eth = new Ethernet();
 
         if (dstMac == null) {
@@ -541,7 +543,7 @@
         arp.setProtocolType(ARP.PROTO_TYPE_IP);
         arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
 
-        arp.setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH);
+        arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
         arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
         arp.setSenderHardwareAddress(srcMac.toBytes());
 
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java
index 99f73c1..dcf22e9 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/service/WriteRequest.java
@@ -17,24 +17,59 @@
     private final long previousVersion;
     private final byte[] oldValue;
 
-    // put regardless of previous value
-    public WriteRequest(String tableName, String key, byte[] newValue) {
-        this(tableName, key, newValue, -1, null);
+    /**
+     * Creates a write request, which will
+     * put the specified value to the table regardless of the previous value.
+     *
+     * @param tableName name of the table
+     * @param key       key in the table
+     * @param newValue  value to write
+     * @return WriteRequest
+     */
+    public static WriteRequest put(String tableName, String key,
+                                   byte[] newValue) {
+        return new WriteRequest(tableName, key, newValue, -1, null);
     }
 
-    // put if version matches
-    public WriteRequest(String tableName, String key, byte[] newValue, long previousVersion) {
-        this(tableName, key, newValue, previousVersion, null);
+    // FIXME: Is there a special version value to realize putIfAbsent?
+    /**
+     * Creates a write request, which will
+     * put the specified value to the table if the previous version matches.
+     *
+     * @param tableName name of the table
+     * @param key       key in the table
+     * @param newValue  value to write
+     * @param previousVersion previous version expected
+     * @return WriteRequest
+     */
+    public static WriteRequest putIfVersionMatches(String tableName, String key,
+                                                   byte[] newValue,
+                                                   long previousVersion) {
         checkArgument(previousVersion >= 0);
+        return new WriteRequest(tableName, key, newValue, previousVersion, null);
     }
 
-    // put if value matches
-    public WriteRequest(String tableName, String key, byte[] newValue, byte[] oldValue) {
-        this(tableName, key, newValue, -1, oldValue);
+    // FIXME: What is the behavior of oldValue=null? putIfAbsent?
+    /**
+     * Creates a write request, which will
+     * put the specified value to the table if the previous value matches.
+     *
+     * @param tableName name of the table
+     * @param key       key in the table
+     * @param newValue  value to write
+     * @param oldValue  previous value expected
+     * @return WriteRequest
+     */
+    public static WriteRequest putIfValueMatches(String tableName, String key,
+                                                 byte[] newValue,
+                                                 byte[] oldValue) {
+        return new WriteRequest(tableName, key, newValue, -1, oldValue);
     }
 
+    // FIXME: How do we remove value? newValue=null?
+
     // hidden constructor
-    private WriteRequest(String tableName, String key, byte[] newValue, long previousVersion, byte[] oldValue) {
+    protected WriteRequest(String tableName, String key, byte[] newValue, long previousVersion, byte[] oldValue) {
 
         checkArgument(tableName != null);
         checkArgument(key != null);