blob: 7439e6ff615b4dabe8cc1861be54ea82705cb3c6 [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
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -08009import random
10import threading
Chetan Gaonkere88c95c2016-03-02 05:21:47 -080011from IGMP import *
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080012from McastTraffic import *
13from Stats import Stats
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080014from OnosCtrl import OnosCtrl
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080015log.setLevel('INFO')
Chetan Gaonker25470972016-02-26 08:52:15 -080016
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080017IGMP_DST_MAC = "01:00:5e:00:01:01"
18IGMP_SRC_MAC = "5a:e1:ac:ec:4d:a1"
19IP_SRC = '1.2.3.4'
20IP_DST = '224.0.1.1'
21
22igmp_eth = Ether(dst = IGMP_DST_MAC, src = IGMP_SRC_MAC, type = ETH_P_IP)
23igmp_ip = IP(dst = IP_DST, src = IP_SRC)
24
Chetan Gaonker25470972016-02-26 08:52:15 -080025class IGMPTestState:
26
27 def __init__(self, groups = [], df = None, state = 0):
28 self.df = df
29 self.state = state
30 self.counter = 0
31 self.groups = groups
32 self.group_map = {} ##create a send/recv count map
33 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080034 self.group_map[g] = (Stats(), Stats())
35
36 def update(self, group, tx = 0, rx = 0, t = 0):
37 self.counter += 1
Chetan Gaonker25470972016-02-26 08:52:15 -080038 index = 0 if rx == 0 else 1
39 v = tx if rx == 0 else rx
40 if self.group_map.has_key(group):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080041 self.group_map[group][index].update(packets = v, t = t)
Chetan Gaonker25470972016-02-26 08:52:15 -080042
43 def update_state(self):
44 self.state = self.state ^ 1
Chetan Gaonker25470972016-02-26 08:52:15 -080045
46class igmp_exchange(unittest.TestCase):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080047
48 IGMP_TEST_TIMEOUT = 5
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -080049 IGMP_QUERY_TIMEOUT = 30
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080050 MCAST_TRAFFIC_TIMEOUT = 10
51 max_packets = 100
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080052 app = 'org.onosproject.igmp'
53
54 def setUp(self):
55 ''' Activate the dhcp app'''
56 self.onos_ctrl = OnosCtrl(self.app)
57 status, _ = self.onos_ctrl.activate()
58 assert_equal(status, True)
59 time.sleep(3)
60
61 def teardown(self):
62 '''Deactivate the dhcp app'''
63 self.onos_ctrl.deactivate()
64
65 def onos_load_config(self, config):
66 status, code = self.onos_ctrl.config(config)
67 if status is False:
68 log.info('JSON request returned status %d' %code)
69 assert_equal(status, True)
70 time.sleep(2)
71
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080072 def onos_ssm_table_load(self, groups, src_list):
73 ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
74 ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
75 for g in groups:
76 for s in src_list:
77 d = {}
78 d['source'] = s
79 d['group'] = g
80 ssm_xlate_list.append(d)
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080081 self.onos_load_config(ssm_dict)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080082 time.sleep(2)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080083
84 def igmp_verify_join(self, igmpStateList):
85 sendState, recvState = igmpStateList
Chetan Gaonker25470972016-02-26 08:52:15 -080086 ## check if the send is received for the groups
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080087 for g in sendState.groups:
88 tx_stats = sendState.group_map[g][0]
89 tx = tx_stats.count
Chetan Gaonker25470972016-02-26 08:52:15 -080090 assert_greater(tx, 0)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080091 rx_stats = recvState.group_map[g][1]
92 rx = rx_stats.count
93 assert_greater(rx, 0)
94 log.info('Receive stats %s for group %s' %(rx_stats, g))
95
96 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -080097
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080098 def igmp_verify_leave(self, igmpStateList, leave_groups):
99 sendState, recvState = igmpStateList[0], igmpStateList[1]
100 ## check if the send is received for the groups
101 for g in sendState.groups:
102 tx_stats = sendState.group_map[g][0]
103 rx_stats = recvState.group_map[g][1]
104 tx = tx_stats.count
105 rx = rx_stats.count
106 assert_greater(tx, 0)
107 if g not in leave_groups:
108 log.info('Received %d packets for group %s' %(rx, g))
109 for g in leave_groups:
110 rx = recvState.group_map[g][1].count
111 assert_equal(rx, 0)
112
113 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -0800114
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800115 def mcast_traffic_timer(self):
116 self.mcastTraffic.stopReceives()
117
118 def send_mcast_cb(self, send_state):
119 for g in send_state.groups:
120 send_state.update(g, tx = 1)
Chetan Gaonker25470972016-02-26 08:52:15 -0800121 return 0
122
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800123 ##Runs in the context of twisted reactor thread
Chetan Gaonker25470972016-02-26 08:52:15 -0800124 def igmp_recv(self, igmpState, iface = 'veth0'):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800125 p = self.recv_socket.recv()
126 send_time = float(p.payload.load)
127 recv_time = monotonic.monotonic()
128 #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
129 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
Chetan Gaonker25470972016-02-26 08:52:15 -0800130 return 0
131
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800132 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
133 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800134 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
135 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800136 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800137 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=g)
138 gr.sources = src_list
139 igmp.grps.append(gr)
140
141 pkt = igmp_eth/igmp_ip/igmp
142 IGMPv3.fixup(pkt)
143 sendp(pkt, iface=iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800144 if delay != 0:
145 time.sleep(delay)
146
147 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800148 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
149 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800150 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800151 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=g)
152 gr.sources = src_list
153 igmp.grps.append(gr)
154
155 pkt = igmp_eth/igmp_ip/igmp
156 IGMPv3.fixup(pkt)
157 sendp(pkt, iface = iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800158 if delay != 0:
159 time.sleep(delay)
Chetan Gaonker25470972016-02-26 08:52:15 -0800160
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800161 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
162 def test_igmp_join_verify_traffic(self):
Chetan Gaonker25470972016-02-26 08:52:15 -0800163 groups = ['224.0.1.1', '225.0.0.1']
Chetan Gaonker25470972016-02-26 08:52:15 -0800164 df = defer.Deferred()
165 igmpState = IGMPTestState(groups = groups, df = df)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800166 igmpStateRecv = IGMPTestState(groups = groups, df = df)
167 igmpStateList = (igmpState, igmpStateRecv)
168 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb, arg = igmpState)
169 self.df = df
170 self.mcastTraffic = mcastTraffic
171 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
172
173 def igmp_srp_task(stateList):
174 igmpSendState, igmpRecvState = stateList
175 if not mcastTraffic.isRecvStopped():
176 result = self.igmp_recv(igmpRecvState)
177 reactor.callLater(0, igmp_srp_task, stateList)
178 else:
179 self.mcastTraffic.stop()
180 self.recv_socket.close()
181 self.igmp_verify_join(stateList)
182 self.df.callback(0)
183
184 self.send_igmp_join(groups)
185 mcastTraffic.start()
186 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
187 reactor.callLater(0, igmp_srp_task, igmpStateList)
Chetan Gaonker25470972016-02-26 08:52:15 -0800188 return df
189
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800190 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
191 def test_igmp_leave_verify_traffic(self):
192 groups = ['224.0.1.10', '225.0.0.10']
193 leave_groups = ['224.0.1.10']
194 df = defer.Deferred()
195 igmpState = IGMPTestState(groups = groups, df = df)
196 igmpStateRecv = IGMPTestState(groups = groups, df = df)
197 igmpStateList = (igmpState, igmpStateRecv)
198 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
199 arg = igmpState)
200 self.df = df
201 self.mcastTraffic = mcastTraffic
202 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
203
204 def igmp_srp_task(stateList):
205 igmpSendState, igmpRecvState = stateList
206 if not mcastTraffic.isRecvStopped():
207 result = self.igmp_recv(igmpRecvState)
208 reactor.callLater(0, igmp_srp_task, stateList)
209 else:
210 self.mcastTraffic.stop()
211 self.recv_socket.close()
212 self.igmp_verify_leave(stateList, leave_groups)
213 self.df.callback(0)
214
215 self.send_igmp_join(groups)
216 self.send_igmp_leave(leave_groups, delay = 3)
217 mcastTraffic.start()
218 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
219 reactor.callLater(0, igmp_srp_task, igmpStateList)
220 return df
221
222 @deferred(timeout=100)
223 def test_igmp_leave_join_loop(self):
224 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
225 self.src_list = ['3.4.5.6', '7.8.9.10']
226 df = defer.Deferred()
227 self.df = df
228 self.iterations = 0
229 self.num_groups = len(self.groups)
230 self.MAX_TEST_ITERATIONS = 10
231
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800232 def igmp_srp_task(v):
233 if self.iterations < self.MAX_TEST_ITERATIONS:
234 if v == 1:
235 ##join test
236 self.num_groups = random.randint(0, len(self.groups))
237 self.send_igmp_join(self.groups[:self.num_groups],
238 src_list = self.src_list,
239 iface = 'veth0', delay = 0)
240 else:
241 self.send_igmp_leave(self.groups[:self.num_groups],
242 src_list = self.src_list,
243 iface = 'veth0', delay = 0)
244 self.iterations += 1
245 v ^= 1
246 reactor.callLater(1.0 + 0.5*self.num_groups,
247 igmp_srp_task, v)
248 else:
249 self.df.callback(0)
250
251 reactor.callLater(0, igmp_srp_task, 1)
252 return df
253
254 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
255 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800256 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
257 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800258 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800259 gr = IGMPv3gr(rtype = IGMP_V3_GR_TYPE_EXCLUDE, mcaddr = g)
260 gr.sources = src_list
261 igmp.grps.append(gr)
262
263 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800264 state.group_map[g][0].update(1, t = monotonic.monotonic())
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800265
266 pkt = igmp_eth/igmp_ip/igmp
267 IGMPv3.fixup(pkt)
268 sendp(pkt, iface=intf)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800269 log.debug('Returning from join task')
270
271 def igmp_recv_task(self, intf, groups, join_state):
272 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
273 group_map = {}
274 for g in groups:
275 group_map[g] = [0,0]
276
277 while True:
278 p = recv_socket.recv()
279 if p.dst in groups and group_map[p.dst][0] == 0:
280 group_map[p.dst][0] += 1
281 group_map[p.dst][1] = monotonic.monotonic()
282 c = 0
283 for g in groups:
284 c += group_map[g][0]
285 if c == len(groups):
286 break
287 for g in groups:
288 join_start = join_state.group_map[g][0].start
289 recv_time = group_map[g][1] * 1000000
290 delta = (recv_time - join_start)
291 log.info('Join for group %s received in %.3f usecs' %
292 (g, delta))
293
294 recv_socket.close()
295 log.debug('Returning from recv task')
296
297 def group_latency_check(self, groups):
298 tasks = []
299 self.send_igmp_leave(groups = groups)
300 join_state = IGMPTestState(groups = groups)
301 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
302 traffic_state = IGMPTestState(groups = groups)
303 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
304 arg = traffic_state)
305 mcast_traffic.start()
306 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
307 for t in tasks:
308 t.start()
309 for t in tasks:
310 t.join()
311
312 mcast_traffic.stop()
313 self.send_igmp_leave(groups = groups)
314 return
315
316 def test_igmp_1group_join_latency(self):
317 groups = ['239.0.1.1']
318 self.group_latency_check(groups)
319
320 def test_igmp_2group_join_latency(self):
321 groups = ['239.0.1.1', '240.0.1.1']
322 self.group_latency_check(groups)
323
324 def test_igmp_Ngroup_join_latency(self):
325 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
326 self.group_latency_check(groups)
327
328
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800329 def test_igmp_join_rover(self):
330 '''Keep sending joins across multicast range of addresses'''
331 '''For now, restricting it to 50/100'''
332 s = (224 << 24) | 1
333 #e = (225 << 24) | (255 << 16) | (255 << 16) | 255
334 e = (224 << 24) | 50
335 for i in xrange(s, e+1):
336 if i&0xff:
337 ip = '%d.%d.%d.%d'%((i>>24)&0xff, (i>>16)&0xff, (i>>8)&0xff, i&0xff)
338 self.send_igmp_join([ip], delay = 0)
339
340 @deferred(timeout=IGMP_QUERY_TIMEOUT + 10)
341 def test_igmp_query(self):
342 groups = ['224.0.0.1'] ##igmp query group
343 df = defer.Deferred()
344 self.df = df
345 self.recv_socket = L2Socket(iface = 'veth0', type = ETH_P_IP)
346
347 def igmp_query_timeout():
348
349 def igmp_query_cb(pkt):
Chetan Gaonkerbd4390f2016-03-09 18:56:52 -0800350 log.info('Got IGMP query packet from %s for %s' %(pkt[IP].src, pkt[IP].dst))
351 assert_equal(pkt[IP].dst, '224.0.0.1')
352
353 sniff(prn = igmp_query_cb, count=1, lfilter = lambda p: p[IP].dst in groups,
354 opened_socket = self.recv_socket)
Chetan Gaonker1f7c3f82016-03-08 12:17:37 -0800355 self.recv_socket.close()
356 self.df.callback(0)
357
358 self.send_igmp_join(groups)
359 self.test_timer = reactor.callLater(self.IGMP_QUERY_TIMEOUT, igmp_query_timeout)
360 return df
361