Refactors IGMPProxy application in API and APP bundles
Change-Id: I465d6c0f49079804ae8e0a1f464581c25c6d2300
diff --git a/app/src/main/java/org/opencord/igmpproxy/impl/SingleStateMachine.java b/app/src/main/java/org/opencord/igmpproxy/impl/SingleStateMachine.java
new file mode 100644
index 0000000..c4d73a8
--- /dev/null
+++ b/app/src/main/java/org/opencord/igmpproxy/impl/SingleStateMachine.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.igmpproxy.impl;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * State machine for single IGMP group member. The state machine is implemented on
+ * RFC 2236 "6. Host State Diagram".
+ */
+public class SingleStateMachine {
+ // Only for tests purposes
+ static boolean sendQuery = true;
+
+ static final int STATE_NON = 0;
+ static final int STATE_DELAY = 1;
+ static final int STATE_IDLE = 2;
+ static final int TRANSITION_JOIN = 0;
+ static final int TRANSITION_LEAVE = 1;
+ static final int TRANSITION_QUERY = 2;
+ static final int TRANSITION_TIMEOUT = 3;
+ static final int DEFAULT_MAX_RESP = 0xfffffff;
+ static final int DEFAULT_COUNT = 1;
+ private DeviceId devId;
+ private Ip4Address groupIp;
+ private Ip4Address srcIp;
+ private PortNumber upLinkPort;
+
+ private AtomicInteger count = new AtomicInteger(DEFAULT_COUNT);
+ private int timerId = IgmpTimer.INVALID_TIMER_ID;
+ private int timeOut = DEFAULT_MAX_RESP;
+ private State[] states =
+ {
+ new NonMember(), new DelayMember(), new IdleMember()
+ };
+ private int[] nonTransition =
+ {STATE_DELAY, STATE_NON, STATE_NON, STATE_NON};
+ private int[] delayTransition =
+ {STATE_DELAY, STATE_NON, STATE_DELAY, STATE_IDLE};
+ private int[] idleTransition =
+ {STATE_IDLE, STATE_NON, STATE_DELAY, STATE_IDLE};
+ //THE TRANSITION TABLE
+ private int[][] transition =
+ {nonTransition, delayTransition, idleTransition};
+ private int currentState = STATE_NON;
+
+ public SingleStateMachine(DeviceId devId, Ip4Address groupIp, Ip4Address src, PortNumber upLinkPort) {
+ this.devId = devId;
+ this.groupIp = groupIp;
+ this.srcIp = src;
+ this.upLinkPort = upLinkPort;
+ }
+
+ public Ip4Address getGroupIp() {
+ return groupIp;
+ }
+
+ public DeviceId getDeviceId() {
+ return devId;
+ }
+ public boolean increaseCounter() {
+ count.incrementAndGet();
+ return true;
+ }
+
+ public boolean decreaseCounter() {
+ if (count.get() > 0) {
+ count.decrementAndGet();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public int getCounter() {
+ return count.get();
+ }
+ public int currentState() {
+ return currentState;
+ }
+
+ private void next(int msg) {
+ currentState = transition[currentState][msg];
+ }
+
+ public void join(boolean messageOutAllowed) {
+ states[currentState].join(messageOutAllowed);
+ next(TRANSITION_JOIN);
+ }
+
+ public void leave(boolean messageOutAllowed) {
+ states[currentState].leave(messageOutAllowed);
+ next(TRANSITION_LEAVE);
+ }
+
+ public void query(int maxResp) {
+ states[currentState].query(maxResp);
+ next(TRANSITION_QUERY);
+ }
+
+ public void timeOut() {
+ states[currentState].timeOut();
+ next(TRANSITION_TIMEOUT);
+ }
+
+ int getTimeOut(int maxTimeOut) {
+ Random random = new Random();
+ return Math.abs(random.nextInt()) % maxTimeOut;
+ }
+
+ protected void cancelTimer() {
+ if (IgmpTimer.INVALID_TIMER_ID != timerId) {
+ IgmpTimer.cancel(timerId);
+ }
+ }
+
+ class State {
+ public void join(boolean messageOutAllowed) {
+ }
+
+ public void leave(boolean messageOutAllowed) {
+ if (messageOutAllowed) {
+ Ethernet eth = IgmpSender.getInstance().buildIgmpV3Leave(groupIp, srcIp);
+ IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
+ }
+ }
+
+ public void query(int maxResp) {
+ }
+
+ public void timeOut() {
+ }
+
+ }
+
+ class NonMember extends State {
+ public void join(boolean messageOutAllowed) {
+ if (messageOutAllowed) {
+ Ethernet eth = IgmpSender.getInstance().buildIgmpV3Join(groupIp, srcIp);
+ IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
+ timeOut = getTimeOut(IgmpManager.getUnsolicitedTimeout());
+ timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
+ }
+ }
+ }
+
+ class DelayMember extends State {
+ public void query(int maxResp) {
+ if (maxResp < timeOut) {
+ timeOut = getTimeOut(maxResp);
+ timerId = IgmpTimer.reset(timerId, SingleStateMachine.this, timeOut);
+ }
+ }
+
+ public void timeOut() {
+ if (sendQuery) {
+ Ethernet eth = IgmpSender.getInstance().buildIgmpV3ResponseQuery(groupIp, srcIp);
+ IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
+ timeOut = DEFAULT_MAX_RESP;
+ }
+ }
+
+ }
+
+ class IdleMember extends State {
+ public void query(int maxResp) {
+ timeOut = getTimeOut(maxResp);
+ timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
+ }
+ }
+}