blob: a9083b976350d905d9c27c505938b5e8ec0dfdd1 [file] [log] [blame]
ChetanGaonkerf9c2f8b2016-07-19 15:49:41 -07001
2# Copyright 2016-present Ciena Corporation
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#
16import unittest
17from nose.tools import *
18from scapy.all import *
19from OnosCtrl import OnosCtrl
20from OltConfig import OltConfig
21from OnosFlowCtrl import OnosFlowCtrl, get_mac
22from onosclidriver import OnosCliDriver
23from CordContainer import Container, Onos, Quagga
24from CordTestServer import cord_test_onos_restart, cord_test_quagga_restart
25from portmaps import g_subscriber_port_map
26import threading
27from threading import current_thread
28import time
29import os
30import json
31log.setLevel('INFO')
32
33
34class proxyarp_exchange(unittest.TestCase):
35
36 apps = ('org.onosproject.vrouter','org.onosproject.proxyarp')
37 device_id = 'of:' + get_mac('ovsbr0')
38 device_dict = { "devices" : {
39 "{}".format(device_id) : {
40 "basic" : {
41 "driver" : "softrouter"
42 }
43 }
44 },
45 }
46 test_path = os.path.dirname(os.path.realpath(__file__))
47 onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
48 GATEWAY = '192.168.10.50'
49 INGRESS_PORT = 1
50 EGRESS_PORT = 2
51 MAX_PORTS = 100
52 hosts_list = [ ('192.168.10.1', '00:00:00:00:00:01'), ('192.168.11.1', '00:00:00:00:02:01'), ]
53
54 @classmethod
55 def setUpClass(cls):
56 cls.olt = OltConfig()
57 cls.port_map = cls.olt.olt_port_map()
58 if not cls.port_map:
59 cls.port_map = g_subscriber_port_map
60 time.sleep(3)
61
62 @classmethod
63 def tearDownClass(cls):
64 '''Deactivate the vrouter apps'''
65 #cls.vrouter_host_unload()
66
67 def cliEnter(self):
68 retries = 0
69 while retries < 3:
70 self.cli = OnosCliDriver(connect = True)
71 if self.cli.handle:
72 break
73 else:
74 retries += 1
75 time.sleep(2)
76 def cliExit(self):
77 self.cli.disconnect()
78
79 @classmethod
80 def proxyarp_host_unload(cls):
81 index = 1
82 for host,_ in cls.hosts_list:
83 iface = cls.port_map[index]
84 index += 1
85 config_cmds = ('ifconfig {} 0'.format(iface), )
86 for cmd in config_cmds:
87 log.info('host unload command %s' % cmd)
88 os.system(cmd)
89
90 @classmethod
91 def interface_config_load(cls, interface_cfg = None):
92 if type(interface_cfg) is tuple:
93 res = []
94 for v in interface_cfg:
95 if type(v) == list:
96 pass
97 else:
98 res += v.items()
99 config = dict(res)
100 else:
101 config = interface_cfg
102 cfg = json.dumps(config)
103 with open('{}/network-cfg.json'.format(cls.onos_config_path), 'w') as f:
104 f.write(cfg)
105 return cord_test_onos_restart()
106
107 @classmethod
108 def host_config_load(cls, host_config = None):
109 for host in host_config:
110 status, code = OnosCtrl.host_config(host)
111 if status is False:
112 log.info('JSON request returned status %d' %code)
113 assert_equal(status, True)
114
115 @classmethod
116 def generate_interface_config(cls, hosts = 1):
117 num = 0
118 start_host = ( 192 << 24) | ( 168 << 16) | (10 << 8) | 0
119 end_host = ( 200 << 24 ) | (168 << 16) | (10 << 8) | 0
120 ports_dict = { 'ports' : {} }
121 interface_list = []
122 hosts_list = []
123 for n in xrange(start_host, end_host, 256):
124 port_map = ports_dict['ports']
125 port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
126 device_port_key = '{0}/{1}'.format(cls.device_id, port)
127 try:
128 interfaces = port_map[device_port_key]['interfaces']
129 except:
130 port_map[device_port_key] = { 'interfaces' : [] }
131 interfaces = port_map[device_port_key]['interfaces']
132 ip = n + 1
133 host_ip = n + 2
134 ips = '%d.%d.%d.%d/24'%( (ip >> 24) & 0xff, ( (ip >> 16) & 0xff ), ( (ip >> 8 ) & 0xff ), ip & 0xff)
135 host = '%d.%d.%d.%d' % ( (host_ip >> 24) & 0xff, ( ( host_ip >> 16) & 0xff ), ( (host_ip >> 8 ) & 0xff ), host_ip & 0xff )
136 mac = RandMAC()._fix()
137 hosts_list.append((host, mac))
138 if num < cls.MAX_PORTS - 1:
139 interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : mac }
140 interfaces.append(interface_dict)
141 interface_list.append(interface_dict['name'])
142 else:
143 interfaces[0]['ips'].append(ips)
144 num += 1
145 if num == hosts:
146 break
147 cls.hosts_list = hosts_list
148 return (cls.device_dict, ports_dict, hosts_list)
149
150 @classmethod
151 def generate_host_config(cls):
152 num = 0
153 hosts_dict = {}
154 for host, mac in cls.hosts_list:
155 port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
156 hosts_dict[host] = {'mac':mac, 'vlan':'none', 'ipAddresses':[host], 'location':{ 'elementId' : '{}'.format(cls.device_id), 'port': port}}
157 num += 1
158 return hosts_dict.values()
159
160 @classmethod
161 def proxyarp_activate(cls, deactivate = False):
162 app = 'org.onosproject.proxyarp'
163 onos_ctrl = OnosCtrl(app)
164 if deactivate is True:
165 onos_ctrl.deactivate()
166 else:
167 onos_ctrl.activate()
168 time.sleep(3)
169
170 @classmethod
171 def proxyarp_config(cls, hosts = 1):
172 proxyarp_configs = cls.generate_interface_config(hosts = hosts)
173 cls.interface_config_load(interface_cfg = proxyarp_configs)
174 hostcfg = cls.generate_host_config()
175 cls.host_config_load(host_config = hostcfg)
176 return proxyarp_configs
177
178 def proxyarp_arpreply_verify(self, ingress, hostip, hostmac, PositiveTest=True):
179 log.info('verifying arp reply for host ip %s host mac %s on interface %s'%(hostip ,hostmac ,self.port_map[ingress]))
180 self.success = False
181 def recv_task():
182 def recv_cb(pkt):
183 log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
184 self.success = True if PositiveTest == True else False
185 sniff(count=1, timeout=2, lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
186 prn = recv_cb, iface = self.port_map[ingress])
187 t = threading.Thread(target = recv_task)
188 t.start()
189 pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst=hostip))
190 log.info('sending arp request for dest ip %s on interface %s' %
191 (hostip, self.port_map[ingress]))
192 sendp( pkt, count = 10, iface = self.port_map[ingress])
193 t.join()
194 if PositiveTest:
195 assert_equal(self.success, True)
196 else:
197 assert_equal(self.success, False)
198
199 def __proxyarp_hosts_verify(self, hosts = 1,PositiveTest = True):
200 _,_,hosts_config = self.proxyarp_config(hosts = hosts)
201 log.info('\nhosts_config %s and its type %s'%(hosts_config,type(hosts_config)))
202 self.cliEnter()
203 connected_hosts = json.loads(self.cli.hosts(jsonFormat = True))
204 log.info('Discovered hosts: %s' %connected_hosts)
205 #We read from cli if we expect less number of routes to avoid cli timeouts
206 if hosts <= 10000:
207 assert_equal(len(connected_hosts), hosts)
208 ingress = hosts+1
209 for hostip, hostmac in hosts_config:
210 self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = PositiveTest)
211 time.sleep(1)
212 self.cliExit()
213 return True
214
215 def test_proxyarp_with_1_host(self, hosts=1):
216 res = self.__proxyarp_hosts_verify(hosts = hosts)
217 assert_equal(res, True)
218 #cls.proxyarp_host_unload()
219 def test_proxyarp_with_10_hosts(self, hosts=10):
220 res = self.__proxyarp_hosts_verify(hosts = hosts)
221 assert_equal(res, True)
222 def test_proxyarp_with_50_hosts(self, hosts=50):
223 res = self.__proxyarp_hosts_verify(hosts = hosts)
224 assert_equal(res, True)
225 def test_proxyarp_app_with_disabling_and_re_enabling(self,hosts = 3):
226 ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
227 ingress = hosts+1
228 for hostip, hostmac in hosts_config:
229 self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
230 time.sleep(1)
231 log.info('Deactivating proxyarp app and expecting not to get arp reply from ONOS')
232 self.proxyarp_activate(deactivate = True)
233 for hostip, hostmac in hosts_config:
234 self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = False)
235 time.sleep(1)
236 log.info('activating proxyarp app and expecting to get arp reply from ONOS')
237 self.proxyarp_activate(deactivate = False)
238 for hostip, hostmac in hosts_config:
239 self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
240 time.sleep(1)
241
242 def test_proxyarp_nonexisting_host(self,hosts = 1):
243 _,_,hosts_config = self.proxyarp_config(hosts = hosts)
244 ingress = hosts + 2
245 for host, mac in hosts_config:
246 self.proxyarp_arpreply_verify(ingress,host,mac,PositiveTest = True)
247 new_host = hosts_config[-1][0].split('.')
248 new_host[2] = str(int(new_host[2])+1)
249 new_host = '.'.join(new_host)
250 new_mac = RandMAC()._fix()
251 log.info('verifying arp reply for host ip %s on interface %s'%(new_host,self.port_map[ingress]))
252 res=srp1(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst=new_host),timeout=2,iface=self.port_map[ingress])
253 assert_equal(res, None)
254 log.info('arp reply not seen for host ip %s on interface %s as expected'%(new_host,self.port_map[ingress]))
255 hosts = hosts + 1
256 _,_,hosts_config = self.proxyarp_config(hosts = hosts)
257 for host in hosts_config:
258 if host[0] == new_host:
259 new_mac = host[1]
260 self.proxyarp_arpreply_verify(ingress,new_host,new_mac,PositiveTest = True)
261
262 def test_proxyarp_removing_host(self,hosts = 3):
263 ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
264 ingress = hosts+1
265 for hostip, hostmac in hosts_config:
266 self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
267 time.sleep(1)
268 host_mac = hosts_config[0][1]
269 log.info('removing host entry %s' % host_mac)
270 self.cliEnter()
271 hostentries = json.loads(self.cli.hosts(jsonFormat = True))
272 for host in hostentries:
273 res = host_mac.upper() in host.values()
274 if res:
275 break
276 assert_equal(res, True)
277 hostid = host_mac+'/'+'None'
278 delete_host = self.cli.host_remove(hostid)
279 hostentries = json.loads(self.cli.hosts(jsonFormat = True))
280 for host in hostentries:
281 res = host_mac.upper() in host.values()
282 if res:
283 break
284 assert_equal(res, False)
285 self.proxyarp_arpreply_verify(ingress,hosts_config[0][0],host_mac,PositiveTest = False)
286 time.sleep(1)
287 self.cliExit()
288
289 def test_proxyarp_concurrent_requests_with_multiple_host_and_different_interfaces(self,hosts = 10):
290 ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
291 self.success = True
292 ingress = hosts+1
293 ports = range(ingress,ingress+10)
294 hostmac = []
295 hostip = []
296 for ip,mac in hosts_config:
297 hostmac.append(mac)
298 hostip.append(ip)
299 success_dir = {}
300 def verify_proxyarp(*r):
301 ingress,hostmac,hostip = r[0],r[1],r[2]
302 def mac_recv_task():
303 def recv_cb(pkt):
304 log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
305 success_dir[current_thread().name] = True
306 sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
307 prn = recv_cb, iface = self.port_map[ingress])
308 t = threading.Thread(target = mac_recv_task)
309 t.start()
310 pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
311 log.info('sending arp request for dest ip %s on interface %s' %
312 (hostip,self.port_map[ingress]))
313 sendp(pkt, count = 10,iface = self.port_map[ingress])
314 t.join()
315 t = []
316 for i in range(10):
317 t.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
318 for i in range(10):
319 t[i].start()
320 for i in range(10):
321 t[i].join()
322 if len(success_dir) != 10:
323 self.success = False
324 assert_equal(self.success, True)
325
326 def test_proxyarp_disabling_enabling_app_initiating_concurrent_requests(self,hosts = 10):
327 '''Test sending arp requests to multiple host ips at once from different interfaces by disabling and re-enabling proxyarp app'''
328 ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
329 self.success = True
330 ingress = hosts+1
331 ports = range(ingress,ingress+10)
332 hostmac = []
333 hostip = []
334 for ip,mac in hosts_config:
335 hostmac.append(mac)
336 hostip.append(ip)
337 success_dir = {}
338 def verify_proxyarp(*r):
339 ingress,hostmac,hostip = r[0],r[1],r[2]
340 def mac_recv_task():
341 def recv_cb(pkt):
342 log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
343 success_dir[current_thread().name] = True
344 sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
345 prn = recv_cb, iface = self.port_map[ingress])
346 t = threading.Thread(target = mac_recv_task)
347 t.start()
348 pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
349 log.info('sending arp request for dest ip %s on interface %s' %
350 (hostip,self.port_map[ingress]))
351 sendp(pkt, count = 10,iface = self.port_map[ingress])
352 t.join()
353 t1 = []
354 #starting multi threading before proxyarp disable
355 for i in range(10):
356 t1.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
357 for i in range(10):
358 t1[i].start()
359 for i in range(10):
360 t1[i].join()
361 if len(success_dir) != 10:
362 self.success = False
363 assert_equal(self.success, True)
364 self.proxyarp_activate(deactivate = True)
365 #starting multi threading after proxyarp disable
366 t2 = []
367 self.success = False
368 for i in range(10):
369 t2.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
370 for i in range(10):
371 t2[i].start()
372 for i in range(10):
373 t2[i].join()
374 if len(success_dir) != 10:
375 self.success = True
376 assert_equal(self.success, False)
377 self.proxyarp_activate(deactivate = False)
378 #starting multi threading after proxyarp re-enable
379 self.success = True
380 t3 = []
381 for i in range(10):
382 t3.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
383 for i in range(10):
384 t3[i].start()
385 for i in range(10):
386 t3[i].join()
387 if len(success_dir) != 20:
388 self.success = False
389 assert_equal(self.success, True)
390
391 def test_proxyarp_with_existing_and_non_existing_hostIPs_initiating_concurrent_requests(self,hosts = 5):
392 ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
393 self.success = True
394 ingress = hosts+1
395 ports = range(ingress,ingress+10)
396 hostmac = []
397 hostip = []
398 for ip,mac in hosts_config:
399 hostmac.append(mac)
400 hostip.append(ip)
401 #adding 5 non-existing host IPs to hostip list
402 for i in range(1,6):
403 ip = hostip[-1].split('.')
404 ip[3] = str(int(ip[3])+int(i))
405 ip = '.'.join(ip)
406 hostip.append(ip)
407 hostmac.append(RandMAC()._fix())
408 success_dir = {}
409 replied_hosts = []
410 def verify_proxyarp(*r):
411 ingress,hostmac,hostip = r[0],r[1],r[2]
412 def mac_recv_task():
413 def recv_cb(pkt):
414 log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
415 success_dir[current_thread().name] = True
416 replied_hosts.append(hostip)
417 sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].psrc == hostip,
418 prn = recv_cb, iface = self.port_map[ingress])
419 t = threading.Thread(target = mac_recv_task)
420 t.start()
421 pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
422 log.info('sending arp request for dest ip %s on interface %s' %
423 (hostip,self.port_map[ingress]))
424 sendp(pkt, count = 10,iface = self.port_map[ingress])
425 t.join()
426 t = []
427 for i in range(10):
428 t.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
429 for i in range(10):
430 t[i].start()
431 for i in range(10):
432 t[i].join()
433 if len(success_dir) != 5 and len(replied_hosts) != 5:
434 self.success = False
435 assert_equal(self.success, True)
436 for i in range(5):
437 if hostip[i] not in replied_hosts:
438 self.success = False
439 assert_equal(self.success, True)