blob: ceaac528ef3e0234e6b1303ab9550dc430756d51 [file] [log] [blame]
ke han81a38b92017-03-10 18:41:44 +08001package org.opencord.igmpproxy;
2
3import org.onlab.packet.Ethernet;
4import org.onlab.packet.Ip4Address;
5import org.onosproject.net.DeviceId;
6
7import java.util.Random;
8
9/**
10 * State machine for single IGMP group member. The state machine is implemented on
11 * RFC 2236 "6. Host State Diagram".
12 */
13public class SingleStateMachine {
14 static final int STATE_NON = 0;
15 static final int STATE_DELAY = 1;
16 static final int STATE_IDLE = 2;
17 static final int TRANSITION_JOIN = 0;
18 static final int TRANSITION_LEAVE = 1;
19 static final int TRANSITION_QUERY = 2;
20 static final int TRANSITION_TIMEOUT = 3;
21 static final int DEFAULT_MAX_RESP = 0xfffffff;
22 static final int DEFAULT_COUNT = 1;
23 private DeviceId devId;
24 private Ip4Address groupIp;
25 private Ip4Address srcIp;
26
27 private int count = DEFAULT_COUNT;
28 private int timerId = IgmpTimer.INVALID_TIMER_ID;
29 private int timeOut = DEFAULT_MAX_RESP;
30 private State[] states =
31 {
32 new NonMember(), new DelayMember(), new IdleMember()
33 };
34 private int[] nonTransition =
35 {STATE_DELAY, STATE_NON, STATE_NON, STATE_NON};
36 private int[] delayTransition =
37 {STATE_DELAY, STATE_NON, STATE_DELAY, STATE_IDLE};
38 private int[] idleTransition =
39 {STATE_IDLE, STATE_NON, STATE_DELAY, STATE_IDLE};
40 //THE TRANSITION TABLE
41 private int[][] transition =
42 {nonTransition, delayTransition, idleTransition};
43 private int currentState = STATE_NON;
44
45 public SingleStateMachine(DeviceId devId, Ip4Address groupIp, Ip4Address src) {
46 this.devId = devId;
47 this.groupIp = groupIp;
48 this.srcIp = src;
49 }
50
51
52 public DeviceId getDeviceId() {
53 return devId;
54 }
55 public boolean increaseCounter() {
56 count++;
57 return true;
58 }
59
60 public boolean decreaseCounter() {
61 if (count > 0) {
62 count--;
63 return true;
64 } else {
65 return false;
66 }
67 }
68
69 public int getCounter() {
70 return count;
71 }
72 public int currentState() {
73 return currentState;
74 }
75
76 private void next(int msg) {
77 currentState = transition[currentState][msg];
78 }
79
80 public void join() {
81 states[currentState].join();
82 next(TRANSITION_JOIN);
83 }
84
85 public void leave() {
86 states[currentState].leave();
87 next(TRANSITION_LEAVE);
88 }
89
90 public void query(int maxResp) {
91 states[currentState].query(maxResp);
92 next(TRANSITION_QUERY);
93 }
94
95 public void timeOut() {
96 states[currentState].timeOut();
97 next(TRANSITION_TIMEOUT);
98 }
99
100 int getTimeOut(int maxTimeOut) {
101 Random random = new Random();
102 return Math.abs(random.nextInt()) % maxTimeOut;
103 }
104
105 protected void cancelTimer() {
106 if (IgmpTimer.INVALID_TIMER_ID != timerId) {
107 IgmpTimer.cancel(timerId);
108 }
109 }
110
111 class State {
112 public void join() {
113 }
114
115 public void leave() {
116 Ethernet eth = IgmpSender.getInstance().buildIgmpV3Leave(groupIp, srcIp);
117 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId);
118 }
119
120 public void query(int maxResp) {
121 }
122
123 public void timeOut() {
124 }
125
126 }
127
128 class NonMember extends State {
129 public void join() {
130 Ethernet eth = IgmpSender.getInstance().buildIgmpV3Join(groupIp, srcIp);
131 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId);
132 timeOut = getTimeOut(IgmpManager.getUnsolicitedTimeout());
133 timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
134 }
135 }
136
137 class DelayMember extends State {
138 public void query(int maxResp) {
139 if (maxResp < timeOut) {
140 timeOut = getTimeOut(maxResp);
141 timerId = IgmpTimer.reset(timerId, SingleStateMachine.this, timeOut);
142 }
143 }
144
145 public void timeOut() {
146 Ethernet eth = IgmpSender.getInstance().buildIgmpV3ResponseQuery(groupIp, srcIp);
147 IgmpSender.getInstance().sendIgmpPacketUplink(eth, devId);
148 timeOut = DEFAULT_MAX_RESP;
149 }
150
151 }
152
153 class IdleMember extends State {
154 public void query(int maxResp) {
155 timeOut = getTimeOut(maxResp);
156 timerId = IgmpTimer.start(SingleStateMachine.this, timeOut);
157 }
158 }
159}