org.opendaylight.sfc.scfvpprenderer.processors.VppClassifierProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.sfc.scfvpprenderer.processors.VppClassifierProcessor.java

Source

/*
 * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.sfc.scfvpprenderer.processors;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;

import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.sfc.provider.api.SfcProviderAclAPI;
import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
import org.opendaylight.sfc.util.vpp.SfcVppUtils;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.acl.rev151001.Actions1;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.acl.rev151001.access.lists.acl.access.list.entries.ace.actions.sfc.action.AclRenderedServicePath;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.scf.rev140701.attachment.point.attachment.point.type.Interface;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.scf.rev140701.service.function.classifiers.ServiceFunctionClassifier;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.scf.rev140701.service.function.classifiers.service.function.classifier.SclServiceFunctionForwarder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.classify.table.base.attributes.ClassifySessionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.rev161214.vpp.classifier.ClassifyTableBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class VppClassifierProcessor {

    private static final Logger LOG = LoggerFactory.getLogger(VppClassifierProcessor.class);

    private final VppNodeManager nodeManager;
    private static final String SFC_BD_NAME = new String("SFCVPP");
    private static final String DUMMY_BD_NAME = new String("SFCDUMMY");
    private final Map<String, String> bridgeDomainCreated = new HashMap<>();

    class Pair<T> {
        private final T m_mask;
        private final T m_match;

        public Pair(T mask, T match) {
            m_mask = mask;
            m_match = match;
        }

        public T getMask() {
            return m_mask;
        }

        public T getMatch() {
            return m_match;
        }
    }

    class SffInfo {
        public DataBroker mountPoint;
        public SffName sffName;
        public String bridgeDomainName;
        public IpAddress ip;
        public Long pathId;
        public Short serviceIndex;
        public Short reverseServiceIndex;

        public SffInfo(DataBroker mountPoint, SffName sffName, String bridgeDomainName, IpAddress ip, Long pathId,
                Short serviceIndex, Short reverseServiceIndex) {
            this.mountPoint = mountPoint;
            this.sffName = sffName;
            this.bridgeDomainName = bridgeDomainName;
            this.ip = ip;
            this.pathId = pathId;
            this.serviceIndex = serviceIndex;
            this.reverseServiceIndex = reverseServiceIndex;
        }
    }

    public VppClassifierProcessor(VppNodeManager nodeManager) {
        this.nodeManager = Preconditions.checkNotNull(nodeManager);
    }

    private Optional<Acl> extractAcl(ServiceFunctionClassifier scf) {
        return Optional.ofNullable(scf).map(ServiceFunctionClassifier::getAcl)
                .map(acl -> SfcProviderAclAPI.readAccessList(acl.getName(), acl.getType()));
    }

    private Optional<String> getInterfaceNameFromClassifier(SclServiceFunctionForwarder theClassifier) {
        return Optional.ofNullable(theClassifier)
                .filter(classifier -> classifier.getAttachmentPointType() instanceof Interface)
                .map(classifier -> (Interface) classifier.getAttachmentPointType()).map(Interface::getInterface);
    }

    private boolean validateInputs(Acl theAcl) {
        String aclName = theAcl.getAclName();
        if (aclName == null) {
            LOG.error("aclName is null");
            return false;
        }

        List<Ace> theAces = Optional.ofNullable(theAcl.getAccessListEntries()).map(AccessListEntries::getAce)
                .orElse(Collections.emptyList());

        if (theAces.isEmpty()) {
            LOG.error("acesList is null");
            return false;
        }

        return true;
    }

    private byte[] ipv4AddressPrefixToBytes(final Ipv4Prefix ipv4Prefix) {
        byte[] retval = new byte[4];
        String[] address = ipv4Prefix.getValue().substring(0, ipv4Prefix.getValue().indexOf('/')).split("\\.");
        String prefix = ipv4Prefix.getValue().substring(ipv4Prefix.getValue().indexOf('/') + 1);
        int mask = (int) Short.parseShort(prefix);
        for (int i = mask % 8; i < 4; i++) {
            address[i] = "0";
        }

        for (int d = 0; d < 4; d++) {
            retval[d] = (byte) (Short.parseShort(address[d]) & 0xff);
        }
        return retval;
    }

    private byte[] ipv6AddressNoZoneToArray(final String address) {
        byte[] retval = new byte[16];

        //splits address and add ommited zeros for easier parsing
        List<String> segments = Arrays.asList(address.split(":")).stream()
                .map(segment -> StringUtils.repeat('0', 4 - segment.length()) + segment)
                .collect(Collectors.toList());

        byte index = 0;
        for (String segment : segments) {

            String firstPart = segment.substring(0, 2);
            String secondPart = segment.substring(2);

            //first part should be ommited
            if ("00".equals(firstPart)) {
                index++;
            } else {
                retval[index++] = ((byte) Short.parseShort(firstPart, 16));
            }

            retval[index++] = ((byte) Short.parseShort(secondPart, 16));
        }

        return retval;
    }

    private byte[] ipv6AddressPrefixToBytes(final Ipv6Prefix ipv6Prefix) {
        return ipv6AddressNoZoneToArray(new Ipv6AddressNoZone(
                new Ipv6Address(ipv6Prefix.getValue().substring(0, ipv6Prefix.getValue().indexOf('/'))))
                        .getValue());
    }

    private Pair<HexString> getMaskAndMatch(Matches matches) {
        int maskLength = 0;
        String mask = new String("");
        String match = new String("");
        if (matches == null) {
            return null;
        }

        if (matches.getAceType() instanceof AceEth) {
            AceEth eth = (AceEth) matches.getAceType();

            if (eth.getDestinationMacAddress() != null) {
                mask = mask + "ff:ff:ff:ff:ff:ff";
                match = match + eth.getDestinationMacAddress().getValue();
            } else {
                mask = mask + "00:00:00:00:00:00";
                match = match + "00:00:00:00:00:00";
            }
            if (eth.getSourceMacAddress() != null) {
                mask = mask + ":ff:ff:ff:ff:ff:ff";
                match = match + ":" + eth.getSourceMacAddress().getValue();
            } else {
                mask = mask + ":00:00:00:00:00:00";
                match = match + ":00:00:00:00:00:00";
            }
            maskLength += 12;
        } else if (matches.getAceType() instanceof AceIp) {
            mask = mask + "00:00:00:00:00:00:00:00:00:00:00:00";
            match = match + "00:00:00:00:00:00:00:00:00:00:00:00";
            maskLength += 12;
            AceIp aceip = (AceIp) matches.getAceType();

            if (aceip.getAceIpVersion() instanceof AceIpv4) {
                //Ethernet Type
                mask = mask + ":ff:ff";
                match = match + ":08:00";
                maskLength += 2;

                //Transport Type
                mask = mask + ":00:00:00:00:00:00:00:00:00:ff:00:00";
                match = match + String.format(":00:00:00:00:00:00:00:00:00:%1$02x:00:00", aceip.getProtocol());
                maskLength += 12;

                //L3: IPv4
                AceIpv4 ipv4 = (AceIpv4) aceip.getAceIpVersion();
                Ipv4Prefix src = ipv4.getSourceIpv4Network();
                if (src != null) {
                    byte[] retval = ipv4AddressPrefixToBytes(src);
                    for (int i = 0; i < 4; i++) {
                        if (retval[i] == 0) {
                            mask = mask + ":00";
                        } else {
                            mask = mask + ":ff";
                        }
                    }
                    match = match + String.format(":%1$02x:%2$02x:%3$02x:%4$02x", retval[0], retval[1], retval[2],
                            retval[3]);
                } else {
                    mask = mask + ":00:00:00:00";
                    match = match + ":00:00:00:00";
                }
                maskLength += 4;

                Ipv4Prefix dst = ipv4.getDestinationIpv4Network();
                if (dst != null) {
                    byte[] retval = ipv4AddressPrefixToBytes(dst);
                    for (int i = 0; i < 4; i++) {
                        if (retval[i] == 0) {
                            mask = mask + ":00";
                        } else {
                            mask = mask + ":ff";
                        }
                    }
                    match = match + String.format(":%1$02x:%2$02x:%3$02x:%4$02x", retval[0], retval[1], retval[2],
                            retval[3]);
                } else {
                    mask = mask + ":00:00:00:00";
                    match = match + ":00:00:00:00";
                }
                maskLength += 4;
            } else if (aceip.getAceIpVersion() instanceof AceIpv6) {
                //Ethernet Type
                mask = mask + ":ff:ff";
                match = match + ":86:dd";
                maskLength += 2;

                //Transport Type
                mask = mask + ":00:00:00:00:00:00:ff:00";
                match = match + String.format(":00:00:00:00:00:00:%1$02x:00", aceip.getProtocol());
                maskLength += 8;

                //L3: IPv6
                AceIpv6 ipv6 = (AceIpv6) aceip.getAceIpVersion();
                Ipv6Prefix src = ipv6.getSourceIpv6Network();
                if (src != null) {
                    byte[] retval = ipv6AddressPrefixToBytes(src);
                    mask = mask + ":ff:ff:ff:ff:ff:ff:ff:ff::ff:ff:ff:ff:ff:ff:ff:ff";
                    match = match + String.format(
                            ":%1$02x:%2$02x:%3$02x:%4$02x:%5$02x:%6$02x:%7$02x:%8$02x:%9$02x:%10$02x:%11$02x:%12$02x:%13$02x:%14$02x:%15$02x:%16$02x",
                            retval[0], retval[1], retval[2], retval[3], retval[4], retval[5], retval[6], retval[7],
                            retval[8], retval[9], retval[10], retval[11], retval[12], retval[13], retval[14],
                            retval[15]);
                } else {
                    mask = mask + ":00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
                    match = match + ":00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
                }
                maskLength += 16;

                Ipv6Prefix dst = ipv6.getDestinationIpv6Network();
                if (dst != null) {
                    byte[] retval = ipv6AddressPrefixToBytes(dst);
                    mask = mask + ":ff:ff:ff:ff:ff:ff:ff:ff::ff:ff:ff:ff:ff:ff:ff:ff";
                    match = match + String.format(
                            ":%1$02x:%2$02x:%3$02x:%4$02x:%5$02x:%6$02x:%7$02x:%8$02x:%9$02x:%10$02x:%11$02x:%12$02x:%13$02x:%14$02x:%15$02x:%16$02x",
                            retval[0], retval[1], retval[2], retval[3], retval[4], retval[5], retval[6], retval[7],
                            retval[8], retval[9], retval[10], retval[11], retval[12], retval[13], retval[14],
                            retval[15]);
                } else {
                    mask = mask + ":00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
                    match = match + ":00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
                }
                maskLength += 16;
            }

            if (aceip.getProtocol() != null) {
                aceip.getProtocol();

                Integer srcPort = null;
                Integer dstPort = null;

                if (aceip.getSourcePortRange() != null && aceip.getSourcePortRange().getLowerPort() != null
                        && aceip.getSourcePortRange().getLowerPort().getValue() != null
                        && aceip.getSourcePortRange().getLowerPort().getValue() != 0) {
                    srcPort = aceip.getSourcePortRange().getLowerPort().getValue();
                }
                if (aceip.getDestinationPortRange() != null
                        && aceip.getDestinationPortRange().getLowerPort() != null
                        && aceip.getDestinationPortRange().getLowerPort().getValue() != null
                        && aceip.getDestinationPortRange().getLowerPort().getValue() != 0) {
                    dstPort = aceip.getDestinationPortRange().getLowerPort().getValue();
                }

                // don't support port range
                if (srcPort != null) {
                    mask = mask + ":ff:ff";
                    match = match + String.format(":%1$02x:%2$02x", srcPort.intValue() & 0xFF00,
                            srcPort.intValue() & 0x00FF);
                } else {
                    mask = mask + ":00:00";
                    match = match + ":00:00";
                }
                maskLength += 2;

                if (dstPort != null) {
                    mask = mask + ":ff:ff";
                    match = match + String.format(":%1$02x:%2$02x", dstPort.intValue() & 0xFF00,
                            dstPort.intValue() & 0x00FF);
                } else {
                    mask = mask + ":00:00";
                    match = match + ":00:00";
                }
                maskLength += 2;
            }
        }
        if (maskLength == 0) {
            return null;
        } else {
            if (maskLength % 16 != 0) {
                int padLength = 16 - maskLength % 16;
                mask = mask + StringUtils.repeat(":00", padLength);
                match = match + StringUtils.repeat(":00", padLength);
            }
            return new Pair(new HexString(mask), new HexString(match));
        }
    }

    private RspName getReverseRspName(RspName rspName) {
        String name = rspName.getValue();
        if (name.endsWith("-Reverse")) {
            return new RspName(name.replaceAll("-Reverse", ""));
        } else {
            return new RspName(name + "-Reverse");
        }
    }

    private RenderedServicePath getRenderedServicePath(RspName rspName) {
        if (rspName == null) {
            LOG.error("rspName is null\n");
            return null;
        }

        return SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
    }

    private SffInfo getFirstSffInfoInRsp(RspName rspName) {
        RenderedServicePath renderedServicePath = getRenderedServicePath(rspName);
        if (renderedServicePath == null) {
            LOG.error("renderedServicePath is null\n");
            return null;
        }

        Long pathId = renderedServicePath.getPathId();
        List<RenderedServicePathHop> hopList = renderedServicePath.getRenderedServicePathHop();
        if (hopList == null || hopList.isEmpty()) {
            LOG.error("Rendered path {} does not contain any hop", renderedServicePath.getName().getValue());
            return null;
        }

        RenderedServicePathHop firstRspHop = hopList.get(0);
        RenderedServicePathHop lastRspHop = Iterables.getLast(hopList);
        if (firstRspHop == null) {
            LOG.error("first rsp hop is null\n");
            return null;
        }

        Short serviceIndex = firstRspHop.getServiceIndex();
        Short reverseServiceIndex = (short) (lastRspHop.getServiceIndex() - 1);
        SffName sffName = firstRspHop.getServiceFunctionForwarder();
        IpAddress sffIp = SfcVppUtils.getSffFirstDplIp(sffName);
        DataBroker mountPoint = SfcVppUtils.getSffMountpoint(this.nodeManager.getMountPointService(), sffName);
        return new SffInfo(mountPoint, sffName, SFC_BD_NAME, sffIp, pathId, serviceIndex, reverseServiceIndex);
    }

    private boolean configureVxlanGpeClassifier(ServiceFunctionClassifier scf) {
        Optional<Acl> theAcl = extractAcl(scf);
        if (!theAcl.isPresent() || !validateInputs(theAcl.get())) {
            LOG.error("Could not retrieve the ACL from the classifier: {}", scf);
            return false;
        }
        Map<RspName, List<Pair<HexString>>> rspPairList = new HashMap<RspName, List<Pair<HexString>>>();
        List<Ace> aceList = theAcl.get().getAccessListEntries().getAce();
        for (Ace ace : aceList) {
            Optional<RspName> rspName = Optional.ofNullable(ace.getActions())
                    .map(theActions -> theActions.getAugmentation(Actions1.class))
                    .map(actions1 -> (AclRenderedServicePath) actions1.getSfcAction())
                    .map(aclRsp -> new RspName(aclRsp.getRenderedServicePath()));
            if (!rspName.isPresent()) {
                LOG.error("Could not retrieve the RSP from the classifier: {}", scf);
            }
            List<Pair<HexString>> pairList = rspPairList.get(rspName.get());
            if (pairList == null) {
                pairList = new ArrayList<>();
                rspPairList.put(rspName.get(), pairList);
            }
            pairList.add(getMaskAndMatch(ace.getMatches()));
        }

        List<SclServiceFunctionForwarder> sfflist = scf.getSclServiceFunctionForwarder();
        if (sfflist == null) {
            LOG.error("sfflist is null");
            return false;
        }

        for (SclServiceFunctionForwarder sclSff : sfflist) {
            SffName sffName = new SffName(sclSff.getName());
            Optional<String> itfName = getInterfaceNameFromClassifier(sclSff);

            if (!itfName.isPresent()) {
                LOG.error("Could not get LogicalInterface from the classifier's attachment point");
            }

            IpAddress sffIp = SfcVppUtils.getSffFirstDplIp(sffName);
            DataBroker mountPoint = SfcVppUtils.getSffMountpoint(this.nodeManager.getMountPointService(), sffName);
            if (!bridgeDomainCreated.containsKey(sffName.getValue())) {
                SfcVppUtils.addDummyBridgeDomain(mountPoint, DUMMY_BD_NAME, sffName.getValue());
                SfcVppUtils.addDummyNshEntry(mountPoint, 0L, (short) 1, sffName.getValue());
                SfcVppUtils.addDummyNshMap(mountPoint, 0L, (short) 1, 0L, (short) 1, new String("local0"),
                        sffName.getValue());
                SfcVppUtils.addBridgeDomain(mountPoint, SFC_BD_NAME, sffName.getValue());
                bridgeDomainCreated.put(sffName.getValue(), SFC_BD_NAME);
            }
            for (RspName rsp : rspPairList.keySet()) {
                SffInfo sffInfo = getFirstSffInfoInRsp(rsp);
                RspName reverseRspName = getReverseRspName(rsp);
                RenderedServicePath reverseRenderedServicePath = getRenderedServicePath(reverseRspName);
                if (reverseRenderedServicePath == null) {
                    LOG.error("reverseRenderedServicePath is null\n");
                    return false;
                }

                Long reversePathId = reverseRenderedServicePath.getPathId();
                List<RenderedServicePathHop> hopList = reverseRenderedServicePath.getRenderedServicePathHop();
                if (hopList == null || hopList.isEmpty()) {
                    LOG.error("Rendered path {} does not contain any hop",
                            reverseRenderedServicePath.getName().getValue());
                    return false;
                }

                RenderedServicePathHop lastRspHop = Iterables.getLast(hopList);
                if (lastRspHop == null) {
                    LOG.error("kast rsp hop is null\n");
                    return false;
                }

                Short reverseServiceIndex = (short) (lastRspHop.getServiceIndex() - 1);
                List<Pair<HexString>> pairList = rspPairList.get(rsp);
                int length = pairList.size();
                int index = 0;

                // Configure VPP classfier classify tables, sessions and enable ingress ACL
                List<ClassifyTableBuilder> classifyTableList = new ArrayList<>();
                List<ClassifySessionBuilder> classifySessionList = new ArrayList<>();
                for (Pair<HexString> maskMatch : pairList) {
                    boolean hasNext = false;
                    if (index < length - 1) {
                        hasNext = true;
                    }
                    ClassifyTableBuilder classifyTableBuilder = SfcVppUtils.buildVppClassifyTable(sffName,
                            rsp.getValue(), maskMatch.getMask(), hasNext);
                    classifyTableList.add(classifyTableBuilder);
                    classifySessionList.add(SfcVppUtils.buildVppClassifySession(classifyTableBuilder,
                            maskMatch.getMatch(), sffInfo.pathId, sffInfo.serviceIndex));
                    SfcVppUtils.increaseNextTableIndex(sffName.getValue());
                    index++;
                }
                SfcVppUtils.configureVppClassifier(mountPoint, sffName, classifyTableList, classifySessionList);

                //Enable Ingress Acl on table 0
                SfcVppUtils.enableIngressAcl(mountPoint, itfName.get(), SfcVppUtils.buildClassifyTableKey(0),
                        sffName.getValue());

                // Configure VPP classifier node
                SfcVppUtils.configureClassifierVxlanGpeNsh(mountPoint, sffName, SFC_BD_NAME, sffIp, sffInfo.ip,
                        sffInfo.pathId, sffInfo.serviceIndex);

                // For the traffic from the first SFF to VPP classifier node
                SfcVppUtils.addNshEntry(mountPoint, reversePathId, reverseServiceIndex, sffName.getValue());
                SfcVppUtils.addNshMapWithPop(mountPoint, reversePathId, reverseServiceIndex, reversePathId,
                        reverseServiceIndex, null, sffName.getValue());

                // Configure the first SFF, VPP renderer doesn't know this
                SfcVppUtils.configureVxlanGpeNsh(sffInfo.mountPoint, sffInfo.sffName, SFC_BD_NAME, sffInfo.ip,
                        sffIp, reversePathId, reverseServiceIndex);
            }
        }
        return true;
    }

    private boolean removeVxlanGpeClassifier(ServiceFunctionClassifier scf) {
        Optional<Acl> theAcl = extractAcl(scf);
        if (!theAcl.isPresent() || !validateInputs(theAcl.get())) {
            LOG.error("Could not retrieve the ACL from the classifier: {}", scf);
            return false;
        }
        Map<RspName, List<Pair<HexString>>> rspPairList = new HashMap<RspName, List<Pair<HexString>>>();
        List<Ace> aceList = theAcl.get().getAccessListEntries().getAce();
        for (Ace ace : aceList) {
            Optional<RspName> rspName = Optional.ofNullable(ace.getActions())
                    .map(theActions -> theActions.getAugmentation(Actions1.class))
                    .map(actions1 -> (AclRenderedServicePath) actions1.getSfcAction())
                    .map(aclRsp -> new RspName(aclRsp.getRenderedServicePath()));
            if (!rspName.isPresent()) {
                LOG.error("Could not retrieve the RSP from the classifier: {}", scf);
            }
            List<Pair<HexString>> pairList = rspPairList.get(rspName.get());
            if (pairList == null) {
                pairList = new ArrayList<>();
                rspPairList.put(rspName.get(), pairList);
            }
            pairList.add(getMaskAndMatch(ace.getMatches()));
        }

        List<SclServiceFunctionForwarder> sfflist = scf.getSclServiceFunctionForwarder();
        if (sfflist == null) {
            LOG.error("sfflist is null");
            return false;
        }

        for (SclServiceFunctionForwarder sclSff : sfflist) {
            SffName sffName = new SffName(sclSff.getName());
            Optional<String> itfName = getInterfaceNameFromClassifier(sclSff);

            if (!itfName.isPresent()) {
                LOG.error("Could not get LogicalInterface from the classifier's attachment point");
            }

            IpAddress sffIp = SfcVppUtils.getSffFirstDplIp(sffName);
            DataBroker mountPoint = SfcVppUtils.getSffMountpoint(this.nodeManager.getMountPointService(), sffName);
            for (RspName rsp : rspPairList.keySet()) {
                SffInfo sffInfo = getFirstSffInfoInRsp(rsp);
                RspName reverseRspName = getReverseRspName(rsp);
                RenderedServicePath reverseRenderedServicePath = getRenderedServicePath(reverseRspName);
                if (reverseRenderedServicePath == null) {
                    LOG.error("reverseRenderedServicePath is null\n");
                    return false;
                }

                Long reversePathId = reverseRenderedServicePath.getPathId();
                List<RenderedServicePathHop> hopList = reverseRenderedServicePath.getRenderedServicePathHop();
                if (hopList == null || hopList.isEmpty()) {
                    LOG.error("Rendered path {} does not contain any hop",
                            reverseRenderedServicePath.getName().getValue());
                    return false;
                }

                RenderedServicePathHop lastRspHop = Iterables.getLast(hopList);
                if (lastRspHop == null) {
                    LOG.error("kast rsp hop is null\n");
                    return false;
                }

                Short reverseServiceIndex = (short) (lastRspHop.getServiceIndex() - 1);

                // Remove VPP classfier classify tables, sessions and disable ingress ACL
                List<Pair<HexString>> pairList = rspPairList.get(rsp);
                int length = pairList.size();
                int index = 0;
                List<String> tableKeyList = new ArrayList<>();
                List<HexString> matchList = new ArrayList<>();
                for (Pair<HexString> maskMatch : pairList) {
                    String classifyTableKey = SfcVppUtils.getSavedClassifyTableKey(sffName.getValue(),
                            rsp.getValue(), index);
                    tableKeyList.add(classifyTableKey);
                    matchList.add(maskMatch.getMatch());
                    index++;
                }
                // Disable Ingress Acl
                SfcVppUtils.disableIngressAcl(mountPoint, itfName.get(), SfcVppUtils.buildClassifyTableKey(0),
                        sffName.getValue());

                // Remove classify sessions and tables
                SfcVppUtils.removeVppClassifier(mountPoint, sffName, tableKeyList, matchList);

                // Remove NSH entry and map for the traffic from the first SFF to VPP classifier node
                SfcVppUtils.removeNshMap(mountPoint, reversePathId, reverseServiceIndex, reversePathId,
                        reverseServiceIndex, sffName.getValue());
                SfcVppUtils.removeNshEntry(mountPoint, reversePathId, reverseServiceIndex, sffName.getValue());

                // Remove configuration for the first SFF, VPP renderer doesn't know this
                SfcVppUtils.removeVxlanGpeNsh(sffInfo.mountPoint, sffInfo.sffName, sffInfo.ip, sffIp, reversePathId,
                        reverseServiceIndex);

                // Remove vxlan-gpe port and nsh entry and map for classifier
                SfcVppUtils.removeClassifierVxlanGpeNsh(mountPoint, sffName, SFC_BD_NAME, sffIp, sffInfo.ip,
                        sffInfo.pathId, sffInfo.serviceIndex);
            }
        }
        return true;
    }

    public void addScf(ServiceFunctionClassifier scf) {
        configureVxlanGpeClassifier(scf);
    }

    public void removeScf(ServiceFunctionClassifier scf) {
        removeVxlanGpeClassifier(scf);
    }

}