blob: b92b58b00f7ba67e80d4a59bd248bcc6d76b5bb4 [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 Gaonker5a5204e2016-03-02 01:35:13 -080016#from IGMP import *
17from IGMP_scapy import *
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080018from McastTraffic import *
19from Stats import Stats
20log.setLevel('INFO')
Chetan Gaonker25470972016-02-26 08:52:15 -080021
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080022IGMP_DST_MAC = "01:00:5e:00:01:01"
23IGMP_SRC_MAC = "5a:e1:ac:ec:4d:a1"
24IP_SRC = '1.2.3.4'
25IP_DST = '224.0.1.1'
26
27igmp_eth = Ether(dst = IGMP_DST_MAC, src = IGMP_SRC_MAC, type = ETH_P_IP)
28igmp_ip = IP(dst = IP_DST, src = IP_SRC)
29
Chetan Gaonker25470972016-02-26 08:52:15 -080030class IGMPTestState:
31
32 def __init__(self, groups = [], df = None, state = 0):
33 self.df = df
34 self.state = state
35 self.counter = 0
36 self.groups = groups
37 self.group_map = {} ##create a send/recv count map
38 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080039 self.group_map[g] = (Stats(), Stats())
40
41 def update(self, group, tx = 0, rx = 0, t = 0):
42 self.counter += 1
Chetan Gaonker25470972016-02-26 08:52:15 -080043 index = 0 if rx == 0 else 1
44 v = tx if rx == 0 else rx
45 if self.group_map.has_key(group):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080046 self.group_map[group][index].update(packets = v, t = t)
Chetan Gaonker25470972016-02-26 08:52:15 -080047
48 def update_state(self):
49 self.state = self.state ^ 1
Chetan Gaonker25470972016-02-26 08:52:15 -080050
51class igmp_exchange(unittest.TestCase):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080052
53 IGMP_TEST_TIMEOUT = 5
54 MCAST_TRAFFIC_TIMEOUT = 10
55 max_packets = 100
Chetan Gaonker25470972016-02-26 08:52:15 -080056
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080057 def onos_ssm_table_load(self, groups, src_list):
58 ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
59 ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
60 for g in groups:
61 for s in src_list:
62 d = {}
63 d['source'] = s
64 d['group'] = g
65 ssm_xlate_list.append(d)
66 json_dict = json.JSONEncoder().encode(ssm_dict)
67 with tempfile.NamedTemporaryFile(delete=False) as temp:
68 temp.write(json_dict)
69 temp.flush()
70 temp.close()
71 log.debug('Loading SSM config in file %s to ONOS.' %temp.name)
72 os.system('./igmp_ssm_load.sh %s' %temp.name)
73 os.unlink(temp.name)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -080074 ##Wait for ONOS to populate the SSM map before sending join.Huh
75 time.sleep(2)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080076
77 def igmp_verify_join(self, igmpStateList):
78 sendState, recvState = igmpStateList
Chetan Gaonker25470972016-02-26 08:52:15 -080079 ## check if the send is received for the groups
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080080 for g in sendState.groups:
81 tx_stats = sendState.group_map[g][0]
82 tx = tx_stats.count
Chetan Gaonker25470972016-02-26 08:52:15 -080083 assert_greater(tx, 0)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080084 rx_stats = recvState.group_map[g][1]
85 rx = rx_stats.count
86 assert_greater(rx, 0)
87 log.info('Receive stats %s for group %s' %(rx_stats, g))
88
89 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -080090
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -080091 def igmp_verify_leave(self, igmpStateList, leave_groups):
92 sendState, recvState = igmpStateList[0], igmpStateList[1]
93 ## check if the send is received for the groups
94 for g in sendState.groups:
95 tx_stats = sendState.group_map[g][0]
96 rx_stats = recvState.group_map[g][1]
97 tx = tx_stats.count
98 rx = rx_stats.count
99 assert_greater(tx, 0)
100 if g not in leave_groups:
101 log.info('Received %d packets for group %s' %(rx, g))
102 for g in leave_groups:
103 rx = recvState.group_map[g][1].count
104 assert_equal(rx, 0)
105
106 log.info('IGMP test verification success')
Chetan Gaonker25470972016-02-26 08:52:15 -0800107
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800108 def mcast_traffic_timer(self):
109 self.mcastTraffic.stopReceives()
110
111 def send_mcast_cb(self, send_state):
112 for g in send_state.groups:
113 send_state.update(g, tx = 1)
Chetan Gaonker25470972016-02-26 08:52:15 -0800114 return 0
115
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800116 ##Runs in the context of twisted reactor thread
Chetan Gaonker25470972016-02-26 08:52:15 -0800117 def igmp_recv(self, igmpState, iface = 'veth0'):
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800118 p = self.recv_socket.recv()
119 send_time = float(p.payload.load)
120 recv_time = monotonic.monotonic()
121 #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
122 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
Chetan Gaonker25470972016-02-26 08:52:15 -0800123 return 0
124
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800125 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
126 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800127 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
128 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800129 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800130 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=g)
131 gr.sources = src_list
132 igmp.grps.append(gr)
133
134 pkt = igmp_eth/igmp_ip/igmp
135 IGMPv3.fixup(pkt)
136 sendp(pkt, iface=iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800137 if delay != 0:
138 time.sleep(delay)
139
140 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], iface = 'veth0', delay = 2):
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800141 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
142 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800143 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800144 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=g)
145 gr.sources = src_list
146 igmp.grps.append(gr)
147
148 pkt = igmp_eth/igmp_ip/igmp
149 IGMPv3.fixup(pkt)
150 sendp(pkt, iface = iface)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800151 if delay != 0:
152 time.sleep(delay)
Chetan Gaonker25470972016-02-26 08:52:15 -0800153
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800154 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
155 def test_igmp_join_verify_traffic(self):
Chetan Gaonker25470972016-02-26 08:52:15 -0800156 groups = ['224.0.1.1', '225.0.0.1']
Chetan Gaonker25470972016-02-26 08:52:15 -0800157 df = defer.Deferred()
158 igmpState = IGMPTestState(groups = groups, df = df)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800159 igmpStateRecv = IGMPTestState(groups = groups, df = df)
160 igmpStateList = (igmpState, igmpStateRecv)
161 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb, arg = igmpState)
162 self.df = df
163 self.mcastTraffic = mcastTraffic
164 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
165
166 def igmp_srp_task(stateList):
167 igmpSendState, igmpRecvState = stateList
168 if not mcastTraffic.isRecvStopped():
169 result = self.igmp_recv(igmpRecvState)
170 reactor.callLater(0, igmp_srp_task, stateList)
171 else:
172 self.mcastTraffic.stop()
173 self.recv_socket.close()
174 self.igmp_verify_join(stateList)
175 self.df.callback(0)
176
177 self.send_igmp_join(groups)
178 mcastTraffic.start()
179 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
180 reactor.callLater(0, igmp_srp_task, igmpStateList)
Chetan Gaonker25470972016-02-26 08:52:15 -0800181 return df
182
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800183 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
184 def test_igmp_leave_verify_traffic(self):
185 groups = ['224.0.1.10', '225.0.0.10']
186 leave_groups = ['224.0.1.10']
187 df = defer.Deferred()
188 igmpState = IGMPTestState(groups = groups, df = df)
189 igmpStateRecv = IGMPTestState(groups = groups, df = df)
190 igmpStateList = (igmpState, igmpStateRecv)
191 mcastTraffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
192 arg = igmpState)
193 self.df = df
194 self.mcastTraffic = mcastTraffic
195 self.recv_socket = L3PacketSocket(iface = 'veth0', type = ETH_P_IP)
196
197 def igmp_srp_task(stateList):
198 igmpSendState, igmpRecvState = stateList
199 if not mcastTraffic.isRecvStopped():
200 result = self.igmp_recv(igmpRecvState)
201 reactor.callLater(0, igmp_srp_task, stateList)
202 else:
203 self.mcastTraffic.stop()
204 self.recv_socket.close()
205 self.igmp_verify_leave(stateList, leave_groups)
206 self.df.callback(0)
207
208 self.send_igmp_join(groups)
209 self.send_igmp_leave(leave_groups, delay = 3)
210 mcastTraffic.start()
211 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
212 reactor.callLater(0, igmp_srp_task, igmpStateList)
213 return df
214
215 @deferred(timeout=100)
216 def test_igmp_leave_join_loop(self):
217 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
218 self.src_list = ['3.4.5.6', '7.8.9.10']
219 df = defer.Deferred()
220 self.df = df
221 self.iterations = 0
222 self.num_groups = len(self.groups)
223 self.MAX_TEST_ITERATIONS = 10
224
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800225 def igmp_srp_task(v):
226 if self.iterations < self.MAX_TEST_ITERATIONS:
227 if v == 1:
228 ##join test
229 self.num_groups = random.randint(0, len(self.groups))
230 self.send_igmp_join(self.groups[:self.num_groups],
231 src_list = self.src_list,
232 iface = 'veth0', delay = 0)
233 else:
234 self.send_igmp_leave(self.groups[:self.num_groups],
235 src_list = self.src_list,
236 iface = 'veth0', delay = 0)
237 self.iterations += 1
238 v ^= 1
239 reactor.callLater(1.0 + 0.5*self.num_groups,
240 igmp_srp_task, v)
241 else:
242 self.df.callback(0)
243
244 reactor.callLater(0, igmp_srp_task, 1)
245 return df
246
247 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
248 self.onos_ssm_table_load(groups, src_list)
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800249 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
250 gaddr='224.0.1.1')
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800251 for g in groups:
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800252 gr = IGMPv3gr(rtype = IGMP_V3_GR_TYPE_EXCLUDE, mcaddr = g)
253 gr.sources = src_list
254 igmp.grps.append(gr)
255
256 for g in groups:
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800257 state.group_map[g][0].update(1, t = monotonic.monotonic())
Chetan Gaonker5a5204e2016-03-02 01:35:13 -0800258
259 pkt = igmp_eth/igmp_ip/igmp
260 IGMPv3.fixup(pkt)
261 sendp(pkt, iface=intf)
Chetan Gaonkereb2b24b2016-03-01 14:04:45 -0800262 log.debug('Returning from join task')
263
264 def igmp_recv_task(self, intf, groups, join_state):
265 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
266 group_map = {}
267 for g in groups:
268 group_map[g] = [0,0]
269
270 while True:
271 p = recv_socket.recv()
272 if p.dst in groups and group_map[p.dst][0] == 0:
273 group_map[p.dst][0] += 1
274 group_map[p.dst][1] = monotonic.monotonic()
275 c = 0
276 for g in groups:
277 c += group_map[g][0]
278 if c == len(groups):
279 break
280 for g in groups:
281 join_start = join_state.group_map[g][0].start
282 recv_time = group_map[g][1] * 1000000
283 delta = (recv_time - join_start)
284 log.info('Join for group %s received in %.3f usecs' %
285 (g, delta))
286
287 recv_socket.close()
288 log.debug('Returning from recv task')
289
290 def group_latency_check(self, groups):
291 tasks = []
292 self.send_igmp_leave(groups = groups)
293 join_state = IGMPTestState(groups = groups)
294 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
295 traffic_state = IGMPTestState(groups = groups)
296 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
297 arg = traffic_state)
298 mcast_traffic.start()
299 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
300 for t in tasks:
301 t.start()
302 for t in tasks:
303 t.join()
304
305 mcast_traffic.stop()
306 self.send_igmp_leave(groups = groups)
307 return
308
309 def test_igmp_1group_join_latency(self):
310 groups = ['239.0.1.1']
311 self.group_latency_check(groups)
312
313 def test_igmp_2group_join_latency(self):
314 groups = ['239.0.1.1', '240.0.1.1']
315 self.group_latency_check(groups)
316
317 def test_igmp_Ngroup_join_latency(self):
318 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
319 self.group_latency_check(groups)
320
321