blob: 1c06af5d7866d31f240a090eee194a569d33bf98 [file] [log] [blame]
Chetan Gaonker25470972016-02-26 08:52:15 -08001import unittest
2from nose.tools import *
3from nose.twistedtools import reactor, deferred
4from twisted.internet import defer
5from scapy.all import *
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -08006import time, monotonic
Chetan Gaonker25470972016-02-26 08:52:15 -08007import os, sys
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -08008import tempfile
9import json
10import random
11import threading
12
Chetan Gaonker25470972016-02-26 08:52:15 -080013CORD_TEST_UTILS = 'utils'
14test_root = os.getenv('CORD_TEST_ROOT') or './'
15sys.path.append(test_root + CORD_TEST_UTILS)
16from IGMP import *
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080017from McastTraffic import *
18from Stats import Stats
19log.setLevel('INFO')
Chetan Gaonker25470972016-02-26 08:52:15 -080020
21class IGMPTestState:
22
23 def __init__(self, groups = [], df = None, state = 0):
24 self.df = df
25 self.state = state
26 self.counter = 0
27 self.groups = groups
28 self.group_map = {} ##create a send/recv count map
29 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080030 self.group_map[g] = (Stats(), Stats())
31
32 def update(self, group, tx = 0, rx = 0, t = 0):
33 self.counter += 1
Chetan Gaonker25470972016-02-26 08:52:15 -080034 index = 0 if rx == 0 else 1
35 v = tx if rx == 0 else rx
36 if self.group_map.has_key(group):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080037 self.group_map[group][index].update(packets = v, t = t)
Chetan Gaonker25470972016-02-26 08:52:15 -080038
39 def update_state(self):
40 self.state = self.state ^ 1
Chetan Gaonker25470972016-02-26 08:52:15 -080041
42class igmp_exchange(unittest.TestCase):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080043
44 IGMP_TEST_TIMEOUT = 5
45 MCAST_TRAFFIC_TIMEOUT = 10
46 max_packets = 100
Chetan Gaonker25470972016-02-26 08:52:15 -080047
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080048 def onos_ssm_table_load(self, groups, src_list):
49 ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
50 ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
51 for g in groups:
52 for s in src_list:
53 d = {}
54 d['source'] = s
55 d['group'] = g
56 ssm_xlate_list.append(d)
57 json_dict = json.JSONEncoder().encode(ssm_dict)
58 with tempfile.NamedTemporaryFile(delete=False) as temp:
59 temp.write(json_dict)
60 temp.flush()
61 temp.close()
62 log.debug('Loading SSM config in file %s to ONOS.' %temp.name)
63 os.system('./igmp_ssm_load.sh %s' %temp.name)
64 os.unlink(temp.name)
65
66 def igmp_verify_join(self, igmpStateList):
67 sendState, recvState = igmpStateList
Chetan Gaonker25470972016-02-26 08:52:15 -080068 ## check if the send is received for the groups
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080069 for g in sendState.groups:
70 tx_stats = sendState.group_map[g][0]
71 tx = tx_stats.count
Chetan Gaonker25470972016-02-26 08:52:15 -080072 assert_greater(tx, 0)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080073 rx_stats = recvState.group_map[g][1]
74 rx = rx_stats.count
75 assert_greater(rx, 0)
76 log.info('Receive stats %s for group %s' %(rx_stats, g))
77
78 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -080079
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080080 def igmp_verify_leave(self, igmpStateList, leave_groups):
81 sendState, recvState = igmpStateList[0], igmpStateList[1]
82 ## check if the send is received for the groups
83 for g in sendState.groups:
84 tx_stats = sendState.group_map[g][0]
85 rx_stats = recvState.group_map[g][1]
86 tx = tx_stats.count
87 rx = rx_stats.count
88 assert_greater(tx, 0)
89 if g not in leave_groups:
90 log.info('Received %d packets for group %s' %(rx, g))
91 for g in leave_groups:
92 rx = recvState.group_map[g][1].count
93 assert_equal(rx, 0)
94
95 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -080096
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080097 def mcast_traffic_timer(self):
98 self.mcastTraffic.stopReceives()
99
100 def send_mcast_cb(self, send_state):
101 for g in send_state.groups:
102 send_state.update(g, tx = 1)
Chetan Gaonker25470972016-02-26 08:52:15 -0800103 return 0
104
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800105 ##Runs in the context of twisted reactor thread
Chetan Gaonker25470972016-02-26 08:52:15 -0800106 def igmp_recv(self, igmpState, iface = 'veth0'):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800107 p = self.recv_socket.recv()
108 send_time = float(p.payload.load)
109 recv_time = monotonic.monotonic()
110 #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
111 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
Chetan Gaonker25470972016-02-26 08:52:15 -0800112 return 0
113
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800114 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
115 self.onos_ssm_table_load(groups, src_list)
116 for g in groups:
117 igmp = IGMP(mtype = IGMPV3_REPORT,
118 group = g,
119 rtype = IGMP_INCLUDE,
120 src_list = src_list)
121 sendp(igmp.scapify(), iface = iface)
122 if delay != 0:
123 time.sleep(delay)
124
125 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
126 for g in groups:
Chetan Gaonker25470972016-02-26 08:52:15 -0800127 igmp = IGMP(mtype = IGMPV3_REPORT,
128 group = g,
129 rtype = IGMP_EXCLUDE,
130 src_list = src_list)
131 sendp(igmp.scapify(), iface = iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800132 if delay != 0:
133 time.sleep(delay)
Chetan Gaonker25470972016-02-26 08:52:15 -0800134
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800135 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
136 def test_igmp_join_verify_traffic(self):
Chetan Gaonker25470972016-02-26 08:52:15 -0800137 groups = ['224.0.1.1', '225.0.0.1']
Chetan Gaonker25470972016-02-26 08:52:15 -0800138 df = defer.Deferred()
139 igmpState = IGMPTestState(groups = groups, df = df)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800140 igmpStateRecv = IGMPTestState(groups = groups, df = df)
141 igmpStateList = (igmpState, igmpStateRecv)
142 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb, arg = igmpState)
143 self.df = df
144 self.mcastTraffic = mcastTraffic
145 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
146
147 def igmp_srp_task(stateList):
148 igmpSendState, igmpRecvState = stateList
149 if not mcastTraffic.isRecvStopped():
150 result = self.igmp_recv(igmpRecvState)
151 reactor.callLater(0, igmp_srp_task, stateList)
152 else:
153 self.mcastTraffic.stop()
154 self.recv_socket.close()
155 self.igmp_verify_join(stateList)
156 self.df.callback(0)
157
158 self.send_igmp_join(groups)
159 mcastTraffic.start()
160 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
161 reactor.callLater(0, igmp_srp_task, igmpStateList)
Chetan Gaonker25470972016-02-26 08:52:15 -0800162 return df
163
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800164 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
165 def test_igmp_leave_verify_traffic(self):
166 groups = ['224.0.1.10', '225.0.0.10']
167 leave_groups = ['224.0.1.10']
168 df = defer.Deferred()
169 igmpState = IGMPTestState(groups = groups, df = df)
170 igmpStateRecv = IGMPTestState(groups = groups, df = df)
171 igmpStateList = (igmpState, igmpStateRecv)
172 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
173 arg = igmpState)
174 self.df = df
175 self.mcastTraffic = mcastTraffic
176 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
177
178 def igmp_srp_task(stateList):
179 igmpSendState, igmpRecvState = stateList
180 if not mcastTraffic.isRecvStopped():
181 result = self.igmp_recv(igmpRecvState)
182 reactor.callLater(0, igmp_srp_task, stateList)
183 else:
184 self.mcastTraffic.stop()
185 self.recv_socket.close()
186 self.igmp_verify_leave(stateList, leave_groups)
187 self.df.callback(0)
188
189 self.send_igmp_join(groups)
190 self.send_igmp_leave(leave_groups, delay = 3)
191 mcastTraffic.start()
192 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
193 reactor.callLater(0, igmp_srp_task, igmpStateList)
194 return df
195
196 @deferred(timeout=100)
197 def test_igmp_leave_join_loop(self):
198 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
199 self.src_list = ['3.4.5.6', '7.8.9.10']
200 df = defer.Deferred()
201 self.df = df
202 self.iterations = 0
203 self.num_groups = len(self.groups)
204 self.MAX_TEST_ITERATIONS = 10
205
206 #self.onos_ssm_table_load(self.groups, self.src_list)
207 def igmp_srp_task(v):
208 if self.iterations < self.MAX_TEST_ITERATIONS:
209 if v == 1:
210 ##join test
211 self.num_groups = random.randint(0, len(self.groups))
212 self.send_igmp_join(self.groups[:self.num_groups],
213 src_list = self.src_list,
214 iface = 'veth0', delay = 0)
215 else:
216 self.send_igmp_leave(self.groups[:self.num_groups],
217 src_list = self.src_list,
218 iface = 'veth0', delay = 0)
219 self.iterations += 1
220 v ^= 1
221 reactor.callLater(1.0 + 0.5*self.num_groups,
222 igmp_srp_task, v)
223 else:
224 self.df.callback(0)
225
226 reactor.callLater(0, igmp_srp_task, 1)
227 return df
228
229 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
230 self.onos_ssm_table_load(groups, src_list)
231 for g in groups:
232 igmp = IGMP(mtype = IGMPV3_REPORT,
233 group = g,
234 rtype = IGMP_INCLUDE,
235 src_list = src_list)
236 state.group_map[g][0].update(1, t = monotonic.monotonic())
237 sendp(igmp.scapify(), iface = intf)
238 log.debug('Returning from join task')
239
240 def igmp_recv_task(self, intf, groups, join_state):
241 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
242 group_map = {}
243 for g in groups:
244 group_map[g] = [0,0]
245
246 while True:
247 p = recv_socket.recv()
248 if p.dst in groups and group_map[p.dst][0] == 0:
249 group_map[p.dst][0] += 1
250 group_map[p.dst][1] = monotonic.monotonic()
251 c = 0
252 for g in groups:
253 c += group_map[g][0]
254 if c == len(groups):
255 break
256 for g in groups:
257 join_start = join_state.group_map[g][0].start
258 recv_time = group_map[g][1] * 1000000
259 delta = (recv_time - join_start)
260 log.info('Join for group %s received in %.3f usecs' %
261 (g, delta))
262
263 recv_socket.close()
264 log.debug('Returning from recv task')
265
266 def group_latency_check(self, groups):
267 tasks = []
268 self.send_igmp_leave(groups = groups)
269 join_state = IGMPTestState(groups = groups)
270 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
271 traffic_state = IGMPTestState(groups = groups)
272 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
273 arg = traffic_state)
274 mcast_traffic.start()
275 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
276 for t in tasks:
277 t.start()
278 for t in tasks:
279 t.join()
280
281 mcast_traffic.stop()
282 self.send_igmp_leave(groups = groups)
283 return
284
285 def test_igmp_1group_join_latency(self):
286 groups = ['239.0.1.1']
287 self.group_latency_check(groups)
288
289 def test_igmp_2group_join_latency(self):
290 groups = ['239.0.1.1', '240.0.1.1']
291 self.group_latency_check(groups)
292
293 def test_igmp_Ngroup_join_latency(self):
294 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
295 self.group_latency_check(groups)
296
297