Support double-tagged host

Change-Id: Ie4041a0b5159e7a8b3a9ed82b55ce3c26b520a3b
diff --git a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
index fa125f7..683ab26 100644
--- a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
@@ -38,7 +38,8 @@
 public class HostsListCommand extends AbstractShellCommand {
 
     private static final String FMT =
-            "id=%s, mac=%s, locations=%s, vlan=%s, ip(s)=%s%s, provider=%s:%s, configured=%s";
+            "id=%s, mac=%s, locations=%s, vlan=%s, ip(s)=%s%s, innerVlan=%s, outerTPID=%s, " +
+                    "provider=%s:%s, configured=%s";
 
     private static final String FMT_SHORT =
             "id=%s, mac=%s, locations=%s, vlan=%s, ip(s)=%s";
@@ -94,6 +95,7 @@
             print(FMT, host.id(), host.mac(),
                   host.locations(),
                   host.vlan(), host.ipAddresses(), annotations(host.annotations()),
+                  host.innerVlan(), host.tpid().toString(),
                   host.providerId().scheme(), host.providerId().id(),
                   host.configured());
         }
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultHost.java b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
index b430020..ec0d9b5 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultHost.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultHost.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net;
 
+import org.onlab.packet.EthType;
 import org.onosproject.net.provider.ProviderId;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
@@ -37,8 +38,11 @@
     private final VlanId vlan;
     private final Set<HostLocation> locations;
     private final Set<IpAddress> ips;
+    private final VlanId innerVlan;
+    private final EthType tpid;
     private final boolean configured;
 
+    // TODO consider moving this constructor to a builder pattern.
     /**
      * Creates an end-station host using the supplied information.
      *
@@ -78,24 +82,47 @@
     /**
      * Creates an end-station host using the supplied information.
      *
-     * @param providerId  provider identity
-     * @param id          host identifier
-     * @param mac         host MAC address
-     * @param vlan        host VLAN identifier
-     * @param locations   set of host locations
-     * @param ips         host IP addresses
+     * @param providerId provider identity
+     * @param id         host identifier
+     * @param mac        host MAC address
+     * @param vlan       host VLAN identifier
+     * @param locations  set of host locations
+     * @param ips        host IP addresses
      * @param configured  true if configured via NetworkConfiguration
      * @param annotations optional key/value annotations
      */
     public DefaultHost(ProviderId providerId, HostId id, MacAddress mac,
                        VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
                        boolean configured, Annotations... annotations) {
+        this(providerId, id, mac, vlan, locations, ips, VlanId.NONE,
+             EthType.EtherType.UNKNOWN.ethType(), configured, annotations);
+    }
+
+    /**
+     * Creates an end-station host using the supplied information.
+     *
+     * @param providerId  provider identity
+     * @param id          host identifier
+     * @param mac         host MAC address
+     * @param vlan        host VLAN identifier
+     * @param locations   set of host locations
+     * @param ips         host IP addresses
+     * @param innerVlan   host inner VLAN identifier
+     * @param tpid        outer TPID of a host
+     * @param configured  true if configured via NetworkConfiguration
+     * @param annotations optional key/value annotations
+     */
+    public DefaultHost(ProviderId providerId, HostId id, MacAddress mac, VlanId vlan,
+                       Set<HostLocation> locations, Set<IpAddress> ips, VlanId innerVlan,
+                       EthType tpid, boolean configured, Annotations... annotations) {
         super(providerId, id, annotations);
         this.mac = mac;
         this.vlan = vlan;
         this.locations = new HashSet<>(locations);
         this.ips = new HashSet<>(ips);
         this.configured = configured;
+        this.innerVlan = innerVlan;
+        this.tpid = tpid;
     }
 
     @Override
@@ -135,6 +162,16 @@
     }
 
     @Override
+    public VlanId innerVlan() {
+        return innerVlan;
+    }
+
+    @Override
+    public EthType tpid() {
+        return tpid;
+    }
+
+    @Override
     public boolean configured() {
         return configured;
     }
@@ -156,6 +193,8 @@
                     Objects.equals(this.vlan, other.vlan) &&
                     Objects.equals(this.locations, other.locations) &&
                     Objects.equals(this.ipAddresses(), other.ipAddresses()) &&
+                    Objects.equals(this.innerVlan, other.innerVlan) &&
+                    Objects.equals(this.tpid, other.tpid) &&
                     Objects.equals(this.annotations(), other.annotations());
         }
         return false;
@@ -171,6 +210,8 @@
                 .add("ipAddresses", ipAddresses())
                 .add("annotations", annotations())
                 .add("configured", configured())
+                .add("innerVlanId", innerVlan())
+                .add("outerTPID", tpid())
                 .toString();
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/Host.java b/core/api/src/main/java/org/onosproject/net/Host.java
index b47f843..1c2e50a 100644
--- a/core/api/src/main/java/org/onosproject/net/Host.java
+++ b/core/api/src/main/java/org/onosproject/net/Host.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.net;
 
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -77,6 +78,24 @@
     default boolean configured() {
         return false;
     }
+
+    /**
+     * Returns the inner VLAN ID tied to this host.
+     *
+     * @return VLAN ID value; VlanId.NONE if only one VLAN ID is tied to this host
+     */
+    default VlanId innerVlan() {
+        return VlanId.NONE;
+    }
+
+    /**
+     * Returns the TPID of the outermost VLAN associated with this host.
+     *
+     * @return TPID of the outermost VLAN header
+     */
+    default EthType tpid() {
+        return EthType.EtherType.UNKNOWN.ethType();
+    }
     // TODO: explore capturing list of recent locations to aid in mobility
 
 }
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
index 3879257..17abc53 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
@@ -17,7 +17,9 @@
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
@@ -25,6 +27,8 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 /**
  * Basic configuration for network end-station hosts.
  */
@@ -32,16 +36,29 @@
 
     private static final String IPS = "ips";
     private static final String LOCATIONS = "locations";
+    private static final String INNER_VLAN = "innerVlan";
+    private static final String OUTER_TPID = "outerTpid";
     private static final String DASH = "-";
 
     @Override
     public boolean isValid() {
         // locations is mandatory and must have at least one
         // ipAddresses can be absent, but if present must be valid
+        // innerVlan: 0 < innerVlan < VlanId.MAX_VLAN, if present
+        // outerTpid: either 0x8100 or 0x88a8, if present
+        if (!isIntegralNumber(object, INNER_VLAN, FieldPresence.OPTIONAL, 0, VlanId.MAX_VLAN)) {
+            return false;
+        }
+        checkArgument(!hasField(object, OUTER_TPID) ||
+                              (short) (Integer.decode(get(OUTER_TPID, "0")) & 0xFFFF) ==
+                              EthType.EtherType.QINQ.ethType().toShort() ||
+                              (short) (Integer.decode(get(OUTER_TPID, "0")) & 0xFFFF) ==
+                              EthType.EtherType.VLAN.ethType().toShort());
         this.locations();
         this.ipAddresses();
-        return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE, ROLES,
-                             GRID_X, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS);
+        return hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE,
+                             GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS,
+                             INNER_VLAN, OUTER_TPID);
     }
 
     @Override
@@ -119,4 +136,26 @@
     public BasicHostConfig setIps(Set<IpAddress> ipAddresses) {
         return (BasicHostConfig) setOrClear(IPS, ipAddresses);
     }
-}
\ No newline at end of file
+
+    public VlanId innerVlan() {
+        String vlan = get(INNER_VLAN, null);
+        return vlan == null ? VlanId.NONE : VlanId.vlanId(Short.valueOf(vlan));
+    }
+
+    public BasicHostConfig setInnerVlan(VlanId vlanId) {
+        return (BasicHostConfig) setOrClear(INNER_VLAN, vlanId.toString());
+    }
+
+    public EthType outerTpid() {
+        short tpid = (short) (Integer.decode(get(OUTER_TPID, "0")) & 0xFFFF);
+        if (!(tpid == EthType.EtherType.VLAN.ethType().toShort() ||
+                tpid == EthType.EtherType.QINQ.ethType().toShort())) {
+            return EthType.EtherType.UNKNOWN.ethType();
+        }
+        return EthType.EtherType.lookup(tpid).ethType();
+    }
+
+    public BasicHostConfig setOuterTpid(EthType tpid) {
+        return (BasicHostConfig) setOrClear(OUTER_TPID, String.format("0x%04X", tpid.toShort()));
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java b/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
index e063c95..1992283 100644
--- a/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/host/DefaultHostDescription.java
@@ -20,6 +20,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import org.onlab.packet.EthType;
 import org.onosproject.net.AbstractDescription;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.SparseAnnotations;
@@ -42,6 +43,8 @@
     private final VlanId vlan;
     private final Set<HostLocation> locations;
     private final Set<IpAddress> ip;
+    private final VlanId innerVlan;
+    private final EthType tpid;
     private final boolean configured;
 
     /**
@@ -135,11 +138,32 @@
                                   Set<HostLocation> locations,
                                   Set<IpAddress> ip, boolean configured,
                                   SparseAnnotations... annotations) {
+        this(mac, vlan, locations, ip, VlanId.NONE, EthType.EtherType.UNKNOWN.ethType(),
+             configured, annotations);
+    }
+
+    /**
+     * Creates a host description using the supplied information.
+     *
+     * @param mac          host MAC address
+     * @param vlan         host VLAN identifier
+     * @param locations    host locations
+     * @param ip           host IP address
+     * @param innerVlan    host inner VLAN identifier
+     * @param tpid         outer TPID of a host
+     * @param configured   true if configured via NetworkConfiguration
+     * @param annotations  optional key/value annotations map
+     */
+    public DefaultHostDescription(MacAddress mac, VlanId vlan, Set<HostLocation> locations,
+                                  Set<IpAddress> ip, VlanId innerVlan, EthType tpid,
+                                  boolean configured, SparseAnnotations... annotations) {
         super(annotations);
         this.mac = mac;
         this.vlan = vlan;
         this.locations = new HashSet<>(locations);
         this.ip = new HashSet<>(ip);
+        this.innerVlan = innerVlan;
+        this.tpid = tpid;
         this.configured = configured;
     }
 
@@ -176,6 +200,16 @@
     }
 
     @Override
+    public VlanId innerVlan() {
+        return innerVlan;
+    }
+
+    @Override
+    public EthType tpid() {
+        return tpid;
+    }
+
+    @Override
     public String toString() {
         return toStringHelper(this)
                 .add("mac", mac)
@@ -183,6 +217,8 @@
                 .add("locations", locations)
                 .add("ipAddress", ip)
                 .add("configured", configured)
+                .add("innerVlanId", innerVlan)
+                .add("outerTPID", tpid)
                 .toString();
     }
 
@@ -201,7 +237,9 @@
             return Objects.equal(this.mac, that.mac)
                     && Objects.equal(this.vlan, that.vlan)
                     && Objects.equal(this.locations, that.locations)
-                    && Objects.equal(this.ip, that.ip);
+                    && Objects.equal(this.ip, that.ip)
+                    && Objects.equal(this.innerVlan, that.innerVlan)
+                    && Objects.equal(this.tpid, that.tpid);
         }
         return false;
     }
diff --git a/core/api/src/main/java/org/onosproject/net/host/HostDescription.java b/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
index 7994599..543836e 100644
--- a/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/host/HostDescription.java
@@ -22,6 +22,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.packet.EthType;
 
 /**
  * Information describing host and its location.
@@ -43,6 +44,24 @@
     VlanId vlan();
 
     /**
+     * Returns the inner VLAN associated with this host.
+     *
+     * @return VLAN ID value; VlanId.NONE if only one VLAN ID is associated with this host
+     */
+    default VlanId innerVlan() {
+        return VlanId.NONE;
+    }
+
+    /**
+     * Returns the TPID of the outermost VLAN associated with this host.
+     *
+     * @return TPID of the outermost VLAN header
+     */
+    default EthType tpid() {
+        return EthType.EtherType.UNKNOWN.ethType();
+    }
+
+    /**
      * Returns the most recent location of the host on the network edge.
      *
      * @return the most recent host location
diff --git a/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java b/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
index 6721cc7..dab2c9d 100644
--- a/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
+++ b/core/api/src/test/java/org/onosproject/net/config/basics/BasicHostConfigTest.java
@@ -20,7 +20,9 @@
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.google.common.collect.ImmutableSet;
 import org.junit.Test;
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.NetTestTools;
@@ -36,7 +38,7 @@
 public class BasicHostConfigTest {
 
     /**
-     * Tests construction, setters and getters of a BasicLinkConfig object.
+     * Tests construction, setters and getters of a BasicHostConfig object.
      */
     @Test
     public void testConstruction() {
@@ -53,11 +55,15 @@
         HostLocation loc2 = new HostLocation(
                 NetTestTools.connectPoint("d2", 2), System.currentTimeMillis());
         Set<HostLocation> locs = ImmutableSet.of(loc1, loc2);
+        VlanId vlanId = VlanId.vlanId((short) 10);
+        EthType ethType = EthType.EtherType.lookup((short) 0x88a8).ethType();
 
         config.init(hostId, "KEY", JsonNodeFactory.instance.objectNode(), mapper, delegate);
 
         config.setIps(ips)
-              .setLocations(locs);
+              .setLocations(locs)
+              .setInnerVlan(vlanId)
+              .setOuterTpid(ethType);
 
         assertThat(config.isValid(), is(true));
         assertThat(config.name(), is("-"));
@@ -65,6 +71,8 @@
         assertThat(config.ipAddresses(), hasItems(ip1, ip2, ip3));
         assertThat(config.locations(), hasSize(2));
         assertThat(config.locations(), hasItems(loc1, loc2));
+        assertThat(config.innerVlan(), is(vlanId));
+        assertThat(config.outerTpid(), is(ethType));
     }
 
 }
\ No newline at end of file
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
index b82be46..5f8ca9d 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/HostCodec.java
@@ -40,6 +40,8 @@
                 .put("id", host.id().toString())
                 .put("mac", host.mac().toString())
                 .put("vlan", host.vlan().toString())
+                .put("innerVlan", host.innerVlan().toString())
+                .put("outerTpid", host.tpid().toString())
                 .put("configured", host.configured());
 
         final ArrayNode jsonIpAddresses = result.putArray("ipAddresses");
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
index 976e324..b196737 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
@@ -69,8 +69,8 @@
 
         SparseAnnotations sa = combine(cfg, descr.annotations());
         return new DefaultHostDescription(descr.hwAddress(), descr.vlan(),
-                                          locations, ipAddresses,
-                                          descr.configured(), sa);
+                                          locations, ipAddresses, descr.innerVlan(),
+                                          descr.tpid(), descr.configured(), sa);
     }
 
     /**
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
index bc7f197..8dc1613 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
@@ -218,6 +218,8 @@
         if (!Objects.equals(existingHost.providerId(), providerId) ||
                 !Objects.equals(existingHost.mac(), hostDescription.hwAddress()) ||
                 !Objects.equals(existingHost.vlan(), hostDescription.vlan()) ||
+                !Objects.equals(existingHost.innerVlan(), hostDescription.innerVlan()) ||
+                !Objects.equals(existingHost.tpid(), hostDescription.tpid()) ||
                 !Objects.equals(existingHost.locations(), hostDescription.locations())) {
             return true;
         }
@@ -275,6 +277,8 @@
                                                   hostDescription.vlan(),
                                                   hostDescription.locations(),
                                                   addresses,
+                                                  hostDescription.innerVlan(),
+                                                  hostDescription.tpid(),
                                                   hostDescription.configured(),
                                                   annotations);
                        });
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index 0633d12..45ed92d 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -28,6 +28,7 @@
 import org.onlab.packet.BasePacket;
 import org.onlab.packet.DHCP;
 import org.onlab.packet.DHCP6;
+import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ICMP6;
 import org.onlab.packet.IPacket;
@@ -441,15 +442,17 @@
          * Create or update host information.
          * Will not update IP if IP is null, all zero or self-assigned.
          *
-         * @param hid  host ID
-         * @param mac  source Mac address
-         * @param vlan VLAN ID
-         * @param hloc host location
-         * @param ip   source IP address or null if not updating
+         * @param hid       host ID
+         * @param mac       source Mac address
+         * @param vlan      VLAN ID
+         * @param innerVlan inner VLAN ID
+         * @param outerTpid outer TPID
+         * @param hloc      host location
+         * @param ip        source IP address or null if not updating
          */
-        private void createOrUpdateHost(HostId hid, MacAddress mac,
-                                        VlanId vlan, HostLocation hloc,
-                                        IpAddress ip) {
+        private void createOrUpdateHost(HostId hid, MacAddress mac, VlanId vlan,
+                                        VlanId innerVlan, EthType outerTpid,
+                                        HostLocation hloc, IpAddress ip) {
             Set<HostLocation> newLocations = Sets.newHashSet(hloc);
 
             if (multihomingEnabled) {
@@ -473,8 +476,10 @@
             }
 
             HostDescription desc = ip == null || ip.isZero() || ip.isSelfAssigned() ?
-                    new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(), false) :
-                    new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(ip), false);
+                    new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(),
+                                               innerVlan, outerTpid, false) :
+                    new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(ip),
+                                               innerVlan, outerTpid, false);
             try {
                 providerService.hostDetected(hid, desc, false);
             } catch (IllegalStateException e) {
@@ -521,6 +526,15 @@
             }
 
             VlanId vlan = VlanId.vlanId(eth.getVlanID());
+            VlanId outerVlan = VlanId.vlanId(eth.getQinQVID());
+            VlanId innerVlan = VlanId.NONE;
+            EthType outerTpid = EthType.EtherType.UNKNOWN.ethType();
+            // Set up values for double-tagged hosts
+            if (outerVlan.toShort() != Ethernet.VLAN_UNTAGGED) {
+                innerVlan = vlan;
+                vlan = outerVlan;
+                outerTpid = EthType.EtherType.lookup(eth.getQinQTPID()).ethType();
+            }
             ConnectPoint heardOn = context.inPacket().receivedFrom();
 
             // If this arrived on control port, bail out.
@@ -550,12 +564,12 @@
                 ARP arp = (ARP) eth.getPayload();
                 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
                                                  arp.getSenderProtocolAddress());
-                createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
+                createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, ip);
 
             // IPv4: update location only
             } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
                 // Update host location
-                createOrUpdateHost(hid, srcMac, vlan, hloc, null);
+                createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, null);
                 if (useDhcp) {
                     DHCP dhcp = findDhcp(eth).orElse(null);
                     // DHCP ACK: additionally update IP of DHCP client
@@ -586,7 +600,7 @@
                 // DHCPv6 protocol
                 DHCP6 dhcp6 = findDhcp6(pkt).orElse(null);
                 if (dhcp6 != null && useDhcp6) {
-                    createOrUpdateHost(hid, srcMac, vlan, hloc, null);
+                    createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, null);
                     handleDhcp6(dhcp6, vlan);
                     return;
                 }
@@ -605,13 +619,13 @@
                                 return;
                             }
                             // NeighborSolicitation, NeighborAdvertisement
-                            createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
+                            createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, ip);
 
                             // Also learn from the target address of NeighborAdvertisement
                             if (pkt instanceof NeighborAdvertisement) {
                                 NeighborAdvertisement na = (NeighborAdvertisement) pkt;
                                 Ip6Address targetAddr = Ip6Address.valueOf(na.getTargetAddress());
-                                createOrUpdateHost(hid, srcMac, vlan, hloc, targetAddr);
+                                createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, targetAddr);
                             }
                             return;
                         }
@@ -624,7 +638,7 @@
                 }
 
                 // normal IPv6 packets
-                createOrUpdateHost(hid, srcMac, vlan, hloc, null);
+                createOrUpdateHost(hid, srcMac, vlan, innerVlan, outerTpid, hloc, null);
             }
         }
 
diff --git a/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java b/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
index 42278d3..a0be2d9 100644
--- a/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
+++ b/providers/netcfghost/src/main/java/org/onosproject/provider/netcfghost/NetworkConfigHostProvider.java
@@ -21,6 +21,7 @@
 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.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -120,6 +121,28 @@
     }
 
     /**
+     * Adds host information.
+     * IP information will be appended if host exists.
+     *
+     * @param mac       MAC address of the host
+     * @param vlan      VLAN ID of the host
+     * @param locations Location of the host
+     * @param ips       Set of IP addresses of the host
+     * @param innerVlan host inner VLAN identifier
+     * @param outerTpid outer TPID of a host
+     */
+    protected void addHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
+                           VlanId innerVlan, EthType outerTpid) {
+        HostId hid = HostId.hostId(mac, vlan);
+        HostDescription desc = (ips != null) ?
+                new DefaultHostDescription(mac, vlan, locations, ips,
+                                           innerVlan, outerTpid, true) :
+                new DefaultHostDescription(mac, vlan, locations, Collections.emptySet(),
+                                           innerVlan, outerTpid, true);
+        providerService.hostDetected(hid, desc, true);
+    }
+
+    /**
      * Updates host information.
      * IP information will be replaced if host exists.
      *
@@ -135,6 +158,25 @@
     }
 
     /**
+     * Updates host information.
+     * IP information will be replaced if host exists.
+     *
+     * @param mac       MAC address of the host
+     * @param vlan      VLAN ID of the host
+     * @param locations Location of the host
+     * @param ips       Set of IP addresses of the host
+     * @param innerVlan host inner VLAN identifier
+     * @param outerTpid outer TPID of a host
+     */
+    protected void updateHost(MacAddress mac, VlanId vlan, Set<HostLocation> locations, Set<IpAddress> ips,
+                              VlanId innerVlan, EthType outerTpid) {
+        HostId hid = HostId.hostId(mac, vlan);
+        HostDescription desc = new DefaultHostDescription(mac, vlan, locations, ips,
+                                                          innerVlan, outerTpid, true);
+        providerService.hostDetected(hid, desc, true);
+    }
+
+    /**
      * Removes host information.
      *
      * @param mac MAC address of the host
@@ -155,7 +197,9 @@
             Set<HostLocation> locations = hostConfig.locations().stream()
                     .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
                     .collect(Collectors.toSet());
-            addHost(mac, vlan, locations, ipAddresses);
+            VlanId innerVlan = hostConfig.innerVlan();
+            EthType outerTpid = hostConfig.outerTpid();
+            addHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
         });
     }
 
@@ -175,6 +219,8 @@
             BasicHostConfig hostConfig = networkConfigRegistry.getConfig(hostId, BasicHostConfig.class);
             Set<IpAddress> ipAddresses = null;
             Set<HostLocation> locations = null;
+            VlanId innerVlan = VlanId.NONE;
+            EthType outerTpid = EthType.EtherType.UNKNOWN.ethType();
 
             // Note: There will be no config presented in the CONFIG_REMOVE case
             if (hostConfig != null) {
@@ -187,14 +233,16 @@
                 locations = locations.stream()
                         .map(hostLocation -> new HostLocation(hostLocation, System.currentTimeMillis()))
                         .collect(Collectors.toSet());
+                innerVlan = hostConfig.innerVlan();
+                outerTpid = hostConfig.outerTpid();
             }
 
             switch (event.type()) {
                 case CONFIG_ADDED:
-                    addHost(mac, vlan, locations, ipAddresses);
+                    addHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
                     break;
                 case CONFIG_UPDATED:
-                    updateHost(mac, vlan, locations, ipAddresses);
+                    updateHost(mac, vlan, locations, ipAddresses, innerVlan, outerTpid);
                     break;
                 case CONFIG_REMOVED:
                     removeHost(mac, vlan);
diff --git a/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java b/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
index 36ad76d..36de211 100644
--- a/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
+++ b/providers/netcfghost/src/test/java/org/onosproject/provider/netcfghost/NetworkConfigHostProviderTest.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -54,6 +55,8 @@
     private Set<IpAddress> ips = new HashSet<>();
     private HostId hostId = HostId.hostId(mac, vlan);
     private HostDescription hostDescription;
+    private VlanId innerVlan = VlanId.vlanId((short) 20);
+    private EthType outerTpid = EthType.EtherType.lookup((short) 0x88a8).ethType();
 
     @Before
     public void setUp() {
@@ -62,12 +65,13 @@
         // Initialize test variables
         ips.add(IpAddress.valueOf("10.0.0.1"));
         ips.add(IpAddress.valueOf("192.168.0.1"));
-        hostDescription = new DefaultHostDescription(mac, vlan, locations, ips, true);
+        hostDescription = new DefaultHostDescription(mac, vlan, locations, ips,
+                                                     innerVlan, outerTpid, true);
     }
 
     @Test
     public void testAddHost() throws Exception {
-        provider.addHost(mac, vlan, locations, ips);
+        provider.addHost(mac, vlan, locations, ips, innerVlan, outerTpid);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));
@@ -76,7 +80,7 @@
 
     @Test
     public void testUpdateHost() throws Exception {
-        provider.updateHost(mac, vlan, locations, ips);
+        provider.updateHost(mac, vlan, locations, ips, innerVlan, outerTpid);
         assertThat(providerService.hostId, is(hostId));
         assertThat(providerService.hostDescription, is(hostDescription));
         assertThat(providerService.event, is("hostDetected"));
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
index 4bb9523..5ab2614 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
@@ -217,7 +217,7 @@
         @Override
         public boolean matchesSafely(JsonArray json) {
             boolean hostFound = false;
-            final int expectedAttributes = 6;
+            final int expectedAttributes = 8;
             for (int jsonHostIndex = 0; jsonHostIndex < json.size();
                  jsonHostIndex++) {