summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/dhcp-client/override.conf.j22
-rw-r--r--data/templates/frr/bgpd.frr.j22
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i33
-rw-r--r--interface-definitions/include/firewall/firewall-hashing-parameters.xml.i35
-rw-r--r--interface-definitions/include/firewall/nat-balance.xml.i28
-rw-r--r--interface-definitions/include/nat-rule.xml.i9
-rw-r--r--op-mode-definitions/rpki.xml.in (renamed from op-mode-definitions/show-rpki.xml.in)10
-rw-r--r--python/vyos/ifconfig/interface.py4
-rw-r--r--python/vyos/nat.py33
-rw-r--r--python/vyos/qos/base.py21
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py5
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py36
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py5
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py4
-rwxr-xr-xsrc/conf_mode/nat.py17
-rw-r--r--src/systemd/dhclient@.service2
18 files changed, 217 insertions, 36 deletions
diff --git a/data/templates/dhcp-client/override.conf.j2 b/data/templates/dhcp-client/override.conf.j2
index d4d5eb49f..03fd71bf1 100644
--- a/data/templates/dhcp-client/override.conf.j2
+++ b/data/templates/dhcp-client/override.conf.j2
@@ -10,6 +10,6 @@ ConditionPathExists={{ isc_dhclient_dir }}/dhclient_%i.conf
ExecStart=
ExecStart={{ vrf_command }}/sbin/dhclient -4 {{ dhclient_options }} {{ ifname }}
ExecStop=
-ExecStop={{ vrf_command }}/sbin/dhclient -4 {{ dhclient_options }} {{ ifname }} -r
+ExecStop=/sbin/dhclient -4 -r {{ dhclient_options }} {{ ifname }}
WorkingDirectory={{ isc_dhclient_dir }}
PIDFile={{ isc_dhclient_dir }}/dhclient_%i.pid
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index ddfba2306..4535758da 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -481,7 +481,7 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
bgp bestpath compare-routerid
{% endif %}
{% if parameters.bestpath.med is vyos_defined %}
- bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is vyos_defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is vyos_defined }}
+ bgp bestpath med {{ parameters.bestpath.med | join(' ') | replace('_', '-') }}
{% endif %}
{% if parameters.bestpath.peer_type is vyos_defined %}
bgp bestpath peer-type {{ 'multipath-relax' if parameters.bestpath.peer_type.multipath_relax is vyos_defined }}
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 12024ed8b..504385b53 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1123,25 +1123,26 @@
<valueless/>
</properties>
</leafNode>
- <node name="med">
+ <leafNode name="med">
<properties>
<help>MED attribute comparison parameters</help>
+ <completionHelp>
+ <list>confed missing-as-worst</list>
+ </completionHelp>
+ <valueHelp>
+ <format>confed</format>
+ <description>Compare MEDs among confederation paths</description>
+ </valueHelp>
+ <valueHelp>
+ <format>missing-as-worst</format>
+ <description>Treat missing route as a MED as the least preferred one</description>
+ </valueHelp>
+ <constraint>
+ <regex>(confed|missing-as-worst)</regex>
+ </constraint>
+ <multi/>
</properties>
- <children>
- <leafNode name="confed">
- <properties>
- <help>Compare MEDs among confederation paths</help>
- <valueless/>
- </properties>
- </leafNode>
- <leafNode name="missing-as-worst">
- <properties>
- <help>Treat missing route as a MED as the least preferred one</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
+ </leafNode>
<node name="peer-type">
<properties>
<help>Peer type</help>
diff --git a/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i b/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i
new file mode 100644
index 000000000..7f34de3ba
--- /dev/null
+++ b/interface-definitions/include/firewall/firewall-hashing-parameters.xml.i
@@ -0,0 +1,35 @@
+<!-- include start from firewall/firewall-hashing-parameters.xml.i -->
+<leafNode name="hash">
+ <properties>
+ <help>Define the parameters of the packet header to apply the hashing</help>
+ <completionHelp>
+ <list>source-address destination-address source-port destination-port random</list>
+ </completionHelp>
+ <valueHelp>
+ <format>source-address</format>
+ <description>Use source IP address for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>destination-address</format>
+ <description>Use destination IP address for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>source-port</format>
+ <description>Use source port for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>destination-port</format>
+ <description>Use destination port for hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>random</format>
+ <description>Do not use information from ip header. Use random value.</description>
+ </valueHelp>
+ <constraint>
+ <regex>(source-address|destination-address|source-port|destination-port|random)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ <defaultValue>random</defaultValue>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/nat-balance.xml.i b/interface-definitions/include/firewall/nat-balance.xml.i
new file mode 100644
index 000000000..01793f06b
--- /dev/null
+++ b/interface-definitions/include/firewall/nat-balance.xml.i
@@ -0,0 +1,28 @@
+<!-- include start from firewall/nat-balance.xml.i -->
+<tagNode name="backend">
+ <properties>
+ <help>Translated IP address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="weight">
+ <properties>
+ <help>Set probability for this output value</help>
+ <valueHelp>
+ <format>u32:1-100</format>
+ <description>Set probability for this output value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 1-100"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i
index 7b3b8804e..6234e6195 100644
--- a/interface-definitions/include/nat-rule.xml.i
+++ b/interface-definitions/include/nat-rule.xml.i
@@ -25,6 +25,15 @@
</node>
#include <include/generic-disable-node.xml.i>
#include <include/nat-exclude.xml.i>
+ <node name="load-balance">
+ <properties>
+ <help>Apply NAT load balance</help>
+ </properties>
+ <children>
+ #include <include/firewall/firewall-hashing-parameters.xml.i>
+ #include <include/firewall/nat-balance.xml.i>
+ </children>
+ </node>
<leafNode name="log">
<properties>
<help>NAT rule logging</help>
diff --git a/op-mode-definitions/show-rpki.xml.in b/op-mode-definitions/rpki.xml.in
index c1902ccec..72d378b88 100644
--- a/op-mode-definitions/show-rpki.xml.in
+++ b/op-mode-definitions/rpki.xml.in
@@ -29,4 +29,14 @@
</node>
</children>
</node>
+ <node name="reset">
+ <children>
+ <leafNode name="rpki">
+ <properties>
+ <help>Reset RPKI</help>
+ </properties>
+ <command>vtysh -c "rpki reset"</command>
+ </leafNode>
+ </children>
+ </node>
</interfaceDefinition>
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 8ed5cb114..75c5f27a9 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -1275,7 +1275,6 @@ class Interface(Control):
# running, but it should be running (e.g. on system startup)
if 'dhcp_options_changed' in self.config or not is_systemd_service_active(systemd_service):
return self._cmd(f'systemctl restart {systemd_service}')
- return None
else:
if is_systemd_service_active(systemd_service):
self._cmd(f'systemctl stop {systemd_service}')
@@ -1284,6 +1283,7 @@ class Interface(Control):
if os.path.isfile(file):
os.remove(file)
+ return None
def set_dhcpv6(self, enable):
"""
@@ -1317,6 +1317,8 @@ class Interface(Control):
if os.path.isfile(config_file):
os.remove(config_file)
+ return None
+
def set_mirror_redirect(self):
# Please refer to the document for details
# - https://man7.org/linux/man-pages/man8/tc.8.html
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
index 603fedb9b..418efe649 100644
--- a/python/vyos/nat.py
+++ b/python/vyos/nat.py
@@ -94,6 +94,39 @@ def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
if options:
translation_str += f' {",".join(options)}'
+ if 'backend' in rule_conf['load_balance']:
+ hash_input_items = []
+ current_prob = 0
+ nat_map = []
+
+ for trans_addr, addr in rule_conf['load_balance']['backend'].items():
+ item_prob = int(addr['weight'])
+ upper_limit = current_prob + item_prob - 1
+ hash_val = str(current_prob) + '-' + str(upper_limit)
+ element = hash_val + " : " + trans_addr
+ nat_map.append(element)
+ current_prob = current_prob + item_prob
+
+ elements = ' , '.join(nat_map)
+
+ if 'hash' in rule_conf['load_balance'] and 'random' in rule_conf['load_balance']['hash']:
+ translation_str += ' numgen random mod 100 map ' + '{ ' + f'{elements}' + ' }'
+ else:
+ for input_param in rule_conf['load_balance']['hash']:
+ if input_param == 'source-address':
+ param = 'ip saddr'
+ elif input_param == 'destination-address':
+ param = 'ip daddr'
+ elif input_param == 'source-port':
+ prot = rule_conf['protocol']
+ param = f'{prot} sport'
+ elif input_param == 'destination-port':
+ prot = rule_conf['protocol']
+ param = f'{prot} dport'
+ hash_input_items.append(param)
+ hash_input = ' . '.join(hash_input_items)
+ translation_str += f' jhash ' + f'{hash_input}' + ' mod 100 map ' + '{ ' + f'{elements}' + ' }'
+
for target in ['source', 'destination']:
if target not in rule_conf:
continue
diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py
index 1eac0d1ee..d8bbfe970 100644
--- a/python/vyos/qos/base.py
+++ b/python/vyos/qos/base.py
@@ -298,6 +298,27 @@ class QoSBase:
filter_cmd += f' flowid {self._parent:x}:{cls:x}'
self._cmd(filter_cmd)
+ if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config):
+ filter_cmd += f' action police'
+
+ if 'exceed' in cls_config:
+ action = cls_config['exceed']
+ filter_cmd += f' conform-exceed {action}'
+ if 'not_exceed' in cls_config:
+ action = cls_config['not_exceed']
+ filter_cmd += f'/{action}'
+
+ if 'bandwidth' in cls_config:
+ rate = self._rate_convert(cls_config['bandwidth'])
+ filter_cmd += f' rate {rate}'
+
+ if 'burst' in cls_config:
+ burst = cls_config['burst']
+ filter_cmd += f' burst {burst}'
+ cls = int(cls)
+ filter_cmd += f' flowid {self._parent:x}:{cls:x}'
+ self._cmd(filter_cmd)
+
else:
filter_cmd += ' basic'
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 4244410bb..cef61e42b 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -146,8 +146,6 @@ class BasicInterfaceTest:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
- self.cli_set(self._base_path + [interface, 'disable'])
-
# Also enable DHCP (ISC DHCP always places interface in admin up
# state so we check that we do not start DHCP client.
# https://vyos.dev/T2767
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index d8e6bde5c..8867cb427 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -37,9 +37,8 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
if 'TEST_ETH' in os.environ:
cls._members = os.environ['TEST_ETH'].split()
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._members.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._members.append(tmp)
cls._options = {'bond0' : []}
for member in cls._members:
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index ce46643fb..cdff49f4b 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -41,9 +41,8 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
if 'TEST_ETH' in os.environ:
cls._members = os.environ['TEST_ETH'].split()
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._members.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._members.append(tmp)
cls._options['br0'] = []
for member in cls._members:
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 28d566eba..e6eaedeff 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -252,5 +252,41 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_nat')
+ def test_nat_balance(self):
+ ifname = 'eth0'
+ member_1 = '198.51.100.1'
+ weight_1 = '10'
+ member_2 = '198.51.100.2'
+ weight_2 = '90'
+ member_3 = '192.0.2.1'
+ weight_3 = '35'
+ member_4 = '192.0.2.2'
+ weight_4 = '65'
+ dst_port = '443'
+
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
+ self.cli_set(dst_path + ['rule', '1', 'destination', 'port', dst_port])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'source-address'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'source-port'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'destination-address'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'destination-port'])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_1, 'weight', weight_1])
+ self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_2, 'weight', weight_2])
+
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', ifname])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'hash', 'random'])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_3, 'weight', weight_3])
+ self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_4, 'weight', weight_4])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'iifname "{ifname}"', f'tcp dport {dst_port}', f'dnat to jhash ip saddr . tcp sport . ip daddr . tcp dport mod 100 map', f'0-9 : {member_1}, 10-99 : {member_2}'],
+ [f'oifname "{ifname}"', f'snat to numgen random mod 100 map', f'0-34 : {member_3}, 35-99 : {member_4}']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index b4faac67d..5fb599a87 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -47,9 +47,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
tmp = os.environ['TEST_ETH'].split()
cls._interfaces = tmp
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._interfaces.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._interfaces.append(tmp)
# call base-classes classmethod
super(VRFTest, cls).setUpClass()
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index b3b4e6342..0bd306ed0 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -195,11 +195,11 @@ def verify(bond):
raise ConfigError(error_msg + 'it does not exist!')
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 444a3fef6..f9d711b36 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -124,6 +124,18 @@ def verify_rule(config, err_msg, groups_dict):
if config['protocol'] not in ['tcp', 'udp', 'tcp_udp']:
raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port-group')
+ if 'load_balance' in config:
+ for item in ['source-port', 'destination-port']:
+ if item in config['load_balance']['hash'] and config['protocol'] not in ['tcp', 'udp']:
+ raise ConfigError('Protocol must be tcp or udp when specifying hash ports')
+ count = 0
+ if 'backend' in config['load_balance']:
+ for member in config['load_balance']['backend']:
+ weight = config['load_balance']['backend'][member]['weight']
+ count = count + int(weight)
+ if count != 100:
+ Warning(f'Sum of weight for nat load balance rule is not 100. You may get unexpected behaviour')
+
def get_config(config=None):
if config:
conf = config
@@ -190,7 +202,7 @@ def verify(nat):
Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config):
- if 'exclude' not in config:
+ if 'exclude' not in config and 'backend' not in config['load_balance']:
raise ConfigError(f'{err_msg} translation requires address and/or port')
addr = dict_search('translation.address', config)
@@ -202,7 +214,6 @@ def verify(nat):
# common rule verification
verify_rule(config, err_msg, nat['firewall_group'])
-
if dict_search('destination.rule', nat):
for rule, config in dict_search('destination.rule', nat).items():
err_msg = f'Destination NAT configuration error in rule {rule}:'
@@ -214,7 +225,7 @@ def verify(nat):
Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config) and not dict_search('translation.redirect.port', config):
- if 'exclude' not in config:
+ if 'exclude' not in config and 'backend' not in config['load_balance']:
raise ConfigError(f'{err_msg} translation requires address and/or port')
# common rule verification
diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service
index 6d41de232..099f7ed52 100644
--- a/src/systemd/dhclient@.service
+++ b/src/systemd/dhclient@.service
@@ -7,7 +7,7 @@ After=vyos-router.service
[Service]
Type=exec
ExecStart=/sbin/dhclient -4 -d $DHCLIENT_OPTS
-ExecStop=/sbin/dhclient -4 -d $DHCLIENT_OPTS -r
+ExecStop=/sbin/dhclient -4 -r $DHCLIENT_OPTS
Restart=always
RestartPreventExitStatus=
RestartSec=10