[Falcon] CORD-366 Implemented CORD service dependency API and pipeline

Done
- Implement service dependency APIs
- Populate or remove basic tenant connectivity rules when VM created or removed
- Populate direct/indirect service access rules when service dependency created
- Remove service dependency rules

Todo
- Add/remove bucket to proper group when a VM is created or terminated
- Populate service dependency rules for existing VMs when service is activated
- Cleanup flow rules remove

Change-Id: I1daaf7ac9b41d7f2694605cb9b75f12d42144dbd
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
index 2e4a59e..0d09bb3 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordService.java
@@ -18,8 +18,18 @@
 import com.google.common.base.MoreObjects;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Host;
+import org.onosproject.openstackswitching.OpenstackNetwork;
+import org.onosproject.openstackswitching.OpenstackSubnet;
 
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.cordvtn.CordService.ServiceType.*;
+import static org.onosproject.cordvtn.CordService.ServiceType.PRIVATE;
+import static org.onosproject.cordvtn.CordService.ServiceType.PUBLIC_INDIRECT;
 
 public final class CordService {
 
@@ -36,23 +46,25 @@
     private final ServiceType serviceType;
     private final IpPrefix serviceIpRange;
     private final IpAddress serviceIp;
+    private final Map<Host, IpAddress> hosts;
+    private final Set<CordServiceId> tenantServices;
 
     /**
      * Default constructor.
      *
-     * @param id service id, which is identical to OpenStack network id
-     * @param segmentationId segmentation id, which is identical to VNI
-     * @param serviceType service type
-     * @param serviceIpRange service ip range
-     * @param serviceIp service ip
+     * @param vNet OpenStack network
+     * @param hosts host and tunnel ip map
+     * @param tenantServices list of tenant service ids
      */
-    public CordService(CordServiceId id, long segmentationId, ServiceType serviceType,
-                   IpPrefix serviceIpRange, IpAddress serviceIp) {
-        this.id = id;
-        this.segmentationId = segmentationId;
-        this.serviceType = serviceType;
-        this.serviceIpRange = serviceIpRange;
-        this.serviceIp = serviceIp;
+    public CordService(OpenstackNetwork vNet, OpenstackSubnet subnet,
+                       Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices) {
+        this.id = CordServiceId.of(vNet.id());
+        this.segmentationId = Long.parseLong(vNet.segmentId());
+        this.serviceType = getServiceType(vNet.name());
+        this.serviceIpRange = IpPrefix.valueOf(subnet.cidr());
+        this.serviceIp = IpAddress.valueOf(subnet.gatewayIp());
+        this.hosts = hosts;
+        this.tenantServices = tenantServices;
     }
 
     /**
@@ -100,6 +112,24 @@
         return serviceIp;
     }
 
+    /**
+     * Returns hosts associated with this service.
+     *
+     * @return list of hosts
+     */
+    public Map<Host, IpAddress> hosts() {
+        return hosts;
+    }
+
+    /**
+     * Returns tenant service IDs.
+     *
+     * @return list of tenant service id
+     */
+    public Set<CordServiceId> tenantServices() {
+        return tenantServices;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(id);
@@ -125,6 +155,33 @@
                 .add("serviceType", serviceType)
                 .add("serviceIpRange", serviceIpRange)
                 .add("serviceIp", serviceIp)
+                .add("tenantServices", tenantServices)
                 .toString();
     }
+
+    /**
+     * Returns network type from network name.
+     * It assumes that network name contains network type.
+     *
+     * @param netName network name
+     * @return network type, or null if it doesn't match any type
+     */
+    private ServiceType getServiceType(String netName) {
+        checkNotNull(netName);
+
+        String name = netName.toUpperCase();
+        if (name.contains(PRIVATE_DIRECT.toString())) {
+            return PRIVATE_DIRECT;
+        } else if (name.contains(PRIVATE_INDIRECT.toString())) {
+            return PRIVATE_INDIRECT;
+        } else if (name.contains(PUBLIC_DIRECT.toString())) {
+            return PUBLIC_DIRECT;
+        } else if (name.contains(PUBLIC_INDIRECT.toString())) {
+            return PUBLIC_INDIRECT;
+        } else if (name.contains(PRIVATE.toString())) {
+            return PRIVATE;
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
index d1ff7f0..d119a5a 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.cordvtn;
 
-import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
@@ -24,21 +23,23 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
 import org.onlab.util.ItemNotFoundException;
 import org.onlab.packet.IpAddress;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.BridgeConfig;
 import org.onosproject.net.behaviour.BridgeName;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.behaviour.ControllerInfo;
 import org.onosproject.net.behaviour.DefaultTunnelDescription;
 import org.onosproject.net.behaviour.TunnelConfig;
@@ -50,12 +51,14 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.group.GroupService;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.onosproject.openstackswitching.OpenstackNetwork;
 import org.onosproject.openstackswitching.OpenstackPort;
+import org.onosproject.openstackswitching.OpenstackSubnet;
 import org.onosproject.openstackswitching.OpenstackSwitchingService;
 import org.onosproject.ovsdb.controller.OvsdbClientService;
 import org.onosproject.ovsdb.controller.OvsdbController;
@@ -71,6 +74,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -99,6 +103,7 @@
             .register(NodeState.class);
     private static final String DEFAULT_BRIDGE = "br-int";
     private static final String VPORT_PREFIX = "tap";
+    private static final String GWPORT_PREFIX = "qr-";
     private static final String DEFAULT_TUNNEL = "vxlan";
     private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
         {
@@ -128,7 +133,7 @@
     protected DeviceAdminService adminService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
+    protected FlowRuleService flowRuleService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OvsdbController controller;
@@ -137,6 +142,12 @@
     protected ClusterService clusterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackSwitchingService openstackService;
 
     private final ExecutorService eventExecutor = Executors
@@ -149,8 +160,9 @@
     private final BridgeHandler bridgeHandler = new BridgeHandler();
     private final VmHandler vmHandler = new VmHandler();
 
+    private ApplicationId appId;
     private ConsistentMap<CordVtnNode, NodeState> nodeStore;
-    private Map<HostId, String> hostNetworkMap = Maps.newHashMap();
+    private Map<HostId, OpenstackNetwork> hostNetMap = Maps.newHashMap();
     private CordVtnRuleInstaller ruleInstaller;
 
     private enum NodeState {
@@ -198,15 +210,20 @@
 
     @Activate
     protected void activate() {
-        ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
+        appId = coreService.registerApplication("org.onosproject.cordvtn");
         nodeStore = storageService.<CordVtnNode, NodeState>consistentMapBuilder()
                 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
                 .withName("cordvtn-nodestore")
                 .withApplicationId(appId)
                 .build();
 
-        ruleInstaller = new CordVtnRuleInstaller(appId, flowObjectiveService,
-                                                 driverService, DEFAULT_TUNNEL);
+        ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
+                                                 deviceService,
+                                                 driverService,
+                                                 groupService,
+                                                 mastershipService,
+                                                 DEFAULT_TUNNEL);
+
         deviceService.addListener(deviceListener);
         hostService.addListener(hostListener);
 
@@ -277,19 +294,29 @@
     }
 
     @Override
-    public void createServiceDependency(CordServiceId tenantCordServiceId,
-                                        CordServiceId providerCordServiceId) {
-        CordService tenantService = getCordService(tenantCordServiceId);
-        CordService providerService = getCordService(providerCordServiceId);
+    public void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
+        CordService tService = getCordService(tServiceId);
+        CordService pService = getCordService(pServiceId);
 
-        // TODO populate flow rules to create service dependency
+        if (tService == null || pService == null) {
+            log.error("Failed to create CordService for {}", tServiceId.id());
+            return;
+        }
+
+        ruleInstaller.populateServiceDependencyRules(tService, pService);
     }
 
     @Override
-    public void removeServiceDependency(CordServiceId tenantCordServiceId) {
-        CordService tenantService = getCordService(tenantCordServiceId);
+    public void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
+        CordService tService = getCordService(tServiceId);
+        CordService pService = getCordService(pServiceId);
 
-        //TODO uninstall flow rules to remove service dependency
+        if (tService == null || pService == null) {
+            log.error("Failed to create CordService for {}", tServiceId.id());
+            return;
+        }
+
+        ruleInstaller.removeServiceDependencyRules(tService, pService);
     }
 
     /**
@@ -352,21 +379,13 @@
      * @param node cordvtn node
      */
     private void postInit(CordVtnNode node) {
+        log.info("Initializing {}", node.hostname());
         disconnect(node);
 
-        Set<OpenstackNetwork> vNets = Sets.newHashSet();
+        ruleInstaller.init(node.intBrId(), getTunnelPort(node.intBrId()));
         hostService.getConnectedHosts(node.intBrId())
                 .stream()
-                .forEach(host -> {
-                    OpenstackNetwork vNet = getOpenstackNetworkByHost(host);
-                    if (vNet != null) {
-                        log.info("VM {} is detected", host.id());
-
-                        hostNetworkMap.put(host.id(), vNet.id());
-                        vNets.add(vNet);
-                    }
-                });
-        vNets.stream().forEach(this::installFlowRules);
+                .forEach(vmHandler::connected);
     }
 
     /**
@@ -558,14 +577,14 @@
      * Returns tunnel port of the device.
      *
      * @param bridgeId device id
-     * @return port, null if no tunnel port exists on a given device
+     * @return port number, null if no tunnel port exists on a given device
      */
-    private Port getTunnelPort(DeviceId bridgeId) {
+    private PortNumber getTunnelPort(DeviceId bridgeId) {
         try {
             return deviceService.getPorts(bridgeId).stream()
                     .filter(p -> p.annotations().value("portName").contains(DEFAULT_TUNNEL)
                             && p.isEnabled())
-                    .findFirst().get();
+                    .findFirst().get().number();
         } catch (NoSuchElementException e) {
             return null;
         }
@@ -577,67 +596,17 @@
      * @param bridgeId device id
      * @return ip address, null if no such device exists
      */
-    private IpAddress getRemoteIp(DeviceId bridgeId) {
+    private Ip4Address getRemoteIp(DeviceId bridgeId) {
         CordVtnNode node = getNodeByBridgeId(bridgeId);
         if (node != null) {
             // TODO get data plane IP for tunneling
-            return node.ovsdbIp();
+            return node.ovsdbIp().getIp4Address();
         } else {
             return null;
         }
     }
 
     /**
-     * Returns destination information of all ports associated with a given
-     * OpenStack network. Output of the destination information is set to local
-     * port or tunnel port according to a given device id.
-     *
-     * @param deviceId device id to install flow rules
-     * @param vNet OpenStack network
-     * @return list of flow information, empty list if no flow information exists
-     */
-    private List<DestinationInfo> getSameNetworkPortsInfo(DeviceId deviceId, OpenstackNetwork vNet) {
-        List<DestinationInfo> dstInfos = Lists.newArrayList();
-        long tunnelId = Long.valueOf(vNet.segmentId());
-
-        for (OpenstackPort vPort : openstackService.ports(vNet.id())) {
-            ConnectPoint cp = getConnectPoint(vPort);
-            if (cp == null) {
-                log.debug("Couldn't find connection point for OpenStack port {}", vPort.id());
-                continue;
-            }
-
-            DestinationInfo.Builder dBuilder = cp.deviceId().equals(deviceId) ?
-                    DestinationInfo.builder(deviceService.getPort(cp.deviceId(), cp.port())) :
-                    DestinationInfo.builder(getTunnelPort(deviceId))
-                            .setRemoteIp(getRemoteIp(cp.deviceId()));
-
-            dBuilder.setMac(vPort.macAddress())
-                    .setTunnelId(tunnelId);
-            dstInfos.add(dBuilder.build());
-        }
-        return dstInfos;
-    }
-
-    /**
-     * Returns local ports associated with a given OpenStack network.
-     *
-     * @param bridgeId device id
-     * @param vNet OpenStack network
-     * @return port list, empty list if no port exists
-     */
-    private List<Port> getLocalSameNetworkPorts(DeviceId bridgeId, OpenstackNetwork vNet) {
-        List<Port> ports = new ArrayList<>();
-        openstackService.ports(vNet.id()).stream().forEach(port -> {
-            ConnectPoint cp = getConnectPoint(port);
-            if (cp != null && cp.deviceId().equals(bridgeId)) {
-                ports.add(deviceService.getPort(cp.deviceId(), cp.port()));
-            }
-        });
-        return ports;
-    }
-
-    /**
      * Returns OpenStack port associated with a given host.
      *
      * @param host host
@@ -646,6 +615,10 @@
     private OpenstackPort getOpenstackPortByHost(Host host) {
         Port port = deviceService.getPort(host.location().deviceId(),
                                           host.location().port());
+        if (port == null) {
+            log.debug("Failed to get port for {}", host.id());
+            return null;
+        }
         return openstackService.port(port);
     }
 
@@ -665,6 +638,44 @@
     }
 
     /**
+     * Returns hosts associated with a given OpenStack network.
+     *
+     * @param vNet openstack network
+     * @return set of hosts
+     */
+    private Set<Host> getHostsWithOpenstackNetwork(OpenstackNetwork vNet) {
+        checkNotNull(vNet);
+
+        return openstackService.ports(vNet.id()).stream()
+                .filter(port -> port.deviceOwner().contains("compute"))
+                .map(port -> hostService.getHostsByMac(port.macAddress())
+                        .stream()
+                        .findFirst()
+                        .orElse(null))
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns host IP assigned by OpenStack.
+     *
+     * @param host host
+     * @return IPv4 prefix, or null if it fails to get IP from OpenStack
+     */
+    private IpAddress getHostIpFromOpenstack(Host host) {
+        OpenstackPort vPort = getOpenstackPortByHost(host);
+
+        if (vPort == null || vPort.fixedIps().isEmpty()) {
+            log.error("Failed to get VM IP for {}", host.id());
+            return null;
+        }
+        // Assumes there's only one fixed IP is assigned to a port
+        return (Ip4Address) vPort.fixedIps().values()
+                .stream()
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
      * Returns port name with OpenStack port information.
      *
      * @param vPort OpenStack port
@@ -676,27 +687,20 @@
     }
 
     /**
-     * Returns connect point of a given OpenStack port.
-     * It assumes there's only one physical port associated with an OpenStack port.
+     * Returns if the host is gateway interface.
+     * This codes should be removed after adding proxy arp for the gateway.
      *
-     * @param vPort openstack port
-     * @return connect point, null if no such port exists
+     * @param host host
+     * @return true if the host is gateway
      */
-    private ConnectPoint getConnectPoint(OpenstackPort vPort) {
-        try {
-            Host host = hostService.getHostsByMac(vPort.macAddress())
-                    .stream()
-                    .findFirst()
-                    .get();
-            return new ConnectPoint(host.location().deviceId(), host.location().port());
-        } catch (NoSuchElementException e) {
-            log.debug("Not a valid host with {}", vPort.macAddress());
-            return null;
-        }
+    private boolean isGateway(Host host) {
+        Port port = deviceService.getPort(host.location().deviceId(),
+                                          host.location().port());
+        return port.annotations().value("portName").contains(GWPORT_PREFIX);
     }
 
     /**
-     * Returns OpenStack network associated with a given CORD service.
+     * Returns CordService by service ID.
      *
      * @param serviceId service id
      * @return cord service, or null if it fails to get network from OpenStack
@@ -708,73 +712,52 @@
             return null;
         }
 
-        // TODO create CordService with network/subnet information from Neutron
-        return null;
+        OpenstackSubnet subnet = vNet.subnets().stream()
+                .findFirst()
+                .orElse(null);
+        if (subnet == null) {
+            log.warn("Couldn't find OpenStack subnet for service {}", serviceId.id());
+            return null;
+        }
+
+        Set<CordServiceId> tServices = Sets.newHashSet();
+        // TODO get tenant services from XOS
+
+        Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
+                .stream()
+                .collect(Collectors.toMap(host -> host,
+                                          host -> getRemoteIp(host.location().deviceId())));
+
+        return new CordService(vNet, subnet, hosts, tServices);
     }
 
     /**
-     * Installs flow rules for a given OpenStack network.
+     * Returns CordService by OpenStack network.
      *
      * @param vNet OpenStack network
+     * @return cord service
      */
-    private void installFlowRules(OpenstackNetwork vNet) {
-        checkNotNull(vNet, "Tenant network should not be null");
+    private CordService getCordService(OpenstackNetwork vNet) {
+        checkNotNull(vNet);
 
-        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
-            List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
-
-            for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
-                List<DestinationInfo> localInInfos = dstInfos.stream()
-                        .filter(info -> !info.output().equals(inPort))
-                        .collect(Collectors.toList());
-                ruleInstaller.installFlowRulesLocalIn(device.id(), inPort, localInInfos);
-            }
-
-            Port tunPort = getTunnelPort(device.id());
-            List<DestinationInfo> tunnelInInfos = dstInfos.stream()
-                    .filter(info -> !info.output().equals(tunPort))
-                    .collect(Collectors.toList());
-            ruleInstaller.installFlowRulesTunnelIn(device.id(), tunPort, tunnelInInfos);
+        CordServiceId serviceId = CordServiceId.of(vNet.id());
+        OpenstackSubnet subnet = vNet.subnets().stream()
+                .findFirst()
+                .orElse(null);
+        if (subnet == null) {
+            log.warn("Couldn't find OpenStack subnet for service {}", serviceId);
+            return null;
         }
-    }
 
-    /**
-     * Uninstalls flow rules associated with a given host for a given OpenStack network.
-     *
-     * @param vNet OpenStack network
-     * @param host removed host
-     */
-    private void uninstallFlowRules(OpenstackNetwork vNet, Host host) {
-        checkNotNull(vNet, "Tenant network should not be null");
+        Set<CordServiceId> tServices = Sets.newHashSet();
+        // TODO get tenant services from XOS
 
-        Port removedPort = deviceService.getPort(host.location().deviceId(),
-                                                 host.location().port());
+        Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(vNet)
+                .stream()
+                .collect(Collectors.toMap(host -> host,
+                                          host -> getRemoteIp(host.location().deviceId())));
 
-        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
-            List<DestinationInfo> dstInfos = getSameNetworkPortsInfo(device.id(), vNet);
-
-            for (Port inPort : getLocalSameNetworkPorts(device.id(), vNet)) {
-                List<DestinationInfo> localInInfos = Lists.newArrayList(
-                        DestinationInfo.builder(getTunnelPort(device.id()))
-                                .setTunnelId(Long.valueOf(vNet.segmentId()))
-                                .setMac(host.mac())
-                                .setRemoteIp(getRemoteIp(host.location().deviceId()))
-                                .build());
-                ruleInstaller.uninstallFlowRules(device.id(), inPort, localInInfos);
-            }
-
-            if (device.id().equals(host.location().deviceId())) {
-                Port tunPort = getTunnelPort(device.id());
-                List<DestinationInfo> tunnelInInfo = Lists.newArrayList(
-                        DestinationInfo.builder(removedPort)
-                                .setTunnelId(Long.valueOf(vNet.segmentId()))
-                                .setMac(host.mac())
-                                .build());
-
-                ruleInstaller.uninstallFlowRules(device.id(), tunPort, tunnelInInfo);
-                ruleInstaller.uninstallFlowRules(device.id(), removedPort, dstInfos);
-            }
-        }
+        return new CordService(vNet, subnet, hosts, tServices);
     }
 
     private class InternalDeviceListener implements DeviceListener {
@@ -873,6 +856,7 @@
          * @param port port
          */
         public void portAdded(Port port) {
+            // TODO add host by updating network config
             if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
                 return;
             }
@@ -891,6 +875,7 @@
          * @param port port
          */
         public void portRemoved(Port port) {
+            // TODO remove host by updating network config
             if (!port.annotations().value("portName").contains(DEFAULT_TUNNEL)) {
                 return;
             }
@@ -907,8 +892,13 @@
 
         @Override
         public void connected(Host host) {
+            // TODO remove check gateway here after applying network config host provider
+            if (isGateway(host)) {
+                return;
+            }
+
             CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
-            if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
+            if (node == null || !Objects.equals(getNodeState(node), NodeState.COMPLETE)) {
                 // do nothing for the host on unregistered or unprepared device
                 return;
             }
@@ -918,29 +908,43 @@
                 return;
             }
 
-            log.info("VM {} is detected", host.id());
+            // TODO host ip should be set in host information after applying network config host provider
+            IpAddress hostIp = getHostIpFromOpenstack(host);
+            if (hostIp == null) {
+                log.error("Failed to get host IP of {}", host.id());
+                return;
+            }
 
-            hostNetworkMap.put(host.id(), vNet.id());
-            installFlowRules(vNet);
+            log.info("VM {} is detected", host.id());
+            hostNetMap.put(host.id(), vNet);
+
+            ruleInstaller.populateBasicConnectionRules(
+                    host,
+                    hostIp,
+                    checkNotNull(getRemoteIp(host.location().deviceId())).getIp4Address(),
+                    vNet);
+
+            // TODO add new VM to related service group if exists
         }
 
         @Override
         public void disconnected(Host host) {
             CordVtnNode node = getNodeByBridgeId(host.location().deviceId());
-            if (node == null || !getNodeState(node).equals(NodeState.COMPLETE)) {
+            if (node == null || !Objects.equals(getNodeState(node), NodeState.COMPLETE)) {
                 // do nothing for the host on unregistered or unprepared device
                 return;
             }
 
-            OpenstackNetwork vNet = openstackService.network(hostNetworkMap.get(host.id()));
+            OpenstackNetwork vNet = hostNetMap.get(host.id());
             if (vNet == null) {
                 return;
             }
 
             log.info("VM {} is vanished", host.id());
+            ruleInstaller.removeBasicConnectionRules(host);
 
-            uninstallFlowRules(vNet, host);
-            hostNetworkMap.remove(host.id());
+            // TODO remove the VM from related service group if exists
+            hostNetMap.remove(host.id());
         }
     }
 }
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 9e22997..4e044e1 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -15,195 +15,859 @@
  */
 package org.onosproject.cordvtn;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+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.util.ItemNotFoundException;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
+import org.onosproject.net.Host;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DefaultDriverData;
 import org.onosproject.net.driver.DefaultDriverHandler;
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.instructions.ExtensionPropertyException;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.openstackswitching.OpenstackNetwork;
+import org.onosproject.openstackswitching.OpenstackSubnet;
 import org.slf4j.Logger;
 
 import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Populates rules for virtual tenant network.
+ * Populates rules for CORD VTN service.
  */
-public final class CordVtnRuleInstaller {
+public class CordVtnRuleInstaller {
+
     protected final Logger log = getLogger(getClass());
 
+    private static final int TABLE_IN_PORT = 0;
+    private static final int TABLE_ACCESS_TYPE = 1;
+    private static final int TABLE_IN_SERVICE = 2;
+    private static final int TABLE_DST_IP = 3;
+    private static final int TABLE_TUNNEL_IN = 4;
+
     private static final int DEFAULT_PRIORITY = 5000;
+    private static final int LOWER_PRIORITY = 4000;
+    private static final int LOWEST_PRIORITY = 0;
 
     private final ApplicationId appId;
-    private final FlowObjectiveService flowObjectiveService;
+    private final FlowRuleService flowRuleService;
+    private final DeviceService deviceService;
     private final DriverService driverService;
+    private final GroupService groupService;
+    private final MastershipService mastershipService;
     private final String tunnelType;
 
     /**
-     * Creates a new rule installer.
+     * Creates a new rule populator.
      *
      * @param appId application id
-     * @param flowObjectiveService flow objective service
+     * @param flowRuleService flow rule service
+     * @param deviceService device service
      * @param driverService driver service
      * @param tunnelType tunnel type
      */
     public CordVtnRuleInstaller(ApplicationId appId,
-                                FlowObjectiveService flowObjectiveService,
+                                FlowRuleService flowRuleService,
+                                DeviceService deviceService,
                                 DriverService driverService,
+                                GroupService groupService,
+                                MastershipService mastershipService,
                                 String tunnelType) {
         this.appId = appId;
-        this.flowObjectiveService = flowObjectiveService;
+        this.flowRuleService = flowRuleService;
+        this.deviceService = deviceService;
         this.driverService = driverService;
+        this.groupService = groupService;
+        this.mastershipService = mastershipService;
         this.tunnelType = checkNotNull(tunnelType);
     }
 
     /**
-     * Installs flow rules for tunnel in traffic.
+     * Installs table miss rule to a give device.
      *
-     * @param deviceId device id to install flow rules
-     * @param inPort in port
-     * @param dstInfos list of destination info
+     * @param deviceId device id to install the rules
+     * @param tunnelPort tunnel port number of the device
      */
-    public void installFlowRulesTunnelIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
-        dstInfos.stream().forEach(dstInfo -> {
-            ForwardingObjective.Builder fBuilder = vtnRulesSameNode(inPort, dstInfo);
-            if (fBuilder != null) {
-                flowObjectiveService.forward(deviceId, fBuilder.add());
-            }
-        });
+    public void init(DeviceId deviceId, PortNumber tunnelPort) {
+        // default is drop packets which can be accomplished without
+        // a table miss entry for all table.
+        populateTunnelInPortRule(deviceId, tunnelPort);
+        processAccessTypeTable(deviceId);
     }
 
     /**
-     * Installs flow rules for local in traffic.
+     * Populates basic rules that connect a VM to the other VMs in the system.
      *
-     * @param deviceId device id to install flow rules
-     * @param inPort in port
-     * @param dstInfos list of destination info
+     * @param host host
+     * @param hostIp host ip
+     * @param tunnelIp tunnel ip
+     * @param vNet openstack network
      */
-    public void installFlowRulesLocalIn(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
-        dstInfos.stream().forEach(dstInfo -> {
-            ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
-                    vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
+    public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
+                                             OpenstackNetwork vNet) {
+        // TODO we can get host ip from host.ip() after applying NetworkConfig host provider
+        checkNotNull(host);
+        checkNotNull(vNet);
 
-            if (fBuilder != null) {
-                flowObjectiveService.forward(deviceId, fBuilder.add());
-            }
-        });
-    }
-
-    /**
-     * Uninstalls flow rules associated with a given port from a given device.
-     *
-     * @param deviceId device id
-     * @param inPort port associated with removed host
-     * @param dstInfos list of destination info
-     */
-    public void uninstallFlowRules(DeviceId deviceId, Port inPort, List<DestinationInfo> dstInfos) {
-        dstInfos.stream().forEach(dstInfo -> {
-            ForwardingObjective.Builder fBuilder = isTunnelPort(dstInfo.output()) ?
-                    vtnRulesRemoteNode(deviceId, inPort, dstInfo) : vtnRulesSameNode(inPort, dstInfo);
-
-            if (fBuilder != null) {
-                flowObjectiveService.forward(deviceId, fBuilder.remove());
-            }
-        });
-    }
-
-    /**
-     * Returns forwarding objective builder to provision basic virtual tenant network.
-     * This method cares for the traffics whose source and destination device is the same.
-     *
-     * @param inPort in port
-     * @param dstInfo destination information
-     * @return forwarding objective builder
-     */
-    private ForwardingObjective.Builder vtnRulesSameNode(Port inPort, DestinationInfo dstInfo) {
-        checkArgument(inPort.element().id().equals(dstInfo.output().element().id()));
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchInPort(inPort.number())
-                .matchEthDst(dstInfo.mac());
-        if (isTunnelPort(inPort)) {
-            sBuilder.matchTunnelId(dstInfo.tunnelId());
+        DeviceId deviceId = host.location().deviceId();
+        if (!mastershipService.isLocalMaster(deviceId)) {
+            return;
         }
 
-        tBuilder.setOutput(dstInfo.output().number());
+        PortNumber inPort = host.location().port();
+        MacAddress dstMac = host.mac();
+        long tunnelId = Long.parseLong(vNet.segmentId());
 
-        return DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(DEFAULT_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.VERSATILE)
-                .fromApp(appId)
-                .makePermanent();
+        OpenstackSubnet subnet = vNet.subnets().stream()
+                .findFirst()
+                .orElse(null);
+
+        if (subnet == null) {
+            log.error("Failed to get subnet for {}", host.id());
+            return;
+        }
+
+        populateLocalInPortRule(deviceId, inPort, hostIp);
+        populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
+        populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
+        populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
     }
 
     /**
-     * Returns forwarding objective builder to provision basic virtual tenant network.
-     * This method cares for the traffics whose source and destination is not the same.
+     * Populates service dependency rules.
      *
-     * @param deviceId device id to install flow rules
-     * @param inPort in port
-     * @param dstInfo destination information
-     * @return forwarding objective, or null if it fails to build it
+     * @param tService tenant cord service
+     * @param pService provider cord service
      */
-    private ForwardingObjective.Builder vtnRulesRemoteNode(DeviceId deviceId, Port inPort, DestinationInfo dstInfo) {
-        checkArgument(isTunnelPort(dstInfo.output()));
+    public void populateServiceDependencyRules(CordService tService, CordService pService) {
+        checkNotNull(tService);
+        checkNotNull(pService);
 
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+        Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+        Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+        Ip4Address serviceIp = pService.serviceIp().getIp4Address();
 
-        ExtensionTreatment extTreatment =
-                getTunnelDstInstruction(deviceId, dstInfo.remoteIp().getIp4Address());
-        if (extTreatment == null) {
+        Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+        Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
+
+        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+            GroupId groupId = createServiceGroup(device.id(), pService);
+            outGroups.put(device.id(), groupId);
+
+            Set<PortNumber> vms = tService.hosts().keySet()
+                    .stream()
+                    .filter(host -> host.location().deviceId().equals(device.id()))
+                    .map(host -> host.location().port())
+                    .collect(Collectors.toSet());
+            inPorts.put(device.id(), vms);
+        }
+
+        populateIndirectAccessRule(srcRange, serviceIp, outGroups);
+        populateDirectAccessRule(srcRange, dstRange);
+        populateInServiceRule(inPorts, outGroups);
+    }
+
+    /**
+     * Removes basic rules related to a given flow information.
+     *
+     * @param host host to be removed
+     */
+    public void removeBasicConnectionRules(Host host) {
+        checkNotNull(host);
+
+        DeviceId deviceId = host.location().deviceId();
+        MacAddress mac = host.mac();
+        PortNumber port = host.location().port();
+        IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
+
+        if (!mastershipService.isLocalMaster(deviceId)) {
+            return;
+        }
+
+        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+            if (flowRule.deviceId().equals(deviceId)) {
+                PortNumber inPort = getInPort(flowRule);
+                if (inPort != null && inPort.equals(port)) {
+                    processFlowRule(false, flowRule);
+                    continue;
+                }
+            }
+
+            MacAddress dstMac = getDstMacFromTreatment(flowRule);
+            if (dstMac != null && dstMac.equals(mac)) {
+                processFlowRule(false, flowRule);
+                continue;
+            }
+
+            dstMac = getDstMacFromSelector(flowRule);
+            if (dstMac != null && dstMac.equals(mac)) {
+                processFlowRule(false, flowRule);
+                continue;
+            }
+
+            IpPrefix dstIp = getDstIpFromSelector(flowRule);
+            if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
+                processFlowRule(false, flowRule);
+            }
+
+        }
+
+        // TODO uninstall same network access rule in access table if no vm exists in the network
+    }
+
+    /**
+     * Removes service dependency rules.
+     *
+     * @param tService tenant cord service
+     * @param pService provider cord service
+     */
+    public void removeServiceDependencyRules(CordService tService, CordService pService) {
+        checkNotNull(tService);
+        checkNotNull(pService);
+
+        Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
+        Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
+        IpPrefix serviceIp = pService.serviceIp().toIpPrefix();
+
+        Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+        GroupKey groupKey = new DefaultGroupKey(pService.id().id().getBytes());
+
+        deviceService.getAvailableDevices(SWITCH).forEach(device -> {
+            Group group = groupService.getGroup(device.id(), groupKey);
+            if (group != null) {
+                outGroups.put(device.id(), group.id());
+            }
+        });
+
+        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+            IpPrefix dstIp = getDstIpFromSelector(flowRule);
+            IpPrefix srcIp = getSrcIpFromSelector(flowRule);
+
+            if (dstIp != null && dstIp.equals(serviceIp)) {
+                processFlowRule(false, flowRule);
+                continue;
+            }
+
+            if (dstIp != null && srcIp != null) {
+                if (dstIp.equals(dstRange) && srcIp.equals(srcRange)) {
+                    processFlowRule(false, flowRule);
+                    continue;
+                }
+
+                if (dstIp.equals(srcRange) && srcIp.equals(dstRange)) {
+                    processFlowRule(false, flowRule);
+                    continue;
+                }
+            }
+
+            GroupId groupId = getGroupIdFromTreatment(flowRule);
+            if (groupId != null && groupId.equals(outGroups.get(flowRule.deviceId()))) {
+                processFlowRule(false, flowRule);
+            }
+        }
+
+        // TODO remove the group if it is not in use
+    }
+
+    /**
+     * Creates a new group for a given service.
+     *
+     * @param deviceId device id to create a group
+     * @param service cord service
+     * @return group id, or null if it fails to create
+     */
+    private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
+        checkNotNull(service);
+
+        GroupKey groupKey = getGroupKey(service.id());
+        Group group = groupService.getGroup(deviceId, groupKey);
+        GroupId groupId = getGroupId(service.id(), deviceId);
+
+        if (group != null) {
+            log.debug("Group {} is already exist in {}", service.id(), deviceId);
+            return groupId;
+        }
+
+        GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
+        GroupDescription groupDescription = new DefaultGroupDescription(
+                deviceId,
+                GroupDescription.Type.SELECT,
+                buckets,
+                groupKey,
+                groupId.id(),
+                appId);
+
+        groupService.addGroup(groupDescription);
+
+        return groupId;
+    }
+
+    /**
+     * Returns group buckets for a given device.
+     *
+     * @param deviceId device id
+     * @param tunnelId tunnel id
+     * @param hosts list of host
+     * @return group buckets
+     */
+    private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
+        List<GroupBucket> buckets = Lists.newArrayList();
+
+        for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
+            Host host = entry.getKey();
+            Ip4Address remoteIp = entry.getValue().getIp4Address();
+            DeviceId hostDevice = host.location().deviceId();
+
+            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+                    .builder()
+                    .setEthDst(host.mac());
+
+            if (deviceId.equals(hostDevice)) {
+                tBuilder.setOutput(host.location().port());
+            } else {
+                ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
+                if (tunnelDst == null) {
+                    continue;
+                }
+
+                tBuilder.extension(tunnelDst, deviceId)
+                        .setTunnelId(tunnelId)
+                        .setOutput(getTunnelPort(hostDevice));
+            }
+
+            buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
+        }
+
+        return new GroupBuckets(buckets);
+    }
+
+    /**
+     * Returns globally unique group ID.
+     *
+     * @param serviceId service id
+     * @param deviceId device id
+     * @return group id
+     */
+    private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
+        return new DefaultGroupId(Objects.hash(serviceId, deviceId));
+    }
+
+    /**
+     * Returns group key of a service.
+     *
+     * @param serviceId service id
+     * @return group key
+     */
+    private GroupKey getGroupKey(CordServiceId serviceId) {
+        return new DefaultGroupKey(serviceId.id().getBytes());
+    }
+
+    /**
+     * Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
+     *
+     * @param deviceId device id
+     */
+    private void processAccessTypeTable(DeviceId deviceId) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_IN_SERVICE)
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(LOWEST_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_ACCESS_TYPE)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+    }
+
+    /**
+     * Populates rules for tunnel flows in port in IN_PORT table.
+     * All flows from tunnel port are forwarded to TUNNEL_ID table.
+     *
+     * @param deviceId device id to install the rules
+     * @param tunnelPort tunnel port
+     */
+    private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
+        checkNotNull(tunnelPort);
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(tunnelPort)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_TUNNEL_IN)
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_IN_PORT)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+    }
+
+    /**
+     * Populates rules for local in port in IN_PORT table.
+     * Flows from a given in port, whose source IP is service IP transition
+     * to DST_TYPE table. Other flows transition to IN_SERVICE table.
+     *
+     * @param deviceId device id to install the rules
+     * @param inPort in port
+     * @param srcIp source ip
+     */
+    private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort)
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(srcIp.toIpPrefix())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_ACCESS_TYPE)
+                .build();
+
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_IN_PORT)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort)
+                .build();
+        treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_IN_SERVICE)
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(LOWER_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_IN_PORT)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+    }
+
+    /**
+     * Populates direct VM access rules for ACCESS_TYPE table.
+     * These rules are installed to all devices.
+     *
+     * @param srcRange source ip range
+     * @param dstRange destination ip range
+     */
+    private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(srcRange)
+                .matchIPDst(dstRange)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_DST_IP)
+                .build();
+
+        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+            FlowRule flowRuleDirect = DefaultFlowRule.builder()
+                    .fromApp(appId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(LOWER_PRIORITY)
+                    .forDevice(device.id())
+                    .forTable(TABLE_ACCESS_TYPE)
+                    .makePermanent()
+                    .build();
+
+            processFlowRule(true, flowRuleDirect);
+        }
+    }
+
+    /**
+     * Populates indirect service access rules for ACCESS_TYPE table.
+     * These rules are installed to all devices.
+     *
+     * @param srcRange source range
+     * @param serviceIp service ip
+     * @param outGroups list of output group
+     */
+    private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
+                                            Map<DeviceId, GroupId> outGroups) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(srcRange)
+                .matchIPDst(serviceIp.toIpPrefix())
+                .build();
+
+        for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .group(outGroup.getValue())
+                    .build();
+
+            FlowRule flowRule = DefaultFlowRule.builder()
+                    .fromApp(appId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .forDevice(outGroup.getKey())
+                    .forTable(TABLE_ACCESS_TYPE)
+                    .makePermanent()
+                    .build();
+
+            processFlowRule(true, flowRule);
+        }
+    }
+
+    /**
+     * Populates flow rules for IN_SERVICE table.
+     *
+     * @param inPorts list of inports related to the service for each device
+     * @param outGroups set of output groups
+     */
+    private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
+        checkNotNull(inPorts);
+        checkNotNull(outGroups);
+
+        for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
+            Set<PortNumber> ports = entry.getValue();
+            DeviceId deviceId = entry.getKey();
+
+            GroupId groupId = outGroups.get(deviceId);
+            if (groupId == null) {
+                continue;
+            }
+
+            ports.stream().forEach(port -> {
+                TrafficSelector selector = DefaultTrafficSelector.builder()
+                        .matchInPort(port)
+                        .build();
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .group(groupId)
+                        .build();
+
+                FlowRule flowRule = DefaultFlowRule.builder()
+                        .fromApp(appId)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .withPriority(DEFAULT_PRIORITY)
+                        .forDevice(deviceId)
+                        .forTable(TABLE_IN_SERVICE)
+                        .makePermanent()
+                        .build();
+
+                processFlowRule(true, flowRule);
+            });
+        }
+    }
+
+    /**
+     * Populates flow rules for DST_IP table.
+     *
+     * @param deviceId device id
+     * @param inPort in port
+     * @param dstMac mac address
+     * @param dstIp destination ip
+     * @param tunnelId tunnel id
+     * @param tunnelIp tunnel remote ip
+     */
+    private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
+                                   IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(dstIp.toIpPrefix())
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(dstMac)
+                .setOutput(inPort)
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_DST_IP)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+
+        for (Device device : deviceService.getAvailableDevices(SWITCH)) {
+            if (device.id().equals(deviceId)) {
+                continue;
+            }
+
+            ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
+            if (tunnelDst == null) {
+                continue;
+            }
+
+            treatment = DefaultTrafficTreatment.builder()
+                    .setEthDst(dstMac)
+                    .setTunnelId(tunnelId)
+                    .extension(tunnelDst, device.id())
+                    .setOutput(getTunnelPort(device.id()))
+                    .build();
+
+            flowRule = DefaultFlowRule.builder()
+                    .fromApp(appId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .forDevice(device.id())
+                    .forTable(TABLE_DST_IP)
+                    .makePermanent()
+                    .build();
+
+            processFlowRule(true, flowRule);
+        }
+    }
+
+    /**
+     * Populates flow rules for TUNNEL_ID table.
+     *
+     * @param deviceId device id
+     * @param inPort in port
+     * @param mac mac address
+     * @param tunnelId tunnel id
+     */
+    private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchTunnelId(tunnelId)
+                .matchEthDst(mac)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(inPort)
+                .build();
+
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .forDevice(deviceId)
+                .forTable(TABLE_TUNNEL_IN)
+                .makePermanent()
+                .build();
+
+        processFlowRule(true, flowRule);
+    }
+
+    /**
+     * Installs or uninstall a given rule.
+     *
+     * @param install true to install, false to uninstall
+     * @param rule rule
+     */
+    private void processFlowRule(boolean install, FlowRule rule) {
+        FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
+        oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
+
+        flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
+            }
+        }));
+    }
+
+    /**
+     * Returns tunnel port of the device.
+     *
+     * @param deviceId device id
+     * @return tunnel port number, or null if no tunnel port exists on a given device
+     */
+    private PortNumber getTunnelPort(DeviceId deviceId) {
+        try {
+            return deviceService.getPorts(deviceId).stream()
+                    .filter(p -> p.annotations().value("portName").contains(tunnelType))
+                    .findFirst().get().number();
+        } catch (NoSuchElementException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the inport from a given flow rule if the rule contains the match of it.
+     *
+     * @param flowRule flow rule
+     * @return port number, or null if the rule doesn't have inport match
+     */
+    private PortNumber getInPort(FlowRule flowRule) {
+        Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
+        if (criterion != null && criterion instanceof PortCriterion) {
+            PortCriterion port = (PortCriterion) criterion;
+            return port.port();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the destination mac address from a given flow rule if the rule
+     * contains the instruction of it.
+     *
+     * @param flowRule flow rule
+     * @return mac address, or null if the rule doesn't have destination mac instruction
+     */
+    private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
+        Instruction instruction = flowRule.treatment().allInstructions().stream()
+                .filter(inst -> inst instanceof ModEtherInstruction &&
+                        ((ModEtherInstruction) inst).subtype().equals(ETH_DST))
+                .findFirst()
+                .orElse(null);
+
+        if (instruction == null) {
             return null;
         }
 
-        sBuilder.matchInPort(inPort.number())
-                .matchEthDst(dstInfo.mac());
-
-        tBuilder.extension(extTreatment, deviceId)
-                .setTunnelId(dstInfo.tunnelId())
-                .setOutput(dstInfo.output().number());
-
-        return DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(DEFAULT_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.VERSATILE)
-                .fromApp(appId)
-                .makePermanent();
+        return ((ModEtherInstruction) instruction).mac();
     }
 
     /**
-     * Checks if a given port is tunnel interface or not.
-     * It assumes the tunnel interface contains tunnelType string in its name.
+     * Returns the destination mac address from a given flow rule if the rule
+     * contains the match of it.
      *
-     * @param port port
-     * @return true if the port is tunnel interface, false otherwise.
+     * @param flowRule flow rule
+     * @return mac address, or null if the rule doesn't have destination mac match
      */
-    private boolean isTunnelPort(Port port) {
-        return port.annotations().value("portName").contains(tunnelType);
+    private MacAddress getDstMacFromSelector(FlowRule flowRule) {
+        Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
+        if (criterion != null && criterion instanceof EthCriterion) {
+            EthCriterion eth = (EthCriterion) criterion;
+            return eth.mac();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the destination IP from a given flow rule if the rule contains
+     * the match of it.
+     *
+     * @param flowRule flow rule
+     * @return ip prefix, or null if the rule doesn't have ip match
+     */
+    private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
+        Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
+        if (criterion != null && criterion instanceof IPCriterion) {
+            IPCriterion ip = (IPCriterion) criterion;
+            return ip.ip();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the source IP from a given flow rule if the rule contains
+     * the match of it.
+     *
+     * @param flowRule flow rule
+     * @return ip prefix, or null if the rule doesn't have ip match
+     */
+    private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
+        Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
+        if (criterion != null && criterion instanceof IPCriterion) {
+            IPCriterion ip = (IPCriterion) criterion;
+            return ip.ip();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the group ID from a given flow rule if the rule contains the
+     * treatment of it.
+     *
+     * @param flowRule flow rule
+     * @return group id, or null if the rule doesn't have group instruction
+     */
+    private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
+        Instruction instruction = flowRule.treatment().allInstructions().stream()
+                .filter(inst -> inst instanceof Instructions.GroupInstruction)
+                .findFirst()
+                .orElse(null);
+
+        if (instruction == null) {
+            return null;
+        }
+
+        return ((Instructions.GroupInstruction) instruction).groupId();
     }
 
     /**
@@ -213,19 +877,23 @@
      * @param remoteIp tunnel destination address
      * @return extension treatment or null if it fails to get instruction
      */
-    private ExtensionTreatment getTunnelDstInstruction(DeviceId deviceId, Ip4Address remoteIp) {
+    private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
         try {
             Driver driver = driverService.getDriver(deviceId);
-            DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
-            ExtensionTreatmentResolver resolver =  handler.behaviour(ExtensionTreatmentResolver.class);
+            DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
+            DriverHandler handler = new DefaultDriverHandler(driverData);
+            ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
 
-            ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+            ExtensionTreatment treatment =
+                    resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
             treatment.setPropertyValue("tunnelDst", remoteIp);
 
             return treatment;
-        } catch (ItemNotFoundException | UnsupportedOperationException | ExtensionPropertyException e) {
-            log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
+        } catch (ItemNotFoundException | UnsupportedOperationException |
+                ExtensionPropertyException e) {
+            log.error("Failed to get extension instruction {}", deviceId);
             return null;
         }
     }
 }
+
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
index 29b4525..2c3c23b 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java
@@ -69,15 +69,16 @@
     /**
      * Creates dependencies for a given tenant service.
      *
-     * @param tenantCordServiceId id of the service which has a dependency
-     * @param providerCordServiceId id of the service which provide dependency
+     * @param tServiceId id of the service which has a dependency
+     * @param pServiceId id of the service which provide dependency
      */
-    void createServiceDependency(CordServiceId tenantCordServiceId, CordServiceId providerCordServiceId);
+    void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId);
 
     /**
      * Removes all dependencies from a given tenant service.
      *
-     * @param tenantCordServiceId id of the service which has a dependency
+     * @param tServiceId id of the service which has a dependency
+     * @param pServiceId id of the service which provide dependency
      */
-    void removeServiceDependency(CordServiceId tenantCordServiceId);
+    void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId);
 }
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DestinationInfo.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DestinationInfo.java
deleted file mode 100644
index 290cc17..0000000
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DestinationInfo.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2014-2015 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.cordvtn;
-
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.Port;
-
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Contains destination information.
- */
-public final class DestinationInfo {
-
-    private final Port output;
-    private final List<IpAddress> ip;
-    private final MacAddress mac;
-    private final IpAddress remoteIp;
-    private final long tunnelId;
-
-    /**
-     * Creates a new destination information.
-     *
-     * @param output output port
-     * @param ip destination ip address
-     * @param mac destination mac address
-     * @param remoteIp tunnel remote ip address
-     * @param tunnelId segment id
-     */
-    public DestinationInfo(Port output, List<IpAddress> ip, MacAddress mac,
-                           IpAddress remoteIp, long tunnelId) {
-        this.output = checkNotNull(output);
-        this.ip = ip;
-        this.mac = mac;
-        this.remoteIp = remoteIp;
-        this.tunnelId = tunnelId;
-    }
-
-    /**
-     * Returns output port.
-     *
-     * @return port
-     */
-    public Port output() {
-        return output;
-    }
-
-    /**
-     * Returns destination ip addresses.
-     *
-     * @return list of ip address
-     */
-    public List<IpAddress> ip() {
-        return ip;
-    }
-
-    /**
-     * Returns destination mac address.
-     *
-     * @return mac address
-     */
-    public MacAddress mac() {
-        return mac;
-    }
-
-    /**
-     * Returns tunnel remote ip address.
-     *
-     * @return ip address
-     */
-    public IpAddress remoteIp() {
-        return remoteIp;
-    }
-
-    /**
-     * Returns tunnel id.
-     *
-     * @return tunnel id
-     */
-    public long tunnelId() {
-        return tunnelId;
-    }
-
-    /**
-     * Returns a new destination info builder.
-     *
-     * @return destination info builder
-     */
-    public static DestinationInfo.Builder builder(Port output) {
-        return new Builder(output);
-    }
-
-    /**
-     * DestinationInfo builder class.
-     */
-    public static final class Builder {
-
-        private final Port output;
-        private List<IpAddress> ip;
-        private MacAddress mac;
-        private IpAddress remoteIp;
-        private long tunnelId;
-
-        /**
-         * Creates a new destination information builder.
-         *
-         * @param output output port
-         */
-        public Builder(Port output) {
-            this.output = checkNotNull(output, "Output port cannot be null");
-        }
-
-        /**
-         * Sets the destination ip address.
-         *
-         * @param ip ip address
-         * @return destination info builder
-         */
-        public Builder setIp(List<IpAddress> ip) {
-            this.ip = checkNotNull(ip, "IP cannot be null");
-            return this;
-        }
-
-        /**
-         * Sets the destination mac address.
-         *
-         * @param mac mac address
-         * @return destination info builder
-         */
-        public Builder setMac(MacAddress mac) {
-            this.mac = checkNotNull(mac, "MAC address cannot be null");
-            return this;
-        }
-
-        /**
-         * Sets the tunnel remote ip address.
-         *
-         * @param remoteIp ip address
-         * @return destination info builder
-         */
-        public Builder setRemoteIp(IpAddress remoteIp) {
-            this.remoteIp = checkNotNull(remoteIp, "Remote IP address cannot be null");
-            return this;
-        }
-
-        /**
-         * Sets the tunnel id.
-         *
-         * @param tunnelId tunnel id
-         * @return destination info builder
-         */
-        public Builder setTunnelId(long tunnelId) {
-            this.tunnelId = checkNotNull(tunnelId, "Tunnel ID cannot be null");
-            return this;
-        }
-
-        /**
-         * Build a destination information.
-         *
-         * @return destination info object
-         */
-        public DestinationInfo build() {
-            return new DestinationInfo(this);
-        }
-    }
-
-    private DestinationInfo(Builder builder) {
-        output = builder.output;
-        ip = builder.ip;
-        mac = builder.mac;
-        remoteIp = builder.remoteIp;
-        tunnelId = builder.tunnelId;
-    }
-}
diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
index d2c5567..c5d1ded 100644
--- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
+++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/rest/ServiceDependencyWebResource.java
@@ -58,14 +58,16 @@
     /**
      * Removes service dependencies.
      *
-     * @param serviceId service id
+     * @param tServiceId tenant service id
+     * @param pServiceId provider service id
      * @return 200 OK, or 400 Bad Request
      */
     @DELETE
-    @Path("{serviceId}")
+    @Path("{tenantServiceId}/{providerServiceId}")
     @Produces(MediaType.APPLICATION_JSON)
-    public Response removeServiceDependency(@PathParam("serviceId") String serviceId) {
-        service.removeServiceDependency(CordServiceId.of(serviceId));
+    public Response removeServiceDependency(@PathParam("tenantServiceId") String tServiceId,
+                                            @PathParam("providerServiceId") String pServiceId) {
+        service.removeServiceDependency(CordServiceId.of(tServiceId), CordServiceId.of(pServiceId));
         return Response.status(Response.Status.OK).build();
     }