diff options
author | Christian Poessinger <christian@poessinger.com> | 2019-09-22 19:13:07 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2019-09-22 19:13:07 +0200 |
commit | 79a655a12875f5f152abba2d17eb6a1033b59131 (patch) | |
tree | 5a75960039a0dd81608b5a38351a8c8314cabccf /src | |
parent | c4d0b9ed4736911d341efdebf34997e6cee8c5a8 (diff) | |
parent | 2b9c84594a693c66b949183a25cc32dfcdee72e1 (diff) | |
download | vyos-1x-79a655a12875f5f152abba2d17eb6a1033b59131.tar.gz vyos-1x-79a655a12875f5f152abba2d17eb6a1033b59131.zip |
Merge branch 'current' of github.com:vyos/vyos-1x into equuleus
* 'current' of github.com:vyos/vyos-1x: (49 commits)
Jenkins: ease Pipeline
vxlan: T1636: simplyfy code (don't delete intf addresses)
ethernet: T1637: interfaces in a bond can be disabled
ethernet: T1637: fix calling arp_cache_tmo property
ethernet: T1637: do not overwrite interface description with interface name
ethernet: T1637: support offloading functions
Python/ifconfig: T1557: ethernet: add offloading interfaces
Python/ifconfig: T1557: update comments
Python/ifconfig: T1557: delete all assigned IP addresses on remove()
ethernet: T1637: call remove() on interface deletion
Python/ifconfig: T1557: use proper inheritance levels on remove()
ethernet: T1637: remove debug pprint
bridge: T1556: minor comment cleanup
bonding: T1614: minor comment cleanup
Python/ifconfig: T1557: unify '/sys/class/net/{}' path
Python/ifconfig: T1557: vmxnet3/virtio_net do not support changing speed/duplex control
Python/ifconfig: T1557: vmxnet3/virtio_net do not support changing flow control
Python/ifconfig: T1557: query driver if it supports auto negotiation
Python/ifconfig: T1557: call ethtool with full path
Python/ifconfig: T1557: return stdout string for _cmd()
...
Diffstat (limited to 'src')
-rwxr-xr-x | src/conf_mode/interface-bonding.py | 12 | ||||
-rwxr-xr-x | src/conf_mode/interface-bridge.py | 3 | ||||
-rwxr-xr-x | src/conf_mode/interface-ethernet.py | 382 | ||||
-rwxr-xr-x | src/conf_mode/interface-openvpn.py | 36 | ||||
-rwxr-xr-x | src/conf_mode/interface-vxlan.py | 16 | ||||
-rwxr-xr-x | src/conf_mode/interface-wireguard.py | 26 | ||||
-rwxr-xr-x | src/conf_mode/ipoe_server.py | 23 | ||||
-rwxr-xr-x | src/helpers/vyos-boot-config-loader.py | 153 | ||||
-rwxr-xr-x | src/services/vyos-hostsd | 2 |
9 files changed, 576 insertions, 77 deletions
diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py index f0a33beff..9049913e6 100755 --- a/src/conf_mode/interface-bonding.py +++ b/src/conf_mode/interface-bonding.py @@ -22,7 +22,7 @@ from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BondIf, EthernetIf +from vyos.ifconfig import BondIf, VLANIf from vyos.configdict import list_diff, vlan_to_dict from vyos.config import Config from vyos import ConfigError @@ -82,7 +82,7 @@ def apply_vlan_config(vlan, config): to a VLAN interface """ - if type(vlan) != type(EthernetIf("lo")): + if type(vlan) != type(VLANIf("lo")): raise TypeError() # update interface description used e.g. within SNMP @@ -279,11 +279,6 @@ def verify(bond): raise ConfigError('can not enslave interface {} which already ' \ 'belongs to {}'.format(intf, tmp)) - # we can not add disabled slave interfaces to our bond - if conf.exists('interfaces ethernet ' + intf + ' disable'): - raise ConfigError('can not enslave disabled interface {}' \ - .format(intf)) - # can not add interfaces with an assigned address to a bond if conf.exists('interfaces ethernet ' + intf + ' address'): raise ConfigError('can not enslave interface {} which has an address ' \ @@ -339,8 +334,7 @@ def apply(bond): b = BondIf(bond['intf']) if bond['deleted']: - # - # delete bonding interface + # delete interface b.remove() else: # Some parameters can not be changed when the bond is up. diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 401182a0d..62589c798 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -183,8 +183,7 @@ def apply(bridge): br = BridgeIf(bridge['intf']) if bridge['deleted']: - # delete bridge interface - # DHCP is stopped inside remove() + # delete interface br.remove() else: # enable interface diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py new file mode 100755 index 000000000..f82105847 --- /dev/null +++ b/src/conf_mode/interface-ethernet.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from copy import deepcopy +from sys import exit + +from vyos.ifconfig import EthernetIf, VLANIf +from vyos.configdict import list_diff, vlan_to_dict +from vyos.config import Config +from vyos import ConfigError + +default_config_data = { + 'address': [], + 'address_remove': [], + 'description': '', + 'deleted': False, + 'dhcp_client_id': '', + 'dhcp_hostname': '', + 'dhcpv6_prm_only': False, + 'dhcpv6_temporary': False, + 'disable': False, + 'disable_link_detect': 1, + 'duplex': 'auto', + 'flow_control': 'on', + 'hw_id': '', + 'ip_arp_cache_tmo': 30, + 'ip_proxy_arp': 0, + 'ip_proxy_arp_pvlan': 0, + 'intf': '', + 'mac': '', + 'mtu': 1500, + 'offload_gro': 'off', + 'offload_gso': 'off', + 'offload_sg': 'off', + 'offload_tso': 'off', + 'offload_ufo': 'off', + 'speed': 'auto', + 'vif_s': [], + 'vif_s_remove': [], + 'vif': [], + 'vif_remove': [] +} + + +def apply_vlan_config(vlan, config): + """ + Generic function to apply a VLAN configuration from a dictionary + to a VLAN interface + """ + + if type(vlan) != type(VLANIf("lo")): + raise TypeError() + + # update interface description used e.g. within SNMP + vlan.ifalias = config['description'] + # ignore link state changes + vlan.link_detect = config['disable_link_detect'] + # Maximum Transmission Unit (MTU) + vlan.mtu = config['mtu'] + # Change VLAN interface MAC address + if config['mac']: + vlan.mac = config['mac'] + + # enable/disable VLAN interface + if config['disable']: + vlan.state = 'down' + else: + vlan.state = 'up' + + # Configure interface address(es) + # - not longer required addresses get removed first + # - newly addresses will be added second + for addr in config['address_remove']: + vlan.del_addr(addr) + for addr in config['address']: + vlan.add_addr(addr) + + +def get_config(): + eth = deepcopy(default_config_data) + conf = Config() + + # determine tagNode instance + try: + eth['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + except KeyError as E: + print("Interface not specified") + + # check if ethernet interface has been removed + cfg_base = 'interfaces ethernet ' + eth['intf'] + if not conf.exists(cfg_base): + eth['deleted'] = True + # we can not bail out early as ethernet interface can not be removed + # Kernel will complain with: RTNETLINK answers: Operation not supported. + # Thus we need to remove individual settings + return eth + + # set new configuration level + conf.set_level(cfg_base) + + # retrieve configured interface addresses + if conf.exists('address'): + eth['address'] = conf.return_values('address') + + # get interface addresses (currently effective) - to determine which + # address is no longer valid and needs to be removed + eff_addr = conf.return_effective_values('address') + eth['address_remove'] = list_diff(eff_addr, eth['address']) + + # retrieve interface description + if conf.exists('description'): + eth['description'] = conf.return_value('description') + + # get DHCP client identifier + if conf.exists('dhcp-options client-id'): + eth['dhcp_client_id'] = conf.return_value('dhcp-options client-id') + + # DHCP client host name (overrides the system host name) + if conf.exists('dhcp-options host-name'): + eth['dhcp_hostname'] = conf.return_value('dhcp-options host-name') + + # DHCPv6 only acquire config parameters, no address + if conf.exists('dhcpv6-options parameters-only'): + eth['dhcpv6_prm_only'] = conf.return_value('dhcpv6-options parameters-only') + + # DHCPv6 temporary IPv6 address + if conf.exists('dhcpv6-options temporary'): + eth['dhcpv6_temporary'] = conf.return_value('dhcpv6-options temporary') + + # ignore link state changes + if conf.exists('disable-link-detect'): + eth['disable_link_detect'] = 2 + + # disable ethernet flow control (pause frames) + if conf.exists('disable-flow-control'): + eth['flow_control'] = 'off' + + # retrieve real hardware address + if conf.exists('hw-id'): + eth['hw_id'] = conf.return_value('hw-id') + + # disable interface + if conf.exists('disable'): + eth['disable'] = True + + # interface duplex + if conf.exists('duplex'): + eth['duplex'] = conf.return_value('duplex') + + # ARP cache entry timeout in seconds + if conf.exists('ip arp-cache-timeout'): + eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) + + # Enable proxy-arp on this interface + if conf.exists('ip enable-proxy-arp'): + eth['ip_proxy_arp'] = 1 + + # Enable private VLAN proxy ARP on this interface + if conf.exists('ip proxy-arp-pvlan'): + eth['ip_proxy_arp_pvlan'] = 1 + + # Media Access Control (MAC) address + if conf.exists('mac'): + eth['mac'] = conf.return_value('mac') + + # Maximum Transmission Unit (MTU) + if conf.exists('mtu'): + eth['mtu'] = int(conf.return_value('mtu')) + + # GRO (generic receive offload) + if conf.exists('offload-options generic-receive'): + eth['offload_gro'] = conf.return_value('offload-options generic-receive') + + # GSO (generic segmentation offload) + if conf.exists('offload-options generic-segmentation'): + eth['offload_gso'] = conf.return_value('offload-options generic-segmentation') + + # scatter-gather option + if conf.exists('offload-options scatter-gather'): + eth['offload_sg'] = conf.return_value('offload-options scatter-gather') + + # TSO (TCP segmentation offloading) + if conf.exists('offload-options tcp-segmentation'): + eth['offload_tso'] = conf.return_value('offload-options tcp-segmentation') + + # UDP fragmentation offloading + if conf.exists('offload-options udp-fragmentation'): + eth['offload_ufo'] = conf.return_value('offload-options udp-fragmentation') + + # interface speed + if conf.exists('speed'): + eth['speed'] = conf.return_value('speed') + + # re-set configuration level and retrieve vif-s interfaces + conf.set_level(cfg_base) + # get vif-s interfaces (currently effective) - to determine which vif-s + # interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif-s') + act_intf = conf.list_nodes('vif-s') + eth['vif_s_remove'] = list_diff(eff_intf, act_intf) + + if conf.exists('vif-s'): + for vif_s in conf.list_nodes('vif-s'): + # set config level to vif-s interface + conf.set_level(cfg_base + ' vif-s ' + vif_s) + eth['vif_s'].append(vlan_to_dict(conf)) + + # re-set configuration level and retrieve vif-s interfaces + conf.set_level(cfg_base) + # Determine vif interfaces (currently effective) - to determine which + # vif interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif') + act_intf = conf.list_nodes('vif') + eth['vif_remove'] = list_diff(eff_intf, act_intf) + + if conf.exists('vif'): + for vif in conf.list_nodes('vif'): + # set config level to vif interface + conf.set_level(cfg_base + ' vif ' + vif) + eth['vif'].append(vlan_to_dict(conf)) + + return eth + + +def verify(eth): + if eth['deleted']: + return None + + if eth['speed'] == 'auto': + if eth['duplex'] != 'auto': + raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too') + + if eth['duplex'] == 'auto': + if eth['speed'] != 'auto': + raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') + + conf = Config() + # some options can not be changed when interface is enslaved to a bond + for bond in conf.list_nodes('interfaces bonding'): + if conf.exists('interfaces bonding ' + bond + ' member interface'): + bond_member = conf.return_values('interfaces bonding ' + bond + ' member interface') + if eth['name'] in bond_member: + if eth['address']: + raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond) + + + return None + +def generate(eth): + return None + +def apply(eth): + e = EthernetIf(eth['intf']) + if eth['deleted']: + # delete interface + e.remove() + else: + # update interface description used e.g. within SNMP + e.ifalias = eth['description'] + + # + # missing DHCP/DHCPv6 options go here + # + + # ignore link state changes + e.link_detect = eth['disable_link_detect'] + # disable ethernet flow control (pause frames) + e.set_flow_control(eth['flow_control']) + # configure ARP cache timeout in milliseconds + e.arp_cache_tmo = eth['ip_arp_cache_tmo'] + # Enable proxy-arp on this interface + e.proxy_arp = eth['ip_proxy_arp'] + # Enable private VLAN proxy ARP on this interface + e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan'] + + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed + if eth['mac']: + e.mac = eth['mac'] + else: + e.mac = eth['hw_id'] + + # Maximum Transmission Unit (MTU) + e.mtu = eth['mtu'] + + # GRO (generic receive offload) + e.set_gro(eth['offload_gro']) + + # GSO (generic segmentation offload) + e.set_gso(eth['offload_gso']) + + # scatter-gather option + e.set_sg(eth['offload_sg']) + + # TSO (TCP segmentation offloading) + e.set_tso(eth['offload_tso']) + + # UDP fragmentation offloading + e.set_ufo(eth['offload_ufo']) + + # Set physical interface speed and duplex + e.set_speed_duplex(eth['speed'], eth['duplex']) + + # Configure interface address(es) + # - not longer required addresses get removed first + # - newly addresses will be added second + for addr in eth['address_remove']: + e.del_addr(addr) + for addr in eth['address']: + e.add_addr(addr) + + # Enable/Disable interface + if eth['disable']: + e.state = 'down' + else: + e.state = 'up' + + # remove no longer required service VLAN interfaces (vif-s) + for vif_s in eth['vif_s_remove']: + e.del_vlan(vif_s) + + # create service VLAN interfaces (vif-s) + for vif_s in eth['vif_s']: + s_vlan = e.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) + apply_vlan_config(s_vlan, vif_s) + + # remove no longer required client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c_remove']: + s_vlan.del_vlan(vif_c) + + # create client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c']: + c_vlan = s_vlan.add_vlan(vif_c['id']) + apply_vlan_config(c_vlan, vif_c) + + # remove no longer required VLAN interfaces (vif) + for vif in eth['vif_remove']: + e.del_vlan(vif) + + # create VLAN interfaces (vif) + for vif in eth['vif']: + # QoS priority mapping can only be set during interface creation + # so we delete the interface first if required. + if vif['egress_qos_changed'] or vif['ingress_qos_changed']: + try: + # on system bootup the above condition is true but the interface + # does not exists, which throws an exception, but that's legal + e.del_vlan(vif['id']) + except: + pass + + vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) + apply_vlan_config(vlan, vif) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index 34c094862..35e7928c2 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -167,10 +167,18 @@ key {{ tls_key }} crl-verify {{ tls_crl }} {% endif %} +{%- if tls_version_min %} +tls-version-min {{tls_version_min}} +{% endif %} + {%- if tls_dh %} dh {{ tls_dh }} {% endif %} +{%- if tls_auth %} +tls-auth {{tls_auth}} +{% endif %} + {%- if 'active' in tls_role %} tls-client {%- elif 'passive' in tls_role %} @@ -277,12 +285,14 @@ default_config_data = { 'server_topology': '', 'shared_secret_file': '', 'tls': False, + 'tls_auth': '', 'tls_ca_cert': '', 'tls_cert': '', 'tls_crl': '', 'tls_dh': '', 'tls_key': '', 'tls_role': '', + 'tls_version_min': '', 'type': 'tun', 'uid': user, 'gid': group, @@ -532,6 +542,11 @@ def get_config(): if conf.exists('server reject-unconfigured-clients'): openvpn['server_reject_unconfigured'] = True + # File containing TLS auth static key + if conf.exists('tls auth-file'): + openvpn['tls_auth'] = conf.return_value('tls auth-file') + openvpn['tls'] = True + # File containing certificate for Certificate Authority (CA) if conf.exists('tls ca-cert-file'): openvpn['tls_ca_cert'] = conf.return_value('tls ca-cert-file') @@ -562,6 +577,10 @@ def get_config(): openvpn['tls_role'] = conf.return_value('tls role') openvpn['tls'] = True + # Minimum required TLS version + if conf.exists('tls tls-version-min'): + openvpn['tls_version_min'] = conf.return_value('tls tls-version-min') + if conf.exists('shared-secret-key-file'): openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file') @@ -714,11 +733,17 @@ def verify(openvpn): if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_ca_cert']): raise ConfigError('Specified ca-cert-file "{}" is invalid'.format(openvpn['tls_ca_cert'])) - if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']): - raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert'])) + if openvpn['tls_auth']: + if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['tls_auth']): + raise ConfigError('Specified auth-file "{}" is invalid'.format(openvpn['tls_auth'])) + + if openvpn['tls_cert']: + if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']): + raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert'])) - if not checkCertHeader('-----BEGIN (?:RSA )?PRIVATE KEY-----', openvpn['tls_key']): - raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key'])) + if openvpn['tls_key']: + if not checkCertHeader('-----BEGIN (?:RSA )?PRIVATE KEY-----', openvpn['tls_key']): + raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key'])) if openvpn['tls_crl']: if not checkCertHeader('-----BEGIN X509 CRL-----', openvpn['tls_crl']): @@ -730,7 +755,8 @@ def verify(openvpn): if openvpn['tls_role']: if openvpn['mode'] in ['client', 'server']: - raise ConfigError('Cannot specify "tls role" in client-server mode') + if not openvpn['tls_auth']: + raise ConfigError('Cannot specify "tls role" in client-server mode') if openvpn['tls_role'] == 'active': if openvpn['protocol'] == 'tcp-passive': diff --git a/src/conf_mode/interface-vxlan.py b/src/conf_mode/interface-vxlan.py index 59022238e..e97b4bf99 100755 --- a/src/conf_mode/interface-vxlan.py +++ b/src/conf_mode/interface-vxlan.py @@ -28,7 +28,6 @@ from netifaces import interfaces default_config_data = { 'address': [], - 'address_remove': [], 'deleted': False, 'description': '', 'disable': False, @@ -43,7 +42,6 @@ default_config_data = { # the IANA's selection of a standard destination port } - def get_config(): vxlan = deepcopy(default_config_data) conf = Config() @@ -66,12 +64,6 @@ def get_config(): if conf.exists('address'): vxlan['address'] = conf.return_values('address') - # Determine interface addresses (currently effective) - to determine which - # address is no longer valid and needs to be removed from the interface - eff_addr = conf.return_effective_values('address') - act_addr = conf.return_values('address') - vxlan['address_remove'] = list_diff(eff_addr, act_addr) - # retrieve interface description if conf.exists('description'): vxlan['description'] = conf.return_value('description') @@ -180,11 +172,9 @@ def apply(vxlan): # Enable proxy-arp on this interface v.proxy_arp = vxlan['ip_proxy_arp'] - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in vxlan['address_remove']: - v.del_addr(addr) + # Configure interface address(es) - no need to implicitly delete the + # old addresses as they have already been removed by deleting the + # interface above for addr in vxlan['address']: v.add_addr(addr) diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py index d51a7a08d..4ae3251fe 100755 --- a/src/conf_mode/interface-wireguard.py +++ b/src/conf_mode/interface-wireguard.py @@ -26,12 +26,16 @@ from vyos.config import Config from vyos import ConfigError from vyos.ifconfig import WireGuardIf -ifname = str(os.environ['VYOS_TAGNODE_VALUE']) -intfc = WireGuardIf(ifname) +try: + ifname = str(os.environ['VYOS_TAGNODE_VALUE']) + intfc = WireGuardIf(ifname) +except KeyError: + print("Interface not specified") + sys.exit(1) kdir = r'/config/auth/wireguard' -def check_kmod(): +def _check_kmod(): if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") if os.system('sudo modprobe wireguard') != 0: @@ -39,6 +43,19 @@ def check_kmod(): raise ConfigError("modprobe wireguard failed") +def _migrate_default_keys(): + if os.path.exists('{}/private.key'.format(kdir)) and not os.path.exists('{}/default/private.key'.format(kdir)): + sl.syslog(sl.LOG_NOTICE, "migrate keypair to default") + old_umask = os.umask(0o027) + location = '{}/default'.format(kdir) + subprocess.call(['sudo mkdir -p ' + location], shell=True) + subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True) + subprocess.call(['sudo chmod 750 ' + location], shell=True) + os.rename('{}/private.key'.format(kdir),'{}/private.key'.format(location)) + os.rename('{}/public.key'.format(kdir),'{}/public.key'.format(location)) + os.umask(old_umask) + + def get_config(): c = Config() if not c.exists('interfaces wireguard'): @@ -257,7 +274,8 @@ def apply(c): if __name__ == '__main__': try: - check_kmod() + _check_kmod() + _migrate_default_keys() c = get_config() verify(c) apply(c) diff --git a/src/conf_mode/ipoe_server.py b/src/conf_mode/ipoe_server.py index ca6b423e5..a60379760 100755 --- a/src/conf_mode/ipoe_server.py +++ b/src/conf_mode/ipoe_server.py @@ -41,7 +41,6 @@ ipoe_config = ''' ### generated by ipoe.py ### [modules] log_syslog -ippool ipoe shaper ipv6pool @@ -50,6 +49,7 @@ ipv6_dhcp {% if auth['mech'] == 'radius' %} radius {% endif -%} +ippool {% if auth['mech'] == 'local' %} chap-secrets {% endif %} @@ -65,7 +65,11 @@ level=5 [ipoe] verbose=1 {% for intfc in interfaces %} +{% if interfaces[intfc]['vlan_mon'] %} +interface=re:{{intfc}}\.\d+,\ +{% else %} interface={{intfc}},\ +{% endif %} shared={{interfaces[intfc]['shared']}},\ mode={{interfaces[intfc]['mode']}},\ ifcfg={{interfaces[intfc]['ifcfg']}},\ @@ -83,8 +87,7 @@ password=csid {%- for intfc in interfaces %} {% if (interfaces[intfc]['shared'] == '0') and (interfaces[intfc]['vlan_mon']) %} -vlan_mon={{interfaces[intfc]['vlan_mon']|join(',')}} -interface=re:{{intfc}}\.(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2}) +vlan-mon={{intfc}},{{interfaces[intfc]['vlan_mon']|join(',')}} {% endif %} {% endfor %} @@ -160,16 +163,24 @@ nas-identifier={{auth['radsettings']['nas-identifier']}} tcp=127.0.0.1:2002 ''' -### pppoe chap secrets +### chap secrets chap_secrets_conf = ''' # username server password acceptable local IP addresses shaper {% for aifc in auth['auth_if'] %} {% for mac in auth['auth_if'][aifc] %} {% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %} +{% if auth['auth_if'][aifc][mac]['vlan'] %} +{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} +{% else %} {{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} +{% endif %} +{% else %} +{% if auth['auth_if'][aifc][mac]['vlan'] %} +{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t* {% else %} {{aifc}}\t*\t{{mac.lower()}}\t* {% endif %} +{% endif %} {% endfor %} {% endfor %} ''' @@ -213,6 +224,7 @@ def accel_cmd(cmd=''): ### chap_secrets file if auth mode local def gen_chap_secrets(c): + tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True) chap_secrets_txt = tmpl.render(c) old_umask = os.umask(0o077) @@ -296,6 +308,9 @@ def get_config(): config_data['auth']['auth_if'][auth_int][mac] = {} config_data['auth']['auth_if'][auth_int][mac]['up'] = None config_data['auth']['auth_if'][auth_int][mac]['down'] = None + ## client vlan-id + if c.exists('authentication interface ' + auth_int + ' mac-address ' + mac + ' vlan-id'): + config_data['auth']['auth_if'][auth_int][mac]['vlan'] = c.return_value('authentication interface ' + auth_int + ' mac-address ' + mac + ' vlan-id') if c.exists('authentication mode radius'): for rsrv in c.list_nodes('authentication radius-server'): config_data['auth']['radius'][rsrv] = {} diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py index 06c95765f..7c81a4c3c 100755 --- a/src/helpers/vyos-boot-config-loader.py +++ b/src/helpers/vyos-boot-config-loader.py @@ -18,41 +18,72 @@ import os import sys +import pwd +import grp import subprocess import traceback +from datetime import datetime +from vyos.defaults import directories from vyos.configsession import ConfigSession, ConfigSessionError from vyos.configtree import ConfigTree STATUS_FILE = '/tmp/vyos-config-status' TRACE_FILE = '/tmp/boot-config-trace' -session = ConfigSession(os.getpid(), 'vyos-boot-config-loader') -env = session.get_session_env() +CFG_GROUP = 'vyattacfg' -default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default' - -if len(sys.argv) < 1: - print("Must be called with argument.") - sys.exit(1) +if 'log' in directories: + LOG_DIR = directories['log'] else: - file_name = sys.argv[1] + LOG_DIR = '/var/log/vyatta' + +LOG_FILE = LOG_DIR + '/vyos-boot-config-loader.log' + +try: + with open('/proc/cmdline', 'r') as f: + cmdline = f.read() + if 'vyos-debug' in cmdline: + os.environ['VYOS_DEBUG'] = 'yes' +except Exception as e: + print('{0}'.format(e)) def write_config_status(status): - with open(STATUS_FILE, 'w') as f: - f.write('{0}\n'.format(status)) + try: + with open(STATUS_FILE, 'w') as f: + f.write('{0}\n'.format(status)) + except Exception as e: + print('{0}'.format(e)) def trace_to_file(trace_file_name): - with open(trace_file_name, 'w') as trace_file: - traceback.print_exc(file=trace_file) + try: + with open(trace_file_name, 'w') as trace_file: + traceback.print_exc(file=trace_file) + except Exception as e: + print('{0}'.format(e)) + +def failsafe(config_file_name): + fail_msg = """ + !!!!! + There were errors loading the configuration + Please examine the errors in + {0} + and correct + !!!!! + """.format(TRACE_FILE) + + print(fail_msg, file=sys.stderr) + + users = [x[0] for x in pwd.getpwall()] + if 'vyos' in users: + return -def failsafe(): try: - with open(default_file_name, 'r') as f: + with open(config_file_name, 'r') as f: config_file = f.read() except Exception as e: print("Catastrophic: no default config file " - "'{0}'".format(default_file_name)) + "'{0}'".format(config_file_name)) sys.exit(1) config = ConfigTree(config_file) @@ -73,29 +104,73 @@ def failsafe(): except subprocess.CalledProcessError as e: sys.exit("{0}".format(e)) - with open('/etc/motd', 'a+') as f: - f.write('\n\n') - f.write('!!!!!\n') - f.write('There were errors loading the initial configuration;\n') - f.write('please examine the errors in {0} and correct.' - '\n'.format(TRACE_FILE)) - f.write('!!!!!\n\n') +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Must specify boot config file.") + sys.exit(1) + else: + file_name = sys.argv[1] -try: - with open(file_name, 'r') as f: - config_file = f.read() -except Exception as e: - write_config_status(1) - failsafe() - trace_to_file(TRACE_FILE) - sys.exit("{0}".format(e)) + # Set user and group options, so that others will be able to commit + # Currently, the only caller does 'sg CFG_GROUP', but that may change + cfg_group = grp.getgrnam(CFG_GROUP) + os.setgid(cfg_group.gr_gid) -try: - session.load_config(file_name) - session.commit() - write_config_status(0) -except ConfigSessionError as e: - write_config_status(1) - failsafe() - trace_to_file(TRACE_FILE) - sys.exit(1) + # Need to set file permissions to 775 so that every vyattacfg group + # member has write access to the running config + os.umask(0o002) + + session = ConfigSession(os.getpid(), 'vyos-boot-config-loader') + env = session.get_session_env() + + default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default' + + try: + with open(file_name, 'r') as f: + config_file = f.read() + except Exception: + write_config_status(1) + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + try: + time_begin_load = datetime.now() + load_out = session.load_config(file_name) + time_end_load = datetime.now() + time_begin_commit = datetime.now() + commit_out = session.commit() + time_end_commit = datetime.now() + write_config_status(0) + except ConfigSessionError: + # If here, there is no use doing session.discard, as we have no + # recoverable config environment, and will only throw an error + write_config_status(1) + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + time_elapsed_load = time_end_load - time_begin_load + time_elapsed_commit = time_end_commit - time_begin_commit + + try: + if not os.path.exists(LOG_DIR): + os.mkdir(LOG_DIR) + with open(LOG_FILE, 'a') as f: + f.write('\n\n') + f.write('{0} Begin config load\n' + ''.format(time_begin_load)) + f.write(load_out) + f.write('{0} End config load\n' + ''.format(time_end_load)) + f.write('Elapsed time for config load: {0}\n' + ''.format(time_elapsed_load)) + f.write('{0} Begin config commit\n' + ''.format(time_begin_commit)) + f.write(commit_out) + f.write('{0} End config commit\n' + ''.format(time_end_commit)) + f.write('Elapsed time for config commit: {0}\n' + ''.format(time_elapsed_commit)) + except Exception as e: + print('{0}'.format(e)) diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index 8f70eb4e9..e7ecd8573 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -43,7 +43,7 @@ hosts_tmpl_source = """ # Local host 127.0.0.1 localhost -127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }}{% endif %} +127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }} {{ host_name }}{% endif %} # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback |