summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py358
-rwxr-xr-xsrc/conf_mode/http-api.py37
-rwxr-xr-xsrc/conf_mode/https.py5
-rwxr-xr-xsrc/conf_mode/protocols_ospfv3.py36
-rw-r--r--src/etc/systemd/system/uacctd.service.d/override.conf14
-rwxr-xr-xsrc/migration-scripts/flow-accounting/0-to-169
-rwxr-xr-xsrc/op_mode/conntrack_sync.py45
-rw-r--r--src/systemd/vyos-http-api.service21
8 files changed, 316 insertions, 269 deletions
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index daad00067..e01f3066b 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -16,65 +16,30 @@
import os
import re
+
from sys import exit
import ipaddress
from ipaddress import ip_address
-from jinja2 import FileSystemLoader, Environment
+from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos.ifconfig import Section
from vyos.ifconfig import Interface
-from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import cmd
from vyos.template import render
-
+from vyos.util import cmd
+from vyos.validate import is_addr_assigned
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-# default values
-default_sflow_server_port = 6343
-default_netflow_server_port = 2055
-default_plugin_pipe_size = 10
-default_captured_packet_size = 128
-default_netflow_version = '9'
-default_sflow_agentip = 'auto'
-uacctd_conf_path = '/etc/pmacct/uacctd.conf'
+uacctd_conf_path = '/run/pmacct/uacctd.conf'
iptables_nflog_table = 'raw'
iptables_nflog_chain = 'VYATTA_CT_PREROUTING_HOOK'
egress_iptables_nflog_table = 'mangle'
egress_iptables_nflog_chain = 'FORWARD'
-# helper functions
-# check if node exists and return True if this is true
-def _node_exists(path):
- vyos_config = Config()
- if vyos_config.exists(path):
- return True
-
-# get sFlow agent-ip if agent-address is "auto" (default behaviour)
-def _sflow_default_agentip(config):
- # check if any of BGP, OSPF, OSPFv3 protocols are configured and use router-id from there
- if config.exists('protocols bgp'):
- bgp_router_id = config.return_value("protocols bgp {} parameters router-id".format(config.list_nodes('protocols bgp')[0]))
- if bgp_router_id:
- return bgp_router_id
- if config.return_value('protocols ospf parameters router-id'):
- return config.return_value('protocols ospf parameters router-id')
- if config.return_value('protocols ospfv3 parameters router-id'):
- return config.return_value('protocols ospfv3 parameters router-id')
-
- # if router-id was not found, use first available ip of any interface
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # return an IP, if this is not loopback
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- return regex_filter.search(address).group('ipaddr')
-
- # return nothing by default
- return None
-
# get iptables rule dict for chain in table
def _iptables_get_nflog(chain, table):
# define list with rules
@@ -99,7 +64,7 @@ def _iptables_get_nflog(chain, table):
return rules
# modify iptables rules
-def _iptables_config(configured_ifaces, direction):
+def _iptables_config(configured_ifaces, direction, length=None):
# define list of iptables commands to modify settings
iptable_commands = []
iptables_chain = iptables_nflog_chain
@@ -146,7 +111,7 @@ def _iptables_config(configured_ifaces, direction):
if direction == "egress":
iptables_op = "-o"
- rule_definition = f'{iptables_chain} {iptables_op} {iface} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size {default_captured_packet_size} --nflog-threshold 100'
+ rule_definition = f'{iptables_chain} {iptables_op} {iface} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size {length} --nflog-threshold 100'
iptable_commands.append(f'{iptables} -t {iptables_table} -I {rule_definition}')
# change iptables
@@ -154,232 +119,157 @@ def _iptables_config(configured_ifaces, direction):
cmd(command, raising=ConfigError)
-def get_config():
- vc = Config()
- vc.set_level('')
- # Convert the VyOS config to an abstract internal representation
- flow_config = {
- 'flow-accounting-configured': vc.exists('system flow-accounting'),
- 'buffer-size': vc.return_value('system flow-accounting buffer-size'),
- 'enable-egress': _node_exists('system flow-accounting enable-egress'),
- 'disable-imt': _node_exists('system flow-accounting disable-imt'),
- 'syslog-facility': vc.return_value('system flow-accounting syslog-facility'),
- 'interfaces': None,
- 'sflow': {
- 'configured': vc.exists('system flow-accounting sflow'),
- 'agent-address': vc.return_value('system flow-accounting sflow agent-address'),
- 'sampling-rate': vc.return_value('system flow-accounting sflow sampling-rate'),
- 'servers': None,
- 'source-address': vc.return_value('system flow-accounting sflow source-address')
- },
- 'netflow': {
- 'configured': vc.exists('system flow-accounting netflow'),
- 'engine-id': vc.return_value('system flow-accounting netflow engine-id'),
- 'max-flows': vc.return_value('system flow-accounting netflow max-flows'),
- 'sampling-rate': vc.return_value('system flow-accounting netflow sampling-rate'),
- 'source-ip': vc.return_value('system flow-accounting netflow source-ip'),
- 'version': vc.return_value('system flow-accounting netflow version'),
- 'timeout': {
- 'expint': vc.return_value('system flow-accounting netflow timeout expiry-interval'),
- 'general': vc.return_value('system flow-accounting netflow timeout flow-generic'),
- 'icmp': vc.return_value('system flow-accounting netflow timeout icmp'),
- 'maxlife': vc.return_value('system flow-accounting netflow timeout max-active-life'),
- 'tcp.fin': vc.return_value('system flow-accounting netflow timeout tcp-fin'),
- 'tcp': vc.return_value('system flow-accounting netflow timeout tcp-generic'),
- 'tcp.rst': vc.return_value('system flow-accounting netflow timeout tcp-rst'),
- 'udp': vc.return_value('system flow-accounting netflow timeout udp')
- },
- 'servers': None
- }
- }
-
- # get interfaces list
- if vc.exists('system flow-accounting interface'):
- flow_config['interfaces'] = vc.return_values('system flow-accounting interface')
-
- # get sFlow collectors list
- if vc.exists('system flow-accounting sflow server'):
- flow_config['sflow']['servers'] = []
- sflow_collectors = vc.list_nodes('system flow-accounting sflow server')
- for collector in sflow_collectors:
- port = default_sflow_server_port
- if vc.return_value("system flow-accounting sflow server {} port".format(collector)):
- port = vc.return_value("system flow-accounting sflow server {} port".format(collector))
- flow_config['sflow']['servers'].append({ 'address': collector, 'port': port })
-
- # get NetFlow collectors list
- if vc.exists('system flow-accounting netflow server'):
- flow_config['netflow']['servers'] = []
- netflow_collectors = vc.list_nodes('system flow-accounting netflow server')
- for collector in netflow_collectors:
- port = default_netflow_server_port
- if vc.return_value("system flow-accounting netflow server {} port".format(collector)):
- port = vc.return_value("system flow-accounting netflow server {} port".format(collector))
- flow_config['netflow']['servers'].append({ 'address': collector, 'port': port })
-
- # get sflow agent-id
- if flow_config['sflow']['agent-address'] == None or flow_config['sflow']['agent-address'] == 'auto':
- flow_config['sflow']['agent-address'] = _sflow_default_agentip(vc)
-
- # get NetFlow version
- if not flow_config['netflow']['version']:
- flow_config['netflow']['version'] = default_netflow_version
-
- # convert NetFlow engine-id format, if this is necessary
- if flow_config['netflow']['engine-id'] and flow_config['netflow']['version'] == '5':
- regex_filter = re.compile('^\d+$')
- if regex_filter.search(flow_config['netflow']['engine-id']):
- flow_config['netflow']['engine-id'] = "{}:0".format(flow_config['netflow']['engine-id'])
-
- # return dict with flow-accounting configuration
- return flow_config
-
-def verify(config):
- # Verify that configuration is valid
- # skip all checks if flow-accounting was removed
- if not config['flow-accounting-configured']:
- return True
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['system', 'flow-accounting']
+ if not conf.exists(base):
+ return None
+
+ flow_accounting = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+
+ # delete individual flow type default - should only be added if user uses
+ # this feature
+ for flow_type in ['sflow', 'netflow']:
+ if flow_type in default_values:
+ del default_values[flow_type]
+ flow_accounting = dict_merge(default_values, flow_accounting)
+
+ for flow_type in ['sflow', 'netflow']:
+ if flow_type in flow_accounting:
+ default_values = defaults(base + [flow_type])
+ # we need to merge individual server configurations
+ if 'server' in default_values:
+ del default_values['server']
+ flow_accounting[flow_type] = dict_merge(default_values, flow_accounting[flow_type])
+
+ if 'server' in flow_accounting[flow_type]:
+ default_values = defaults(base + [flow_type, 'server'])
+ for server in flow_accounting[flow_type]['server']:
+ flow_accounting[flow_type]['server'][server] = dict_merge(
+ default_values,flow_accounting[flow_type]['server'][server])
+
+ return flow_accounting
+
+def verify(flow_config):
+ if not flow_config:
+ return None
# check if at least one collector is enabled
- if not (config['sflow']['configured'] or config['netflow']['configured'] or not config['disable-imt']):
- raise ConfigError("You need to configure at least one sFlow or NetFlow protocol, or not set \"disable-imt\" for flow-accounting")
+ if 'sflow' not in flow_config and 'netflow' not in flow_config and 'disable_imt' in flow_config:
+ raise ConfigError('You need to configure at least sFlow or NetFlow, ' \
+ 'or not set "disable-imt" for flow-accounting!')
# Check if at least one interface is configured
- if not config['interfaces']:
- raise ConfigError("You need to configure at least one interface for flow-accounting")
+ if 'interface' not in flow_config:
+ raise ConfigError('Flow accounting requires at least one interface to ' \
+ 'be configured!')
# check that all configured interfaces exists in the system
- for iface in config['interfaces']:
- if not iface in Section.interfaces():
- # chnged from error to warning to allow adding dynamic interfaces and interface templates
- # raise ConfigError("The {} interface is not presented in the system".format(iface))
- print("Warning: the {} interface is not presented in the system".format(iface))
+ for interface in flow_config['interface']:
+ if interface not in Section.interfaces():
+ # Changed from error to warning to allow adding dynamic interfaces
+ # and interface templates
+ print(f'Warning: Interface "{interface}" is not presented in the system')
# check sFlow configuration
- if config['sflow']['configured']:
- # check if at least one sFlow collector is configured if sFlow configuration is presented
- if not config['sflow']['servers']:
- raise ConfigError("You need to configure at least one sFlow server")
+ if 'sflow' in flow_config:
+ # check if at least one sFlow collector is configured
+ if 'server' not in flow_config['sflow']:
+ raise ConfigError('You need to configure at least one sFlow server!')
# check that all sFlow collectors use the same IP protocol version
sflow_collector_ipver = None
- for sflow_collector in config['sflow']['servers']:
+ for server in flow_config['sflow']['server']:
if sflow_collector_ipver:
- if sflow_collector_ipver != ip_address(sflow_collector['address']).version:
+ if sflow_collector_ipver != ip_address(server).version:
raise ConfigError("All sFlow servers must use the same IP protocol")
else:
- sflow_collector_ipver = ip_address(sflow_collector['address']).version
-
+ sflow_collector_ipver = ip_address(server).version
# check agent-id for sFlow: we should avoid mixing IPv4 agent-id with IPv6 collectors and vice-versa
- for sflow_collector in config['sflow']['servers']:
- if ip_address(sflow_collector['address']).version != ip_address(config['sflow']['agent-address']).version:
- raise ConfigError("Different IP address versions cannot be mixed in \"sflow agent-address\" and \"sflow server\". You need to set manually the same IP version for \"agent-address\" as for all sFlow servers")
-
- # check if configured sFlow agent-id exist in the system
- agent_id_presented = None
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # check an IP, if this is not loopback
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- if regex_filter.search(address).group('ipaddr') == config['sflow']['agent-address']:
- agent_id_presented = True
- break
- if not agent_id_presented:
- raise ConfigError("Your \"sflow agent-address\" does not exist in the system")
+ for server in flow_config['sflow']['server']:
+ if 'agent_address' in flow_config['sflow']:
+ if ip_address(server).version != ip_address(flow_config['sflow']['agent_address']).version:
+ raise ConfigError('IPv4 and IPv6 addresses can not be mixed in "sflow agent-address" and "sflow '\
+ 'server". You need to set the same IP version for both "agent-address" and '\
+ 'all sFlow servers')
+
+ if 'agent_address' in flow_config['sflow']:
+ tmp = flow_config['sflow']['agent_address']
+ if not is_addr_assigned(tmp):
+ print(f'Warning: Configured "sflow agent-address {tmp}" does not exist in the system!')
# check NetFlow configuration
- if config['netflow']['configured']:
+ if 'netflow' in flow_config:
# check if at least one NetFlow collector is configured if NetFlow configuration is presented
- if not config['netflow']['servers']:
- raise ConfigError("You need to configure at least one NetFlow server")
-
- # check if configured netflow source-ip exist in the system
- if config['netflow']['source-ip']:
- source_ip_presented = None
- for iface in Section.interfaces():
- for address in Interface(iface).get_addr():
- # check an IP
- regex_filter = re.compile('^(?!(127)|(::1)|(fe80))(?P<ipaddr>[a-f\d\.:]+)/\d+$')
- if regex_filter.search(address):
- if regex_filter.search(address).group('ipaddr') == config['netflow']['source-ip']:
- source_ip_presented = True
- break
- if not source_ip_presented:
- print("Warning: your \"netflow source-ip\" does not exist in the system")
-
- # check if engine-id compatible with selected protocol version
- if config['netflow']['engine-id']:
+ if 'server' not in flow_config['netflow']:
+ raise ConfigError('You need to configure at least one NetFlow server!')
+
+ # Check if configured netflow source-address exist in the system
+ if 'source_address' in flow_config['netflow']:
+ if not is_addr_assigned(flow_config['netflow']['source_address']):
+ tmp = flow_config['netflow']['source_address']
+ print(f'Warning: Configured "netflow source-address {tmp}" does not exist on the system!')
+
+ # Check if engine-id compatible with selected protocol version
+ if 'engine_id' in flow_config['netflow']:
v5_filter = '^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]):(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$'
v9v10_filter = '^(\d|[1-9]\d{1,8}|[1-3]\d{9}|4[01]\d{8}|42[0-8]\d{7}|429[0-3]\d{6}|4294[0-8]\d{5}|42949[0-5]\d{4}|429496[0-6]\d{3}|4294967[01]\d{2}|42949672[0-8]\d|429496729[0-5])$'
- if config['netflow']['version'] == '5':
+ engine_id = flow_config['netflow']['engine_id']
+ version = flow_config['netflow']['version']
+
+ if flow_config['netflow']['version'] == '5':
regex_filter = re.compile(v5_filter)
- if not regex_filter.search(config['netflow']['engine-id']):
- raise ConfigError("You cannot use NetFlow engine-id {} together with NetFlow protocol version {}".format(config['netflow']['engine-id'], config['netflow']['version']))
+ if not regex_filter.search(engine_id):
+ raise ConfigError(f'You cannot use NetFlow engine-id "{engine_id}" '\
+ f'together with NetFlow protocol version "{version}"!')
else:
regex_filter = re.compile(v9v10_filter)
- if not regex_filter.search(config['netflow']['engine-id']):
- raise ConfigError("You cannot use NetFlow engine-id {} together with NetFlow protocol version {}".format(config['netflow']['engine-id'], config['netflow']['version']))
+ if not regex_filter.search(flow_config['netflow']['engine_id']):
+ raise ConfigError(f'Can not use NetFlow engine-id "{engine_id}" together '\
+ f'with NetFlow protocol version "{version}"!')
# return True if all checks were passed
return True
-def generate(config):
- # skip all checks if flow-accounting was removed
- if not config['flow-accounting-configured']:
- return True
-
- # Calculate all necessary values
- if config['buffer-size']:
- # circular queue size
- config['plugin_pipe_size'] = int(config['buffer-size']) * 1024**2
- else:
- config['plugin_pipe_size'] = default_plugin_pipe_size * 1024**2
- # transfer buffer size
- # recommended value from pmacct developers 1/1000 of pipe size
- config['plugin_buffer_size'] = int(config['plugin_pipe_size'] / 1000)
-
- # Prepare a timeouts string
- timeout_string = ''
- for timeout_type, timeout_value in config['netflow']['timeout'].items():
- if timeout_value:
- if timeout_string == '':
- timeout_string = "{}{}={}".format(timeout_string, timeout_type, timeout_value)
- else:
- timeout_string = "{}:{}={}".format(timeout_string, timeout_type, timeout_value)
- config['netflow']['timeout_string'] = timeout_string
-
- render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', {
- 'templatecfg': config,
- 'snaplen': default_captured_packet_size,
- })
+def generate(flow_config):
+ if not flow_config:
+ return None
+ render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', flow_config)
-def apply(config):
- # define variables
- command = None
+def apply(flow_config):
+ action = 'restart'
# Check if flow-accounting was removed and define command
- if not config['flow-accounting-configured']:
- command = 'systemctl stop uacctd.service'
- else:
- command = 'systemctl restart uacctd.service'
+ if not flow_config:
+ _iptables_config([], 'ingress')
+ _iptables_config([], 'egress')
+
+ # Stop flow-accounting daemon and remove configuration file
+ cmd('systemctl stop uacctd.service')
+ if os.path.exists(uacctd_conf_path):
+ os.unlink(uacctd_conf_path)
+ return
- # run command to start or stop flow-accounting
- cmd(command, raising=ConfigError, message='Failed to start/stop flow-accounting')
+ # Start/reload flow-accounting daemon
+ cmd(f'systemctl restart uacctd.service')
# configure iptables rules for defined interfaces
- if config['interfaces']:
- _iptables_config(config['interfaces'], 'ingress')
+ if 'interface' in flow_config:
+ _iptables_config(flow_config['interface'], 'ingress', flow_config['packet_length'])
# configure egress the same way if configured otherwise remove it
- if config['enable-egress']:
- _iptables_config(config['interfaces'], 'egress')
+ if 'enable_egress' in flow_config:
+ _iptables_config(flow_config['interface'], 'egress', flow_config['packet_length'])
else:
_iptables_config([], 'egress')
- else:
- _iptables_config([], 'ingress')
- _iptables_config([], 'egress')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index ea0743cd5..b5f5e919f 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 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
@@ -13,25 +13,26 @@
#
# 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 sys
import os
import json
-import time
+
+from time import sleep
from copy import deepcopy
import vyos.defaults
+
from vyos.config import Config
-from vyos import ConfigError
+from vyos.template import render
from vyos.util import cmd
from vyos.util import call
-
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
api_conf_file = '/etc/vyos/http-api.conf'
+systemd_service = '/run/systemd/system/vyos-http-api.service'
vyos_conf_scripts_dir=vyos.defaults.directories['conf_mode']
@@ -49,11 +50,16 @@ def get_config(config=None):
else:
conf = Config()
- if not conf.exists('service https api'):
+ base = ['service', 'https', 'api']
+ if not conf.exists(base):
return None
- else:
- conf.set_level('service https api')
+ # Do we run inside a VRF context?
+ vrf_path = ['service', 'https', 'vrf']
+ if conf.exists(vrf_path):
+ http_api['vrf'] = conf.return_value(vrf_path)
+
+ conf.set_level('service https api')
if conf.exists('strict'):
http_api['strict'] = True
@@ -92,6 +98,8 @@ def verify(http_api):
def generate(http_api):
if http_api is None:
+ if os.path.exists(systemd_service):
+ os.unlink(systemd_service)
return None
if not os.path.exists('/etc/vyos'):
@@ -100,16 +108,21 @@ def generate(http_api):
with open(api_conf_file, 'w') as f:
json.dump(http_api, f, indent=2)
+ render(systemd_service, 'https/vyos-http-api.service.tmpl', http_api)
return None
def apply(http_api):
+ # Reload systemd manager configuration
+ call('systemctl daemon-reload')
+ service_name = 'vyos-http-api.service'
+
if http_api is not None:
- call('systemctl restart vyos-http-api.service')
+ call(f'systemctl restart {service_name}')
else:
- call('systemctl stop vyos-http-api.service')
+ call(f'systemctl stop {service_name}')
# Let uvicorn settle before restarting Nginx
- time.sleep(1)
+ sleep(1)
cmd(f'{vyos_conf_scripts_dir}/https.py', raising=ConfigError)
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index 053ee5d4a..37fa36797 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -61,10 +61,11 @@ def get_config(config=None):
else:
conf = Config()
- if not conf.exists('service https'):
+ base = ['service', 'https']
+ if not conf.exists(base):
return None
- https = conf.get_config_dict('service https', get_first_key=True)
+ https = conf.get_config_dict(base, get_first_key=True)
if https:
https['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py
index d0460b830..f8e733ba5 100755
--- a/src/conf_mode/protocols_ospfv3.py
+++ b/src/conf_mode/protocols_ospfv3.py
@@ -23,8 +23,11 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configverify import verify_common_route_maps
+from vyos.configverify import verify_route_map
+from vyos.configverify import verify_interface_exists
from vyos.template import render_to_string
from vyos.ifconfig import Interface
+from vyos.util import dict_search
from vyos.util import get_interface_config
from vyos.xml import defaults
from vyos import ConfigError
@@ -66,6 +69,28 @@ def get_config(config=None):
ospfv3.update({'deleted' : ''})
return ospfv3
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ # XXX: Note that we can not call defaults(base), as defaults does not work
+ # on an instance of a tag node. As we use the exact same CLI definition for
+ # both the non-vrf and vrf version this is absolutely safe!
+ default_values = defaults(base_path)
+
+ # We have to cleanup the default dict, as default values could enable features
+ # which are not explicitly enabled on the CLI. Example: default-information
+ # originate comes with a default metric-type of 2, which will enable the
+ # entire default-information originate tree, even when not set via CLI so we
+ # need to check this first and probably drop that key.
+ if dict_search('default_information.originate', ospfv3) is None:
+ del default_values['default_information']
+
+ # XXX: T2665: we currently have no nice way for defaults under tag nodes,
+ # clean them out and add them manually :(
+ del default_values['interface']
+
+ # merge in remaining default values
+ ospfv3 = dict_merge(default_values, ospfv3)
+
# We also need some additional information from the config, prefix-lists
# and route-maps for instance. They will be used in verify().
#
@@ -83,8 +108,19 @@ def verify(ospfv3):
verify_common_route_maps(ospfv3)
+ # As we can have a default-information route-map, we need to validate it!
+ route_map_name = dict_search('default_information.originate.route_map', ospfv3)
+ if route_map_name: verify_route_map(route_map_name, ospfv3)
+
+ if 'area' in ospfv3:
+ for area, area_config in ospfv3['area'].items():
+ if 'area_type' in area_config:
+ if len(area_config['area_type']) > 1:
+ raise ConfigError(f'Can only configure one area-type for OSPFv3 area "{area}"!')
+
if 'interface' in ospfv3:
for interface, interface_config in ospfv3['interface'].items():
+ verify_interface_exists(interface)
if 'ifmtu' in interface_config:
mtu = Interface(interface).get_mtu()
if int(interface_config['ifmtu']) > int(mtu):
diff --git a/src/etc/systemd/system/uacctd.service.d/override.conf b/src/etc/systemd/system/uacctd.service.d/override.conf
new file mode 100644
index 000000000..38bcce515
--- /dev/null
+++ b/src/etc/systemd/system/uacctd.service.d/override.conf
@@ -0,0 +1,14 @@
+[Unit]
+After=
+After=vyos-router.service
+ConditionPathExists=
+ConditionPathExists=/run/pmacct/uacctd.conf
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=/usr/sbin/uacctd -f /run/pmacct/uacctd.conf
+WorkingDirectory=
+WorkingDirectory=/run/pmacct
+PIDFile=
+PIDFile=/run/pmacct/uacctd.pid
diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1
new file mode 100755
index 000000000..72cce77b0
--- /dev/null
+++ b/src/migration-scripts/flow-accounting/0-to-1
@@ -0,0 +1,69 @@
+#!/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/>.
+
+# T4099: flow-accounting: sync "source-ip" and "source-address" between netflow
+# and sflow ion CLI
+# T4105: flow-accounting: drop "sflow agent-address auto"
+
+from sys import argv
+from vyos.configtree import ConfigTree
+
+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()
+
+base = ['system', 'flow-accounting']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# T4099
+tmp = base + ['netflow', 'source-ip']
+if config.exists(tmp):
+ config.rename(tmp, 'source-address')
+
+# T4105
+tmp = base + ['sflow', 'agent-address']
+if config.exists(tmp):
+ value = config.return_value(tmp)
+ if value == 'auto':
+ # delete the "auto"
+ config.delete(tmp)
+
+ # 1) check if BGP router-id is set
+ # 2) check if OSPF router-id is set
+ # 3) check if OSPFv3 router-id is set
+ router_id = None
+ for protocol in ['bgp', 'ospf', 'ospfv3']:
+ if config.exists(['protocols', protocol, 'parameters', 'router-id']):
+ router_id = config.return_value(['protocols', protocol, 'parameters', 'router-id'])
+ break
+ if router_id:
+ config.set(tmp, value=router_id)
+
+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/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py
index 66ecf8439..89f6df4b9 100755
--- a/src/op_mode/conntrack_sync.py
+++ b/src/op_mode/conntrack_sync.py
@@ -20,12 +20,15 @@ import xmltodict
from argparse import ArgumentParser
from vyos.configquery import CliShellApiConfigQuery
+from vyos.configquery import ConfigTreeQuery
+from vyos.util import call
from vyos.util import cmd
from vyos.util import run
from vyos.template import render_to_string
conntrackd_bin = '/usr/sbin/conntrackd'
conntrackd_config = '/run/conntrackd/conntrackd.conf'
+failover_state_file = '/var/run/vyatta-conntrackd-failover-state'
parser = ArgumentParser(description='Conntrack Sync')
group = parser.add_mutually_exclusive_group()
@@ -36,6 +39,8 @@ group.add_argument('--show-internal', help='Show internal (main) tracking cache'
group.add_argument('--show-external', help='Show external (main) tracking cache', action='store_true')
group.add_argument('--show-internal-expect', help='Show internal (expect) tracking cache', action='store_true')
group.add_argument('--show-external-expect', help='Show external (expect) tracking cache', action='store_true')
+group.add_argument('--show-statistics', help='Show connection syncing statistics', action='store_true')
+group.add_argument('--show-status', help='Show conntrack-sync status', action='store_true')
def is_configured():
""" Check if conntrack-sync service is configured """
@@ -131,6 +136,46 @@ if __name__ == '__main__':
out = cmd(f'sudo {conntrackd_bin} -C {conntrackd_config} {opt} -x')
xml_to_stdout(out)
+ elif args.show_statistics:
+ is_configured()
+ config = ConfigTreeQuery()
+ print('\nMain Table Statistics:\n')
+ call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s')
+ print()
+ if config.exists(['service', 'conntrack-sync', 'expect-sync']):
+ print('\nExpect Table Statistics:\n')
+ call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s exp')
+ print()
+
+ elif args.show_status:
+ is_configured()
+ config = ConfigTreeQuery()
+ ct_sync_intf = config.list_nodes(['service', 'conntrack-sync', 'interface'])
+ ct_sync_intf = ', '.join(ct_sync_intf)
+ failover_state = "no transition yet!"
+ expect_sync_protocols = "disabled"
+
+ if config.exists(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp']):
+ failover_mechanism = "vrrp"
+ vrrp_sync_grp = config.value(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group'])
+
+ if os.path.isfile(failover_state_file):
+ with open(failover_state_file, "r") as f:
+ failover_state = f.readline()
+
+ if config.exists(['service', 'conntrack-sync', 'expect-sync']):
+ expect_sync_protocols = config.values(['service', 'conntrack-sync', 'expect-sync'])
+ if 'all' in expect_sync_protocols:
+ expect_sync_protocols = ["ftp", "sip", "h323", "nfs", "sqlnet"]
+ expect_sync_protocols = ', '.join(expect_sync_protocols)
+
+ show_status = (f'\nsync-interface : {ct_sync_intf}\n'
+ f'failover-mechanism : {failover_mechanism} [sync-group {vrrp_sync_grp}]\n'
+ f'last state transition : {failover_state}'
+ f'ExpectationSync : {expect_sync_protocols}')
+
+ print(show_status)
+
else:
parser.print_help()
exit(1)
diff --git a/src/systemd/vyos-http-api.service b/src/systemd/vyos-http-api.service
deleted file mode 100644
index 55370b356..000000000
--- a/src/systemd/vyos-http-api.service
+++ /dev/null
@@ -1,21 +0,0 @@
-[Unit]
-Description=VyOS HTTP API service
-After=vyos-router.service
-Requires=vyos-router.service
-
-[Service]
-ExecStart=/usr/libexec/vyos/services/vyos-http-api-server
-Type=idle
-
-SyslogIdentifier=vyos-http-api
-SyslogFacility=daemon
-
-Restart=on-failure
-
-# Does't work but leave it here
-User=root
-Group=vyattacfg
-
-[Install]
-WantedBy=vyos.target
-