diff options
Diffstat (limited to 'src/conf_mode/interfaces-pppoe.py')
-rwxr-xr-x | src/conf_mode/interfaces-pppoe.py | 211 |
1 files changed, 89 insertions, 122 deletions
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 6acb45d5e..353a5a12c 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -18,85 +18,14 @@ import os from sys import exit from copy import deepcopy -from jinja2 import Template -from subprocess import Popen, PIPE -from time import sleep -from pwd import getpwnam -from grp import getgrnam +from jinja2 import FileSystemLoader, Environment +from netifaces import interfaces from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir from vyos.ifconfig import Interface +from vyos.util import chown, chmod_x, cmd from vyos import ConfigError -from netifaces import interfaces - -# Please be careful if you edit the template. -config_pppoe_tmpl = """ -### Autogenerated by interfaces-pppoe.py ### - -{% if description %} -# {{ description }} -{% endif %} - -# Require peer to provide the local IP address if it is not -# specified explicitly in the config file. -noipdefault - -# Don't show the password in logfiles: -hide-password - -# Standard Link Control Protocol (LCP) parameters: -lcp-echo-interval 20 -lcp-echo-failure 3 - -# RFC 2516, paragraph 7 mandates that the following options MUST NOT be -# requested and MUST be rejected if requested by the peer: -# Address-and-Control-Field-Compression (ACFC) -noaccomp - -# Asynchronous-Control-Character-Map (ACCM) -default-asyncmap - -# Override any connect script that may have been set in /etc/ppp/options. -connect /bin/true - -# Don't try to authenticate the remote node -noauth - -# Don't try to proxy ARP for the remote endpoint. User can set proxy -# arp entries up manually if they wish. More importantly, having -# the "proxyarp" parameter set disables the "defaultroute" option. -noproxyarp - -plugin rp-pppoe.so -{{ source_interface }} -persist -ifname {{ intf }} -ipparam {{ intf }} -debug -logfile {{ logfile }} -{% if 'auto' in default_route -%} -defaultroute -{% elif 'force' in default_route -%} -defaultroute -replacedefaultroute -{% endif %} -mtu {{ mtu }} -mru {{ mtu }} -user "{{ auth_username }}" -password "{{ auth_password }}" -{% if name_server -%} -usepeerdns -{% endif %} -{% if ipv6_enable -%} -+ipv6 -{% endif %} -{% if service_name -%} -rp_pppoe_service "{{ service_name }}" -{% endif %} - -""" - -PPP_LOGFILE = '/var/log/vyatta/ppp_{}.log' default_config_data = { 'access_concentrator': '', @@ -105,7 +34,7 @@ default_config_data = { 'on_demand': False, 'default_route': 'auto', 'deleted': False, - 'description': '', + 'description': '\0', 'disable': False, 'intf': '', 'idle_timeout': '', @@ -117,24 +46,21 @@ default_config_data = { 'name_server': True, 'remote_address': '', 'service_name': '', - 'source_interface': '' + 'source_interface': '', + 'vrf': '' } -def subprocess_cmd(command): - p = Popen(command, stdout=PIPE, shell=True) - p.communicate() - def get_config(): pppoe = deepcopy(default_config_data) conf = Config() base_path = ['interfaces', 'pppoe'] # determine tagNode instance - try: - pppoe['intf'] = os.environ['VYOS_TAGNODE_VALUE'] - pppoe['logfile'] = PPP_LOGFILE.format(pppoe['intf']) - except KeyError as E: - print("Interface not specified") + if 'VYOS_TAGNODE_VALUE' not in os.environ: + raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') + + pppoe['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + pppoe['logfile'] = f"/var/log/vyatta/ppp_{pppoe['intf']}.log" # Check if interface has been removed if not conf.exists(base_path + [pppoe['intf']]): @@ -190,7 +116,7 @@ def get_config(): # Physical Interface used for this PPPoE session if conf.exists(['source-interface']): - pppoe['source_interface'] = conf.return_value('source-interface') + pppoe['source_interface'] = conf.return_value(['source-interface']) # Maximum Transmission Unit (MTU) if conf.exists(['mtu']): @@ -208,6 +134,10 @@ def get_config(): 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']) + return pppoe def verify(pppoe): @@ -216,32 +146,90 @@ def verify(pppoe): return None if not pppoe['source_interface']: - raise ConfigError('PPPoE source interface is missing') + 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') - if pppoe['source_interface'] not in interfaces(): - raise ConfigError('PPPoE source interface does not exist') + if pppoe['on_demand'] and pppoe['vrf']: + raise ConfigError('On-demand dialing and VRF can not be used at the same time') return None def generate(pppoe): - config_file_pppoe = '/etc/ppp/peers/{}'.format(pppoe['intf']) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir["data"], "templates", "pppoe") + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader, trim_blocks=True) + + # 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_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up, + script_pppoe_ip_down, script_pppoe_ipv6_up] + + # Ensure directories for config files exist - otherwise create them on demand + for file in config_files: + dirname = os.path.dirname(file) + if not os.path.isdir(dirname): + os.mkdir(dirname) # Always hang-up PPPoE connection prior generating new configuration file - cmd = 'systemctl stop ppp@{}.service'.format(pppoe['intf']) - subprocess_cmd(cmd) + cmd(f'systemctl stop ppp@{intf}.service') if pppoe['deleted']: # Delete PPP configuration files - if os.path.exists(config_file_pppoe): - os.unlink(config_file_pppoe) + for file in config_files: + if os.path.exists(file): + os.unlink(file) else: # Create PPP configuration files - tmpl = Template(config_pppoe_tmpl) + tmpl = env.get_template('peer.tmpl') + config_text = tmpl.render(pppoe) + with open(config_pppoe, 'w') as f: + f.write(config_text) + + # Create script for ip-pre-up.d + tmpl = env.get_template('ip-pre-up.script.tmpl') config_text = tmpl.render(pppoe) - with open(config_file_pppoe, 'w') as f: + with open(script_pppoe_pre_up, 'w') as f: f.write(config_text) + # Create script for ip-up.d + tmpl = env.get_template('ip-up.script.tmpl') + config_text = tmpl.render(pppoe) + with open(script_pppoe_ip_up, 'w') as f: + f.write(config_text) + + # Create script for ip-down.d + tmpl = env.get_template('ip-down.script.tmpl') + config_text = tmpl.render(pppoe) + with open(script_pppoe_ip_down, 'w') as f: + f.write(config_text) + + # Create script for ipv6-up.d + tmpl = env.get_template('ipv6-up.script.tmpl') + config_text = tmpl.render(pppoe) + with open(script_pppoe_ipv6_up, 'w') as f: + f.write(config_text) + + # make generated script file executable + chmod_x(script_pppoe_pre_up) + chmod_x(script_pppoe_ip_up) + chmod_x(script_pppoe_ip_down) + chmod_x(script_pppoe_ipv6_up) + return None def apply(pppoe): @@ -250,33 +238,12 @@ def apply(pppoe): return None if not pppoe['disable']: - # dial PPPoE connection - cmd = 'systemctl start ppp@{}.service'.format(pppoe['intf']) - subprocess_cmd(cmd) + # "dial" PPPoE connection + intf = pppoe['intf'] + cmd(f'systemctl start ppp@{intf}.service') # make logfile owned by root / vyattacfg - if os.path.isfile(pppoe['logfile']): - uid = getpwnam('root').pw_uid - gid = getgrnam('vyattacfg').gr_gid - os.chown(pppoe['logfile'], uid, gid) - - # better late then sorry ... but we can only set interface alias after - # pppd has been launched and created the interface - cnt = 0 - while pppoe['intf'] not in interfaces(): - cnt += 1 - if cnt == 50: - break - - # sleep 250ms - sleep(0.250) - - try: - # we need to catch the exception if the interface is not up due to - # reason stated above - Interface(pppoe['intf']).set_alias(pppoe['description']) - except: - pass + chown(pppoe['logfile'], 'root', 'vyattacfg') return None |