diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/configtree.py | 36 | ||||
-rwxr-xr-x | python/vyos/firewall.py | 53 | ||||
-rw-r--r-- | python/vyos/nat.py | 7 | ||||
-rw-r--r-- | python/vyos/utils/config.py | 66 | ||||
-rw-r--r-- | python/vyos/xml_ref/definition.py | 28 |
5 files changed, 168 insertions, 22 deletions
diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index bd77ab899..ee8ca8b83 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -88,6 +88,10 @@ class ConfigTree(object): self.__to_json_ast.argtypes = [c_void_p] self.__to_json_ast.restype = c_char_p + self.__create_node = self.__lib.create_node + self.__create_node.argtypes = [c_void_p, c_char_p] + self.__create_node.restype = c_int + self.__set_add_value = self.__lib.set_add_value self.__set_add_value.argtypes = [c_void_p, c_char_p, c_char_p] self.__set_add_value.restype = c_int @@ -140,6 +144,14 @@ class ConfigTree(object): self.__set_tag.argtypes = [c_void_p, c_char_p] self.__set_tag.restype = c_int + self.__is_leaf = self.__lib.is_leaf + self.__is_leaf.argtypes = [c_void_p, c_char_p] + self.__is_leaf.restype = c_bool + + self.__set_leaf = self.__lib.set_leaf + self.__set_leaf.argtypes = [c_void_p, c_char_p, c_bool] + self.__set_leaf.restype = c_int + self.__get_subtree = self.__lib.get_subtree self.__get_subtree.argtypes = [c_void_p, c_char_p] self.__get_subtree.restype = c_void_p @@ -197,6 +209,14 @@ class ConfigTree(object): def to_json_ast(self): return self.__to_json_ast(self.__config).decode() + def create_node(self, path): + check_path(path) + path_str = " ".join(map(str, path)).encode() + + res = self.__create_node(self.__config, path_str) + if (res != 0): + raise ConfigTreeError(f"Path already exists: {path}") + def set(self, path, value=None, replace=True): """Set new entry in VyOS configuration. path: configuration path e.g. 'system dns forwarding listen-address' @@ -349,6 +369,22 @@ class ConfigTree(object): else: raise ConfigTreeError("Path [{}] doesn't exist".format(path_str)) + def is_leaf(self, path): + check_path(path) + path_str = " ".join(map(str, path)).encode() + + return self.__is_leaf(self.__config, path_str) + + def set_leaf(self, path, value): + check_path(path) + path_str = " ".join(map(str, path)).encode() + + res = self.__set_leaf(self.__config, path_str, value) + if (res == 0): + return True + else: + raise ConfigTreeError("Path [{}] doesn't exist".format(path_str)) + def get_subtree(self, path, with_node=False): check_path(path) path_str = " ".join(map(str, path)).encode() diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 64fed8177..314e8dfe3 100755 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -53,25 +53,32 @@ def conntrack_required(conf): # Domain Resolver -def fqdn_config_parse(firewall): - firewall['ip_fqdn'] = {} - firewall['ip6_fqdn'] = {} - - for domain, path in dict_search_recursive(firewall, 'fqdn'): - hook_name = path[1] - priority = path[2] - - fw_name = path[2] - rule = path[4] - suffix = path[5][0] - set_name = f'{hook_name}_{priority}_{rule}_{suffix}' - - if (path[0] == 'ipv4') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'): - firewall['ip_fqdn'][set_name] = domain - elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'): - if path[1] == 'name': - set_name = f'name6_{priority}_{rule}_{suffix}' - firewall['ip6_fqdn'][set_name] = domain +def fqdn_config_parse(config, node): + config['ip_fqdn'] = {} + config['ip6_fqdn'] = {} + + for domain, path in dict_search_recursive(config, 'fqdn'): + if node != 'nat': + hook_name = path[1] + priority = path[2] + + rule = path[4] + suffix = path[5][0] + set_name = f'{hook_name}_{priority}_{rule}_{suffix}' + + if (path[0] == 'ipv4') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'): + config['ip_fqdn'][set_name] = domain + elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'): + if path[1] == 'name': + set_name = f'name6_{priority}_{rule}_{suffix}' + config['ip6_fqdn'][set_name] = domain + else: + # Parse FQDN for NAT + nat_direction = path[0] + nat_rule = path[2] + suffix = path[3][0] + set_name = f'{nat_direction}_{nat_rule}_{suffix}' + config['ip_fqdn'][set_name] = domain def fqdn_resolve(fqdn, ipv6=False): try: @@ -80,8 +87,6 @@ def fqdn_resolve(fqdn, ipv6=False): except: return None -# End Domain Resolver - def find_nftables_rule(table, chain, rule_matches=[]): # Find rule in table/chain that matches all criteria and return the handle results = cmd(f'sudo nft --handle list chain {table} {chain}').split("\n") @@ -578,6 +583,12 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'tcp_mss' in rule_conf['set']: mss = rule_conf['set']['tcp_mss'] output.append(f'tcp option maxseg size set {mss}') + if 'ttl' in rule_conf['set']: + ttl = rule_conf['set']['ttl'] + output.append(f'ip ttl set {ttl}') + if 'hop_limit' in rule_conf['set']: + hoplimit = rule_conf['set']['hop_limit'] + output.append(f'ip6 hoplimit set {hoplimit}') if 'action' in rule_conf: if rule_conf['action'] == 'offload': diff --git a/python/vyos/nat.py b/python/vyos/nat.py index 5fab3c2a1..29f8e961b 100644 --- a/python/vyos/nat.py +++ b/python/vyos/nat.py @@ -242,6 +242,13 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False): output.append(f'{proto} {prefix}port {operator} @P_{group_name}') + if 'fqdn' in side_conf: + fqdn = side_conf['fqdn'] + operator = '' + if fqdn[0] == '!': + operator = '!=' + output.append(f' ip {prefix}addr {operator} @FQDN_nat_{nat_type}_{rule_id}_{prefix}') + output.append('counter') if 'log' in rule_conf: diff --git a/python/vyos/utils/config.py b/python/vyos/utils/config.py index 33047010b..deda13c13 100644 --- a/python/vyos/utils/config.py +++ b/python/vyos/utils/config.py @@ -14,8 +14,14 @@ # License along with this library. If not, see <http://www.gnu.org/licenses/>. import os +from typing import TYPE_CHECKING + from vyos.defaults import directories +# https://peps.python.org/pep-0484/#forward-references +if TYPE_CHECKING: + from vyos.configtree import ConfigTree + config_file = os.path.join(directories['config'], 'config.boot') def read_saved_value(path: list): @@ -37,3 +43,63 @@ def read_saved_value(path: list): if len(res) == 1: return ' '.join(res) return res + +def flag(l: list) -> list: + res = [l[0:i] for i,_ in enumerate(l, start=1)] + return res + +def tag_node_of_path(p: list) -> list: + from vyos.xml_ref import is_tag + + fl = flag(p) + res = list(map(is_tag, fl)) + + return res + +def set_tags(ct: 'ConfigTree', path: list) -> None: + fl = flag(path) + if_tag = tag_node_of_path(path) + for condition, target in zip(if_tag, fl): + if condition: + ct.set_tag(target) + +def parse_commands(cmds: str) -> dict: + from re import split as re_split + from shlex import split as shlex_split + + from vyos.xml_ref import definition + from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference + + ref_tree = definition.Xml() + ref_tree.define(reference) + + res = [] + + cmds = re_split(r'\n+', cmds) + for c in cmds: + cmd_parts = shlex_split(c) + + if not cmd_parts: + # Ignore empty lines + continue + + path = cmd_parts[1:] + op = cmd_parts[0] + + try: + path, value = ref_tree.split_path(path) + except ValueError as e: + raise ValueError(f'Incorrect command: {e}') + + entry = {} + entry["op"] = op + entry["path"] = path + entry["value"] = value + + entry["is_multi"] = ref_tree.is_multi(path) + entry["is_leaf"] = ref_tree.is_leaf(path) + entry["is_tag"] = ref_tree.is_tag(path) + + res.append(entry) + + return res diff --git a/python/vyos/xml_ref/definition.py b/python/vyos/xml_ref/definition.py index 5ff28daed..4e755ab72 100644 --- a/python/vyos/xml_ref/definition.py +++ b/python/vyos/xml_ref/definition.py @@ -13,7 +13,7 @@ # 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 typing import Optional, Union, Any, TYPE_CHECKING +from typing import Tuple, Optional, Union, Any, TYPE_CHECKING # https://peps.python.org/pep-0484/#forward-references # for type 'ConfigDict' @@ -90,6 +90,32 @@ class Xml: res = self._get_ref_node_data(node, 'node_type') return res == 'tag' + def exists(self, path: list) -> bool: + try: + _ = self._get_ref_path(path) + return True + except ValueError: + return False + + def split_path(self, path: list) -> Tuple[list, Optional[str]]: + """ Splits a list into config path and value components """ + + # First, check if the complete path is valid by itself + if self.exists(path): + if self.is_valueless(path) or not self.is_leaf(path): + # It's a complete path for a valueless node + # or a path to an empy non-leaf node + return (path, None) + else: + raise ValueError(f'Path "{path}" needs a value or children') + else: + # If the complete path doesn't exist, it's probably a path with a value + if self.exists(path[0:-1]): + return (path[0:-1], path[-1]) + else: + # Or not a valid path at all + raise ValueError(f'Path "{path}" is incorrect') + def is_tag(self, path: list) -> bool: ref_path = path.copy() d = self.ref |