CORD-800 Use new interface vlan config for VLAN assignment

In addition
  - Change the name and type of INTERNAL_VLAN

Change-Id: I48bb89cc1cb1fe4155b4d889b1e7384c8bf673fa
diff --git a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
index 30c6ada..8f0b7d3 100644
--- a/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
@@ -135,8 +135,7 @@
             // ARP reply for unknown host, Flood in the subnet.
             } else {
                 // Don't flood to non-edge ports
-                if (pkt.vlan().equals(
-                        VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
+                if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
                     return;
                 }
                 flood(pkt);
diff --git a/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 62ab1ce..b229793 100644
--- a/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -16,10 +16,10 @@
 
 package org.onosproject.segmentrouting;
 
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostLocation;
@@ -42,6 +42,8 @@
 import com.google.common.collect.Sets;
 import java.util.Set;
 
+import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
+
 /**
  * Handles host-related events.
  */
@@ -258,15 +260,8 @@
     private ForwardingObjective.Builder hostFwdObjBuilder(
             DeviceId deviceId, MacAddress mac, VlanId vlanId,
             PortNumber outport) {
-        // Get assigned VLAN for the subnets
-        VlanId outvlan = null;
-        // FIXME L2 forwarding should consider also IPv6
-        Ip4Prefix subnet = srManager.deviceConfiguration.getPortIPv4Subnet(deviceId, outport);
-        if (subnet == null) {
-            outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
-        } else {
-            outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
-        }
+        VlanId untaggedVlan = srManager.getUntaggedVlanId(new ConnectPoint(deviceId, outport));
+        VlanId outvlan = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
 
         // match rule
         TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index c924929..16784c2 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -387,7 +387,7 @@
                 /*
                  * We don't have to flood towards spine facing ports.
                  */
-                if (pkt.vlan().equals(VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET))) {
+                if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
                     return;
                 }
                 log.debug("Flooding the reply to the subnet");
diff --git a/src/main/java/org/onosproject/segmentrouting/McastHandler.java b/src/main/java/org/onosproject/segmentrouting/McastHandler.java
index f95addc..c1fa518 100644
--- a/src/main/java/org/onosproject/segmentrouting/McastHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/McastHandler.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -69,6 +68,7 @@
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 
 /**
  * Handles multicast-related events.
@@ -375,19 +375,14 @@
             return;
         }
 
-        // Reuse unicast VLAN if the port has subnet configured
-        Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortIPv4Subnet(deviceId, port);
-        VlanId unicastVlan = srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
-        final VlanId finalVlanId = (unicastVlan != null) ? unicastVlan : assignedVlan;
-
         FilteringObjective.Builder filtObjBuilder =
-                filterObjBuilder(deviceId, port, finalVlanId);
+                filterObjBuilder(deviceId, port, assignedVlan);
         ObjectiveContext context = new DefaultObjectiveContext(
                 (objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
-                        deviceId, port.toLong(), finalVlanId),
+                        deviceId, port.toLong(), assignedVlan),
                 (objective, error) ->
                         log.warn("Failed to add filter on {}/{}, vlan {}: {}",
-                                deviceId, port.toLong(), finalVlanId, error));
+                                deviceId, port.toLong(), assignedVlan, error));
         srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
     }
 
@@ -763,15 +758,11 @@
         }
         // Reuse unicast VLAN if the port has subnet configured
         if (cp != null) {
-            Ip4Prefix portSubnet = srManager.deviceConfiguration
-                    .getPortIPv4Subnet(cp.deviceId(), cp.port());
-            VlanId unicastVlan = srManager.getSubnetAssignedVlanId(cp.deviceId(), portSubnet);
-            if (unicastVlan != null) {
-                return unicastVlan;
-            }
+            VlanId untaggedVlan = srManager.getUntaggedVlanId(cp);
+            return (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
         }
-        // By default, use VLAN_NO_SUBNET
-        return VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
+        // Use DEFAULT_VLAN if none of the above matches
+        return SegmentRoutingManager.INTERNAL_VLAN;
     }
 
     /**
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 050bb97..54e43d5 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -19,13 +19,13 @@
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.intf.Interface;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.flowobjective.DefaultObjectiveContext;
 import org.onosproject.net.flowobjective.Objective;
@@ -35,7 +35,6 @@
 import org.onosproject.segmentrouting.DefaultRoutingHandler.PortFilterInfo;
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
@@ -62,12 +61,14 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.packet.Ethernet.TYPE_ARP;
 import static org.onlab.packet.Ethernet.TYPE_IPV6;
 import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
 import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
+import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 
 /**
  * Populator of segment routing flow rules.
@@ -203,13 +204,9 @@
 
         // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
         // for switch pipelines that need it, provide outgoing vlan as metadata
-        VlanId outvlan;
-        Ip4Prefix subnet = srManager.deviceConfiguration.getPortIPv4Subnet(deviceId, outPort);
-        if (subnet == null) {
-            outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
-        } else {
-            outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
-        }
+        VlanId untaggedVlan = srManager.getUntaggedVlanId(new ConnectPoint(deviceId, outPort));
+        VlanId outvlan = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
+
         TrafficSelector meta = DefaultTrafficSelector.builder()
                                     .matchVlanId(outvlan).build();
         int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
@@ -307,8 +304,7 @@
         // if needed by the switch pipeline. Since neighbor sets are always to
         // other neighboring routers, there is no subnet assigned on those ports.
         TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(
-            VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
+        metabuilder.matchVlanId(SegmentRoutingManager.INTERNAL_VLAN);
 
         int nextId = srManager.getNextObjectiveId(deviceId, ns, metabuilder.build());
         if (nextId <= 0) {
@@ -404,8 +400,7 @@
         // if needed by the switch pipeline. Since mpls next-hops are always to
         // other neighboring routers, there is no subnet assigned on those ports.
         TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(
-                VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
+        metabuilder.matchVlanId(SegmentRoutingManager.INTERNAL_VLAN);
 
         if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
             // If the next hop is the destination router for the segment, do pop
@@ -679,7 +674,6 @@
         return;
     }
 
-
     private FilteringObjective.Builder buildFilteringObjective(DeviceId deviceId,
                                                                PortNumber portnum) {
         MacAddress deviceMac;
@@ -691,23 +685,9 @@
         }
         // suppressed ports still have filtering rules pushed by SR using default vlan
         ConnectPoint connectPoint = new ConnectPoint(deviceId, portnum);
-        boolean isSuppressed = false;
-        SegmentRoutingAppConfig appConfig = srManager.cfgService
-                .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
-        if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
-            isSuppressed = true;
-        }
 
-        Ip4Prefix portSubnet = config.getPortIPv4Subnet(deviceId, portnum);
-        VlanId assignedVlan = (portSubnet == null || isSuppressed)
-                ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
-                : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
-
-        if (assignedVlan == null) {
-            log.warn("Assigned vlan is null for {} in {} - Processing "
-                    + "SinglePortFilters aborted", portnum, deviceId);
-            return null;
-        }
+        VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
+        VlanId assignedVlan = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
 
         FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
         fob.withKey(Criteria.matchInPort(portnum))
@@ -870,22 +850,20 @@
      * @param deviceId switch ID to set the rules
      */
     public void populateSubnetBroadcastRule(DeviceId deviceId) {
-        config.getSubnets(deviceId).forEach(subnet -> {
-            if (subnet.isIp4()) {
-                if (subnet.prefixLength() == 0 || subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH) {
-                    return;
-                }
-            } else {
-                if (subnet.prefixLength() == 0 || subnet.prefixLength() == IpPrefix.MAX_INET6_MASK_LENGTH) {
-                    return;
-                }
-            }
-            int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
-            VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
+        Set<Interface> interfaces = srManager.interfaceService.getInterfaces();
+        Set<VlanId> vlans =
+                interfaces.stream()
+                        .filter(intf -> intf.connectPoint().deviceId().equals(deviceId) &&
+                                !intf.vlanUntagged().equals(VlanId.NONE))
+                        .map(Interface::vlanUntagged)
+                        .collect(Collectors.toSet());
 
-            if (nextId < 0 || vlanId == null) {
-                log.error("Cannot install subnet {} broadcast rule in dev:{} due"
-                        + "to vlanId:{} or nextId:{}", subnet, deviceId, vlanId, nextId);
+        vlans.forEach(vlanId -> {
+            int nextId = srManager.getVlanNextObjectiveId(deviceId, vlanId);
+
+            if (nextId < 0) {
+                log.error("Cannot install vlan {} broadcast rule in dev:{} due"
+                        + "to vlanId:{} or nextId:{}", vlanId, deviceId, vlanId, nextId);
                 return;
             }
 
@@ -903,9 +881,9 @@
                     .fromApp(srManager.appId)
                     .makePermanent();
             ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("Subnet broadcast rule for {} populated", subnet),
+                    (objective) -> log.debug("Vlan broadcast rule for {} populated", vlanId),
                     (objective, error) ->
-                            log.warn("Failed to populate subnet broadcast rule for {}: {}", subnet, error));
+                            log.warn("Failed to populate vlan broadcast rule for {}: {}", vlanId, error));
             srManager.flowObjectiveService.forward(deviceId, fob.add(context));
         });
     }
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 8cd657b..0c30cef 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -26,7 +26,6 @@
 import org.onlab.packet.ICMP6;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IPv6;
-import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
@@ -88,7 +87,7 @@
 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.SubnetAssignedVidStoreKey;
-import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
 import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
 import org.onosproject.store.serializers.KryoNamespaces;
@@ -102,8 +101,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -113,7 +110,6 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkState;
 import static org.onlab.packet.Ethernet.TYPE_ARP;
@@ -224,17 +220,14 @@
     /**
      * Per device next objective ID store with (device id + subnet) as key.
      */
-    public EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
-            subnetNextObjStore = null;
+    public EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
+            vlanNextObjStore = null;
     /**
      * Per device next objective ID store with (device id + port) as key.
      */
     public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
             portNextObjStore = null;
-    // Per device, per-subnet assigned-vlans store, with (device id + subnet
-    // IPv4 prefix) as key
-    private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
-            subnetVidStore = null;
+
     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
     private EventuallyConsistentMap<String, Policy> policyStore = null;
 
@@ -298,14 +291,11 @@
      * Segment Routing App ID.
      */
     public static final String APP_NAME = "org.onosproject.segmentrouting";
-    /**
-     * The starting value of per-subnet VLAN ID assignment.
-     */
-    private static final short ASSIGNED_VLAN_START = 4093;
+
     /**
      * The default VLAN ID assigned to the interfaces without subnet config.
      */
-    public static final short ASSIGNED_VLAN_NO_SUBNET = 4094;
+    public static final VlanId INTERNAL_VLAN = VlanId.vlanId((short) 4094);
 
     @Activate
     protected void activate() {
@@ -321,11 +311,11 @@
                 .build();
         log.trace("Current size {}", nsNextObjStore.size());
 
-        log.debug("Creating EC map subnetnextobjectivestore");
-        EventuallyConsistentMapBuilder<SubnetNextObjectiveStoreKey, Integer>
-                subnetNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        subnetNextObjStore = subnetNextObjMapBuilder
-                .withName("subnetnextobjectivestore")
+        log.debug("Creating EC map vlannextobjectivestore");
+        EventuallyConsistentMapBuilder<VlanNextObjectiveStoreKey, Integer>
+                vlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
+        vlanNextObjStore = vlanNextObjMapBuilder
+                .withName("vlannextobjectivestore")
                 .withSerializer(createSerializer())
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
@@ -355,14 +345,6 @@
                 .withTimestampProvider((k, v) -> new WallClockTimestamp())
                 .build();
 
-        EventuallyConsistentMapBuilder<SubnetAssignedVidStoreKey, VlanId>
-            subnetVidStoreMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        subnetVidStore = subnetVidStoreMapBuilder
-                .withName("subnetvidstore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
         compCfgService.preSetProperty("org.onosproject.net.group.impl.GroupManager",
                                       "purgeOnDisconnection", "true");
         compCfgService.preSetProperty("org.onosproject.net.flow.impl.FlowRuleManager",
@@ -413,7 +395,7 @@
         return new KryoNamespace.Builder()
                 .register(KryoNamespaces.API)
                 .register(NeighborSetNextObjectiveStoreKey.class,
-                        SubnetNextObjectiveStoreKey.class,
+                        VlanNextObjectiveStoreKey.class,
                         SubnetAssignedVidStoreKey.class,
                         NeighborSet.class,
                         Tunnel.class,
@@ -450,11 +432,10 @@
         groupHandlerMap.clear();
 
         nsNextObjStore.destroy();
-        subnetNextObjStore.destroy();
+        vlanNextObjStore.destroy();
         portNextObjStore.destroy();
         tunnelStore.destroy();
         policyStore.destroy();
-        subnetVidStore.destroy();
         log.info("Stopped");
     }
 
@@ -539,69 +520,16 @@
     }
 
     /**
-     * Returns the vlan-id assigned to the subnet configured for a device.
-     * If no vlan-id has been assigned, a new one is assigned out of a pool of ids,
-     * if and only if this controller instance is the master for the device.
-     * <p>
-     * USAGE: The assigned vlans are meant to be applied to untagged packets on those
-     * switches/pipelines that need this functionality. These vids are meant
-     * to be used internally within a switch, and thus need to be unique only
-     * on a switch level. Note that packets never go out on the wire with these
-     * vlans. Currently, vlan ids are assigned from value 4093 down.
-     * Vlan id 4094 expected to be used for all ports that are not assigned subnets.
-     * Vlan id 4095 is reserved and unused. Only a single vlan id is assigned
-     * per subnet.
+     * Returns untagged VLAN configured on given connect point.
      *
-     * @param deviceId switch dpid
-     * @param subnet IP prefix for which assigned vlan is desired
-     * @return VlanId assigned for the subnet on the device, or
-     *         null if no vlan assignment was found and this instance is not
-     *         the master for the device.
+     * @param connectPoint connect point
+     * @return untagged VLAN or null if not configured
      */
-    // TODO: We should avoid assigning VLAN IDs that are used by VLAN cross-connection.
-    public VlanId getSubnetAssignedVlanId(DeviceId deviceId, IpPrefix subnet) {
-        VlanId assignedVid = subnetVidStore.get(new SubnetAssignedVidStoreKey(
-                                                        deviceId, subnet));
-        if (assignedVid != null) {
-            log.debug("Query for subnet:{} on device:{} returned assigned-vlan "
-                    + "{}", subnet, deviceId, assignedVid);
-            return assignedVid;
-        }
-        //check mastership for the right to assign a vlan
-        if (!mastershipService.isLocalMaster(deviceId)) {
-            log.warn("This controller instance is not the master for device {}. "
-                    + "Cannot assign vlan-id for subnet {}", deviceId, subnet);
-            return null;
-        }
-        // vlan assignment is expensive but done only once
-        // FIXME for now we will do assignment considering only the ipv4 subnet.
-        Set<Ip4Prefix> configuredSubnets = deviceConfiguration.getSubnets(deviceId)
-                .stream()
-                .filter(IpPrefix::isIp4)
-                .map(IpPrefix::getIp4Prefix)
-                .collect(Collectors.toSet());
-        Set<Short> assignedVlans = new HashSet<>();
-        Set<Ip4Prefix> unassignedSubnets = new HashSet<>();
-        for (Ip4Prefix sub : configuredSubnets) {
-            VlanId v = subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId,
-                                                                        sub));
-            if (v != null) {
-                assignedVlans.add(v.toShort());
-            } else {
-                unassignedSubnets.add(sub);
-            }
-        }
-        short nextAssignedVlan = ASSIGNED_VLAN_START;
-        if (!assignedVlans.isEmpty()) {
-            nextAssignedVlan = (short) (Collections.min(assignedVlans) - 1);
-        }
-        for (Ip4Prefix unsub : unassignedSubnets) {
-            subnetVidStore.put(new SubnetAssignedVidStoreKey(deviceId, unsub),
-                    VlanId.vlanId(nextAssignedVlan--));
-            log.info("Assigned vlan: {} to subnet: {} on device: {}",
-                    nextAssignedVlan + 1, unsub, deviceId);
-        }
-        return subnetVidStore.get(new SubnetAssignedVidStoreKey(deviceId, subnet));
+    public VlanId getUntaggedVlanId(ConnectPoint connectPoint) {
+        return interfaceService.getInterfacesByPort(connectPoint).stream()
+                .filter(intf -> !intf.vlanUntagged().equals(VlanId.NONE))
+                .map(Interface::vlanUntagged)
+                .findFirst().orElse(null);
     }
 
     /**
@@ -648,19 +576,19 @@
 
     /**
      * Returns the next objective ID for the given subnet prefix. It is expected
+     * Returns the next objective ID for the given vlan id. It is expected
      * that the next-objective has been pre-created from configuration.
      *
      * @param deviceId Device ID
-     * @param prefix Subnet
+     * @param vlanId VLAN ID
      * @return next objective ID or -1 if it was not found
      */
-    public int getSubnetNextObjectiveId(DeviceId deviceId, IpPrefix prefix) {
+    public int getVlanNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
         if (groupHandlerMap.get(deviceId) != null) {
-            log.trace("getSubnetNextObjectiveId query in device {}", deviceId);
-            return groupHandlerMap
-                    .get(deviceId).getSubnetNextObjectiveId(prefix);
+            log.trace("getVlanNextObjectiveId query in device {}", deviceId);
+            return groupHandlerMap.get(deviceId).getVlanNextObjectiveId(vlanId);
         } else {
-            log.warn("getSubnetNextObjectiveId query - groupHandler for "
+            log.warn("getVlanNextObjectiveId query - groupHandler for "
                     + "device {} not found", deviceId);
             return -1;
         }
@@ -963,7 +891,7 @@
             xConnectHandler.init(deviceId);
             cordConfigHandler.init(deviceId);
             DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
-            groupHandler.createGroupsFromSubnetConfig();
+            groupHandler.createGroupsFromVlanConfig();
             routingRulePopulator.populateSubnetBroadcastRule(deviceId);
         }
 
@@ -977,21 +905,16 @@
                 .forEach(entry -> {
                     nsNextObjStore.remove(entry.getKey());
                 });
-        subnetNextObjStore.entrySet().stream()
+        vlanNextObjStore.entrySet().stream()
                 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
                 .forEach(entry -> {
-                    subnetNextObjStore.remove(entry.getKey());
+                    vlanNextObjStore.remove(entry.getKey());
                 });
         portNextObjStore.entrySet().stream()
                 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
                 .forEach(entry -> {
                     portNextObjStore.remove(entry.getKey());
                 });
-        subnetVidStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
-                .forEach(entry -> {
-                    subnetVidStore.remove(entry.getKey());
-                });
         groupHandlerMap.remove(device.id());
         defaultRoutingHandler.purgeEcmpGraph(device.id());
         mcastHandler.removeDevice(device.id());
@@ -1028,29 +951,30 @@
         // to switch ports, link-events should take care of any re-routing or
         // group editing necessary for port up/down. Here we only process edge ports
         // that are already configured.
-        Ip4Prefix configuredSubnet = deviceConfiguration.getPortIPv4Subnet(device.id(),
-                                                                           port.number());
-        if (configuredSubnet == null) {
+        VlanId untaggedVlan = getUntaggedVlanId(new ConnectPoint(device.id(), port.number()));
+        VlanId vlanId = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
+
+        if (vlanId.equals(INTERNAL_VLAN)) {
             log.debug("Not handling port updated event for unconfigured port "
                     + "dev/port: {}/{}", device.id(), port.number());
             return;
         }
-        processEdgePort(device, port, configuredSubnet);
+        processEdgePort(device, port, vlanId);
     }
 
-    private void processEdgePort(Device device, Port port, Ip4Prefix subnet) {
+    private void processEdgePort(Device device, Port port, VlanId vlanId) {
         boolean portUp = port.isEnabled();
         if (portUp) {
             log.info("Device:EdgePort {}:{} is enabled in subnet: {}", device.id(),
-                     port.number(), subnet);
+                     port.number(), vlanId);
         } else {
             log.info("Device:EdgePort {}:{} is disabled in subnet: {}", device.id(),
-                     port.number(), subnet);
+                     port.number(), vlanId);
         }
 
         DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
         if (groupHandler != null) {
-            groupHandler.processEdgePort(port.number(), subnet, portUp);
+            groupHandler.processEdgePort(port.number(), vlanId, portUp);
         } else {
             log.warn("Group handler not found for dev:{}. Not handling edge port"
                     + " {} event for port:{}", device.id(),
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 0e8f220..2b1a8c2 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -15,15 +15,16 @@
  */
 package org.onosproject.segmentrouting.grouphandler;
 
+
 import com.google.common.collect.Iterables;
 import org.apache.commons.lang3.RandomUtils;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
@@ -42,7 +43,7 @@
 import org.onosproject.segmentrouting.config.DeviceProperties;
 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey;
+import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.slf4j.Logger;
 
@@ -57,6 +58,7 @@
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -88,8 +90,8 @@
     protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
             nsNextObjStore = null;
     // distributed store for (device+subnet-ip-prefix) mapped to next-id
-    protected EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
-            subnetNextObjStore = null;
+    protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
+            vlanNextObjStore = null;
     // distributed store for (device+port+treatment) mapped to next-id
     protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
             portNextObjStore = null;
@@ -124,7 +126,7 @@
         }
         this.flowObjectiveService = flowObjService;
         this.nsNextObjStore = srManager.nsNextObjStore;
-        this.subnetNextObjStore = srManager.subnetNextObjStore;
+        this.vlanNextObjStore = srManager.vlanNextObjStore;
         this.portNextObjStore = srManager.portNextObjStore;
         this.srManager = srManager;
 
@@ -247,8 +249,7 @@
                 // if needed by the switch pipeline. Since hashed next-hops are always to
                 // other neighboring routers, there is no subnet assigned on those ports.
                 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-                metabuilder.matchVlanId(
-                    VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
+                metabuilder.matchVlanId(INTERNAL_VLAN);
 
                 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
                         .withId(nextId)
@@ -363,20 +364,20 @@
      * Should only be called by the master instance for this device/port.
      *
      * @param port the port on this device that needs to be added/removed to a bcast group
-     * @param subnet the subnet corresponding  to the broadcast group
+     * @param vlanId the vlan id corresponding to the broadcast group
      * @param portUp true if port is enabled, false if disabled
      */
-    public void processEdgePort(PortNumber port, Ip4Prefix subnet, boolean portUp) {
+    public void processEdgePort(PortNumber port, VlanId vlanId, boolean portUp) {
         //get the next id for the subnet and edit it.
-        Integer nextId = getSubnetNextObjectiveId(subnet);
+        Integer nextId = getVlanNextObjectiveId(vlanId);
         if (nextId == -1) {
             if (portUp) {
                 log.debug("**Creating flooding group for first port enabled in"
-                        + " subnet {} on dev {} port {}", subnet, deviceId, port);
-                createBcastGroupFromSubnet(subnet, Collections.singletonList(port));
+                        + " subnet {} on dev {} port {}", vlanId, deviceId, port);
+                createBcastGroupFromVlan(vlanId, Collections.singleton(port));
             } else {
                 log.warn("Could not find flooding group for subnet {} on dev:{} when"
-                        + " removing port:{}", subnet, deviceId, port);
+                        + " removing port:{}", vlanId, deviceId, port);
             }
             return;
         }
@@ -390,8 +391,9 @@
         tBuilder.popVlan();
         tBuilder.setOutput(port);
 
-        VlanId assignedVlanId =
-                srManager.getSubnetAssignedVlanId(this.deviceId, subnet);
+        VlanId untaggedVlan = srManager.getUntaggedVlanId(new ConnectPoint(deviceId, port));
+        VlanId assignedVlanId = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
+
         TrafficSelector metadata =
                 DefaultTrafficSelector.builder().matchVlanId(assignedVlanId).build();
 
@@ -460,18 +462,18 @@
     }
 
     /**
-     * Returns the next objective of type broadcast associated with the subnet,
+     * Returns the next objective of type broadcast associated with the vlan,
      * or -1 if no such objective exists. Note that this method does NOT create
      * the next objective as a side-effect. It is expected that is objective is
      * created at startup from network configuration. Typically this is used
      * for L2 flooding within the subnet configured on the switch.
      *
-     * @param prefix subnet information
+     * @param vlanId vlan id
      * @return int if found or -1
      */
-    public int getSubnetNextObjectiveId(IpPrefix prefix) {
-        Integer nextId = subnetNextObjStore.
-                get(new SubnetNextObjectiveStoreKey(deviceId, prefix));
+    public int getVlanNextObjectiveId(VlanId vlanId) {
+        Integer nextId = vlanNextObjStore.
+                get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
 
         return (nextId != null) ? nextId : -1;
     }
@@ -733,43 +735,43 @@
      * Creates broadcast groups for all ports in the same subnet for
      * all configured subnets.
      */
-    public void createGroupsFromSubnetConfig() {
-        Map<IpPrefix, List<PortNumber>> subnetPortMap;
-        try {
-            subnetPortMap = this.deviceConfig.getSubnetPortsMap(this.deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage()
-                     + " Not creating broadcast groups for device: " + deviceId);
-            return;
-        }
-        // Construct a broadcast group for each subnet
-        subnetPortMap.forEach((subnet, ports) -> {
-            if (subnet.isIp4()) {
-                createBcastGroupFromSubnet(subnet.getIp4Prefix(), ports);
-            }
+    public void createGroupsFromVlanConfig() {
+        Set<Interface> interfaces = srManager.interfaceService.getInterfaces();
+        Set<VlanId> vlans =
+        interfaces.stream()
+                .filter(intf -> intf.connectPoint().deviceId().equals(deviceId) &&
+                        !intf.vlanUntagged().equals(VlanId.NONE))
+                .map(Interface::vlanUntagged)
+                .collect(Collectors.toSet());
+
+        vlans.forEach(vlanId -> {
+            Set<PortNumber> ports = interfaces.stream()
+                    .filter(intf -> intf.connectPoint().deviceId().equals(deviceId) &&
+                            intf.vlanUntagged().equals(vlanId))
+                    .map(Interface::connectPoint)
+                    .map(ConnectPoint::port)
+                    .collect(Collectors.toSet());
+            createBcastGroupFromVlan(vlanId, ports);
         });
     }
 
     /**
-     * Creates a single broadcast group from a given subnet and list of ports.
+     * Creates a single broadcast group from a given vlan id and list of ports.
      *
-     * @param subnet a configured subnet
+     * @param vlanId vlan id
      * @param ports list of ports in the subnet
      */
-    public void createBcastGroupFromSubnet(Ip4Prefix subnet, List<PortNumber> ports) {
-        SubnetNextObjectiveStoreKey key =
-                new SubnetNextObjectiveStoreKey(deviceId, subnet);
+    public void createBcastGroupFromVlan(VlanId vlanId, Set<PortNumber> ports) {
+        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
 
-        if (subnetNextObjStore.containsKey(key)) {
+        if (vlanNextObjStore.containsKey(key)) {
             log.debug("Broadcast group for device {} and subnet {} exists",
-                      deviceId, subnet);
+                      deviceId, vlanId);
             return;
         }
 
-        VlanId assignedVlanId =
-                srManager.getSubnetAssignedVlanId(this.deviceId, subnet);
         TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(assignedVlanId).build();
+                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
 
         int nextId = flowObjectiveService.allocateNextId();
 
@@ -787,10 +789,10 @@
 
         NextObjective nextObj = nextObjBuilder.add();
         flowObjectiveService.next(deviceId, nextObj);
-        log.debug("createBcastGroupFromSubnet: Submited next objective {} in device {}",
+        log.debug("createBcastGroupFromVlan: Submited next objective {} in device {}",
                   nextId, deviceId);
 
-        subnetNextObjStore.put(key, nextId);
+        vlanNextObjStore.put(key, nextId);
     }
 
     /**
@@ -870,8 +872,8 @@
                 portNextObjStore.entrySet()) {
             removeGroup(entry.getValue());
         }
-        for (Map.Entry<SubnetNextObjectiveStoreKey, Integer> entry:
-                subnetNextObjStore.entrySet()) {
+        for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
+                vlanNextObjStore.entrySet()) {
             removeGroup(entry.getValue());
         }
         // should probably clean local stores port-neighbor
diff --git a/src/main/java/org/onosproject/segmentrouting/storekey/SubnetNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/storekey/SubnetNextObjectiveStoreKey.java
deleted file mode 100644
index c6e9246..0000000
--- a/src/main/java/org/onosproject/segmentrouting/storekey/SubnetNextObjectiveStoreKey.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.segmentrouting.storekey;
-
-import org.onlab.packet.IpPrefix;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-/**
- * Key of Subnet to NextObjective store.
- */
-public class SubnetNextObjectiveStoreKey {
-    private final DeviceId deviceId;
-    private final IpPrefix prefix;
-
-    /**
-     * Constructs the key of subnet next objective store.
-     *
-     * @param deviceId device ID
-     * @param prefix subnet information
-     */
-    public SubnetNextObjectiveStoreKey(DeviceId deviceId,
-                                       IpPrefix prefix) {
-        this.deviceId = deviceId;
-        this.prefix = prefix;
-    }
-
-    /**
-     * Gets device id in this SubnetNextObjectiveStoreKey.
-     *
-     * @return device id
-     */
-    public DeviceId deviceId() {
-        return this.deviceId;
-    }
-
-    /**
-     * Gets subnet information in this SubnetNextObjectiveStoreKey.
-     *
-     * @return subnet information
-     */
-    public IpPrefix prefix() {
-        return this.prefix;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof SubnetNextObjectiveStoreKey)) {
-            return false;
-        }
-        SubnetNextObjectiveStoreKey that =
-                (SubnetNextObjectiveStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.prefix, that.prefix));
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, prefix);
-    }
-
-    @Override
-    public String toString() {
-        return "Device: " + deviceId + " Subnet: " + prefix;
-    }
-}
diff --git a/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
new file mode 100644
index 0000000..3edb9c8
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.storekey;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * Key of VLAN to NextObjective store.
+ */
+public class VlanNextObjectiveStoreKey {
+    private final DeviceId deviceId;
+    private final VlanId vlanId;
+
+    /**
+     * Constructs the key of VLAN next objective store.
+     *
+     * @param deviceId device ID
+     * @param vlanId VLAN information
+     */
+    public VlanNextObjectiveStoreKey(DeviceId deviceId,
+                                     VlanId vlanId) {
+        this.deviceId = deviceId;
+        this.vlanId = vlanId;
+    }
+
+    /**
+     * Gets device id in this VlanNextObjectiveStoreKey.
+     *
+     * @return device id
+     */
+    public DeviceId deviceId() {
+        return this.deviceId;
+    }
+
+    /**
+     * Gets vlan information in this VlanNextObjectiveStoreKey.
+     *
+     * @return vlan id
+     */
+    public VlanId vlanId() {
+        return this.vlanId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof VlanNextObjectiveStoreKey)) {
+            return false;
+        }
+        VlanNextObjectiveStoreKey that =
+                (VlanNextObjectiveStoreKey) o;
+        return (Objects.equals(this.deviceId, that.deviceId) &&
+                Objects.equals(this.vlanId, that.vlanId));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(deviceId, vlanId);
+    }
+
+    @Override
+    public String toString() {
+        return "deviceId: " + deviceId + " vlanId: " + vlanId;
+    }
+}