diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/interfaces-pppoe.py | 206 | 
1 files changed, 46 insertions, 160 deletions
| diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 611206d84..503a263f8 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2019-2020 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -15,179 +15,65 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  import os +import jmespath  from sys import exit  from copy import deepcopy  from netifaces import interfaces  from vyos.config import Config -from vyos.configdict import dhcpv6_pd_default_data -from vyos.ifconfig import Interface +from vyos.configdict import dict_merge +from vyos.configverify import verify_source_interface +from vyos.configverify import verify_bridge_vrf  from vyos.template import render -from vyos.util import chown, chmod_755, call +from vyos.util import call +from vyos.xml import defaults  from vyos import ConfigError -  from vyos import airbag  airbag.enable() -default_config_data = { -    **dhcpv6_pd_default_data, -    'access_concentrator': '', -    'auth_username': '', -    'auth_password': '', -    'on_demand': False, -    'default_route': 'auto', -    'deleted': False, -    'description': '\0', -    'disable': False, -    'intf': '', -    'idle_timeout': '', -    'ipv6_autoconf': False, -    'ipv6_enable': False, -    'local_address': '', -    'mtu': '1492', -    'name_server': True, -    'remote_address': '', -    'service_name': '', -    'source_interface': '', -    'vrf': '' -} -  def get_config(): -    pppoe = deepcopy(default_config_data) +    """ Retrive CLI config as dictionary. Dictionary can never be empty, +    as at least the interface name will be added or a deleted flag """      conf = Config() -    base_path = ['interfaces', 'pppoe']      # determine tagNode instance      if 'VYOS_TAGNODE_VALUE' not in os.environ:          raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') -    pppoe['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - -    # Check if interface has been removed -    if not conf.exists(base_path + [pppoe['intf']]): -        pppoe['deleted'] = True -        return pppoe - -    # set new configuration level -    conf.set_level(base_path + [pppoe['intf']]) - -    # Access concentrator name (only connect to this concentrator) -    if conf.exists(['access-concentrator']): -        pppoe['access_concentrator'] = conf.return_values(['access-concentrator']) - -    # Authentication name supplied to PPPoE server -    if conf.exists(['authentication', 'user']): -        pppoe['auth_username'] = conf.return_value(['authentication', 'user']) - -    # Password for authenticating local machine to PPPoE server -    if conf.exists(['authentication', 'password']): -        pppoe['auth_password'] = conf.return_value(['authentication', 'password']) - -    # Access concentrator name (only connect to this concentrator) -    if conf.exists(['connect-on-demand']): -        pppoe['on_demand'] = True - -    # Enable/Disable default route to peer when link comes up -    if conf.exists(['default-route']): -        pppoe['default_route'] = conf.return_value(['default-route']) - -    # Retrieve interface description -    if conf.exists(['description']): -        pppoe['description'] = conf.return_value(['description']) - -    # Disable this interface -    if conf.exists(['disable']): -        pppoe['disable'] = True - -    # Delay before disconnecting idle session (in seconds) -    if conf.exists(['idle-timeout']): -        pppoe['idle_timeout'] = conf.return_value(['idle-timeout']) - -    # Enable Stateless Address Autoconfiguration (SLAAC) -    if conf.exists(['ipv6', 'address', 'autoconf']): -        pppoe['ipv6_autoconf'] = True +    # retrieve interface default values +    base = ['interfaces', 'pppoe'] +    default_values = defaults(base) +    # PPPoE is "special" the default MTU is 1492 - update accordingly +    default_values['mtu'] = '1492' -    # Activate IPv6 support on this connection -    if conf.exists(['ipv6', 'enable']): -        pppoe['ipv6_enable'] = True +    ifname = os.environ['VYOS_TAGNODE_VALUE'] +    base = base + [ifname] -    # IPv4 address of local end of PPPoE link -    if conf.exists(['local-address']): -        pppoe['local_address'] = conf.return_value(['local-address']) - -    # Physical Interface used for this PPPoE session -    if conf.exists(['source-interface']): -        pppoe['source_interface'] = conf.return_value(['source-interface']) - -    # Maximum Transmission Unit (MTU) -    if conf.exists(['mtu']): -        pppoe['mtu'] = conf.return_value(['mtu']) - -    # Do not use DNS servers provided by the peer -    if conf.exists(['no-peer-dns']): -        pppoe['name_server'] = False - -    # IPv4 address for remote end of PPPoE session -    if conf.exists(['remote-address']): -        pppoe['remote_address'] = conf.return_value(['remote-address']) - -    # Service name, only connect to access concentrators advertising this -    if conf.exists(['service-name']): -        pppoe['service_name'] = conf.return_value(['service-name']) - -    # retrieve VRF instance -    if conf.exists('vrf'): -        pppoe['vrf'] = conf.return_value(['vrf']) - -    if conf.exists(['dhcpv6-options', 'prefix-delegation']): -        dhcpv6_pd_path = base_path + [pppoe['intf'], -                                      'dhcpv6-options', 'prefix-delegation'] -        conf.set_level(dhcpv6_pd_path) - -        # Retrieve DHCPv6-PD prefix helper length as some ISPs only hand out a -        # /64 by default (https://phabricator.vyos.net/T2506) -        if conf.exists(['length']): -            pppoe['dhcpv6_pd_length'] = conf.return_value(['length']) - -        for interface in conf.list_nodes(['interface']): -            conf.set_level(dhcpv6_pd_path + ['interface', interface]) -            pd = { -                'ifname': interface, -                'sla_id': '', -                'sla_len': '', -                'if_id': '' -            } - -            if conf.exists(['sla-id']): -                pd['sla_id'] = conf.return_value(['sla-id']) - -            if conf.exists(['sla-len']): -                pd['sla_len'] = conf.return_value(['sla-len']) +    pppoe = conf.get_config_dict(base, key_mangling=('-', '_')) +    # Check if interface has been removed +    if pppoe == {}: +        pppoe.update({'deleted' : ''}) -            if conf.exists(['address']): -                pd['if_id'] = conf.return_value(['address']) +    # We have gathered the dict representation of the CLI, but there are +    # default options which we need to update into the dictionary +    # retrived. +    pppoe = dict_merge(default_values, pppoe) -            pppoe['dhcpv6_pd_interfaces'].append(pd) +    # Add interface instance name into dictionary +    pppoe.update({'ifname': ifname})      return pppoe  def verify(pppoe): -    if pppoe['deleted']: +    if 'deleted' in pppoe.keys():          # bail out early          return None -    if not pppoe['source_interface']: -        raise ConfigError('PPPoE source interface missing') - -    if not pppoe['source_interface'] in interfaces(): -        raise ConfigError(f"PPPoE source interface {pppoe['source_interface']} does not exist") - -    vrf_name = pppoe['vrf'] -    if vrf_name and vrf_name not in interfaces(): -        raise ConfigError(f'VRF {vrf_name} does not exist') +    verify_source_interface(pppoe) +    verify_bridge_vrf(pppoe) -    if pppoe['on_demand'] and pppoe['vrf']: +    if {'connect_on_demand', 'vrf'} <= set(pppoe):          raise ConfigError('On-demand dialing and VRF can not be used at the same time')      return None @@ -195,22 +81,22 @@ def verify(pppoe):  def generate(pppoe):      # set up configuration file path variables where our templates will be      # rendered into -    intf = pppoe['intf'] -    config_pppoe = f'/etc/ppp/peers/{intf}' -    script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{intf}' -    script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{intf}' -    script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{intf}' -    script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{intf}' -    config_wide_dhcp6c = f'/run/dhcp6c/dhcp6c.{intf}.conf' +    ifname = pppoe['ifname'] +    config_pppoe = f'/etc/ppp/peers/{ifname}' +    script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{ifname}' +    script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{ifname}' +    script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{ifname}' +    script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{ifname}' +    config_wide_dhcp6c = f'/run/dhcp6c/dhcp6c.{ifname}.conf'      config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up,                      script_pppoe_ip_down, script_pppoe_ipv6_up, config_wide_dhcp6c] -    if pppoe['deleted']: +    if 'deleted' in pppoe.keys():          # stop DHCPv6-PD client -        call(f'systemctl stop dhcp6c@{intf}.service') +        call(f'systemctl stop dhcp6c@{ifname}.service')          # Hang-up PPPoE connection -        call(f'systemctl stop ppp@{intf}.service') +        call(f'systemctl stop ppp@{ifname}.service')          # Delete PPP configuration files          for file in config_files: @@ -235,22 +121,22 @@ def generate(pppoe):      render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl',             pppoe, trim_blocks=True, permission=0o755) -    if len(pppoe['dhcpv6_pd_interfaces']) > 0: +    tmp = jmespath.search('dhcpv6_options.prefix_delegation.interface', pppoe) +    if tmp and len(tmp) > 0:          # ipv6.tmpl relies on ifname - this should be made consitent in the          # future better then double key-ing the same value -        pppoe['ifname'] = intf -        render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe, trim_blocks=True) +        render(config_wide_dhcp6c, 'dhcp-client/ipv6_new.tmpl', pppoe, trim_blocks=True)      return None  def apply(pppoe): -    if pppoe['deleted']: +    if 'deleted' in pppoe.keys():          # bail out early          return None -    if not pppoe['disable']: +    if 'disable' not in pppoe.keys():          # Dial PPPoE connection -        call('systemctl restart ppp@{intf}.service'.format(**pppoe)) +        call('systemctl restart ppp@{ifname}.service'.format(**pppoe))      return None | 
