igmp proxy app

Change-Id: If053dc39b611a6176b1d59004a480d65bfac9091
diff --git a/src/main/java/org/opencord/igmpproxy/GroupMember.java b/src/main/java/org/opencord/igmpproxy/GroupMember.java
new file mode 100644
index 0000000..4fab7d4
--- /dev/null
+++ b/src/main/java/org/opencord/igmpproxy/GroupMember.java
@@ -0,0 +1,262 @@
+package org.opencord.igmpproxy;
+
+import org.onlab.packet.IGMPMembership;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Date struct to keep Igmp member infomations.
+ */
+public final class  GroupMember {
+
+    private final VlanId vlan;
+    private final DeviceId deviceId;
+    private final PortNumber portNumber;
+    private final Ip4Address groupIp;
+    private final boolean v2;
+    private byte recordType = IGMPMembership.MODE_IS_INCLUDE;
+    private ArrayList<Ip4Address> sourceList = new ArrayList<>();
+    private int keepAliveQueryInterval = 0;
+    private int keepAliveQueryCount = 0;
+    private int lastQueryInterval = 0;
+    private int lastQueryCount = 0;
+    private boolean leave = false;
+
+    public GroupMember(Ip4Address groupIp, VlanId vlan, DeviceId deviceId, PortNumber portNum, boolean isV2) {
+        this.groupIp = groupIp;
+        this.vlan = vlan;
+        this.deviceId = deviceId;
+        this.portNumber = portNum;
+        v2 = isV2;
+    }
+
+    static String getkey(Ip4Address groupIp, DeviceId deviceId, PortNumber portNum) {
+        return groupIp.toString() + deviceId.toString() + portNum.toString();
+    }
+
+    public String getkey() {
+        return GroupMember.getkey(groupIp, deviceId, portNumber);
+    }
+
+    public String getId() {
+        return getkey();
+    }
+
+    public VlanId getvlan() {
+        return vlan;
+    }
+
+    public DeviceId getDeviceId() {
+        return deviceId;
+    }
+
+    public PortNumber getPortNumber() {
+        return portNumber;
+    }
+
+    public Ip4Address getGroupIp() {
+        return groupIp;
+    }
+
+    public byte getRecordType() {
+        return recordType;
+    }
+
+    public boolean getv2() {
+        return v2;
+    }
+
+    public ArrayList<Ip4Address> getSourceList() {
+        return sourceList;
+    }
+
+
+    public void updateList(byte recordType, ArrayList<Ip4Address> newSourceList) {
+        this.recordType = recordType;
+        this.sourceList.clear();
+        this.sourceList.addAll(newSourceList);
+
+        /*TODO : support SSM
+        if (this.recordType == IGMPMembership.MODE_IS_INCLUDE) {
+            switch (recordType) {
+                case IGMPMembership.MODE_IS_INCLUDE:
+                case IGMPMembership.CHANGE_TO_INCLUDE_MODE:
+                    //however , set to include<B> anyway
+                    this.sourceList = sourceList;
+                    this.recordType = IGMPMembership.MODE_IS_INCLUDE;
+                    break;
+                case IGMPMembership.MODE_IS_EXCLUDE:
+                case IGMPMembership.CHANGE_TO_EXCLUDE_MODE:
+                    //set to exclude<B>
+                    this.sourceList = sourceList;
+                    this.recordType = IGMPMembership.MODE_IS_EXCLUDE;
+                    break;
+                case IGMPMembership.ALLOW_NEW_SOURCES:
+                    //set to include <A+B>
+                    join(this.sourceList, sourceList);
+                    break;
+                case IGMPMembership.BLOCK_OLD_SOURCES:
+                    //set to include <A-B>
+                    exclude(this.sourceList, sourceList);
+                    break;
+                default:
+                    break;
+            }
+        } else if (this.recordType == IGMPMembership.MODE_IS_EXCLUDE) {
+            switch (recordType) {
+                case IGMPMembership.MODE_IS_INCLUDE:
+                case IGMPMembership.CHANGE_TO_INCLUDE_MODE:
+                    //set to include<B>
+                    this.recordType = IGMPMembership.MODE_IS_INCLUDE;
+                    this.sourceList = sourceList;
+                    break;
+                case IGMPMembership.MODE_IS_EXCLUDE:
+                case IGMPMembership.CHANGE_TO_EXCLUDE_MODE:
+                    this.sourceList = sourceList;
+                    this.recordType = IGMPMembership.MODE_IS_EXCLUDE;
+                    break;
+                case IGMPMembership.ALLOW_NEW_SOURCES:
+                    //set to exclude <A-B>
+                    exclude(this.sourceList, sourceList);
+                    break;
+                case IGMPMembership.BLOCK_OLD_SOURCES:
+                    //set to exclude <A+B>
+                    join(this.sourceList, sourceList);
+                    break;
+                default:
+                    break;
+            }
+        }*/
+
+        return;
+    }
+
+
+    /*join B to A (A+B)*/
+    private void join(ArrayList<Integer> listA, ArrayList<Integer> listB) {
+        Iterator<Integer> iterA = null;
+        Iterator<Integer> iterB = listB.iterator();
+        boolean exists;
+        while (iterB.hasNext()) {
+            iterA = listA.iterator();
+            exists = false;
+            int ipToAdd = iterB.next();
+            while (iterA.hasNext()) {
+                if (iterA.next().equals(ipToAdd)) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (!exists) {
+                listA.add(ipToAdd);
+            }
+        }
+    }
+
+    /* include A and B (A*B)*/
+    private void intersection(ArrayList<Integer> listA, ArrayList<Integer> listB) {
+        Iterator<Integer> iterA = listA.iterator();
+        Iterator<Integer> iterB;
+        boolean exists;
+
+        while (iterA.hasNext()) {
+            iterB = listB.iterator();
+            int ipToInclude = iterA.next();
+            exists = false;
+            while (iterB.hasNext()) {
+                if (iterB.next().equals(ipToInclude)) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (!exists) {
+                iterA.remove();
+            }
+        }
+    }
+
+    /*exclude B from A (A-B)*/
+    private void exclude(ArrayList<Integer> listA, ArrayList<Integer> listB) {
+        Iterator<Integer> iterA = null;
+        Iterator<Integer> iterB = listB.iterator();
+
+        while (iterB.hasNext()) {
+            iterA = listA.iterator();
+            int ipToDel = iterB.next();
+            while (iterA.hasNext()) {
+                if (iterA.next().equals(ipToDel)) {
+                    iterA.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    public void setLeave(boolean l) {
+        leave = l;
+    }
+
+    public boolean isLeave() {
+        return leave;
+    }
+
+    public int getKeepAliveQueryInterval() {
+        return keepAliveQueryInterval;
+    }
+
+    public int getKeepAliveQueryCount() {
+        return keepAliveQueryCount;
+    }
+
+    public int getLastQueryInterval() {
+        return lastQueryInterval;
+    }
+
+    public int getLastQueryCount() {
+        return lastQueryCount;
+    }
+
+    public void keepAliveQueryCount(boolean add) {
+        if (add) {
+            keepAliveQueryCount++;
+        } else {
+            keepAliveQueryCount = 0;
+        }
+    }
+
+    public void lastQueryCount(boolean add) {
+        if (add) {
+            lastQueryCount++;
+        } else {
+            lastQueryCount = 0;
+        }
+    }
+
+    public void keepAliveInterval(boolean add) {
+        if (add) {
+            keepAliveQueryInterval++;
+        } else {
+            keepAliveQueryInterval = 0;
+        }
+    }
+
+    public void lastQueryInterval(boolean add) {
+        if (add) {
+            lastQueryInterval++;
+        } else {
+            lastQueryInterval = 0;
+        }
+    }
+
+    public void resetAllTimers() {
+        keepAliveQueryInterval = 0;
+        keepAliveQueryCount = 0;
+        lastQueryInterval = 0;
+        lastQueryCount = 0;
+    }
+}