summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/interfaces-erspan.py32
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py30
-rwxr-xr-xsrc/conf_mode/interfaces-geneve.py12
-rwxr-xr-xsrc/conf_mode/interfaces-l2tpv3.py33
-rwxr-xr-xsrc/conf_mode/interfaces-macsec.py10
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py5
-rwxr-xr-xsrc/conf_mode/interfaces-pseudo-ethernet.py12
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py88
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py13
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py11
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py230
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py55
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py4
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py12
-rwxr-xr-xsrc/conf_mode/service_console-server.py23
-rwxr-xr-xsrc/conf_mode/service_webproxy.py3
-rwxr-xr-xsrc/conf_mode/vrrp.py6
-rwxr-xr-xsrc/migration-scripts/interfaces/19-to-2061
-rwxr-xr-xsrc/migration-scripts/nat/4-to-56
-rwxr-xr-xsrc/migration-scripts/quagga/6-to-764
-rwxr-xr-xsrc/op_mode/show_interfaces.py5
-rwxr-xr-xsrc/op_mode/vtysh_wrapper.sh4
-rwxr-xr-xsrc/services/vyos-configd43
-rwxr-xr-xsrc/system/on-dhcp-event.sh25
-rw-r--r--src/systemd/dropbear@.service5
-rw-r--r--src/tests/test_util.py4
-rwxr-xr-xsrc/validators/fqdn5
-rwxr-xr-xsrc/validators/interface-name2
28 files changed, 385 insertions, 418 deletions
diff --git a/src/conf_mode/interfaces-erspan.py b/src/conf_mode/interfaces-erspan.py
index 2d65b834c..97ae3cf55 100755
--- a/src/conf_mode/interfaces-erspan.py
+++ b/src/conf_mode/interfaces-erspan.py
@@ -48,7 +48,7 @@ def get_config(config=None):
conf = Config()
base = ['interfaces', 'erspan']
erspan = get_interface_dict(conf, base)
-
+
tmp = leaf_node_changed(conf, ['encapsulation'])
if tmp:
erspan.update({'encapsulation_changed': {}})
@@ -58,30 +58,30 @@ def get_config(config=None):
def verify(erspan):
if 'deleted' in erspan:
return None
-
+
if 'encapsulation' not in erspan:
raise ConfigError('Unable to detect the following ERSPAN tunnel encapsulation'\
'{ifname}!'.format(**erspan))
verify_mtu_ipv6(erspan)
verify_tunnel(erspan)
-
+
key = dict_search('parameters.ip.key',erspan)
if key == None:
raise ConfigError('parameters.ip.key is mandatory for ERSPAN tunnel')
-
+
def generate(erspan):
return None
def apply(erspan):
- if 'deleted' in erspan or 'encapsulation_changed' in erspan:
- if erspan['ifname'] in interfaces():
- tmp = Interface(erspan['ifname'])
- tmp.remove()
- if 'deleted' in erspan:
- return None
-
+ if 'deleted' in erspan or 'encapsulation_changed' in erspan:
+ if erspan['ifname'] in interfaces():
+ tmp = Interface(erspan['ifname'])
+ tmp.remove()
+ if 'deleted' in erspan:
+ return None
+
dispatch = {
'erspan': ERSpanIf,
'ip6erspan': ER6SpanIf
@@ -90,14 +90,8 @@ def apply(erspan):
# We need to re-map the tunnel encapsulation proto to a valid interface class
encap = erspan['encapsulation']
klass = dispatch[encap]
-
- conf = deepcopy(erspan)
-
- conf.update(klass.get_config())
-
- del conf['ifname']
-
- erspan_tunnel = klass(erspan['ifname'],**conf)
+
+ erspan_tunnel = klass(**erspan)
erspan_tunnel.change_options()
erspan_tunnel.update(erspan)
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index e7f0cd6a5..378f400b8 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -30,6 +30,7 @@ from vyos.configverify import verify_mtu
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
+from vyos.ethtool import Ethtool
from vyos.ifconfig import EthernetIf
from vyos.template import render
from vyos.util import call
@@ -76,10 +77,37 @@ def verify(ethernet):
verify_mirror(ethernet)
# verify offloading capabilities
- if 'offload' in ethernet and 'rps' in ethernet['offload']:
+ if dict_search('offload.rps', ethernet) != None:
if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'):
raise ConfigError('Interface does not suport RPS!')
+ driver = EthernetIf(ifname).get_driver_name()
+ # T3342 - Xen driver requires special treatment
+ if driver == 'vif':
+ if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None:
+ raise ConfigError('Xen netback drivers requires scatter-gatter offloading '\
+ 'for MTU size larger then 1500 bytes')
+
+ ethtool = Ethtool(ifname)
+ if 'ring_buffer' in ethernet:
+ max_rx = ethtool.get_rx_buffer()
+ if not max_rx:
+ raise ConfigError('Driver does not support RX ring-buffer configuration!')
+
+ max_tx = ethtool.get_tx_buffer()
+ if not max_tx:
+ raise ConfigError('Driver does not support TX ring-buffer configuration!')
+
+ rx = dict_search('ring_buffer.rx', ethernet)
+ if rx and int(rx) > int(max_rx):
+ raise ConfigError(f'Driver only supports a maximum RX ring-buffer '\
+ f'size of "{max_rx}" bytes!')
+
+ tx = dict_search('ring_buffer.tx', ethernet)
+ if tx and int(tx) > int(max_tx):
+ raise ConfigError(f'Driver only supports a maximum TX ring-buffer '\
+ f'size of "{max_tx}" bytes!')
+
# XDP requires multiple TX queues
if 'xdp' in ethernet:
queues = glob(f'/sys/class/net/{ifname}/queues/tx-*')
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py
index 979a5612e..2a63b60aa 100755
--- a/src/conf_mode/interfaces-geneve.py
+++ b/src/conf_mode/interfaces-geneve.py
@@ -72,18 +72,8 @@ def apply(geneve):
g.remove()
if 'deleted' not in geneve:
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = GeneveIf.get_config()
-
- # Assign GENEVE instance configuration parameters to config dict
- conf['vni'] = geneve['vni']
- conf['remote'] = geneve['remote']
-
# Finally create the new interface
- g = GeneveIf(geneve['ifname'], **conf)
+ g = GeneveIf(**geneve)
g.update(geneve)
return None
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 7b3afa058..9b6ddd5aa 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -34,7 +34,6 @@ airbag.enable()
k_mod = ['l2tp_eth', 'l2tp_netlink', 'l2tp_ip', 'l2tp_ip6']
-
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -65,15 +64,15 @@ def verify(l2tpv3):
interface = l2tpv3['ifname']
- for key in ['local_ip', 'remote_ip', 'tunnel_id', 'peer_tunnel_id',
+ for key in ['source_address', 'remote', 'tunnel_id', 'peer_tunnel_id',
'session_id', 'peer_session_id']:
if key not in l2tpv3:
tmp = key.replace('_', '-')
- raise ConfigError(f'L2TPv3 {tmp} must be configured!')
+ raise ConfigError(f'Missing mandatory L2TPv3 option: "{tmp}"!')
- if not is_addr_assigned(l2tpv3['local_ip']):
- raise ConfigError('L2TPv3 local-ip address '
- '"{local_ip}" is not configured!'.format(**l2tpv3))
+ if not is_addr_assigned(l2tpv3['source_address']):
+ raise ConfigError('L2TPv3 source-address address "{source_address}" '
+ 'not configured on any interface!'.format(**l2tpv3))
verify_mtu_ipv6(l2tpv3)
verify_address(l2tpv3)
@@ -83,34 +82,16 @@ def generate(l2tpv3):
return None
def apply(l2tpv3):
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = L2TPv3If.get_config()
-
# Check if L2TPv3 interface already exists
if l2tpv3['ifname'] in interfaces():
# L2TPv3 is picky when changing tunnels/sessions, thus we can simply
# always delete it first.
- conf['session_id'] = l2tpv3['session_id']
- conf['tunnel_id'] = l2tpv3['tunnel_id']
- l = L2TPv3If(l2tpv3['ifname'], **conf)
+ l = L2TPv3If(**l2tpv3)
l.remove()
if 'deleted' not in l2tpv3:
- conf['peer_tunnel_id'] = l2tpv3['peer_tunnel_id']
- conf['local_port'] = l2tpv3['source_port']
- conf['remote_port'] = l2tpv3['destination_port']
- conf['encapsulation'] = l2tpv3['encapsulation']
- conf['local_address'] = l2tpv3['local_ip']
- conf['remote_address'] = l2tpv3['remote_ip']
- conf['session_id'] = l2tpv3['session_id']
- conf['tunnel_id'] = l2tpv3['tunnel_id']
- conf['peer_session_id'] = l2tpv3['peer_session_id']
-
# Finally create the new interface
- l = L2TPv3If(l2tpv3['ifname'], **conf)
+ l = L2TPv3If(**l2tpv3)
l.update(l2tpv3)
return None
diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py
index bfebed7e4..eab69f36e 100755
--- a/src/conf_mode/interfaces-macsec.py
+++ b/src/conf_mode/interfaces-macsec.py
@@ -115,17 +115,9 @@ def apply(macsec):
os.unlink(wpa_suppl_conf.format(**macsec))
else:
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = MACsecIf.get_config()
- conf['source_interface'] = macsec['source_interface']
- conf['security_cipher'] = macsec['security']['cipher']
-
# It is safe to "re-create" the interface always, there is a sanity
# check that the interface will only be create if its non existent
- i = MACsecIf(macsec['ifname'], **conf)
+ i = MACsecIf(**macsec)
i.update(macsec)
call('systemctl restart wpa_supplicant-macsec@{source_interface}'
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index ee6f05fcd..4afb85526 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -502,10 +502,7 @@ def apply(openvpn):
# existed - nevertheless, spawn new OpenVPN process
call(f'systemctl start openvpn@{interface}.service')
- conf = VTunIf.get_config()
- conf['device_type'] = openvpn['device_type']
-
- o = VTunIf(interface, **conf)
+ o = VTunIf(**openvpn)
o.update(openvpn)
return None
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index ddbef56ac..34a054837 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -75,19 +75,9 @@ def apply(peth):
if 'mode_old' in peth:
MACVLANIf(peth['ifname']).remove()
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = MACVLANIf.get_config()
-
- # Assign MACVLAN instance configuration parameters to config dict
- conf['source_interface'] = peth['source_interface']
- conf['mode'] = peth['mode']
-
# It is safe to "re-create" the interface always, there is a sanity check
# that the interface will only be create if its non existent
- p = MACVLANIf(peth['ifname'], **conf)
+ p = MACVLANIf(**peth)
p.update(peth)
return None
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 034bd6dd1..b63312750 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -31,16 +31,10 @@ from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_vrf
from vyos.configverify import verify_tunnel
from vyos.ifconfig import Interface
-from vyos.ifconfig import GREIf
-from vyos.ifconfig import GRETapIf
-from vyos.ifconfig import IPIPIf
-from vyos.ifconfig import IP6GREIf
-from vyos.ifconfig import IPIP6If
-from vyos.ifconfig import IP6IP6If
-from vyos.ifconfig import SitIf
-from vyos.ifconfig import Sit6RDIf
+from vyos.ifconfig import TunnelIf
from vyos.template import is_ipv4
from vyos.template import is_ipv6
+from vyos.util import get_json_iface_options
from vyos.util import dict_search
from vyos import ConfigError
from vyos import airbag
@@ -79,8 +73,8 @@ def verify(tunnel):
return None
if 'encapsulation' not in tunnel:
- raise ConfigError('Must configure the tunnel encapsulation for '\
- '{ifname}!'.format(**tunnel))
+ error = 'Must configure encapsulation for "{ifname}"!'
+ raise ConfigError(error.format(**tunnel))
verify_mtu_ipv6(tunnel)
verify_address(tunnel)
@@ -103,68 +97,26 @@ def generate(tunnel):
return None
def apply(tunnel):
- if 'deleted' in tunnel or 'encapsulation_changed' in tunnel:
- if tunnel['ifname'] in interfaces():
- tmp = Interface(tunnel['ifname'])
+ interface = tunnel['ifname']
+ # If a gretap tunnel is already existing we can not "simply" change local or
+ # remote addresses. This returns "Operation not supported" by the Kernel.
+ # There is no other solution to destroy and recreate the tunnel.
+ encap = ''
+ remote = ''
+ tmp = get_json_iface_options(interface)
+ if tmp:
+ encap = dict_search('linkinfo.info_kind', tmp)
+ remote = dict_search('linkinfo.info_data.remote', tmp)
+
+ if ('deleted' in tunnel or 'encapsulation_changed' in tunnel or
+ encap in ['gretap', 'ip6gretap'] or remote in ['any']):
+ if interface in interfaces():
+ tmp = Interface(interface)
tmp.remove()
if 'deleted' in tunnel:
return None
- dispatch = {
- 'gre': GREIf,
- 'gre-bridge': GRETapIf,
- 'ipip': IPIPIf,
- 'ipip6': IPIP6If,
- 'ip6ip6': IP6IP6If,
- 'ip6gre': IP6GREIf,
- 'sit': SitIf,
- }
-
- # We need to re-map the tunnel encapsulation proto to a valid interface class
- encap = tunnel['encapsulation']
- klass = dispatch[encap]
-
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = klass.get_config()
-
- # Copy/re-assign our dictionary values to values understood by the
- # derived _Tunnel classes
- mapping = {
- # this : get_config()
- 'local_ip' : 'local',
- 'remote_ip' : 'remote',
- 'source_interface' : 'dev',
- 'parameters.ip.ttl' : 'ttl',
- 'parameters.ip.tos' : 'tos',
- 'parameters.ip.key' : 'key',
- 'parameters.ipv6.encaplimit' : 'encaplimit'
- }
-
- # Add additional IPv6 options if tunnel is IPv6 aware
- if tunnel['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
- mappingv6 = {
- # this : get_config()
- 'parameters.ipv6.encaplimit' : 'encaplimit',
- 'parameters.ipv6.flowlabel' : 'flowlabel',
- 'parameters.ipv6.hoplimit' : 'hoplimit',
- 'parameters.ipv6.tclass' : 'flowlabel'
- }
- mapping.update(mappingv6)
-
- for our_key, their_key in mapping.items():
- if dict_search(our_key, tunnel) and their_key in conf:
- conf[their_key] = dict_search(our_key, tunnel)
-
- if dict_search('parameters.ip.no_pmtu_discovery', tunnel) != None:
- if 'pmtudisc' in conf['raw']:
- conf['raw'].remove('pmtudisc')
- conf['raw'].append('nopmtudisc')
-
- tun = klass(tunnel['ifname'], **conf)
- tun.change_options()
+ tun = TunnelIf(**tunnel)
tun.update(tunnel)
return None
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 9a6d72772..8e6247a30 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -90,19 +90,8 @@ def apply(vxlan):
v.remove()
if 'deleted' not in vxlan:
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = VXLANIf.get_config()
-
- # Assign VXLAN instance configuration parameters to config dict
- for tmp in ['vni', 'group', 'source_address', 'source_interface', 'remote', 'port']:
- if tmp in vxlan:
- conf[tmp] = vxlan[tmp]
-
# Finally create the new interface
- v = VXLANIf(vxlan['ifname'], **conf)
+ v = VXLANIf(**vxlan)
v.update(vxlan)
return None
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index b25fcd4e0..7b3de6e8a 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -255,17 +255,8 @@ def apply(wifi):
if 'deleted' in wifi:
WiFiIf(interface).remove()
else:
- # This is a special type of interface which needs additional parameters
- # when created using iproute2. Instead of passing a ton of arguments,
- # use a dictionary provided by the interface class which holds all the
- # options necessary.
- conf = WiFiIf.get_config()
-
- # Assign WiFi instance configuration parameters to config dict
- conf['phy'] = wifi['physical_device']
-
# Finally create the new interface
- w = WiFiIf(interface, **conf)
+ w = WiFiIf(**wifi)
w.update(wifi)
# Enable/Disable interface - interface is always placed in
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index d1e551cad..a43eed504 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 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
@@ -17,191 +17,97 @@
import os
from sys import exit
-from copy import deepcopy
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.template import is_ipv6
-from vyos.template import render
+from vyos.template import render_to_string
from vyos.util import call
from vyos.validate import is_ipv6_link_local
+from vyos.xml import defaults
from vyos import ConfigError
+from vyos import frr
from vyos import airbag
airbag.enable()
-config_file = r'/tmp/bfd.frr'
-
-default_config_data = {
- 'new_peers': [],
- 'old_peers' : []
-}
-
-# get configuration for BFD peer from proposed or effective configuration
-def get_bfd_peer_config(peer, conf_mode="proposed"):
- conf = Config()
- conf.set_level('protocols bfd peer {0}'.format(peer))
-
- bfd_peer = {
- 'remote': peer,
- 'shutdown': False,
- 'src_if': '',
- 'src_addr': '',
- 'multiplier': '3',
- 'rx_interval': '300',
- 'tx_interval': '300',
- 'multihop': False,
- 'echo_interval': '',
- 'echo_mode': False,
- }
-
- # Check if individual peer is disabled
- if conf_mode == "effective" and conf.exists_effective('shutdown'):
- bfd_peer['shutdown'] = True
- if conf_mode == "proposed" and conf.exists('shutdown'):
- bfd_peer['shutdown'] = True
-
- # Check if peer has a local source interface configured
- if conf_mode == "effective" and conf.exists_effective('source interface'):
- bfd_peer['src_if'] = conf.return_effective_value('source interface')
- if conf_mode == "proposed" and conf.exists('source interface'):
- bfd_peer['src_if'] = conf.return_value('source interface')
-
- # Check if peer has a local source address configured - this is mandatory for IPv6
- if conf_mode == "effective" and conf.exists_effective('source address'):
- bfd_peer['src_addr'] = conf.return_effective_value('source address')
- if conf_mode == "proposed" and conf.exists('source address'):
- bfd_peer['src_addr'] = conf.return_value('source address')
-
- # Tell BFD daemon that we should expect packets with TTL less than 254
- # (because it will take more than one hop) and to listen on the multihop
- # port (4784)
- if conf_mode == "effective" and conf.exists_effective('multihop'):
- bfd_peer['multihop'] = True
- if conf_mode == "proposed" and conf.exists('multihop'):
- bfd_peer['multihop'] = True
-
- # Configures the minimum interval that this system is capable of receiving
- # control packets. The default value is 300 milliseconds.
- if conf_mode == "effective" and conf.exists_effective('interval receive'):
- bfd_peer['rx_interval'] = conf.return_effective_value('interval receive')
- if conf_mode == "proposed" and conf.exists('interval receive'):
- bfd_peer['rx_interval'] = conf.return_value('interval receive')
-
- # The minimum transmission interval (less jitter) that this system wants
- # to use to send BFD control packets.
- if conf_mode == "effective" and conf.exists_effective('interval transmit'):
- bfd_peer['tx_interval'] = conf.return_effective_value('interval transmit')
- if conf_mode == "proposed" and conf.exists('interval transmit'):
- bfd_peer['tx_interval'] = conf.return_value('interval transmit')
-
- # Configures the detection multiplier to determine packet loss. The remote
- # transmission interval will be multiplied by this value to determine the
- # connection loss detection timer. The default value is 3.
- if conf_mode == "effective" and conf.exists_effective('interval multiplier'):
- bfd_peer['multiplier'] = conf.return_effective_value('interval multiplier')
- if conf_mode == "proposed" and conf.exists('interval multiplier'):
- bfd_peer['multiplier'] = conf.return_value('interval multiplier')
-
- # Configures the minimal echo receive transmission interval that this system is capable of handling
- if conf_mode == "effective" and conf.exists_effective('interval echo-interval'):
- bfd_peer['echo_interval'] = conf.return_effective_value('interval echo-interval')
- if conf_mode == "proposed" and conf.exists('interval echo-interval'):
- bfd_peer['echo_interval'] = conf.return_value('interval echo-interval')
-
- # Enables or disables the echo transmission mode
- if conf_mode == "effective" and conf.exists_effective('echo-mode'):
- bfd_peer['echo_mode'] = True
- if conf_mode == "proposed" and conf.exists('echo-mode'):
- bfd_peer['echo_mode'] = True
-
- return bfd_peer
-
-def get_config():
- bfd = deepcopy(default_config_data)
- conf = Config()
- if not (conf.exists('protocols bfd') or conf.exists_effective('protocols bfd')):
- return None
+def get_config(config=None):
+ if config:
+ conf = config
else:
- conf.set_level('protocols bfd')
-
- # as we have to use vtysh to talk to FRR we also need to know
- # which peers are gone due to a config removal - thus we read in
- # all peers (active or to delete)
- for peer in conf.list_effective_nodes('peer'):
- bfd['old_peers'].append(get_bfd_peer_config(peer, "effective"))
-
- for peer in conf.list_nodes('peer'):
- bfd['new_peers'].append(get_bfd_peer_config(peer))
-
- # find deleted peers
- set_new_peers = set(conf.list_nodes('peer'))
- set_old_peers = set(conf.list_effective_nodes('peer'))
- bfd['deleted_peers'] = set_old_peers - set_new_peers
+ conf = Config()
+ base = ['protocols', 'bfd']
+ bfd = conf.get_config_dict(base, get_first_key=True)
+
+ # Bail out early if configuration tree does not exist
+ if not conf.exists(base):
+ return bfd
+
+ # We have gathered the dict representation of the CLI, but there are
+ # default options which we need to update into the dictionary retrived.
+ # XXX: T2665: we currently have no nice way for defaults under tag
+ # nodes, thus we load the defaults "by hand"
+ default_values = defaults(base + ['peer'])
+ if 'peer' in bfd:
+ for peer in bfd['peer']:
+ bfd['peer'][peer] = dict_merge(default_values, bfd['peer'][peer])
+
+ if 'profile' in bfd:
+ for profile in bfd['profile']:
+ bfd['profile'][profile] = dict_merge(default_values, bfd['profile'][profile])
return bfd
def verify(bfd):
- if bfd is None:
+ if not bfd:
return None
- # some variables to use later
- conf = Config()
-
- for peer in bfd['new_peers']:
- # IPv6 link local peers require an explicit local address/interface
- if is_ipv6_link_local(peer['remote']):
- if not (peer['src_if'] and peer['src_addr']):
- raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
-
- # IPv6 peers require an explicit local address
- if is_ipv6(peer['remote']):
- if not peer['src_addr']:
- raise ConfigError('BFD IPv6 peers require explicit local address setting')
-
- # multihop require source address
- if peer['multihop'] and not peer['src_addr']:
- raise ConfigError('Multihop require source address')
-
- # multihop and echo-mode cannot be used together
- if peer['multihop'] and peer['echo_mode']:
- raise ConfigError('Multihop and echo-mode cannot be used together')
-
- # multihop doesn't accept interface names
- if peer['multihop'] and peer['src_if']:
- raise ConfigError('Multihop and source interface cannot be used together')
-
- # echo interval can be configured only with enabled echo-mode
- if peer['echo_interval'] != '' and not peer['echo_mode']:
- raise ConfigError('echo-interval can be configured only with enabled echo-mode')
-
- # check if we deleted peers are not used in configuration
- if conf.exists('protocols bgp'):
- bgp_as = conf.list_nodes('protocols bgp')[0]
-
- # check BGP neighbors
- for peer in bfd['deleted_peers']:
- if conf.exists('protocols bgp {0} neighbor {1} bfd'.format(bgp_as, peer)):
- raise ConfigError('Cannot delete BFD peer {0}: it is used in BGP configuration'.format(peer))
- if conf.exists('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer)):
- peer_group = conf.return_value('protocols bgp {0} neighbor {1} peer-group'.format(bgp_as, peer))
- if conf.exists('protocols bgp {0} peer-group {1} bfd'.format(bgp_as, peer_group)):
- raise ConfigError('Cannot delete BFD peer {0}: it belongs to BGP peer-group {1} with enabled BFD'.format(peer, peer_group))
+ if 'peer' in bfd:
+ for peer, peer_config in bfd['peer'].items():
+ # IPv6 link local peers require an explicit local address/interface
+ if is_ipv6_link_local(peer):
+ if 'source' not in peer_config or len(peer_config['source'] < 2):
+ raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
+
+ # IPv6 peers require an explicit local address
+ if is_ipv6(peer):
+ if 'source' not in peer_config or 'address' not in peer_config['source']:
+ raise ConfigError('BFD IPv6 peers require explicit local address setting')
+
+ if 'multihop' in peer_config:
+ # multihop require source address
+ if 'source' not in peer_config or 'address' not in peer_config['source']:
+ raise ConfigError('BFD multihop require source address')
+
+ # multihop and echo-mode cannot be used together
+ if 'echo_mode' in peer_config:
+ raise ConfigError('Multihop and echo-mode cannot be used together')
+
+ # multihop doesn't accept interface names
+ if 'source' in peer_config and 'interface' in peer_config['source']:
+ raise ConfigError('Multihop and source interface cannot be used together')
return None
def generate(bfd):
- if bfd is None:
+ if not bfd:
+ bfd['new_frr_config'] = ''
return None
- render(config_file, 'frr/bfd.frr.tmpl', bfd)
- return None
+ bfd['new_frr_config'] = render_to_string('frr/bfd.frr.tmpl', bfd)
def apply(bfd):
- if bfd is None:
- return None
-
- call("vtysh -d bfdd -f " + config_file)
- if os.path.exists(config_file):
- os.remove(config_file)
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+ frr_cfg.load_configuration()
+ frr_cfg.modify_section('^bfd', '')
+ frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bfd['new_frr_config'])
+ frr_cfg.commit_configuration()
+
+ # If FRR config is blank, rerun the blank commit x times due to frr-reload
+ # behavior/bug not properly clearing out on one commit.
+ if bfd['new_frr_config'] == '':
+ for a in range(5):
+ frr_cfg.commit_configuration()
return None
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 54352460c..7dede74a1 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -20,9 +20,11 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
+from vyos.template import is_ip
from vyos.template import render_to_string
from vyos.util import call
from vyos.util import dict_search
+from vyos.validate import is_addr_assigned
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -54,6 +56,26 @@ def get_config(config=None):
return bgp
+def verify_remote_as(peer_config, asn_config):
+ if 'remote_as' in peer_config:
+ return peer_config['remote_as']
+
+ if 'peer_group' in peer_config:
+ peer_group_name = peer_config['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ if 'interface' in peer_config:
+ if 'remote_as' in peer_config['interface']:
+ return peer_config['interface']['remote_as']
+
+ if 'peer_group' in peer_config['interface']:
+ peer_group_name = peer_config['interface']['peer_group']
+ tmp = dict_search(f'peer_group.{peer_group_name}.remote_as', asn_config)
+ if tmp: return tmp
+
+ return None
+
def verify(bgp):
if not bgp:
return None
@@ -75,22 +97,29 @@ def verify(bgp):
# Check if the configure peer-group exists
if 'peer_group' in peer_config:
peer_group = peer_config['peer_group']
- if peer_group not in asn_config['peer_group']:
+ if 'peer_group' not in asn_config or peer_group not in asn_config['peer_group']:
raise ConfigError(f'Specified peer-group "{peer_group}" for '\
f'neighbor "{neighbor}" does not exist!')
- # Some checks can/must only be done on a neighbor and nor a peer-group
+ # ttl-security and ebgp-multihop can't be used in the same configration
+ if 'ebgp_multihop' in peer_config and 'ttl_security' in peer_config:
+ raise ConfigError('You can\'t set both ebgp-multihop and ttl-security hops')
+
+ # Check spaces in the password
+ if 'password' in peer_config and ' ' in peer_config['password']:
+ raise ConfigError('You can\'t use spaces in the password')
+
+ # Some checks can/must only be done on a neighbor and not a peer-group
if neighbor == 'neighbor':
# remote-as must be either set explicitly for the neighbor
# or for the entire peer-group
- if 'interface' in peer_config:
- if 'remote_as' not in peer_config['interface']:
- if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+ if not verify_remote_as(peer_config, asn_config):
+ raise ConfigError(f'Neighbor "{peer}" remote-as must be set!')
- elif 'remote_as' not in peer_config:
- if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]:
- raise ConfigError('Remote AS must be set for neighbor or peer-group!')
+ # Only checks for ipv4 and ipv6 neighbors
+ # Check if neighbor address is assigned as system interface address
+ if is_ip(peer) and is_addr_assigned(peer):
+ raise ConfigError(f'Can\'t configure local address as neighbor "{peer}"')
for afi in ['ipv4_unicast', 'ipv6_unicast', 'l2vpn_evpn']:
# Bail out early if address family is not configured
@@ -127,10 +156,10 @@ def verify(bgp):
if 'remote_as' in peer_config and asn != peer_config['remote_as']:
raise ConfigError('route-reflector-client only supported for iBGP peers')
else:
- peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config)
- if 'peer_group' in peer_config and peer_group_as != None and peer_group_as != asn:
- raise ConfigError('route-reflector-client only supported for iBGP peers')
-
+ if 'peer_group' in peer_config:
+ peer_group_as = dict_search(f'peer_group.{peer_group}.remote_as', asn_config)
+ if peer_group_as != None and peer_group_as != asn:
+ raise ConfigError('route-reflector-client only supported for iBGP peers')
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', asn_config) or []:
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 2ce0ab530..aefe7c23e 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -60,7 +60,7 @@ def get_config(config=None):
del default_values['area']['area_type']['nssa']
if 'mpls_te' not in ospf:
del default_values['mpls_te']
- for protocol in ['bgp', 'connected', 'kernel', 'rip', 'static']:
+ for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static']:
if dict_search(f'redistribute.{protocol}', ospf) is None:
del default_values['redistribute'][protocol]
# XXX: T2665: we currently have no nice way for defaults under tag nodes,
@@ -137,7 +137,7 @@ def apply(ospf):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section(r'interface \S+', '')
+ frr_cfg.modify_section(r'^interface \S+', '')
frr_cfg.modify_section('^router ospf$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config'])
frr_cfg.commit_configuration(frr_daemon)
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index 6c3aaf426..6f068b196 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -23,6 +23,7 @@ from vyos.configdict import dict_merge
from vyos.configverify import verify_route_maps
from vyos.template import render_to_string
from vyos.util import call
+from vyos.ifconfig import Interface
from vyos.xml import defaults
from vyos import ConfigError
from vyos import frr
@@ -57,6 +58,14 @@ def verify(ospfv3):
return None
verify_route_maps(ospfv3)
+
+ if 'interface' in ospfv3:
+ for ifname, if_config in ospfv3['interface'].items():
+ if 'ifmtu' in if_config:
+ mtu = Interface(ifname).get_mtu()
+ if int(if_config['ifmtu']) > int(mtu):
+ raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"')
+
return None
def generate(ospfv3):
@@ -71,7 +80,8 @@ def apply(ospfv3):
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
frr_cfg.load_configuration(frr_daemon)
- frr_cfg.modify_section('router ospf6', '')
+ frr_cfg.modify_section(r'^interface \S+', '')
+ frr_cfg.modify_section('^router ospf6$', '')
frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config'])
frr_cfg.commit_configuration(frr_daemon)
diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py
index 0e5fc75b0..6e94a19ae 100755
--- a/src/conf_mode/service_console-server.py
+++ b/src/conf_mode/service_console-server.py
@@ -25,7 +25,8 @@ from vyos.util import call
from vyos.xml import defaults
from vyos import ConfigError
-config_file = r'/run/conserver/conserver.cf'
+config_file = '/run/conserver/conserver.cf'
+dropbear_systemd_file = '/etc/systemd/system/dropbear@{port}.service.d/override.conf'
def get_config(config=None):
if config:
@@ -75,9 +76,22 @@ def generate(proxy):
return None
render(config_file, 'conserver/conserver.conf.tmpl', proxy)
+ if 'device' in proxy:
+ for device in proxy['device']:
+ if 'ssh' not in proxy['device'][device]:
+ continue
+
+ tmp = {
+ 'device' : device,
+ 'port' : proxy['device'][device]['ssh']['port'],
+ }
+ render(dropbear_systemd_file.format(**tmp),
+ 'conserver/dropbear@.service.tmpl', tmp)
+
return None
def apply(proxy):
+ call('systemctl daemon-reload')
call('systemctl stop dropbear@*.service conserver-server.service')
if not proxy:
@@ -89,9 +103,10 @@ def apply(proxy):
if 'device' in proxy:
for device in proxy['device']:
- if 'ssh' in proxy['device'][device]:
- port = proxy['device'][device]['ssh']['port']
- call(f'systemctl restart dropbear@{device}.service')
+ if 'ssh' not in proxy['device'][device]:
+ continue
+ port = proxy['device'][device]['ssh']['port']
+ call(f'systemctl restart dropbear@{port}.service')
return None
diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py
index 8dfae348a..cbbd2e0bc 100755
--- a/src/conf_mode/service_webproxy.py
+++ b/src/conf_mode/service_webproxy.py
@@ -123,9 +123,6 @@ def verify(proxy):
ldap_auth = dict_search('authentication.method', proxy) == 'ldap'
for address, config in proxy['listen_address'].items():
- if not is_addr_assigned(address):
- raise ConfigError(
- f'listen-address "{address}" not assigned on any interface!')
if ldap_auth and 'disable_transparent' not in config:
raise ConfigError('Authentication can not be configured when ' \
'proxy is in transparent mode')
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py
index 4510dd3e7..680a80859 100755
--- a/src/conf_mode/vrrp.py
+++ b/src/conf_mode/vrrp.py
@@ -75,6 +75,7 @@ def get_config(config=None):
group["backup_script"] = config.return_value("transition-script backup")
group["fault_script"] = config.return_value("transition-script fault")
group["stop_script"] = config.return_value("transition-script stop")
+ group["script_mode_force"] = config.exists("transition-script mode-force")
if config.exists("no-preempt"):
group["preempt"] = False
@@ -183,6 +184,11 @@ def verify(data):
if isinstance(pa, IPv4Address):
raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"]))
+ # Warn the user about the deprecated mode-force option
+ if group['script_mode_force']:
+ print("""Warning: "transition-script mode-force" VRRP option is deprecated and will be removed in VyOS 1.4.""")
+ print("""It's no longer necessary, so you can safely remove it from your config now.""")
+
# Disallow same VRID on multiple interfaces
_groups = sorted(vrrp_groups, key=(lambda x: x["interface"]))
count = len(_groups) - 1
diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20
new file mode 100755
index 000000000..e96663e54
--- /dev/null
+++ b/src/migration-scripts/interfaces/19-to-20
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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/>.
+
+from sys import argv
+from sys import exit
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = argv[1]
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+
+ for type in ['tunnel', 'l2tpv3']:
+ base = ['interfaces', type]
+ if not config.exists(base):
+ # Nothing to do
+ continue
+
+ for interface in config.list_nodes(base):
+ # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap
+ encap_path = base + [interface, 'encapsulation']
+ if type == 'tunnel' and config.exists(encap_path):
+ tmp = config.return_value(encap_path)
+ if tmp == 'gre-bridge':
+ config.set(encap_path, value='gretap')
+
+ # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address
+ # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote
+ local_ip_path = base + [interface, 'local-ip']
+ if config.exists(local_ip_path):
+ config.rename(local_ip_path, 'source-address')
+
+ remote_ip_path = base + [interface, 'remote-ip']
+ if config.exists(remote_ip_path):
+ config.rename(remote_ip_path, 'remote')
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/nat/4-to-5 b/src/migration-scripts/nat/4-to-5
index dda191719..b791996e2 100755
--- a/src/migration-scripts/nat/4-to-5
+++ b/src/migration-scripts/nat/4-to-5
@@ -36,9 +36,15 @@ if not config.exists(['nat']):
exit(0)
else:
for direction in ['source', 'destination']:
+ # If a node doesn't exist, we obviously have nothing to do.
if not config.exists(['nat', direction]):
continue
+ # However, we also need to handle the case when a 'source' or 'destination' sub-node does exist,
+ # but there are no rules under it.
+ if not config.list_nodes(['nat', direction]):
+ continue
+
for rule in config.list_nodes(['nat', direction, 'rule']):
base = ['nat', direction, 'rule', rule]
diff --git a/src/migration-scripts/quagga/6-to-7 b/src/migration-scripts/quagga/6-to-7
index f7aca0d2b..25cf5eebd 100755
--- a/src/migration-scripts/quagga/6-to-7
+++ b/src/migration-scripts/quagga/6-to-7
@@ -17,14 +17,17 @@
# - T3037, BGP address-family ipv6-unicast capability dynamic does not exist in
# FRR, there is only a base, per neighbor dynamic capability, migrate config
-import sys
+from sys import argv
+from sys import exit
from vyos.configtree import ConfigTree
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
-if (len(sys.argv) < 2):
+if (len(argv) < 2):
print("Must specify file name!")
- sys.exit(1)
+ exit(1)
-file_name = sys.argv[1]
+file_name = argv[1]
with open(file_name, 'r') as f:
config_file = f.read()
@@ -34,7 +37,7 @@ config = ConfigTree(config_file)
if not config.exists(base):
# Nothing to do
- sys.exit(0)
+ exit(0)
# Check if BGP is actually configured and obtain the ASN
asn_list = config.list_nodes(base)
@@ -55,30 +58,59 @@ if asn_list:
config.delete(send_comm_path)
cap_dynamic = False
+ peer_group = None
for afi in ['ipv4-unicast', 'ipv6-unicast']:
- afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi, 'capability', 'dynamic']
- if config.exists(afi_path):
+ afi_path = bgp_base + [neighbor_type, neighbor, 'address-family', afi]
+ # Exit loop early if AFI does not exist
+ if not config.exists(afi_path):
+ continue
+
+ cap_path = afi_path + ['capability', 'dynamic']
+ if config.exists(cap_path):
cap_dynamic = True
- config.delete(afi_path)
+ config.delete(cap_path)
+
+ # We have now successfully migrated the address-family
+ # specific dynamic capability to the neighbor/peer-group
+ # level. If this has been the only option under the
+ # address-family nodes, we can clean them up by checking if
+ # no other nodes are left under that tree and if so, delete
+ # the parent.
+ #
+ # We walk from the most inner node to the most outer one.
+ cleanup = -1
+ while len(config.list_nodes(cap_path[:cleanup])) == 0:
+ config.delete(cap_path[:cleanup])
+ cleanup -= 1
+
+ peer_group_path = afi_path + ['peer-group']
+ if config.exists(peer_group_path):
+ if ((is_ipv4(neighbor) and afi == 'ipv4-unicast') or
+ (is_ipv6(neighbor) and afi == 'ipv6-unicast')):
+ peer_group = config.return_value(peer_group_path)
+
+ config.delete(peer_group_path)
- # We have now successfully migrated the address-family specific
- # dynamic capability to the neighbor/peer-group level. If this
- # has been the only option under the address-family nodes, we
- # can clean them up by checking if no other nodes are left under
- # that tree and if so, delete the parent.
+ # We have now successfully migrated the address-family
+ # specific peer-group to the neighbor level. If this has
+ # been the only option under the address-family nodes, we
+ # can clean them up by checking if no other nodes are left
+ # under that tree and if so, delete the parent.
#
# We walk from the most inner node to the most outer one.
cleanup = -1
- while len(config.list_nodes(afi_path[:cleanup])) == 0:
- config.delete(afi_path[:cleanup])
+ while len(config.list_nodes(peer_group_path[:cleanup])) == 0:
+ config.delete(peer_group_path[:cleanup])
cleanup -= 1
if cap_dynamic:
config.set(bgp_base + [neighbor_type, neighbor, 'capability', 'dynamic'])
+ if peer_group:
+ config.set(bgp_base + [neighbor_type, neighbor, 'peer-group'], value=peer_group)
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
except OSError as e:
print("Failed to save the modified config: {}".format(e))
- sys.exit(1)
+ exit(1)
diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py
index 256c86d2a..5375a8406 100755
--- a/src/op_mode/show_interfaces.py
+++ b/src/op_mode/show_interfaces.py
@@ -71,10 +71,7 @@ def filtered_interfaces(ifnames, iftypes, vif, vrrp):
if ifnames and ifname not in ifnames:
continue
- # return the class which can handle this interface name
- klass = Section.klass(ifname)
- # connect to the interface
- interface = klass(ifname, create=False, debug=False)
+ interface = Interface(ifname)
if iftypes and interface.definition['section'] not in iftypes:
continue
diff --git a/src/op_mode/vtysh_wrapper.sh b/src/op_mode/vtysh_wrapper.sh
new file mode 100755
index 000000000..47d88330b
--- /dev/null
+++ b/src/op_mode/vtysh_wrapper.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+declare -a tmp
+tmp=$@
+vtysh -c "$tmp"
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 5b1ab1f1f..1e60e53df 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -25,6 +25,7 @@ import logging
import signal
import importlib.util
import zmq
+from contextlib import redirect_stdout, redirect_stderr
from vyos.defaults import directories
from vyos.configsource import ConfigSourceString, ConfigSourceError
@@ -33,6 +34,8 @@ from vyos import ConfigError
CFG_GROUP = 'vyattacfg'
+script_stdout_log = '/tmp/vyos-configd-script-stdout'
+
debug = True
logger = logging.getLogger(__name__)
@@ -59,7 +62,8 @@ configd_env_unset_file = os.path.join(directories['data'], 'vyos-configd-env-uns
# sourced on entering config session
configd_env_file = '/etc/default/vyos-configd-env'
-session_tty = None
+session_out = None
+session_mode = None
def key_name_from_file_name(f):
return os.path.splitext(f)[0]
@@ -104,33 +108,30 @@ conf_mode_scripts = dict(zip(imports, modules))
exclude_set = {key_name_from_file_name(f) for f in filenames if f not in include}
include_set = {key_name_from_file_name(f) for f in filenames if f in include}
-def explicit_print(t, m):
- try:
- with open(t, 'w') as f:
- f.write(m)
- f.write("\n")
- f.flush()
- except Exception:
- pass
def run_script(script, config) -> int:
config.set_level([])
try:
- c = script.get_config(config)
- script.verify(c)
- script.generate(c)
- script.apply(c)
+ with open(session_out, session_mode) as f, redirect_stdout(f):
+ with redirect_stderr(f):
+ c = script.get_config(config)
+ script.verify(c)
+ script.generate(c)
+ script.apply(c)
except ConfigError as e:
logger.critical(e)
- explicit_print(session_tty, str(e))
+ with open(session_out, session_mode) as f, redirect_stdout(f):
+ print(f"{e}\n")
return R_ERROR_COMMIT
- except Exception:
+ except Exception as e:
+ logger.critical(e)
return R_ERROR_DAEMON
return R_SUCCESS
def initialization(socket):
- global session_tty
+ global session_out
+ global session_mode
# Reset config strings:
active_string = ''
session_string = ''
@@ -158,9 +159,15 @@ def initialization(socket):
logger.debug(f"config session pid is {pid_string}")
try:
- session_tty = os.readlink(f"/proc/{pid_string}/fd/1")
+ session_out = os.readlink(f"/proc/{pid_string}/fd/1")
+ session_mode = 'w'
except FileNotFoundError:
- session_tty = None
+ session_out = None
+
+ # if not a 'live' session, for example on boot, write to file
+ if not session_out or '/dev/pts' not in session_out:
+ session_out = script_stdout_log
+ session_mode = 'a'
try:
configsource = ConfigSourceString(running_config_text=active_string,
diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh
index a062dc810..49e53d7e1 100755
--- a/src/system/on-dhcp-event.sh
+++ b/src/system/on-dhcp-event.sh
@@ -21,21 +21,20 @@ client_mac=$4
domain=$5
hostsd_client="/usr/bin/vyos-hostsd-client"
-if [ -z "$client_name" ]; then
- logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead"
- client_name=$(echo "client-"$client_mac | tr : -)
-fi
-
-if [ "$domain" == "..YYZ!" ]; then
- client_fqdn_name=$client_name
- client_search_expr=$client_name
-else
- client_fqdn_name=$client_name.$domain
- client_search_expr="$client_name\\.$domain"
-fi
-
case "$action" in
commit) # add mapping for new lease
+ if [ -z "$client_name" ]; then
+ logger -s -t on-dhcp-event "Client name was empty, using MAC \"$client_mac\" instead"
+ client_name=$(echo "client-"$client_mac | tr : -)
+ fi
+
+ if [ "$domain" == "..YYZ!" ]; then
+ client_fqdn_name=$client_name
+ client_search_expr=$client_name
+ else
+ client_fqdn_name=$client_name.$domain
+ client_search_expr="$client_name\\.$domain"
+ fi
$hostsd_client --add-hosts "$client_fqdn_name,$client_ip" --tag "dhcp-server-$client_ip" --apply
exit 0
;;
diff --git a/src/systemd/dropbear@.service b/src/systemd/dropbear@.service
index a3fde5708..acf926af9 100644
--- a/src/systemd/dropbear@.service
+++ b/src/systemd/dropbear@.service
@@ -8,9 +8,8 @@ StartLimitIntervalSec=0
[Service]
Type=forking
-ExecStartPre=/usr/bin/bash -c '/usr/bin/systemctl set-environment PORT=$(cli-shell-api returnActiveValue service console-server device "%I" ssh port)'
-ExecStart=-/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console %I" -P /run/conserver/dropbear.%I.pid -p ${PORT}
-PIDFile=/run/conserver/dropbear.%I.pid
+ExecStart=/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -P /run/dropbear/dropbear.%I.pid -p %I
+PIDFile=/run/dropbear/dropbear.%I.pid
KillMode=process
Restart=always
RestartSec=10
diff --git a/src/tests/test_util.py b/src/tests/test_util.py
index f7405cbde..22bc085c5 100644
--- a/src/tests/test_util.py
+++ b/src/tests/test_util.py
@@ -17,11 +17,7 @@
from unittest import TestCase
from vyos.util import mangle_dict_keys
-
class TestVyOSUtil(TestCase):
- def setUp(self):
- pass
-
def test_key_mangline(self):
data = {"foo-bar": {"baz-quux": None}}
expected_data = {"foo_bar": {"baz_quux": None}}
diff --git a/src/validators/fqdn b/src/validators/fqdn
index 66276c093..a4027e4ca 100755
--- a/src/validators/fqdn
+++ b/src/validators/fqdn
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2021 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
@@ -17,8 +17,7 @@
import re
import sys
-# pattern copied from: https://www.regextester.com/103452
-pattern = "(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)"
+pattern = '[A-Za-z0-9][-.A-Za-z0-9]*'
if __name__ == '__main__':
if len(sys.argv) != 2:
diff --git a/src/validators/interface-name b/src/validators/interface-name
index 32cd42fbd..72e9fd54a 100755
--- a/src/validators/interface-name
+++ b/src/validators/interface-name
@@ -17,7 +17,7 @@
import re
import sys
-pattern = '^(br|bond|dum|en|eth|gnv|peth|pppoe|tun|vti|vtun|vxlan|wg|wlan)[0-9]+|lo$'
+pattern = '^(bond|br|dum|en|ersp|eth|gnv|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|vti|vtun|vxlan|wg|wlan|wlm)[0-9]+(.\d+)?|lo$'
if __name__ == '__main__':
if len(sys.argv) != 2: