Calculate IGMP checksum and use more reasonble max response time.

Also made IGMP properties configurable at runtime.

Change-Id: I98b40a43a0c17b7bf21f1bd622032c64d7434214
diff --git a/apps/cordmcast/src/main/java/org/onosproject/cordmcast/CordMcast.java b/apps/cordmcast/src/main/java/org/onosproject/cordmcast/CordMcast.java
index 86cd367..8fd533a 100644
--- a/apps/cordmcast/src/main/java/org/onosproject/cordmcast/CordMcast.java
+++ b/apps/cordmcast/src/main/java/org/onosproject/cordmcast/CordMcast.java
@@ -138,7 +138,9 @@
     private String fabricOnosUrl;
 
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
+        modified(context);
+
         appId = coreService.registerApplication("org.onosproject.cordmcast");
         componentConfigService.registerProperties(getClass());
         mcastService.addListener(listener);
diff --git a/apps/igmp/src/main/java/org/onosproject/igmp/IgmpSnoop.java b/apps/igmp/src/main/java/org/onosproject/igmp/IgmpSnoop.java
index 03f561b..19bd221 100644
--- a/apps/igmp/src/main/java/org/onosproject/igmp/IgmpSnoop.java
+++ b/apps/igmp/src/main/java/org/onosproject/igmp/IgmpSnoop.java
@@ -18,6 +18,7 @@
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -31,6 +32,8 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.util.SafeRecurringTask;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
@@ -63,12 +66,15 @@
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.olt.AccessDeviceConfig;
 import org.onosproject.olt.AccessDeviceData;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
 import java.util.Collection;
+import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -90,7 +96,7 @@
     private static final String DEST_IP = "224.0.0.1";
 
     private static final int DEFAULT_QUERY_PERIOD_SECS = 60;
-    private static final byte DEFAULT_IGMP_RESP_CODE = 0;
+    private static final byte DEFAULT_IGMP_RESP_CODE = 100;
     private static final String DEFAULT_MCAST_ADDR = "224.0.0.0/4";
 
     @Property(name = "multicastAddress",
@@ -118,6 +124,9 @@
     protected NetworkConfigRegistry networkConfig;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService componentConfigService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MulticastRouteService multicastService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -169,9 +178,13 @@
 
 
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
+        modified(context);
+
         appId = coreService.registerApplication("org.onosproject.igmp");
 
+        componentConfigService.registerProperties(getClass());
+
         packetService.addProcessor(processor, PacketProcessor.director(1));
 
         networkConfig.registerConfigFactory(configFactory);
@@ -208,13 +221,7 @@
 
         deviceService.addListener(deviceListener);
 
-        queryPacket = buildQueryPacket();
-
-        queryTask = queryService.scheduleWithFixedDelay(
-                SafeRecurringTask.wrap(this::querySubscribers),
-                0,
-                queryPeriod,
-                TimeUnit.SECONDS);
+        restartQueryTask();
 
         log.info("Started");
     }
@@ -229,9 +236,49 @@
         networkConfig.unregisterConfigFactory(ssmTranslateConfigFactory);
         queryTask.cancel(true);
         queryService.shutdownNow();
+        componentConfigService.unregisterProperties(getClass(), false);
         log.info("Stopped");
     }
 
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+        String strQueryPeriod = Tools.get(properties, "queryPeriod");
+        String strResponseCode = Tools.get(properties, "maxRespCode");
+        try {
+            byte newMaxRespCode = Byte.parseByte(strResponseCode);
+            if (maxRespCode != newMaxRespCode) {
+                maxRespCode = newMaxRespCode;
+                queryPacket = buildQueryPacket();
+            }
+
+            int newQueryPeriod = Integer.parseInt(strQueryPeriod);
+            if (newQueryPeriod != queryPeriod) {
+                queryPeriod = newQueryPeriod;
+                restartQueryTask();
+            }
+
+        } catch (NumberFormatException e) {
+            log.warn("Error parsing config input", e);
+        }
+
+        log.info("queryPeriod set to {}", queryPeriod);
+        log.info("maxRespCode set to {}", maxRespCode);
+    }
+
+    private void restartQueryTask() {
+        if (queryTask != null) {
+            queryTask.cancel(true);
+        }
+        queryPacket = buildQueryPacket();
+        queryTask = queryService.scheduleWithFixedDelay(
+                SafeRecurringTask.wrap(this::querySubscribers),
+                0,
+                queryPeriod,
+                TimeUnit.SECONDS);
+    }
+
     private void processFilterObjective(DeviceId devId, Port port, boolean remove) {
 
         //TODO migrate to packet requests when packet service uses filtering objectives
diff --git a/core/api/src/main/java/org/onosproject/cfg/ConfigProperty.java b/core/api/src/main/java/org/onosproject/cfg/ConfigProperty.java
index f1b602e..8d0bce8 100644
--- a/core/api/src/main/java/org/onosproject/cfg/ConfigProperty.java
+++ b/core/api/src/main/java/org/onosproject/cfg/ConfigProperty.java
@@ -44,6 +44,11 @@
         STRING,
 
         /**
+         * Indicates the value is a byte.
+         */
+        BYTE,
+
+        /**
          * Indicates the value is an integer.
          */
         INTEGER,
@@ -194,6 +199,16 @@
     }
 
     /**
+     * Returns the property value as a byte.
+     *
+     * @return byte value
+     */
+    public byte asByte() {
+        checkState(type == Type.BYTE, "Value is not a byte");
+        return Byte.parseByte(value);
+    }
+
+    /**
      * Returns the property value as an integer.
      *
      * @return integer value
diff --git a/utils/misc/src/main/java/org/onlab/packet/IGMP.java b/utils/misc/src/main/java/org/onlab/packet/IGMP.java
index 212c7a1..af70d8c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/IGMP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/IGMP.java
@@ -15,6 +15,8 @@
  */
 package org.onlab.packet;
 
+import org.slf4j.Logger;
+
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -22,8 +24,6 @@
 import java.util.List;
 import java.util.Map;
 
-import org.slf4j.Logger;
-
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.packet.PacketUtils.checkInput;
@@ -33,7 +33,7 @@
  * Implements IGMP control packet format.
  */
 public class IGMP extends BasePacket {
-    private final Logger log = getLogger(getClass());
+    private static final Logger log = getLogger(IGMP.class);
 
     public static final byte TYPE_IGMPV3_MEMBERSHIP_QUERY = 0x11;
     public static final byte TYPE_IGMPV1_MEMBERSHIP_REPORT = 0x12;
@@ -169,6 +169,8 @@
         // Must calculate checksum
         bb.putShort((short) 0);
 
+
+
         switch (this.igmpType) {
 
             case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
@@ -191,6 +193,21 @@
         }
 
         int size = bb.position();
+
+        // compute checksum if needed
+        if (this.checksum == 0) {
+            bb.rewind();
+            int accumulation = 0;
+            for (int i = 0; i < size * 2; ++i) {
+                accumulation += 0xffff & bb.getShort();
+            }
+            accumulation = (accumulation >> 16 & 0xffff)
+                    + (accumulation & 0xffff);
+            this.checksum = (short) (~accumulation & 0xffff);
+            bb.putShort(2, this.checksum);
+        }
+
+
         bb.position(0);
         byte[] rdata = new byte[size];
         bb.get(rdata, 0, size);
@@ -238,7 +255,7 @@
             igmp.igmpType = bb.get();
             igmp.resField = bb.get();
             igmp.checksum = bb.getShort();
-            int len = MINIMUM_HEADER_LEN;
+
             String msg;
 
             switch (igmp.igmpType) {