diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/high-availability.py | 21 | ||||
| -rwxr-xr-x | src/conf_mode/interfaces-pseudo-ethernet.py | 28 | ||||
| -rwxr-xr-x | src/conf_mode/nat.py | 18 | 
3 files changed, 46 insertions, 21 deletions
| diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py index e14050dd3..8a959dc79 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -88,15 +88,12 @@ def verify(ha):                  if not {'password', 'type'} <= set(group_config['authentication']):                      raise ConfigError(f'Authentication requires both type and passwortd to be set in VRRP group "{group}"') -            # We can not use a VRID once per interface +            # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction +            # We also need to make sure VRID is not used twice on the same interface with the +            # same address family. +              interface = group_config['interface']              vrid = group_config['vrid'] -            tmp = {'interface': interface, 'vrid': vrid} -            if tmp in used_vrid_if: -                raise ConfigError(f'VRID "{vrid}" can only be used once on interface "{interface}"!') -            used_vrid_if.append(tmp) - -            # Keepalived doesn't allow mixing IPv4 and IPv6 in one group, so we mirror that restriction              # XXX: filter on map object is destructive, so we force it to list.              # Additionally, filter objects always evaluate to True, empty or not, @@ -109,6 +106,11 @@ def verify(ha):                  raise ConfigError(f'VRRP group "{group}" mixes IPv4 and IPv6 virtual addresses, this is not allowed.\n' \                                    'Create individual groups for IPv4 and IPv6!')              if vaddrs4: +                tmp = {'interface': interface, 'vrid': vrid, 'ipver': 'IPv4'} +                if tmp in used_vrid_if: +                    raise ConfigError(f'VRID "{vrid}" can only be used once on interface "{interface} with address family IPv4"!') +                used_vrid_if.append(tmp) +                  if 'hello_source_address' in group_config:                      if is_ipv6(group_config['hello_source_address']):                          raise ConfigError(f'VRRP group "{group}" uses IPv4 but hello-source-address is IPv6!') @@ -118,6 +120,11 @@ def verify(ha):                          raise ConfigError(f'VRRP group "{group}" uses IPv4 but peer-address is IPv6!')              if vaddrs6: +                tmp = {'interface': interface, 'vrid': vrid, 'ipver': 'IPv6'} +                if tmp in used_vrid_if: +                    raise ConfigError(f'VRID "{vrid}" can only be used once on interface "{interface} with address family IPv6"!') +                used_vrid_if.append(tmp) +                  if 'hello_source_address' in group_config:                      if is_ipv4(group_config['hello_source_address']):                          raise ConfigError(f'VRRP group "{group}" uses IPv6 but hello-source-address is IPv4!') diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py index 20f2b1975..4c65bc0b6 100755 --- a/src/conf_mode/interfaces-pseudo-ethernet.py +++ b/src/conf_mode/interfaces-pseudo-ethernet.py @@ -15,11 +15,13 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  from sys import exit +from netifaces import interfaces  from vyos.config import Config  from vyos.configdict import get_interface_dict  from vyos.configdict import is_node_changed  from vyos.configdict import is_source_interface +from vyos.configdict import leaf_node_changed  from vyos.configverify import verify_vrf  from vyos.configverify import verify_address  from vyos.configverify import verify_bridge_delete @@ -49,6 +51,9 @@ def get_config(config=None):      mode = is_node_changed(conf, ['mode'])      if mode: peth.update({'shutdown_required' : {}}) +    if leaf_node_changed(conf, base + [ifname, 'mode']): +        peth.update({'rebuild_required': {}}) +      if 'source_interface' in peth:          _, peth['parent'] = get_interface_dict(conf, ['interfaces', 'ethernet'],                                                 peth['source_interface']) @@ -77,21 +82,18 @@ def generate(peth):      return None  def apply(peth): -    if 'deleted' in peth: -        # delete interface -        MACVLANIf(peth['ifname']).remove() -        return None +    # Check if the MACVLAN interface already exists +    if 'rebuild_required' in peth or 'deleted' in peth: +        if peth['ifname'] in interfaces(): +            p = MACVLANIf(peth['ifname']) +            # MACVLAN is always needs to be recreated, +            # thus we can simply always delete it first. +            p.remove() -    # Check if MACVLAN interface already exists. Parameters like the underlaying -    # source-interface device or mode can not be changed on the fly and the -    # interface needs to be recreated from the bottom. -    if 'mode_old' in peth: -        MACVLANIf(peth['ifname']).remove() +    if 'deleted' not in peth: +        p = MACVLANIf(**peth) +        p.update(peth) -    # 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) -    p.update(peth)      return None  if __name__ == '__main__': diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index a72e82a83..e75418ba5 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -45,6 +45,7 @@ else:      k_mod = ['nft_nat', 'nft_chain_nat_ipv4']  nftables_nat_config = '/run/nftables_nat.conf' +nftables_static_nat_conf = '/run/nftables_static-nat-rules.nft'  def get_handler(json, chain, target):      """ Get nftable rule handler number of given chain/target combination. @@ -88,7 +89,7 @@ def get_config(config=None):      # T2665: we must add the tagNode defaults individually until this is      # moved to the base class -    for direction in ['source', 'destination']: +    for direction in ['source', 'destination', 'static']:          if direction in nat:              default_values = defaults(base + [direction, 'rule'])              for rule in dict_search(f'{direction}.rule', nat) or []: @@ -178,20 +179,35 @@ def verify(nat):              # common rule verification              verify_rule(config, err_msg) +    if dict_search('static.rule', nat): +        for rule, config in dict_search('static.rule', nat).items(): +            err_msg = f'Static NAT configuration error in rule {rule}:' + +            if 'inbound_interface' not in config: +                raise ConfigError(f'{err_msg}\n' \ +                                  'inbound-interface not specified') + +            # common rule verification +            verify_rule(config, err_msg) +      return None  def generate(nat):      render(nftables_nat_config, 'firewall/nftables-nat.j2', nat) +    render(nftables_static_nat_conf, 'firewall/nftables-static-nat.j2', nat)      # dry-run newly generated configuration      tmp = run(f'nft -c -f {nftables_nat_config}')      if tmp > 0:          raise ConfigError('Configuration file errors encountered!') +    tmp = run(f'nft -c -f {nftables_nat_config}') +      return None  def apply(nat):      cmd(f'nft -f {nftables_nat_config}') +    cmd(f'nft -f {nftables_static_nat_conf}')      return None | 
