diff --git a/config/main.py b/config/main.py index 18eda659d9..d9edcd44d2 100644 --- a/config/main.py +++ b/config/main.py @@ -500,6 +500,21 @@ def set_interface_naming_mode(mode): f.close() click.echo("Please logout and log back in for changes take effect.") +def get_intf_ipv6_link_local_mode(ctx, interface_name, table_name): + config_db = ctx.obj["config_db"] + intf = config_db.get_table(table_name) + if interface_name in intf: + if 'ipv6_use_link_local_only' in intf[interface_name]: + return intf[interface_name]['ipv6_use_link_local_only'] + else: + return "disable" + else: + return "" + +def disable_intf_ipv6_mode(interface_name): + cmd = "sysctl -w net.ipv6.conf.{}.disable_ipv6=1".format(interface_name) + proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) + proc.communicate() def _is_neighbor_ipaddress(config_db, ipaddress): """Returns True if a neighbor has the IP address , False if not @@ -2663,7 +2678,7 @@ def remove(ctx, interface_name, ip_addr): ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]") config_db.set_entry(table_name, (interface_name, ip_addr), None) interface_dependent = interface_ipaddr_dependent_on_interface(config_db, interface_name) - if len(interface_dependent) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False: + if len(interface_dependent) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False and get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) != "enable": config_db.set_entry(table_name, interface_name, None) if multi_asic.is_multi_asic(): @@ -3008,6 +3023,11 @@ def bind(ctx, interface_name, vrf_name): for interface_del in interface_dependent: config_db.set_entry(table_name, interface_del, None) config_db.set_entry(table_name, interface_name, None) + + ipv6LinkLocalMode = get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) + if ipv6LinkLocalMode is not None and ipv6LinkLocalMode == "enable": + disable_intf_ipv6_mode(interface_name) + # When config_db del entry and then add entry with same key, the DEL will lost. if ctx.obj['namespace'] is DEFAULT_NAMESPACE: state_db = SonicV2Connector(use_unix_socket_path=True) @@ -3047,6 +3067,129 @@ def unbind(ctx, interface_name): config_db.set_entry(table_name, interface_del, None) config_db.set_entry(table_name, interface_name, None) + ipv6LinkLocalMode = get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) + if ipv6LinkLocalMode is not None and ipv6LinkLocalMode == "enable": + disable_intf_ipv6_mode(interface_name) + + +# +# 'ipv6' subgroup ('config interface ipv6 ...') +# + +@interface.group() +@click.pass_context +def ipv6(ctx): + """Enable or Disable IPv6 processing on interface""" + pass + +@ipv6.group('enable') +def enable(): + """Enable IPv6 processing on interface""" + pass + +@ipv6.group('disable') +def disable(): + """Disble IPv6 processing on interface""" + pass + +# +# 'config interface ipv6 enable use-link-local-only ' +# + +@enable.command('use-link-local-only') +@click.pass_context +@click.argument('interface_name', metavar='', required=True) +def enable_use_link_local_only(ctx, interface_name): + """Enable IPv6 link local address on interface""" + config_db = ctx.obj["config_db"] + + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(config_db, interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + + if interface_name.startswith("Ethernet"): + interface_type = "INTERFACE" + elif interface_name.startswith("PortChannel"): + interface_type = "PORTCHANNEL_INTERFACE" + elif interface_name.startswith("Vlan"): + interface_type = "VLAN_INTERFACE" + else: + ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan]") + + if (interface_type == "INTERFACE" ) or (interface_type == "PORTCHANNEL_INTERFACE"): + if interface_name_is_valid(config_db, interface_name) is False: + ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) + + if (interface_type == "VLAN_INTERFACE"): + vlan = config_db.get_entry('VLAN', interface_name) + if len(vlan) == 0: + ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) + + portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') + + if interface_is_in_portchannel(portchannel_member_table, interface_name): + ctx.fail("{} is configured as a member of portchannel. Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + vlan_member_table = config_db.get_table('VLAN_MEMBER') + + if interface_is_in_vlan(vlan_member_table, interface_name): + ctx.fail("{} is configured as a member of vlan. Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + interface_dict = config_db.get_table(interface_type) + set_ipv6_link_local_only_on_interface(config_db, interface_dict, interface_type, interface_name, "enable") + +# +# 'config interface ipv6 disable use-link-local-only ' +# + +@disable.command('use-link-local-only') +@click.pass_context +@click.argument('interface_name', metavar='', required=True) +def disable_use_link_local_only(ctx, interface_name): + """Disable IPv6 link local address on interface""" + config_db = ctx.obj["config_db"] + + if clicommon.get_interface_naming_mode() == "alias": + interface_name = interface_alias_to_name(config_db, interface_name) + if interface_name is None: + ctx.fail("'interface_name' is None!") + + interface_type = "" + if interface_name.startswith("Ethernet"): + interface_type = "INTERFACE" + elif interface_name.startswith("PortChannel"): + interface_type = "PORTCHANNEL_INTERFACE" + elif interface_name.startswith("Vlan"): + interface_type = "VLAN_INTERFACE" + else: + ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan]") + + if (interface_type == "INTERFACE" ) or (interface_type == "PORTCHANNEL_INTERFACE"): + if interface_name_is_valid(config_db, interface_name) is False: + ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) + + if (interface_type == "VLAN_INTERFACE"): + vlan = config_db.get_entry('VLAN', interface_name) + if len(vlan) == 0: + ctx.fail("Interface name %s is invalid. Please enter a valid interface name!!" %(interface_name)) + + portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') + + if interface_is_in_portchannel(portchannel_member_table, interface_name): + ctx.fail("{} is configured as a member of portchannel. Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + vlan_member_table = config_db.get_table('VLAN_MEMBER') + if interface_is_in_vlan(vlan_member_table, interface_name): + ctx.fail("{} is configured as a member of vlan. Cannot configure the IPv6 link local mode!" + .format(interface_name)) + + interface_dict = config_db.get_table(interface_type) + set_ipv6_link_local_only_on_interface(config_db, interface_dict, interface_type, interface_name, "disable") + # # 'vrf' group ('config vrf ...') @@ -4414,6 +4557,108 @@ def delete(ctx): config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) +# +# set ipv6 link local mode on a given interface +# +def set_ipv6_link_local_only_on_interface(config_db, interface_dict, interface_type, interface_name, mode): + + curr_mode = config_db.get_entry(interface_type, interface_name).get('ipv6_use_link_local_only') + if curr_mode is not None: + if curr_mode == mode: + return + else: + if mode == "disable": + return + + if mode == "enable": + config_db.mod_entry(interface_type, interface_name, {"ipv6_use_link_local_only": mode}) + return + + # If we are disabling the ipv6 link local on an interface, and if no other interface + # attributes/ip addresses are configured on the interface, delete the interface from the interface table + exists = False + for key in interface_dict.keys(): + if not isinstance(key, tuple): + if interface_name == key: + #Interface bound to non-default-vrf do not delete the entry + if 'vrf_name' in interface_dict[key]: + if len(interface_dict[key]['vrf_name']) > 0: + exists = True + break + continue + if interface_name in key: + exists = True + break + + if exists: + config_db.mod_entry(interface_type, interface_name, {"ipv6_use_link_local_only": mode}) + else: + config_db.set_entry(interface_type, interface_name, None) + +# +# 'ipv6' group ('config ipv6 ...') +# + +@config.group() +@click.pass_context +def ipv6(ctx): + """IPv6 configuration""" + +# +# 'enable' command ('config ipv6 enable ...') +# +@ipv6.command() +@click.pass_context +def enable(ctx): + """Enable IPv6 on all interfaces """ + config_db = ConfigDBConnector() + config_db.connect() + vlan_member_table = config_db.get_table('VLAN_MEMBER') + portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') + + mode = "enable" + + # Enable ipv6 link local on VLANs + vlan_dict = config_db.get_table('VLAN') + for key in vlan_dict.keys(): + set_ipv6_link_local_only_on_interface(config_db, vlan_dict, 'VLAN_INTERFACE', key, mode) + + # Enable ipv6 link local on PortChannels + portchannel_dict = config_db.get_table('PORTCHANNEL') + for key in portchannel_dict.keys(): + if interface_is_in_vlan(vlan_member_table, key): + continue + set_ipv6_link_local_only_on_interface(config_db, portchannel_dict, 'PORTCHANNEL_INTERFACE', key, mode) + + port_dict = config_db.get_table('PORT') + for key in port_dict.keys(): + if interface_is_in_portchannel(portchannel_member_table, key) or interface_is_in_vlan(vlan_member_table, key): + continue + set_ipv6_link_local_only_on_interface(config_db, port_dict, 'INTERFACE', key, mode) + +# +# 'disable' command ('config ipv6 disable ...') +# +@ipv6.command() +@click.pass_context +def disable(ctx): + """Disable IPv6 on all interfaces """ + config_db = ConfigDBConnector() + config_db.connect() + + mode = "disable" + + tables = ['INTERFACE', 'VLAN_INTERFACE', 'PORTCHANNEL_INTERFACE'] + + for table_type in tables: + table_dict = config_db.get_table(table_type) + if table_dict: + for key in table_dict.keys(): + if isinstance(key, unicode) is False: + continue + set_ipv6_link_local_only_on_interface(config_db, table_dict, table_type, key, mode) + + # Load plugins and register them helper = util_base.UtilHelper() for plugin in helper.load_plugins(plugins):