blob: 836cb3b6a833d193de528f64e6cf199512c1c462 [file] [log] [blame]
# Copyright 2016-present Ciena Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import unittest
from nose.tools import *
from scapy.all import *
from CordTestUtils import get_mac
from OnosCtrl import OnosCtrl
from OltConfig import OltConfig
from OnosFlowCtrl import OnosFlowCtrl
from onosclidriver import OnosCliDriver
from CordContainer import Container, Onos, Quagga
from CordTestServer import cord_test_onos_restart, cord_test_quagga_restart
from portmaps import g_subscriber_port_map
import threading
from threading import current_thread
import time
import os
import json
log.setLevel('INFO')
class proxyarp_exchange(unittest.TestCase):
apps = ('org.onosproject.vrouter','org.onosproject.proxyarp')
device_id = 'of:' + get_mac()
device_dict = { "devices" : {
"{}".format(device_id) : {
"basic" : {
"driver" : "softrouter"
}
}
},
}
test_path = os.path.dirname(os.path.realpath(__file__))
onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
GATEWAY = '192.168.10.50'
INGRESS_PORT = 1
EGRESS_PORT = 2
MAX_PORTS = 100
hosts_list = [ ('192.168.10.1', '00:00:00:00:00:01'), ('192.168.11.1', '00:00:00:00:02:01'), ]
@classmethod
def setUpClass(cls):
cls.olt = OltConfig()
cls.port_map, _ = cls.olt.olt_port_map()
if not cls.port_map:
cls.port_map = g_subscriber_port_map
time.sleep(3)
cls.load_device_id()
@classmethod
def tearDownClass(cls):
'''Deactivate the vrouter apps'''
#cls.vrouter_host_unload()
@classmethod
def load_device_id(cls):
did = OnosCtrl.get_device_id()
cls.device_id = did
cls.device_dict = { "devices" : {
"{}".format(did) : {
"basic" : {
"driver" : "softrouter"
}
}
},
}
def cliEnter(self):
retries = 0
while retries < 3:
self.cli = OnosCliDriver(connect = True)
if self.cli.handle:
break
else:
retries += 1
time.sleep(2)
def cliExit(self):
self.cli.disconnect()
@classmethod
def proxyarp_host_unload(cls):
index = 1
for host,_ in cls.hosts_list:
iface = cls.port_map[index]
index += 1
config_cmds = ('ifconfig {} 0'.format(iface), )
for cmd in config_cmds:
log.info('host unload command %s' % cmd)
os.system(cmd)
@classmethod
def interface_config_load(cls, interface_cfg = None):
if type(interface_cfg) is tuple:
res = []
for v in interface_cfg:
if type(v) == list:
pass
else:
res += v.items()
config = dict(res)
else:
config = interface_cfg
cfg = json.dumps(config)
with open('{}/network-cfg.json'.format(cls.onos_config_path), 'w') as f:
f.write(cfg)
return cord_test_onos_restart()
@classmethod
def host_config_load(cls, host_config = None):
for host in host_config:
status, code = OnosCtrl.host_config(host)
if status is False:
log.info('JSON request returned status %d' %code)
assert_equal(status, True)
@classmethod
def generate_interface_config(cls, hosts = 1):
num = 0
start_host = ( 192 << 24) | ( 168 << 16) | (10 << 8) | 0
end_host = ( 200 << 24 ) | (168 << 16) | (10 << 8) | 0
ports_dict = { 'ports' : {} }
interface_list = []
hosts_list = []
for n in xrange(start_host, end_host, 256):
port_map = ports_dict['ports']
port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
device_port_key = '{0}/{1}'.format(cls.device_id, port)
try:
interfaces = port_map[device_port_key]['interfaces']
except:
port_map[device_port_key] = { 'interfaces' : [] }
interfaces = port_map[device_port_key]['interfaces']
ip = n + 1
host_ip = n + 2
ips = '%d.%d.%d.%d/24'%( (ip >> 24) & 0xff, ( (ip >> 16) & 0xff ), ( (ip >> 8 ) & 0xff ), ip & 0xff)
host = '%d.%d.%d.%d' % ( (host_ip >> 24) & 0xff, ( ( host_ip >> 16) & 0xff ), ( (host_ip >> 8 ) & 0xff ), host_ip & 0xff )
mac = RandMAC()._fix()
hosts_list.append((host, mac))
if num < cls.MAX_PORTS - 1:
interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : mac }
interfaces.append(interface_dict)
interface_list.append(interface_dict['name'])
else:
interfaces[0]['ips'].append(ips)
num += 1
if num == hosts:
break
cls.hosts_list = hosts_list
return (cls.device_dict, ports_dict, hosts_list)
@classmethod
def generate_host_config(cls):
num = 0
hosts_dict = {}
for host, mac in cls.hosts_list:
port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
hosts_dict[host] = {'mac':mac, 'vlan':'none', 'ipAddresses':[host], 'location':{ 'elementId' : '{}'.format(cls.device_id), 'port': port}}
num += 1
return hosts_dict.values()
@classmethod
def proxyarp_activate(cls, deactivate = False):
app = 'org.onosproject.proxyarp'
onos_ctrl = OnosCtrl(app)
if deactivate is True:
onos_ctrl.deactivate()
else:
onos_ctrl.activate()
time.sleep(3)
@classmethod
def proxyarp_config(cls, hosts = 1):
proxyarp_configs = cls.generate_interface_config(hosts = hosts)
cls.interface_config_load(interface_cfg = proxyarp_configs)
hostcfg = cls.generate_host_config()
cls.host_config_load(host_config = hostcfg)
return proxyarp_configs
def proxyarp_arpreply_verify(self, ingress, hostip, hostmac, PositiveTest=True):
log.info('verifying arp reply for host ip %s host mac %s on interface %s'%(hostip ,hostmac ,self.port_map[ingress]))
self.success = False
def recv_task():
def recv_cb(pkt):
log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
self.success = True if PositiveTest == True else False
sniff(count=1, timeout=2, lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
prn = recv_cb, iface = self.port_map[ingress])
t = threading.Thread(target = recv_task)
t.start()
pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst=hostip))
log.info('sending arp request for dest ip %s on interface %s' %
(hostip, self.port_map[ingress]))
sendp( pkt, count = 10, iface = self.port_map[ingress])
t.join()
if PositiveTest:
assert_equal(self.success, True)
else:
assert_equal(self.success, False)
def __proxyarp_hosts_verify(self, hosts = 1,PositiveTest = True):
_,_,hosts_config = self.proxyarp_config(hosts = hosts)
log.info('\nhosts_config %s and its type %s'%(hosts_config,type(hosts_config)))
self.cliEnter()
connected_hosts = json.loads(self.cli.hosts(jsonFormat = True))
log.info('Discovered hosts: %s' %connected_hosts)
#We read from cli if we expect less number of routes to avoid cli timeouts
if hosts <= 10000:
assert_equal(len(connected_hosts), hosts)
ingress = hosts+1
for hostip, hostmac in hosts_config:
self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = PositiveTest)
time.sleep(1)
self.cliExit()
return True
def test_proxyarp_with_1_host(self, hosts=1):
res = self.__proxyarp_hosts_verify(hosts = hosts)
assert_equal(res, True)
#cls.proxyarp_host_unload()
def test_proxyarp_with_10_hosts(self, hosts=10):
res = self.__proxyarp_hosts_verify(hosts = hosts)
assert_equal(res, True)
def test_proxyarp_with_50_hosts(self, hosts=50):
res = self.__proxyarp_hosts_verify(hosts = hosts)
assert_equal(res, True)
def test_proxyarp_app_with_disabling_and_re_enabling(self,hosts = 3):
ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
ingress = hosts+1
for hostip, hostmac in hosts_config:
self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
time.sleep(1)
log.info('Deactivating proxyarp app and expecting not to get arp reply from ONOS')
self.proxyarp_activate(deactivate = True)
for hostip, hostmac in hosts_config:
self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = False)
time.sleep(1)
log.info('activating proxyarp app and expecting to get arp reply from ONOS')
self.proxyarp_activate(deactivate = False)
for hostip, hostmac in hosts_config:
self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
time.sleep(1)
def test_proxyarp_nonexisting_host(self,hosts = 1):
_,_,hosts_config = self.proxyarp_config(hosts = hosts)
ingress = hosts + 2
for host, mac in hosts_config:
self.proxyarp_arpreply_verify(ingress,host,mac,PositiveTest = True)
new_host = hosts_config[-1][0].split('.')
new_host[2] = str(int(new_host[2])+1)
new_host = '.'.join(new_host)
new_mac = RandMAC()._fix()
log.info('verifying arp reply for host ip %s on interface %s'%(new_host,self.port_map[ingress]))
res=srp1(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst=new_host),timeout=2,iface=self.port_map[ingress])
assert_equal(res, None)
log.info('arp reply not seen for host ip %s on interface %s as expected'%(new_host,self.port_map[ingress]))
hosts = hosts + 1
_,_,hosts_config = self.proxyarp_config(hosts = hosts)
for host in hosts_config:
if host[0] == new_host:
new_mac = host[1]
self.proxyarp_arpreply_verify(ingress,new_host,new_mac,PositiveTest = True)
def test_proxyarp_removing_host(self,hosts = 3):
ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
ingress = hosts+1
for hostip, hostmac in hosts_config:
self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = True)
time.sleep(1)
host_mac = hosts_config[0][1]
log.info('removing host entry %s' % host_mac)
self.cliEnter()
hostentries = json.loads(self.cli.hosts(jsonFormat = True))
for host in hostentries:
res = host_mac.upper() in host.values()
if res:
break
assert_equal(res, True)
hostid = host_mac+'/'+'None'
delete_host = self.cli.host_remove(hostid)
hostentries = json.loads(self.cli.hosts(jsonFormat = True))
for host in hostentries:
res = host_mac.upper() in host.values()
if res:
break
assert_equal(res, False)
self.proxyarp_arpreply_verify(ingress,hosts_config[0][0],host_mac,PositiveTest = False)
time.sleep(1)
self.cliExit()
def test_proxyarp_concurrent_requests_with_multiple_host_and_different_interfaces(self,hosts = 10):
ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
self.success = True
ingress = hosts+1
ports = range(ingress,ingress+10)
hostmac = []
hostip = []
for ip,mac in hosts_config:
hostmac.append(mac)
hostip.append(ip)
success_dir = {}
def verify_proxyarp(*r):
ingress,hostmac,hostip = r[0],r[1],r[2]
def mac_recv_task():
def recv_cb(pkt):
log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
success_dir[current_thread().name] = True
sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
prn = recv_cb, iface = self.port_map[ingress])
t = threading.Thread(target = mac_recv_task)
t.start()
pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
log.info('sending arp request for dest ip %s on interface %s' %
(hostip,self.port_map[ingress]))
sendp(pkt, count = 10,iface = self.port_map[ingress])
t.join()
t = []
for i in range(10):
t.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
for i in range(10):
t[i].start()
for i in range(10):
t[i].join()
if len(success_dir) != 10:
self.success = False
assert_equal(self.success, True)
def test_proxyarp_disabling_enabling_app_initiating_concurrent_requests(self,hosts = 10):
'''Test sending arp requests to multiple host ips at once from different interfaces by disabling and re-enabling proxyarp app'''
ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
self.success = True
ingress = hosts+1
ports = range(ingress,ingress+10)
hostmac = []
hostip = []
for ip,mac in hosts_config:
hostmac.append(mac)
hostip.append(ip)
success_dir = {}
def verify_proxyarp(*r):
ingress,hostmac,hostip = r[0],r[1],r[2]
def mac_recv_task():
def recv_cb(pkt):
log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
success_dir[current_thread().name] = True
sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
prn = recv_cb, iface = self.port_map[ingress])
t = threading.Thread(target = mac_recv_task)
t.start()
pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
log.info('sending arp request for dest ip %s on interface %s' %
(hostip,self.port_map[ingress]))
sendp(pkt, count = 10,iface = self.port_map[ingress])
t.join()
t1 = []
#starting multi threading before proxyarp disable
for i in range(10):
t1.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
for i in range(10):
t1[i].start()
for i in range(10):
t1[i].join()
if len(success_dir) != 10:
self.success = False
assert_equal(self.success, True)
self.proxyarp_activate(deactivate = True)
#starting multi threading after proxyarp disable
t2 = []
self.success = False
for i in range(10):
t2.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
for i in range(10):
t2[i].start()
for i in range(10):
t2[i].join()
if len(success_dir) != 10:
self.success = True
assert_equal(self.success, False)
self.proxyarp_activate(deactivate = False)
#starting multi threading after proxyarp re-enable
self.success = True
t3 = []
for i in range(10):
t3.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
for i in range(10):
t3[i].start()
for i in range(10):
t3[i].join()
if len(success_dir) != 20:
self.success = False
assert_equal(self.success, True)
def test_proxyarp_with_existing_and_non_existing_hostIPs_initiating_concurrent_requests(self,hosts = 5):
ports_map, egress_map,hosts_config = self.proxyarp_config(hosts = hosts)
self.success = True
ingress = hosts+1
ports = range(ingress,ingress+10)
hostmac = []
hostip = []
for ip,mac in hosts_config:
hostmac.append(mac)
hostip.append(ip)
#adding 5 non-existing host IPs to hostip list
for i in range(1,6):
ip = hostip[-1].split('.')
ip[3] = str(int(ip[3])+int(i))
ip = '.'.join(ip)
hostip.append(ip)
hostmac.append(RandMAC()._fix())
success_dir = {}
replied_hosts = []
def verify_proxyarp(*r):
ingress,hostmac,hostip = r[0],r[1],r[2]
def mac_recv_task():
def recv_cb(pkt):
log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
success_dir[current_thread().name] = True
replied_hosts.append(hostip)
sniff(count=1, timeout=5,lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].psrc == hostip,
prn = recv_cb, iface = self.port_map[ingress])
t = threading.Thread(target = mac_recv_task)
t.start()
pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst= hostip))
log.info('sending arp request for dest ip %s on interface %s' %
(hostip,self.port_map[ingress]))
sendp(pkt, count = 10,iface = self.port_map[ingress])
t.join()
t = []
for i in range(10):
t.append(threading.Thread(target = verify_proxyarp, args = [ports[i],hostmac[i],hostip[i]]))
for i in range(10):
t[i].start()
for i in range(10):
t[i].join()
if len(success_dir) != 5 and len(replied_hosts) != 5:
self.success = False
assert_equal(self.success, True)
for i in range(5):
if hostip[i] not in replied_hosts:
self.success = False
assert_equal(self.success, True)