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/apps/config/src/main/java/org/onosproject/config/AddressEntry.java b/apps/config/src/main/java/org/onosproject/config/AddressEntry.java
index e1fe288..caaee6f 100644
--- a/apps/config/src/main/java/org/onosproject/config/AddressEntry.java
+++ b/apps/config/src/main/java/org/onosproject/config/AddressEntry.java
@@ -27,6 +27,7 @@
     private long portNumber;
     private List<String> ipAddresses;
     private String macAddress;
+    private Short vlan;
 
     public String getDpid() {
         return dpid;
@@ -63,4 +64,13 @@
     public void setMacAddress(String macAddress) {
         this.macAddress = macAddress;
     }
+
+    public Short getVlan() {
+        return vlan;
+    }
+
+    @JsonProperty("vlan")
+    public void setVlan(short vlan) {
+        this.vlan = vlan;
+    }
 }
diff --git a/apps/config/src/main/java/org/onosproject/config/NetworkConfigReader.java b/apps/config/src/main/java/org/onosproject/config/NetworkConfigReader.java
index ffddfea..1868a70 100644
--- a/apps/config/src/main/java/org/onosproject/config/NetworkConfigReader.java
+++ b/apps/config/src/main/java/org/onosproject/config/NetworkConfigReader.java
@@ -28,15 +28,16 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.host.HostAdminService;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.host.PortAddresses;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
 import org.slf4j.Logger;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -115,7 +116,8 @@
                 try {
                     String[] splits = strIp.split("/");
                     if (splits.length != 2) {
-                        throw new IllegalArgumentException("Invalid IP address and prefix length format");
+                        throw new IllegalArgumentException(
+                            "Invalid IP address and prefix length format");
                     }
                     // NOTE: IpPrefix will mask-out the bits after the prefix length.
                     IpPrefix subnet = IpPrefix.valueOf(strIp);
@@ -138,8 +140,21 @@
                 }
             }
 
+            VlanId vlan = null;
+            if (entry.getVlan() == null) {
+                vlan = VlanId.NONE;
+            } else {
+                try {
+                    vlan = VlanId.vlanId(entry.getVlan());
+                } catch (IllegalArgumentException e) {
+                    log.warn("Bad format for VLAN id in config: {}",
+                             entry.getVlan());
+                    vlan = VlanId.NONE;
+                }
+            }
+
             PortAddresses addresses = new PortAddresses(cp,
-                        interfaceIpAddresses, macAddress);
+                        interfaceIpAddresses, macAddress, vlan);
             hostAdminService.bindAddressesToPort(addresses);
         }
     }
diff --git a/apps/config/src/main/resources/addresses.json b/apps/config/src/main/resources/addresses.json
index fa00eae..a88ed62 100644
--- a/apps/config/src/main/resources/addresses.json
+++ b/apps/config/src/main/resources/addresses.json
@@ -4,13 +4,15 @@
 	    "dpid" : "00:00:00:00:00:00:00:a3",
 	    "port" : "1",
 	    "ips" : ["192.168.10.101/24"],
-	    "mac" : "00:00:00:00:00:01"
+	    "mac" : "00:00:00:00:00:01",
+	    "vlan" : "1"
 	},
 	{
 	    "dpid" : "00:00:00:00:00:00:00:a5",
 	    "port" : "1",
 	    "ips" : ["192.168.20.101/24"],
-	    "mac" : "00:00:00:00:00:01"
+	    "mac" : "00:00:00:00:00:01",
+	    "vlan" : "2"
 	},
 	{
 	    "dpid" : "00:00:00:00:00:00:00:a2",
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
index d34a395..2b2213a 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/Router.java
@@ -30,10 +30,11 @@
 
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
@@ -272,7 +273,7 @@
      * or IPv6.
      *
      * @param prefix the prefix to use
-     * @return true if the rotue was found and removed, otherwise false
+     * @return true if the route was found and removed, otherwise false
      */
     boolean removeRibRoute(IpPrefix prefix) {
         if (prefix.version() == Ip4Address.VERSION) {
@@ -455,25 +456,26 @@
         }
 
         // Match the destination IP prefix at the first hop
-        TrafficSelector selector;
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         if (prefix.version() == Ip4Address.VERSION) {
-            selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(prefix)
-                .build();
+            selector.matchEthType(Ethernet.TYPE_IPV4);
         } else {
-            selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV6)
-                .matchIPDst(prefix)
-                .build();
+            selector.matchEthType(Ethernet.TYPE_IPV6);
         }
+        selector.matchIPDst(prefix);
 
         // Rewrite the destination MAC address
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setEthDst(nextHopMacAddress)
-                .build();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(nextHopMacAddress);
+        if (!egressInterface.vlan().equals(VlanId.NONE)) {
+            treatment.setVlanId(egressInterface.vlan());
+            // If we set VLAN ID, we have to make sure a VLAN tag exists.
+            // TODO support no VLAN -> VLAN routing
+            selector.matchVlanId(VlanId.ANY);
+        }
 
-        return new MultiPointToSinglePointIntent(appId, selector, treatment,
+        return new MultiPointToSinglePointIntent(appId, selector.build(),
+                                                 treatment.build(),
                                                  ingressPorts, egressPort);
     }
 
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/config/Interface.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/config/Interface.java
index 7ddf0ae..b495f07 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/config/Interface.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/config/Interface.java
@@ -19,6 +19,7 @@
 import java.util.Set;
 
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.host.PortAddresses;
@@ -34,6 +35,7 @@
     private final ConnectPoint connectPoint;
     private final Set<InterfaceIpAddress> ipAddresses;
     private final MacAddress macAddress;
+    private final VlanId vlan;
 
     /**
      * Creates an Interface based on a connection point, a set of interface
@@ -45,10 +47,11 @@
      */
     public Interface(ConnectPoint connectPoint,
                      Set<InterfaceIpAddress> ipAddresses,
-                     MacAddress macAddress) {
+                     MacAddress macAddress, VlanId vlan) {
         this.connectPoint = connectPoint;
         this.ipAddresses = Sets.newHashSet(ipAddresses);
         this.macAddress = macAddress;
+        this.vlan = vlan;
     }
 
     /**
@@ -60,6 +63,7 @@
         connectPoint = portAddresses.connectPoint();
         ipAddresses = Sets.newHashSet(portAddresses.ipAddresses());
         macAddress = portAddresses.mac();
+        vlan = portAddresses.vlan();
     }
 
     /**
@@ -76,18 +80,27 @@
      *
      * @return the set of interface IP addresses
      */
-   public Set<InterfaceIpAddress> ipAddresses() {
+    public Set<InterfaceIpAddress> ipAddresses() {
         return ipAddresses;
     }
 
-   /**
-    * Retrieves the MAC address that is assigned to the interface.
-    *
-    * @return the MAC address
-    */
-   public MacAddress mac() {
-       return macAddress;
-   }
+    /**
+     * Retrieves the MAC address that is assigned to the interface.
+     *
+     * @return the MAC address
+     */
+    public MacAddress mac() {
+        return macAddress;
+    }
+
+    /**
+     * Retrieves the VLAN ID that is assigned to the interface.
+     *
+     * @return the VLAN ID
+     */
+    public VlanId vlan() {
+        return vlan;
+    }
 
     @Override
     public boolean equals(Object other) {
@@ -99,12 +112,13 @@
 
         return  connectPoint.equals(otherInterface.connectPoint) &&
                 ipAddresses.equals(otherInterface.ipAddresses) &&
-                macAddress.equals(otherInterface.macAddress);
+                macAddress.equals(otherInterface.macAddress) &&
+                vlan.equals(otherInterface.vlan);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(connectPoint, ipAddresses, macAddress);
+        return Objects.hash(connectPoint, ipAddresses, macAddress, vlan);
     }
 
     @Override
@@ -113,6 +127,7 @@
                 .add("connectPoint", connectPoint)
                 .add("ipAddresses", ipAddresses)
                 .add("macAddress", macAddress)
+                .add("vlan", vlan)
                 .toString();
     }
 }
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/HostToInterfaceAdaptorTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/HostToInterfaceAdaptorTest.java
index d63581f..096e63c 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/HostToInterfaceAdaptorTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/HostToInterfaceAdaptorTest.java
@@ -29,6 +29,10 @@
 
 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.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -36,9 +40,6 @@
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.host.PortAddresses;
 import org.onosproject.sdnip.config.Interface;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
 
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
@@ -76,7 +77,8 @@
                                    IpPrefix.valueOf("192.168.1.0/24"));
         createPortAddressesAndInterface(CP1,
                 Sets.newHashSet(ia11),
-                MacAddress.valueOf("00:00:00:00:00:01"));
+                MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
 
         // Two addresses in the same subnet
         InterfaceIpAddress ia21 =
@@ -87,7 +89,8 @@
                                    IpPrefix.valueOf("192.168.2.0/24"));
         createPortAddressesAndInterface(CP2,
                 Sets.newHashSet(ia21, ia22),
-                MacAddress.valueOf("00:00:00:00:00:02"));
+                MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.vlanId((short) 4));
 
         // Two addresses in different subnets
         InterfaceIpAddress ia31 =
@@ -98,7 +101,8 @@
                                    IpPrefix.valueOf("192.168.4.0/24"));
         createPortAddressesAndInterface(CP3,
                 Sets.newHashSet(ia31, ia41),
-                MacAddress.valueOf("00:00:00:00:00:03"));
+                MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
 
         expect(hostService.getAddressBindings()).andReturn(portAddresses).anyTimes();
 
@@ -114,16 +118,17 @@
      * @param cp the connect point
      * @param ipAddresses the set of interface IP addresses
      * @param mac the MAC address
+     * @param vlan the VLAN ID
      */
     private void createPortAddressesAndInterface(
             ConnectPoint cp, Set<InterfaceIpAddress> ipAddresses,
-            MacAddress mac) {
-        PortAddresses pa = new PortAddresses(cp, ipAddresses, mac);
+            MacAddress mac, VlanId vlan) {
+        PortAddresses pa = new PortAddresses(cp, ipAddresses, mac, vlan);
         portAddresses.add(pa);
         expect(hostService.getAddressBindingsForPort(cp)).andReturn(
                 Collections.singleton(pa)).anyTimes();
 
-        Interface intf = new Interface(cp, ipAddresses, mac);
+        Interface intf = new Interface(cp, ipAddresses, mac, vlan);
         interfaces.put(cp, intf);
     }
 
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
index 0868582..9960481 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java
@@ -36,6 +36,13 @@
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultHost;
@@ -59,13 +66,6 @@
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.sdnip.config.Interface;
-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;
 
 import com.google.common.collect.Sets;
 import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
@@ -135,7 +135,8 @@
                 IpAddress.valueOf("192.168.10.101"),
                 IpPrefix.valueOf("192.168.10.0/24")));
         Interface sw1Eth1 = new Interface(SW1_ETH1,
-                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"));
+                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
         interfaces.add(sw1Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
@@ -143,7 +144,8 @@
                 IpAddress.valueOf("192.168.20.101"),
                 IpPrefix.valueOf("192.168.20.0/24")));
         Interface sw2Eth1 = new Interface(SW2_ETH1,
-                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"));
+                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
         interfaces.add(sw2Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
@@ -151,7 +153,8 @@
                 IpAddress.valueOf("192.168.30.101"),
                 IpPrefix.valueOf("192.168.30.0/24")));
         Interface sw3Eth1 = new Interface(SW3_ETH1,
-                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"));
+                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
         interfaces.add(sw3Eth1);
 
         expect(interfaceService.getInterface(SW1_ETH1)).andReturn(
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
index 2ce9956..e542eb0 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java
@@ -38,6 +38,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -189,7 +190,8 @@
                                    IpPrefix.valueOf("192.168.10.0/24"));
         Interface intfsw1eth1 = new Interface(s1Eth1,
                 Collections.singleton(ia1),
-                MacAddress.valueOf("00:00:00:00:00:01"));
+                MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
 
         configuredInterfaces.put(interfaceSw1Eth1, intfsw1eth1);
         String interfaceSw2Eth1 = "s2-eth1";
@@ -198,7 +200,8 @@
                                    IpPrefix.valueOf("192.168.20.0/24"));
         Interface intfsw2eth1 = new Interface(s2Eth1,
                 Collections.singleton(ia2),
-                MacAddress.valueOf("00:00:00:00:00:02"));
+                MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
         configuredInterfaces.put(interfaceSw2Eth1, intfsw2eth1);
 
         interfaceService = createMock(InterfaceService.class);
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
index f8db770..80fb578 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterAsyncArpTest.java
@@ -35,6 +35,13 @@
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultHost;
@@ -50,24 +57,17 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentOperations;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
 import org.onosproject.sdnip.Router.InternalHostListener;
 import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.Interface;
 import org.onosproject.sdnip.config.SdnIpConfigurationService;
-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;
 
 import com.google.common.collect.Sets;
 import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
@@ -168,7 +168,8 @@
                 IpAddress.valueOf("192.168.10.101"),
                 IpPrefix.valueOf("192.168.10.0/24")));
         Interface sw1Eth1 = new Interface(SW1_ETH1,
-                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"));
+                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
         interfaces.add(sw1Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
@@ -176,7 +177,8 @@
                 IpAddress.valueOf("192.168.20.101"),
                 IpPrefix.valueOf("192.168.20.0/24")));
         Interface sw2Eth1 = new Interface(SW2_ETH1,
-                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"));
+                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
         interfaces.add(sw2Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
@@ -184,7 +186,8 @@
                 IpAddress.valueOf("192.168.30.101"),
                 IpPrefix.valueOf("192.168.30.0/24")));
         Interface sw3Eth1 = new Interface(SW3_ETH1,
-                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"));
+                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
         interfaces.add(sw3Eth1);
 
         expect(interfaceService.getInterface(SW1_ETH1)).andReturn(sw1Eth1).anyTimes();
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
index 9ef1f69..6c98efb 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/RouterTest.java
@@ -36,10 +36,10 @@
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
 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.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
@@ -95,6 +95,10 @@
             DeviceId.deviceId("of:0000000000000003"),
             PortNumber.portNumber(1));
 
+    private static final ConnectPoint SW4_ETH1 = new ConnectPoint(
+            DeviceId.deviceId("of:0000000000000004"),
+            PortNumber.portNumber(1));
+
     private static final ApplicationId APPID = new ApplicationId() {
         @Override
         public short id() {
@@ -146,6 +150,10 @@
         peers.put(IpAddress.valueOf(peer2Sw2Eth1),
                 new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
 
+        String peer1Sw4Eth1 = "192.168.40.1";
+        peers.put(IpAddress.valueOf(peer1Sw4Eth1),
+                new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1));
+
         sdnIpConfigService = createMock(SdnIpConfigurationService.class);
         expect(sdnIpConfigService.getBgpPeers()).andReturn(peers).anyTimes();
         replay(sdnIpConfigService);
@@ -166,7 +174,8 @@
                                    IpPrefix.valueOf("192.168.10.0/24"));
         Interface sw1Eth1 = new Interface(SW1_ETH1,
                 Sets.newHashSet(ia1),
-                MacAddress.valueOf("00:00:00:00:00:01"));
+                MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
 
         expect(interfaceService.getInterface(SW1_ETH1)).andReturn(sw1Eth1).anyTimes();
         interfaces.add(sw1Eth1);
@@ -176,7 +185,8 @@
                                    IpPrefix.valueOf("192.168.20.0/24"));
         Interface sw2Eth1 = new Interface(SW2_ETH1,
                 Sets.newHashSet(ia2),
-                MacAddress.valueOf("00:00:00:00:00:02"));
+                MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
 
         expect(interfaceService.getInterface(SW2_ETH1)).andReturn(sw2Eth1).anyTimes();
         interfaces.add(sw2Eth1);
@@ -186,11 +196,23 @@
                                    IpPrefix.valueOf("192.168.30.0/24"));
         Interface sw3Eth1 = new Interface(SW3_ETH1,
                 Sets.newHashSet(ia3),
-                MacAddress.valueOf("00:00:00:00:00:03"));
+                MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
 
         expect(interfaceService.getInterface(SW3_ETH1)).andReturn(sw3Eth1).anyTimes();
         interfaces.add(sw3Eth1);
 
+        InterfaceIpAddress ia4 =
+                new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"),
+                                       IpPrefix.valueOf("192.168.40.0/24"));
+            Interface sw4Eth1 = new Interface(SW4_ETH1,
+                    Sets.newHashSet(ia4),
+                    MacAddress.valueOf("00:00:00:00:00:04"),
+                    VlanId.vlanId((short) 1));
+
+            expect(interfaceService.getInterface(SW4_ETH1)).andReturn(sw4Eth1).anyTimes();
+            interfaces.add(sw4Eth1);
+
         expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes();
 
         replay(interfaceService);
@@ -228,6 +250,18 @@
         hostService.startMonitoringIp(host2Address);
         expectLastCall().anyTimes();
 
+        // Next hop on a VLAN
+        IpAddress host3Address = IpAddress.valueOf("192.168.40.1");
+        Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE,
+                MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1),
+                new HostLocation(SW4_ETH1, 1),
+                Sets.newHashSet(host3Address));
+
+        expect(hostService.getHostsByIp(host3Address))
+                .andReturn(Sets.newHashSet(host3)).anyTimes();
+        hostService.startMonitoringIp(host3Address);
+        expectLastCall().anyTimes();
+
 
         replay(hostService);
     }
@@ -255,6 +289,7 @@
         Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
         ingressPoints.add(SW2_ETH1);
         ingressPoints.add(SW3_ETH1);
+        ingressPoints.add(SW4_ETH1);
 
         MultiPointToSinglePointIntent intent =
                 new MultiPointToSinglePointIntent(APPID,
@@ -290,6 +325,66 @@
     }
 
     /**
+     * This method tests adding a route entry.
+     */
+    @Test
+    public void testRouteAddWithVlan() throws TestUtilsException {
+        // Construct a route entry
+        RouteEntry routeEntry = new RouteEntry(
+                Ip4Prefix.valueOf("3.3.3.0/24"),
+                Ip4Address.valueOf("192.168.40.1"));
+
+        // Construct a MultiPointToSinglePointIntent intent
+        TrafficSelector.Builder selectorBuilder =
+                DefaultTrafficSelector.builder();
+        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                       .matchIPDst(routeEntry.prefix())
+                       .matchVlanId(VlanId.ANY);
+
+        TrafficTreatment.Builder treatmentBuilder =
+                DefaultTrafficTreatment.builder();
+        treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:03"))
+                        .setVlanId(VlanId.vlanId((short) 1));
+
+        Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
+        ingressPoints.add(SW1_ETH1);
+        ingressPoints.add(SW2_ETH1);
+        ingressPoints.add(SW3_ETH1);
+
+        MultiPointToSinglePointIntent intent =
+                new MultiPointToSinglePointIntent(APPID,
+                        selectorBuilder.build(), treatmentBuilder.build(),
+                        ingressPoints, SW4_ETH1);
+
+        // Set up test expectation
+        reset(intentService);
+        // Setup the expected intents
+        IntentOperations.Builder builder = IntentOperations.builder(APPID);
+        builder.addSubmitOperation(intent);
+        intentService.execute(TestIntentServiceHelper.eqExceptId(
+                                builder.build()));
+        replay(intentService);
+
+        // Call the processRouteUpdates() method in Router class
+        intentSynchronizer.leaderChanged(true);
+        TestUtils.setField(intentSynchronizer, "isActivatedLeader", true);
+        RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
+                                                  routeEntry);
+        router.processRouteUpdates(Collections.<RouteUpdate>singletonList(routeUpdate));
+
+        // Verify
+        assertEquals(router.getRoutes4().size(), 1);
+        assertTrue(router.getRoutes4().contains(routeEntry));
+        assertEquals(intentSynchronizer.getRouteIntents().size(), 1);
+        Intent firstIntent =
+            intentSynchronizer.getRouteIntents().iterator().next();
+        IntentKey firstIntentKey = new IntentKey(firstIntent);
+        IntentKey intentKey = new IntentKey(intent);
+        assertTrue(firstIntentKey.equals(intentKey));
+        verify(intentService);
+    }
+
+    /**
      * This method tests updating a route entry.
      *
      * @throws TestUtilsException
@@ -321,6 +416,7 @@
         Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>();
         ingressPointsNew.add(SW1_ETH1);
         ingressPointsNew.add(SW3_ETH1);
+        ingressPointsNew.add(SW4_ETH1);
 
         MultiPointToSinglePointIntent intentNew =
                 new MultiPointToSinglePointIntent(APPID,
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
index 0100c39..36bd3b7 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpTest.java
@@ -42,6 +42,13 @@
 import org.onlab.junit.IntegrationTest;
 import org.onlab.junit.TestUtils;
 import org.onlab.junit.TestUtils.TestUtilsException;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -52,18 +59,12 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.sdnip.config.BgpPeer;
 import org.onosproject.sdnip.config.Interface;
 import org.onosproject.sdnip.config.SdnIpConfigurationService;
-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 com.google.common.collect.Sets;
 
@@ -148,7 +149,8 @@
                 IpAddress.valueOf("192.168.10.101"),
                 IpPrefix.valueOf("192.168.10.0/24")));
         Interface sw1Eth1 = new Interface(SW1_ETH1,
-                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"));
+                interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"),
+                VlanId.NONE);
         interfaces.add(sw1Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet();
@@ -156,7 +158,8 @@
                 IpAddress.valueOf("192.168.20.101"),
                 IpPrefix.valueOf("192.168.20.0/24")));
         Interface sw2Eth1 = new Interface(SW2_ETH1,
-                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"));
+                interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"),
+                VlanId.NONE);
         interfaces.add(sw2Eth1);
 
         Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet();
@@ -164,7 +167,8 @@
                 IpAddress.valueOf("192.168.30.101"),
                 IpPrefix.valueOf("192.168.30.0/24")));
         Interface sw3Eth1 = new Interface(SW3_ETH1,
-                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"));
+                interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"),
+                VlanId.NONE);
         interfaces.add(sw3Eth1);
 
         expect(interfaceService.getInterface(SW1_ETH1)).andReturn(
diff --git a/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java b/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
index 9aaa6fb..b01e1ae 100644
--- a/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
+++ b/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.sdnip;
 
+import static org.easymock.EasyMock.reportMatcher;
+
 import java.util.LinkedList;
 import java.util.List;
 
@@ -26,8 +28,6 @@
 import org.onosproject.net.intent.IntentOperations;
 import org.onosproject.sdnip.IntentSynchronizer.IntentKey;
 
-import static org.easymock.EasyMock.reportMatcher;
-
 /**
  * Helper class for testing operations submitted to the IntentService.
  */
@@ -187,7 +187,7 @@
          * @param submitIntents the SUBMIT intents
          * @param withdrawIntentIds the WITHDRAW intents IDs
          * @param replaceIntents the REPLACE intents
-         * @param updateIntents the UPDATE intens
+         * @param updateIntents the UPDATE intents
          */
         private void extractIntents(IntentOperations intentOperations,
                                     List<IntentKey> submitIntents,
diff --git a/cli/src/main/java/org/onosproject/cli/net/AddressBindingsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/AddressBindingsListCommand.java
index 7f5b5c3..e3f8dd5 100644
--- a/cli/src/main/java/org/onosproject/cli/net/AddressBindingsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/AddressBindingsListCommand.java
@@ -36,7 +36,7 @@
 public class AddressBindingsListCommand extends AbstractShellCommand {
 
     private static final String FORMAT =
-            "port=%s/%s, ip(s)=%s, mac=%s";
+            "port=%s/%s, ip(s)=%s, mac=%s, vlan=%s";
 
     @Override
     protected void execute() {
@@ -49,7 +49,7 @@
 
         for (PortAddresses pa : addresses) {
             print(FORMAT, pa.connectPoint().deviceId(), pa.connectPoint().port(),
-                    printIpAddresses(pa.ipAddresses()), pa.mac());
+                    printIpAddresses(pa.ipAddresses()), pa.mac(), pa.vlan());
         }
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/host/PortAddresses.java b/core/api/src/main/java/org/onosproject/net/host/PortAddresses.java
index 543b1f4..a034ad1 100644
--- a/core/api/src/main/java/org/onosproject/net/host/PortAddresses.java
+++ b/core/api/src/main/java/org/onosproject/net/host/PortAddresses.java
@@ -20,8 +20,9 @@
 import java.util.Objects;
 import java.util.Set;
 
-import org.onosproject.net.ConnectPoint;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 
 import com.google.common.base.MoreObjects;
 
@@ -33,6 +34,7 @@
     private final ConnectPoint connectPoint;
     private final Set<InterfaceIpAddress> ipAddresses;
     private final MacAddress macAddress;
+    private final VlanId vlan;
 
     /**
      * Constructs a PortAddresses object for the given connection point, with a
@@ -42,14 +44,16 @@
      * @param connectPoint the connection point these addresses are for
      * @param ipAddresses a set of interface IP addresses
      * @param mac a MAC address
+     * @param vlan a VLAN ID
      */
     public PortAddresses(ConnectPoint connectPoint,
-            Set<InterfaceIpAddress> ipAddresses, MacAddress mac) {
+            Set<InterfaceIpAddress> ipAddresses, MacAddress mac, VlanId vlan) {
         this.connectPoint = connectPoint;
         this.ipAddresses = (ipAddresses == null) ?
             Collections.<InterfaceIpAddress>emptySet()
             : new HashSet<>(ipAddresses);
         this.macAddress = mac;
+        this.vlan = vlan;
     }
 
     /**
@@ -79,6 +83,15 @@
         return macAddress;
     }
 
+    /**
+     * Returns the VLAN ID.
+     *
+     * @return the VLAN ID
+     */
+    public VlanId vlan() {
+        return vlan;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
@@ -93,12 +106,13 @@
 
         return Objects.equals(this.connectPoint, otherPa.connectPoint)
                 && Objects.equals(this.ipAddresses, otherPa.ipAddresses)
-                && Objects.equals(this.macAddress, otherPa.macAddress);
+                && Objects.equals(this.macAddress, otherPa.macAddress)
+                && Objects.equals(this.vlan, otherPa.vlan);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(connectPoint, ipAddresses, macAddress);
+        return Objects.hash(connectPoint, ipAddresses, macAddress, vlan);
     }
 
     @Override
@@ -107,6 +121,7 @@
             .add("connect-point", connectPoint)
             .add("ip-addresses", ipAddresses)
             .add("mac-address", macAddress)
+            .add("vlan", vlan)
             .toString();
     }
 }
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.
      */
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index 576c003..a78cadb 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -15,10 +15,7 @@
  */
 package org.onosproject.provider.of.flow.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.List;
-
+import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
@@ -63,7 +60,9 @@
 import org.projectfloodlight.openflow.types.VlanPcp;
 import org.slf4j.Logger;
 
-import com.google.common.collect.Lists;
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
 
 public class FlowEntryBuilder {
     private final Logger log = getLogger(getClass());
@@ -380,8 +379,19 @@
                 builder.matchVlanPcp(vlanPcp);
                 break;
             case VLAN_VID:
-                VlanId vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan());
-                builder.matchVlanId(vlanId);
+                VlanId vlanId = null;
+                if (match.isPartiallyMasked(MatchField.VLAN_VID)) {
+                    Masked<OFVlanVidMatch> masked = match.getMasked(MatchField.VLAN_VID);
+                    if (masked.getValue().equals(OFVlanVidMatch.PRESENT)
+                            && masked.getMask().equals(OFVlanVidMatch.PRESENT)) {
+                        vlanId = VlanId.ANY;
+                    }
+                } else {
+                    vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan());
+                }
+                if (vlanId != null) {
+                    builder.matchVlanId(vlanId);
+                }
                 break;
             case TCP_DST:
                 builder.matchTcpDst((short) match.get(MatchField.TCP_DST).getPort());
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index 78efee2..016b0cf 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -15,10 +15,11 @@
  */
 package org.onosproject.provider.of.flow.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.Ip6Prefix;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criteria;
@@ -26,16 +27,14 @@
 import org.onosproject.net.flow.criteria.Criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.Criteria.IPCriterion;
 import org.onosproject.net.flow.criteria.Criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.Criteria.Icmpv6CodeCriterion;
+import org.onosproject.net.flow.criteria.Criteria.Icmpv6TypeCriterion;
 import org.onosproject.net.flow.criteria.Criteria.LambdaCriterion;
 import org.onosproject.net.flow.criteria.Criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.Criteria.TcpPortCriterion;
 import org.onosproject.net.flow.criteria.Criteria.VlanIdCriterion;
 import org.onosproject.net.flow.criteria.Criteria.VlanPcpCriterion;
-import org.onosproject.net.flow.criteria.Criteria.Icmpv6TypeCriterion;
-import org.onosproject.net.flow.criteria.Criteria.Icmpv6CodeCriterion;
 import org.onosproject.net.flow.criteria.Criterion;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFlowAdd;
 import org.projectfloodlight.openflow.protocol.OFFlowDelete;
@@ -60,6 +59,8 @@
 
 import java.util.Optional;
 
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Builder for OpenFlow flow mods based on FlowRules.
  */
@@ -199,8 +200,14 @@
                 break;
             case VLAN_VID:
                 VlanIdCriterion vid = (VlanIdCriterion) c;
-                mBuilder.setExact(MatchField.VLAN_VID,
-                        OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
+
+                if (vid.vlanId().equals(VlanId.ANY)) {
+                    mBuilder.setMasked(MatchField.VLAN_VID, OFVlanVidMatch.PRESENT,
+                                       OFVlanVidMatch.PRESENT);
+                } else {
+                    mBuilder.setExact(MatchField.VLAN_VID,
+                            OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
+                }
                 break;
             case TCP_DST:
                 tp = (TcpPortCriterion) c;
diff --git a/utils/misc/src/main/java/org/onlab/packet/VlanId.java b/utils/misc/src/main/java/org/onlab/packet/VlanId.java
index 26a0162..c517bd0 100644
--- a/utils/misc/src/main/java/org/onlab/packet/VlanId.java
+++ b/utils/misc/src/main/java/org/onlab/packet/VlanId.java
@@ -25,7 +25,13 @@
     // Based on convention used elsewhere? Check and change if needed
     public static final short UNTAGGED = (short) 0xffff;
 
+    // In a traffic selector, this means that a VLAN ID must be present, but
+    // can have any value. We use the same value as OpenFlow, but this is not
+    // required.
+    public static final short ANY_VALUE = (short) 0x1000;
+
     public static final VlanId NONE = VlanId.vlanId(UNTAGGED);
+    public static final VlanId ANY = VlanId.vlanId(ANY_VALUE);
 
     // A VLAN ID is actually 12 bits of a VLAN tag.
     public static final short MAX_VLAN = 4095;
@@ -47,6 +53,10 @@
             return new VlanId();
         }
 
+        if (value == ANY_VALUE) {
+            return new VlanId(ANY_VALUE);
+        }
+
         if (value > MAX_VLAN) {
             throw new IllegalArgumentException(
                     "value exceeds allowed maximum VLAN ID value (4095)");
@@ -83,6 +93,9 @@
 
     @Override
     public String toString() {
+        if (this.value == ANY_VALUE) {
+            return "Any";
+        }
         return String.valueOf(this.value);
     }
 }