[SEBA-40, SEBA-942 & SEBA-957] Operational status for IGMP Channel, IGMP Group and Invalid IGMP Packets

Change-Id: I8efb475f69ca5df8f2f4c1daaaba52b8ce8572f7
diff --git a/src/main/java/org/opencord/igmpproxy/IgmpManager.java b/src/main/java/org/opencord/igmpproxy/IgmpManager.java
index a42d41b..d5677f0 100644
--- a/src/main/java/org/opencord/igmpproxy/IgmpManager.java
+++ b/src/main/java/org/opencord/igmpproxy/IgmpManager.java
@@ -72,6 +72,8 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -85,6 +87,13 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
+import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
+import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
+import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
+import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
+import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
+import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
+
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 
@@ -202,12 +211,15 @@
 
     protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
 
+    private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE,  MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
+                              CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
+
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(APP_NAME);
         coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
         packetService.addProcessor(processor, PacketProcessor.director(4));
-        IgmpSender.init(packetService, mastershipService);
+        IgmpSender.init(packetService, mastershipService, igmpStatisticsManager);
 
         networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
         networkConfig.registerConfigFactory(igmpproxyConfigFactory);
@@ -268,8 +280,10 @@
         maxResp = calculateMaxResp(maxResp);
         if (gAddr != null && !gAddr.isZero()) {
             StateMachine.specialQuery(deviceId, gAddr, maxResp);
+            igmpStatisticsManager.getIgmpStats().increaseIgmpGrpSpecificMembershipQuery();
         } else {
             StateMachine.generalQuery(deviceId, maxResp);
+            igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
         }
     }
 
@@ -284,12 +298,15 @@
                 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
                 if (accessDevice.isPresent()) {
                     StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
+                    igmpStatisticsManager.getIgmpStats().increaseIgmpGrpAndSrcSpecificMembershipQuery();
                 }
             });
+            igmpStatisticsManager.getIgmpStats().increaseCurrentGrpNumCounter();
         } else {
             //Don't know which group is targeted by the query
             //So query all the members(in all the OLTs) and proxy their reports
             StateMachine.generalQuery(maxResponseTime);
+            igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
         }
     }
 
@@ -315,6 +332,7 @@
         Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
         if (!groupIp.isMulticast()) {
             log.info(groupIp.toString() + " is not a valid group address");
+            igmpStatisticsManager.getIgmpStats().increaseFailJoinReqUnknownMulticastIpCounter();
             return;
         }
         Ip4Address srcIp = getDeviceIp(deviceId);
@@ -324,6 +342,9 @@
 
         ArrayList<Ip4Address> sourceList = new ArrayList<>();
 
+        if (!validMembershipModes.contains(recordType)) {
+            igmpStatisticsManager.getIgmpStats().increaseReportsRxWithWrongModeCounter();
+        }
         if (igmpGroup.getSources().size() > 0) {
             igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
             if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
@@ -368,6 +389,7 @@
                     igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
                     log.warn("Unable to process IGMP Join from {} since no source " +
                                      "configuration is found.", deviceId);
+                    igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
                     return;
                 }
 
@@ -389,6 +411,7 @@
                 boolean isJoined = StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
                 if (isJoined) {
                     igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
+                    igmpStatisticsManager.getIgmpStats().increaseIgmpChannelJoinCounter();
                 } else {
                     igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
                 }
@@ -403,6 +426,7 @@
                     //add sink to the route
                     multicastService.addSinks(route, Sets.newHashSet(cp));
                 });
+                igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
 
             }
             groupMember.resetAllTimers();
@@ -413,6 +437,7 @@
             if (groupMember == null) {
                 log.info("receive leave but no instance, group " + groupIp.toString() +
                         " device:" + deviceId.toString() + " port:" + portNumber.toString());
+                igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
                 return;
             } else {
                 groupMember.setLeave(true);
@@ -480,12 +505,14 @@
                         return;
                     }
 
+                    igmpStatisticsManager.getIgmpStats().increaseIgmpValidChecksumCounter();
                     short vlan = ethPkt.getVlanID();
                     DeviceId deviceId = pkt.receivedFrom().deviceId();
 
                     if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
                             !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
                         log.error("Device not registered in netcfg :" + deviceId.toString());
+                        igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
                         return;
                     }
 
@@ -535,6 +562,7 @@
                         default:
                             log.warn("wrong IGMP v3 type:" + igmp.getIgmpType());
                             igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
+                            igmpStatisticsManager.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
                             break;
                     }
 
diff --git a/src/main/java/org/opencord/igmpproxy/IgmpSender.java b/src/main/java/org/opencord/igmpproxy/IgmpSender.java
index b455cb4..02cc4ee 100644
--- a/src/main/java/org/opencord/igmpproxy/IgmpSender.java
+++ b/src/main/java/org/opencord/igmpproxy/IgmpSender.java
@@ -17,6 +17,8 @@
 
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IGMP;
+import org.onlab.packet.IGMP.IGMPv2;
+import org.onlab.packet.IGMP.IGMPv3;
 import org.onlab.packet.IGMPMembership;
 import org.onlab.packet.IGMPQuery;
 import org.onlab.packet.IPv4;
@@ -49,6 +51,7 @@
     private static IgmpSender instance = null;
     private PacketService packetService;
     private MastershipService mastershipService;
+    private IgmpStatisticsService igmpStatisticsService;
     private boolean withRAUplink = true;
     private boolean withRADownlink = false;
     private short mvlan = DEFAULT_MVLAN;
@@ -56,13 +59,16 @@
     private int maxResp = DEFAULT_MEX_RESP;
     private Logger log = LoggerFactory.getLogger(getClass());
 
-    private IgmpSender(PacketService packetService, MastershipService mastershipService) {
+    private IgmpSender(PacketService packetService, MastershipService mastershipService,
+             IgmpStatisticsService igmpStatisticsService) {
         this.packetService = packetService;
         this.mastershipService = mastershipService;
+        this.igmpStatisticsService = igmpStatisticsService;
     }
 
-    public static void init(PacketService packetService, MastershipService mastershipService) {
-        instance = new IgmpSender(packetService, mastershipService);
+    public static void init(PacketService packetService, MastershipService mastershipService,
+            IgmpStatisticsService igmpStatisticsService) {
+        instance = new IgmpSender(packetService, mastershipService, igmpStatisticsService);
     }
 
     public static IgmpSender getInstance() {
@@ -118,7 +124,7 @@
         return buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY, groupIp, null, sourceIp, false);
     }
 
-    private Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
+    protected Ethernet buildIgmpPacket(byte type, Ip4Address groupIp, IGMPMembership igmpMembership,
                                      Ip4Address sourceIp, boolean isV2Query) {
 
         IGMP igmpPacket;
@@ -164,6 +170,7 @@
             case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
                 return null;
             default:
+                igmpStatisticsService.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
                 return null;
         }
 
@@ -216,10 +223,23 @@
             return;
         }
 
+        IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
+        IGMP igmp = (IGMP) ipv4Pkt.getPayload();
+        // We are checking the length of packets. Right now the counter value will be 0 because of internal translation
+        // As packet length will always be valid
+        // This counter will be useful in future if we change the procedure to generate the packets.
+        if ((igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT
+             || igmp.getIgmpType() == IGMP.TYPE_IGMPV2_LEAVE_GROUP) && igmp.serialize().length < IGMPv2.HEADER_LENGTH) {
+                 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
+        } else if (igmp.getIgmpType() == IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT
+            && igmp.serialize().length < IGMPv3.MINIMUM_HEADER_LEN) {
+                 igmpStatisticsService.getIgmpStats().increaseInvalidIgmpLength();
+        }
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setOutput(portNumber).build();
         OutboundPacket packet = new DefaultOutboundPacket(deviceId,
                 treatment, ByteBuffer.wrap(ethPkt.serialize()));
+        igmpStatisticsService.getIgmpStats().increaseValidIgmpPacketCounter();
         packetService.emit(packet);
 
     }
diff --git a/src/main/java/org/opencord/igmpproxy/IgmpStatistics.java b/src/main/java/org/opencord/igmpproxy/IgmpStatistics.java
index c7c4650..f330e9f 100644
--- a/src/main/java/org/opencord/igmpproxy/IgmpStatistics.java
+++ b/src/main/java/org/opencord/igmpproxy/IgmpStatistics.java
@@ -50,6 +50,32 @@
     private AtomicLong igmpMsgReceived = new AtomicLong();
     //Total number of invalid IGMP messages received
     private AtomicLong invalidIgmpMsgReceived = new AtomicLong();
+    //Counter for unknown igmp type
+    private AtomicLong unknownIgmpTypePacketsRxCounter = new AtomicLong();
+    // Counter for igmp report with wrong mode.
+    private AtomicLong reportsRxWithWrongModeCounter = new AtomicLong();
+    // Counter for failed join due to insufficient permission access
+    private AtomicLong failJoinReqInsuffPermissionAccessCounter = new AtomicLong();
+    // Counter for invalid group ip address i.e not a valid multicast address.
+    private AtomicLong failJoinReqUnknownMulticastIpCounter = new AtomicLong();
+    // Counter for unconfigured group
+    private AtomicLong unconfiguredGroupCounter = new AtomicLong();
+    // Counter for valid igmp packet
+    private AtomicLong validIgmpPacketCounter = new AtomicLong();
+    // Counter for current number of igmp channel joins
+    private AtomicLong igmpChannelJoinCounter = new AtomicLong();
+    // Counter for current group number
+    private AtomicLong currentGrpNumCounter = new AtomicLong();
+    // Counter for igmp Checksum
+    private AtomicLong igmpValidChecksumCounter = new AtomicLong();
+    // Counter for Invalid Igmp Length
+    private AtomicLong invalidIgmpLength = new AtomicLong();
+    //Total number of general IGMP membership query messages received
+    private AtomicLong igmpGeneralMembershipQuery = new AtomicLong();
+    //Total number of group specific IGMP membership query messages received
+    private AtomicLong igmpGrpSpecificMembershipQuery = new AtomicLong();
+    //Total number of group and source specific IGMP membership query messages received
+    private AtomicLong igmpGrpAndSrcSpecificMembershipQuery = new AtomicLong();
 
     public Long getIgmpJoinReq() {
         return igmpJoinReq.get();
@@ -156,4 +182,107 @@
         totalMsgReceived.incrementAndGet();
     }
 
+    public Long getValidIgmpPacketCounter() {
+        return validIgmpPacketCounter.get();
+    }
+
+    public void increaseValidIgmpPacketCounter() {
+        validIgmpPacketCounter.incrementAndGet();
+    }
+
+    public Long getCurrentGrpNumCounter() {
+        return currentGrpNumCounter.get();
+    }
+
+    public void increaseCurrentGrpNumCounter() {
+        currentGrpNumCounter.incrementAndGet();
+    }
+
+    public Long getIgmpChannelJoinCounter() {
+        return igmpChannelJoinCounter.get();
+    }
+    public Long getIgmpValidChecksumCounter() {
+        return igmpValidChecksumCounter.get();
+    }
+
+    public void increaseIgmpChannelJoinCounter() {
+        igmpChannelJoinCounter.incrementAndGet();
+    }
+
+    public void increaseIgmpValidChecksumCounter() {
+        igmpValidChecksumCounter.incrementAndGet();
+    }
+
+    public Long getUnconfiguredGroupCounter() {
+        return unconfiguredGroupCounter.get();
+    }
+
+    public void increaseUnconfiguredGroupCounter() {
+        unconfiguredGroupCounter.incrementAndGet();
+    }
+
+    public Long getFailJoinReqUnknownMulticastIpCounter() {
+        return failJoinReqUnknownMulticastIpCounter.get();
+    }
+
+    public void increaseFailJoinReqUnknownMulticastIpCounter() {
+        failJoinReqUnknownMulticastIpCounter.incrementAndGet();
+    }
+
+    public Long getFailJoinReqInsuffPermissionAccessCounter() {
+        return failJoinReqInsuffPermissionAccessCounter.get();
+    }
+
+    public void increaseFailJoinReqInsuffPermissionAccessCounter() {
+        failJoinReqInsuffPermissionAccessCounter.incrementAndGet();
+    }
+
+    public Long getReportsRxWithWrongModeCounter() {
+        return reportsRxWithWrongModeCounter.get();
+    }
+
+    public Long getUnknownIgmpTypePacketsRxCounter() {
+        return unknownIgmpTypePacketsRxCounter.get();
+    }
+
+    public void increaseUnknownIgmpTypePacketsRxCounter() {
+        unknownIgmpTypePacketsRxCounter.incrementAndGet();
+    }
+
+    public void increaseReportsRxWithWrongModeCounter() {
+        reportsRxWithWrongModeCounter.incrementAndGet();
+    }
+
+    public Long getInvalidIgmpLength() {
+        return invalidIgmpLength.get();
+    }
+
+    public void increaseInvalidIgmpLength() {
+        invalidIgmpLength.incrementAndGet();
+    }
+
+    public Long getIgmpGeneralMembershipQuery() {
+        return igmpGeneralMembershipQuery.get();
+    }
+
+    public Long getIgmpGrpSpecificMembershipQuery() {
+        return igmpGrpSpecificMembershipQuery.get();
+    }
+
+    public Long getIgmpGrpAndSrcSpecificMembershipQuery() {
+        return igmpGrpAndSrcSpecificMembershipQuery.get();
+    }
+
+    public void increaseIgmpGeneralMembershipQuery() {
+        igmpGeneralMembershipQuery.incrementAndGet();
+    }
+
+    public void increaseIgmpGrpSpecificMembershipQuery() {
+        igmpGrpSpecificMembershipQuery.incrementAndGet();
+    }
+
+    public void increaseIgmpGrpAndSrcSpecificMembershipQuery() {
+        igmpGrpAndSrcSpecificMembershipQuery.incrementAndGet();
+    }
+
 }
diff --git a/src/main/java/org/opencord/igmpproxy/IgmpStatisticsManager.java b/src/main/java/org/opencord/igmpproxy/IgmpStatisticsManager.java
index a30c5e8..af834ef 100644
--- a/src/main/java/org/opencord/igmpproxy/IgmpStatisticsManager.java
+++ b/src/main/java/org/opencord/igmpproxy/IgmpStatisticsManager.java
@@ -132,6 +132,19 @@
             log.debug("--Igmpv3MembershipReport--" + igmpStats.getIgmpv3MembershipReport());
             log.debug("--InvalidIgmpMsgReceived--" + igmpStats.getInvalidIgmpMsgReceived());
             log.debug("--TotalMsgReceived--  " + igmpStats.getTotalMsgReceived());
+            log.debug("--UnknownIgmpTypePacketsRx--" + igmpStats.getUnknownIgmpTypePacketsRxCounter());
+            log.debug("--ReportsRxWithWrongMode--" + igmpStats.getReportsRxWithWrongModeCounter());
+            log.debug("--FailJoinReqInsuffPermission--" + igmpStats.getFailJoinReqInsuffPermissionAccessCounter());
+            log.debug("--FailJoinReqUnknownMulticastIp--" + igmpStats.getFailJoinReqUnknownMulticastIpCounter());
+            log.debug("--UnconfiguredGroupCounter--" + igmpStats.getUnconfiguredGroupCounter());
+            log.debug("--ValidIgmpPacketCounter--" + igmpStats.getValidIgmpPacketCounter());
+            log.debug("--IgmpChannelJoinCounter--" + igmpStats.getIgmpChannelJoinCounter());
+            log.debug("--CurrentGrpNumCounter--" + igmpStats.getCurrentGrpNumCounter());
+            log.debug("--IgmpValidChecksumCounter--" + igmpStats.getIgmpValidChecksumCounter());
+            log.debug("--InvalidIgmpLength--" + igmpStats.getInvalidIgmpLength());
+            log.debug("--IgmpGeneralMembershipQuery--" + igmpStats.getIgmpGeneralMembershipQuery());
+            log.debug("--IgmpGrpSpecificMembershipQuery--" + igmpStats.getIgmpGrpSpecificMembershipQuery());
+            log.debug("--IgmpGrpAndSrcSpecificMembershipQuery--" + igmpStats.getIgmpGrpAndSrcSpecificMembershipQuery());
         }
 
         post(new IgmpStatisticsEvent(IgmpStatisticsEvent.Type.STATS_UPDATE, igmpStats));
diff --git a/src/test/java/org/opencord/igmpproxy/IgmpManagerBase.java b/src/test/java/org/opencord/igmpproxy/IgmpManagerBase.java
index 05581ed..4c68b1b 100644
--- a/src/test/java/org/opencord/igmpproxy/IgmpManagerBase.java
+++ b/src/test/java/org/opencord/igmpproxy/IgmpManagerBase.java
@@ -17,6 +17,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.IGMP;
+import org.onlab.packet.IGMPMembership;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
@@ -61,8 +63,6 @@
 import org.opencord.sadis.BaseInformationService;
 import org.opencord.sadis.SadisService;
 import org.opencord.sadis.SubscriberAndDeviceInformation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -84,9 +84,12 @@
     // Device configuration
     protected static final DeviceId DEVICE_ID_OF_A = DeviceId.deviceId("of:1");
     protected static final DeviceId DEVICE_ID_OF_B = DeviceId.deviceId("of:2");
+    protected static final DeviceId DEVICE_ID_OF_C = DeviceId.deviceId("of:00000000000000003");
 
     //Multicast ip address
     protected static final Ip4Address GROUP_IP = Ip4Address.valueOf("224.0.0.0");
+    //Unknown group Ip address
+    protected static final Ip4Address UNKNOWN_GRP_IP = Ip4Address.valueOf("124.0.0.0");
     // Source ip address of two different device.
     protected static final Ip4Address SOURCE_IP_OF_A = Ip4Address.valueOf("10.177.125.4");
     protected static final Ip4Address SOURCE_IP_OF_B = Ip4Address.valueOf("10.177.125.5");
@@ -97,11 +100,13 @@
     // Uplink ports for two olts A and B
     protected static final PortNumber PORT_A = PortNumber.portNumber(1);
     protected static final PortNumber PORT_B = PortNumber.portNumber(2);
+    protected static final PortNumber PORT_C = PortNumber.portNumber(3);
     protected static final PortNumber PORT_NNI = PortNumber.portNumber(65536);
 
     // Connect Point mode for two olts
     protected static final ConnectPoint CONNECT_POINT_A = new ConnectPoint(DEVICE_ID_OF_A, PORT_A);
     protected static final ConnectPoint CONNECT_POINT_B = new ConnectPoint(DEVICE_ID_OF_B, PORT_B);
+    protected static final ConnectPoint CONNECT_POINT_C = new ConnectPoint(DEVICE_ID_OF_C, PORT_C);
 
     protected static final String CLIENT_NAS_PORT_ID = "PON 1/1";
     protected static final String CLIENT_CIRCUIT_ID = "CIR-PON 1/1";
@@ -112,15 +117,19 @@
     private static final String NNI_PREFIX = "nni";
 
     protected List<Port> lsPorts = new ArrayList<Port>();
+    protected List<Device> lsDevices = new ArrayList<Device>();
     // Flag for adding two different devices in oltData
     protected boolean flagForDevice = true;
     PacketContext context;
     // Flag for sending two different packets
     protected boolean flagForPacket = true;
+    // Flag for sending two different packets
+    protected boolean flagForQueryPacket = false;
+    // Flag to check permission
+    boolean flagForPermission = false;
     // List to store the packets emitted
     protected List<OutboundPacket> savedPackets;
     protected PacketProcessor packetProcessor;
-    private Logger log = LoggerFactory.getLogger(getClass());
 
     class MockDeviceService extends DeviceServiceAdapter {
 
@@ -149,6 +158,17 @@
         }
 
         @Override
+        public Iterable<Device> getAvailableDevices() {
+            DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
+                    .set(AnnotationKeys.MANAGEMENT_ADDRESS, SOURCE_IP_OF_A.toString());
+            SparseAnnotations annotations = annotationsBuilder.build();
+            Annotations[] da = {annotations };
+            Device deviceA = new DefaultDevice(null, DEVICE_ID_OF_C, Device.Type.OTHER, "", "", "", "", null, da);
+            lsDevices.add(deviceA);
+            return lsDevices;
+        }
+
+        @Override
         public Port getPort(DeviceId deviceId, PortNumber portNumber) {
             if (portNumber.equals(PORT_NNI)) {
                 DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
@@ -196,7 +216,10 @@
 
         @Override
         public ConnectPoint getSourceDeviceAndPort() {
-              return COMMON_CONNECT_POINT;
+            if (flagForPermission) {
+                return null;
+            }
+            return COMMON_CONNECT_POINT;
         }
 
         @Override
@@ -400,32 +423,32 @@
      * @param reply Ethernet packet
      * @throws InterruptedException
      */
-   void sendPacket(Ethernet reply, boolean isSingleSend) {
+     void sendPacket(Ethernet reply) {
 
-        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+         if (reply != null) {
+             final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
 
-        if (isSingleSend) {
-            InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
-            context = new TestPacketContext(127L, inBoundPacket, null, false);
+             if (flagForQueryPacket) {
+                 InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_C, reply, byteBuffer);
+                 context = new TestPacketContext(127L, inBoundPacket, null, false);
+                 packetProcessor.process(context);
+             } else {
+                 if (flagForPacket) {
+                     InboundPacket inPacket = new DefaultInboundPacket(CONNECT_POINT_A, reply, byteBuffer);
+                     context = new TestPacketContext(127L, inPacket, null, false);
+                     flagForPacket = false;
 
-            packetProcessor.process(context);
-        } else {
-            if (flagForPacket) {
-                InboundPacket inPacket = new DefaultInboundPacket(CONNECT_POINT_A, reply, byteBuffer);
-                context = new TestPacketContext(127L, inPacket, null, false);
-                flagForPacket = false;
+                     packetProcessor.process(context);
+                 } else {
+                     InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
+                     context = new TestPacketContext(127L, inBoundPacket, null, false);
+                     flagForPacket = true;
 
-                packetProcessor.process(context);
-            } else {
-                InboundPacket inBoundPacket = new DefaultInboundPacket(CONNECT_POINT_B, reply, byteBuffer);
-                context = new TestPacketContext(127L, inBoundPacket, null, false);
-                flagForPacket = true;
-
-                packetProcessor.process(context);
-            }
-        }
-
-   }
+                     packetProcessor.process(context);
+                 }
+             }
+         }
+    }
 
     protected class MockSadisService implements SadisService {
 
@@ -640,4 +663,20 @@
              return null;
          }
     }
+
+    Ethernet buildWrongIgmpPacket(Ip4Address groupIp, Ip4Address sourceIp) {
+        IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+        igmpMembership.setRecordType((byte) 0x33);
+
+        return IgmpSender.getInstance().buildIgmpPacket(IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT, groupIp,
+            igmpMembership, sourceIp, false);
+    }
+
+    Ethernet buildUnknownIgmpPacket(Ip4Address groupIp, Ip4Address sourceIp) {
+        IGMPMembership igmpMembership = new IGMPMembership(groupIp);
+        igmpMembership.setRecordType((byte) 0x33);
+
+        return IgmpSender.getInstance().buildIgmpPacket((byte) 0x44, groupIp, igmpMembership, sourceIp, false);
+    }
+
 }
diff --git a/src/test/java/org/opencord/igmpproxy/IgmpManagerTest.java b/src/test/java/org/opencord/igmpproxy/IgmpManagerTest.java
index b77a09c..8159463 100644
--- a/src/test/java/org/opencord/igmpproxy/IgmpManagerTest.java
+++ b/src/test/java/org/opencord/igmpproxy/IgmpManagerTest.java
@@ -96,7 +96,7 @@
         Ethernet firstPacket = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_A);
         Ethernet secondPacket = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_B);
         // Sending first packet and here shouldSendjoin flag will be true
-        sendPacket(firstPacket, false);
+        sendPacket(firstPacket);
         // Emitted packet is stored in list savedPackets
         assertNotNull(savedPackets);
         synchronized (savedPackets) {
@@ -106,7 +106,7 @@
         assertNotNull(savedPackets);
         assertEquals(1, savedPackets.size());
         // Sending the second packet with same group ip address
-        sendPacket(secondPacket, false);
+        sendPacket(secondPacket);
         synchronized (savedPackets) {
             savedPackets.wait(WAIT_TIMEOUT);
         }
@@ -126,7 +126,7 @@
         Ethernet firstPacket = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_A);
         Ethernet secondPacket = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_B);
         // Sending first packet and here shouldSendjoin flag will be true
-        sendPacket(firstPacket, false);
+        sendPacket(firstPacket);
         // Emitted packet is stored in list savedPackets
         synchronized (savedPackets) {
           savedPackets.wait(WAIT_TIMEOUT);
@@ -135,7 +135,7 @@
         assertEquals(1, savedPackets.size());
         // Sending the second packet with same group ip address which will not be emitted
         // shouldSendJoin flag will be false.
-        sendPacket(secondPacket, false);
+        sendPacket(secondPacket);
         synchronized (savedPackets) {
             savedPackets.wait(WAIT_TIMEOUT);
         }
diff --git a/src/test/java/org/opencord/igmpproxy/IgmpStatisticsTest.java b/src/test/java/org/opencord/igmpproxy/IgmpStatisticsTest.java
index e1e4f35..1198d1a 100644
--- a/src/test/java/org/opencord/igmpproxy/IgmpStatisticsTest.java
+++ b/src/test/java/org/opencord/igmpproxy/IgmpStatisticsTest.java
@@ -26,6 +26,7 @@
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
 import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.net.flow.FlowRuleServiceAdapter;
 import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
@@ -79,17 +80,21 @@
     //Test Igmp Statistics.
     @Test
     public void testIgmpStatistics() throws InterruptedException {
+        SingleStateMachine.sendQuery = false;
         igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
         igmpManager.activate();
+
         //IGMPv3 Join
+        flagForPacket = false;
         Ethernet igmpv3MembershipReportPkt = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_A);
-        sendPacket(igmpv3MembershipReportPkt, true);
+        sendPacket(igmpv3MembershipReportPkt);
         synchronized (savedPackets) {
             savedPackets.wait(WAIT_TIMEOUT);
         }
         //Leave
+        flagForPacket = false;
         Ethernet igmpv3LeavePkt = IgmpSender.getInstance().buildIgmpV3Leave(GROUP_IP, SOURCE_IP_OF_A);
-        sendPacket(igmpv3LeavePkt, true);
+        sendPacket(igmpv3LeavePkt);
         synchronized (savedPackets) {
             savedPackets.wait(WAIT_TIMEOUT);
         }
@@ -99,12 +104,56 @@
         assertEquals((long) 1, igmpStatisticsManager.getIgmpStats().getIgmpJoinReq().longValue());
         assertEquals((long) 2, igmpStatisticsManager.getIgmpStats().getIgmpv3MembershipReport().longValue());
         assertEquals((long) 1, igmpStatisticsManager.getIgmpStats().getIgmpSuccessJoinRejoinReq().longValue());
-
+        assertEquals((long) 1, igmpStatisticsManager.getIgmpStats().getUnconfiguredGroupCounter().longValue());
+        assertEquals((long) 2, igmpStatisticsManager.getIgmpStats().getValidIgmpPacketCounter().longValue());
+        assertEquals((long) 1, igmpStatisticsManager.getIgmpStats().getIgmpChannelJoinCounter().longValue());
         assertEquals((long) 1, igmpStatisticsManager.getIgmpStats().getIgmpLeaveReq().longValue());
         assertEquals((long) 2, igmpStatisticsManager.getIgmpStats().getIgmpMsgReceived().longValue());
+        assertEquals((long) 2, igmpStatisticsManager.getIgmpStats().getIgmpValidChecksumCounter().longValue());
 
     }
 
+    //Test packet with Unknown Multicast IpAddress
+    @Test
+    public void testIgmpUnknownMulticastIpAddress() throws InterruptedException {
+        SingleStateMachine.sendQuery = false;
+
+        igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
+        igmpManager.activate();
+
+        Ethernet firstPacket =
+             IgmpSender.getInstance().buildIgmpV3Join(UNKNOWN_GRP_IP, SOURCE_IP_OF_A);
+        // Sending first packet
+        sendPacket(firstPacket);
+        assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
+        assertEquals((long) 1,
+             igmpStatisticsManager.getIgmpStats().getFailJoinReqUnknownMulticastIpCounter().longValue()));
+    }
+
+    //Test Igmp Query Statistics.
+    @Test
+    public void testIgmpQueryStatistics() throws InterruptedException {
+        igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
+        igmpManager.activate();
+
+        flagForQueryPacket = true;
+        //IGMPV3 Group Specific Membership Query packet
+        Ethernet igmpv3MembershipQueryPkt = IgmpSender.getInstance().buildIgmpV3Query(GROUP_IP, SOURCE_IP_OF_A);
+        sendPacket(igmpv3MembershipQueryPkt);
+
+        //IGMPV3 General Membership Query packet
+        Ethernet igmpv3MembershipQueryPkt1 =
+              IgmpSender.getInstance().buildIgmpV3Query(Ip4Address.valueOf(0), SOURCE_IP_OF_A);
+        sendPacket(igmpv3MembershipQueryPkt1);
+        assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
+        assertEquals(igmpStatisticsManager.getIgmpStats()
+            .getIgmpGrpAndSrcSpecificMembershipQuery().longValue(), 1));
+        assertEquals(igmpStatisticsManager.getIgmpStats()
+            .getIgmpGeneralMembershipQuery().longValue(), 1);
+        assertEquals(igmpStatisticsManager.getIgmpStats()
+             .getCurrentGrpNumCounter().longValue(), 1);
+    }
+
     //Test Events
     @Test
     public void testIgmpStatisticsEvent() {
@@ -121,6 +170,55 @@
         }
     }
 
+    //Test packet with Unknown Wrong Membership mode
+    @Test
+    public void testWrongIgmpPacket() throws InterruptedException {
+        SingleStateMachine.sendQuery = false;
+
+        igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
+        igmpManager.activate();
+
+        Ethernet firstPacket = buildWrongIgmpPacket(GROUP_IP, SOURCE_IP_OF_A);
+        // Sending first packet
+        sendPacket(firstPacket);
+        assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
+        assertEquals((long) 1,
+            igmpStatisticsManager.getIgmpStats().getReportsRxWithWrongModeCounter().longValue()));
+    }
+
+    //Test packet with Unknown IGMP type.
+    @Test
+    public void testUnknownIgmpPacket() throws InterruptedException {
+        SingleStateMachine.sendQuery = false;
+
+        igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
+        igmpManager.activate();
+
+        Ethernet firstPacket = buildUnknownIgmpPacket(GROUP_IP, SOURCE_IP_OF_A);
+        // Sending first packet
+        sendPacket(firstPacket);
+        assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
+        assertEquals((long) 1,
+            igmpStatisticsManager.getIgmpStats().getUnknownIgmpTypePacketsRxCounter().longValue()));
+    }
+
+    //Test packet with Insufficient Permission.
+    @Test
+    public void testSufficientPermission() throws InterruptedException {
+        SingleStateMachine.sendQuery = false;
+
+        flagForPermission = true;
+        igmpManager.networkConfig = new TestNetworkConfigRegistry(false);
+        igmpManager.activate();
+
+        Ethernet firstPacket = IgmpSender.getInstance().buildIgmpV3Join(GROUP_IP, SOURCE_IP_OF_A);
+        // Sending first packet
+        sendPacket(firstPacket);
+        assertAfter(WAIT_TIMEOUT, WAIT_TIMEOUT * 2, () ->
+        assertEquals((long) 1,
+            igmpStatisticsManager.getIgmpStats().getFailJoinReqInsuffPermissionAccessCounter().longValue()));
+    }
+
     public class MockIgmpStatisticsEventListener implements IgmpStatisticsEventListener {
         protected List<IgmpStatisticsEvent> events = Lists.newArrayList();