[ONOS-4612]Update SFC flows inline with the Official OVS NSH patch

Change-Id: If58517841096a939860d88aa78eca7cae46b9935
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/ServiceFunctionForwarderService.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/ServiceFunctionForwarderService.java
deleted file mode 100644
index b270b13..0000000
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/ServiceFunctionForwarderService.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2015-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.sfc.forwarder;
-
-import java.util.List;
-
-import org.onosproject.net.NshServicePathId;
-import org.onosproject.vtnrsc.PortChain;
-import org.onosproject.vtnrsc.PortPairId;
-
-/**
- * Abstraction of an entity which provides service function forwarder.
- */
-public interface ServiceFunctionForwarderService {
-
-    /**
-     * Install forwarding rule.
-     *
-     * @param portChain port-chain
-     * @param nshSpi nsh spi
-     */
-    @Deprecated
-    void installForwardingRule(PortChain portChain, NshServicePathId nshSpi);
-
-    /**
-     * Uninstall forwarding rule.
-     *
-     * @param portChain port-chain
-     * @param nshSpi nsh spi
-     */
-    @Deprecated
-    void unInstallForwardingRule(PortChain portChain, NshServicePathId nshSpi);
-
-    /**
-     * Install load balanced forwarding rules.
-     *
-     * @param path load balanced path of port pairs
-     * @param nshSpi nsh service path index
-     */
-    void installLoadBalancedForwardingRule(List<PortPairId> path, NshServicePathId nshSpi);
-
-    /**
-     * Uninstall load balanced forwarding rules.
-     *
-     * @param path load balanced path of port pairs
-     * @param nshSpi nsh service path index
-     */
-    void unInstallLoadBalancedForwardingRule(List<PortPairId> path, NshServicePathId nshSpi);
-}
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImpl.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImpl.java
deleted file mode 100644
index 9875e4d..0000000
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImpl.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright 2015-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.sfc.forwarder.impl;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SPI;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.List;
-import java.util.ListIterator;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.NshServicePathId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.ExtensionSelectorResolver;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.ExtensionSelector;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.sfc.forwarder.ServiceFunctionForwarderService;
-import org.onosproject.vtnrsc.PortChain;
-import org.onosproject.vtnrsc.PortPair;
-import org.onosproject.vtnrsc.PortPairId;
-import org.onosproject.vtnrsc.VirtualPortId;
-import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
-import org.onosproject.vtnrsc.portchain.PortChainService;
-import org.onosproject.vtnrsc.portpair.PortPairService;
-import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
-import org.onosproject.vtnrsc.service.VtnRscService;
-import org.onosproject.vtnrsc.virtualport.VirtualPortService;
-import org.slf4j.Logger;
-
-/**
- * Provides service function forwarder implementation.
- */
-public class ServiceFunctionForwarderImpl implements ServiceFunctionForwarderService {
-
-    private final Logger log = getLogger(getClass());
-    protected VirtualPortService virtualPortService;
-    protected VtnRscService vtnRscService;
-    protected PortPairService portPairService;
-    protected PortPairGroupService portPairGroupService;
-    protected FlowClassifierService flowClassifierService;
-    protected PortChainService portChainService;
-    protected DriverService driverService;
-    protected FlowObjectiveService flowObjectiveService;
-    protected HostService hostService;
-    protected ApplicationId appId;
-
-    private static final String PATH_NOT_NULL = "Load balanced path cannot be null";
-    private static final String APP_ID_NOT_NULL = "Application-Id cannot be null";
-
-    /**
-     * Default constructor.
-     */
-    public ServiceFunctionForwarderImpl() {
-    }
-
-    /**
-     * Explicit constructor.
-     *
-     * @param appId application id
-     */
-    public ServiceFunctionForwarderImpl(ApplicationId appId) {
-        this.appId = checkNotNull(appId, APP_ID_NOT_NULL);
-        ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
-        this.flowObjectiveService = serviceDirectory.get(FlowObjectiveService.class);
-        this.driverService = serviceDirectory.get(DriverService.class);
-        this.virtualPortService = serviceDirectory.get(VirtualPortService.class);
-        this.vtnRscService = serviceDirectory.get(VtnRscService.class);
-        this.portPairService = serviceDirectory.get(PortPairService.class);
-        this.portPairGroupService = serviceDirectory.get(PortPairGroupService.class);
-        this.flowClassifierService = serviceDirectory.get(FlowClassifierService.class);
-        this.hostService = serviceDirectory.get(HostService.class);
-        this.portChainService = serviceDirectory.get(PortChainService.class);
-    }
-
-    @Override
-    public void installForwardingRule(PortChain portChain, NshServicePathId nshSpi) {
-        //TODO this method will be removed
-    }
-
-    @Override
-    public void unInstallForwardingRule(PortChain portChain, NshServicePathId nshSpi) {
-        //TODO this method will be removed
-    }
-
-    @Override
-    public void installLoadBalancedForwardingRule(List<PortPairId> path, NshServicePathId nshSpi) {
-        checkNotNull(path, PATH_NOT_NULL);
-        processForwardingRule(path, nshSpi, Objective.Operation.ADD);
-    }
-
-    @Override
-    public void unInstallLoadBalancedForwardingRule(List<PortPairId> path, NshServicePathId nshSpi) {
-        checkNotNull(path, PATH_NOT_NULL);
-        processForwardingRule(path, nshSpi, Objective.Operation.REMOVE);
-    }
-
-    /**
-     * Process the required forwarding rules for the given path.
-     *
-     * @param path list of port pair ids
-     * @param nshSpi service path index
-     * @param type operation type ADD/REMOVE
-     */
-    private void processForwardingRule(List<PortPairId> path, NshServicePathId nshSpi,
-                                       Objective.Operation type) {
-
-        // Get the first port pair
-        ListIterator<PortPairId> portPairListIterator = path.listIterator();
-        PortPair currentPortPair = portPairService.getPortPair(portPairListIterator.next());
-
-        // Get destination port pair group
-        if (!portPairListIterator.hasNext()) {
-            log.debug("Path is empty");
-            return;
-        }
-        PortPair nextPortPair = portPairService.getPortPair(portPairListIterator.next());
-        DeviceId currentDeviceId = null;
-        DeviceId nextDeviceId = null;
-
-        // Travel from SF to SF.
-        do {
-            currentDeviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(currentPortPair.egress()));
-            nextDeviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(nextPortPair.ingress()));
-            // pack traffic selector
-            TrafficSelector.Builder selector = packTrafficSelector(currentDeviceId, currentPortPair, nshSpi);
-            // Pack treatment
-            if (currentDeviceId.equals(nextDeviceId)) {
-                TrafficTreatment.Builder treatment = packTrafficTreatment(nextPortPair, true);
-                // Send SFF to SFF
-                sendServiceFunctionForwarder(selector, treatment, currentDeviceId, type);
-            } else {
-                TrafficTreatment.Builder treatment = packTrafficTreatment(nextPortPair, false);
-                // Send SFF to OVS
-                sendServiceFunctionForwarder(selector, treatment, currentDeviceId, type);
-
-                // At the other device get the packet from vlan and send to first port pair
-                TrafficSelector.Builder selectorDst = DefaultTrafficSelector.builder();
-                selectorDst.matchVlanId((VlanId.vlanId(Short.parseShort((vtnRscService
-                        .getL3vni(nextPortPair.tenantId()).toString())))));
-                TrafficTreatment.Builder treatmentDst = DefaultTrafficTreatment.builder();
-                MacAddress macAddr = virtualPortService.getPort(VirtualPortId.portId(nextPortPair.ingress()))
-                        .macAddress();
-                Host host = hostService.getHost(HostId.hostId(macAddr));
-                PortNumber port = host.location().port();
-                treatmentDst.setOutput(port);
-                // Send OVS to SFF
-                sendServiceFunctionForwarder(selectorDst, treatmentDst, nextDeviceId, type);
-            }
-
-            // Move to next service function
-            currentPortPair = nextPortPair;
-            if (!portPairListIterator.hasNext()) {
-                break;
-            }
-            nextPortPair = portPairService.getPortPair(portPairListIterator.next());
-        } while (true);
-    }
-
-    /**
-     * Pack traffic selector.
-     *
-     * @param deviceId device id
-     * @param portPair port-pair
-     * @param nshSpi nsh service path index
-     * @return traffic selector
-     */
-    public TrafficSelector.Builder packTrafficSelector(DeviceId deviceId,
-                                                       PortPair portPair, NshServicePathId nshSpi) {
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-
-        MacAddress dstMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.egress())).macAddress();
-        Host host = hostService.getHost(HostId.hostId(dstMacAddress));
-        PortNumber port = host.location().port();
-        selector.matchInPort(port);
-
-        DriverHandler handler = driverService.createHandler(deviceId);
-        ExtensionSelectorResolver resolver = handler.behaviour(ExtensionSelectorResolver.class);
-        ExtensionSelector nspSpiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
-
-        try {
-            nspSpiSelector.setPropertyValue("nshSpi", nshSpi);
-        } catch (Exception e) {
-            log.error("Failed to get extension instruction to set Nsh Spi Id {}", deviceId);
-        }
-
-        selector.extension(nspSpiSelector, deviceId);
-
-        return selector;
-    }
-
-    /**
-     * Pack traffic treatment.
-     *
-     * @param portPair port pair
-     * @param isSameOvs whether the next port pair is in the same ovs
-     * @return traffic treatment
-     */
-    public TrafficTreatment.Builder packTrafficTreatment(PortPair portPair, boolean isSameOvs) {
-        MacAddress srcMacAddress = null;
-
-        // Check the treatment whether destination SF is on same OVS or in
-        // different OVS.
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-        if (isSameOvs) {
-            srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress();
-            Host host = hostService.getHost(HostId.hostId(srcMacAddress));
-            PortNumber port = host.location().port();
-            treatment.setOutput(port);
-        } else {
-            // Vxlan tunnel port for NSH header(Vxlan + NSH).
-            TpPort nshDstPort = TpPort.tpPort(6633);
-            // TODO check whether this logic is correct
-            VlanId vlanId = VlanId.vlanId(Short.parseShort((vtnRscService.getL3vni(portPair.tenantId()).toString())));
-            treatment.setVlanId(vlanId);
-            treatment.setUdpDst(nshDstPort);
-        }
-
-        return treatment;
-    }
-
-    /**
-     * Send service function forwarder to OVS.
-     *
-     * @param selector traffic selector
-     * @param treatment traffic treatment
-     * @param deviceId device id
-     * @param type operation type
-     */
-    public void sendServiceFunctionForwarder(TrafficSelector.Builder selector, TrafficTreatment.Builder treatment,
-                                             DeviceId deviceId, Objective.Operation type) {
-        log.info("Sending flow to serfice-function-forwarder. Selector {}, Treatment {}",
-                 selector.toString(), treatment.toString());
-        ForwardingObjective.Builder objective = DefaultForwardingObjective.builder().withTreatment(treatment.build())
-                .withSelector(selector.build()).fromApp(appId).makePermanent().withFlag(Flag.VERSATILE);
-        if (type.equals(Objective.Operation.ADD)) {
-            log.debug("ADD");
-            flowObjectiveService.forward(deviceId, objective.add());
-        } else {
-            log.debug("REMOVE");
-            flowObjectiveService.forward(deviceId, objective.remove());
-        }
-    }
-}
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/package-info.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/package-info.java
deleted file mode 100644
index 6dfe007..0000000
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/impl/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-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.
- */
-
-/**
- * SFC Service manager for interacting with SFC.
- */
-package org.onosproject.sfc.forwarder.impl;
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/package-info.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/package-info.java
deleted file mode 100644
index 83ac1dd..0000000
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/forwarder/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-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.
- */
-
-/**
- * Service function forwarder for SFC.
- */
-package org.onosproject.sfc.forwarder;
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/FlowClassifierInstallerService.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/SfcFlowRuleInstallerService.java
similarity index 79%
rename from apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/FlowClassifierInstallerService.java
rename to apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/SfcFlowRuleInstallerService.java
index 58e59be..caff305 100644
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/FlowClassifierInstallerService.java
+++ b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/SfcFlowRuleInstallerService.java
@@ -23,7 +23,7 @@
 /**
  * Abstraction of an entity which installs flow classification rules in ovs.
  */
-public interface FlowClassifierInstallerService {
+public interface SfcFlowRuleInstallerService {
 
     /**
      * Install flow classifier.
@@ -44,24 +44,24 @@
     ConnectPoint unInstallFlowClassifier(PortChain portChain, NshServicePathId nshSpiId);
 
     /**
-     * Install load balanced flow classifier.
+     * Install load balanced flow rules.
      *
      * @param portChain port-chain
      * @param fiveTuple five tuple packet information
      * @param nshSpiId service path index identifier
      * @return connectPoint the network identifier
      */
-    ConnectPoint installLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
-                                                   NshServicePathId nshSpiId);
+    ConnectPoint installLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple,
+            NshServicePathId nshSpiId);
 
     /**
-     * Uninstall load balanced flow classifier.
+     * Uninstall load balanced flow rules.
      *
      * @param portChain port-chain
      * @param fiveTuple five tuple packet information
      * @param nshSpiId service path index identifier
      * @return connectPoint the network identifier
      */
-    ConnectPoint unInstallLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
-                                                     NshServicePathId nshSpiId);
+    ConnectPoint unInstallLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple,
+            NshServicePathId nshSpiId);
 }
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImpl.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImpl.java
deleted file mode 100644
index ec12553..0000000
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImpl.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright 2015-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.sfc.installer.impl;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SI;
-import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.NshServiceIndex;
-import org.onosproject.net.NshServicePathId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criteria;
-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.flowobjective.ForwardingObjective.Flag;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.sfc.installer.FlowClassifierInstallerService;
-import org.onosproject.vtnrsc.FiveTuple;
-import org.onosproject.vtnrsc.FlowClassifier;
-import org.onosproject.vtnrsc.FlowClassifierId;
-import org.onosproject.vtnrsc.PortChain;
-import org.onosproject.vtnrsc.PortPair;
-import org.onosproject.vtnrsc.PortPairGroup;
-import org.onosproject.vtnrsc.PortPairGroupId;
-import org.onosproject.vtnrsc.PortPairId;
-import org.onosproject.vtnrsc.VirtualPortId;
-import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
-import org.onosproject.vtnrsc.portpair.PortPairService;
-import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
-import org.onosproject.vtnrsc.service.VtnRscService;
-import org.onosproject.vtnrsc.virtualport.VirtualPortService;
-import org.slf4j.Logger;
-
-/**
- * Provides flow classifier installer implementation.
- */
-public class FlowClassifierInstallerImpl implements FlowClassifierInstallerService {
-    private final Logger log = getLogger(getClass());
-
-    protected VirtualPortService virtualPortService;
-    protected VtnRscService vtnRscService;
-    protected PortPairService portPairService;
-    protected PortPairGroupService portPairGroupService;
-    protected FlowClassifierService flowClassifierService;
-    protected DriverService driverService;
-    protected DeviceService deviceService;
-    protected HostService hostService;
-    protected FlowObjectiveService flowObjectiveService;
-    protected ApplicationId appId;
-
-    private static final String DRIVER_NAME = "onosfw";
-    private static final String FLOW_CLASSIFIER_NOT_NULL = "Flow-Classifier cannot be null";
-    private static final String FLOW_CLASSIFIER_ID_NOT_NULL = "Flow-Classifier-Id cannot be null";
-    private static final String PORT_CHAIN_NOT_NULL = "Port-Chain cannot be null";
-    private static final int NULL = 0;
-    private static final int FLOW_CLASSIFIER_PRIORITY = 0x7fff;
-    private static final short NSH_SI_ID = 0xff;
-
-    /**
-     * Default constructor.
-     */
-    public FlowClassifierInstallerImpl() {
-    }
-
-    /**
-     * Explicit constructor.
-     *
-     * @param appId application id.
-     */
-    public FlowClassifierInstallerImpl(ApplicationId appId) {
-        this.appId = checkNotNull(appId, "ApplicationId can not be null");
-        ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
-        this.flowObjectiveService = serviceDirectory.get(FlowObjectiveService.class);
-        this.driverService = serviceDirectory.get(DriverService.class);
-        this.deviceService = serviceDirectory.get(DeviceService.class);
-        this.hostService = serviceDirectory.get(HostService.class);
-        this.virtualPortService = serviceDirectory.get(VirtualPortService.class);
-        this.vtnRscService = serviceDirectory.get(VtnRscService.class);
-        this.portPairService = serviceDirectory.get(PortPairService.class);
-        this.portPairGroupService = serviceDirectory.get(PortPairGroupService.class);
-        this.flowClassifierService = serviceDirectory.get(FlowClassifierService.class);
-    }
-
-    @Override
-    public ConnectPoint installFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) {
-        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
-        // Get the portPairGroup
-        List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups();
-        ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator();
-        PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next();
-        PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
-        List<PortPairId> llPortPairIdList = portPairGroup.portPairs();
-
-        // Get port pair
-        ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator();
-        PortPairId portPairId = portPairListIterator.next();
-        PortPair portPair = portPairService.getPortPair(portPairId);
-
-        return processFlowClassifier(portChain, portPair, nshSpiId, null, Objective.Operation.ADD);
-    }
-
-    @Override
-    public ConnectPoint unInstallFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) {
-        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
-        // Get the portPairGroup
-        List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups();
-        ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator();
-        PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next();
-        PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
-        List<PortPairId> llPortPairIdList = portPairGroup.portPairs();
-
-        // Get port pair
-        ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator();
-        PortPairId portPairId = portPairListIterator.next();
-        PortPair portPair = portPairService.getPortPair(portPairId);
-
-        return processFlowClassifier(portChain, portPair, nshSpiId, null, Objective.Operation.REMOVE);
-    }
-
-    @Override
-    public ConnectPoint installLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
-                                                          NshServicePathId nshSpiId) {
-        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
-
-        // Get the load balanced path
-        List<PortPairId> portPairs = portChain.getLoadBalancePath(fiveTuple);
-
-        // Get the first port pair
-        ListIterator<PortPairId> portPairListIterator = portPairs.listIterator();
-        PortPairId portPairId = portPairListIterator.next();
-        PortPair portPair = portPairService.getPortPair(portPairId);
-
-        return processFlowClassifier(portChain, portPair, nshSpiId, fiveTuple, Objective.Operation.ADD);
-    }
-
-    @Override
-    public ConnectPoint unInstallLoadBalancedFlowClassifier(PortChain portChain, FiveTuple fiveTuple,
-                                                            NshServicePathId nshSpiId) {
-        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
-        // Get the load balanced path
-        List<PortPairId> portPairs = portChain.getLoadBalancePath(fiveTuple);
-
-        // Get the first port pair
-        ListIterator<PortPairId> portPairListIterator = portPairs.listIterator();
-        PortPairId portPairId = portPairListIterator.next();
-        PortPair portPair = portPairService.getPortPair(portPairId);
-
-        return processFlowClassifier(portChain, portPair, nshSpiId, fiveTuple, Objective.Operation.REMOVE);
-    }
-
-    public ConnectPoint processFlowClassifier(PortChain portChain, PortPair portPair, NshServicePathId nshSpiId,
-                                              FiveTuple fiveTuple, Objective.Operation type) {
-
-        DeviceId deviceIdfromPortPair = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.ingress()));
-        MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress();
-        Host host = hostService.getHost(HostId.hostId(srcMacAddress));
-        PortNumber port = host.location().port();
-
-        DeviceId deviceId = deviceIdfromPortPair;
-
-        // Vxlan tunnel port for NSH header(Vxlan + NSH).
-        TpPort nshDstPort = TpPort.tpPort(6633);
-
-        FlowClassifierInstallerService flowclassifierinstallerService;
-        // get flow classifiers
-        List<FlowClassifierId> llFlowClassifierList = portChain.flowClassifiers();
-        ListIterator<FlowClassifierId> flowClassifierListIterator = llFlowClassifierList.listIterator();
-
-        while (flowClassifierListIterator.hasNext()) {
-            FlowClassifierId flowclassifierId = flowClassifierListIterator.next();
-            FlowClassifier flowClassifier = flowClassifierService.getFlowClassifier(flowclassifierId);
-
-            if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) {
-                deviceId = vtnRscService.getSfToSffMaping(flowClassifier.srcPort());
-            }
-
-            // Build Traffic selector.
-            TrafficSelector.Builder selector = packTrafficSelector(flowClassifier, fiveTuple);
-
-            if (fiveTuple == null) {
-                // Send the packet to controller
-                TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-                treatment.setOutput(PortNumber.CONTROLLER);
-                sendServiceFunctionClassifier(selector, treatment, deviceId, type, flowClassifier.priority());
-            } else if (deviceId.equals(deviceIdfromPortPair)) {
-                // classifier and source device are in the same OVS. So directly send packet to first port pair
-                TrafficTreatment.Builder treatment = packTrafficTreatment(deviceId, port, nshDstPort,
-                                                                          nshSpiId, flowClassifier, true);
-                // Build forwarding objective and send to OVS.
-                sendServiceFunctionClassifier(selector, treatment, deviceId, type, flowClassifier.priority());
-            } else {
-                // classifier and source device are not in the same OVS. Send packet on vlan Tunnel
-                TrafficTreatment.Builder treatment = packTrafficTreatment(deviceId, port, nshDstPort,
-                                                                          nshSpiId, flowClassifier, false);
-                // Build forwarding objective and send to OVS.
-                sendServiceFunctionClassifier(selector, treatment, deviceId, type, flowClassifier.priority());
-
-                // At the other device get the packet from vlan and send to first port pair
-                TrafficSelector.Builder selectorDst = DefaultTrafficSelector.builder();
-                selectorDst.matchVlanId((VlanId.vlanId(Short.parseShort((vtnRscService
-                        .getL3vni(flowClassifier.tenantId()).toString())))));
-                TrafficTreatment.Builder treatmentDst = DefaultTrafficTreatment.builder();
-                Host hostDst = hostService.getHost(HostId.hostId(srcMacAddress));
-                treatmentDst.setOutput(hostDst.location().port());
-                sendServiceFunctionClassifier(selectorDst, treatmentDst, deviceIdfromPortPair, type,
-                                              flowClassifier.priority());
-            }
-        }
-        return host.location();
-    }
-
-    /**
-     * Pack Traffic selector.
-     *
-     * @param flowClassifier flow-classifier
-     * @param fiveTuple five tuple info for the packet
-     * @return traffic selector
-     */
-    public TrafficSelector.Builder packTrafficSelector(FlowClassifier flowClassifier, FiveTuple fiveTuple) {
-
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-
-        if ((flowClassifier.srcIpPrefix() != null) && (flowClassifier.srcIpPrefix().prefixLength() != 0)) {
-            selector.matchIPSrc(flowClassifier.srcIpPrefix());
-        } else if (fiveTuple != null && fiveTuple.ipSrc() != null) {
-            selector.matchIPSrc(IpPrefix.valueOf(fiveTuple.ipSrc(), 24));
-        }
-
-        if ((flowClassifier.dstIpPrefix() != null) && (flowClassifier.dstIpPrefix().prefixLength() != 0)) {
-            selector.matchIPDst(flowClassifier.dstIpPrefix());
-        } else if (fiveTuple != null && fiveTuple.ipDst() != null) {
-            selector.matchIPDst(IpPrefix.valueOf(fiveTuple.ipDst(), 24));
-        }
-
-        if ((flowClassifier.protocol() != null) && (!flowClassifier.protocol().isEmpty())) {
-            if (flowClassifier.protocol().equalsIgnoreCase("TCP")) {
-                selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_TCP));
-            } else if (flowClassifier.protocol().equalsIgnoreCase("UDP")) {
-                selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP));
-            }
-        } else if (fiveTuple != null && fiveTuple.protocol() != 0) {
-            selector.add(Criteria.matchIPProtocol(fiveTuple.protocol()));
-        }
-
-        if (((flowClassifier.etherType() != null) && (!flowClassifier.etherType().isEmpty()))
-                && (flowClassifier.etherType().equals("IPv4") || flowClassifier.etherType().equals("IPv6"))) {
-            if (flowClassifier.etherType().equals("IPv4")) {
-                selector.matchEthType(Ethernet.TYPE_IPV4);
-            } else {
-                selector.matchEthType(Ethernet.TYPE_IPV6);
-            }
-        }
-
-        if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) {
-            VirtualPortId vPortId = VirtualPortId.portId(flowClassifier.srcPort().portId());
-            MacAddress macAddress = virtualPortService.getPort(vPortId).macAddress();
-            Host host = hostService.getHost(HostId.hostId(macAddress));
-            selector.matchInPort(host.location().port());
-        }
-
-        // Take the port information from five tuple only when the protocol is TCP.
-        if (fiveTuple != null && fiveTuple.protocol() == IPv4.PROTOCOL_TCP) {
-            selector.matchTcpSrc(TpPort.tpPort((int) fiveTuple.portSrc().toLong()));
-            selector.matchTcpDst(TpPort.tpPort((int) fiveTuple.portDst().toLong()));
-        } else {
-            // For udp packets take the port information from flow classifier
-            List<TpPort> srcPortRange = new LinkedList<>();
-            List<TpPort> dstPortRange = new LinkedList<>();
-            if ((flowClassifier.minSrcPortRange() != 0) && flowClassifier.maxSrcPortRange() != 0
-                    && flowClassifier.minDstPortRange() != 0 && flowClassifier.maxDstPortRange() != 0) {
-
-                for (int port = flowClassifier.minSrcPortRange(); port <= flowClassifier.maxSrcPortRange(); port++) {
-                    srcPortRange.add(TpPort.tpPort(port));
-                }
-                for (int port = flowClassifier.minDstPortRange(); port <= flowClassifier.maxDstPortRange(); port++) {
-                    dstPortRange.add(TpPort.tpPort(port));
-                }
-            }
-
-            for (TpPort inPort : srcPortRange) {
-                selector.matchUdpSrc(inPort);
-            }
-            for (TpPort outPort : dstPortRange) {
-                selector.matchUdpDst(outPort);
-            }
-        }
-        return selector;
-    }
-
-    /**
-     * Pack traffic treatment.
-     *
-     * @param deviceId device id
-     * @param port port number
-     * @param nshDstPort vxlan tunnel port for nsh header
-     * @param nshSpi nsh spi
-     * @param flowClassifier flow-classifier
-     * @param isSameOvs whether the next service function is in same ovs
-     * @return traffic treatment
-     */
-    public TrafficTreatment.Builder packTrafficTreatment(DeviceId deviceId, PortNumber port,
-                                                         TpPort nshDstPort, NshServicePathId nshSpi,
-                                                         FlowClassifier flowClassifier, boolean isSameOvs) {
-
-        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-
-        if (isSameOvs) {
-            treatmentBuilder.setOutput(port);
-        } else {
-            treatmentBuilder.setVlanId((VlanId.vlanId(Short.parseShort((vtnRscService
-                    .getL3vni(flowClassifier.tenantId()).toString())))));
-            treatmentBuilder.setUdpDst(nshDstPort);
-        }
-
-        // Set NSH
-        DriverHandler handler = driverService.createHandler(deviceId);
-        ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
-        ExtensionTreatment nspIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SPI.type());
-        ExtensionTreatment nsiIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SI.type());
-
-        treatmentBuilder.extension(nspIdTreatment, deviceId);
-        treatmentBuilder.extension(nsiIdTreatment, deviceId);
-
-        try {
-            nspIdTreatment.setPropertyValue("nshSpi", nshSpi);
-        } catch (Exception e) {
-            log.error("Failed to get extension instruction to set Nsh Spi Id {}", deviceId);
-        }
-        try {
-            nsiIdTreatment.setPropertyValue("nshSi", NshServiceIndex.of(NSH_SI_ID));
-        } catch (Exception e) {
-            log.error("Failed to get extension instruction to set Nsh Si Id {}", deviceId);
-        }
-
-        return treatmentBuilder;
-    }
-
-    /**
-     * Send service-function-forwarder to OVS.
-     *
-     * @param selector traffic selector
-     * @param treatment traffic treatment
-     * @param deviceId device id
-     * @param type operation type
-     * @param priority priority of classifier
-     */
-    public void sendServiceFunctionClassifier(TrafficSelector.Builder selector, TrafficTreatment.Builder treatment,
-                                              DeviceId deviceId, Objective.Operation type, int priority) {
-        log.info("Sending flow to service function classifier. Selector {}, Treatment {}",
-                 selector.toString(), treatment.toString());
-        ForwardingObjective.Builder objective = DefaultForwardingObjective.builder().withTreatment(treatment.build())
-                .withSelector(selector.build()).fromApp(appId).makePermanent().withFlag(Flag.VERSATILE)
-                .withPriority(priority);
-
-        if (type.equals(Objective.Operation.ADD)) {
-            log.debug("flowClassifierRules-->ADD");
-            flowObjectiveService.forward(deviceId, objective.add());
-        } else {
-            log.debug("flowClassifierRules-->REMOVE");
-            flowObjectiveService.forward(deviceId, objective.remove());
-        }
-    }
-}
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImpl.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImpl.java
new file mode 100644
index 0000000..71397a7
--- /dev/null
+++ b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImpl.java
@@ -0,0 +1,888 @@
+/*
+ * 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.sfc.installer.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE;
+import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SI;
+import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SPI;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH1;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH2;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH3;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH4;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SI;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.NshContextHeader;
+import org.onosproject.net.NshServiceIndex;
+import org.onosproject.net.NshServicePathId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.ExtensionSelectorResolver;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.ExtensionSelector;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.host.HostService;
+import org.onosproject.sfc.installer.SfcFlowRuleInstallerService;
+import org.onosproject.vtnrsc.FiveTuple;
+import org.onosproject.vtnrsc.FlowClassifier;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.PortChain;
+import org.onosproject.vtnrsc.PortPair;
+import org.onosproject.vtnrsc.PortPairGroup;
+import org.onosproject.vtnrsc.PortPairGroupId;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.SegmentationId;
+import org.onosproject.vtnrsc.VirtualPort;
+import org.onosproject.vtnrsc.VirtualPortId;
+import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
+import org.onosproject.vtnrsc.portpair.PortPairService;
+import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
+import org.onosproject.vtnrsc.service.VtnRscService;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Provides flow classifier installer implementation.
+ */
+public class SfcFlowRuleInstallerImpl implements SfcFlowRuleInstallerService {
+    private final Logger log = getLogger(getClass());
+
+    protected VirtualPortService virtualPortService;
+    protected VtnRscService vtnRscService;
+    protected PortPairService portPairService;
+    protected PortPairGroupService portPairGroupService;
+    protected FlowClassifierService flowClassifierService;
+    protected DriverService driverService;
+    protected DeviceService deviceService;
+    protected HostService hostService;
+    protected TenantNetworkService tenantNetworkService;
+    protected FlowObjectiveService flowObjectiveService;
+    protected ApplicationId appId;
+
+    private static final String PORT_CHAIN_NOT_NULL = "Port-Chain cannot be null";
+    private static final int FLOW_CLASSIFIER_PRIORITY = 0xC738;
+    private static final int DEFAULT_FORWARDER_PRIORITY = 0xD6D8;
+    private static final int ENCAP_OUTPUT_PRIORITY = 0x64;
+    private static final int TUNNEL_SEND_PRIORITY = 0xC8;
+    private static final String SWITCH_CHANNEL_ID = "channelId";
+    private static final int ENCAP_OUTPUT_TABLE = 4;
+    private static final int TUNNEL_SEND_TABLE = 7;
+    private static final short ENCAP_ETH_TYPE = (short) 0x894f;
+    private static final String DEFAULT_IP = "0.0.0.0";
+    private static final String VXLANPORT_HEAD = "vxlan-0.0.0.0";
+
+    /* Port chain params */
+    private short nshSi;
+    List<DeviceId> classifierList;
+    List<DeviceId> forwarderList;
+
+    /**
+     * Default constructor.
+     */
+    public SfcFlowRuleInstallerImpl() {
+    }
+
+    /**
+     * Explicit constructor.
+     *
+     * @param appId application id.
+     */
+    public SfcFlowRuleInstallerImpl(ApplicationId appId) {
+        this.appId = checkNotNull(appId, "ApplicationId can not be null");
+        ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+        this.flowObjectiveService = serviceDirectory.get(FlowObjectiveService.class);
+        this.driverService = serviceDirectory.get(DriverService.class);
+        this.deviceService = serviceDirectory.get(DeviceService.class);
+        this.hostService = serviceDirectory.get(HostService.class);
+        this.virtualPortService = serviceDirectory.get(VirtualPortService.class);
+        this.vtnRscService = serviceDirectory.get(VtnRscService.class);
+        this.portPairService = serviceDirectory.get(PortPairService.class);
+        this.portPairGroupService = serviceDirectory.get(PortPairGroupService.class);
+        this.flowClassifierService = serviceDirectory.get(FlowClassifierService.class);
+        this.tenantNetworkService = serviceDirectory.get(TenantNetworkService.class);
+        nshSi = 0xff;
+    }
+
+    @Override
+    public ConnectPoint installFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) {
+        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
+        // Get the portPairGroup
+        List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups();
+        ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator();
+        PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next();
+        PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
+        List<PortPairId> llPortPairIdList = portPairGroup.portPairs();
+
+        // Get port pair
+        ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator();
+        PortPairId portPairId = portPairListIterator.next();
+        PortPair portPair = portPairService.getPortPair(portPairId);
+
+        return installSfcClassifierRules(portChain, portPair, nshSpiId, null, Objective.Operation.ADD);
+    }
+
+    @Override
+    public ConnectPoint unInstallFlowClassifier(PortChain portChain, NshServicePathId nshSpiId) {
+        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
+        // Get the portPairGroup
+        List<PortPairGroupId> llPortPairGroupIdList = portChain.portPairGroups();
+        ListIterator<PortPairGroupId> portPairGroupIdListIterator = llPortPairGroupIdList.listIterator();
+        PortPairGroupId portPairGroupId = portPairGroupIdListIterator.next();
+        PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
+        List<PortPairId> llPortPairIdList = portPairGroup.portPairs();
+
+        // Get port pair
+        ListIterator<PortPairId> portPairListIterator = llPortPairIdList.listIterator();
+        PortPairId portPairId = portPairListIterator.next();
+        PortPair portPair = portPairService.getPortPair(portPairId);
+
+        return installSfcClassifierRules(portChain, portPair, nshSpiId, null, Objective.Operation.REMOVE);
+    }
+
+    @Override
+    public ConnectPoint installLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple,
+            NshServicePathId nshSpiId) {
+        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
+
+        return installSfcFlowRules(portChain, fiveTuple, nshSpiId, Objective.Operation.ADD);
+    }
+
+    @Override
+    public ConnectPoint unInstallLoadBalancedFlowRules(PortChain portChain, FiveTuple fiveTuple,
+            NshServicePathId nshSpiId) {
+        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
+        return installSfcFlowRules(portChain, fiveTuple, nshSpiId, Objective.Operation.REMOVE);
+    }
+
+    public ConnectPoint installSfcFlowRules(PortChain portChain, FiveTuple fiveTuple, NshServicePathId nshSpiId,
+            Objective.Operation type) {
+        checkNotNull(portChain, PORT_CHAIN_NOT_NULL);
+
+        classifierList = Lists.newArrayList();
+        forwarderList = Lists.newArrayList();
+
+        // Get the load balanced path
+        List<PortPairId> portPairs = portChain.getLoadBalancePath(fiveTuple);
+
+        // Get the first port pair
+        ListIterator<PortPairId> portPairListIterator = portPairs.listIterator();
+        PortPairId portPairId = portPairListIterator.next();
+        PortPair currentPortPair = portPairService.getPortPair(portPairId);
+
+        ConnectPoint connectPoint = installSfcClassifierRules(portChain, currentPortPair, nshSpiId, fiveTuple, type);
+
+        log.info("Installing encap and output for first port pair");
+
+        installSfcEncapOutputRule(currentPortPair, nshSpiId, type);
+
+        PortPair nextPortPair;
+        while (portPairListIterator.hasNext()) {
+            portPairId = portPairListIterator.next();
+            nextPortPair = portPairService.getPortPair(portPairId);
+            installSfcForwardRule(currentPortPair, nextPortPair, nshSpiId, type);
+            installSfcEncapOutputRule(nextPortPair, nshSpiId, type);
+            currentPortPair = nextPortPair;
+        }
+        installSfcEndRule(currentPortPair, nshSpiId, type);
+
+        if (type.equals(Objective.Operation.ADD)) {
+            portChain.addSfcClassifiers(portChain.getLoadBalanceId(fiveTuple), classifierList);
+            portChain.addSfcForwarders(portChain.getLoadBalanceId(fiveTuple), forwarderList);
+        } else {
+            portChain.removeSfcClassifiers(portChain.getLoadBalanceId(fiveTuple), classifierList);
+            portChain.removeSfcForwarders(portChain.getLoadBalanceId(fiveTuple), forwarderList);
+        }
+        return connectPoint;
+    }
+
+    public void installSfcTunnelReceiveRule(DeviceId deviceId, NshServicePathId nshSpiId, Objective.Operation type) {
+
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class);
+        ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
+        ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type());
+
+        try {
+            nshSpiSelector.setPropertyValue("nshSpi", nshSpiId);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage());
+        }
+        try {
+            nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage());
+        }
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.extension(nshSpiSelector, deviceId);
+        selector.extension(nshSiSelector, deviceId);
+
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        treatment.transition(ENCAP_OUTPUT_TABLE);
+
+        sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY);
+    }
+
+    public void installSfcTunnelSendRule(DeviceId deviceId, NshServicePathId nshSpiId, Objective.Operation type) {
+
+        // Prepare selector with nsp, nsi and inport from egress of port pair
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class);
+        ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
+        ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type());
+        ExtensionSelector encapEthTypeSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE
+                                                                                       .type());
+        try {
+            nshSpiSelector.setPropertyValue("nshSpi", nshSpiId);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage());
+        }
+        try {
+            nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage());
+        }
+        try {
+            encapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match encapEthType {}", deviceId);
+        }
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.extension(nshSpiSelector, deviceId);
+        selector.extension(nshSiSelector, deviceId);
+
+        ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class);
+        ExtensionTreatment tunGpeNpTreatment = treatmentResolver.getExtensionInstruction(NICIRA_TUN_GPE_NP.type());
+        try {
+            tunGpeNpTreatment.setPropertyValue("tunGpeNp", ((byte) 4));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set tunGpeNp {}", deviceId);
+        }
+
+        ExtensionTreatment moveC1ToC1 = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_NSH_C1_TO_C1.type());
+
+        ExtensionTreatment moveC2ToC2 = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_NSH_C2_TO_C2.type());
+
+        ExtensionTreatment moveC3ToC3 = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_NSH_C3_TO_C3.type());
+
+        ExtensionTreatment moveC4ToC4 = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_NSH_C4_TO_C4.type());
+
+        ExtensionTreatment moveTunIpv4DstToTunIpv4Dst = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST.type());
+
+        ExtensionTreatment moveTunIdToTunId = treatmentResolver
+                .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                         .NICIRA_MOV_TUN_ID_TO_TUN_ID.type());
+
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        treatment.extension(tunGpeNpTreatment, deviceId);
+        treatment.extension(moveC1ToC1, deviceId);
+        treatment.extension(moveC2ToC2, deviceId);
+        treatment.extension(moveC3ToC3, deviceId);
+        treatment.extension(moveC4ToC4, deviceId);
+        treatment.extension(moveTunIpv4DstToTunIpv4Dst, deviceId);
+        treatment.extension(moveTunIdToTunId, deviceId);
+
+        Iterable<Device> devices = deviceService.getAvailableDevices();
+        DeviceId localControllerId = getControllerId(deviceService.getDevice(deviceId), devices);
+        DriverHandler controllerHandler = driverService.createHandler(localControllerId);
+
+        BridgeConfig bridgeConfig = controllerHandler.behaviour(BridgeConfig.class);
+        Set<PortNumber> ports = bridgeConfig.getPortNumbers();
+        String tunnelName = "vxlan-" + DEFAULT_IP;
+        ports.stream()
+        .filter(p ->p.name().equalsIgnoreCase(tunnelName))
+        .forEach(p -> {
+            treatment.setOutput(p);
+            sendSfcRule(selector, treatment, deviceId, type, TUNNEL_SEND_PRIORITY);
+        });
+    }
+
+    public void installSfcEndRule(PortPair portPair, NshServicePathId nshSpiId, Objective.Operation type) {
+        DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.egress()));
+        MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.egress())).macAddress();
+        Host host = hostService.getHost(HostId.hostId(srcMacAddress));
+        PortNumber port = host.location().port();
+
+        // Prepare selector with nsp, nsi and inport from egress of port pair
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionSelectorResolver selectorResolver = handler.behaviour(ExtensionSelectorResolver.class);
+        ExtensionSelector nshSpiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
+        ExtensionSelector nshSiSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type());
+        ExtensionSelector encapEthTypeSelector = selectorResolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE
+                .type());
+        try {
+            nshSpiSelector.setPropertyValue("nshSpi", nshSpiId);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Spi Id for end rule {}", e.getMessage());
+        }
+        // Decrement the SI
+        nshSi = (short) (nshSi - 1);
+        try {
+            nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id for end rule {}", e.getMessage());
+        }
+        try {
+            encapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match encapEthType {}", deviceId);
+        }
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.extension(encapEthTypeSelector, deviceId);
+        selector.extension(nshSpiSelector, deviceId);
+        selector.extension(nshSiSelector, deviceId);
+        selector.matchInPort(port);
+
+        // Set treatment to pop nsh header, set tunnel id and resubmit to table
+        // 0.
+        ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class);
+        ExtensionTreatment popNshTreatment = treatmentResolver.getExtensionInstruction(NICIRA_POP_NSH.type());
+
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        treatment.extension(popNshTreatment, deviceId);
+
+        VirtualPort virtualPort = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress()));
+        SegmentationId segmentationId = tenantNetworkService.getNetwork(virtualPort.networkId()).segmentationId();
+        treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId.toString())));
+
+        ExtensionTreatment resubmitTableTreatment = treatmentResolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE
+                .type());
+
+        PortNumber vxlanPortNumber = getVxlanPortNumber(deviceId);
+
+        try {
+            resubmitTableTreatment.setPropertyValue("inPort", vxlanPortNumber);
+        } catch (Exception e) {
+            log.error("Failed to set extension treatment for resubmit table in port {}", deviceId);
+        }
+        try {
+            resubmitTableTreatment.setPropertyValue("table", ((short) 0));
+        } catch (Exception e) {
+            log.error("Failed to set extension treatment for resubmit table {}", deviceId);
+        }
+        treatment.extension(resubmitTableTreatment, deviceId);
+
+        sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY);
+    }
+
+    public void installSfcForwardRule(PortPair portPair, PortPair nextPortPair, NshServicePathId nshSpiId,
+            Objective.Operation type) {
+        DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.egress()));
+        MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.egress())).macAddress();
+        Host host = hostService.getHost(HostId.hostId(srcMacAddress));
+        PortNumber port = host.location().port();
+
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionSelectorResolver resolver = handler.behaviour(ExtensionSelectorResolver.class);
+
+        // Prepare selector with nsp, nsi and inport from egress of port pair
+        ExtensionSelector nshSpiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
+        ExtensionSelector nshSiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type());
+        try {
+            nshSpiSelector.setPropertyValue("nshSpi", nshSpiId);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Spi Id for forward rule {}", e.getMessage());
+        }
+        // Decrement the SI
+        nshSi = (short) (nshSi - 1);
+        try {
+            nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id for forward rule {}", e.getMessage());
+        }
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.extension(nshSpiSelector, deviceId);
+        selector.extension(nshSiSelector, deviceId);
+        selector.matchInPort(port);
+
+        DeviceId nextDeviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(nextPortPair.ingress()));
+        if (deviceId.equals(nextDeviceId)) {
+
+            // Treatment with transition
+            TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+            treatment.transition(ENCAP_OUTPUT_TABLE);
+
+            sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY);
+        } else {
+            // Treatment with with transition to send on tunnel
+            ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class);
+            ExtensionTreatment moveC2ToTunId = treatmentResolver
+                    .getExtensionInstruction(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                             .NICIRA_MOV_NSH_C2_TO_TUN_ID.type());
+
+            Device remoteDevice = deviceService.getDevice(nextDeviceId);
+            String url = remoteDevice.annotations().value(SWITCH_CHANNEL_ID);
+            String remoteControllerIp = url.substring(0, url.lastIndexOf(":"));
+            if (remoteControllerIp == null) {
+                log.error("Can't find remote controller of device: {}", nextDeviceId.toString());
+                return;
+            }
+
+            ExtensionTreatment tunnelDsttreatment = treatmentResolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST
+                                                                                              .type());
+            try {
+                tunnelDsttreatment.setPropertyValue("tunnelDst", Ip4Address.valueOf(remoteControllerIp));
+            } catch (Exception e) {
+                log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
+            }
+
+            TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+            treatment.extension(moveC2ToTunId, deviceId);
+            treatment.extension(tunnelDsttreatment, deviceId);
+            treatment.transition(TUNNEL_SEND_TABLE);
+
+            sendSfcRule(selector, treatment, deviceId, type, DEFAULT_FORWARDER_PRIORITY);
+
+            installSfcTunnelSendRule(deviceId, nshSpiId, type);
+            installSfcTunnelReceiveRule(nextDeviceId, nshSpiId, type);
+        }
+    }
+
+    public void installSfcEncapOutputRule(PortPair portPair, NshServicePathId nshSpiId, Objective.Operation type) {
+
+        DeviceId deviceId = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.ingress()));
+        MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress();
+        Host host = hostService.getHost(HostId.hostId(srcMacAddress));
+        PortNumber port = host.location().port();
+
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionSelectorResolver resolver = handler.behaviour(ExtensionSelectorResolver.class);
+
+        // Prepare selector with nsp, nsi and encap eth type
+        ExtensionSelector nshSpiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SPI.type());
+        ExtensionSelector nshSiSelector = resolver.getExtensionSelector(NICIRA_MATCH_NSH_SI.type());
+        ExtensionSelector nshEncapEthTypeSelector = resolver.getExtensionSelector(NICIRA_MATCH_ENCAP_ETH_TYPE.type());
+
+        try {
+            nshSpiSelector.setPropertyValue("nshSpi", nshSpiId);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Spi Id for encap rule {}", e.getMessage());
+        }
+        try {
+            nshSiSelector.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id for encap rule {}", e.getMessage());
+        }
+        try {
+            nshEncapEthTypeSelector.setPropertyValue("encapEthType", ENCAP_ETH_TYPE);
+        } catch (Exception e) {
+            log.error("Failed to set extension selector to match Nsh Si Id {}", deviceId);
+        }
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        selector.extension(nshSpiSelector, deviceId);
+        selector.extension(nshSiSelector, deviceId);
+
+        ExtensionTreatmentResolver treatmentResolver = handler.behaviour(ExtensionTreatmentResolver.class);
+        ExtensionTreatment encapEthSrcTreatment = treatmentResolver
+                .getExtensionInstruction(NICIRA_ENCAP_ETH_SRC.type());
+        ExtensionTreatment encapEthDstTreatment = treatmentResolver
+                .getExtensionInstruction(NICIRA_ENCAP_ETH_DST.type());
+
+        try {
+            encapEthDstTreatment.setPropertyValue("encapEthDst", srcMacAddress);
+        } catch (Exception e) {
+            log.error("Failed to set extension treatment to set encap eth dst {}", deviceId);
+        }
+        // TODO: move from packet source mac address
+        try {
+            encapEthSrcTreatment.setPropertyValue("encapEthSrc", srcMacAddress);
+        } catch (Exception e) {
+            log.error("Failed to set extension treatment to set encap eth src {}", deviceId);
+        }
+
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        treatment.extension(encapEthSrcTreatment, deviceId);
+        treatment.extension(encapEthDstTreatment, deviceId);
+        treatment.setOutput(port);
+
+        sendSfcRule(selector, treatment, deviceId, type, ENCAP_OUTPUT_PRIORITY);
+        forwarderList.add(deviceId);
+    }
+
+    public ConnectPoint installSfcClassifierRules(PortChain portChain, PortPair portPair, NshServicePathId nshSpiId,
+            FiveTuple fiveTuple, Objective.Operation type) {
+
+        DeviceId deviceIdfromPortPair = vtnRscService.getSfToSffMaping(VirtualPortId.portId(portPair.ingress()));
+        MacAddress srcMacAddress = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress())).macAddress();
+        VirtualPort virtualPort = virtualPortService.getPort(VirtualPortId.portId(portPair.ingress()));
+        Host host = hostService.getHost(HostId.hostId(srcMacAddress));
+        PortNumber port = host.location().port();
+
+        DeviceId deviceId = deviceIdfromPortPair;
+
+        // get flow classifiers
+        List<FlowClassifierId> llFlowClassifierList = portChain.flowClassifiers();
+        ListIterator<FlowClassifierId> flowClassifierListIterator = llFlowClassifierList.listIterator();
+
+        while (flowClassifierListIterator.hasNext()) {
+            FlowClassifierId flowclassifierId = flowClassifierListIterator.next();
+            FlowClassifier flowClassifier = flowClassifierService.getFlowClassifier(flowclassifierId);
+
+            if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) {
+                deviceId = vtnRscService.getSfToSffMaping(flowClassifier.srcPort());
+            }
+
+            // Build Traffic selector.
+            TrafficSelector.Builder selector = packClassifierSelector(flowClassifier, fiveTuple);
+
+            if (fiveTuple == null) {
+                // Send the packet to controller
+                log.info("Downloading rule to send packet to controller");
+                TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+                treatment.setOutput(PortNumber.CONTROLLER);
+                sendSfcRule(selector, treatment, deviceId, type, FLOW_CLASSIFIER_PRIORITY);
+                continue;
+            }
+
+            if (deviceId != null && !deviceId.equals(deviceIdfromPortPair)) {
+                // First SF is in another device. Set tunnel ipv4 destination to
+                // treatment
+                Device remoteDevice = deviceService.getDevice(deviceIdfromPortPair);
+                String url = remoteDevice.annotations().value(SWITCH_CHANNEL_ID);
+                String remoteControllerIp = url.substring(0, url.lastIndexOf(":"));
+                if (remoteControllerIp == null) {
+                    log.error("Can't find remote controller of device: {}", deviceIdfromPortPair.toString());
+                    return null;
+                }
+
+                DriverHandler handler = driverService.createHandler(deviceId);
+                ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
+                ExtensionTreatment tunnelDsttreatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+                try {
+                    tunnelDsttreatment.setPropertyValue("tunnelDst", Ip4Address.valueOf(remoteControllerIp));
+                } catch (Exception e) {
+                    log.error("Failed to get extension instruction to set tunnel dst {}", deviceId);
+                }
+
+                TrafficTreatment.Builder treatment = packClassifierTreatment(deviceId, virtualPort, port,
+                                                                             nshSpiId, flowClassifier);
+                treatment.extension(tunnelDsttreatment, deviceId);
+                treatment.transition(TUNNEL_SEND_TABLE);
+                sendSfcRule(selector, treatment, deviceId, type, flowClassifier.priority());
+                classifierList.add(deviceIdfromPortPair);
+
+                installSfcTunnelSendRule(deviceId, nshSpiId, type);
+                installSfcTunnelReceiveRule(deviceIdfromPortPair, nshSpiId, type);
+
+            } else {
+                // classifier and port pair are in the same OVS. So directly
+                // send packet to first port pair
+                TrafficTreatment.Builder treatment = packClassifierTreatment(deviceIdfromPortPair, virtualPort, port,
+                                                                             nshSpiId, flowClassifier);
+                treatment.transition(ENCAP_OUTPUT_TABLE);
+                sendSfcRule(selector, treatment, deviceIdfromPortPair, type, flowClassifier.priority());
+                classifierList.add(deviceIdfromPortPair);
+            }
+        }
+
+        return host.location();
+    }
+
+    /**
+     * Pack Traffic selector.
+     *
+     * @param flowClassifier flow-classifier
+     * @param fiveTuple five tuple info for the packet
+     * @return traffic selector
+     */
+    public TrafficSelector.Builder packClassifierSelector(FlowClassifier flowClassifier, FiveTuple fiveTuple) {
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+        if ((flowClassifier.srcIpPrefix() != null) && (flowClassifier.srcIpPrefix().prefixLength() != 0)) {
+            selector.matchIPSrc(flowClassifier.srcIpPrefix());
+        } else if (fiveTuple != null && fiveTuple.ipSrc() != null) {
+            selector.matchIPSrc(IpPrefix.valueOf(fiveTuple.ipSrc(), 24));
+        }
+
+        if ((flowClassifier.dstIpPrefix() != null) && (flowClassifier.dstIpPrefix().prefixLength() != 0)) {
+            selector.matchIPDst(flowClassifier.dstIpPrefix());
+        } else if (fiveTuple != null && fiveTuple.ipDst() != null) {
+            selector.matchIPDst(IpPrefix.valueOf(fiveTuple.ipDst(), 24));
+        }
+
+        if ((flowClassifier.protocol() != null) && (!flowClassifier.protocol().isEmpty())) {
+            if (flowClassifier.protocol().equalsIgnoreCase("TCP")) {
+                selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_TCP));
+            } else if (flowClassifier.protocol().equalsIgnoreCase("UDP")) {
+                selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP));
+            } else if (flowClassifier.protocol().equalsIgnoreCase("ICMP")) {
+                selector.add(Criteria.matchIPProtocol(IPv4.PROTOCOL_ICMP));
+            }
+        } else if (fiveTuple != null && fiveTuple.protocol() != 0) {
+            selector.add(Criteria.matchIPProtocol(fiveTuple.protocol()));
+        }
+
+        if (((flowClassifier.etherType() != null) && (!flowClassifier.etherType().isEmpty()))
+                && (flowClassifier.etherType().equals("IPv4") || flowClassifier.etherType().equals("IPv6"))) {
+            if (flowClassifier.etherType().equals("IPv4")) {
+                selector.matchEthType(Ethernet.TYPE_IPV4);
+            } else {
+                selector.matchEthType(Ethernet.TYPE_IPV6);
+            }
+        }
+
+        if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) {
+            VirtualPortId vPortId = VirtualPortId.portId(flowClassifier.srcPort().portId());
+            MacAddress macAddress = virtualPortService.getPort(vPortId).macAddress();
+            Host host = hostService.getHost(HostId.hostId(macAddress));
+            selector.matchInPort(host.location().port());
+        }
+
+        // Take the port information from five tuple only when the protocol is
+        // TCP.
+        if (fiveTuple != null && fiveTuple.protocol() == IPv4.PROTOCOL_TCP) {
+            selector.matchTcpSrc(TpPort.tpPort((int) fiveTuple.portSrc().toLong()));
+            selector.matchTcpDst(TpPort.tpPort((int) fiveTuple.portDst().toLong()));
+        } else {
+            // For udp packets take the port information from flow classifier
+            List<TpPort> srcPortRange = new LinkedList<>();
+            List<TpPort> dstPortRange = new LinkedList<>();
+            if ((flowClassifier.minSrcPortRange() != 0) && flowClassifier.maxSrcPortRange() != 0
+                    && flowClassifier.minDstPortRange() != 0 && flowClassifier.maxDstPortRange() != 0) {
+
+                for (int port = flowClassifier.minSrcPortRange(); port <= flowClassifier.maxSrcPortRange(); port++) {
+                    srcPortRange.add(TpPort.tpPort(port));
+                }
+                for (int port = flowClassifier.minDstPortRange(); port <= flowClassifier.maxDstPortRange(); port++) {
+                    dstPortRange.add(TpPort.tpPort(port));
+                }
+            }
+
+            for (TpPort inPort : srcPortRange) {
+                selector.matchUdpSrc(inPort);
+            }
+            for (TpPort outPort : dstPortRange) {
+                selector.matchUdpDst(outPort);
+            }
+        }
+        return selector;
+    }
+
+    /**
+     * Pack traffic treatment.
+     *
+     * @param deviceId device id
+     * @param virtualPort virtual port
+     * @param port port number
+     * @param nshSpi nsh spi
+     * @param flowClassifier flow-classifier
+     * @return traffic treatment
+     */
+    public TrafficTreatment.Builder packClassifierTreatment(DeviceId deviceId, VirtualPort virtualPort,
+            PortNumber port, NshServicePathId nshSpi, FlowClassifier flowClassifier) {
+
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+        // set tunnel id
+        SegmentationId segmentationId = tenantNetworkService.getNetwork(virtualPort.networkId()).segmentationId();
+        treatmentBuilder.add(Instructions.modTunnelId(Long.parseLong(segmentationId.toString())));
+
+        // Set all NSH header fields
+        DriverHandler handler = driverService.createHandler(deviceId);
+        ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
+        ExtensionTreatment nspIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SPI.type());
+        ExtensionTreatment nsiIdTreatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_SI.type());
+        ExtensionTreatment pushNshTreatment = resolver.getExtensionInstruction(NICIRA_PUSH_NSH.type());
+
+        ExtensionTreatment nshCh1Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH1.type());
+        ExtensionTreatment nshCh2Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH2.type());
+        ExtensionTreatment nshCh3Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH3.type());
+        ExtensionTreatment nshCh4Treatment = resolver.getExtensionInstruction(NICIRA_SET_NSH_CH4.type());
+        ExtensionTreatment nshMdTypeTreatment = resolver.getExtensionInstruction(NICIRA_NSH_MDTYPE.type());
+        ExtensionTreatment nshNpTreatment = resolver.getExtensionInstruction(NICIRA_NSH_NP.type());
+
+        try {
+            nshMdTypeTreatment.setPropertyValue("nshMdType", ((byte) 1));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set nshMdType {}", deviceId);
+        }
+        try {
+            nshNpTreatment.setPropertyValue("nshNp", ((byte) 3));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set nshNp {}", deviceId);
+        }
+        try {
+            nspIdTreatment.setPropertyValue("nshSpi", nshSpi);
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set Nsh Spi Id {}", deviceId);
+        }
+        try {
+            nsiIdTreatment.setPropertyValue("nshSi", NshServiceIndex.of(nshSi));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set Nsh Si Id {}", deviceId);
+        }
+        try {
+            nshCh1Treatment.setPropertyValue("nshCh", NshContextHeader.of(1));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set NshCh1 {}", deviceId);
+        }
+        try {
+            nshCh2Treatment.setPropertyValue("nshCh", NshContextHeader.of(Integer.parseInt(segmentationId.toString())));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set NshCh2 {}", deviceId);
+        }
+        try {
+            nshCh3Treatment.setPropertyValue("nshCh", NshContextHeader.of(3));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set NshCh3 {}", deviceId);
+        }
+        try {
+            nshCh4Treatment.setPropertyValue("nshCh", NshContextHeader.of(4));
+        } catch (Exception e) {
+            log.error("Failed to get extension instruction to set NshCh4 {}", deviceId);
+        }
+        treatmentBuilder.extension(pushNshTreatment, deviceId);
+        treatmentBuilder.extension(nshMdTypeTreatment, deviceId);
+        treatmentBuilder.extension(nshNpTreatment, deviceId);
+        treatmentBuilder.extension(nspIdTreatment, deviceId);
+        treatmentBuilder.extension(nsiIdTreatment, deviceId);
+        treatmentBuilder.extension(nshCh1Treatment, deviceId);
+        treatmentBuilder.extension(nshCh2Treatment, deviceId);
+        treatmentBuilder.extension(nshCh3Treatment, deviceId);
+        treatmentBuilder.extension(nshCh4Treatment, deviceId);
+
+        return treatmentBuilder;
+    }
+
+    /**
+     * Get the ControllerId from the device .
+     *
+     * @param device Device
+     * @param devices Devices
+     * @return Controller Id
+     */
+    public DeviceId getControllerId(Device device, Iterable<Device> devices) {
+        for (Device d : devices) {
+            if (d.type() == Device.Type.CONTROLLER && d.id().toString()
+                    .contains(getControllerIpOfSwitch(device))) {
+                return d.id();
+            }
+        }
+        log.info("Can not find controller for device : {}", device.id());
+        return null;
+    }
+
+    /**
+     * Get the ControllerIp from the device .
+     *
+     * @param device Device
+     * @return Controller Ip
+     */
+    public String getControllerIpOfSwitch(Device device) {
+        String url = device.annotations().value(SWITCH_CHANNEL_ID);
+        return url.substring(0, url.lastIndexOf(":"));
+    }
+
+    /**
+     * Send service-function-forwarder to OVS.
+     *
+     * @param selector traffic selector
+     * @param treatment traffic treatment
+     * @param deviceId device id
+     * @param type operation type
+     * @param priority priority of classifier
+     */
+    public void sendSfcRule(TrafficSelector.Builder selector, TrafficTreatment.Builder treatment, DeviceId deviceId,
+            Objective.Operation type, int priority) {
+
+        log.info("Sending sfc flow rule. Selector {}, Treatment {}", selector.toString(),
+                 treatment.toString());
+        ForwardingObjective.Builder objective = DefaultForwardingObjective.builder().withTreatment(treatment.build())
+                .withSelector(selector.build()).fromApp(appId).makePermanent().withFlag(Flag.VERSATILE)
+                .withPriority(priority);
+
+        if (type.equals(Objective.Operation.ADD)) {
+            log.debug("flowClassifierRules-->ADD");
+            flowObjectiveService.forward(deviceId, objective.add());
+        } else {
+            log.debug("flowClassifierRules-->REMOVE");
+            flowObjectiveService.forward(deviceId, objective.remove());
+        }
+    }
+
+    private PortNumber getVxlanPortNumber(DeviceId deviceId) {
+        Iterable<Port> ports = deviceService.getPorts(deviceId);
+        Port vxlanPort = Sets.newHashSet(ports).stream()
+                .filter(p ->!p.number().equals(PortNumber.LOCAL))
+                .filter(p ->p.annotations().value(AnnotationKeys.PORT_NAME)
+                        .startsWith(VXLANPORT_HEAD))
+                .findFirst().get();
+        return vxlanPort.number();
+    }
+}
diff --git a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/manager/impl/SfcManager.java b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/manager/impl/SfcManager.java
index 0d7702c..3292e23 100644
--- a/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/manager/impl/SfcManager.java
+++ b/apps/vtn/sfcmgr/src/main/java/org/onosproject/sfc/manager/impl/SfcManager.java
@@ -20,8 +20,9 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Optional;
+import java.util.ListIterator;
 import java.util.Set;
+import java.util.UUID;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -50,10 +51,7 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.sfc.forwarder.ServiceFunctionForwarderService;
-import org.onosproject.sfc.forwarder.impl.ServiceFunctionForwarderImpl;
-import org.onosproject.sfc.installer.FlowClassifierInstallerService;
-import org.onosproject.sfc.installer.impl.FlowClassifierInstallerImpl;
+import org.onosproject.sfc.installer.impl.SfcFlowRuleInstallerImpl;
 import org.onosproject.sfc.manager.SfcService;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.DistributedSet;
@@ -77,7 +75,6 @@
 import org.onosproject.vtnrsc.VirtualPort;
 import org.onosproject.vtnrsc.VirtualPortId;
 import org.onosproject.vtnrsc.event.VtnRscEvent;
-import org.onosproject.vtnrsc.event.VtnRscEventFeedback;
 import org.onosproject.vtnrsc.event.VtnRscListener;
 import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
 import org.onosproject.vtnrsc.portchain.PortChainService;
@@ -100,7 +97,6 @@
     private String nshSpiIdTopic = "nsh-spi-id";
     private static final String APP_ID = "org.onosproject.app.vtn";
     private static final int SFC_PRIORITY = 1000;
-    private static final int NULL_PORT = 0;
     private static final int MAX_NSH_SPI_ID = 0x7FFFF;
     private static final int MAX_LOAD_BALANCE_ID = 0x20;
 
@@ -131,10 +127,9 @@
     protected SfcPacketProcessor processor = new SfcPacketProcessor();
 
     protected ApplicationId appId;
-    protected ServiceFunctionForwarderService serviceFunctionForwarder;
-    protected FlowClassifierInstallerService flowClassifierInstaller;
     protected IdGenerator nshSpiIdGenerator;
     protected EventuallyConsistentMap<PortChainId, Integer> nshSpiPortChainMap;
+    protected EventuallyConsistentMap<PortChainId, List<FiveTuple>> portChainFiveTupleMap;
     protected DistributedSet<Integer> nshSpiIdFreeList;
 
     private final VtnRscListener vtnRscListener = new InnerVtnRscListener();
@@ -142,24 +137,22 @@
     @Activate
     public void activate() {
         appId = coreService.registerApplication(APP_ID);
-        serviceFunctionForwarder = new ServiceFunctionForwarderImpl(appId);
-        flowClassifierInstaller = new FlowClassifierInstallerImpl(appId);
         nshSpiIdGenerator = coreService.getIdGenerator(nshSpiIdTopic);
 
         vtnRscService.addListener(vtnRscListener);
 
-        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
-                .register(TenantId.class)
-                .register(PortPairId.class)
-                .register(PortPairGroupId.class)
-                .register(FlowClassifierId.class)
-                .register(PortChainId.class);
+        KryoNamespace.Builder serializer = KryoNamespace
+                .newBuilder()
+                .register(PortChainId.class, UUID.class, FiveTuple.class, IpAddress.class, PortNumber.class,
+                          DefaultFiveTuple.class, IpAddress.Version.class, TenantId.class);
 
         nshSpiPortChainMap = storageService.<PortChainId, Integer>eventuallyConsistentMapBuilder()
-                .withName("nshSpiPortChainMap")
-                .withSerializer(serializer)
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
+                .withName("nshSpiPortChainMap").withSerializer(serializer)
+                .withTimestampProvider((k, v) ->new WallClockTimestamp()).build();
+
+        portChainFiveTupleMap = storageService.<PortChainId, List<FiveTuple>>eventuallyConsistentMapBuilder()
+                .withName("portChainFiveTupleMap").withSerializer(serializer)
+                .withTimestampProvider((k, v) ->new WallClockTimestamp()).build();
 
         nshSpiIdFreeList = storageService.<Integer>setBuilder()
                 .withName("nshSpiIdDeletedList")
@@ -186,43 +179,47 @@
         public void event(VtnRscEvent event) {
 
             if (VtnRscEvent.Type.PORT_PAIR_PUT == event.type()) {
-                PortPair portPair = ((VtnRscEventFeedback) event.subject()).portPair();
+                PortPair portPair = event.subject().portPair();
                 onPortPairCreated(portPair);
             } else if (VtnRscEvent.Type.PORT_PAIR_DELETE == event.type()) {
-                PortPair portPair = ((VtnRscEventFeedback) event.subject()).portPair();
+                PortPair portPair = event.subject().portPair();
                 onPortPairDeleted(portPair);
             } else if (VtnRscEvent.Type.PORT_PAIR_UPDATE == event.type()) {
-                PortPair portPair = ((VtnRscEventFeedback) event.subject()).portPair();
+                PortPair portPair = event.subject().portPair();
                 onPortPairDeleted(portPair);
                 onPortPairCreated(portPair);
             } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_PUT == event.type()) {
-                PortPairGroup portPairGroup = ((VtnRscEventFeedback) event.subject()).portPairGroup();
+                PortPairGroup portPairGroup = event.subject().portPairGroup();
                 onPortPairGroupCreated(portPairGroup);
             } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_DELETE == event.type()) {
-                PortPairGroup portPairGroup = ((VtnRscEventFeedback) event.subject()).portPairGroup();
+                PortPairGroup portPairGroup = event.subject().portPairGroup();
                 onPortPairGroupDeleted(portPairGroup);
             } else if (VtnRscEvent.Type.PORT_PAIR_GROUP_UPDATE == event.type()) {
-                PortPairGroup portPairGroup = ((VtnRscEventFeedback) event.subject()).portPairGroup();
+                PortPairGroup portPairGroup = event.subject().portPairGroup();
                 onPortPairGroupDeleted(portPairGroup);
                 onPortPairGroupCreated(portPairGroup);
             } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_PUT == event.type()) {
-                FlowClassifier flowClassifier = ((VtnRscEventFeedback) event.subject()).flowClassifier();
+                FlowClassifier flowClassifier = event.subject().flowClassifier();
                 onFlowClassifierCreated(flowClassifier);
             } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_DELETE == event.type()) {
-                FlowClassifier flowClassifier = ((VtnRscEventFeedback) event.subject()).flowClassifier();
+                FlowClassifier flowClassifier = event.subject().flowClassifier();
                 onFlowClassifierDeleted(flowClassifier);
             } else if (VtnRscEvent.Type.FLOW_CLASSIFIER_UPDATE == event.type()) {
-                FlowClassifier flowClassifier = ((VtnRscEventFeedback) event.subject()).flowClassifier();
+                FlowClassifier flowClassifier = event.subject().flowClassifier();
                 onFlowClassifierDeleted(flowClassifier);
                 onFlowClassifierCreated(flowClassifier);
             } else if (VtnRscEvent.Type.PORT_CHAIN_PUT == event.type()) {
-                PortChain portChain = (PortChain) ((VtnRscEventFeedback) event.subject()).portChain();
+                PortChain portChain = event.subject().portChain();
+                if (portChain.oldPortChain() != null) {
+                    onPortChainDeleted(portChain.oldPortChain());
+                }
                 onPortChainCreated(portChain);
             } else if (VtnRscEvent.Type.PORT_CHAIN_DELETE == event.type()) {
-                PortChain portChain = (PortChain) ((VtnRscEventFeedback) event.subject()).portChain();
+                PortChain portChain = event.subject().portChain();
                 onPortChainDeleted(portChain);
+                portChainFiveTupleMap.remove(portChain.portChainId());
             } else if (VtnRscEvent.Type.PORT_CHAIN_UPDATE == event.type()) {
-                PortChain portChain = (PortChain) ((VtnRscEventFeedback) event.subject()).portChain();
+                PortChain portChain = event.subject().portChain();
                 onPortChainDeleted(portChain);
                 onPortChainCreated(portChain);
             }
@@ -232,58 +229,69 @@
     @Override
     public void onPortPairCreated(PortPair portPair) {
         log.debug("onPortPairCreated");
-        // TODO: Modify forwarding rule on port-pair creation.
+        // Do nothing
     }
 
     @Override
     public void onPortPairDeleted(PortPair portPair) {
         log.debug("onPortPairDeleted");
-        // TODO: Modify forwarding rule on port-pair deletion.
+        // Do nothing
     }
 
     @Override
     public void onPortPairGroupCreated(PortPairGroup portPairGroup) {
         log.debug("onPortPairGroupCreated");
-        // TODO: Modify forwarding rule on port-pair-group creation.
+        // Do nothing
     }
 
     @Override
     public void onPortPairGroupDeleted(PortPairGroup portPairGroup) {
         log.debug("onPortPairGroupDeleted");
-        // TODO: Modify forwarding rule on port-pair-group deletion.
+        // Do nothing
     }
 
     @Override
     public void onFlowClassifierCreated(FlowClassifier flowClassifier) {
         log.debug("onFlowClassifierCreated");
-        // TODO: Modify forwarding rule on flow-classifier creation.
+        // Do nothing
     }
 
     @Override
     public void onFlowClassifierDeleted(FlowClassifier flowClassifier) {
         log.debug("onFlowClassifierDeleted");
-        // TODO: Modify forwarding rule on flow-classifier deletion.
+        // Do nothing
     }
 
     @Override
     public void onPortChainCreated(PortChain portChain) {
         NshServicePathId nshSpi;
-        log.info("onPortChainCreated");
-        if (nshSpiPortChainMap.containsKey(portChain.portChainId())) {
-            nshSpi = NshServicePathId.of(nshSpiPortChainMap.get(portChain.portChainId()));
-        } else {
-            int id = getNextNshSpi();
-            if (id > MAX_NSH_SPI_ID) {
-                log.error("Reached max limit of service path index."
-                        + "Failed to install SFC for port chain {}", portChain.portChainId().toString());
-                return;
-            }
-            nshSpi = NshServicePathId.of(id);
-            nshSpiPortChainMap.put(portChain.portChainId(), new Integer(id));
-        }
+        log.info("On port chain created");
 
+        int spi = getNextNshSpi();
+        if (spi > MAX_NSH_SPI_ID) {
+            log.error("Reached max limit of service path index." + "Failed to install SFC for port chain {}",
+                      portChain.portChainId().toString());
+            return;
+        }
+        nshSpi = NshServicePathId.of(spi);
+        nshSpiPortChainMap.put(portChain.portChainId(), new Integer(spi));
+        if (!portChainFiveTupleMap.containsKey(portChain.portChainId())) {
+            portChainFiveTupleMap.put(portChain.portChainId(), Lists.newArrayList());
+        }
         // Install classifier rule to send the packet to controller
-        flowClassifierInstaller.installFlowClassifier(portChain, nshSpi);
+        SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId);
+        flowRuleInstaller.installFlowClassifier(portChain, nshSpi);
+
+        // Install rules for already identified five tuples.
+        List<FiveTuple> list = portChainFiveTupleMap.get(portChain.portChainId());
+        for (FiveTuple fiveTuple : list) {
+            LoadBalanceId id = loadBalanceSfc(portChain.portChainId(), fiveTuple);
+            // Get nsh service path index
+            nshSpi = NshServicePathId.of(getNshServicePathId(id, spi));
+            // download the required flow rules for classifier and
+            // forwarding
+            flowRuleInstaller.installLoadBalancedFlowRules(portChain, fiveTuple, nshSpi);
+        }
     }
 
     @Override
@@ -295,7 +303,8 @@
 
         int nshSpiId = nshSpiPortChainMap.get(portChain.portChainId());
         // Uninstall classifier rules
-        flowClassifierInstaller.unInstallFlowClassifier(portChain, NshServicePathId.of(nshSpiId));
+        SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId);
+        flowRuleInstaller.unInstallFlowClassifier(portChain, NshServicePathId.of(nshSpiId));
         // remove from nshSpiPortChainMap and add to nshSpiIdFreeList
         nshSpiPortChainMap.remove(portChain.portChainId());
         nshSpiIdFreeList.add(nshSpiId);
@@ -314,9 +323,16 @@
                 processedIdList.add(id);
             }
             nshSpi = NshServicePathId.of(getNshServicePathId(id, nshSpiId));
-            flowClassifierInstaller.unInstallLoadBalancedFlowClassifier(portChain, fiveTuple, nshSpi);
-            serviceFunctionForwarder.unInstallLoadBalancedForwardingRule(portChain.getLoadBalancePath(fiveTuple),
-                                                                         nshSpi);
+            flowRuleInstaller.unInstallLoadBalancedFlowRules(portChain, fiveTuple, nshSpi);
+        }
+
+        // Reset load for all the port pairs
+        List<PortPairGroupId> ppgIdlist = portChain.portPairGroups();
+        ListIterator<PortPairGroupId> ppgIdListIterator = ppgIdlist.listIterator();
+        while (ppgIdListIterator.hasNext()) {
+            PortPairGroupId portPairGroupId = ppgIdListIterator.next();
+            PortPairGroup ppg = portPairGroupService.getPortPairGroup(portPairGroupId);
+            ppg.resetLoad();
         }
     }
 
@@ -388,9 +404,12 @@
                     boolean match = false;
                     // Check whether protocol is set in flow classifier
                     if (flowClassifier.protocol() != null) {
-                        if ((flowClassifier.protocol().equals("TCP") && fiveTuple.protocol() == IPv4.PROTOCOL_TCP) ||
-                                (flowClassifier.protocol().equals("UDP") &&
-                                        fiveTuple.protocol() == IPv4.PROTOCOL_UDP)) {
+                        if ((flowClassifier.protocol().equalsIgnoreCase("TCP")
+                                && fiveTuple.protocol() == IPv4.PROTOCOL_TCP)
+                                || (flowClassifier.protocol().equalsIgnoreCase("UDP")
+                                        && fiveTuple.protocol() == IPv4.PROTOCOL_UDP)
+                                        || (flowClassifier.protocol().equalsIgnoreCase("ICMP")
+                                                && fiveTuple.protocol() == IPv4.PROTOCOL_ICMP)) {
                             match = true;
                         } else {
                             continue;
@@ -459,68 +478,6 @@
         }
 
         /**
-         * Find the load balanced path set it to port chain for the given five tuple.
-         *
-         * @param portChainId port chain id
-         * @param fiveTuple five tuple info
-         * @return load balance id
-         */
-        private LoadBalanceId loadBalanceSfc(PortChainId portChainId, FiveTuple fiveTuple) {
-
-            // Get the port chain
-            PortChain portChain = portChainService.getPortChain(portChainId);
-            List<PortPairId> loadBalancePath = Lists.newArrayList();
-            LoadBalanceId id;
-            int paths = portChain.getLoadBalancePathSize();
-            if (paths >= MAX_LOAD_BALANCE_ID) {
-                log.info("Max limit reached for load balance paths. "
-                        + "Reusing the created path for port chain {} with five tuple {}",
-                        portChainId, fiveTuple);
-                id = LoadBalanceId.of((byte) ((paths + 1) % MAX_LOAD_BALANCE_ID));
-                portChain.addLoadBalancePath(fiveTuple, id, portChain.getLoadBalancePath(id));
-            }
-
-            // Get the list of port pair groups from port chain
-            Iterable<PortPairGroupId> portPairGroups = portChain.portPairGroups();
-            for (final PortPairGroupId portPairGroupId : portPairGroups) {
-                PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
-
-                // Get the list of port pair ids from port pair group.
-                Iterable<PortPairId> portPairs = portPairGroup.portPairs();
-                int minLoad = 0xFFF;
-                PortPairId minLoadPortPairId = null;
-                for (final PortPairId portPairId : portPairs) {
-                    int load = portPairGroup.getLoad(portPairId);
-                    if (load == 0) {
-                        minLoadPortPairId = portPairId;
-                        break;
-                    } else {
-                        // Check the port pair which has min load.
-                        if (load < minLoad) {
-                            minLoad = load;
-                            minLoadPortPairId = portPairId;
-                        }
-                    }
-                }
-                if (minLoadPortPairId != null) {
-                    loadBalancePath.add(minLoadPortPairId);
-                    portPairGroup.addLoad(minLoadPortPairId);
-                }
-            }
-
-            // Check if the path already exists, if not create a new id
-            Optional<LoadBalanceId> output = portChain.matchPath(loadBalancePath);
-            if (output.isPresent()) {
-                id = output.get();
-            } else {
-                id = LoadBalanceId.of((byte) (paths + 1));
-            }
-
-            portChain.addLoadBalancePath(fiveTuple, id, loadBalancePath);
-            return id;
-        }
-
-        /**
          * Get the tenant id for the given mac address.
          *
          * @param mac mac address
@@ -561,7 +518,7 @@
                     TCP tcpPacket = (TCP) ipv4Packet.getPayload();
                     portSrc = tcpPacket.getSourcePort();
                     portDst = tcpPacket.getDestinationPort();
-                } else  if (protocol == IPv4.PROTOCOL_UDP) {
+                } else if (protocol == IPv4.PROTOCOL_UDP) {
                     UDP udpPacket = (UDP) ipv4Packet.getPayload();
                     portSrc = udpPacket.getSourcePort();
                     portDst = udpPacket.getDestinationPort();
@@ -571,7 +528,7 @@
                     // No need to process other packets received by controller.
                     return;
                 }
-            } else if (ethType == Ethernet.TYPE_IPV6) {
+            } else {
                 return;
             }
 
@@ -587,11 +544,11 @@
             PortChainId portChainId = findPortChainFromFiveTuple(fiveTuple);
 
             if (portChainId == null) {
-                log.error("Packet does not match with any classifier");
                 return;
             }
 
             // Once the 5 tuple and port chain are identified, give this input for load balancing
+            addToPortChainIdFiveTupleMap(portChainId, fiveTuple);
             LoadBalanceId id = loadBalanceSfc(portChainId, fiveTuple);
             // Get nsh service path index
             NshServicePathId nshSpi;
@@ -611,11 +568,9 @@
             }
             // download the required flow rules for classifier and forwarding
             // install in OVS.
-            ConnectPoint connectPoint = flowClassifierInstaller.installLoadBalancedFlowClassifier(portChain,
-                                                                                                  fiveTuple, nshSpi);
-            serviceFunctionForwarder.installLoadBalancedForwardingRule(portChain.getLoadBalancePath(fiveTuple),
-                                                                       nshSpi);
-            sendPacket(context, connectPoint);
+            SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl(appId);
+            flowRuleInstaller.installLoadBalancedFlowRules(portChain, fiveTuple, nshSpi);
+            sendPacket(context);
         }
 
         /**
@@ -624,11 +579,13 @@
          * @param context packet context
          * @param connectPoint connect point of first service function
          */
-        private void sendPacket(PacketContext context, ConnectPoint connectPoint) {
+        private void sendPacket(PacketContext context) {
 
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
-            OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
-                                                              context.inPacket().unparsed());
+            ConnectPoint sourcePoint = context.inPacket().receivedFrom();
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(sourcePoint.port()).build();
+            OutboundPacket packet = new DefaultOutboundPacket(sourcePoint.deviceId(), treatment, context.inPacket()
+                    .unparsed());
             packetService.emit(packet);
             log.trace("Sending packet: {}", packet);
         }
@@ -646,4 +603,71 @@
         nshSpiNew = nshSpiNew | id.loadBalanceId();
         return nshSpiNew;
     }
+
+    private void addToPortChainIdFiveTupleMap(PortChainId portChainId, FiveTuple fiveTuple) {
+        List<FiveTuple> list = portChainFiveTupleMap.get(portChainId);
+        list.add(fiveTuple);
+        portChainFiveTupleMap.put(portChainId, list);
+    }
+
+    /**
+     * Find the load balanced path set it to port chain for the given five
+     * tuple.
+     *
+     * @param portChainId port chain id
+     * @param fiveTuple five tuple info
+     * @return load balance id
+     */
+    private LoadBalanceId loadBalanceSfc(PortChainId portChainId, FiveTuple fiveTuple) {
+
+        // Get the port chain
+        PortChain portChain = portChainService.getPortChain(portChainId);
+        List<PortPairId> loadBalancePath = Lists.newArrayList();
+        LoadBalanceId id;
+        int paths = portChain.getLoadBalancePathSize();
+        if (paths >= MAX_LOAD_BALANCE_ID) {
+            log.info("Max limit reached for load balance paths. "
+                    + "Reusing the created path for port chain {} with five tuple {}", portChainId, fiveTuple);
+            id = LoadBalanceId.of((byte) ((paths + 1) % MAX_LOAD_BALANCE_ID));
+            portChain.addLoadBalancePath(fiveTuple, id, portChain.getLoadBalancePath(id));
+        }
+
+        // Get the list of port pair groups from port chain
+        Iterable<PortPairGroupId> portPairGroups = portChain.portPairGroups();
+        for (final PortPairGroupId portPairGroupId : portPairGroups) {
+            PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
+
+            // Get the list of port pair ids from port pair group.
+            Iterable<PortPairId> portPairs = portPairGroup.portPairs();
+            int minLoad = 0xFFF;
+            PortPairId minLoadPortPairId = null;
+            for (final PortPairId portPairId : portPairs) {
+                int load = portPairGroup.getLoad(portPairId);
+                if (load == 0) {
+                    minLoadPortPairId = portPairId;
+                    break;
+                } else {
+                    // Check the port pair which has min load.
+                    if (load < minLoad) {
+                        minLoad = load;
+                        minLoadPortPairId = portPairId;
+                    }
+                }
+            }
+            if (minLoadPortPairId != null) {
+                loadBalancePath.add(minLoadPortPairId);
+                portPairGroup.addLoad(minLoadPortPairId);
+            }
+        }
+
+        // Check if the path already exists, if not create a new id
+        id = portChain.matchPath(loadBalancePath);
+        if (id == null) {
+            id = LoadBalanceId.of((byte) (paths + 1));
+        }
+
+        portChain.addLoadBalancePath(fiveTuple, id, loadBalancePath);
+        return id;
+    }
+
 }
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImplTest.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImplTest.java
deleted file mode 100644
index 06b3585..0000000
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/forwarder/impl/ServiceFunctionForwarderImplTest.java
+++ /dev/null
@@ -1,201 +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.sfc.forwarder.impl;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.Test;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.NshServicePathId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.device.DeviceServiceAdapter;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.HostServiceAdapter;
-import org.onosproject.sfc.util.FlowClassifierAdapter;
-import org.onosproject.sfc.util.FlowObjectiveAdapter;
-import org.onosproject.sfc.util.MockDriverHandler;
-import org.onosproject.sfc.util.PortPairAdapter;
-import org.onosproject.sfc.util.PortPairGroupAdapter;
-import org.onosproject.sfc.util.VirtualPortAdapter;
-import org.onosproject.sfc.util.VtnRscAdapter;
-import org.onosproject.vtnrsc.AllowedAddressPair;
-import org.onosproject.vtnrsc.BindingHostId;
-import org.onosproject.vtnrsc.DefaultPortPair;
-import org.onosproject.vtnrsc.DefaultVirtualPort;
-import org.onosproject.vtnrsc.FixedIp;
-import org.onosproject.vtnrsc.PortPair;
-import org.onosproject.vtnrsc.PortPairId;
-import org.onosproject.vtnrsc.SecurityGroup;
-import org.onosproject.vtnrsc.SubnetId;
-import org.onosproject.vtnrsc.TenantId;
-import org.onosproject.vtnrsc.TenantNetworkId;
-import org.onosproject.vtnrsc.VirtualPort;
-import org.onosproject.vtnrsc.VirtualPortId;
-import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
-import org.onosproject.vtnrsc.portpair.PortPairService;
-import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
-import org.onosproject.vtnrsc.service.VtnRscService;
-import org.onosproject.vtnrsc.virtualport.VirtualPortService;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-public class ServiceFunctionForwarderImplTest {
-
-    FlowObjectiveService flowObjectiveService = new FlowObjectiveAdapter();
-    DeviceService deviceService = new DeviceServiceAdapter();
-    HostService hostService = new HostServiceAdapter();
-    VirtualPortService virtualPortService = new VirtualPortAdapter();
-    VtnRscService vtnRscService = new VtnRscAdapter();
-    PortPairService portPairService = new PortPairAdapter();
-    PortPairGroupService portPairGroupService = new PortPairGroupAdapter();
-    FlowClassifierService flowClassifierService = new FlowClassifierAdapter();
-
-    final DriverService driverService = createMock(DriverService.class);
-
-    DeviceId deviceId = DeviceId.deviceId("of:000000000000001");
-    final TenantId tenantId = TenantId.tenantId("1");
-
-    final DriverHandler driverHandler = new MockDriverHandler();
-
-    private VirtualPort createVirtualPort(VirtualPortId id) {
-        Set<FixedIp> fixedIps;
-        Map<String, String> propertyMap;
-        Set<AllowedAddressPair> allowedAddressPairs;
-        Set<SecurityGroup> securityGroups = Sets.newHashSet();
-
-        String macAddressStr = "fa:12:3e:56:ee:a2";
-        String ipAddress = "10.1.1.1";
-        String tenantNetworkId = "1234567";
-        String subnet = "1212";
-        String hostIdStr = "fa:e2:3e:56:ee:a2";
-        String deviceOwner = "james";
-        propertyMap = Maps.newHashMap();
-        propertyMap.putIfAbsent("deviceOwner", deviceOwner);
-
-        TenantNetworkId networkId = TenantNetworkId.networkId(tenantNetworkId);
-        MacAddress macAddress = MacAddress.valueOf(macAddressStr);
-        BindingHostId bindingHostId = BindingHostId.bindingHostId(hostIdStr);
-        FixedIp fixedIp = FixedIp.fixedIp(SubnetId.subnetId(subnet),
-                                          IpAddress.valueOf(ipAddress));
-        fixedIps = Sets.newHashSet();
-        fixedIps.add(fixedIp);
-
-        allowedAddressPairs = Sets.newHashSet();
-        AllowedAddressPair allowedAddressPair = AllowedAddressPair
-                .allowedAddressPair(IpAddress.valueOf(ipAddress),
-                                    MacAddress.valueOf(macAddressStr));
-        allowedAddressPairs.add(allowedAddressPair);
-
-        VirtualPort d1 = new DefaultVirtualPort(id, networkId, true,
-                                                propertyMap,
-                                                VirtualPort.State.ACTIVE,
-                                                macAddress, tenantId, deviceId,
-                                                fixedIps, bindingHostId,
-                                                allowedAddressPairs,
-                                                securityGroups);
-        return d1;
-    }
-
-    @Test
-    public void testInstallFlowClassifier() {
-
-        PortPairId portPairId1 = PortPairId.of("73333333-fc23-aeb6-f44b-56dc5e2fb3ae");
-        PortPairId portPairId2 = PortPairId.of("74444444-fc23-aeb6-f44b-56dc5e2fb3ae");
-
-        final String ppName1 = "PortPair1";
-        final String ppDescription1 = "PortPair1";
-        final String ingress1 = "d3333333-24fc-4fae-af4b-321c5e2eb3d1";
-        final String egress1 = "a4444444-4a56-2a6e-cd3a-9dee4e2ec345";
-        DefaultPortPair.Builder portPairBuilder = new DefaultPortPair.Builder();
-        PortPair portPair1 = portPairBuilder.setId(portPairId1).setName(ppName1).setTenantId(tenantId)
-                .setDescription(ppDescription1).setIngress(ingress1).setEgress(egress1).build();
-
-        final String ppName2 = "PortPair2";
-        final String ppDescription2 = "PortPair2";
-        final String ingress2 = "d5555555-24fc-4fae-af4b-321c5e2eb3d1";
-        final String egress2 = "a6666666-4a56-2a6e-cd3a-9dee4e2ec345";
-        PortPair portPair2 = portPairBuilder.setId(portPairId2).setName(ppName2).setTenantId(tenantId)
-                .setDescription(ppDescription2).setIngress(ingress2).setEgress(egress2).build();
-
-        ApplicationId appId = new DefaultApplicationId(1, "test");
-        ServiceFunctionForwarderImpl serviceFunctionForwarder = new ServiceFunctionForwarderImpl();
-        serviceFunctionForwarder.virtualPortService = virtualPortService;
-        serviceFunctionForwarder.vtnRscService = vtnRscService;
-        serviceFunctionForwarder.portPairService = portPairService;
-        serviceFunctionForwarder.portPairGroupService = portPairGroupService;
-        serviceFunctionForwarder.flowClassifierService = flowClassifierService;
-        serviceFunctionForwarder.driverService = driverService;
-        serviceFunctionForwarder.hostService = hostService;
-        serviceFunctionForwarder.flowObjectiveService = flowObjectiveService;
-        serviceFunctionForwarder.appId = appId;
-
-        NshServicePathId nshSpiId = NshServicePathId.of(10);
-
-        portPairService.createPortPair(portPair1);
-        portPairService.createPortPair(portPair2);
-
-        List<PortPairId> path = Lists.newArrayList();
-        path.add(portPairId1);
-        path.add(portPairId2);
-
-        List<VirtualPort> virtualPortList = Lists.newArrayList();
-        virtualPortList.add(createVirtualPort(VirtualPortId.portId(egress1)));
-        virtualPortList.add(createVirtualPort(VirtualPortId.portId(ingress2)));
-        virtualPortService.createPorts(virtualPortList);
-
-        expect(driverService.createHandler(deviceId)).andReturn(driverHandler).anyTimes();
-        replay(driverService);
-
-        serviceFunctionForwarder.installLoadBalancedForwardingRule(path, nshSpiId);
-
-        ForwardingObjective forObj = ((FlowObjectiveAdapter) flowObjectiveService).forwardingObjective();
-
-        // Check for Selector
-        assertThat(forObj.selector().getCriterion(Criterion.Type.IN_PORT), instanceOf(PortCriterion.class));
-
-        // Check for treatment
-        List<Instruction> instructions = forObj.treatment().allInstructions();
-        for (Instruction instruction : instructions) {
-            if (instruction.type() == Instruction.Type.OUTPUT) {
-                assertThat(((OutputInstruction) instruction).port(), is(PortNumber.P0));
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImplTest.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImplTest.java
similarity index 61%
rename from apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImplTest.java
rename to apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImplTest.java
index a51ea21..61ad4e8 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/FlowClassifierInstallerImplTest.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/installer/impl/SfcFlowRuleInstallerImplTest.java
@@ -18,48 +18,74 @@
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.Port.Type.COPPER;
 
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Annotations;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.NshServicePathId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
 import org.onosproject.sfc.util.FlowClassifierAdapter;
 import org.onosproject.sfc.util.FlowObjectiveAdapter;
 import org.onosproject.sfc.util.MockDriverHandler;
 import org.onosproject.sfc.util.PortPairAdapter;
 import org.onosproject.sfc.util.PortPairGroupAdapter;
+import org.onosproject.sfc.util.TenantNetworkAdapter;
 import org.onosproject.sfc.util.VirtualPortAdapter;
 import org.onosproject.sfc.util.VtnRscAdapter;
 import org.onosproject.vtnrsc.AllowedAddressPair;
 import org.onosproject.vtnrsc.BindingHostId;
+import org.onosproject.vtnrsc.DefaultFiveTuple;
 import org.onosproject.vtnrsc.DefaultFlowClassifier;
 import org.onosproject.vtnrsc.DefaultPortChain;
 import org.onosproject.vtnrsc.DefaultPortPair;
 import org.onosproject.vtnrsc.DefaultPortPairGroup;
+import org.onosproject.vtnrsc.DefaultTenantNetwork;
 import org.onosproject.vtnrsc.DefaultVirtualPort;
+import org.onosproject.vtnrsc.FiveTuple;
 import org.onosproject.vtnrsc.FixedIp;
 import org.onosproject.vtnrsc.FlowClassifier;
 import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.LoadBalanceId;
+import org.onosproject.vtnrsc.PhysicalNetwork;
 import org.onosproject.vtnrsc.PortChain;
 import org.onosproject.vtnrsc.PortChainId;
 import org.onosproject.vtnrsc.PortPair;
@@ -67,8 +93,10 @@
 import org.onosproject.vtnrsc.PortPairGroupId;
 import org.onosproject.vtnrsc.PortPairId;
 import org.onosproject.vtnrsc.SecurityGroup;
+import org.onosproject.vtnrsc.SegmentationId;
 import org.onosproject.vtnrsc.SubnetId;
 import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.TenantNetwork;
 import org.onosproject.vtnrsc.TenantNetworkId;
 import org.onosproject.vtnrsc.VirtualPort;
 import org.onosproject.vtnrsc.VirtualPortId;
@@ -76,25 +104,30 @@
 import org.onosproject.vtnrsc.portpair.PortPairService;
 import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
 import org.onosproject.vtnrsc.service.VtnRscService;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
 import org.onosproject.vtnrsc.virtualport.VirtualPortService;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-public class FlowClassifierInstallerImplTest {
+public class SfcFlowRuleInstallerImplTest {
 
     FlowObjectiveService flowObjectiveService = new FlowObjectiveAdapter();
-    DeviceService deviceService = new DeviceServiceAdapter();
+    DeviceService deviceService = new DeviceServiceAdapter(createPortList());
+
     HostService hostService = new HostServiceAdapter();
     VirtualPortService virtualPortService = new VirtualPortAdapter();
     VtnRscService vtnRscService = new VtnRscAdapter();
     PortPairService portPairService = new PortPairAdapter();
     PortPairGroupService portPairGroupService = new PortPairGroupAdapter();
     FlowClassifierService flowClassifierService = new FlowClassifierAdapter();
+    TenantNetworkService tenantNetworkService = new TenantNetworkAdapter();
 
     final DriverService driverService = createMock(DriverService.class);
 
+    private final String networkIdStr = "123";
+
     final PortChainId portChainId = PortChainId.of("78888888-fc23-aeb6-f44b-56dc5e2fb3ae");
     final TenantId tenantId = TenantId.tenantId("1");
     final String name = "PortChain";
@@ -115,7 +148,6 @@
     final String ingress = "d3333333-24fc-4fae-af4b-321c5e2eb3d1";
     final String egress = "a4444444-4a56-2a6e-cd3a-9dee4e2ec345";
 
-
     final String ppgName = "PortPairGroup";
     final String ppgDescription = "PortPairGroup";
     final List<PortPairId> portPairList = new LinkedList<PortPairId>();
@@ -127,7 +159,21 @@
 
     final DriverHandler driverHandler = new MockDriverHandler();
 
-    private PortPair createPortPair(PortPairId  ppId) {
+    private List<Port> createPortList() {
+        List<Port> portList = Lists.newArrayList();
+        long sp1 = 1_000_000;
+        ProviderId pid = new ProviderId("of", "foo");
+        ChassisId cid = new ChassisId();
+        Device device = new DefaultDevice(pid, deviceId, SWITCH, "whitebox", "1.1.x", "3.9.1", "43311-12345", cid);
+        Annotations annotations = DefaultAnnotations
+                .builder()
+                .set(AnnotationKeys.PORT_NAME, "vxlan-0.0.0.0").build();
+        Port p1 = new DefaultPort(device, PortNumber.ANY, true, COPPER, sp1, annotations);
+        portList.add(p1);
+        return portList;
+    }
+
+    private PortPair createPortPair(PortPairId ppId) {
         DefaultPortPair.Builder portPairBuilder = new DefaultPortPair.Builder();
         PortPair portPair = portPairBuilder.setId(ppId).setName(ppName).setTenantId(tenantId)
                 .setDescription(ppDescription).setIngress(ingress).setEgress(egress).build();
@@ -193,7 +239,7 @@
         return flowClassifier;
     }
 
-    private VirtualPort createVirtualPort() {
+    private VirtualPort createVirtualPort(VirtualPortId id) {
         Set<FixedIp> fixedIps;
         Map<String, String> propertyMap;
         Set<AllowedAddressPair> allowedAddressPairs;
@@ -201,14 +247,13 @@
 
         String macAddressStr = "fa:12:3e:56:ee:a2";
         String ipAddress = "10.1.1.1";
-        String tenantNetworkId = "1234567";
         String subnet = "1212";
         String hostIdStr = "fa:e2:3e:56:ee:a2";
         String deviceOwner = "james";
         propertyMap = Maps.newHashMap();
         propertyMap.putIfAbsent("deviceOwner", deviceOwner);
 
-        TenantNetworkId networkId = TenantNetworkId.networkId(tenantNetworkId);
+        TenantNetworkId networkId = TenantNetworkId.networkId(networkIdStr);
         MacAddress macAddress = MacAddress.valueOf(macAddressStr);
         BindingHostId bindingHostId = BindingHostId.bindingHostId(hostIdStr);
         FixedIp fixedIp = FixedIp.fixedIp(SubnetId.subnetId(subnet),
@@ -222,7 +267,7 @@
                                     MacAddress.valueOf(macAddressStr));
         allowedAddressPairs.add(allowedAddressPair);
 
-        VirtualPort d1 = new DefaultVirtualPort(id1, networkId, true,
+        VirtualPort d1 = new DefaultVirtualPort(id, networkId, true,
                                                 propertyMap,
                                                 VirtualPort.State.ACTIVE,
                                                 macAddress, tenantId, deviceId,
@@ -236,7 +281,7 @@
     public void testInstallFlowClassifier() {
 
         ApplicationId appId = new DefaultApplicationId(1, "test");
-        FlowClassifierInstallerImpl flowClassifierInstaller = new FlowClassifierInstallerImpl();
+        SfcFlowRuleInstallerImpl flowClassifierInstaller = new SfcFlowRuleInstallerImpl();
         flowClassifierInstaller.virtualPortService = virtualPortService;
         flowClassifierInstaller.vtnRscService = vtnRscService;
         flowClassifierInstaller.portPairService = portPairService;
@@ -261,7 +306,7 @@
         flowClassifierService.createFlowClassifier(fc2);
 
         List<VirtualPort> virtualPortList = Lists.newArrayList();
-        virtualPortList.add(createVirtualPort());
+        virtualPortList.add(createVirtualPort(VirtualPortId.portId(ingress)));
         virtualPortService.createPorts(virtualPortList);
 
         expect(driverService.createHandler(deviceId)).andReturn(driverHandler).anyTimes();
@@ -271,4 +316,102 @@
 
         assertThat(connectPoint, is(HostLocation.NONE));
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testInstallLoadBalancedFlowRules() {
+        ApplicationId appId = new DefaultApplicationId(1, "test");
+        SfcFlowRuleInstallerImpl flowRuleInstaller = new SfcFlowRuleInstallerImpl();
+        flowRuleInstaller.virtualPortService = virtualPortService;
+        flowRuleInstaller.vtnRscService = vtnRscService;
+        flowRuleInstaller.portPairService = portPairService;
+        flowRuleInstaller.portPairGroupService = portPairGroupService;
+        flowRuleInstaller.flowClassifierService = flowClassifierService;
+        flowRuleInstaller.driverService = driverService;
+        flowRuleInstaller.deviceService = deviceService;
+        flowRuleInstaller.hostService = hostService;
+        flowRuleInstaller.flowObjectiveService = flowObjectiveService;
+        flowRuleInstaller.tenantNetworkService = tenantNetworkService;
+        flowRuleInstaller.appId = appId;
+
+        final PortChain portChain = createPortChain();
+
+        final String ppName1 = "PortPair1";
+        final String ppDescription1 = "PortPair1";
+        final String ingress1 = "d3333333-24fc-4fae-af4b-321c5e2eb3d1";
+        final String egress1 = "a4444444-4a56-2a6e-cd3a-9dee4e2ec345";
+        DefaultPortPair.Builder portPairBuilder = new DefaultPortPair.Builder();
+        PortPair portPair1 = portPairBuilder.setId(portPairId1).setName(ppName1).setTenantId(tenantId)
+                .setDescription(ppDescription1).setIngress(ingress1).setEgress(egress1).build();
+
+        final String ppName2 = "PortPair2";
+        final String ppDescription2 = "PortPair2";
+        final String ingress2 = "d5555555-24fc-4fae-af4b-321c5e2eb3d1";
+        final String egress2 = "a6666666-4a56-2a6e-cd3a-9dee4e2ec345";
+        PortPair portPair2 = portPairBuilder.setId(portPairId2).setName(ppName2).setTenantId(tenantId)
+                .setDescription(ppDescription2).setIngress(ingress2).setEgress(egress2).build();
+
+        portPairService.createPortPair(portPair1);
+        portPairService.createPortPair(portPair2);
+
+        FlowClassifier fc1 = createFlowClassifier(flowClassifierId1);
+        FlowClassifier fc2 = createFlowClassifier(flowClassifierId2);
+        flowClassifierService.createFlowClassifier(fc1);
+        flowClassifierService.createFlowClassifier(fc2);
+
+        NshServicePathId nshSpiId = NshServicePathId.of(10);
+        FiveTuple fiveTuple = DefaultFiveTuple.builder().setIpSrc(IpAddress.valueOf("3.3.3.3"))
+                .setIpDst(IpAddress.valueOf("4.4.4.4"))
+                .setPortSrc(PortNumber.portNumber(1500))
+                .setPortDst(PortNumber.portNumber(2000))
+                .setProtocol(IPv4.PROTOCOL_UDP)
+                .setTenantId(TenantId.tenantId("bbb"))
+                .build();
+        LoadBalanceId id = LoadBalanceId.of((byte) 1);
+
+        List<PortPairId> path = Lists.newArrayList();
+        path.add(portPairId1);
+        path.add(portPairId2);
+
+        List<VirtualPort> virtualPortList = Lists.newArrayList();
+        virtualPortList.add(createVirtualPort(VirtualPortId.portId(ingress1)));
+        virtualPortList.add(createVirtualPort(VirtualPortId.portId(egress1)));
+        virtualPortList.add(createVirtualPort(VirtualPortId.portId(ingress2)));
+        virtualPortList.add(createVirtualPort(VirtualPortId.portId(egress2)));
+        virtualPortService.createPorts(virtualPortList);
+
+        portChain.addLoadBalancePath(fiveTuple, id, path);
+
+        String physicalNetworkStr = "1234";
+        String segmentationIdStr = "1";
+        SegmentationId segmentationID = SegmentationId
+                .segmentationId(segmentationIdStr);
+        TenantNetworkId networkid1 = TenantNetworkId.networkId(networkIdStr);
+        PhysicalNetwork physicalNetwork = PhysicalNetwork
+                .physicalNetwork(physicalNetworkStr);
+        TenantNetwork p1 = new DefaultTenantNetwork(networkid1, name, false,
+                                                    TenantNetwork.State.ACTIVE,
+                                                    false, tenantId, false,
+                                                    TenantNetwork.Type.LOCAL,
+                                                    physicalNetwork,
+                                                    segmentationID);
+        tenantNetworkService.createNetworks(Collections.singletonList(p1));
+
+        expect(driverService.createHandler(deviceId)).andReturn(driverHandler).anyTimes();
+        replay(driverService);
+
+        flowRuleInstaller.installLoadBalancedFlowRules(portChain, fiveTuple, nshSpiId);
+
+        ForwardingObjective forObj = ((FlowObjectiveAdapter) flowObjectiveService).forwardingObjective();
+
+        // Check for Selector
+        assertThat(forObj.selector().getCriterion(Criterion.Type.IN_PORT), instanceOf(PortCriterion.class));
+
+        // Check for treatment
+        List<Instruction> instructions = forObj.treatment().allInstructions();
+        for (Instruction instruction : instructions) {
+            if (instruction.type() == Instruction.Type.OUTPUT) {
+                assertThat(((OutputInstruction) instruction).port(), is(PortNumber.P0));
+            }
+        }
+    }
+}
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelector.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelector.java
index a59c37b..6b8ca07f 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelector.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelector.java
@@ -23,6 +23,12 @@
 
 public class MockExtensionSelector implements ExtensionSelector {
 
+    private ExtensionSelectorType type;
+
+    public MockExtensionSelector(ExtensionSelectorType type) {
+        this.type = type;
+    }
+
     @Override
     public <T> void setPropertyValue(String key, T value) throws ExtensionPropertyException {
     }
@@ -48,6 +54,6 @@
 
     @Override
     public ExtensionSelectorType type() {
-        return null;
+        return type;
     }
 }
\ No newline at end of file
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelectorResolver.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelectorResolver.java
index f7d90ad..1c2f46c 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelectorResolver.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionSelectorResolver.java
@@ -43,6 +43,6 @@
 
     @Override
     public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) {
-        return new MockExtensionSelector();
+        return new MockExtensionSelector(type);
     }
 }
\ No newline at end of file
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatment.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatment.java
index 3106e0f..b782133 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatment.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatment.java
@@ -23,6 +23,12 @@
 
 public class MockExtensionTreatment implements ExtensionTreatment {
 
+    private ExtensionTreatmentType type;
+
+    public MockExtensionTreatment(ExtensionTreatmentType type) {
+        this.type = type;
+    }
+
     @Override
     public <T> void setPropertyValue(String key, T value) throws ExtensionPropertyException {
     }
@@ -48,7 +54,7 @@
 
     @Override
     public ExtensionTreatmentType type() {
-        return null;
+        return type;
     }
 
 }
\ No newline at end of file
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatmentResolver.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatmentResolver.java
index 6cbb413..c58804c 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatmentResolver.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/MockExtensionTreatmentResolver.java
@@ -43,7 +43,7 @@
 
     @Override
     public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
-        return new MockExtensionTreatment();
+        return new MockExtensionTreatment(type);
     }
 
 }
\ No newline at end of file
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/TenantNetworkAdapter.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/TenantNetworkAdapter.java
new file mode 100644
index 0000000..f4fa9e8
--- /dev/null
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/TenantNetworkAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.sfc.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.onosproject.vtnrsc.TenantNetwork;
+import org.onosproject.vtnrsc.TenantNetworkId;
+import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Provides implementation of the VtnRsc service.
+ */
+public class TenantNetworkAdapter implements TenantNetworkService {
+
+    private final ConcurrentMap<TenantNetworkId, TenantNetwork> tenantNetworkStore = new ConcurrentHashMap<>();
+
+    @Override
+    public boolean exists(TenantNetworkId networkId) {
+        return tenantNetworkStore.containsKey(networkId);
+    }
+
+    @Override
+    public int getNetworkCount() {
+        return tenantNetworkStore.size();
+    }
+
+    @Override
+    public Iterable<TenantNetwork> getNetworks() {
+        return ImmutableList.copyOf(tenantNetworkStore.values());
+    }
+
+    @Override
+    public TenantNetwork getNetwork(TenantNetworkId networkId) {
+        return tenantNetworkStore.get(networkId);
+    }
+
+    @Override
+    public boolean createNetworks(Iterable<TenantNetwork> networks) {
+        for (TenantNetwork network : networks) {
+            tenantNetworkStore.put(network.id(), network);
+            if (!tenantNetworkStore.containsKey(network.id())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateNetworks(Iterable<TenantNetwork> networks) {
+        return false;
+    }
+
+    @Override
+    public boolean removeNetworks(Iterable<TenantNetworkId> networksIds) {
+        return false;
+    }
+
+}
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/util/VtnConfig.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/util/VtnConfig.java
index 7301a46..e4b0767 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/util/VtnConfig.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/util/VtnConfig.java
@@ -45,6 +45,20 @@
         {
             put("key", "flow");
             put("remote_ip", "flow");
+            put("dst_port", "4790");
+            put("in_nsi", "flow");
+            put("in_nsp", "flow");
+            put("out_nsi", "flow");
+            put("out_nsp", "flow");
+            put("in_nshc1", "flow");
+            put("out_nshc1", "flow");
+            put("in_nshc2", "flow");
+            put("out_nshc2", "flow");
+            put("in_nshc3", "flow");
+            put("out_nshc3", "flow");
+            put("in_nshc4", "flow");
+            put("out_nshc4", "flow");
+            put("exts", "gpe");
         }
     };
     /**
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFlowClassifier.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFlowClassifier.java
index 4f3a5a0..27b3a22 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFlowClassifier.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultFlowClassifier.java
@@ -48,7 +48,7 @@
     private static final String TENANT_ID_NOT_NULL = "Tenant id can not be null.";
     private static final String NAME_NOT_NULL = "Name can not be null.";
     private static final String ETHER_TYPE_NOT_NULL = "Ether Type can not be null.";
-    private static final int DEFAULT_CLASSIFIER_PRIORITY = 0xFFFF;
+    private static final int DEFAULT_CLASSIFIER_PRIORITY = 0xCB20;
 
     /**
      * Constructor to create default flow classifier.
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortChain.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortChain.java
index 879c7ea..9d66de3 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortChain.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortChain.java
@@ -22,7 +22,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -42,11 +41,13 @@
     private final String description;
     private final List<PortPairGroupId> portPairGroupList;
     private final List<FlowClassifierId> flowClassifierList;
+    private final PortChain oldPortChain;
 
     private final Map<FiveTuple, LoadBalanceId> sfcLoadBalanceIdMap = new ConcurrentHashMap<>();
     private final Map<LoadBalanceId, List<PortPairId>> sfcLoadBalancePathMap = new ConcurrentHashMap<>();
     private final Map<LoadBalanceId, List<DeviceId>> sfcClassifiersMap = new ConcurrentHashMap<>();
     private final Map<LoadBalanceId, List<DeviceId>> sfcForwardersMap = new ConcurrentHashMap<>();
+
     /**
      * Default constructor to create port chain.
      *
@@ -58,9 +59,10 @@
      * @param flowClassifierList flow classifier list
      */
     private DefaultPortChain(PortChainId portChainId, TenantId tenantId,
-                             String name, String description,
-                             List<PortPairGroupId> portPairGroupList,
-                             List<FlowClassifierId> flowClassifierList) {
+            String name, String description,
+            List<PortPairGroupId> portPairGroupList,
+            List<FlowClassifierId> flowClassifierList,
+            PortChain portChain) {
 
         this.portChainId = portChainId;
         this.tenantId = tenantId;
@@ -68,6 +70,20 @@
         this.description = description;
         this.portPairGroupList = portPairGroupList;
         this.flowClassifierList = flowClassifierList;
+        this.oldPortChain = portChain;
+    }
+
+    /**
+     * To create port chain for update with old port chain.
+     *
+     * @param newPortChain updated port chain
+     * @param oldPortChain old port chain
+     * @return port chain
+     */
+    public static PortChain create(PortChain newPortChain, PortChain oldPortChain) {
+        return new DefaultPortChain(newPortChain.portChainId(), newPortChain.tenantId(),
+                                    newPortChain.name(), newPortChain.description(),
+                                    newPortChain.portPairGroups(), newPortChain.flowClassifiers(), oldPortChain);
     }
 
     /**
@@ -109,7 +125,7 @@
 
     @Override
     public List<PortPairGroupId> portPairGroups() {
-        return  ImmutableList.copyOf(portPairGroupList);
+        return ImmutableList.copyOf(portPairGroupList);
     }
 
     @Override
@@ -118,6 +134,11 @@
     }
 
     @Override
+    public PortChain oldPortChain() {
+        return oldPortChain;
+    }
+
+    @Override
     public void addLoadBalancePath(FiveTuple fiveTuple, LoadBalanceId id,
                                    List<PortPairId> path) {
         this.sfcLoadBalanceIdMap.put(fiveTuple, id);
@@ -136,14 +157,14 @@
 
     @Override
     public void removeSfcClassifiers(LoadBalanceId id, List<DeviceId> classifierList) {
-        List<DeviceId> list = getSfcClassifiers(id);
+        List<DeviceId> list = sfcClassifiersMap.get(id);
         list.removeAll(classifierList);
         this.sfcForwardersMap.put(id, list);
     }
 
     @Override
     public void removeSfcForwarders(LoadBalanceId id, List<DeviceId> forwarderList) {
-        List<DeviceId> list = getSfcForwarders(id);
+        List<DeviceId> list = sfcForwardersMap.get(id);
         list.removeAll(forwarderList);
         this.sfcForwardersMap.put(id, list);
     }
@@ -192,7 +213,7 @@
     }
 
     @Override
-    public Optional<LoadBalanceId> matchPath(List<PortPairId> path) {
+    public LoadBalanceId matchPath(List<PortPairId> path) {
 
         LoadBalanceId id = null;
         for (Map.Entry<LoadBalanceId, List<PortPairId>> entry : sfcLoadBalancePathMap.entrySet()) {
@@ -202,7 +223,7 @@
                 break;
             }
         }
-        return Optional.of(id);
+        return id;
     }
 
     @Override
@@ -267,6 +288,7 @@
         private String description;
         private List<PortPairGroupId> portPairGroupList;
         private List<FlowClassifierId> flowClassifierList;
+        private PortChain portChain;
 
         @Override
         public Builder setId(PortChainId portChainId) {
@@ -312,7 +334,7 @@
             checkNotNull(portPairGroupList, "Port pair groups cannot be null");
 
             return new DefaultPortChain(portChainId, tenantId, name, description,
-                                        portPairGroupList, flowClassifierList);
+                                        portPairGroupList, flowClassifierList, portChain);
         }
     }
 }
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortPairGroup.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortPairGroup.java
index 9817b0a..af929dd 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortPairGroup.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultPortPairGroup.java
@@ -95,6 +95,13 @@
     }
 
     @Override
+    public void resetLoad() {
+        for (PortPairId portPairId : portPairList) {
+            portPairLoadMap.put(portPairId, new Integer(0));
+        }
+    }
+
+    @Override
     public int getLoad(PortPairId portPairId) {
         return portPairLoadMap.get(portPairId);
     }
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortChain.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortChain.java
index 2906411..c431c7a 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortChain.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortChain.java
@@ -16,7 +16,6 @@
 package org.onosproject.vtnrsc;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 
 import org.onosproject.net.DeviceId;
@@ -74,6 +73,13 @@
     List<FlowClassifierId> flowClassifiers();
 
     /**
+     * Returns the old port chain.
+     *
+     * @return old port chain
+     */
+    PortChain oldPortChain();
+
+    /**
      * Adds a new load balanced path.
      *
      * @param fiveTuple five tuple from the packet
@@ -182,7 +188,7 @@
      * @param path load balanced path
      * @return load balance id if the path matches, null otherwise.
      */
-    Optional<LoadBalanceId> matchPath(List<PortPairId> path);
+    LoadBalanceId matchPath(List<PortPairId> path);
 
     /**
      * Returns whether this port chain is an exact match to the port chain given
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortPairGroup.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortPairGroup.java
index bdcfd1d..0114470 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortPairGroup.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PortPairGroup.java
@@ -67,6 +67,11 @@
     void addLoad(PortPairId portPairId);
 
     /**
+     * Reset the load for all the port pairs in the group.
+     */
+    void resetLoad();
+
+    /**
      * Get the load on the given port pair id.
      *
      * @param portPairId port pair id
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/flowclassifier/impl/FlowClassifierManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/flowclassifier/impl/FlowClassifierManager.java
index 25fca33..5521d2e 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/flowclassifier/impl/FlowClassifierManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/flowclassifier/impl/FlowClassifierManager.java
@@ -15,8 +15,10 @@
  */
 package org.onosproject.vtnrsc.flowclassifier.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.UUID;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -24,6 +26,7 @@
 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.IpPrefix;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.event.AbstractListenerManager;
 import org.onosproject.store.serializers.KryoNamespaces;
@@ -33,8 +36,11 @@
 import org.onosproject.store.service.MultiValuedTimestamp;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.WallClockTimestamp;
-import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.DefaultFlowClassifier;
 import org.onosproject.vtnrsc.FlowClassifier;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.VirtualPortId;
 import org.onosproject.vtnrsc.flowclassifier.FlowClassifierEvent;
 import org.onosproject.vtnrsc.flowclassifier.FlowClassifierListener;
 import org.onosproject.vtnrsc.flowclassifier.FlowClassifierService;
@@ -71,7 +77,8 @@
         KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
                 .register(KryoNamespaces.API)
                 .register(MultiValuedTimestamp.class)
-                .register(FlowClassifier.class);
+                .register(FlowClassifier.class, FlowClassifierId.class, UUID.class, IpPrefix.class,
+                          VirtualPortId.class, DefaultFlowClassifier.class, TenantId.class);
         flowClassifierStore = storageService
                 .<FlowClassifierId, FlowClassifier>eventuallyConsistentMapBuilder()
                 .withName("flowclassifierstore").withSerializer(serializer)
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchain/impl/PortChainManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchain/impl/PortChainManager.java
index 21c848b..21a3d2d 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchain/impl/PortChainManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchain/impl/PortChainManager.java
@@ -19,6 +19,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collections;
+import java.util.UUID;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -28,6 +29,7 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.net.DeviceId;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.onosproject.store.service.EventuallyConsistentMapEvent;
@@ -35,8 +37,15 @@
 import org.onosproject.store.service.MultiValuedTimestamp;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtnrsc.DefaultPortChain;
+import org.onosproject.vtnrsc.FiveTuple;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.LoadBalanceId;
 import org.onosproject.vtnrsc.PortChain;
 import org.onosproject.vtnrsc.PortChainId;
+import org.onosproject.vtnrsc.PortPairGroupId;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.TenantId;
 import org.onosproject.vtnrsc.portchain.PortChainEvent;
 import org.onosproject.vtnrsc.portchain.PortChainListener;
 import org.onosproject.vtnrsc.portchain.PortChainService;
@@ -71,7 +80,9 @@
         KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
                 .register(KryoNamespaces.API)
                 .register(MultiValuedTimestamp.class)
-                .register(PortChain.class);
+                .register(PortChain.class, PortChainId.class, UUID.class, PortPairGroupId.class,
+                          FlowClassifierId.class, FiveTuple.class, LoadBalanceId.class, DeviceId.class,
+                          DefaultPortChain.class, PortPairId.class, TenantId.class);
 
         portChainStore = storageService
                 .<PortChainId, PortChain>eventuallyConsistentMapBuilder()
@@ -118,7 +129,7 @@
 
         portChainStore.put(portChain.portChainId(), portChain);
         if (!portChainStore.containsKey(portChain.portChainId())) {
-            log.debug("The portChain is created failed which identifier was {}", portChain.portChainId()
+            log.error("The portChain created is failed which identifier was {}", portChain.portChainId()
                       .toString());
             return false;
         }
@@ -128,18 +139,20 @@
     @Override
     public boolean updatePortChain(PortChain portChain) {
         checkNotNull(portChain, PORT_CHAIN_NULL);
-
+        PortChain oldPortChain = null;
         if (!portChainStore.containsKey(portChain.portChainId())) {
-            log.debug("The portChain is not exist whose identifier was {} ",
-                      portChain.portChainId().toString());
+            log.warn("The portChain is not exist whose identifier was {} ",
+                     portChain.portChainId().toString());
             return false;
+        } else {
+            oldPortChain = portChainStore.get(portChain.portChainId());
         }
+        PortChain newPortChain = DefaultPortChain.create(portChain, oldPortChain);
+        portChainStore.put(newPortChain.portChainId(), newPortChain);
 
-        portChainStore.put(portChain.portChainId(), portChain);
-
-        if (!portChain.equals(portChainStore.get(portChain.portChainId()))) {
+        if (!newPortChain.equals(portChainStore.get(newPortChain.portChainId()))) {
             log.debug("The portChain is updated failed whose identifier was {} ",
-                      portChain.portChainId().toString());
+                      newPortChain.portChainId().toString());
             return false;
         }
         return true;
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchainsfmap/impl/PortChainSfMapManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchainsfmap/impl/PortChainSfMapManager.java
index 2df0ee6..c769092 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchainsfmap/impl/PortChainSfMapManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portchainsfmap/impl/PortChainSfMapManager.java
@@ -38,6 +38,7 @@
 import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
 import org.slf4j.Logger;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 /**
@@ -86,13 +87,13 @@
         List<PortPairGroupId> portPairGrpList = portChain.portPairGroups();
         ListIterator<PortPairGroupId> listGrpIterator = portPairGrpList.listIterator();
 
-        while (listGrpIterator.next() != null) {
+        while (listGrpIterator.hasNext()) {
             PortPairGroupId portPairGroupId = listGrpIterator.next();
             PortPairGroup portPairGroup = portPairGroupService.getPortPairGroup(portPairGroupId);
             ServiceFunctionGroup sfg = new ServiceFunctionGroup(portPairGroup.name(), portPairGroup.description(),
                                                                 portPairGroup.portPairLoadMap());
             serviceFunctionGroupList.add(sfg);
         }
-        return serviceFunctionGroupList;
+        return ImmutableList.copyOf(serviceFunctionGroupList);
     }
 }
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpair/impl/PortPairManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpair/impl/PortPairManager.java
index 4547764..18105b2 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpair/impl/PortPairManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpair/impl/PortPairManager.java
@@ -19,6 +19,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collections;
+import java.util.UUID;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -35,8 +36,10 @@
 import org.onosproject.store.service.MultiValuedTimestamp;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtnrsc.DefaultPortPair;
 import org.onosproject.vtnrsc.PortPair;
 import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.TenantId;
 import org.onosproject.vtnrsc.portpair.PortPairEvent;
 import org.onosproject.vtnrsc.portpair.PortPairListener;
 import org.onosproject.vtnrsc.portpair.PortPairService;
@@ -72,7 +75,7 @@
         KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
                 .register(KryoNamespaces.API)
                 .register(MultiValuedTimestamp.class)
-                .register(PortPair.class);
+                .register(PortPair.class, PortPairId.class, UUID.class, DefaultPortPair.class, TenantId.class);
 
         portPairStore = storageService.<PortPairId, PortPair>eventuallyConsistentMapBuilder()
                 .withName("portpairstore")
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpairgroup/impl/PortPairGroupManager.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpairgroup/impl/PortPairGroupManager.java
index eca172c..7ef6999 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpairgroup/impl/PortPairGroupManager.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/portpairgroup/impl/PortPairGroupManager.java
@@ -19,6 +19,7 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.Collections;
+import java.util.UUID;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -35,8 +36,11 @@
 import org.onosproject.store.service.MultiValuedTimestamp;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vtnrsc.DefaultPortPairGroup;
 import org.onosproject.vtnrsc.PortPairGroup;
 import org.onosproject.vtnrsc.PortPairGroupId;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.TenantId;
 import org.onosproject.vtnrsc.portpairgroup.PortPairGroupEvent;
 import org.onosproject.vtnrsc.portpairgroup.PortPairGroupListener;
 import org.onosproject.vtnrsc.portpairgroup.PortPairGroupService;
@@ -71,7 +75,8 @@
         KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
                 .register(KryoNamespaces.API)
                 .register(MultiValuedTimestamp.class)
-                .register(PortPairGroup.class);
+                .register(PortPairGroup.class, PortPairGroupId.class, UUID.class, DefaultPortPairGroup.class,
+                          TenantId.class, PortPairId.class);
 
         portPairGroupStore = storageService
                 .<PortPairGroupId, PortPairGroup>eventuallyConsistentMapBuilder()
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainDeviceMapWebResource.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainDeviceMapWebResource.java
new file mode 100644
index 0000000..b391ae4
--- /dev/null
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainDeviceMapWebResource.java
@@ -0,0 +1,91 @@
+/*
+ * 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.vtnweb.resources;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsNotFound;
+
+import java.util.Set;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.LoadBalanceId;
+import org.onosproject.vtnrsc.PortChain;
+import org.onosproject.vtnrsc.PortChainId;
+import org.onosproject.vtnrsc.portchain.PortChainService;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Query and program port chain.
+ */
+
+@Path("portChainDeviceMap")
+public class PortChainDeviceMapWebResource extends AbstractWebResource {
+
+    public static final String PORT_CHAIN_NOT_FOUND = "Port chain not found";
+    public static final String PORT_CHAIN_ID_EXIST = "Port chain exists";
+    public static final String PORT_CHAIN_ID_NOT_EXIST = "Port chain does not exist with identifier";
+
+    private static final String NAME = "name";
+    private static final String ID = "id";
+    private static final String CLASSIFIERS = "classifiers";
+    private static final String FORWARDERS = "forwarders";
+    private static final String LOADBALANCEID = "loadBalanceId";
+
+    /**
+     * Get details of a specified port chain id.
+     *
+     * @param id port chain id
+     * @return 200 OK, 404 if given identifier does not exist
+     */
+    @GET
+    @Path("{chain_id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getPortChainDeviceMap(@PathParam("chain_id") String id) {
+
+        PortChain portChain = nullIsNotFound(get(PortChainService.class).getPortChain(PortChainId.of(id)),
+                                             PORT_CHAIN_NOT_FOUND);
+        ObjectNode result = mapper().createObjectNode();
+        result.set("portChainDeviceMap", encode(portChain, this));
+
+        return ok(result.toString()).build();
+    }
+
+    private ObjectNode encode(PortChain portChain, CodecContext context) {
+        checkNotNull(portChain, "portChain cannot be null");
+        ObjectNode result = context.mapper().createObjectNode();
+        result.put(ID, portChain.portChainId().toString())
+        .put(NAME, portChain.name());
+
+        Set<LoadBalanceId> loadBalanceIds = portChain.getLoadBalancePathMapKeys();
+        for (LoadBalanceId id : loadBalanceIds) {
+            result.put(LOADBALANCEID, id.toString())
+            .put(CLASSIFIERS, portChain.getSfcClassifiers(id).toString())
+            .put(FORWARDERS, portChain.getSfcForwarders(id).toString());
+        }
+        return result;
+    }
+}
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainSfMapWebResource.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainSfMapWebResource.java
new file mode 100644
index 0000000..b644786
--- /dev/null
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/PortChainSfMapWebResource.java
@@ -0,0 +1,71 @@
+/*
+ * 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.vtnweb.resources;
+
+import static org.onlab.util.Tools.nullIsNotFound;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.vtnrsc.PortChainId;
+import org.onosproject.vtnrsc.ServiceFunctionGroup;
+import org.onosproject.vtnrsc.portchainsfmap.PortChainSfMapService;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Query service function and load details by port chain.
+ */
+
+@Path("portChainSfMap")
+public class PortChainSfMapWebResource extends AbstractWebResource {
+
+    public static final String PORT_CHAIN_NOT_FOUND = "Port chain not found";
+    public static final String PORT_CHAIN_ID_EXIST = "Port chain exists";
+    public static final String PORT_CHAIN_ID_NOT_EXIST = "Port chain does not exist with identifier";
+
+    /**
+     * Get service function details of a specified port chain id.
+     *
+     * @param id port chain id
+     * @return 200 OK, 404 if given identifier does not exist
+     */
+    @GET
+    @Path("{chainId}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getPortChainSfMap(@PathParam("chainId") String id) {
+
+        Iterable<ServiceFunctionGroup> serviceFunctionGroups = nullIsNotFound(get(PortChainSfMapService.class)
+                .getServiceFunctions(PortChainId.of(id)),
+                                                                              PORT_CHAIN_NOT_FOUND);
+        ObjectNode result = mapper().createObjectNode();
+        ArrayNode portChainSfMap = result.putArray("portChainSfMap");
+        if (serviceFunctionGroups != null) {
+            for (final ServiceFunctionGroup serviceFunctionGroup : serviceFunctionGroups) {
+                portChainSfMap.add(codec(ServiceFunctionGroup.class).encode(serviceFunctionGroup, this));
+            }
+        }
+        return ok(result.toString()).build();
+    }
+}
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VtnWebApplication.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VtnWebApplication.java
index f22f6df..142be79 100644
--- a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VtnWebApplication.java
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VtnWebApplication.java
@@ -35,7 +35,9 @@
                           PortPairWebResource.class,
                           FloatingIpWebResource.class,
                           RouterWebResource.class,
-                          ClassifierWebResource.class);
+                          ClassifierWebResource.class,
+                          PortChainSfMapWebResource.class,
+                          PortChainDeviceMapWebResource.class);
     }
 }
 
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/FlowClassifierCodec.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/FlowClassifierCodec.java
index 94e02db..e4d4b93 100644
--- a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/FlowClassifierCodec.java
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/FlowClassifierCodec.java
@@ -80,8 +80,10 @@
             resultBuilder.setProtocol(protocol);
         }
 
-        int priority = (json.get(PRIORITY)).asInt();
-        resultBuilder.setPriority(priority);
+        if (json.get(PRIORITY) != null && !(json.get(PRIORITY)).asText().equals("null")) {
+            int priority = (json.get(PRIORITY)).asInt();
+            resultBuilder.setPriority(priority);
+        }
 
         int minSrcPortRange = (json.get(MIN_SRC_PORT_RANGE)).asInt();
         resultBuilder.setMinSrcPortRange(minSrcPortRange);
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/ServiceFunctionCodec.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/ServiceFunctionCodec.java
new file mode 100644
index 0000000..f847b10
--- /dev/null
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/ServiceFunctionCodec.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 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.vtnweb.web;
+
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.vtnrsc.ServiceFunctionGroup;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Service function JSON codec.
+ */
+public final class ServiceFunctionCodec extends JsonCodec<ServiceFunctionGroup> {
+
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String PORT_PAIR_LOAD = "port_pair_load";
+    @Override
+    public ObjectNode encode(ServiceFunctionGroup serviceFunction, CodecContext context) {
+        checkNotNull(serviceFunction, "service cannot be null");
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(NAME, serviceFunction.name())
+                .put(DESCRIPTION, serviceFunction.description())
+                .put(PORT_PAIR_LOAD, serviceFunction.portPairLoadMap().toString());
+        return result;
+    }
+}
diff --git a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/VtnCodecRegistrator.java b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/VtnCodecRegistrator.java
index 3330c25..84ac4a8 100644
--- a/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/VtnCodecRegistrator.java
+++ b/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/web/VtnCodecRegistrator.java
@@ -25,6 +25,7 @@
 import org.onosproject.vtnrsc.PortChain;
 import org.onosproject.vtnrsc.PortPair;
 import org.onosproject.vtnrsc.PortPairGroup;
+import org.onosproject.vtnrsc.ServiceFunctionGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +46,7 @@
         codecService.registerCodec(PortPairGroup.class, new PortPairGroupCodec());
         codecService.registerCodec(FlowClassifier.class, new FlowClassifierCodec());
         codecService.registerCodec(PortChain.class, new PortChainCodec());
-
+        codecService.registerCodec(ServiceFunctionGroup.class, new ServiceFunctionCodec());
         log.info("Started");
     }
 
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java
new file mode 100644
index 0000000..e173051
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.vtnweb.resources;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.codec.CodecService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.FiveTuple;
+import org.onosproject.vtnrsc.FlowClassifierId;
+import org.onosproject.vtnrsc.LoadBalanceId;
+import org.onosproject.vtnrsc.PortChain;
+import org.onosproject.vtnrsc.PortChainId;
+import org.onosproject.vtnrsc.PortPairGroupId;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.TenantId;
+import org.onosproject.vtnrsc.portchain.PortChainService;
+import org.onosproject.vtnweb.web.SfcCodecContext;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * Unit tests for port chain device map REST APIs.
+ */
+public class PortChainDeviceMapResourceTest extends VtnResourceTest {
+
+    final PortChainService portChainService = createMock(PortChainService.class);
+
+    PortChainId portChainId1 = PortChainId.of("78dcd363-fc23-aeb6-f44b-56dc5e2fb3ae");
+    TenantId tenantId1 = TenantId.tenantId("d382007aa9904763a801f68ecf065cf5");
+    private final List<PortPairGroupId> portPairGroupList1 = Lists.newArrayList();
+    private final List<FlowClassifierId> flowClassifierList1 = Lists.newArrayList();
+
+    final MockPortChain portChain1 = new MockPortChain(portChainId1, tenantId1, "portChain1",
+                                                       "Mock port chain", portPairGroupList1,
+                                                       flowClassifierList1);
+
+    /**
+     * Mock class for a port chain.
+     */
+    private static class MockPortChain implements PortChain {
+
+        private final PortChainId portChainId;
+        private final TenantId tenantId;
+        private final String name;
+        private final String description;
+        private final List<PortPairGroupId> portPairGroupList;
+        private final List<FlowClassifierId> flowClassifierList;
+
+        public MockPortChain(PortChainId portChainId, TenantId tenantId,
+                String name, String description,
+                List<PortPairGroupId> portPairGroupList,
+                List<FlowClassifierId> flowClassifierList) {
+
+            this.portChainId = portChainId;
+            this.tenantId = tenantId;
+            this.name = name;
+            this.description = description;
+            this.portPairGroupList = portPairGroupList;
+            this.flowClassifierList = flowClassifierList;
+        }
+
+        @Override
+        public PortChainId portChainId() {
+            return portChainId;
+        }
+
+        @Override
+        public TenantId tenantId() {
+            return tenantId;
+        }
+
+        @Override
+        public String name() {
+            return name;
+        }
+
+        @Override
+        public String description() {
+            return description;
+        }
+
+        @Override
+        public List<PortPairGroupId> portPairGroups() {
+            return ImmutableList.copyOf(portPairGroupList);
+        }
+
+        @Override
+        public List<FlowClassifierId> flowClassifiers() {
+            return ImmutableList.copyOf(flowClassifierList);
+        }
+
+        @Override
+        public boolean exactMatch(PortChain portChain) {
+            return this.equals(portChain) &&
+                    Objects.equals(this.portChainId, portChain.portChainId()) &&
+                    Objects.equals(this.tenantId, portChain.tenantId());
+        }
+
+        @Override
+        public void addLoadBalancePath(FiveTuple fiveTuple, LoadBalanceId id, List<PortPairId> path) {
+        }
+
+        @Override
+        public LoadBalanceId getLoadBalanceId(FiveTuple fiveTuple) {
+            return null;
+        }
+
+        @Override
+        public Set<FiveTuple> getLoadBalanceIdMapKeys() {
+            return null;
+        }
+
+        @Override
+        public List<PortPairId> getLoadBalancePath(LoadBalanceId id) {
+            return null;
+        }
+
+        @Override
+        public List<PortPairId> getLoadBalancePath(FiveTuple fiveTuple) {
+            return null;
+        }
+
+        @Override
+        public LoadBalanceId matchPath(List<PortPairId> path) {
+            return null;
+        }
+
+        @Override
+        public int getLoadBalancePathSize() {
+            return 0;
+        }
+
+        @Override
+        public void addSfcClassifiers(LoadBalanceId id, List<DeviceId> classifierList) {
+        }
+
+        @Override
+        public void addSfcForwarders(LoadBalanceId id, List<DeviceId> forwarderList) {
+        }
+
+        @Override
+        public void removeSfcClassifiers(LoadBalanceId id, List<DeviceId> classifierList) {
+        }
+
+        @Override
+        public void removeSfcForwarders(LoadBalanceId id, List<DeviceId> forwarderList) {
+        }
+
+        @Override
+        public List<DeviceId> getSfcClassifiers(LoadBalanceId id) {
+            DeviceId deviceId1 = DeviceId.deviceId("of:000000000000001");
+            List<DeviceId> classifierList = Lists.newArrayList();
+            classifierList.add(deviceId1);
+            return classifierList;
+        }
+
+        @Override
+        public List<DeviceId> getSfcForwarders(LoadBalanceId id) {
+            DeviceId deviceId1 = DeviceId.deviceId("of:000000000000002");
+            DeviceId deviceId2 = DeviceId.deviceId("of:000000000000003");
+            List<DeviceId> forwarderList = Lists.newArrayList();
+            forwarderList.add(deviceId1);
+            forwarderList.add(deviceId2);
+            return forwarderList;
+        }
+
+        @Override
+        public Set<LoadBalanceId> getLoadBalancePathMapKeys() {
+            LoadBalanceId id = LoadBalanceId.of((byte) 1);
+            Set<LoadBalanceId> set = new HashSet<LoadBalanceId>();
+            set.add(id);
+            return set;
+        }
+
+        @Override
+        public PortChain oldPortChain() {
+            return null;
+        }
+    }
+
+    /**
+     * Sets up the global values for all the tests.
+     */
+    @Before
+    public void setUpTest() {
+        SfcCodecContext context = new SfcCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+        .add(PortChainService.class, portChainService)
+        .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+    }
+
+    /**
+     * Cleans up.
+     */
+    @After
+    public void tearDownTest() {
+    }
+
+    /**
+     * Tests the result of a rest api GET for port chain id.
+     */
+    @Test
+    public void testGetPortChainDeviceMap() {
+
+        expect(portChainService.getPortChain(anyObject())).andReturn(portChain1).anyTimes();
+        replay(portChainService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("portChainDeviceMap/1278dcd4-459f-62ed-754b-87fc5e4a6751").request()
+                .get(String.class);
+        final JsonObject result = Json.parse(response).asObject();
+        assertThat(result, notNullValue());
+        assertThat(result.names().get(0), is("portChainDeviceMap"));
+
+    }
+}
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
index deac05f..8e96a3f 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
@@ -15,10 +15,29 @@
  */
 package org.onosproject.vtnweb.resources;
 
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -38,28 +57,10 @@
 import org.onosproject.vtnrsc.portchain.PortChainService;
 import org.onosproject.vtnweb.web.SfcCodecContext;
 
-import javax.ws.rs.NotFoundException;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 /**
  * Unit tests for port chain REST APIs.
@@ -165,7 +166,7 @@
         }
 
         @Override
-        public Optional<LoadBalanceId> matchPath(List<PortPairId> path) {
+        public LoadBalanceId matchPath(List<PortPairId> path) {
             return null;
         }
 
@@ -204,6 +205,11 @@
         public Set<LoadBalanceId> getLoadBalancePathMapKeys() {
             return null;
         }
+
+        @Override
+        public PortChain oldPortChain() {
+            return null;
+        }
     }
 
     /**
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java
new file mode 100644
index 0000000..b95817c
--- /dev/null
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.vtnweb.resources;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.ws.rs.client.WebTarget;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.rest.BaseResource;
+import org.onosproject.codec.CodecService;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.ServiceFunctionGroup;
+import org.onosproject.vtnrsc.portchainsfmap.PortChainSfMapService;
+import org.onosproject.vtnweb.web.SfcCodecContext;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.Lists;
+
+/**
+ * Unit tests for port chain sf map REST APIs.
+ */
+public class PortChainSfMapResourceTest extends VtnResourceTest {
+
+    final PortChainSfMapService portChainSfMapService = createMock(PortChainSfMapService.class);
+
+    String name1 = "Firewall";
+    String description1 = "Firewall service function";
+    Map<PortPairId, Integer> portPairLoadMap1 = new ConcurrentHashMap<>();
+
+    ServiceFunctionGroup serviceFunction1 = new ServiceFunctionGroup(name1, description1,
+                                                                     portPairLoadMap1);
+
+    /**
+     * Sets up the global values for all the tests.
+     */
+    @Before
+    public void setUpTest() {
+        SfcCodecContext context = new SfcCodecContext();
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(PortChainSfMapService.class, portChainSfMapService)
+                .add(CodecService.class, context.codecManager());
+        BaseResource.setServiceDirectory(testDirectory);
+    }
+
+    /**
+     * Cleans up.
+     */
+    @After
+    public void tearDownTest() {
+    }
+
+    /**
+     * Tests the result of a rest api GET for port chain id.
+     */
+    @Test
+    public void testGetPortChainId() {
+
+        final List<ServiceFunctionGroup> serviceFunctions = Lists.newArrayList();
+        serviceFunctions.add(serviceFunction1);
+
+        expect(portChainSfMapService.getServiceFunctions(anyObject())).andReturn(serviceFunctions).anyTimes();
+        replay(portChainSfMapService);
+
+        final WebTarget wt = target();
+        final String response = wt.path("portChainSfMap/1278dcd4-459f-62ed-754b-87fc5e4a6751").request()
+                .get(String.class);
+        final JsonObject result = Json.parse(response).asObject();
+        assertThat(result, notNullValue());
+        assertThat(result.names().get(0), is("portChainSfMap"));
+    }
+}
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
index c33d929..94aab56 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
@@ -136,6 +136,10 @@
                     Objects.equals(this.portPairGroupId, portPairGroup.portPairGroupId()) &&
                     Objects.equals(this.tenantId, portPairGroup.tenantId());
         }
+
+        @Override
+        public void resetLoad() {
+        }
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/ExtensionSelectorType.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/ExtensionSelectorType.java
index 6952224..dad75cb 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/ExtensionSelectorType.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/ExtensionSelectorType.java
@@ -38,6 +38,7 @@
         NICIRA_MATCH_NSH_CH2(3),
         NICIRA_MATCH_NSH_CH3(4),
         NICIRA_MATCH_NSH_CH4(5),
+        NICIRA_MATCH_ENCAP_ETH_TYPE(6),
         OFDPA_MATCH_VLAN_VID(16),
         BMV2_MATCH_PARAMS(128);
 
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
index f87274e..9550597 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionTreatmentType.java
@@ -38,14 +38,29 @@
         NICIRA_MOV_ARP_SPA_TO_TPA(3),
         NICIRA_MOV_ETH_SRC_TO_DST(4),
         NICIRA_MOV_IP_SRC_TO_DST(5),
+        NICIRA_MOV_NSH_C1_TO_C1(6),
+        NICIRA_MOV_NSH_C2_TO_C2(7),
+        NICIRA_MOV_NSH_C3_TO_C3(8),
+        NICIRA_MOV_NSH_C4_TO_C4(9),
+        NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST(10),
+        NICIRA_MOV_TUN_ID_TO_TUN_ID(11),
+        NICIRA_MOV_NSH_C2_TO_TUN_ID(12),
         NICIRA_RESUBMIT_TABLE(14),
-        NICIRA_SET_NSH_SPI(32),
-        NICIRA_SET_NSH_SI(33),
-        NICIRA_SET_NSH_CH1(34),
-        NICIRA_SET_NSH_CH2(35),
-        NICIRA_SET_NSH_CH3(36),
-        NICIRA_SET_NSH_CH4(37),
+        NICIRA_PUSH_NSH(38),
+        NICIRA_POP_NSH(39),
         OFDPA_SET_VLAN_ID(64),
+        NICIRA_TUN_GPE_NP(111),
+        NICIRA_SET_NSH_SPI(113),
+        NICIRA_SET_NSH_SI(114),
+        NICIRA_SET_NSH_CH1(115),
+        NICIRA_SET_NSH_CH2(116),
+        NICIRA_SET_NSH_CH3(117),
+        NICIRA_SET_NSH_CH4(118),
+        NICIRA_NSH_MDTYPE(119),
+        NICIRA_NSH_NP(120),
+        NICIRA_ENCAP_ETH_SRC(121),
+        NICIRA_ENCAP_ETH_DST(122),
+        NICIRA_ENCAP_ETH_TYPE(123),
         BMV2_ACTION(128);
 
         private ExtensionTreatmentType type;
diff --git a/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
index d9ce4e5..9ffa2bd 100644
--- a/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
@@ -15,7 +15,8 @@
  */
 package org.onosproject.net.device;
 
-import com.google.common.collect.FluentIterable;
+import java.util.Collections;
+import java.util.List;
 
 import org.onosproject.net.Device;
 import org.onosproject.net.Device.Type;
@@ -24,13 +25,31 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 
-import java.util.Collections;
-import java.util.List;
+import com.google.common.collect.FluentIterable;
 
 /**
  * Test adapter for device service.
  */
 public class DeviceServiceAdapter implements DeviceService {
+
+    private List<Port> portList;
+
+    /**
+     * Constructor with port list.
+     *
+     * @param portList port list
+     */
+    public DeviceServiceAdapter(List<Port> portList) {
+        this.portList = portList;
+    }
+
+    /**
+     * Default constructor.
+     */
+    public DeviceServiceAdapter() {
+
+    }
+
     @Override
     public int getDeviceCount() {
         return 0;
@@ -59,7 +78,7 @@
 
     @Override
     public List<Port> getPorts(DeviceId deviceId) {
-        return Collections.emptyList();
+        return portList;
     }
 
     @Override
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/DefaultMoveExtensionTreatment.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/DefaultMoveExtensionTreatment.java
index d305e08..b1ba927 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/DefaultMoveExtensionTreatment.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/DefaultMoveExtensionTreatment.java
@@ -38,7 +38,6 @@
     private ExtensionTreatmentType type;
 
     private final KryoNamespace appKryo = new KryoNamespace.Builder()
-            .register(byte[].class)
             .register(Map.class)
             .build("DefaultMoveExtensionTreatment");
 
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthDst.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthDst.java
new file mode 100644
index 0000000..1f98825
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthDst.java
@@ -0,0 +1,101 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.store.serializers.MacAddressSerializer;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira EncapEthDst extension instruction to set encapsulated eth destination.
+ */
+public class NiciraEncapEthDst extends AbstractExtension implements ExtensionTreatment {
+
+    private MacAddress encapEthDst;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder()
+    .register(new MacAddressSerializer(), MacAddress.class).register(byte[].class).build();;
+
+    /**
+     * Creates a new nshEncapEthDst instruction.
+     */
+    NiciraEncapEthDst() {
+    }
+
+    /**
+     * Creates a new encapEthDst instruction with given mac address.
+     *
+     * @param encapEthDst encapsulated ethernet destination
+     */
+    public NiciraEncapEthDst(MacAddress encapEthDst) {
+        this.encapEthDst = encapEthDst;
+    }
+
+    /**
+     * Gets the encapEthDst.
+     *
+     * @return encapEthDst
+     */
+    public MacAddress encapEthDst() {
+        return encapEthDst;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        encapEthDst = appKryo.deserialize(data);
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(encapEthDst);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(encapEthDst);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraEncapEthDst) {
+            NiciraEncapEthDst that = (NiciraEncapEthDst) obj;
+            return Objects.equals(encapEthDst, that.encapEthDst);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("encapEthDst", encapEthDst).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthSrc.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthSrc.java
new file mode 100644
index 0000000..7ecc1e8
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthSrc.java
@@ -0,0 +1,101 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.packet.MacAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.store.serializers.MacAddressSerializer;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira EncapEthSrc extension instruction to set encapsulated eth source.
+ */
+public class NiciraEncapEthSrc extends AbstractExtension implements ExtensionTreatment {
+
+    private MacAddress encapEthSrc;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder()
+    .register(new MacAddressSerializer(), MacAddress.class).register(byte[].class).build();;
+
+    /**
+     * Creates a new nshEncapEthSrc instruction.
+     */
+    NiciraEncapEthSrc() {
+    }
+
+    /**
+     * Creates a new encapEthSrc instruction with given mac address.
+     *
+     * @param encapEthSrc encapsulated ethernet source
+     */
+    public NiciraEncapEthSrc(MacAddress encapEthSrc) {
+        this.encapEthSrc = encapEthSrc;
+    }
+
+    /**
+     * Gets the encapEthSrc.
+     *
+     * @return encapEthSrc
+     */
+    public MacAddress encapEthSrc() {
+        return encapEthSrc;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        encapEthSrc = appKryo.deserialize(data);
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(encapEthSrc);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(encapEthSrc);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraEncapEthSrc) {
+            NiciraEncapEthSrc that = (NiciraEncapEthSrc) obj;
+            return Objects.equals(encapEthSrc, that.encapEthSrc);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("encapEthSrc", encapEthSrc).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthType.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthType.java
new file mode 100644
index 0000000..8b7f194
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraEncapEthType.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira EncapEthType extension instruction to set encapsulated eth type.
+ */
+public class NiciraEncapEthType extends AbstractExtension implements ExtensionTreatment {
+
+    private short encapEthType;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new nshEncapEthType instruction.
+     */
+    NiciraEncapEthType() {
+        encapEthType = (short) 0;
+    }
+
+    /**
+     * Creates a new nshEncapEthType instruction with given eth type.
+     *
+     * @param encapEthType encapsulated ethernet type
+     */
+    public NiciraEncapEthType(short encapEthType) {
+        this.encapEthType = encapEthType;
+    }
+
+    /**
+     * Gets the encapEthType.
+     *
+     * @return encapEthType
+     */
+    public short encapEthType() {
+        return encapEthType;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_TYPE.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        encapEthType = (short) (appKryo.deserialize(data));
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(encapEthType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(encapEthType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraEncapEthType) {
+            NiciraEncapEthType that = (NiciraEncapEthType) obj;
+            return Objects.equals(encapEthType, that.encapEthType);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("encapEthType", encapEthType).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionSelectorInterpreter.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionSelectorInterpreter.java
index 57f0a67..4bd1aa5 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionSelectorInterpreter.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionSelectorInterpreter.java
@@ -18,13 +18,22 @@
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onosproject.codec.CodecContext;
+import org.onosproject.net.NshServiceIndex;
+import org.onosproject.net.NshServicePathId;
 import org.onosproject.net.behaviour.ExtensionSelectorResolver;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 import org.onosproject.net.flow.criteria.ExtensionSelector;
 import org.onosproject.net.flow.criteria.ExtensionSelectorType;
 import org.onosproject.openflow.controller.ExtensionSelectorInterpreter;
 import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmEncapEthType;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNsi;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNsp;
+import org.projectfloodlight.openflow.types.U16;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U8;
 
 /**
  * Interpreter for Nicira OpenFlow selector extensions.
@@ -53,17 +62,28 @@
         if (extensionSelectorType.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_CH4.type())) {
             return true;
         }
+        if (extensionSelectorType.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE
+                                         .type())) {
+            return true;
+        }
         return false;
     }
 
     @Override
     public OFOxm<?> mapSelector(OFFactory factory, ExtensionSelector extensionSelector) {
         ExtensionSelectorType type = extensionSelector.type();
+
         if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SPI.type())) {
-            // TODO
+            NiciraMatchNshSpi niciraNshSpi = (NiciraMatchNshSpi) extensionSelector;
+            return factory.oxms().nsp(U32.of(niciraNshSpi.nshSpi().servicePathId()));
         }
         if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SI.type())) {
-            // TODO
+            NiciraMatchNshSi niciraNshSi = (NiciraMatchNshSi) extensionSelector;
+            return factory.oxms().nsi(U8.of(niciraNshSi.nshSi().serviceIndex()));
+        }
+        if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE.type())) {
+            NiciraMatchEncapEthType niciraEncapEthType = (NiciraMatchEncapEthType) extensionSelector;
+            return factory.oxms().encapEthType(U16.of(niciraEncapEthType.encapEthType()));
         }
         if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_CH1.type())) {
             // TODO
@@ -82,6 +102,20 @@
 
     @Override
     public ExtensionSelector mapOxm(OFOxm<?> oxm) {
+
+        if (oxm.getMatchField() == MatchField.NSP) {
+            OFOxmNsp oxmField = (OFOxmNsp) oxm;
+            return new NiciraMatchNshSpi(NshServicePathId.of(oxmField.getValue().getRaw()));
+        }
+        if (oxm.getMatchField() == MatchField.NSI) {
+            OFOxmNsi oxmField = (OFOxmNsi) oxm;
+            return new NiciraMatchNshSi(NshServiceIndex.of(oxmField.getValue().getRaw()));
+        }
+        if (oxm.getMatchField() == MatchField.ENCAP_ETH_TYPE) {
+            OFOxmEncapEthType oxmField = (OFOxmEncapEthType) oxm;
+            return new NiciraMatchEncapEthType(oxmField.getValue().getRaw());
+        }
+
         return null;
     }
 
@@ -93,6 +127,9 @@
         if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_SI.type())) {
             return new NiciraMatchNshSi();
         }
+        if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE.type())) {
+            return new NiciraMatchEncapEthType();
+        }
         if (type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_CH1.type())
                 || type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_CH2.type())
                 || type.equals(ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_NSH_CH3.type())
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
index 55f0c3e..3a0778e 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraExtensionTreatmentInterpreter.java
@@ -16,9 +16,14 @@
 
 package org.onosproject.driver.extensions;
 
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+
 import org.onlab.packet.Ip4Address;
 import org.onosproject.codec.CodecContext;
+import org.onosproject.net.NshContextHeader;
+import org.onosproject.net.NshServiceIndex;
+import org.onosproject.net.NshServicePathId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
@@ -32,13 +37,29 @@
 import org.projectfloodlight.openflow.protocol.action.OFActionNicira;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraMove;
 import org.projectfloodlight.openflow.protocol.action.OFActionNiciraResubmit;
+import org.projectfloodlight.openflow.protocol.action.OFActionNiciraResubmitTable;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmEncapEthDst;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmEncapEthSrc;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmEncapEthType;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshC1;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshC2;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshC3;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshC4;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshMdtype;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNshNp;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNsi;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmNsp;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmTunGpeNp;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmTunnelIpv4Dst;
 import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.U16;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U8;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.util.Tools.nullIsIllegal;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
  * Interpreter for Nicira OpenFlow treatment extensions.
@@ -52,8 +73,18 @@
     private static final int SRC_ETH = 0x00000406;
     private static final int SRC_IP = 0x00000e04;
 
+    private static final int NSH_C1 = 0x0001e604;
+    private static final int NSH_C2 = 0x0001e804;
+    private static final int NSH_C3 = 0x0001ea04;
+    private static final int NSH_C4 = 0x0001ec04;
+    private static final int TUN_IPV4_DST = 0x00014004;
+    private static final int TUN_ID = 0x12008;
+
     private static final int SUB_TYPE_RESUBMIT = 1;
+    private static final int SUB_TYPE_RESUBMIT_TABLE = 14;
     private static final int SUB_TYPE_MOVE = 6;
+    private static final int SUB_TYPE_PUSH_NSH = 38;
+    private static final int SUB_TYPE_POP_NSH = 39;
 
     private static final String TUNNEL_DST = "tunnelDst";
     private static final String RESUBMIT = "resubmit";
@@ -115,6 +146,59 @@
                 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_IP_SRC_TO_DST.type())) {
             return true;
         }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_TYPE
+                .type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP.type())) {
+            return true;
+        }
+        if (extensionTreatmentType
+                .equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C1_TO_C1.type())) {
+            return true;
+        }
+        if (extensionTreatmentType
+                .equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_C2.type())) {
+            return true;
+        }
+        if (extensionTreatmentType
+                .equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C3_TO_C3.type())) {
+            return true;
+        }
+        if (extensionTreatmentType
+                .equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C4_TO_C4.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes
+                                          .NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST.type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_TUN_ID_TO_TUN_ID
+                .type())) {
+            return true;
+        }
+        if (extensionTreatmentType.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_TUN_ID
+                                          .type())) {
+            return true;
+        }
         return false;
     }
 
@@ -136,10 +220,75 @@
             return factory.actions().niciraResubmitTable((int) resubmitTable.inPort().toLong(),
                                                          resubmitTable.table());
         }
+
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SPI.type())) {
+            NiciraSetNshSpi niciraNshSpi = (NiciraSetNshSpi) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nsp(U32.of(niciraNshSpi.nshSpi().servicePathId())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_SI.type())) {
+            NiciraSetNshSi niciraNshSi = (NiciraSetNshSi) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nsi(U8.of(niciraNshSi.nshSi().serviceIndex())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH1.type())) {
+            NiciraSetNshContextHeader niciraNshch = (NiciraSetNshContextHeader) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshC1(U32.of(niciraNshch.nshCh().nshContextHeader())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH2.type())) {
+            NiciraSetNshContextHeader niciraNshch = (NiciraSetNshContextHeader) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshC2(U32.of(niciraNshch.nshCh().nshContextHeader())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH3.type())) {
+            NiciraSetNshContextHeader niciraNshch = (NiciraSetNshContextHeader) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshC3(U32.of(niciraNshch.nshCh().nshContextHeader())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH4.type())) {
+            NiciraSetNshContextHeader niciraNshch = (NiciraSetNshContextHeader) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshC4(U32.of(niciraNshch.nshCh().nshContextHeader())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE.type())) {
+            NiciraNshMdType niciraNshMdType = (NiciraNshMdType) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshMdtype(U8.of(niciraNshMdType.nshMdType())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP.type())) {
+            NiciraNshNp niciraNshNp = (NiciraNshNp) extensionTreatment;
+            return factory.actions().setField(factory.oxms().nshNp(U8.of(niciraNshNp.nshNp())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC.type())) {
+            NiciraEncapEthSrc niciraEncapEthSrc = (NiciraEncapEthSrc) extensionTreatment;
+            return factory.actions().setField(factory.oxms().encapEthSrc(MacAddress.of(niciraEncapEthSrc.encapEthSrc()
+                    .toBytes())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST.type())) {
+            NiciraEncapEthDst niciraEncapEthDst = (NiciraEncapEthDst) extensionTreatment;
+            return factory.actions().setField(factory.oxms().encapEthDst(MacAddress.of(niciraEncapEthDst.encapEthDst()
+                    .toBytes())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_TYPE.type())) {
+            NiciraEncapEthType niciraEncapEthType = (NiciraEncapEthType) extensionTreatment;
+            return factory.actions().setField(factory.oxms().encapEthType(U16.of(niciraEncapEthType.encapEthType())));
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH.type())) {
+            return factory.actions().niciraPushNsh();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH.type())) {
+            return factory.actions().niciraPopNsh();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP.type())) {
+            NiciraTunGpeNp niciraTunGpeNp = (NiciraTunGpeNp) extensionTreatment;
+            return factory.actions().setField(factory.oxms().tunGpeNp(U8.of(niciraTunGpeNp.tunGpeNp())));
+        }
         if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_ARP_SHA_TO_THA.type())
                 || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_ARP_SPA_TO_TPA.type())
                 || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_ETH_SRC_TO_DST.type())
-                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_IP_SRC_TO_DST.type())) {
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_IP_SRC_TO_DST.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C1_TO_C1.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_C2.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C3_TO_C3.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C4_TO_C4.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST
+                        .type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_TUN_ID_TO_TUN_ID.type())
+                || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_TUN_ID.type())) {
             MoveExtensionTreatment mov = (MoveExtensionTreatment) extensionTreatment;
             OFActionNiciraMove.Builder action = factory.actions()
                     .buildNiciraMove();
@@ -162,6 +311,50 @@
             case TUNNEL_IPV4_DST:
                 OFOxmTunnelIpv4Dst tunnelIpv4Dst = (OFOxmTunnelIpv4Dst) oxm;
                 return new NiciraSetTunnelDst(Ip4Address.valueOf(tunnelIpv4Dst.getValue().getInt()));
+            case NSP:
+                OFOxmNsp nsp = (OFOxmNsp) oxm;
+                return new NiciraSetNshSpi(NshServicePathId.of((nsp.getValue().getRaw())));
+            case NSI:
+                OFOxmNsi nsi = (OFOxmNsi) oxm;
+                return new NiciraSetNshSi(NshServiceIndex.of((nsi.getValue().getRaw())));
+            case NSH_C1:
+                OFOxmNshC1 nshC1 = (OFOxmNshC1) oxm;
+                return new NiciraSetNshContextHeader(NshContextHeader.of((nshC1.getValue().getRaw())),
+                                                     ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH1
+                                                     .type());
+            case NSH_C2:
+                OFOxmNshC2 nshC2 = (OFOxmNshC2) oxm;
+                return new NiciraSetNshContextHeader(NshContextHeader.of((nshC2.getValue().getRaw())),
+                                                     ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH2
+                                                     .type());
+            case NSH_C3:
+                OFOxmNshC3 nshC3 = (OFOxmNshC3) oxm;
+                return new NiciraSetNshContextHeader(NshContextHeader.of((nshC3.getValue().getRaw())),
+                                                     ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH3
+                                                     .type());
+            case NSH_C4:
+                OFOxmNshC4 nshC4 = (OFOxmNshC4) oxm;
+                return new NiciraSetNshContextHeader(NshContextHeader.of((nshC4.getValue().getRaw())),
+                                                     ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH4
+                                                     .type());
+            case NSH_MDTYPE:
+                OFOxmNshMdtype nshMdType = (OFOxmNshMdtype) oxm;
+                return new NiciraNshMdType((nshMdType.getValue().getRaw()));
+            case NSH_NP:
+                OFOxmNshNp nshNp = (OFOxmNshNp) oxm;
+                return new NiciraNshNp((nshNp.getValue().getRaw()));
+            case ENCAP_ETH_SRC:
+                OFOxmEncapEthSrc encapEthSrc = (OFOxmEncapEthSrc) oxm;
+                return new NiciraEncapEthSrc(org.onlab.packet.MacAddress.valueOf((encapEthSrc.getValue().getBytes())));
+            case ENCAP_ETH_DST:
+                OFOxmEncapEthDst encapEthDst = (OFOxmEncapEthDst) oxm;
+                return new NiciraEncapEthDst(org.onlab.packet.MacAddress.valueOf((encapEthDst.getValue().getBytes())));
+            case ENCAP_ETH_TYPE:
+                OFOxmEncapEthType encapEthType = (OFOxmEncapEthType) oxm;
+                return new NiciraEncapEthType((encapEthType.getValue().getRaw()));
+            case TUN_GPE_NP:
+                OFOxmTunGpeNp tunGpeNp = (OFOxmTunGpeNp) oxm;
+                return new NiciraTunGpeNp((tunGpeNp.getValue().getRaw()));
             default:
                 throw new UnsupportedOperationException(
                         "Driver does not support extension type " + oxm.getMatchField().id);
@@ -188,14 +381,37 @@
                             case SRC_ARP_SPA:
                                 return NiciraMoveTreatmentFactory
                                         .createNiciraMovArpSpaToTpa();
+                    case NSH_C1:
+                        return NiciraMoveTreatmentFactory.createNiciraMovNshC1ToC1();
+                    case NSH_C2:
+                        if (Long.valueOf(moveAction.getDst()).intValue() == TUN_ID) {
+                            return NiciraMoveTreatmentFactory.createNiciraMovNshC2ToTunId();
+                        }
+                        return NiciraMoveTreatmentFactory.createNiciraMovNshC2ToC2();
+                    case NSH_C3:
+                        return NiciraMoveTreatmentFactory.createNiciraMovNshC3ToC3();
+                    case NSH_C4:
+                        return NiciraMoveTreatmentFactory.createNiciraMovNshC4ToC4();
+                    case TUN_IPV4_DST:
+                        return NiciraMoveTreatmentFactory.createNiciraMovTunDstToTunDst();
+                    case TUN_ID:
+                        return NiciraMoveTreatmentFactory.createNiciraMovTunIdToTunId();
                             default:
                                 throw new UnsupportedOperationException("Driver does not support move from "
-                                        + moveAction.getSrc() + " to "
-                                        + moveAction.getDst());
+                                + moveAction.getSrc() + " to " + moveAction.getDst() + "of length "
+                                + moveAction.getNBits());
                         }
                     case SUB_TYPE_RESUBMIT:
                         OFActionNiciraResubmit resubmitAction = (OFActionNiciraResubmit) nicira;
                         return new NiciraResubmit(PortNumber.portNumber(resubmitAction.getInPort()));
+                case SUB_TYPE_PUSH_NSH:
+                    return new NiciraPushNsh();
+                case SUB_TYPE_POP_NSH:
+                    return new NiciraPopNsh();
+                case SUB_TYPE_RESUBMIT_TABLE:
+                    OFActionNiciraResubmitTable resubmitTable = (OFActionNiciraResubmitTable) nicira;
+                    return new NiciraResubmitTable(PortNumber.portNumber(resubmitTable.getInPort()),
+                                                   resubmitTable.getTable());
                     default:
                         throw new UnsupportedOperationException("Driver does not support extension subtype "
                                 + nicira.getSubtype());
@@ -228,6 +444,27 @@
                 || type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_NSH_CH4.type())) {
             return new NiciraSetNshContextHeader(type);
         }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH.type())) {
+            return new NiciraPushNsh();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH.type())) {
+            return new NiciraPopNsh();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE.type())) {
+            return new NiciraNshMdType();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP.type())) {
+            return new NiciraNshNp();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_SRC.type())) {
+            return new NiciraEncapEthSrc();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_DST.type())) {
+            return new NiciraEncapEthDst();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_ENCAP_ETH_TYPE.type())) {
+            return new NiciraEncapEthType();
+        }
         if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_ARP_SHA_TO_THA.type())) {
             return NiciraMoveTreatmentFactory.createNiciraMovArpShaToTha();
         }
@@ -240,8 +477,32 @@
         if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_IP_SRC_TO_DST.type())) {
             return NiciraMoveTreatmentFactory.createNiciraMovIpSrcToDst();
         }
-        throw new UnsupportedOperationException(
-                "Driver does not support extension type " + type.toString());
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP.type())) {
+            return new NiciraTunGpeNp();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C1_TO_C1.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovNshC1ToC1();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_C2.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovNshC2ToC2();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C3_TO_C3.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovNshC3ToC3();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C4_TO_C4.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovNshC4ToC4();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST
+                .type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovTunDstToTunDst();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_TUN_ID_TO_TUN_ID.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovTunIdToTunId();
+        }
+        if (type.equals(ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_MOV_NSH_C2_TO_TUN_ID.type())) {
+            return NiciraMoveTreatmentFactory.createNiciraMovNshC2ToTunId();
+        }
+        throw new UnsupportedOperationException("Driver does not support extension type " + type.toString());
     }
 
     @Override
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMatchEncapEthType.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMatchEncapEthType.java
new file mode 100644
index 0000000..e0ae08a
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMatchEncapEthType.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.criteria.ExtensionSelector;
+import org.onosproject.net.flow.criteria.ExtensionSelectorType;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira EncapEthType extension selector to set encapsulated eth type.
+ */
+public class NiciraMatchEncapEthType extends AbstractExtension implements ExtensionSelector {
+
+    private short encapEthType;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new nshEncapEthType selector.
+     */
+    NiciraMatchEncapEthType() {
+        encapEthType = (short) 0;
+    }
+
+    /**
+     * Creates a new nshEncapEthType selector with given eth type.
+     *
+     * @param encapEthType encapsulated ethernet type
+     */
+    public NiciraMatchEncapEthType(short encapEthType) {
+        this.encapEthType = encapEthType;
+    }
+
+    /**
+     * Gets the encapEthType.
+     *
+     * @return encapEthType
+     */
+    public short encapEthType() {
+        return encapEthType;
+    }
+
+    @Override
+    public ExtensionSelectorType type() {
+        return ExtensionSelectorType.ExtensionSelectorTypes.NICIRA_MATCH_ENCAP_ETH_TYPE.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        encapEthType = (short) (appKryo.deserialize(data));
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(encapEthType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(encapEthType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraMatchEncapEthType) {
+            NiciraMatchEncapEthType that = (NiciraMatchEncapEthType) obj;
+            return Objects.equals(encapEthType, that.encapEthType);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("encapEthType", encapEthType).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMoveTreatmentFactory.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMoveTreatmentFactory.java
index 2b861eb..57f6e8b 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMoveTreatmentFactory.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraMoveTreatmentFactory.java
@@ -97,4 +97,88 @@
                                                  ExtensionTreatmentType.ExtensionTreatmentTypes
                                                  .NICIRA_MOV_IP_SRC_TO_DST.type());
     }
+
+    public static ExtensionTreatment createNiciraMovNshC1ToC1() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcC1 = 0x0001e604;
+        int dstC1 = 0x0001e604;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcC1,
+                                                 dstC1,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_NSH_C1_TO_C1.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovNshC2ToC2() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcC2 = 0x0001e804;
+        int dstC2 = 0x0001e804;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcC2,
+                                                 dstC2,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_NSH_C2_TO_C2.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovNshC3ToC3() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcC3 = 0x0001ea04;
+        int dstC3 = 0x0001ea04;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcC3,
+                                                 dstC3,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_NSH_C3_TO_C3.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovNshC4ToC4() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcC4 = 0x0001ec04;
+        int dstC4 = 0x0001ec04;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcC4,
+                                                 dstC4,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_NSH_C4_TO_C4.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovTunDstToTunDst() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcTunIpv4Dst = 0x00014004;
+        int dstTunIpv4Dst = 0x00014004;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcTunIpv4Dst,
+                                                 dstTunIpv4Dst,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_TUN_IPV4_DST_TO_TUN_IPV4_DST.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovTunIdToTunId() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 64;
+        int srcTunId = 0x12008;
+        int dstTunId = 0x12008; // 0x80004c08;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcTunId,
+                                                 dstTunId,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_TUN_ID_TO_TUN_ID.type());
+    }
+
+    public static ExtensionTreatment createNiciraMovNshC2ToTunId() {
+        int srcOfs = 0;
+        int dstOfs = 0;
+        int nBits = 32;
+        int srcC2 = 0x0001e804;
+        int dstTunId = 0x80004c08;
+        return new DefaultMoveExtensionTreatment(srcOfs, dstOfs, nBits, srcC2,
+                                                 dstTunId,
+                                                 ExtensionTreatmentType.ExtensionTreatmentTypes
+                                                 .NICIRA_MOV_NSH_C2_TO_TUN_ID.type());
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshMdType.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshMdType.java
new file mode 100644
index 0000000..333e470
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshMdType.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira nshMdType extension instruction.
+ */
+public class NiciraNshMdType extends AbstractExtension implements ExtensionTreatment {
+
+    private byte nshMdType;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new nshMdType instruction.
+     */
+    NiciraNshMdType() {
+        nshMdType = (byte) 0;
+    }
+
+    /**
+     * Creates a new nshMdType instruction with given nsh md type.
+     *
+     * @param nshMdType nsh md type
+     */
+    public NiciraNshMdType(byte nshMdType) {
+        this.nshMdType = nshMdType;
+    }
+
+    /**
+     * Gets the nsh md type.
+     *
+     * @return nshMdType
+     */
+    public byte nshMdType() {
+        return nshMdType;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_MDTYPE.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        nshMdType = (byte) (appKryo.deserialize(data));
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(nshMdType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(nshMdType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraNshMdType) {
+            NiciraNshMdType that = (NiciraNshMdType) obj;
+            return Objects.equals(nshMdType, that.nshMdType);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("nshMdType", nshMdType).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshNp.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshNp.java
new file mode 100644
index 0000000..fc319c5
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraNshNp.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira nshNp extension instruction to set next protocol value in nsh header.
+ */
+public class NiciraNshNp extends AbstractExtension implements ExtensionTreatment {
+
+    private byte nshNp;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new nshNp instruction.
+     */
+    NiciraNshNp() {
+        nshNp = (byte) 0;
+    }
+
+    /**
+     * Creates a new nshNp instruction with given nsh np.
+     *
+     * @param nshNp nsh next protocol value
+     */
+    public NiciraNshNp(byte nshNp) {
+        this.nshNp = nshNp;
+    }
+
+    /**
+     * Gets the nsh np.
+     *
+     * @return nshNp
+     */
+    public byte nshNp() {
+        return nshNp;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_NSH_NP.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        nshNp = (byte) (appKryo.deserialize(data));
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(nshNp);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(nshNp);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraNshNp) {
+            NiciraNshNp that = (NiciraNshNp) obj;
+            return Objects.equals(nshNp, that.nshNp);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("nshNp", nshNp).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPopNsh.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPopNsh.java
new file mode 100644
index 0000000..2e319f3
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPopNsh.java
@@ -0,0 +1,66 @@
+/*
+ * 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.driver.extensions;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+/**
+ * Nicira pop nsh extension instruction.
+ */
+public class NiciraPopNsh extends AbstractExtension implements ExtensionTreatment {
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new pop nsh instruction.
+     */
+    public NiciraPopNsh() {
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_POP_NSH.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(0);
+    }
+
+    @Override
+    public int hashCode() {
+        return 1;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraPopNsh) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPushNsh.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPushNsh.java
new file mode 100644
index 0000000..8d3a96d
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraPushNsh.java
@@ -0,0 +1,67 @@
+/*
+ * 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.driver.extensions;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+/**
+ * Nicira push nsh extension instruction.
+ */
+public class NiciraPushNsh extends AbstractExtension implements ExtensionTreatment {
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new push nsh instruction.
+     */
+    public NiciraPushNsh() {
+
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_PUSH_NSH.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(0);
+    }
+
+    @Override
+    public int hashCode() {
+        return 1;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraPushNsh) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraSetNshContextHeader.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraSetNshContextHeader.java
index ebf2382..65ebf28 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraSetNshContextHeader.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraSetNshContextHeader.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.driver.extensions;
 
+import java.util.Map;
 import java.util.Objects;
 
 import org.onlab.util.KryoNamespace;
@@ -25,6 +26,7 @@
 import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.Maps;
 
 /**
  * Nicira set NSH Context header extension instruction.
@@ -74,12 +76,17 @@
 
     @Override
     public void deserialize(byte[] data) {
-        nshCh = NshContextHeader.of(appKryo.deserialize(data));
+        Map<String, Object> values = appKryo.deserialize(data);
+        nshCh = (NshContextHeader) values.get("nshCh");
+        type = (ExtensionTreatmentType) values.get("type");
     }
 
     @Override
     public byte[] serialize() {
-        return appKryo.serialize(nshCh.nshContextHeader());
+        Map<String, Object> values = Maps.newHashMap();
+        values.put("nshCh", nshCh);
+        values.put("type", type);
+        return appKryo.serialize(values);
     }
 
     @Override
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraTunGpeNp.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraTunGpeNp.java
new file mode 100644
index 0000000..de15948
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraTunGpeNp.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.extensions;
+
+import java.util.Objects;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.flow.AbstractExtension;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Nicira tunnel gpe next protocol extension instruction to tunGpeNp value.
+ */
+public class NiciraTunGpeNp extends AbstractExtension implements ExtensionTreatment {
+
+    private byte tunGpeNp;
+
+    private final KryoNamespace appKryo = new KryoNamespace.Builder().build();
+
+    /**
+     * Creates a new NiciraTunGpeNp instruction.
+     */
+    NiciraTunGpeNp() {
+        tunGpeNp = (byte) 0;
+    }
+
+    /**
+     * Creates a new NiciraTunGpeNp instruction with given value.
+     *
+     * @param tunGpeNp tunnel gpe next protocol value
+     */
+    public NiciraTunGpeNp(byte tunGpeNp) {
+        this.tunGpeNp = tunGpeNp;
+    }
+
+    /**
+     * Gets the tunGpeNp.
+     *
+     * @return tunGpeNp
+     */
+    public byte tunGpeNp() {
+        return tunGpeNp;
+    }
+
+    @Override
+    public ExtensionTreatmentType type() {
+        return ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_TUN_GPE_NP.type();
+    }
+
+    @Override
+    public void deserialize(byte[] data) {
+        tunGpeNp = (byte) (appKryo.deserialize(data));
+    }
+
+    @Override
+    public byte[] serialize() {
+        return appKryo.serialize(tunGpeNp);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(tunGpeNp);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof NiciraTunGpeNp) {
+            NiciraTunGpeNp that = (NiciraTunGpeNp) obj;
+            return Objects.equals(tunGpeNp, that.tunGpeNp);
+
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass()).add("tunGpeNp", tunGpeNp).toString();
+    }
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
index 74cb756..0120e7e 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
@@ -70,6 +70,8 @@
     protected DeviceService deviceService;
     private static final int TIME_OUT = 0;
     private static final int CLASSIFIER_TABLE = 0;
+    private static final int ENCAP_OUTPUT_TABLE = 4;
+    private static final int TUN_SEND_TABLE = 7;
     private static final int ARP_TABLE = 10;
     private static final int DNAT_TABLE = 20;
     private static final int L3FWD_TABLE = 30;
@@ -278,7 +280,23 @@
 
     private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
         log.debug("Processing versatile forwarding objective");
-        return Collections.emptyList();
+        TrafficSelector selector = fwd.selector();
+        TrafficTreatment tb = fwd.treatment();
+        FlowRule.Builder ruleBuilder = DefaultFlowRule.builder().fromApp(fwd.appId()).withPriority(fwd.priority())
+                .forDevice(deviceId).withSelector(selector).withTreatment(tb).makeTemporary(TIME_OUT);
+        ruleBuilder.withPriority(fwd.priority());
+        if (fwd.priority() == 100) {
+            ruleBuilder.forTable(ENCAP_OUTPUT_TABLE);
+        } else if (fwd.priority() == 200) {
+            ruleBuilder.forTable(TUN_SEND_TABLE);
+        } else {
+            ruleBuilder.forTable(CLASSIFIER_TABLE);
+        }
+
+        if (fwd.permanent()) {
+            ruleBuilder.makePermanent();
+        }
+        return Collections.singletonList(ruleBuilder.build());
     }
 
     private Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthDstTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthDstTest.java
new file mode 100644
index 0000000..971a700
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthDstTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraEncapEthDstTest class.
+ */
+public class NiciraEncapEthDstTest {
+
+    private final MacAddress mac1 = MacAddress.valueOf("fa:16:3e:da:45:23");
+    private final MacAddress mac2 = MacAddress.valueOf("fa:16:3e:f3:d1:fe");
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraEncapEthDst encapEthDst1 = new NiciraEncapEthDst(mac1);
+        final NiciraEncapEthDst sameAsEncapEthDst1 = new NiciraEncapEthDst(mac1);
+        final NiciraEncapEthDst encapEthDst2 = new NiciraEncapEthDst(mac2);
+
+        new EqualsTester().addEqualityGroup(encapEthDst1, sameAsEncapEthDst1).addEqualityGroup(encapEthDst2)
+        .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraEncapEthDstTest object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraEncapEthDst encapEthDst1 = new NiciraEncapEthDst(mac1);
+        assertThat(encapEthDst1, is(notNullValue()));
+        assertThat(encapEthDst1.encapEthDst(), is(mac1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthSrcTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthSrcTest.java
new file mode 100644
index 0000000..f54f8b2
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthSrcTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraEncapEthSrcTest class.
+ */
+public class NiciraEncapEthSrcTest {
+
+    private final MacAddress mac1 = MacAddress.valueOf("fa:16:3e:11:00:01");
+    private final MacAddress mac2 = MacAddress.valueOf("fa:16:3e:22:00:02");
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraEncapEthDst encapEthDst1 = new NiciraEncapEthDst(mac1);
+        final NiciraEncapEthDst sameAsEncapEthDst1 = new NiciraEncapEthDst(mac1);
+        final NiciraEncapEthDst encapEthDst2 = new NiciraEncapEthDst(mac2);
+
+        new EqualsTester().addEqualityGroup(encapEthDst1, sameAsEncapEthDst1).addEqualityGroup(encapEthDst2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraEncapEthSrcTest object.
+     */
+    @Test
+    public void testConstruction() {
+
+        final NiciraEncapEthDst encapEthDst1 = new NiciraEncapEthDst(mac1);
+        assertThat(encapEthDst1, is(notNullValue()));
+        assertThat(encapEthDst1.encapEthDst(), is(mac1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthTypeTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthTypeTest.java
new file mode 100644
index 0000000..311c81c
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraEncapEthTypeTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraEncapEthType class.
+ */
+public class NiciraEncapEthTypeTest {
+    final short ethType1 = (short) 0x894f;
+    final short ethType2 = (short) 0x800;
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraEncapEthType encapEthType1 = new NiciraEncapEthType(ethType1);
+        final NiciraEncapEthType sameAsEncapEthType1 = new NiciraEncapEthType(ethType1);
+        final NiciraEncapEthType encapEthType2 = new NiciraEncapEthType(ethType2);
+
+        new EqualsTester().addEqualityGroup(encapEthType1, sameAsEncapEthType1).addEqualityGroup(encapEthType2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraEncapEthType object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraEncapEthType encapEthType = new NiciraEncapEthType(ethType1);
+        assertThat(encapEthType, is(notNullValue()));
+        assertThat(encapEthType.encapEthType(), is(ethType1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraMatchEncapEthTypeTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraMatchEncapEthTypeTest.java
new file mode 100644
index 0000000..b4b38f8
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraMatchEncapEthTypeTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraMatchEncapEthType class.
+ */
+public class NiciraMatchEncapEthTypeTest {
+    final short ethType1 = (short) 0x894f;
+    final short ethType2 = (short) 0x800;
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraMatchEncapEthType encapEthType1 = new NiciraMatchEncapEthType(ethType1);
+        final NiciraMatchEncapEthType sameAsEncapEthType1 = new NiciraMatchEncapEthType(ethType1);
+        final NiciraMatchEncapEthType encapEthType2 = new NiciraMatchEncapEthType(ethType2);
+
+        new EqualsTester().addEqualityGroup(encapEthType1, sameAsEncapEthType1).addEqualityGroup(encapEthType2)
+        .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraMatchEncapEthType object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraMatchEncapEthType encapEthType = new NiciraMatchEncapEthType(ethType1);
+        assertThat(encapEthType, is(notNullValue()));
+        assertThat(encapEthType.encapEthType(), is(ethType1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshMdTypeTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshMdTypeTest.java
new file mode 100644
index 0000000..8c299bd
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshMdTypeTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraNshMdType class.
+ */
+public class NiciraNshMdTypeTest {
+    final byte mdType1 = (byte) 1;
+    final byte mdType2 = (byte) 2;
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraNshMdType nshMdType1 = new NiciraNshMdType(mdType1);
+        final NiciraNshMdType sameAsnshMdType1 = new NiciraNshMdType(mdType1);
+        final NiciraNshMdType nshMdType2 = new NiciraNshMdType(mdType2);
+
+        new EqualsTester().addEqualityGroup(nshMdType1, sameAsnshMdType1).addEqualityGroup(nshMdType2)
+        .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraNshMdType object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraNshMdType nshMdType = new NiciraNshMdType(mdType1);
+        assertThat(nshMdType, is(notNullValue()));
+        assertThat(nshMdType.nshMdType(), is(mdType1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshNpTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshNpTest.java
new file mode 100644
index 0000000..17ed7ae
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraNshNpTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraNshNp class.
+ */
+public class NiciraNshNpTest {
+    final byte np1 = (byte) 1;
+    final byte np2 = (byte) 4;
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraNshNp nshNp1 = new NiciraNshNp(np1);
+        final NiciraNshNp sameAsNshNp1 = new NiciraNshNp(np1);
+        final NiciraNshNp nshNp2 = new NiciraNshNp(np2);
+
+        new EqualsTester().addEqualityGroup(nshNp1, sameAsNshNp1).addEqualityGroup(nshNp2)
+                .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraNshNp object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraNshNp nshNp1 = new NiciraNshNp(np1);
+        assertThat(nshNp1, is(notNullValue()));
+        assertThat(nshNp1.nshNp(), is(np1));
+    }
+}
diff --git a/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraTunGpeNpTest.java b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraTunGpeNpTest.java
new file mode 100644
index 0000000..259ebb5
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/extensions/NiciraTunGpeNpTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.driver.extensions;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import org.junit.Test;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Unit tests for NiciraTunGpeNp class.
+ */
+public class NiciraTunGpeNpTest {
+    final byte np1 = (byte) 1;
+    final byte np2 = (byte) 2;
+
+    /**
+     * Checks the operation of equals() methods.
+     */
+    @Test
+    public void testEquals() {
+        final NiciraTunGpeNp tunGpeNp1 = new NiciraTunGpeNp(np1);
+        final NiciraTunGpeNp sameAsTunGpeNp1 = new NiciraTunGpeNp(np1);
+        final NiciraTunGpeNp tunGpeNp2 = new NiciraTunGpeNp(np2);
+
+        new EqualsTester().addEqualityGroup(tunGpeNp1, sameAsTunGpeNp1).addEqualityGroup(tunGpeNp2)
+        .testEquals();
+    }
+
+    /**
+     * Checks the construction of a NiciraTunGpeNp object.
+     */
+    @Test
+    public void testConstruction() {
+        final NiciraTunGpeNp tunGpeNp1 = new NiciraTunGpeNp(np1);
+        assertThat(tunGpeNp1, is(notNullValue()));
+        assertThat(tunGpeNp1.tunGpeNp(), is(np1));
+    }
+}
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
index 3ca0979..8c3cb92 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
@@ -511,6 +511,18 @@
             builder.setUdpSrc(TpPort.tpPort(udpsrc.getValue().getPort()));
             break;
         case TUNNEL_IPV4_DST:
+        case NSP:
+        case NSI:
+        case NSH_C1:
+        case NSH_C2:
+        case NSH_C3:
+        case NSH_C4:
+        case NSH_MDTYPE:
+        case NSH_NP:
+        case ENCAP_ETH_SRC:
+        case ENCAP_ETH_DST:
+        case ENCAP_ETH_TYPE:
+        case TUN_GPE_NP:
             if (treatmentInterpreter != null) {
                 try {
                     builder.extension(treatmentInterpreter.mapAction(action), deviceId);
@@ -900,6 +912,36 @@
                 ip = Ip4Address.valueOf(match.get(MatchField.ARP_TPA).getInt());
                 builder.matchArpTpa(ip);
                 break;
+            case NSP:
+                if (selectorInterpreter != null) {
+                    try {
+                        OFOxm oxm = ((OFMatchV3) match).getOxmList().get(MatchField.NSP);
+                        builder.extension(selectorInterpreter.mapOxm(oxm), deviceId);
+                    } catch (UnsupportedOperationException e) {
+                        log.debug(e.getMessage());
+                    }
+                }
+                break;
+            case NSI:
+                if (selectorInterpreter != null) {
+                    try {
+                        OFOxm oxm = ((OFMatchV3) match).getOxmList().get(MatchField.NSI);
+                        builder.extension(selectorInterpreter.mapOxm(oxm), deviceId);
+                    } catch (UnsupportedOperationException e) {
+                        log.debug(e.getMessage());
+                    }
+                }
+                break;
+            case ENCAP_ETH_TYPE:
+                if (selectorInterpreter != null) {
+                    try {
+                        OFOxm oxm = ((OFMatchV3) match).getOxmList().get(MatchField.ENCAP_ETH_TYPE);
+                        builder.extension(selectorInterpreter.mapOxm(oxm), deviceId);
+                    } catch (UnsupportedOperationException e) {
+                        log.debug(e.getMessage());
+                    }
+                }
+                break;
             case MPLS_TC:
             default:
                 log.warn("Match type {} not yet implemented.", field.id);