From 015e26acc8ed65b6a7d778107a83ee8604950f90 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 24 Dec 2021 22:12:19 +0100 Subject: flow-accounting: T4097: move to get_config_dict() --- src/conf_mode/flow_accounting_conf.py | 314 ++++++++++++---------------------- 1 file changed, 113 insertions(+), 201 deletions(-) (limited to 'src/conf_mode') diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index daad00067..5f903bf69 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,42 +16,33 @@ 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' 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 @@ -154,232 +145,153 @@ 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]) + + flow_accounting['snaplen'] = default_captured_packet_size + + 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[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 flow_config['sflow']['agent_address'] != 'auto': + if ip_address(server).version != ip_address(flow_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") + + if 'agent_address' in flow_config['sflow']: + agent_address = flow_config['sflow']['agent_address'] + if agent_address != 'auto' and not is_addr_assigned(agent_address): + print(f'Warning: Configured "sflow agent-address" 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") + if 'server' not in flow_config['netflow']: + 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[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") + if 'source_ip' in flow_config['netflow']: + if not is_addr_assigned(flow_config['netflow']['source_ip']): + print(f'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 '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']): + if not regex_filter.search(flow_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'])) # 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 +def generate(flow_config): + if not flow_config: + return None - # 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', flow_config) - render(uacctd_conf_path, 'netflow/uacctd.conf.tmpl', { - 'templatecfg': config, - 'snaplen': default_captured_packet_size, - }) - - -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 + cmd('systemctl stop uacctd.service') + 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') # 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') else: _iptables_config([], 'egress') - else: - _iptables_config([], 'ingress') - _iptables_config([], 'egress') if __name__ == '__main__': try: -- cgit v1.2.3 From 0030f9fc844036a0d1a0381a9096b1b9d368e35f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 24 Dec 2021 22:49:36 +0100 Subject: flow-accounting: T4099: rename "netflow source-ip" to source-address sFlow uses the source-address CLI node and netflow uses source-ip this is just confusing and should be synced to the common source-address CLI node. --- data/templates/netflow/uacctd.conf.tmpl | 4 +- interface-definitions/flow-accounting-conf.xml.in | 18 +-------- src/conf_mode/flow_accounting_conf.py | 6 +-- src/migration-scripts/flow-accounting/0-to-1 | 49 +++++++++++++++++++++++ 4 files changed, 55 insertions(+), 22 deletions(-) create mode 100755 src/migration-scripts/flow-accounting/0-to-1 (limited to 'src/conf_mode') diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl index 2a43b3ce2..27a157531 100644 --- a/data/templates/netflow/uacctd.conf.tmpl +++ b/data/templates/netflow/uacctd.conf.tmpl @@ -48,8 +48,8 @@ nfprobe_maxflows[nf_{{ server }}]: {{ netflow.max_flows }} {% if netflow.sampling_rate is defined and netflow.sampling_rate is not none %} sampling_rate[nf_{{ server }}]: {{ netflow.sampling_rate }} {% endif %} -{% if netflow.source_ip is defined and netflow.source_ip is not none %} -nfprobe_source_ip[nf_{{ server }}]: {{ netflow.source_ip }} +{% if netflow.source_address is defined and netflow.source_address is not none %} +nfprobe_source_ip[nf_{{ server }}]: {{ netflow.source_address }} {% endif %} {% if netflow.timeout is defined and netflow.timeout is not none %} nfprobe_timeouts[nf_{{ server }}]: expint={{ netflow.timeout.expiry_interval }}:general={{ netflow.timeout.flow_generic }}:icmp={{ netflow.timeout.icmp }}:maxlife={{ netflow.timeout.max_active_life }}:tcp.fin={{ netflow.timeout.tcp_fin }}:tcp={{ netflow.timeout.tcp_generic }}:tcp.rst={{ netflow.timeout.tcp_rst }}:udp={{ netflow.timeout.udp }} diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index 02364425f..7b110e733 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -183,23 +183,7 @@ - - - IPv4 or IPv6 source address of NetFlow packets - - ipv4 - IPv4 source address of NetFlow packets - - - ipv6 - IPv6 source address of NetFlow packets - - - - - - - + #include NetFlow version to export diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 5f903bf69..9467e805c 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -239,9 +239,9 @@ def verify(flow_config): raise ConfigError('You need to configure at least one NetFlow server!') # check if configured netflow source-ip exist in the system - if 'source_ip' in flow_config['netflow']: - if not is_addr_assigned(flow_config['netflow']['source_ip']): - print(f'Warning: your "netflow source-ip" does not exist in the system!') + if 'source_address' in flow_config['netflow']: + if not is_addr_assigned(flow_config['netflow']['source_address']): + print(f'Warning: Configured "netflow source-address" does not exist on the system!') # check if engine-id compatible with selected protocol version if 'engine_id' in flow_config['netflow']: 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..9e9d7132d --- /dev/null +++ b/src/migration-scripts/flow-accounting/0-to-1 @@ -0,0 +1,49 @@ +#!/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 . + +# T4099: flow-accounting: sync "source-ip" and "source-address" between netflow +# and sflow ion CLI + +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') + +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) -- cgit v1.2.3 From b9b5070203c3c3b31a7b297c5ddba8934b1ca34d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Dec 2021 22:57:59 +0100 Subject: flow-accounting: T4105: drop "sflow agent-address auto" The implementation of the "auto" option to specify the sflow/netflow agent-address is very error prone. The current implementation will determine the IP address used for the "auto" value as follow: Get BGP router-id 1) If not found use OSPF router-id 2) If not found use OSPFv3 router-id 3) If not found use "the first IP address found on the system Well, what is the "first IP address found"? Also this changes if DHCP is in use. Also another disadvantage is when the BGP/OSPF/OSPFv3 router-id is changed, the agent-address is not updated upon the next reboot of the system. This task is about removing the "auto" keyword from the CLI at all and make it either entirely configurable by the user and hardcode the value in CLI, or not use this at all. If "auto" is specified we will query the system in the above order and set the proper router-id in the CLI. If none can be found the CLI node is removed. --- interface-definitions/flow-accounting-conf.xml.in | 6 ---- smoketest/configs/bgp-big-as-cloud | 6 ++++ src/conf_mode/flow_accounting_conf.py | 38 ++++++----------------- src/migration-scripts/flow-accounting/0-to-1 | 20 ++++++++++++ 4 files changed, 35 insertions(+), 35 deletions(-) (limited to 'src/conf_mode') diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index 7b110e733..ba5c70979 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -362,20 +362,14 @@ auto - - auto - auto select sFlow agent-address (default) - ipv4 sFlow IPv4 agent address - ^auto$ - auto diff --git a/smoketest/configs/bgp-big-as-cloud b/smoketest/configs/bgp-big-as-cloud index 694243d1e..10660ec87 100644 --- a/smoketest/configs/bgp-big-as-cloud +++ b/smoketest/configs/bgp-big-as-cloud @@ -1819,6 +1819,12 @@ system { } version 9 } + sflow { + agent-address auto + server 1.2.3.4 { + port 1234 + } + } syslog-facility daemon } host-name vyos diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 9467e805c..86fbd96b1 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -43,29 +43,6 @@ iptables_nflog_chain = 'VYATTA_CT_PREROUTING_HOOK' egress_iptables_nflog_table = 'mangle' egress_iptables_nflog_chain = 'FORWARD' -# 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[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 @@ -223,14 +200,16 @@ def verify(flow_config): # check agent-id for sFlow: we should avoid mixing IPv4 agent-id with IPv6 collectors and vice-versa for server in flow_config['sflow']['server']: - if flow_config['sflow']['agent_address'] != 'auto': + if 'agent_address' in flow_config['sflow']: if ip_address(server).version != ip_address(flow_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") + 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']: - agent_address = flow_config['sflow']['agent_address'] - if agent_address != 'auto' and not is_addr_assigned(agent_address): - print(f'Warning: Configured "sflow agent-address" does not exist in the system!') + if not is_addr_assigned(agent_address): + tmp = flow_config['sflow']['agent_address'] + print(f'Warning: Configured "sflow agent-address {tmp}" does not exist in the system!') # check NetFlow configuration if 'netflow' in flow_config: @@ -241,7 +220,8 @@ def verify(flow_config): # check if configured netflow source-ip exist in the system if 'source_address' in flow_config['netflow']: if not is_addr_assigned(flow_config['netflow']['source_address']): - print(f'Warning: Configured "netflow source-address" does not exist on the system!') + 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']: diff --git a/src/migration-scripts/flow-accounting/0-to-1 b/src/migration-scripts/flow-accounting/0-to-1 index 9e9d7132d..72cce77b0 100755 --- a/src/migration-scripts/flow-accounting/0-to-1 +++ b/src/migration-scripts/flow-accounting/0-to-1 @@ -16,6 +16,7 @@ # 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 @@ -41,6 +42,25 @@ 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()) -- cgit v1.2.3 From 344c2776bd6e157d33ea81f548d1eacde1d3e644 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 25 Dec 2021 23:26:36 +0100 Subject: flow-accounting: T4106: support specification of capture packet length --- data/templates/netflow/uacctd.conf.tmpl | 2 +- interface-definitions/flow-accounting-conf.xml.in | 13 +++++++++++++ src/conf_mode/flow_accounting_conf.py | 19 +++++++------------ 3 files changed, 21 insertions(+), 13 deletions(-) (limited to 'src/conf_mode') diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl index 27a157531..b6d31746f 100644 --- a/data/templates/netflow/uacctd.conf.tmpl +++ b/data/templates/netflow/uacctd.conf.tmpl @@ -4,7 +4,7 @@ promisc: false pidfile: /var/run/uacctd.pid uacctd_group: 2 uacctd_nl_size: 2097152 -snaplen: {{ snaplen }} +snaplen: {{ packet_length }} aggregate: in_iface{{ ',out_iface' if enable_egress is defined }},src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows {% set pipe_size = buffer_size | int *1024 *1024 %} plugin_pipe_size: {{ pipe_size }} diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index ba5c70979..1b57d706c 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -22,6 +22,19 @@ 10 + + + Specifies the maximum number of bytes to capture for each packet + + u32:128-750 + Packet length in bytes (default: 128) + + + + + + 128 + Enable egress flow accounting diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 86fbd96b1..3d3b03e10 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -34,9 +34,6 @@ from vyos import ConfigError from vyos import airbag airbag.enable() -# default values -default_captured_packet_size = 128 - uacctd_conf_path = '/etc/pmacct/uacctd.conf' iptables_nflog_table = 'raw' iptables_nflog_chain = 'VYATTA_CT_PREROUTING_HOOK' @@ -67,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): # define list of iptables commands to modify settings iptable_commands = [] iptables_chain = iptables_nflog_chain @@ -114,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 @@ -158,8 +155,6 @@ def get_config(config=None): flow_accounting[flow_type]['server'][server] = dict_merge( default_values,flow_accounting[flow_type]['server'][server]) - flow_accounting['snaplen'] = default_captured_packet_size - return flow_accounting def verify(flow_config): @@ -253,8 +248,8 @@ def apply(flow_config): action = 'restart' # Check if flow-accounting was removed and define command if not flow_config: - _iptables_config([], 'ingress') - _iptables_config([], 'egress') + _iptables_config([], 'ingress', flow_config['packet_length']) + _iptables_config([], 'egress', flow_config['packet_length']) # Stop flow-accounting daemon cmd('systemctl stop uacctd.service') @@ -265,13 +260,13 @@ def apply(flow_config): # configure iptables rules for defined interfaces if 'interface' in flow_config: - _iptables_config(flow_config['interface'], 'ingress') + _iptables_config(flow_config['interface'], 'ingress', flow_config['packet_length']) # configure egress the same way if configured otherwise remove it if 'enable_egress' in flow_config: - _iptables_config(flow_config['interface'], 'egress') + _iptables_config(flow_config['interface'], 'egress', flow_config['packet_length']) else: - _iptables_config([], 'egress') + _iptables_config([], 'egress', flow_config['packet_length']) if __name__ == '__main__': try: -- cgit v1.2.3