Implemented VLAN-to-VLAN routing support for SDN-IP.

SDN-IP can now support peering and routing between hosts that are connected
on VLANs.

Changes include:
 * Updated NetworkConfigReader app to read (optional) VLAN configuration
 * Updated VlanId to support the 'VLAN present' value - in a match this means
   that a VLAN tag must be present, but it can contain any value.
 * Updated SDN-IP to set destination VLAN tag values if appropriate
 * Updated FlowModBuilder and FlowEntryBuilder to support 'VLAN present' value
 * Slew of test updates.

Change-Id: Ief48cede5c1fd50e1efa851da5a97fb4a8edda29
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
index bea69f3..b5833cf 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -15,17 +15,14 @@
  */
 package org.onosproject.net.host.impl;
 
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
 import org.jboss.netty.util.Timeout;
 import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Timer;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -43,11 +40,15 @@
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.ProviderId;
-import org.onlab.packet.ARP;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.util.Timer;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Monitors hosts on the dataplane to detect changes in host data.
@@ -182,7 +183,8 @@
                     for (InterfaceIpAddress ia : portAddresses.ipAddresses()) {
                         if (ia.subnetAddress().contains(targetIp)) {
                             sendProbe(device.id(), port, targetIp,
-                                      ia.ipAddress(), portAddresses.mac());
+                                      ia.ipAddress(), portAddresses.mac(),
+                                      portAddresses.vlan());
                         }
                     }
                 }
@@ -191,8 +193,8 @@
     }
 
     private void sendProbe(DeviceId deviceId, Port port, IpAddress targetIp,
-            IpAddress sourceIp, MacAddress sourceMac) {
-        Ethernet arpPacket = buildArpRequest(targetIp, sourceIp, sourceMac);
+            IpAddress sourceIp, MacAddress sourceMac, VlanId vlan) {
+        Ethernet arpPacket = buildArpRequest(targetIp, sourceIp, sourceMac, vlan);
 
         List<Instruction> instructions = new ArrayList<>();
         instructions.add(Instructions.createOutput(port.number()));
@@ -209,7 +211,7 @@
     }
 
     private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
-            MacAddress sourceMac) {
+            MacAddress sourceMac, VlanId vlan) {
 
         ARP arp = new ARP();
         arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
@@ -229,6 +231,10 @@
                 .setSourceMACAddress(sourceMac)
                 .setPayload(arp);
 
+        if (!vlan.equals(VlanId.NONE)) {
+            ethernet.setVlanID(vlan.toShort());
+        }
+
         return ethernet;
     }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
index 90bc15e..1fcda24 100644
--- a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -15,16 +15,9 @@
  */
 package org.onosproject.net.proxyarp.impl;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.nio.ByteBuffer;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -62,9 +55,15 @@
 import org.onosproject.net.proxyarp.ProxyArpService;
 import org.slf4j.Logger;
 
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
 
 @Component(immediate = true)
 @Service
@@ -131,6 +130,8 @@
         checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
         checkNotNull(inPort);
 
+        VlanId vlan = VlanId.vlanId(eth.getVlanID());
+
         // If the request came from outside the network, only reply if it was
         // for one of our external addresses.
         if (isOutsidePort(inPort)) {
@@ -159,7 +160,8 @@
             boolean matched = false;
             for (PortAddresses pa : sourceAddresses) {
                 for (InterfaceIpAddress ia : pa.ipAddresses()) {
-                    if (ia.ipAddress().equals(source)) {
+                    if (ia.ipAddress().equals(source) &&
+                            pa.vlan().equals(vlan)) {
                         matched = true;
                         sendTo(eth, pa.connectPoint());
                     }
@@ -173,7 +175,6 @@
 
         // Continue with normal proxy ARP case
 
-        VlanId vlan = VlanId.vlanId(eth.getVlanID());
         Set<Host> hosts = hostService.getHostsByIp(
                         Ip4Address.valueOf(arp.getTargetProtocolAddress()));
 
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
index ce7de7e..2a461bf 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
@@ -31,6 +31,10 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.event.Event;
 import org.onosproject.event.impl.TestEventDispatcher;
 import org.onosproject.net.ConnectPoint;
@@ -51,10 +55,6 @@
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.trivial.impl.SimpleHostStore;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -231,7 +231,7 @@
     @Test
     public void bindAddressesToPort() {
         PortAddresses add1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
+            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE);
 
         mgr.bindAddressesToPort(add1);
         Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -241,7 +241,8 @@
 
         // Add some more addresses and check that they're added correctly
         PortAddresses add2 =
-            new PortAddresses(CP1, Sets.newHashSet(IA3),  null);
+            new PortAddresses(CP1, Sets.newHashSet(IA3),  null,
+                              VlanId.vlanId((short) 2));
 
         mgr.bindAddressesToPort(add2);
         storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -250,7 +251,7 @@
         assertTrue(storedAddresses.contains(add1));
         assertTrue(storedAddresses.contains(add2));
 
-        PortAddresses add3 = new PortAddresses(CP1, null, MAC2);
+        PortAddresses add3 = new PortAddresses(CP1, null, MAC2, VlanId.NONE);
 
         mgr.bindAddressesToPort(add3);
         storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -264,7 +265,7 @@
     @Test
     public void unbindAddressesFromPort() {
         PortAddresses add1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
+            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE);
 
         mgr.bindAddressesToPort(add1);
         Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -273,7 +274,7 @@
         assertTrue(storedAddresses.contains(add1));
 
         PortAddresses rem1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1), null);
+            new PortAddresses(CP1, Sets.newHashSet(IA1), null, VlanId.NONE);
 
         mgr.unbindAddressesFromPort(rem1);
         storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -292,7 +293,7 @@
     @Test
     public void clearAddresses() {
         PortAddresses add1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
+            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE);
 
         mgr.bindAddressesToPort(add1);
         Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -309,7 +310,7 @@
     @Test
     public void getAddressBindingsForPort() {
         PortAddresses add1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
+            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE);
 
         mgr.bindAddressesToPort(add1);
         Set<PortAddresses> storedAddresses = mgr.getAddressBindingsForPort(CP1);
@@ -325,7 +326,7 @@
         assertTrue(storedAddresses.isEmpty());
 
         PortAddresses add1 =
-            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1);
+            new PortAddresses(CP1, Sets.newHashSet(IA1, IA2), MAC1, VlanId.NONE);
 
         mgr.bindAddressesToPort(add1);
 
@@ -334,7 +335,7 @@
         assertTrue(storedAddresses.size() == 1);
 
         PortAddresses add2 =
-            new PortAddresses(CP2, Sets.newHashSet(IA3), MAC2);
+            new PortAddresses(CP2, Sets.newHashSet(IA3), MAC2, VlanId.NONE);
 
         mgr.bindAddressesToPort(add2);
 
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
index 834379b..7152ad9 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
@@ -37,6 +37,7 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -130,7 +131,7 @@
 
         ConnectPoint cp = new ConnectPoint(devId, portNum);
         PortAddresses pa =
-            new PortAddresses(cp, Collections.singleton(IA1), sourceMac);
+            new PortAddresses(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE);
 
         expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
                 .andReturn(Collections.<Host>emptySet()).anyTimes();
@@ -165,6 +166,76 @@
         final byte[] pktData = new byte[packet.data().remaining()];
         packet.data().get(pktData);
         eth.deserialize(pktData, 0, pktData.length);
+        assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID());
+        ARP arp = (ARP) eth.getPayload();
+        assertArrayEquals(SOURCE_ADDR.toOctets(),
+                          arp.getSenderProtocolAddress());
+        assertArrayEquals(sourceMac.toBytes(),
+                          arp.getSenderHardwareAddress());
+        assertArrayEquals(TARGET_IP_ADDR.toOctets(),
+                          arp.getTargetProtocolAddress());
+    }
+
+    @Test
+    public void testMonitorHostDoesNotExistWithVlan() throws Exception {
+
+        HostManager hostManager = createMock(HostManager.class);
+
+        DeviceId devId = DeviceId.deviceId("fake");
+        short vlan = 5;
+
+        Device device = createMock(Device.class);
+        expect(device.id()).andReturn(devId).anyTimes();
+        replay(device);
+
+        PortNumber portNum = PortNumber.portNumber(1L);
+
+        Port port = createMock(Port.class);
+        expect(port.number()).andReturn(portNum).anyTimes();
+        replay(port);
+
+        TestDeviceService deviceService = new TestDeviceService();
+        deviceService.addDevice(device, Collections.singleton(port));
+
+        ConnectPoint cp = new ConnectPoint(devId, portNum);
+        PortAddresses pa =
+            new PortAddresses(cp, Collections.singleton(IA1), sourceMac,
+                              VlanId.vlanId(vlan));
+
+        expect(hostManager.getHostsByIp(TARGET_IP_ADDR))
+                .andReturn(Collections.<Host>emptySet()).anyTimes();
+        expect(hostManager.getAddressBindingsForPort(cp))
+                .andReturn(Collections.singleton(pa)).anyTimes();
+        replay(hostManager);
+
+        TestPacketService packetService = new TestPacketService();
+
+
+        // Run the test
+        hostMonitor = new HostMonitor(deviceService, packetService, hostManager);
+
+        hostMonitor.addMonitoringFor(TARGET_IP_ADDR);
+        hostMonitor.run(null);
+
+
+        // Check that a packet was sent to our PacketService and that it has
+        // the properties we expect
+        assertEquals(1, packetService.packets.size());
+        OutboundPacket packet = packetService.packets.get(0);
+
+        // Check the output port is correct
+        assertEquals(1, packet.treatment().instructions().size());
+        Instruction instruction = packet.treatment().instructions().get(0);
+        assertTrue(instruction instanceof OutputInstruction);
+        OutputInstruction oi = (OutputInstruction) instruction;
+        assertEquals(portNum, oi.port());
+
+        // Check the output packet is correct (well the important bits anyway)
+        Ethernet eth = new Ethernet();
+        final byte[] pktData = new byte[packet.data().remaining()];
+        packet.data().get(pktData);
+        eth.deserialize(pktData, 0, pktData.length);
+        assertEquals(vlan, eth.getVlanID());
         ARP arp = (ARP) eth.getPayload();
         assertArrayEquals(SOURCE_ADDR.toOctets(),
                           arp.getSenderProtocolAddress());
diff --git a/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java b/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
index a349738f..ca218c6 100644
--- a/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
@@ -216,10 +216,12 @@
             InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2);
             PortAddresses pa1 =
                 new PortAddresses(cp, Sets.newHashSet(ia1),
-                                  MacAddress.valueOf(2 * i - 1));
+                                  MacAddress.valueOf(2 * i - 1),
+                                  VlanId.vlanId((short) 1));
             PortAddresses pa2 =
                     new PortAddresses(cp, Sets.newHashSet(ia2),
-                                      MacAddress.valueOf(2 * i));
+                                      MacAddress.valueOf(2 * i),
+                                      VlanId.NONE);
 
             addresses.add(pa1);
             addresses.add(pa2);
@@ -269,7 +271,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
+     * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
      * destination host is known.
      * Verifies the correct ARP reply is sent out the correct port.
      */
@@ -297,7 +299,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
+     * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
      * destination host is not known.
      * Verifies the ARP request is flooded out the correct edge ports.
      */
@@ -320,7 +322,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#reply(Ethernet)} in the case where the
+     * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the
      * destination host is known for that IP address, but is not on the same
      * VLAN as the source host.
      * Verifies the ARP request is flooded out the correct edge ports.
@@ -421,7 +423,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
+     * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
      * destination host is known.
      * Verifies the correct ARP request is sent out the correct port.
      */
@@ -444,7 +446,7 @@
     }
 
     /**
-     * Tests {@link ProxyArpManager#forward(Ethernet)} in the case where the
+     * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the
      * destination host is not known.
      * Verifies the correct ARP request is flooded out the correct edge ports.
      */