blob: db7765e7a0f43dfe0a6384dd7da500ff95d5a5f8 [file] [log] [blame]
Matteo Scandolo48d3d2d2017-08-08 13:05:27 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Chetan Gaonker26ae67e2017-07-18 19:58:30 +000017#
18# Copyright 2016-present Ciena Corporation
19#
20# Licensed under the Apache License, Version 2.0 (the "License");
21# you may not use this file except in compliance with the License.
22# You may obtain a copy of the License at
23#
24# http://www.apache.org/licenses/LICENSE-2.0
25#
26# Unless required by applicable law or agreed to in writing, software
27# distributed under the License is distributed on an "AS IS" BASIS,
28# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29# See the License for the specific language governing permissions and
30# limitations under the License.
31#
32from twisted.internet import defer
33from nose.tools import *
34from nose.twistedtools import reactor, deferred
35from scapy.all import *
36from select import select as socket_select
37import time, monotonic
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +000038import requests
Chetan Gaonker26ae67e2017-07-18 19:58:30 +000039import os
40import random
41import threading
42from IGMP import *
43from McastTraffic import *
44from Stats import Stats
45from OnosCtrl import OnosCtrl
46from OltConfig import OltConfig
47from Channels import IgmpChannel
48from CordLogger import CordLogger
49from CordTestConfig import setup_module, teardown_module
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +000050from onosclidriver import OnosCliDriver
51from CordTestUtils import get_mac, get_controller
52from portmaps import g_subscriber_port_map
Chetan Gaonker26ae67e2017-07-18 19:58:30 +000053from CordTestUtils import log_test
54log_test.setLevel('INFO')
55
56class IGMPProxyTestState:
57
58 def __init__(self, groups = [], df = None, state = 0):
59 self.df = df
60 self.state = state
61 self.counter = 0
62 self.groups = groups
63 self.group_map = {} ##create a send/recv count map
64 for g in groups:
65 self.group_map[g] = (Stats(), Stats())
66
67 def update(self, group, tx = 0, rx = 0, t = 0):
68 self.counter += 1
69 index = 0 if rx == 0 else 1
70 v = tx if rx == 0 else rx
71 if self.group_map.has_key(group):
72 self.group_map[group][index].update(packets = v, t = t)
73
74 def update_state(self):
75 self.state = self.state ^ 1
76
77class igmpproxy_exchange(CordLogger):
78
79 V_INF1 = 'veth0'
80 MGROUP1 = '239.1.2.3'
81 MGROUP2 = '239.2.2.3'
82 MINVALIDGROUP1 = '255.255.255.255'
83 MINVALIDGROUP2 = '239.255.255.255'
84 MMACGROUP1 = "01:00:5e:01:02:03"
85 MMACGROUP2 = "01:00:5e:02:02:03"
86 IGMP_DST_MAC = "01:00:5e:00:00:16"
87 IGMP_SRC_MAC = "5a:e1:ac:ec:4d:a1"
88 IP_SRC = '1.2.3.4'
89 IP_DST = '224.0.0.22'
90 NEGATIVE_TRAFFIC_STATUS = 1
91 igmp_eth = Ether(dst = IGMP_DST_MAC, type = ETH_P_IP)
92 igmp_ip = IP(dst = IP_DST)
93 IGMP_TEST_TIMEOUT = 5
94 IGMP_QUERY_TIMEOUT = 60
95 MCAST_TRAFFIC_TIMEOUT = 20
96 PORT_TX_DEFAULT = 2
97 PORT_RX_DEFAULT = 1
98 max_packets = 100
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +000099 MAX_PORTS = 100
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000100 app = 'org.opencord.igmpproxy'
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000101 cord_config_app = 'org.opencord.config'
102 test_path = os.path.dirname(os.path.realpath(__file__))
103 proxy_device_id = OnosCtrl.get_device_id()
104 controller = get_controller()
105 app_files = [os.path.join(test_path, '..', 'apps/cord-config-3.0-SNAPSHOT.oar'), os.path.join(test_path, '..', 'apps/olt-app-3.0-SNAPSHOT.oar'), os.path.join(test_path, '..', 'apps/mcast-1.3.0-SNAPSHOT.oar'), os.path.join(test_path, '..', 'apps/onos-app-igmpproxy-1.1.0-SNAPSHOT.oar')]
106 proxy_config_file = os.path.join(test_path, '..', 'igmpproxy/igmpproxyconfig.json')
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000107 olt_conf_file = os.getenv('OLT_CONFIG_FILE', os.path.join(os.path.dirname(os.path.realpath(__file__)), '../setup/olt_config.json'))
108 ROVER_TEST_TIMEOUT = 300 #3600*86
109 ROVER_TIMEOUT = (ROVER_TEST_TIMEOUT - 100)
110 ROVER_JOIN_TIMEOUT = 60
111 VOLTHA_ENABLED = bool(int(os.getenv('VOLTHA_ENABLED', 0)))
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000112 configs = {}
113 proxy_interfaces_last = ()
114 interface_to_mac_map = {}
115 host_ip_map = {}
116 MAX_PORTS = 100
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000117
118 @classmethod
119 def setUpClass(cls):
120 cls.olt = OltConfig(olt_conf_file = cls.olt_conf_file)
121 cls.port_map, _ = cls.olt.olt_port_map()
122 if cls.VOLTHA_ENABLED is False:
123 OnosCtrl.config_device_driver()
124 OnosCtrl.cord_olt_config(cls.olt)
125 time.sleep(2)
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000126 cls.uninstall_cord_config_app()
127 cls.install_igmpproxy()
128 cls.igmp_proxy_setup()
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000129
130 @classmethod
131 def tearDownClass(cls):
132 if cls.VOLTHA_ENABLED is False:
133 OnosCtrl.config_device_driver(driver = 'ovs')
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000134 #cls.igmp_proxy_cleanup()
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000135
136 def setUp(self):
137 ''' Activate the igmp proxy app'''
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000138 super(igmpproxy_exchange, self).setUp()
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000139 self.igmp_channel = IgmpChannel()
140
141 def tearDown(self):
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000142 super(igmpproxy_exchange, self).tearDown()
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000143
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000144 @classmethod
145 def uninstall_cord_config_app(cls):
146 log_test.info('Uninstalling org.opencord.config 1.2 version app')
147 OnosCtrl(cls.cord_config_app).deactivate()
148 OnosCtrl.uninstall_app(cls.cord_config_app, onos_ip = cls.controller)
149
150 @classmethod
151 def install_igmpproxy(cls):
152 for app in cls.app_files:
153 OnosCtrl.install_app(app, onos_ip = cls.controller)
154 OnosCtrl(app).activate()
155
156 @classmethod
157 def igmp_proxy_setup(cls):
158 did = OnosCtrl.get_device_id()
159 cls.proxy_device_id = did
160 cls.olt = OltConfig(olt_conf_file = cls.olt_conf_file)
161 cls.port_map, _ = cls.olt.olt_port_map()
162 log_test.info('port map is %s'%cls.port_map)
163 if cls.port_map:
164 ##Per subscriber, we use 1 relay port
165 try:
166 proxy_port = cls.port_map[cls.port_map['relay_ports'][0]]
167 except:
168 proxy_port = cls.port_map['uplink']
169 cls.proxy_interface_port = proxy_port
170 cls.proxy_interfaces = (cls.port_map[cls.proxy_interface_port],)
171 else:
172 cls.proxy_interface_port = 100
173 cls.proxy_interfaces = (g_subscriber_port_map[cls.proxy_interface_port],)
174 cls.proxy_interfaces_last = cls.proxy_interfaces
175 if cls.port_map:
176 ##generate a ip/mac client virtual interface config for onos
177 interface_list = []
178 for port in cls.port_map['ports']:
179 port_num = cls.port_map[port]
180 if port_num == cls.port_map['uplink']:
181 continue
182 ip = cls.get_host_ip(port_num)
183 mac = cls.get_mac(port)
184 interface_list.append((port_num, ip, mac))
185
186 #configure igmp proxy virtual interface
187 proxy_ip = cls.get_host_ip(interface_list[0][0])
188 proxy_mac = cls.get_mac(cls.port_map[cls.proxy_interface_port])
189 interface_list.append((cls.proxy_interface_port, proxy_ip, proxy_mac))
190 cls.onos_interface_load(interface_list)
191
192 @classmethod
193 def igmp_proxy_cleanup(cls):
194 #reset the ONOS port configuration back to default
195 for config in cls.configs.items():
196 OnosCtrl.delete(config)
197 # if cls.onos_restartable is True:
198 # log_test.info('Cleaning up dhcp relay config by restarting ONOS with default network cfg')
199 # return cord_test_onos_restart(config = {})
200
201 @classmethod
202 def onos_load_config(cls, config,json_file=False):
203 log_test.info('onos load config is %s'%config)
204 status, code = OnosCtrl.config(config,json_file=json_file)
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000205 if status is False:
206 log_test.info('JSON request returned status %d' %code)
207 assert_equal(status, True)
208 time.sleep(2)
209
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000210 @classmethod
211 def onos_interface_load(cls, interface_list):
212 interface_dict = { 'ports': {} }
213 for port_num, ip, mac in interface_list:
214 port_map = interface_dict['ports']
215 port = '{}/{}'.format(cls.proxy_device_id, port_num)
216 port_map[port] = { 'interfaces': [] }
217 interface_list = port_map[port]['interfaces']
218 interface_map = { 'ips' : [ '{}/{}'.format(ip, 24) ],
219 'mac' : mac,
220 'name': 'vir-{}'.format(port_num)
221 }
222 interface_list.append(interface_map)
223
224 #cls.onos_load_config(interface_dict)
225 cls.configs['interface_config'] = interface_dict
226
227 @classmethod
228 def onos_igmp_proxy_config_load(cls):
229 proxy_connect_point = '{}/{}'.format(cls.proxy_device_id, cls.proxy_interface_port)
230 #log_test.info('\nrelay interface port is %s'%cls.proxy_interface_port)
231 #log_test.info('\nrelay interface is %s'%cls.port_map[cls.proxy_interface_port])
232 #log_test.info('\nconnect point is %s'%proxy_connect_point)
233 #cls.onos_load_config(cls.proxy_config_file,json_file=True)
234 igmpproxy_dict = {'apps':{
235 'org.opencord.igmpproxy':{
236 'igmpproxy':
237 {'globalConnectPointMode': 'true',
238 'globalConnectPoint': proxy_connect_point,
239 'UnsolicitedTimeOut': '2',
240 'MaxResp': '10',
241 'KeepAliveInterval': '120',
242 'KeepAliveCount': '3',
243 'LastQueryInterval': '2',
244 'LastQueryCount': '2',
245 'FastLeave': 'false',
246 'PeriodicQuery': 'true',
247 'IgmpCos': '7',
248 'withRAUpLink': 'true',
249 'withRADownLink': 'true'
250 }
251 },
252 'org.opencord.mcast':{
253 'ingressVlan': '222',
254 'egressVlan': '17'
255 }
256 }
257 }
258 device_dict = {'devices':{
259 cls.proxy_device_id: {
260 'basic': {
261 'driver': 'default'
262 },
263 'accessDevice': {
264 'uplink': '2',
265 'vlan': '222',
266 'defaultVlan': '1'
267 }
268 }
269 }
270 }
271 log_test.info('igmp proxy dict is %s'%igmpproxy_dict)
272 cls.onos_load_config(igmpproxy_dict)
273 cls.onos_load_config(device_dict)
274 cls.configs['relay_config'] = igmpproxy_dict
275 cls.configs['device_config'] = device_dict
276
277 @classmethod
278 def get_host_ip(cls, port):
279 if cls.host_ip_map.has_key(port):
280 return cls.host_ip_map[port]
281 cls.host_ip_map[port] = '192.168.1.{}'.format(port)
282 return cls.host_ip_map[port]
283
284 @classmethod
285 def host_load(cls, iface):
286 '''Have ONOS discover the hosts for dhcp-relay responses'''
287 port = g_subscriber_port_map[iface]
288 host = '173.17.1.{}'.format(port)
289 cmds = ( 'ifconfig {} 0'.format(iface),
290 'ifconfig {0} {1}'.format(iface, host),
291 'arping -I {0} {1} -c 2'.format(iface, host),)
292 #'ifconfig {} 0'.format(iface), )
293 for c in cmds:
294 log_test.info('host load config command %s'%c)
295 os.system(c)
296
297 @classmethod
298 def host_config_load(cls, host_config = None):
299 for host in host_config:
300 status, code = OnosCtrl.host_config(host)
301 if status is False:
302 log_test.info('JSON request returned status %d' %code)
303 assert_equal(status, True)
304
305 @classmethod
306 def generate_host_config(cls,ip,mac):
307 num = 0
308 hosts_dict = {}
309 hosts_list = [(ip,mac),]
310 for host, mac in hosts_list:
311 port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
312 hosts_dict[host] = {'mac':mac, 'vlan':'none', 'ipAddresses':[host], 'location':{ 'elementId' : '{}'.format(cls.proxy_device_id), 'port': port}}
313 num += 1
314 return hosts_dict.values()
315
316
317 @classmethod
318 def get_mac(cls, iface):
319 if cls.interface_to_mac_map.has_key(iface):
320 return cls.interface_to_mac_map[iface]
321 mac = get_mac(iface, pad = 0)
322 cls.interface_to_mac_map[iface] = mac
323 return mac
324
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000325 def onos_ssm_table_load(self, groups, src_list = ['1.2.3.4'],flag = False):
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000326 ssm_dict = {'apps' : { 'org.opencord.igmpproxy' : { 'ssmTranslate' : [] } } }
327 ssm_xlate_list = ssm_dict['apps']['org.opencord.igmpproxy']['ssmTranslate']
328 if flag: #to maintain seperate group-source pair.
329 for i in range(len(groups)):
330 d = {}
331 d['source'] = src_list[i] or '0.0.0.0'
332 d['group'] = groups[i]
333 ssm_xlate_list.append(d)
334 else:
335 for g in groups:
336 for s in src_list:
337 d = {}
338 d['source'] = s or '0.0.0.0'
339 d['group'] = g
340 ssm_xlate_list.append(d)
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000341 log_test.info('onos ssm table config dictionary is %s'%ssm_dict)
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000342 self.onos_load_config(ssm_dict)
343 cord_port_map = {}
344 for g in groups:
345 cord_port_map[g] = (self.PORT_TX_DEFAULT, self.PORT_RX_DEFAULT)
346 self.igmp_channel.cord_port_table_load(cord_port_map)
347 time.sleep(2)
348
349 def get_igmp_intf(self):
350 inst = os.getenv('TEST_INSTANCE', None)
351 if not inst:
352 return 'veth0'
353 inst = int(inst) + 1
354 if inst >= self.port_map['uplink']:
355 inst += 1
356 if self.port_map.has_key(inst):
357 return self.port_map[inst]
358 return 'veth0'
359
360 def igmp_verify_join(self, igmpStateList):
361 sendState, recvState = igmpStateList
362 ## check if the send is received for the groups
363 for g in sendState.groups:
364 tx_stats = sendState.group_map[g][0]
365 tx = tx_stats.count
366 assert_greater(tx, 0)
367 rx_stats = recvState.group_map[g][1]
368 rx = rx_stats.count
369 assert_greater(rx, 0)
370 log_test.info('Receive stats %s for group %s' %(rx_stats, g))
371
372 log_test.info('IGMP test verification success')
373
374 def igmp_verify_leave(self, igmpStateList, leave_groups):
375 sendState, recvState = igmpStateList[0], igmpStateList[1]
376 ## check if the send is received for the groups
377 for g in sendState.groups:
378 tx_stats = sendState.group_map[g][0]
379 rx_stats = recvState.group_map[g][1]
380 tx = tx_stats.count
381 rx = rx_stats.count
382 assert_greater(tx, 0)
383 if g not in leave_groups:
384 log_test.info('Received %d packets for group %s' %(rx, g))
385 for g in leave_groups:
386 rx = recvState.group_map[g][1].count
387 assert_equal(rx, 0)
388
389 log_test.info('IGMP test verification success')
390
391 def mcast_traffic_timer(self):
392 log_test.info('MCAST traffic timer expiry')
393 self.mcastTraffic.stopReceives()
394
395 def send_mcast_cb(self, send_state):
396 for g in send_state.groups:
397 send_state.update(g, tx = 1)
398 return 0
399
400 ##Runs in the context of twisted reactor thread
401 def igmp_recv(self, igmpState):
402 s = socket_select([self.recv_socket], [], [], 1.0)
403 if self.recv_socket in s[0]:
404 p = self.recv_socket.recv()
405 try:
406 send_time = float(p.payload.load)
407 recv_time = monotonic.monotonic()
408 except:
409 log_test.info('Unexpected Payload received: %s' %p.payload.load)
410 return 0
411 #log_test.info( 'Recv in %.6f secs' %(recv_time - send_time))
412 igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
413 return 0
414
415 def send_igmp_join(self, groups, src_list = ['1.2.3.4'], record_type=IGMP_V3_GR_TYPE_INCLUDE,
416 ip_pkt = None, iface = 'veth0', ssm_load = False, delay = 1):
417 if ssm_load is True:
418 self.onos_ssm_table_load(groups, src_list)
419 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
420 gaddr=self.IP_DST)
421 for g in groups:
422 gr = IGMPv3gr(rtype= record_type, mcaddr=g)
423 gr.sources = src_list
424 igmp.grps.append(gr)
425 if ip_pkt is None:
426 ip_pkt = self.igmp_eth/self.igmp_ip
427 pkt = ip_pkt/igmp
428 IGMPv3.fixup(pkt)
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000429 log_test.info('sending igmp join packet %s'%pkt.show())
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000430 sendp(pkt, iface=iface)
431 if delay != 0:
432 time.sleep(delay)
433
434 def send_igmp_join_recvQuery(self, groups, rec_queryCount = None, src_list = ['1.2.3.4'], ip_pkt = None, iface = 'veth0', delay = 2):
435 self.onos_ssm_table_load(groups, src_list)
436 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
437 gaddr=self.IP_DST)
438 for g in groups:
439 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_INCLUDE, mcaddr=g)
440 gr.sources = src_list
441 gr.sources = src_list
442 igmp.grps.append(gr)
443 if ip_pkt is None:
444 ip_pkt = self.igmp_eth/self.igmp_ip
445 pkt = ip_pkt/igmp
446 IGMPv3.fixup(pkt)
447 if rec_queryCount == None:
448 log_test.info('Sending IGMP join for group %s and waiting for one query packet and printing the packet' %groups)
449 resp = srp1(pkt, iface=iface)
450 else:
451 log_test.info('Sending IGMP join for group %s and waiting for periodic query packets and printing one packet' %groups)
452 resp = srp1(pkt, iface=iface)
453# resp = srp1(pkt, iface=iface) if rec_queryCount else srp3(pkt, iface=iface)
454 resp[0].summary()
455 log_test.info('Sent IGMP join for group %s and received a query packet and printing packet' %groups)
456 if delay != 0:
457 time.sleep(delay)
458
459 def send_igmp_leave(self, groups, src_list = ['1.2.3.4'], ip_pkt = None, iface = 'veth0', delay = 2):
460 log_test.info('entering into igmp leave function')
461 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
462 gaddr=self.IP_DST)
463 for g in groups:
464 gr = IGMPv3gr(rtype=IGMP_V3_GR_TYPE_EXCLUDE, mcaddr=g)
465 gr.sources = src_list
466 igmp.grps.append(gr)
467 if ip_pkt is None:
468 ip_pkt = self.igmp_eth/self.igmp_ip
469 pkt = ip_pkt/igmp
470 IGMPv3.fixup(pkt)
471 sendp(pkt, iface = iface)
472 if delay != 0:
473 time.sleep(delay)
474
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000475 def test_igmpproxy_app_installation(self):
476 #self.uninstall_cord_config_app()
477 #self.install_igmpproxy()
478 auth = ('karaf','karaf')
479 url = 'http://{}:8181/onos/v1/applications'.format(self.controller)
480 for file in self.app_files:
481 with open(file, 'rb') as payload:
482 res = requests.post(url,auth=auth,data=payload)
483 assert_equal(res.ok, True)
484
485 def test_igmpproxy_app_netcfg(self):
486 auth = ('karaf','karaf')
487 net_cfg_url = 'http://{}:8181/onos/v1/network/configuration/'.format(self.controller)
488 with open(self.proxy_config_file, 'rb') as payload:
489 res = requests.post(net_cfg_url,auth=auth,data=payload)
490 assert_equal(res.ok, True)
491
492 def test_igmpproxy_for_first_join(self,iface='veth0'):
493 group = ['224.9.8.7']
494 src = ['10.9.8.7']
495 self.onos_igmp_proxy_config_load()
496 self.onos_ssm_table_load(group,src_list=src)
497 self.success = False
498 def recv_task():
499 def igmp_recv_cb(pkt):
500 log_test.info('igmp packet received on proxy interface')
501 self.success = True
502 sniff(prn = igmp_recv_cb,lfilter = lambda p: IP in p and p[IP].proto == 2 and p[IP].dst==self.IP_DST, count=1,timeout = 5, iface=self.proxy_interfaces[0])
503 t = threading.Thread(target = recv_task)
504 t.start()
505 self.send_igmp_join(groups = group, src_list = src,record_type = IGMP_V3_GR_TYPE_INCLUDE,
506 iface = iface)
507 t.join()
508 assert_equal(self.success,True)
509 """
510 ############# Traffic Test
511 def recv_task():
512 def igmp_recv_cb(pkt):
513 log_test.info('igmp data traffic received on proxy interface')
514 self.success = False
515 sniff(prn = igmp_recv_cb,count=1,timeout = 5, iface=self.proxy_interfaces[0])
516 t = threading.Thread(target = recv_task)
517 t.start()
518 data = repr(monotonic.monotonic())
519 pkt2 = Ether(dst='01:00:5e:09:08:07')/IP(src='10.9.8.7',dst='224.9.8.7')/data
520 log_test.info('igmp data traffic packet is %s'%pkt2.show())
521 sendp(pkt2,iface='veth2',count=10)
522 t.join()
523 assert_equal(self.success,False)
524 """
525
526 def test_igmpproxy_for_two_joins(self,iface='veth0'):
527 groups = ['224.9.8.7','224.9.8.8']
528 src = ['10.9.8.7']
529 self.onos_igmp_proxy_config_load()
530 self.onos_ssm_table_load(groups,src_list=src)
531 def recv_task():
532 def igmp_recv_cb(pkt):
533 log_test.info('igmp packet received on proxy interface')
534 self.success = True
535 sniff(prn = igmp_recv_cb,lfilter = lambda p: IP in p and p[IP].proto == 2 and p[IP].dst==self.IP_DST, count=1,timeout = 5, iface=self.proxy_interfaces[0])
536 for group in groups:
537 self.success = False
538 t = threading.Thread(target = recv_task)
539 t.start()
540 self.send_igmp_join(groups = [group], src_list = src,record_type = IGMP_V3_GR_TYPE_INCLUDE,
541 iface = iface)
542 t.join()
543 assert_equal(self.success,True)
544
545 def test_igmpproxy_for_igmp_joins_on_non_proxy_interface(self,iface='veth0',non_proxy_iface='veth2'):
546 group = ['224.9.8.7']
547 src = ['10.9.8.7']
548 self.onos_igmp_proxy_config_load()
549 self.onos_ssm_table_load(group,src_list=src)
550 def recv_task():
551 def igmp_recv_cb(pkt):
552 log_test.info('igmp packet received on non-proxy interface')
553 self.success = True
554 sniff(prn = igmp_recv_cb,lfilter = lambda p: IP in p and p[IP].proto == 2 and p[IP].dst==self.IP_DST, count=1,timeout = 5, iface=non_proxy_iface)
555 self.success = False
556 t = threading.Thread(target = recv_task)
557 t.start()
558 self.send_igmp_join(groups = [group], src_list = src,record_type = IGMP_V3_GR_TYPE_INCLUDE,
559 iface = iface)
560 t.join()
561 assert_equal(self.success,False)
562
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000563 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+10)
564 def test_igmpproxy_with_join_and_verify_traffic(self):
Anil Kumar Sankae08e4aa2017-09-08 22:23:48 +0000565 #groups = [self.MGROUP1, self.MGROUP1]
566 groups = ["238.2.3.4"]
567 self.onos_igmp_proxy_config_load()
568 #self.onos_ssm_table_load(groups)
Chetan Gaonker26ae67e2017-07-18 19:58:30 +0000569 df = defer.Deferred()
570 igmpState = IGMPProxyTestState(groups = groups, df = df)
571 igmpStateRecv = IGMPProxyTestState(groups = groups, df = df)
572 igmpStateList = (igmpState, igmpStateRecv)
573 tx_intf = self.port_map[self.PORT_TX_DEFAULT]
574 rx_intf = self.port_map[self.PORT_RX_DEFAULT]
575 mcastTraffic = McastTraffic(groups, iface= tx_intf, cb = self.send_mcast_cb, arg = igmpState)
576 self.df = df
577 self.mcastTraffic = mcastTraffic
578 self.recv_socket = L3PacketSocket(iface = rx_intf, type = ETH_P_IP)
579
580 def igmp_srp_task(stateList):
581 igmpSendState, igmpRecvState = stateList
582 if not mcastTraffic.isRecvStopped():
583 self.igmp_recv(igmpRecvState)
584 reactor.callLater(0, igmp_srp_task, stateList)
585 else:
586 self.mcastTraffic.stop()
587 #log_test.info('Sending IGMP leave for groups: %s' %groups)
588 self.send_igmp_leave(groups, iface = rx_intf, delay = 2)
589 self.recv_socket.close()
590 self.igmp_verify_join(stateList)
591 self.df.callback(0)
592
593 self.send_igmp_join(groups, iface = rx_intf)
594 mcastTraffic.start()
595 self.test_timer = reactor.callLater(self.MCAST_TRAFFIC_TIMEOUT, self.mcast_traffic_timer)
596 reactor.callLater(0, igmp_srp_task, igmpStateList)
597 return df
598
599 @deferred(timeout=MCAST_TRAFFIC_TIMEOUT+40)
600 def test_igmpproxy_with_leave_and_verify_traffic(self):
601 groups = [self.MGROUP1]
602 leave_groups = [self.MGROUP1]
603 self.onos_ssm_table_load(groups)
604 df = defer.Deferred()
605 igmpState = IGMPProxyTestState(groups = groups, df = df)
606 IGMPProxyTestState(groups = groups, df = df)
607 tx_intf = self.port_map[self.PORT_TX_DEFAULT]
608 rx_intf = self.port_map[self.PORT_RX_DEFAULT]
609 mcastTraffic = McastTraffic(groups, iface= tx_intf, cb = self.send_mcast_cb,
610 arg = igmpState)
611 self.df = df
612 self.mcastTraffic = mcastTraffic
613 self.recv_socket = L3PacketSocket(iface = rx_intf, type = ETH_P_IP)
614
615 mcastTraffic.start()
616 self.send_igmp_join(groups, iface = rx_intf)
617 time.sleep(5)
618 self.send_igmp_leave(leave_groups, delay = 3, iface = rx_intf)
619 time.sleep(10)
620 join_state = IGMPProxyTestState(groups = leave_groups)
621 status = self.igmp_not_recv_task(rx_intf, leave_groups, join_state)
622 log_test.info('verified status for igmp recv task %s'%status)
623 assert status == 1 , 'EXPECTED RESULT'
624 self.df.callback(0)
625 return df
626
627 @deferred(timeout=100)
628 def test_igmpproxy_with_leave_and_join_loop(self):
629 self.groups = ['226.0.1.1', '227.0.0.1', '228.0.0.1', '229.0.0.1', '230.0.0.1' ]
630 self.src_list = ['3.4.5.6', '7.8.9.10']
631 self.onos_ssm_table_load(self.groups,src_list=self.src_list)
632 df = defer.Deferred()
633 self.df = df
634 self.iterations = 0
635 self.num_groups = len(self.groups)
636 self.MAX_TEST_ITERATIONS = 10
637 rx_intf = self.port_map[self.PORT_RX_DEFAULT]
638
639 def igmp_srp_task(v):
640 if self.iterations < self.MAX_TEST_ITERATIONS:
641 if v == 1:
642 ##join test
643 self.num_groups = random.randint(0, len(self.groups))
644 self.send_igmp_join(self.groups[:self.num_groups],
645 src_list = self.src_list,
646 iface = rx_intf, delay = 0)
647 else:
648 self.send_igmp_leave(self.groups[:self.num_groups],
649 src_list = self.src_list,
650 iface = rx_intf, delay = 0)
651 self.iterations += 1
652 v ^= 1
653 reactor.callLater(1.0 + 0.5*self.num_groups,
654 igmp_srp_task, v)
655 else:
656 self.df.callback(0)
657
658 reactor.callLater(0, igmp_srp_task, 1)
659 return df
660
661 def igmp_join_task(self, intf, groups, state, src_list = ['1.2.3.4']):
662 #self.onos_ssm_table_load(groups, src_list)
663 igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
664 gaddr=self.IP_DST)
665 for g in groups:
666 gr = IGMPv3gr(rtype = IGMP_V3_GR_TYPE_INCLUDE, mcaddr = g)
667 gr.sources = src_list
668 igmp.grps.append(gr)
669
670 for g in groups:
671 state.group_map[g][0].update(1, t = monotonic.monotonic())
672
673 pkt = self.igmp_eth/self.igmp_ip/igmp
674 IGMPv3.fixup(pkt)
675 sendp(pkt, iface=intf)
676 log_test.debug('Returning from join task')
677
678 def igmp_recv_task(self, intf, groups, join_state):
679 recv_socket = L3PacketSocket(iface = intf, type = ETH_P_IP)
680 group_map = {}
681 for g in groups:
682 group_map[g] = [0,0]
683
684 log_test.info('Verifying join interface should receive multicast data')
685 while True:
686 p = recv_socket.recv()
687 if p.dst in groups and group_map[p.dst][0] == 0:
688 group_map[p.dst][0] += 1
689 group_map[p.dst][1] = monotonic.monotonic()
690 c = 0
691 for g in groups:
692 c += group_map[g][0]
693 if c == len(groups):
694 break
695 for g in groups:
696 join_start = join_state.group_map[g][0].start
697 recv_time = group_map[g][1] * 1000000
698 delta = (recv_time - join_start)
699 log_test.info('Join for group %s received in %.3f usecs' %
700 (g, delta))
701
702 recv_socket.close()
703 log_test.debug('Returning from recv task')
704
705 def igmp_not_recv_task(self, intf, groups, join_state):
706 log_test.info('Entering igmp not recv task loop')
707 recv_socket = L2Socket(iface = intf, type = ETH_P_IP)
708 group_map = {}
709 for g in groups:
710 group_map[g] = [0,0]
711
712 log_test.info('Verifying join interface, should not receive any multicast data')
713 self.NEGATIVE_TRAFFIC_STATUS = 1
714 def igmp_recv_cb(pkt):
715 log_test.info('Multicast packet %s received for left groups %s' %(pkt[IP].dst, groups))
716 self.NEGATIVE_TRAFFIC_STATUS = 2
717 sniff(prn = igmp_recv_cb, count = 1, lfilter = lambda p: IP in p and p[IP].dst in groups,
718 timeout = 3, opened_socket = recv_socket)
719 recv_socket.close()
720 return self.NEGATIVE_TRAFFIC_STATUS
721
722 def group_latency_check(self, groups):
723 tasks = []
724 self.send_igmp_leave(groups = groups)
725 join_state = IGMPProxyTestState(groups = groups)
726 tasks.append(threading.Thread(target=self.igmp_join_task, args = ('veth0', groups, join_state,)))
727 traffic_state = IGMPProxyTestState(groups = groups)
728 mcast_traffic = McastTraffic(groups, iface= 'veth2', cb = self.send_mcast_cb,
729 arg = traffic_state)
730 mcast_traffic.start()
731 tasks.append(threading.Thread(target=self.igmp_recv_task, args = ('veth0', groups, join_state)))
732 for t in tasks:
733 t.start()
734 for t in tasks:
735 t.join()
736
737 mcast_traffic.stop()
738 self.send_igmp_leave(groups = groups)
739 return
740
741 @deferred(timeout=IGMP_QUERY_TIMEOUT + 10)
742 def test_igmpproxy_with_1group_join_latency(self):
743 groups = ['239.0.1.1']
744 df = defer.Deferred()
745 def igmp_1group_join_latency():
746 self.group_latency_check(groups)
747 df.callback(0)
748 reactor.callLater(0, igmp_1group_join_latency)
749 return df
750
751 @deferred(timeout=IGMP_QUERY_TIMEOUT + 10)
752 def test_igmpproxy_with_2group_join_latency(self):
753 groups = [self.MGROUP1, self.MGROUP1]
754 df = defer.Deferred()
755 def igmp_2group_join_latency():
756 self.group_latency_check(groups)
757 df.callback(0)
758 reactor.callLater(0, igmp_2group_join_latency)
759 return df
760
761 @deferred(timeout=IGMP_QUERY_TIMEOUT + 10)
762 def test_igmpproxy_with_Ngroup_join_latency(self):
763 groups = ['239.0.1.1', '240.0.1.1', '241.0.1.1', '242.0.1.1']
764 df = defer.Deferred()
765 def igmp_Ngroup_join_latency():
766 self.group_latency_check(groups)
767 df.callback(0)
768 reactor.callLater(0, igmp_Ngroup_join_latency)
769 return df
770
771 def test_igmpproxy_with_join_rover_all(self):
772 s = (224 << 24) | 1
773 #e = (225 << 24) | (255 << 16) | (255 << 16) | 255
774 e = (224 << 24) | 10
775 for i in xrange(s, e+1):
776 if i&0xff:
777 ip = '%d.%d.%d.%d'%((i>>24)&0xff, (i>>16)&0xff, (i>>8)&0xff, i&0xff)
778 self.send_igmp_join([ip], delay = 0)
779
780 @deferred(timeout=ROVER_TEST_TIMEOUT)
781 def test_igmpproxy_with_join_rover(self):
782 df = defer.Deferred()
783 iface = self.get_igmp_intf()
784 self.df = df
785 self.count = 0
786 self.timeout = 0
787 self.complete = False
788 def igmp_join_timer():
789 self.timeout += self.ROVER_JOIN_TIMEOUT
790 log_test.info('IGMP joins sent: %d' %self.count)
791 if self.timeout >= self.ROVER_TIMEOUT:
792 self.complete = True
793 reactor.callLater(self.ROVER_JOIN_TIMEOUT, igmp_join_timer)
794
795 reactor.callLater(self.ROVER_JOIN_TIMEOUT, igmp_join_timer)
796 self.start_channel = (224 << 24) | 1
797 self.end_channel = (224 << 24) | 200 #(225 << 24) | (255 << 16) | (255 << 16) | 255
798 self.current_channel = self.start_channel
799 def igmp_join_rover(self):
800 #e = (224 << 24) | 10
801 chan = self.current_channel
802 self.current_channel += 1
803 if self.current_channel >= self.end_channel:
804 chan = self.current_channel = self.start_channel
805 if chan&0xff:
806 ip = '%d.%d.%d.%d'%((chan>>24)&0xff, (chan>>16)&0xff, (chan>>8)&0xff, chan&0xff)
807 self.send_igmp_join([ip], delay = 0, ssm_load = False, iface = iface)
808 self.count += 1
809 if self.complete == True:
810 log_test.info('%d IGMP joins sent in %d seconds over %s' %(self.count, self.timeout, iface))
811 self.df.callback(0)
812 else:
813 reactor.callLater(0, igmp_join_rover, self)
814 reactor.callLater(0, igmp_join_rover, self)
815 return df
816
817 @deferred(timeout=IGMP_QUERY_TIMEOUT + 10)
818 def test_igmpproxy_with_query(self):
819 groups = ['224.0.0.1'] ##igmp query group
820 self.onos_ssm_table_load(groups)
821 df = defer.Deferred()
822 self.df = df
823 self.recv_socket = L2Socket(iface = 'veth0', type = ETH_P_IP)
824
825 def igmp_query_timeout():
826 def igmp_query_cb(pkt):
827 log_test.info('received igmp query packet is %s'%pkt.show())
828 log_test.info('Got IGMP query packet from %s for %s' %(pkt[IP].src, pkt[IP].dst))
829 assert_equal(pkt[IP].dst, '224.0.0.1')
830 sniff(prn = igmp_query_cb, count=1, lfilter = lambda p: IP in p and p[IP].dst in groups,
831 opened_socket = self.recv_socket)
832 self.recv_socket.close()
833 self.df.callback(0)
834
835 #self.send_igmp_join(groups)
836 self.test_timer = reactor.callLater(self.IGMP_QUERY_TIMEOUT, igmp_query_timeout)
837 return df