blob: 28c243af9fc698e5f0ac2ec12b9c753964252530 [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)
Chetan Gaonkere88c95c2016-03-02 05:21:47 -080016from 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
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 Gaonker25470972016-02-26 08:52:15 -080055
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080056 def onos_ssm_table_load(self, groups, src_list):
57 ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
58 ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
59 for g in groups:
60 for s in src_list:
61 d = {}
62 d['source'] = s
63 d['group'] = g
64 ssm_xlate_list.append(d)
65 json_dict = json.JSONEncoder().encode(ssm_dict)
66 with tempfile.NamedTemporaryFile(delete=False) as temp:
67 temp.write(json_dict)
68 temp.flush()
69 temp.close()
70 log.debug('Loading SSM config in file %s to ONOS.' %temp.name)
71 os.system('./igmp_ssm_load.sh %s' %temp.name)
72 os.unlink(temp.name)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080073 ##Wait for ONOS to populate the SSM map before sending join.Huh
74 time.sleep(2)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080075
76 def igmp_verify_join(self, igmpStateList):
77 sendState, recvState = igmpStateList
Chetan Gaonker25470972016-02-26 08:52:15 -080078 ## check if the send is received for the groups
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080079 for g in sendState.groups:
80 tx_stats = sendState.group_map[g][0]
81 tx = tx_stats.count
Chetan Gaonker25470972016-02-26 08:52:15 -080082 assert_greater(tx, 0)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080083 rx_stats = recvState.group_map[g][1]
84 rx = rx_stats.count
85 assert_greater(rx, 0)
86 log.info('Receive stats %s for group %s' %(rx_stats, g))
87
88 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -080089
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080090 def igmp_verify_leave(self, igmpStateList, leave_groups):
91 sendState, recvState = igmpStateList[0], igmpStateList[1]
92 ## check if the send is received for the groups
93 for g in sendState.groups:
94 tx_stats = sendState.group_map[g][0]
95 rx_stats = recvState.group_map[g][1]
96 tx = tx_stats.count
97 rx = rx_stats.count
98 assert_greater(tx, 0)
99 if g not in leave_groups:
100 log.info('Received %d packets for group %s' %(rx, g))
101 for g in leave_groups:
102 rx = recvState.group_map[g][1].count
103 assert_equal(rx, 0)
104
105 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -0800106
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800107 def mcast_traffic_timer(self):
108 self.mcastTraffic.stopReceives()
109
110 def send_mcast_cb(self, send_state):
111 for g in send_state.groups:
112 send_state.update(g, tx = 1)
Chetan Gaonker25470972016-02-26 08:52:15 -0800113 return 0
114
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800115 ##Runs in the context of twisted reactor thread
Chetan Gaonker25470972016-02-26 08:52:15 -0800116 def igmp_recv(self, igmpState, iface = 'veth0'):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800117 p = self.recv_socket.recv()
118 send_time = float(p.payload.load)
119 recv_time = monotonic.monotonic()
120 #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
121 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
Chetan Gaonker25470972016-02-26 08:52:15 -0800122 return 0
123
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800124 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
125 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800126 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
127 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800128 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800129 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=g)
130 gr.sources = src_list
131 igmp.grps.append(gr)
132
133 pkt = igmp_eth/igmp_ip/igmp
134 IGMPv3.fixup(pkt)
135 sendp(pkt, iface=iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800136 if delay != 0:
137 time.sleep(delay)
138
139 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800140 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
141 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800142 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800143 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=g)
144 gr.sources = src_list
145 igmp.grps.append(gr)
146
147 pkt = igmp_eth/igmp_ip/igmp
148 IGMPv3.fixup(pkt)
149 sendp(pkt, iface = iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800150 if delay != 0:
151 time.sleep(delay)
Chetan Gaonker25470972016-02-26 08:52:15 -0800152
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800153 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
154 def test_igmp_join_verify_traffic(self):
Chetan Gaonker25470972016-02-26 08:52:15 -0800155 groups = ['224.0.1.1', '225.0.0.1']
Chetan Gaonker25470972016-02-26 08:52:15 -0800156 df = defer.Deferred()
157 igmpState = IGMPTestState(groups = groups, df = df)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800158 igmpStateRecv = IGMPTestState(groups = groups, df = df)
159 igmpStateList = (igmpState, igmpStateRecv)
160 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb, arg = igmpState)
161 self.df = df
162 self.mcastTraffic = mcastTraffic
163 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
164
165 def igmp_srp_task(stateList):
166 igmpSendState, igmpRecvState = stateList
167 if not mcastTraffic.isRecvStopped():
168 result = self.igmp_recv(igmpRecvState)
169 reactor.callLater(0, igmp_srp_task, stateList)
170 else:
171 self.mcastTraffic.stop()
172 self.recv_socket.close()
173 self.igmp_verify_join(stateList)
174 self.df.callback(0)
175
176 self.send_igmp_join(groups)
177 mcastTraffic.start()
178 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
179 reactor.callLater(0, igmp_srp_task, igmpStateList)
Chetan Gaonker25470972016-02-26 08:52:15 -0800180 return df
181
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800182 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
183 def test_igmp_leave_verify_traffic(self):
184 groups = ['224.0.1.10', '225.0.0.10']
185 leave_groups = ['224.0.1.10']
186 df = defer.Deferred()
187 igmpState = IGMPTestState(groups = groups, df = df)
188 igmpStateRecv = IGMPTestState(groups = groups, df = df)
189 igmpStateList = (igmpState, igmpStateRecv)
190 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
191 arg = igmpState)
192 self.df = df
193 self.mcastTraffic = mcastTraffic
194 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
195
196 def igmp_srp_task(stateList):
197 igmpSendState, igmpRecvState = stateList
198 if not mcastTraffic.isRecvStopped():
199 result = self.igmp_recv(igmpRecvState)
200 reactor.callLater(0, igmp_srp_task, stateList)
201 else:
202 self.mcastTraffic.stop()
203 self.recv_socket.close()
204 self.igmp_verify_leave(stateList, leave_groups)
205 self.df.callback(0)
206
207 self.send_igmp_join(groups)
208 self.send_igmp_leave(leave_groups, delay = 3)
209 mcastTraffic.start()
210 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
211 reactor.callLater(0, igmp_srp_task, igmpStateList)
212 return df
213
214 @deferred(timeout=100)
215 def test_igmp_leave_join_loop(self):
216 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
217 self.src_list = ['3.4.5.6', '7.8.9.10']
218 df = defer.Deferred()
219 self.df = df
220 self.iterations = 0
221 self.num_groups = len(self.groups)
222 self.MAX_TEST_ITERATIONS = 10
223
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800224 def igmp_srp_task(v):
225 if self.iterations < self.MAX_TEST_ITERATIONS:
226 if v == 1:
227 ##join test
228 self.num_groups = random.randint(0, len(self.groups))
229 self.send_igmp_join(self.groups[:self.num_groups],
230 src_list = self.src_list,
231 iface = 'veth0', delay = 0)
232 else:
233 self.send_igmp_leave(self.groups[:self.num_groups],
234 src_list = self.src_list,
235 iface = 'veth0', delay = 0)
236 self.iterations += 1
237 v ^= 1
238 reactor.callLater(1.0 + 0.5*self.num_groups,
239 igmp_srp_task, v)
240 else:
241 self.df.callback(0)
242
243 reactor.callLater(0, igmp_srp_task, 1)
244 return df
245
246 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
247 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800248 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
249 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800250 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800251 gr = IGMPv3gr(rtype = IGMP_V3_GR_TYPE_EXCLUDE, mcaddr = g)
252 gr.sources = src_list
253 igmp.grps.append(gr)
254
255 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800256 state.group_map[g][0].update(1, t = monotonic.monotonic())
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800257
258 pkt = igmp_eth/igmp_ip/igmp
259 IGMPv3.fixup(pkt)
260 sendp(pkt, iface=intf)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800261 log.debug('Returning from join task')
262
263 def igmp_recv_task(self, intf, groups, join_state):
264 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
265 group_map = {}
266 for g in groups:
267 group_map[g] = [0,0]
268
269 while True:
270 p = recv_socket.recv()
271 if p.dst in groups and group_map[p.dst][0] == 0:
272 group_map[p.dst][0] += 1
273 group_map[p.dst][1] = monotonic.monotonic()
274 c = 0
275 for g in groups:
276 c += group_map[g][0]
277 if c == len(groups):
278 break
279 for g in groups:
280 join_start = join_state.group_map[g][0].start
281 recv_time = group_map[g][1] * 1000000
282 delta = (recv_time - join_start)
283 log.info('Join for group %s received in %.3f usecs' %
284 (g, delta))
285
286 recv_socket.close()
287 log.debug('Returning from recv task')
288
289 def group_latency_check(self, groups):
290 tasks = []
291 self.send_igmp_leave(groups = groups)
292 join_state = IGMPTestState(groups = groups)
293 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
294 traffic_state = IGMPTestState(groups = groups)
295 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
296 arg = traffic_state)
297 mcast_traffic.start()
298 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
299 for t in tasks:
300 t.start()
301 for t in tasks:
302 t.join()
303
304 mcast_traffic.stop()
305 self.send_igmp_leave(groups = groups)
306 return
307
308 def test_igmp_1group_join_latency(self):
309 groups = ['239.0.1.1']
310 self.group_latency_check(groups)
311
312 def test_igmp_2group_join_latency(self):
313 groups = ['239.0.1.1', '240.0.1.1']
314 self.group_latency_check(groups)
315
316 def test_igmp_Ngroup_join_latency(self):
317 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
318 self.group_latency_check(groups)
319
320