blob: c2b32a46cd4b7e215402d82b77c7b097abbe1b61 [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Brian O'Connor4d084702017-08-03 22:45:58 -07002 * Copyright 2017-present Open Networking Foundation
David K. Bainbridged77028f2017-08-01 12:47:55 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
ke han81a38b92017-03-10 18:41:44 +080016package org.opencord.igmpproxy;
17
18import org.onlab.packet.Ethernet;
19import org.onlab.packet.Ip4Address;
20import org.onosproject.net.DeviceId;
Esin Karaman305908c2020-02-24 14:42:52 +000021import org.onosproject.net.PortNumber;
ke han81a38b92017-03-10 18:41:44 +080022
23import java.util.Random;
Esin Karamana05342e2019-09-17 13:01:25 +000024import java.util.concurrent.atomic.AtomicInteger;
ke han81a38b92017-03-10 18:41:44 +080025
26/**
David K. Bainbridged77028f2017-08-01 12:47:55 -070027 * State machine for single IGMP group member. The state machine is implemented on
ke han81a38b92017-03-10 18:41:44 +080028 * RFC 2236 "6. Host State Diagram".
29 */
30public class SingleStateMachine {
Vignesh Ethirajd1957c92019-11-18 11:52:49 +000031 // Only for tests purposes
32 static boolean sendQuery = true;
33
ke han81a38b92017-03-10 18:41:44 +080034 static final int STATE_NON = 0;
35 static final int STATE_DELAY = 1;
36 static final int STATE_IDLE = 2;
37 static final int TRANSITION_JOIN = 0;
38 static final int TRANSITION_LEAVE = 1;
39 static final int TRANSITION_QUERY = 2;
40 static final int TRANSITION_TIMEOUT = 3;
41 static final int DEFAULT_MAX_RESP = 0xfffffff;
42 static final int DEFAULT_COUNT = 1;
43 private DeviceId devId;
44 private Ip4Address groupIp;
45 private Ip4Address srcIp;
Esin Karaman305908c2020-02-24 14:42:52 +000046 private PortNumber upLinkPort;
ke han81a38b92017-03-10 18:41:44 +080047
Esin Karamana05342e2019-09-17 13:01:25 +000048 private AtomicInteger count = new AtomicInteger(DEFAULT_COUNT);
ke han81a38b92017-03-10 18:41:44 +080049 private int timerId = IgmpTimer.INVALID_TIMER_ID;
50 private int timeOut = DEFAULT_MAX_RESP;
51 private State[] states =
52 {
53 new NonMember(), new DelayMember(), new IdleMember()
54 };
55 private int[] nonTransition =
56 {STATE_DELAY, STATE_NON, STATE_NON, STATE_NON};
57 private int[] delayTransition =
58 {STATE_DELAY, STATE_NON, STATE_DELAY, STATE_IDLE};
59 private int[] idleTransition =
60 {STATE_IDLE, STATE_NON, STATE_DELAY, STATE_IDLE};
61 //THE TRANSITION TABLE
62 private int[][] transition =
63 {nonTransition, delayTransition, idleTransition};
64 private int currentState = STATE_NON;
65
Esin Karaman305908c2020-02-24 14:42:52 +000066 public SingleStateMachine(DeviceId devId, Ip4Address groupIp, Ip4Address src, PortNumber upLinkPort) {
ke han81a38b92017-03-10 18:41:44 +080067 this.devId = devId;
68 this.groupIp = groupIp;
69 this.srcIp = src;
Esin Karaman305908c2020-02-24 14:42:52 +000070 this.upLinkPort = upLinkPort;
ke han81a38b92017-03-10 18:41:44 +080071 }
72
Esin Karamana05342e2019-09-17 13:01:25 +000073 public Ip4Address getGroupIp() {
74 return groupIp;
75 }
ke han81a38b92017-03-10 18:41:44 +080076
77 public DeviceId getDeviceId() {
78 return devId;
79 }
80 public boolean increaseCounter() {
Esin Karamana05342e2019-09-17 13:01:25 +000081 count.incrementAndGet();
ke han81a38b92017-03-10 18:41:44 +080082 return true;
83 }
84
85 public boolean decreaseCounter() {
Esin Karamana05342e2019-09-17 13:01:25 +000086 if (count.get() > 0) {
87 count.decrementAndGet();
ke han81a38b92017-03-10 18:41:44 +080088 return true;
89 } else {
90 return false;
91 }
92 }
93
94 public int getCounter() {
Esin Karamana05342e2019-09-17 13:01:25 +000095 return count.get();
ke han81a38b92017-03-10 18:41:44 +080096 }
97 public int currentState() {
98 return currentState;
99 }
100
101 private void next(int msg) {
102 currentState = transition[currentState][msg];
103 }
104
Esin Karamana05342e2019-09-17 13:01:25 +0000105 public void join(boolean messageOutAllowed) {
106 states[currentState].join(messageOutAllowed);
ke han81a38b92017-03-10 18:41:44 +0800107 next(TRANSITION_JOIN);
108 }
109
Esin Karamana05342e2019-09-17 13:01:25 +0000110 public void leave(boolean messageOutAllowed) {
111 states[currentState].leave(messageOutAllowed);
ke han81a38b92017-03-10 18:41:44 +0800112 next(TRANSITION_LEAVE);
113 }
114
115 public void query(int maxResp) {
116 states[currentState].query(maxResp);
117 next(TRANSITION_QUERY);
118 }
119
120 public void timeOut() {
121 states[currentState].timeOut();
122 next(TRANSITION_TIMEOUT);
123 }
124
125 int getTimeOut(int maxTimeOut) {
126 Random random = new Random();
127 return Math.abs(random.nextInt()) % maxTimeOut;
128 }
129
130 protected void cancelTimer() {
131 if (IgmpTimer.INVALID_TIMER_ID != timerId) {
132 IgmpTimer.cancel(timerId);
133 }
134 }
135
136 class State {
Esin Karamana05342e2019-09-17 13:01:25 +0000137 public void join(boolean messageOutAllowed) {
ke han81a38b92017-03-10 18:41:44 +0800138 }
139
Esin Karamana05342e2019-09-17 13:01:25 +0000140 public void leave(boolean messageOutAllowed) {
141 if (messageOutAllowed) {
142 Ethernet eth = IgmpSender.getInstance().buildIgmpV3Leave(groupIp, srcIp);
Esin Karaman305908c2020-02-24 14:42:52 +0000143 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
Esin Karamana05342e2019-09-17 13:01:25 +0000144 }
ke han81a38b92017-03-10 18:41:44 +0800145 }
146
147 public void query(int maxResp) {
148 }
149
150 public void timeOut() {
151 }
152
153 }
154
155 class NonMember extends State {
Esin Karamana05342e2019-09-17 13:01:25 +0000156 public void join(boolean messageOutAllowed) {
157 if (messageOutAllowed) {
158 Ethernet eth = IgmpSender.getInstance().buildIgmpV3Join(groupIp, srcIp);
Esin Karaman305908c2020-02-24 14:42:52 +0000159 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
Esin Karamana05342e2019-09-17 13:01:25 +0000160 timeOut = getTimeOut(IgmpManager.getUnsolicitedTimeout());
161 timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
162 }
ke han81a38b92017-03-10 18:41:44 +0800163 }
164 }
165
166 class DelayMember extends State {
167 public void query(int maxResp) {
168 if (maxResp < timeOut) {
169 timeOut = getTimeOut(maxResp);
170 timerId = IgmpTimer.reset(timerId, SingleStateMachine.this, timeOut);
171 }
172 }
173
174 public void timeOut() {
Vignesh Ethirajd1957c92019-11-18 11:52:49 +0000175 if (sendQuery) {
176 Ethernet eth = IgmpSender.getInstance().buildIgmpV3ResponseQuery(groupIp, srcIp);
Esin Karaman305908c2020-02-24 14:42:52 +0000177 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId, upLinkPort);
Vignesh Ethirajd1957c92019-11-18 11:52:49 +0000178 timeOut = DEFAULT_MAX_RESP;
179 }
ke han81a38b92017-03-10 18:41:44 +0800180 }
181
182 }
183
184 class IdleMember extends State {
185 public void query(int maxResp) {
186 timeOut = getTimeOut(maxResp);
187 timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
188 }
189 }
190}