blob: bc3463b83dc1720a10f1e67b106fdb3a350b3f9f [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
11
Chetan Gaonker25470972016-02-26 08:52:15 -080012CORD_TEST_UTILS = 'utils'
13test_root = os.getenv('CORD_TEST_ROOT') or './'
14sys.path.append(test_root + CORD_TEST_UTILS)
Chetan Gaonkere88c95c2016-03-02 05:21:47 -080015from IGMP import *
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080016from McastTraffic import *
17from Stats import Stats
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080018from OnosCtrl import OnosCtrl
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080019log.setLevel('INFO')
Chetan Gaonker25470972016-02-26 08:52:15 -080020
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080021IGMP_DST_MAC = "01:00:5e:00:01:01"
22IGMP_SRC_MAC = "5a:e1:ac:ec:4d:a1"
23IP_SRC = '1.2.3.4'
24IP_DST = '224.0.1.1'
25
26igmp_eth = Ether(dst = IGMP_DST_MAC, src = IGMP_SRC_MAC, type = ETH_P_IP)
27igmp_ip = IP(dst = IP_DST, src = IP_SRC)
28
Chetan Gaonker25470972016-02-26 08:52:15 -080029class IGMPTestState:
30
31 def __init__(self, groups = [], df = None, state = 0):
32 self.df = df
33 self.state = state
34 self.counter = 0
35 self.groups = groups
36 self.group_map = {} ##create a send/recv count map
37 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080038 self.group_map[g] = (Stats(), Stats())
39
40 def update(self, group, tx = 0, rx = 0, t = 0):
41 self.counter += 1
Chetan Gaonker25470972016-02-26 08:52:15 -080042 index = 0 if rx == 0 else 1
43 v = tx if rx == 0 else rx
44 if self.group_map.has_key(group):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080045 self.group_map[group][index].update(packets = v, t = t)
Chetan Gaonker25470972016-02-26 08:52:15 -080046
47 def update_state(self):
48 self.state = self.state ^ 1
Chetan Gaonker25470972016-02-26 08:52:15 -080049
50class igmp_exchange(unittest.TestCase):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080051
52 IGMP_TEST_TIMEOUT = 5
53 MCAST_TRAFFIC_TIMEOUT = 10
54 max_packets = 100
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080055 app = 'org.onosproject.igmp'
56
57 def setUp(self):
58 ''' Activate the dhcp app'''
59 self.onos_ctrl = OnosCtrl(self.app)
60 status, _ = self.onos_ctrl.activate()
61 assert_equal(status, True)
62 time.sleep(3)
63
64 def teardown(self):
65 '''Deactivate the dhcp app'''
66 self.onos_ctrl.deactivate()
67
68 def onos_load_config(self, config):
69 status, code = self.onos_ctrl.config(config)
70 if status is False:
71 log.info('JSON request returned status %d' %code)
72 assert_equal(status, True)
73 time.sleep(2)
74
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080075 def onos_ssm_table_load(self, groups, src_list):
76 ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
77 ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
78 for g in groups:
79 for s in src_list:
80 d = {}
81 d['source'] = s
82 d['group'] = g
83 ssm_xlate_list.append(d)
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -080084 self.onos_load_config(ssm_dict)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080085 time.sleep(2)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080086
87 def igmp_verify_join(self, igmpStateList):
88 sendState, recvState = igmpStateList
Chetan Gaonker25470972016-02-26 08:52:15 -080089 ## check if the send is received for the groups
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080090 for g in sendState.groups:
91 tx_stats = sendState.group_map[g][0]
92 tx = tx_stats.count
Chetan Gaonker25470972016-02-26 08:52:15 -080093 assert_greater(tx, 0)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080094 rx_stats = recvState.group_map[g][1]
95 rx = rx_stats.count
96 assert_greater(rx, 0)
97 log.info('Receive stats %s for group %s' %(rx_stats, g))
98
99 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -0800100
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800101 def igmp_verify_leave(self, igmpStateList, leave_groups):
102 sendState, recvState = igmpStateList[0], igmpStateList[1]
103 ## check if the send is received for the groups
104 for g in sendState.groups:
105 tx_stats = sendState.group_map[g][0]
106 rx_stats = recvState.group_map[g][1]
107 tx = tx_stats.count
108 rx = rx_stats.count
109 assert_greater(tx, 0)
110 if g not in leave_groups:
111 log.info('Received %d packets for group %s' %(rx, g))
112 for g in leave_groups:
113 rx = recvState.group_map[g][1].count
114 assert_equal(rx, 0)
115
116 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -0800117
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800118 def mcast_traffic_timer(self):
119 self.mcastTraffic.stopReceives()
120
121 def send_mcast_cb(self, send_state):
122 for g in send_state.groups:
123 send_state.update(g, tx = 1)
Chetan Gaonker25470972016-02-26 08:52:15 -0800124 return 0
125
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800126 ##Runs in the context of twisted reactor thread
Chetan Gaonker25470972016-02-26 08:52:15 -0800127 def igmp_recv(self, igmpState, iface = 'veth0'):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800128 p = self.recv_socket.recv()
129 send_time = float(p.payload.load)
130 recv_time = monotonic.monotonic()
131 #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
132 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
Chetan Gaonker25470972016-02-26 08:52:15 -0800133 return 0
134
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800135 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
136 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800137 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
138 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800139 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800140 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=g)
141 gr.sources = src_list
142 igmp.grps.append(gr)
143
144 pkt = igmp_eth/igmp_ip/igmp
145 IGMPv3.fixup(pkt)
146 sendp(pkt, iface=iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800147 if delay != 0:
148 time.sleep(delay)
149
150 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800151 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
152 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800153 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800154 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=g)
155 gr.sources = src_list
156 igmp.grps.append(gr)
157
158 pkt = igmp_eth/igmp_ip/igmp
159 IGMPv3.fixup(pkt)
160 sendp(pkt, iface = iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800161 if delay != 0:
162 time.sleep(delay)
Chetan Gaonker25470972016-02-26 08:52:15 -0800163
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800164 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
165 def test_igmp_join_verify_traffic(self):
Chetan Gaonker25470972016-02-26 08:52:15 -0800166 groups = ['224.0.1.1', '225.0.0.1']
Chetan Gaonker25470972016-02-26 08:52:15 -0800167 df = defer.Deferred()
168 igmpState = IGMPTestState(groups = groups, df = df)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800169 igmpStateRecv = IGMPTestState(groups = groups, df = df)
170 igmpStateList = (igmpState, igmpStateRecv)
171 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb, arg = igmpState)
172 self.df = df
173 self.mcastTraffic = mcastTraffic
174 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
175
176 def igmp_srp_task(stateList):
177 igmpSendState, igmpRecvState = stateList
178 if not mcastTraffic.isRecvStopped():
179 result = self.igmp_recv(igmpRecvState)
180 reactor.callLater(0, igmp_srp_task, stateList)
181 else:
182 self.mcastTraffic.stop()
183 self.recv_socket.close()
184 self.igmp_verify_join(stateList)
185 self.df.callback(0)
186
187 self.send_igmp_join(groups)
188 mcastTraffic.start()
189 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
190 reactor.callLater(0, igmp_srp_task, igmpStateList)
Chetan Gaonker25470972016-02-26 08:52:15 -0800191 return df
192
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800193 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
194 def test_igmp_leave_verify_traffic(self):
195 groups = ['224.0.1.10', '225.0.0.10']
196 leave_groups = ['224.0.1.10']
197 df = defer.Deferred()
198 igmpState = IGMPTestState(groups = groups, df = df)
199 igmpStateRecv = IGMPTestState(groups = groups, df = df)
200 igmpStateList = (igmpState, igmpStateRecv)
201 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
202 arg = igmpState)
203 self.df = df
204 self.mcastTraffic = mcastTraffic
205 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
206
207 def igmp_srp_task(stateList):
208 igmpSendState, igmpRecvState = stateList
209 if not mcastTraffic.isRecvStopped():
210 result = self.igmp_recv(igmpRecvState)
211 reactor.callLater(0, igmp_srp_task, stateList)
212 else:
213 self.mcastTraffic.stop()
214 self.recv_socket.close()
215 self.igmp_verify_leave(stateList, leave_groups)
216 self.df.callback(0)
217
218 self.send_igmp_join(groups)
219 self.send_igmp_leave(leave_groups, delay = 3)
220 mcastTraffic.start()
221 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
222 reactor.callLater(0, igmp_srp_task, igmpStateList)
223 return df
224
225 @deferred(timeout=100)
226 def test_igmp_leave_join_loop(self):
227 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
228 self.src_list = ['3.4.5.6', '7.8.9.10']
229 df = defer.Deferred()
230 self.df = df
231 self.iterations = 0
232 self.num_groups = len(self.groups)
233 self.MAX_TEST_ITERATIONS = 10
234
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800235 def igmp_srp_task(v):
236 if self.iterations < self.MAX_TEST_ITERATIONS:
237 if v == 1:
238 ##join test
239 self.num_groups = random.randint(0, len(self.groups))
240 self.send_igmp_join(self.groups[:self.num_groups],
241 src_list = self.src_list,
242 iface = 'veth0', delay = 0)
243 else:
244 self.send_igmp_leave(self.groups[:self.num_groups],
245 src_list = self.src_list,
246 iface = 'veth0', delay = 0)
247 self.iterations += 1
248 v ^= 1
249 reactor.callLater(1.0 + 0.5*self.num_groups,
250 igmp_srp_task, v)
251 else:
252 self.df.callback(0)
253
254 reactor.callLater(0, igmp_srp_task, 1)
255 return df
256
257 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
258 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800259 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
260 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800261 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800262 gr = IGMPv3gr(rtype = IGMP_V3_GR_TYPE_EXCLUDE, mcaddr = g)
263 gr.sources = src_list
264 igmp.grps.append(gr)
265
266 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800267 state.group_map[g][0].update(1, t = monotonic.monotonic())
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800268
269 pkt = igmp_eth/igmp_ip/igmp
270 IGMPv3.fixup(pkt)
271 sendp(pkt, iface=intf)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800272 log.debug('Returning from join task')
273
274 def igmp_recv_task(self, intf, groups, join_state):
275 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
276 group_map = {}
277 for g in groups:
278 group_map[g] = [0,0]
279
280 while True:
281 p = recv_socket.recv()
282 if p.dst in groups and group_map[p.dst][0] == 0:
283 group_map[p.dst][0] += 1
284 group_map[p.dst][1] = monotonic.monotonic()
285 c = 0
286 for g in groups:
287 c += group_map[g][0]
288 if c == len(groups):
289 break
290 for g in groups:
291 join_start = join_state.group_map[g][0].start
292 recv_time = group_map[g][1] * 1000000
293 delta = (recv_time - join_start)
294 log.info('Join for group %s received in %.3f usecs' %
295 (g, delta))
296
297 recv_socket.close()
298 log.debug('Returning from recv task')
299
300 def group_latency_check(self, groups):
301 tasks = []
302 self.send_igmp_leave(groups = groups)
303 join_state = IGMPTestState(groups = groups)
304 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
305 traffic_state = IGMPTestState(groups = groups)
306 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
307 arg = traffic_state)
308 mcast_traffic.start()
309 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
310 for t in tasks:
311 t.start()
312 for t in tasks:
313 t.join()
314
315 mcast_traffic.stop()
316 self.send_igmp_leave(groups = groups)
317 return
318
319 def test_igmp_1group_join_latency(self):
320 groups = ['239.0.1.1']
321 self.group_latency_check(groups)
322
323 def test_igmp_2group_join_latency(self):
324 groups = ['239.0.1.1', '240.0.1.1']
325 self.group_latency_check(groups)
326
327 def test_igmp_Ngroup_join_latency(self):
328 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
329 self.group_latency_check(groups)
330
331