Skip to content

Commit

Permalink
Add ip_prefix len based on proxy_arp status (#1046)
Browse files Browse the repository at this point in the history
* Add ip_prefix len based on proxy_arp status and relevant test
  • Loading branch information
sumukhatv authored Aug 24, 2020
1 parent b4a53be commit 3e52604
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 56 deletions.
100 changes: 50 additions & 50 deletions scripts/neighbor_advertiser
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import argparse
import json
import os
import requests
import subprocess
import sys
import time
import traceback
import warnings

from sonic_py_common import logger
from swsssdk import ConfigDBConnector
from swsssdk import ConfigDBConnector, SonicV2Connector
from netaddr import IPAddress, IPNetwork


Expand Down Expand Up @@ -76,6 +75,15 @@ def connect_config_db():
config_db = ConfigDBConnector()
config_db.connect()

#
# Global variable of app_db
#
appl_db = None

def connect_app_db():
global appl_db
appl_db = SonicV2Connector(host="127.0.0.1")
appl_db.connect(appl_db.APPL_DB)

#
# Check if a DIP returned from ferret is in any of this switch's VLANs
Expand Down Expand Up @@ -125,13 +133,9 @@ def get_switch_hwsku():


def extract_ip_ver_addr(ip_prefix):
addr = ip_prefix.split('/')[0]

if ':' in addr:
ver = 6
else:
ver = 4

ip = IPNetwork(ip_prefix)
addr = str(ip.ip)
ver = ip.ip.version
return (ver, addr)


Expand Down Expand Up @@ -201,65 +205,40 @@ def get_vlan_interface_vxlan_id(vlan_intf_name):
return vlan_intf_name[4:]


def get_vlan_addr(vlan_intf_name, ip_ver):
def get_vlan_addr_prefix(vlan_intf_name, ip_ver):
vlan_intfs = config_db.get_table('VLAN_INTERFACE')
vlan_addr = []
vlan_prefix = []

for intf in vlan_intfs.keys():
if not is_ip_prefix_in_key(intf):
continue
if vlan_intf_name in intf:
intf_ip_prefix = intf[1]
(intf_ip_ver, intf_ip_addr) = extract_ip_ver_addr(intf_ip_prefix)
intf_ip = IPNetwork(intf[1])
intf_ip_addr = str(intf_ip.ip)
intf_ip_ver = intf_ip.ip.version
intf_prefixlen = intf_ip.prefixlen
if intf_ip_ver == ip_ver:
vlan_addr.append(intf_ip_addr)
vlan_prefix.append(intf_prefixlen)

return vlan_addr
return vlan_addr, vlan_prefix


def get_vlan_addresses(vlan_interface):
vlan_id = get_vlan_interface_vlan_id(vlan_interface)
vxlan_id = get_vlan_interface_vxlan_id(vlan_interface)

mac_addr = None
ipv4_addr = []
ipv6_addr = []

'''
Sample output for "ip addr show Vlan101"
218: Vlan101@Bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 98:5d:82:01:d8:48 brd ff:ff:ff:ff:ff:ff
inet 10.171.22.113/29 scope global Vlan101
valid_lft forever preferred_lft forever
inet6 2603:10b0:10f:843c::1/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::9a5d:82ff:fe01:d848/64 scope link
valid_lft forever preferred_lft forever
'''
try:
out = subprocess.check_output(['ip', 'addr', 'show', vlan_interface])
for line in out.splitlines():
keys=line.strip().split(' ')
if keys[0] == 'inet':
ipv4_addr.append(keys[1].split('/')[0])
elif keys[0] == 'inet6':
ipv6_addr.append(keys[1].split('/')[0])
elif keys[0] == 'link/ether':
mac_addr = keys[1]
except Exception:
log.log_error('failed to get %s addresses from o.s.' % vlan_interface)
ipv4_addr, ipv4_prefix = get_vlan_addr_prefix(vlan_interface, 4)
ipv6_addr, ipv6_prefix = get_vlan_addr_prefix(vlan_interface, 6)

metadata = config_db.get_table('DEVICE_METADATA')
mac_addr = metadata['localhost']['mac']
if not mac_addr:
mac_addr = get_vlan_interface_mac_address(vlan_interface)

if not ipv4_addr:
ipv4_addr = get_vlan_addr(vlan_interface, 4)

if not ipv6_addr:
ipv6_addr = get_vlan_addr(vlan_interface, 6)

return vlan_id, vxlan_id, ipv4_addr, ipv6_addr, mac_addr
return vlan_id, vxlan_id, ipv4_addr, ipv4_prefix, ipv6_addr, ipv6_prefix, mac_addr

#
# Set up neighbor advertiser slice on Ferret
Expand All @@ -281,28 +260,46 @@ def construct_neighbor_advertiser_slice():

all_vlan_interfaces = get_vlan_interfaces()

vlan_intf_table = config_db.get_table('VLAN_INTERFACE')

vxlanPort = appl_db.get(appl_db.APPL_DB, 'SWITCH_TABLE:switch', 'vxlan_port')

for vlan_interface in all_vlan_interfaces:
vlan_id, vxlan_id, ipv4_addr, ipv6_addr, mac_addr = get_vlan_addresses(vlan_interface)
vlan_id, vxlan_id, ipv4_addr, ipv4_prefix, ipv6_addr, ipv6_prefix, mac_addr = get_vlan_addresses(vlan_interface)

if not mac_addr:
log.log_warning('Cannot find mac addr of vlan interface {}'.format(vlan_interface))
continue

ipv4_mappings = []
ipv6_mappings = []
ctr = 0
for addr in ipv4_addr:
if 'proxy_arp' in vlan_intf_table[vlan_interface] and vlan_intf_table[vlan_interface]['proxy_arp'] == 'enabled':
ipPrefixLen = str(ipv4_prefix[ctr])
else:
ipPrefixLen = '32'
mapping = {
'ipAddr': addr,
'ipPrefixLen': ipPrefixLen,
'macAddr': mac_addr
}
ipv4_mappings.append(mapping)
ctr += 1

ipv6_mappings = []
ctr = 0
for addr in ipv6_addr:
if 'proxy_arp' in vlan_intf_table[vlan_interface] and vlan_intf_table[vlan_interface]['proxy_arp'] == 'enabled':
ipPrefixLen = str(ipv6_prefix[ctr])
else:
ipPrefixLen = '128'
mapping = {
'ipAddr': addr,
'ipPrefixLen': ipPrefixLen,
'macAddr': mac_addr
}
ipv6_mappings.append(mapping)
ctr += 1

vlan_interface_obj = {
'vlanId': vlan_id,
Expand All @@ -311,6 +308,9 @@ def construct_neighbor_advertiser_slice():
'ipv6AddrMappings': ipv6_mappings
}

if vxlanPort:
vlan_interface_obj['vxlanPort'] = vxlanPort

vlan_interfaces_obj.append(vlan_interface_obj)

slice_obj = {
Expand Down Expand Up @@ -539,7 +539,7 @@ def main():
sys.exit(1)

connect_config_db()

connect_app_db()
if operation_mode == 'set':
set_success = False

Expand Down
3 changes: 3 additions & 0 deletions tests/mock_tables/appl_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,8 @@
"admin_status": "up",
"mtu": "9100",
"oper_status": "up"
},
"SWITCH_TABLE:switch": {
"vxlan_port": "13550"
}
}
30 changes: 30 additions & 0 deletions tests/mock_tables/config_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,16 @@
"dhcp_servers@": "192.0.0.1,192.0.0.2,192.0.0.3,192.0.0.4",
"vlanid": "1000"
},
"VLAN|Vlan2000": {
"dhcp_servers@": "192.0.0.1,192.0.0.2,192.0.0.3,192.0.0.4",
"vlanid": "2000"
},
"VLAN_INTERFACE|Vlan1000": {
"NULL": "NULL"
},
"VLAN_INTERFACE|Vlan2000": {
"proxy_arp": "enabled"
},
"VLAN_INTERFACE|Vlan1000|192.168.0.1/21": {
"NULL": "NULL"
},
Expand All @@ -445,6 +452,18 @@
"VLAN_MEMBER|Vlan1000|Ethernet16": {
"tagging_mode": "untagged"
},
"VLAN_INTERFACE|Vlan2000|192.168.0.10/21": {
"NULL": "NULL"
},
"VLAN_INTERFACE|Vlan2000|fc02:1011::1/64": {
"NULL": "NULL"
},
"VLAN_MEMBER|Vlan2000|Ethernet24": {
"tagging_mode": "untagged"
},
"VLAN_MEMBER|Vlan2000|Ethernet28": {
"tagging_mode": "untagged"
},
"PORTCHANNEL|PortChannel1001": {
"admin_status": "up",
"members@": "Ethernet32",
Expand Down Expand Up @@ -579,6 +598,17 @@
"auto_restart": "enabled",
"high_mem_alert": "disabled"
},
"DEVICE_METADATA|localhost": {
"default_bgp_status": "down",
"default_pfcwd_status": "enable",
"deployment_id": "1",
"docker_routing_config_mode": "separated",
"hostname": "sonic-switch",
"hwsku": "Mellanox-SN3800-D112C8",
"mac": "1d:34:db:16:a6:00",
"platform": "x86_64-mlnx_msn3800-r0",
"type": "ToRRouter"
},
"DEVICE_NEIGHBOR|Ethernet4": {
"name": "Servers0",
"port": "eth0"
Expand Down
49 changes: 49 additions & 0 deletions tests/neighbor_advertiser_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import sys
import os
import pytest
from swsssdk import ConfigDBConnector

test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")
sys.path.insert(0, modules_path)

from imp import load_source
load_source('neighbor_advertiser', scripts_path+'/neighbor_advertiser')
import neighbor_advertiser

class TestNeighborAdvertiser(object):
@pytest.fixture
def set_up(self):
neighbor_advertiser.connect_config_db()
neighbor_advertiser.connect_app_db()

def test_neighbor_advertiser_slice(self, set_up):
output = neighbor_advertiser.construct_neighbor_advertiser_slice()
expected_output = dict({
'respondingSchemes': {'durationInSec': 300},
'switchInfo': {'ipv6Addr': '', 'hwSku': 'Mellanox-SN3800-D112C8', 'ipv4Addr': '', 'name': 'sonic-switch'},
'vlanInterfaces': [{
'ipv4AddrMappings': [
{'macAddr': '1d:34:db:16:a6:00', 'ipAddr': '192.168.0.1', 'ipPrefixLen': '32'}
],
'ipv6AddrMappings': [
{'macAddr': '1d:34:db:16:a6:00', 'ipAddr': 'fc02:1000::1', 'ipPrefixLen': '128'}
],
'vxlanId': u'1000',
'vlanId': u'1000',
'vxlanPort': '13550'
},
{
'ipv4AddrMappings': [
{'macAddr': '1d:34:db:16:a6:00', 'ipAddr': '192.168.0.10', 'ipPrefixLen': '21'}
],
'ipv6AddrMappings': [
{'macAddr': '1d:34:db:16:a6:00', 'ipAddr': 'fc02:1011::1', 'ipPrefixLen': '64'}
],
'vxlanId': u'2000',
'vlanId': u'2000',
'vxlanPort': '13550'
}]
})
assert output == expected_output
Loading

0 comments on commit 3e52604

Please sign in to comment.