[ONOS-3833] Identify the port chain for the packet

Change-Id: Id2ca1373588c8bc77c031149c7e85337c776bc4b
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 8187edd..ae6dfad 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
@@ -17,6 +17,8 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.Collection;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
@@ -28,15 +30,16 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
+import org.onlab.packet.TCP;
+import org.onlab.packet.UDP;
 import org.onlab.util.ItemNotFoundException;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.NshServicePathId;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
@@ -46,6 +49,9 @@
 import org.onosproject.sfc.installer.impl.FlowClassifierInstallerImpl;
 import org.onosproject.sfc.manager.NshSpiIdGenerators;
 import org.onosproject.sfc.manager.SfcService;
+import org.onosproject.vtnrsc.DefaultFiveTuple;
+import org.onosproject.vtnrsc.FiveTuple;
+import org.onosproject.vtnrsc.FixedIp;
 import org.onosproject.vtnrsc.FlowClassifier;
 import org.onosproject.vtnrsc.FlowClassifierId;
 import org.onosproject.vtnrsc.PortChain;
@@ -55,10 +61,15 @@
 import org.onosproject.vtnrsc.PortPairGroupId;
 import org.onosproject.vtnrsc.PortPairId;
 import org.onosproject.vtnrsc.TenantId;
+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;
 import org.onosproject.vtnrsc.service.VtnRscService;
+import org.onosproject.vtnrsc.virtualport.VirtualPortService;
 import org.slf4j.Logger;
 
 /**
@@ -71,6 +82,7 @@
     private final Logger log = getLogger(getClass());
     private static final String APP_ID = "org.onosproject.app.vtn";
     private static final int SFC_PRIORITY = 1000;
+    private static final int NULL_PORT = 0;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected VtnRscService vtnRscService;
@@ -81,6 +93,15 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PortChainService portChainService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowClassifierService flowClassifierService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected VirtualPortService virtualPortService;
+
     private SfcPacketProcessor processor = new SfcPacketProcessor();
 
     protected ApplicationId appId;
@@ -238,32 +259,193 @@
 
     private class SfcPacketProcessor implements PacketProcessor {
 
+        /**
+         * Check for given ip match with the fixed ips for the virtual port.
+         *
+         * @param vPortId virtual port id
+         * @param ip ip address to match
+         * @return true if the ip match with the fixed ips in virtual port false otherwise
+         */
+        private boolean checkIpInVirtualPort(VirtualPortId vPortId, IpAddress ip) {
+            boolean found = false;
+            Set<FixedIp> ips = virtualPortService.getPort(vPortId).fixedIps();
+            for (FixedIp fixedIp : ips) {
+                if (fixedIp.ip().equals(ip)) {
+                    found = true;
+                    break;
+                }
+            }
+            return found;
+        }
+
+        /**
+         * Find the port chain for the received packet.
+         *
+         * @param fiveTuple five tuple info from the packet
+         * @return portChainId id of port chain
+         */
+        private PortChainId findPortChainFromFiveTuple(FiveTuple fiveTuple) {
+
+            PortChainId portChainId = null;
+
+            Iterable<PortChain> portChains = portChainService.getPortChains();
+            if (portChains == null) {
+                log.error("Could not retrive port chain list");
+            }
+
+            // Identify the port chain to which the packet belongs
+            for (final PortChain portChain : portChains) {
+
+                Iterable<FlowClassifierId> flowClassifiers = portChain.flowClassifiers();
+
+                // One port chain can have multiple flow classifiers.
+                for (final FlowClassifierId flowClassifierId : flowClassifiers) {
+
+                    FlowClassifier flowClassifier = flowClassifierService.getFlowClassifier(flowClassifierId);
+                    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)) {
+                            match = true;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    // Check whether source ip prefix is set in flow classifier
+                    if (flowClassifier.srcIpPrefix() != null) {
+                        if (flowClassifier.srcIpPrefix().contains(fiveTuple.ipSrc())) {
+                            match = true;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    // Check whether destination ip prefix is set in flow classifier
+                    if (flowClassifier.dstIpPrefix() != null) {
+                        if (flowClassifier.dstIpPrefix().contains(fiveTuple.ipDst())) {
+                            match = true;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    // Check whether source port is set in flow classifier
+                    if (fiveTuple.portSrc().toLong() >= flowClassifier.minSrcPortRange() ||
+                            fiveTuple.portSrc().toLong() <= flowClassifier.maxSrcPortRange()) {
+                        match = true;
+                    } else {
+                        continue;
+                    }
+
+                    // Check whether destination port is set in flow classifier
+                    if (fiveTuple.portDst().toLong() >= flowClassifier.minSrcPortRange() ||
+                            fiveTuple.portDst().toLong() <= flowClassifier.maxSrcPortRange()) {
+                        match = true;
+                    } else {
+                        continue;
+                    }
+
+                    // Check whether neutron source port is set in flow classfier
+                    if ((flowClassifier.srcPort() != null) && (!flowClassifier.srcPort().portId().isEmpty())) {
+                        match = checkIpInVirtualPort(VirtualPortId.portId(flowClassifier.srcPort().portId()),
+                                                     fiveTuple.ipSrc());
+                        if (!match) {
+                            continue;
+                        }
+                    }
+
+                    // Check whether destination neutron destination port is set in flow classifier
+                    if ((flowClassifier.dstPort() != null) && (!flowClassifier.dstPort().portId().isEmpty())) {
+                        match = checkIpInVirtualPort(VirtualPortId.portId(flowClassifier.dstPort().portId()),
+                                                     fiveTuple.ipDst());
+                        if (!match) {
+                            continue;
+                        }
+                    }
+
+                    if (match) {
+                        portChainId = portChain.portChainId();
+                        break;
+                    }
+                }
+            }
+            return portChainId;
+        }
+
+
+        /**
+         * Get the tenant id for the given mac address.
+         *
+         * @param mac mac address
+         * @return tenantId tenant id for the given mac address
+         */
+        private TenantId getTenantId(MacAddress mac) {
+            Collection<VirtualPort> virtualPorts = virtualPortService.getPorts();
+            for (VirtualPort virtualPort : virtualPorts) {
+                if (virtualPort.macAddress().equals(mac)) {
+                    return virtualPort.tenantId();
+                }
+            }
+            return null;
+        }
+
         @Override
         public void process(PacketContext context) {
             Ethernet packet = context.inPacket().parsed();
             if (packet == null) {
                 return;
             }
-            // get the five tupple parameters for the packet
+            // get the five tuple parameters for the packet
             short ethType = packet.getEtherType();
-            VlanId vlanId = VlanId.vlanId(packet.getVlanID());
-            MacAddress srcMac = packet.getSourceMAC();
-            MacAddress dstMac = packet.getDestinationMAC();
-            IpAddress ipSrc;
-            IpAddress ipDst;
+            IpAddress ipSrc = null;
+            IpAddress ipDst = null;
+            int portSrc = 0;
+            int portDst = 0;
+            byte protocol = 0;
+            MacAddress macSrc = packet.getSourceMAC();
+            TenantId tenantId = getTenantId(macSrc);
 
             if (ethType == Ethernet.TYPE_IPV4) {
                 IPv4 ipv4Packet = (IPv4) packet.getPayload();
                 ipSrc = IpAddress.valueOf(ipv4Packet.getSourceAddress());
                 ipDst = IpAddress.valueOf(ipv4Packet.getDestinationAddress());
+                protocol = ipv4Packet.getProtocol();
+                if (protocol == IPv4.PROTOCOL_TCP) {
+                    TCP tcpPacket = (TCP) ipv4Packet.getPayload();
+                    portSrc = tcpPacket.getSourcePort();
+                    portDst = tcpPacket.getDestinationPort();
+                } else  if (protocol == IPv4.PROTOCOL_UDP) {
+                    UDP udpPacket = (UDP) ipv4Packet.getPayload();
+                    portSrc = udpPacket.getSourcePort();
+                    portDst = udpPacket.getDestinationPort();
+                }
             } else if (ethType == Ethernet.TYPE_IPV6) {
-                IPv6 ipv6Packet = (IPv6) packet.getPayload();
-                ipSrc = IpAddress.valueOf(ipv6Packet.getSourceAddress().toString());
-                ipDst = IpAddress.valueOf(ipv6Packet.getDestinationAddress().toString());
+                return;
             }
 
-            //todo
-           //identify the port chain to which the packet belongs
+
+            FiveTuple fiveTuple = DefaultFiveTuple.builder()
+                    .setIpSrc(ipSrc)
+                    .setIpDst(ipDst)
+                    .setPortSrc(PortNumber.portNumber(portSrc))
+                    .setPortDst(PortNumber.portNumber(portDst))
+                    .setProtocol(protocol)
+                    .setTenantId(tenantId)
+                    .build();
+
+            PortChainId portChainId = findPortChainFromFiveTuple(fiveTuple);
+
+            if (portChainId == null) {
+                log.error("Packet does not match with any classifier");
+                return;
+            }
+
+            // TODO
+            // download the required flow rules for classifier and forwarding
+            // resend the packet back to classifier
         }
     }
 }