diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/ethtool.py | 3 | ||||
-rw-r--r-- | python/vyos/firewall.py | 100 | ||||
-rw-r--r-- | python/vyos/frr.py | 9 | ||||
-rw-r--r-- | python/vyos/ifconfig/ethernet.py | 26 | ||||
-rw-r--r-- | python/vyos/raid.py | 71 | ||||
-rw-r--r-- | python/vyos/template.py | 5 | ||||
-rw-r--r-- | python/vyos/utils/config.py | 34 | ||||
-rw-r--r-- | python/vyos/utils/disk.py | 23 | ||||
-rw-r--r-- | python/vyos/utils/network.py | 2 |
9 files changed, 234 insertions, 39 deletions
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index ca3bcfc3d..f19632719 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -172,6 +172,9 @@ class Ethtool: def get_generic_segmentation_offload(self): return self._get_generic('generic-segmentation-offload') + def get_hw_tc_offload(self): + return self._get_generic('hw-tc-offload') + def get_large_receive_offload(self): return self._get_generic('large-receive-offload') diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 3305eb269..9122e264e 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -102,6 +102,20 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if states: output.append(f'ct state {{{states}}}') + if 'conntrack_helper' in rule_conf: + helper_map = {'h323': ['RAS', 'Q.931'], 'nfs': ['rpc'], 'sqlnet': ['tns']} + helper_out = [] + + for helper in rule_conf['conntrack_helper']: + if helper in helper_map: + helper_out.extend(helper_map[helper]) + else: + helper_out.append(helper) + + if helper_out: + helper_str = ','.join(f'"{s}"' for s in helper_out) + output.append(f'ct helper {{{helper_str}}}') + if 'connection_status' in rule_conf and rule_conf['connection_status']: status = rule_conf['connection_status'] if status['nat'] == 'destination': @@ -249,28 +263,8 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): output.append(f'{proto} {prefix}port {operator} @P_{group_name}') - if 'log' in rule_conf and rule_conf['log'] == 'enable': - action = rule_conf['action'] if 'action' in rule_conf else 'accept' - #output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"') - output.append(f'log prefix "[{family}-{hook}-{fw_name}-{rule_id}-{action[:1].upper()}]"') - ##{family}-{hook}-{fw_name}-{rule_id} - if 'log_options' in rule_conf: - - if 'level' in rule_conf['log_options']: - log_level = rule_conf['log_options']['level'] - output.append(f'log level {log_level}') - - if 'group' in rule_conf['log_options']: - log_group = rule_conf['log_options']['group'] - output.append(f'log group {log_group}') - - if 'queue_threshold' in rule_conf['log_options']: - queue_threshold = rule_conf['log_options']['queue_threshold'] - output.append(f'queue-threshold {queue_threshold}') - - if 'snapshot_length' in rule_conf['log_options']: - log_snaplen = rule_conf['log_options']['snapshot_length'] - output.append(f'snaplen {log_snaplen}') + if dict_search_args(rule_conf, 'action') == 'synproxy': + output.append('ct state invalid,untracked') if 'hop_limit' in rule_conf: operators = {'eq': '==', 'gt': '>', 'lt': '<'} @@ -393,6 +387,28 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'priority' in rule_conf['vlan']: output.append(f'vlan pcp {rule_conf["vlan"]["priority"]}') + if 'log' in rule_conf and rule_conf['log'] == 'enable': + action = rule_conf['action'] if 'action' in rule_conf else 'accept' + #output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"') + output.append(f'log prefix "[{family}-{hook}-{fw_name}-{rule_id}-{action[:1].upper()}]"') + ##{family}-{hook}-{fw_name}-{rule_id} + if 'log_options' in rule_conf: + + if 'level' in rule_conf['log_options']: + log_level = rule_conf['log_options']['level'] + output.append(f'log level {log_level}') + + if 'group' in rule_conf['log_options']: + log_group = rule_conf['log_options']['group'] + output.append(f'log group {log_group}') + + if 'queue_threshold' in rule_conf['log_options']: + queue_threshold = rule_conf['log_options']['queue_threshold'] + output.append(f'queue-threshold {queue_threshold}') + + if 'snapshot_length' in rule_conf['log_options']: + log_snaplen = rule_conf['log_options']['snapshot_length'] + output.append(f'snaplen {log_snaplen}') output.append('counter') @@ -402,19 +418,33 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'action' in rule_conf: # Change action=return to action=action # #output.append(nft_action(rule_conf['action'])) - output.append(f'{rule_conf["action"]}') - if 'jump' in rule_conf['action']: - target = rule_conf['jump_target'] - output.append(f'NAME{def_suffix}_{target}') - - if 'queue' in rule_conf['action']: - if 'queue' in rule_conf: - target = rule_conf['queue'] - output.append(f'num {target}') - - if 'queue_options' in rule_conf: - queue_opts = ','.join(rule_conf['queue_options']) - output.append(f'{queue_opts}') + if rule_conf['action'] == 'offload': + offload_target = rule_conf['offload_target'] + output.append(f'flow add @VYOS_FLOWTABLE_{offload_target}') + else: + output.append(f'{rule_conf["action"]}') + + if 'jump' in rule_conf['action']: + target = rule_conf['jump_target'] + output.append(f'NAME{def_suffix}_{target}') + + if 'queue' in rule_conf['action']: + if 'queue' in rule_conf: + target = rule_conf['queue'] + output.append(f'num {target}') + + if 'queue_options' in rule_conf: + queue_opts = ','.join(rule_conf['queue_options']) + output.append(f'{queue_opts}') + + # Synproxy + if 'synproxy' in rule_conf: + synproxy_mss = dict_search_args(rule_conf, 'synproxy', 'tcp', 'mss') + if synproxy_mss: + output.append(f'mss {synproxy_mss}') + synproxy_ws = dict_search_args(rule_conf, 'synproxy', 'tcp', 'window_scale') + if synproxy_ws: + output.append(f'wscale {synproxy_ws} timestamp sack-perm') else: output.append('return') diff --git a/python/vyos/frr.py b/python/vyos/frr.py index 9c9e50ff7..ad5c207f5 100644 --- a/python/vyos/frr.py +++ b/python/vyos/frr.py @@ -86,9 +86,12 @@ ch2 = logging.StreamHandler(stream=sys.stdout) LOG.addHandler(ch) LOG.addHandler(ch2) -_frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', - 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd', - 'bfdd', 'eigrpd', 'babeld' ,'pim6d'] +# Full list of FRR 9.0/stable daemons for reference +#_frr_daemons = ['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', +# 'isisd', 'pim6d', 'ldpd', 'eigrpd', 'babeld', 'sharpd', 'bfdd', +# 'fabricd', 'pathd'] +_frr_daemons = ['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', + 'isisd', 'pim6d', 'ldpd', 'babeld', 'bfdd'] path_vtysh = '/usr/bin/vtysh' path_frr_reload = '/usr/lib/frr/frr-reload.py' diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 24ce3a803..285542057 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -57,6 +57,10 @@ class EthernetIf(Interface): 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'gso', v), }, + 'hw-tc-offload': { + 'validate': lambda v: assert_list(v, ['on', 'off']), + 'possible': lambda i, v: EthernetIf.feature(i, 'hw-tc-offload', v), + }, 'lro': { 'validate': lambda v: assert_list(v, ['on', 'off']), 'possible': lambda i, v: EthernetIf.feature(i, 'lro', v), @@ -222,6 +226,25 @@ class EthernetIf(Interface): print('Adapter does not support changing generic-segmentation-offload settings!') return False + def set_hw_tc_offload(self, state): + """ + Enable hardware TC flow offload. State can be either True or False. + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_hw_tc_offload(True) + """ + if not isinstance(state, bool): + raise ValueError('Value out of range') + + enabled, fixed = self.ethtool.get_hw_tc_offload() + if enabled != state: + if not fixed: + return self.set_interface('hw-tc-offload', 'on' if state else 'off') + else: + print('Adapter does not support changing hw-tc-offload settings!') + return False + def set_lro(self, state): """ Enable Large Receive offload. State can be either True or False. @@ -358,6 +381,9 @@ class EthernetIf(Interface): # GSO (generic segmentation offload) self.set_gso(dict_search('offload.gso', config) != None) + # GSO (generic segmentation offload) + self.set_hw_tc_offload(dict_search('offload.hw-tc-offload', config) != None) + # LRO (large receive offload) self.set_lro(dict_search('offload.lro', config) != None) diff --git a/python/vyos/raid.py b/python/vyos/raid.py new file mode 100644 index 000000000..7fb794817 --- /dev/null +++ b/python/vyos/raid.py @@ -0,0 +1,71 @@ +# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +from vyos.utils.disk import device_from_id +from vyos.utils.process import cmd + +def raid_sets(): + """ + Returns a list of RAID sets + """ + with open('/proc/mdstat') as f: + return [line.split()[0].rstrip(':') for line in f if line.startswith('md')] + +def raid_set_members(raid_set_name: str): + """ + Returns a list of members of a RAID set + """ + with open('/proc/mdstat') as f: + for line in f: + if line.startswith(raid_set_name): + return [l.split('[')[0] for l in line.split()[4:]] + return [] + +def partitions(): + """ + Returns a list of partitions + """ + with open('/proc/partitions') as f: + p = [l.strip().split()[-1] for l in list(f) if l.strip()] + p.remove('name') + return p + +def add_raid_member(raid_set_name: str, member: str, by_id: bool = False): + """ + Add a member to an existing RAID set + """ + if by_id: + member = device_from_id(member) + if raid_set_name not in raid_sets(): + raise ValueError(f"RAID set {raid_set_name} does not exist") + if member not in partitions(): + raise ValueError(f"Partition {member} does not exist") + if member in raid_set_members(raid_set_name): + raise ValueError(f"Partition {member} is already a member of RAID set {raid_set_name}") + cmd(f'mdadm --add /dev/{raid_set_name} /dev/{member}') + disk = cmd(f'lsblk -ndo PKNAME /dev/{member}') + cmd(f'grub-install /dev/{disk}') + +def delete_raid_member(raid_set_name: str, member: str, by_id: bool = False): + """ + Delete a member from an existing RAID set + """ + if by_id: + member = device_from_id(member) + if raid_set_name not in raid_sets(): + raise ValueError(f"RAID set {raid_set_name} does not exist") + if member not in raid_set_members(raid_set_name): + raise ValueError(f"Partition {member} is not a member of RAID set {raid_set_name}") + cmd(f'mdadm --remove /dev/{raid_set_name} /dev/{member}') diff --git a/python/vyos/template.py b/python/vyos/template.py index add4d3ce5..3be486cc4 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -678,6 +678,11 @@ def conntrack_ignore_rule(rule_conf, rule_id, ipv6=False): proto = rule_conf['protocol'] output.append(f'meta l4proto {proto}') + tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags') + if tcp_flags: + from vyos.firewall import parse_tcp_flags + output.append(parse_tcp_flags(tcp_flags)) + for side in ['source', 'destination']: if side in rule_conf: side_conf = rule_conf[side] diff --git a/python/vyos/utils/config.py b/python/vyos/utils/config.py new file mode 100644 index 000000000..bd363ce46 --- /dev/null +++ b/python/vyos/utils/config.py @@ -0,0 +1,34 @@ +# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os +from vyos.defaults import directories + +config_file = os.path.join(directories['config'], 'config.boot') + +def read_saved_value(path: list): + if not isinstance(path, list) or not path: + return '' + from vyos.configtree import ConfigTree + try: + with open(config_file) as f: + config_string = f.read() + ct = ConfigTree(config_string) + except Exception: + return '' + if not ct.exists(path): + return '' + res = ct.return_values(path) + return res[0] if len(res) == 1 else res diff --git a/python/vyos/utils/disk.py b/python/vyos/utils/disk.py new file mode 100644 index 000000000..ee540b107 --- /dev/null +++ b/python/vyos/utils/disk.py @@ -0,0 +1,23 @@ +# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from pathlib import Path + +def device_from_id(id): + """ Return the device name from (partial) disk id """ + path = Path('/dev/disk/by-id') + for device in path.iterdir(): + if device.name.endswith(id): + return device.readlink().stem diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py index 4c579c760..9354bd495 100644 --- a/python/vyos/utils/network.py +++ b/python/vyos/utils/network.py @@ -139,7 +139,7 @@ def is_ipv6_tentative(iface: str, ipv6_address: str) -> bool: import json from vyos.utils.process import rc_cmd - rc, out = rc_cmd(f'ip -6 --json address show dev {iface} scope global') + rc, out = rc_cmd(f'ip -6 --json address show dev {iface}') if rc: return False |