diff options
-rw-r--r-- | data/op-mode-standardized.json | 1 | ||||
-rw-r--r-- | data/templates/pppoe/peer.j2 | 4 | ||||
-rw-r--r-- | interface-definitions/include/firewall/common-rule.xml.i | 2 | ||||
-rw-r--r-- | interface-definitions/include/firewall/rule-log-options.xml.i | 89 | ||||
-rw-r--r-- | interface-definitions/interfaces-pppoe.xml.in | 14 | ||||
-rw-r--r-- | op-mode-definitions/reset-vpn.xml.in | 120 | ||||
-rw-r--r-- | op-mode-definitions/vpn-ipsec.xml.in | 97 | ||||
-rw-r--r-- | python/vyos/configdict.py | 10 | ||||
-rw-r--r-- | python/vyos/firewall.py | 19 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 18 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_vpn_ipsec.py | 10 | ||||
-rwxr-xr-x | smoketest/scripts/system/test_kernel_options.py | 24 | ||||
-rwxr-xr-x | src/conf_mode/dhcp_server.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/firewall.py | 10 | ||||
-rwxr-xr-x | src/conf_mode/protocols_ospf.py | 4 | ||||
-rwxr-xr-x | src/conf_mode/vpn_ipsec.py | 2 | ||||
-rwxr-xr-x | src/migration-scripts/firewall/9-to-10 | 80 | ||||
-rwxr-xr-x | src/op_mode/accelppp.py | 6 | ||||
-rwxr-xr-x | src/op_mode/dns.py | 4 | ||||
-rwxr-xr-x | src/op_mode/ipsec.py | 81 | ||||
-rwxr-xr-x | src/op_mode/openvpn.py | 23 | ||||
-rwxr-xr-x | src/op_mode/reset_vpn.py | 75 |
22 files changed, 514 insertions, 181 deletions
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json index 3b2599790..c7c67198e 100644 --- a/data/op-mode-standardized.json +++ b/data/op-mode-standardized.json @@ -17,6 +17,7 @@ "nhrp.py", "openconnect.py", "openvpn.py", +"reset_vpn.py", "route.py", "system.py", "ipsec.py", diff --git a/data/templates/pppoe/peer.j2 b/data/templates/pppoe/peer.j2 index 5e650fa3b..f30cefe63 100644 --- a/data/templates/pppoe/peer.j2 +++ b/data/templates/pppoe/peer.j2 @@ -65,6 +65,10 @@ mru {{ mtu }} noipv6 {% endif %} +{% if holdoff is vyos_defined %} +holdoff {{ holdoff }} +{% endif %} + {% if connect_on_demand is vyos_defined %} demand # See T2249. PPP default route options should only be set when in on-demand diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i index 3fe3ca872..7417a3c58 100644 --- a/interface-definitions/include/firewall/common-rule.xml.i +++ b/interface-definitions/include/firewall/common-rule.xml.i @@ -119,7 +119,7 @@ </constraint> </properties> </leafNode> -#include <include/firewall/rule-log-level.xml.i> +#include <include/firewall/rule-log-options.xml.i> <node name="connection-status"> <properties> <help>Connection status</help> diff --git a/interface-definitions/include/firewall/rule-log-options.xml.i b/interface-definitions/include/firewall/rule-log-options.xml.i new file mode 100644 index 000000000..e8b0cdec3 --- /dev/null +++ b/interface-definitions/include/firewall/rule-log-options.xml.i @@ -0,0 +1,89 @@ +<!-- include start from firewall/rule-log-options.xml.i --> +<node name="log-options"> + <properties> + <help>Log options</help> + </properties> + <children> + <leafNode name="group"> + <properties> + <help>Set log group</help> + <valueHelp> + <format>u32:0-65535</format> + <description>Log group to send messages to</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="snapshot-length"> + <properties> + <help>Length of packet payload to include in netlink message</help> + <valueHelp> + <format>u32:0-9000</format> + <description>Length of packet payload to include in netlink message</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-9000"/> + </constraint> + </properties> + </leafNode> + <leafNode name="queue-threshold"> + <properties> + <help>Number of packets to queue inside the kernel before sending them to userspace</help> + <valueHelp> + <format>u32:0-65535</format> + <description>Number of packets to queue inside the kernel before sending them to userspace</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="level"> + <properties> + <help>Set log-level</help> + <completionHelp> + <list>emerg alert crit err warn notice info debug</list> + </completionHelp> + <valueHelp> + <format>emerg</format> + <description>Emerg log level</description> + </valueHelp> + <valueHelp> + <format>alert</format> + <description>Alert log level</description> + </valueHelp> + <valueHelp> + <format>crit</format> + <description>Critical log level</description> + </valueHelp> + <valueHelp> + <format>err</format> + <description>Error log level</description> + </valueHelp> + <valueHelp> + <format>warn</format> + <description>Warning log level</description> + </valueHelp> + <valueHelp> + <format>notice</format> + <description>Notice log level</description> + </valueHelp> + <valueHelp> + <format>info</format> + <description>Info log level</description> + </valueHelp> + <valueHelp> + <format>debug</format> + <description>Debug log level</description> + </valueHelp> + <constraint> + <regex>(emerg|alert|crit|err|warn|notice|info|debug)</regex> + </constraint> + <constraintErrorMessage>level must be alert, crit, debug, emerg, err, info, notice or warn</constraintErrorMessage> + </properties> + </leafNode> + </children> +</node> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index c6fd7096b..b78f92c85 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -50,6 +50,20 @@ <constraintErrorMessage>Host-uniq must be specified as hex-adecimal byte-string (even number of HEX characters)</constraintErrorMessage> </properties> </leafNode> + <leafNode name="holdoff"> + <properties> + <help>Delay before re-dial to the access concentrator when PPP session terminated by peer (in seconds)</help> + <valueHelp> + <format>u32:0-86400</format> + <description>Holdoff time in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-86400"/> + </constraint> + <constraintErrorMessage>Holdoff must be in range 0 to 86400</constraintErrorMessage> + </properties> + <defaultValue>30</defaultValue> + </leafNode> <node name="ip"> <properties> <help>IPv4 routing parameters</help> diff --git a/op-mode-definitions/reset-vpn.xml.in b/op-mode-definitions/reset-vpn.xml.in index 94ee1c7df..8de95d1cc 100644 --- a/op-mode-definitions/reset-vpn.xml.in +++ b/op-mode-definitions/reset-vpn.xml.in @@ -7,82 +7,78 @@ <help>Reset Virtual Private Network (VPN) information</help> </properties> <children> - <node name="remote-access"> + <node name="l2tp"> <properties> - <help>Reset remote access VPN connections</help> + <help>Reset L2TP server VPN sessions</help> </properties> <children> <node name="all"> <properties> - <help>Terminate all users current remote access VPN session(s)</help> + <help>Reset all L2TP server VPN sessions</help> </properties> - <children> - <node name="protocol"> - <properties> - <help>Terminate specified users current remote access VPN session(s) with specified protocol</help> - </properties> - <children> - <leafNode name="l2tp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with L2TP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="l2tp"</command> - </leafNode> - <leafNode name="pptp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with PPTP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="pptp"</command> - </leafNode> - <leafNode name="sstp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with SSTP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users" --protocol="sstp"</command> - </leafNode> - </children> - </node> - </children> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="all_users"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="l2tp"</command> </node> <tagNode name="interface"> <properties> - <help>Terminate a remote access VPN interface</help> + <help>Reset specified interface on L2TP VPN server</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --interface="$5"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="l2tp" --interface="$5"</command> </tagNode> <tagNode name="user"> <properties> - <help>Terminate specified users current remote access VPN session(s)</help> + <help>Reset specified user on L2TP VPN server</help> </properties> - <children> - <node name="protocol"> - <properties> - <help>Terminate specified users current remote access VPN session(s) with specified protocol</help> - </properties> - <children> - <leafNode name="l2tp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with L2TP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="l2tp"</command> - </leafNode> - <leafNode name="pptp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with PPTP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="pptp"</command> - </leafNode> - <leafNode name="sstp"> - <properties> - <help>Terminate all users current remote access VPN session(s) with SSTP protocol</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5" --protocol="sstp"</command> - </leafNode> - </children> - </node> - </children> - <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py --username="$5"</command> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="l2tp" --username="$5"</command> + </tagNode> + </children> + </node> + <node name="pptp"> + <properties> + <help>Reset PPTP server VPN sessions</help> + </properties> + <children> + <node name="all"> + <properties> + <help>Reset all PPTP server VPN sessions</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="pptp"</command> + </node> + <tagNode name="interface"> + <properties> + <help>Reset specified interface on PPTP VPN server</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="pptp" --interface="$5"</command> + </tagNode> + <tagNode name="user"> + <properties> + <help>Reset specified user on PPTP VPN server</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="pptp" --username="$5"</command> + </tagNode> + </children> + </node> + <node name="sstp"> + <properties> + <help>Reset SSTP server VPN sessions</help> + </properties> + <children> + <node name="all"> + <properties> + <help>Reset all SSTP server VPN sessions</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="sstp"</command> + </node> + <tagNode name="interface"> + <properties> + <help>Reset specified interface on SSTP VPN server</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="sstp" --interface="$5"</command> + </tagNode> + <tagNode name="user"> + <properties> + <help>Reset specified user on SSTP VPN server</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/reset_vpn.py reset_conn --protocol="sstp" --username="$5"</command> </tagNode> </children> </node> diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index ee006a2d5..5baaec7ce 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -7,49 +7,88 @@ <help>Reset Virtual Private Network (VPN) information</help> </properties> <children> - <tagNode name="ipsec-peer"> + <node name="ipsec"> <properties> - <help>Reset all tunnels for given peer</help> - <completionHelp> - <path>vpn ipsec site-to-site peer</path> - </completionHelp> + <help>Reset IPSec VPN sessions</help> </properties> <children> - <tagNode name="tunnel"> + <tagNode name="profile"> <properties> - <help>Reset a specific tunnel for given peer</help> + <help>Reset all tunnels for given DMVPN profile</help> <completionHelp> - <path>vpn ipsec site-to-site peer ${COMP_WORDS[3]} tunnel</path> + <path>vpn ipsec profile</path> </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="$6"</command> + <children> + <tagNode name="tunnel"> + <properties> + <help>Reset a specific tunnel for given DMVPN profile</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="$8"</command> + </tagNode> + </children> + <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="all"</command> </tagNode> - <node name="vti"> + <node name="remote-access"> <properties> - <help>Reset the VTI tunnel for given peer</help> + <help>Reset remote access IPSec VPN connections</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4" --tunnel="vti"</command> + <children> + <node name="all"> + <properties> + <help>Reset all users current remote access IPSec VPN sessions</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_ra</command> + </node> + <tagNode name="user"> + <properties> + <help>Reset specified user current remote access IPsec VPN session(s)</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_ra --user="$6"</command> + </tagNode> + </children> </node> - </children> - <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$4"</command> - </tagNode> - <tagNode name="ipsec-profile"> - <properties> - <help>Reset all tunnels for given DMVPN profile</help> - <completionHelp> - <path>vpn ipsec profile</path> - </completionHelp> - </properties> - <children> - <tagNode name="tunnel"> + <node name="site-to-site"> <properties> - <help>Reset a specific tunnel for given DMVPN profile</help> + <help>Reset site-to-site IPSec VPN connections</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$4" --tunnel="$6"</command> - </tagNode> + <children> + <node name="all"> + <properties> + <help>Reset all site-to-site IPSec VPN sessions</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_all_peers</command> + </node> + <tagNode name="peer"> + <properties> + <help>Reset all tunnels for given peer</help> + <completionHelp> + <path>vpn ipsec site-to-site peer</path> + </completionHelp> + </properties> + <children> + <tagNode name="tunnel"> + <properties> + <help>Reset a specific tunnel for given peer</help> + <completionHelp> + <path>vpn ipsec site-to-site peer ${COMP_WORDS[5]} tunnel</path> + </completionHelp> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6" --tunnel="$8"</command> + </tagNode> + <node name="vti"> + <properties> + <help>Reset the VTI tunnel for given peer</help> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6" --tunnel="vti"</command> + </node> + </children> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_peer --peer="$6"</command> + </tagNode> + </children> + </node> </children> - <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$4" --tunnel="all"</command> - </tagNode> + </node> </children> </node> </children> diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 434ff99d7..6ab5c252c 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -333,8 +333,9 @@ def get_dhcp_interfaces(conf, vrf=None): if dict_search('dhcp_options.default_route_distance', config) != None: options.update({'dhcp_options' : config['dhcp_options']}) if 'vrf' in config: - if vrf is config['vrf']: tmp.update({ifname : options}) - else: tmp.update({ifname : options}) + if vrf == config['vrf']: tmp.update({ifname : options}) + else: + if vrf is None: tmp.update({ifname : options}) return tmp @@ -382,8 +383,9 @@ def get_pppoe_interfaces(conf, vrf=None): if 'no_default_route' in ifconfig: options.update({'no_default_route' : {}}) if 'vrf' in ifconfig: - if vrf is ifconfig['vrf']: pppoe_interfaces.update({ifname : options}) - else: pppoe_interfaces.update({ifname : options}) + if vrf == ifconfig['vrf']: pppoe_interfaces.update({ifname : options}) + else: + if vrf is None: pppoe_interfaces.update({ifname : options}) return pppoe_interfaces diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 5be897d5f..919032a41 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -223,10 +223,23 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name): action = rule_conf['action'] if 'action' in rule_conf else 'accept' output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"') - if 'log_level' in rule_conf: - log_level = rule_conf['log_level'] - output.append(f'level {log_level}') + 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 'hop_limit' in rule_conf: operators = {'eq': '==', 'gt': '>', 'lt': '<'} diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index d61534d87..e071b7df9 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -207,13 +207,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'log', 'enable']) - self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-level', 'debug']) + self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-options', 'level', 'debug']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'ttl', 'eq', '15']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'protocol', 'tcp']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'destination', 'port', '8888']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'log', 'enable']) - self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-level', 'err']) + self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-options', 'level', 'err']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'ttl', 'gt', '102']) @@ -247,8 +247,8 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], - ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug', 'ip ttl 15', 'return'], - ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" level err', 'ip ttl > 102', 'reject'], + ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'], + ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'], ['tcp dport 22', 'limit rate 5/minute', 'return'], ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'], ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'], @@ -272,6 +272,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024']) self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17']) self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log', 'enable']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'group', '66']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000']) self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept']) self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000']) @@ -301,7 +305,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], - ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'], + ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'return'], ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'], [f'log prefix "[{name}-default-D]"', 'drop'], ['ip saddr 198.51.100.1', f'jump NAME_{name}'], @@ -357,7 +361,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log', 'enable']) - self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-level', 'crit']) + self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-options', 'level', 'crit']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp']) @@ -374,7 +378,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME6_{name}'], - ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'], + ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'return'], ['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'], ['meta l4proto gre', f'oifname "{interface}"', 'return'], ['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop'] diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index 61363b853..b677f0e45 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -117,6 +117,8 @@ rgiyCHemtMepq57Pl1Nmj49eEA== """ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): + skip_process_check = False + @classmethod def setUpClass(cls): super(TestVPNIPsec, cls).setUpClass() @@ -141,7 +143,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) + if not self.skip_process_check: + self.assertTrue(process_named_running(PROCESS_NAME)) + else: + self.skip_process_check = False # Reset self.cli_delete(base_path) self.cli_delete(tunnel_path) @@ -151,6 +156,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertFalse(process_named_running(PROCESS_NAME)) def test_01_dhcp_fail_handling(self): + # Skip process check - connection is not created for this test + self.skip_process_check = True + # Interface for dhcp-interface self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py index 4d9cbacbe..94be0483a 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2023 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 @@ -14,14 +14,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import gzip import re +import os import platform import unittest +from vyos.util import call from vyos.util import read_file kernel = platform.release() config = read_file(f'/boot/config-{kernel}') +CONFIG = '/proc/config.gz' + class TestKernelModules(unittest.TestCase): """ VyOS makes use of a lot of Kernel drivers, modules and features. The @@ -42,6 +47,22 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + def test_dropmon_enabled(self): + options_to_check = [ + 'CONFIG_NET_DROP_MONITOR=y', + 'CONFIG_UPROBE_EVENTS=y', + 'CONFIG_BPF_EVENTS=y', + 'CONFIG_TRACEPOINTS=y' + ] + if not os.path.isfile(CONFIG): + call('sudo modprobe configs') + + with gzip.open(CONFIG, 'rt') as f: + config_data = f.read() + for option in options_to_check: + self.assertIn(option, config_data, + f"Option {option} is not present in /proc/config.gz") + def test_qemu_support(self): # The bond/lacp interface must be enabled in the OS Kernel for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO', @@ -58,6 +79,7 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 39c87478f..2b2af252d 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -247,7 +247,7 @@ def verify(dhcp): net2 = ip_network(n) if (net != net2): if net.overlaps(net2): - raise ConfigError('Conflicting subnet ranges: "{net}" overlaps "{net2}"!') + raise ConfigError(f'Conflicting subnet ranges: "{net}" overlaps "{net2}"!') # Prevent 'disable' for shared-network if only one network is configured if (shared_networks - disabled_shared_networks) < 1: diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index b63ed4eb9..c41a442df 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -282,6 +282,16 @@ def verify_rule(firewall, rule_conf, ipv6): if rule_conf['protocol'] not in ['tcp', 'udp', 'tcp_udp']: raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group') + if 'log_options' in rule_conf: + if 'log' not in rule_conf or 'enable' not in rule_conf['log']: + raise ConfigError('log-options defined, but log is not enable') + + if 'snapshot_length' in rule_conf['log_options'] and 'group' not in rule_conf['log_options']: + raise ConfigError('log-options snapshot-length defined, but log group is not define') + + if 'queue_threshold' in rule_conf['log_options'] and 'group' not in rule_conf['log_options']: + raise ConfigError('log-options queue-threshold defined, but log group is not define') + def verify_nested_group(group_name, group, groups, seen): if 'include' not in group: return diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 0582d32be..eb64afa0c 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -89,7 +89,7 @@ def get_config(config=None): if 'mpls_te' not in ospf: del default_values['mpls_te'] - for protocol in ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']: + for protocol in ['babel', 'bgp', 'connected', 'isis', 'kernel', 'rip', 'static', 'table']: # table is a tagNode thus we need to clean out all occurances for the # default values and load them in later individually if protocol == 'table': @@ -234,7 +234,7 @@ def verify(ospf): if list(set(global_range) & set(local_range)): raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\ f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!') - + # Check for a blank or invalid value per prefix if dict_search('segment_routing.prefix', ospf): for prefix, prefix_config in ospf['segment_routing']['prefix'].items(): diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d207c63df..63887b278 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -549,6 +549,8 @@ def generate(ipsec): if ipsec['dhcp_no_address']: with open(DHCP_HOOK_IFLIST, 'w') as f: f.write(" ".join(ipsec['dhcp_no_address'].values())) + elif os.path.exists(DHCP_HOOK_IFLIST): + os.unlink(DHCP_HOOK_IFLIST) for path in [swanctl_dir, CERT_PATH, CA_PATH, CRL_PATH, PUBKEY_PATH]: if not os.path.exists(path): diff --git a/src/migration-scripts/firewall/9-to-10 b/src/migration-scripts/firewall/9-to-10 new file mode 100755 index 000000000..6f67cc512 --- /dev/null +++ b/src/migration-scripts/firewall/9-to-10 @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 <http://www.gnu.org/licenses/>. + +# T5050: Log options +# cli changes from: +# set firewall [name | ipv6-name] <name> rule <number> log-level <log_level> +# To +# set firewall [name | ipv6-name] <name> rule <number> log-options level <log_level> + +import re + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +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 = ['firewall'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +if config.exists(base + ['name']): + for name in config.list_nodes(base + ['name']): + if not config.exists(base + ['name', name, 'rule']): + continue + + for rule in config.list_nodes(base + ['name', name, 'rule']): + log_options_base = base + ['name', name, 'rule', rule, 'log-options'] + rule_log_level = base + ['name', name, 'rule', rule, 'log-level'] + + if config.exists(rule_log_level): + tmp = config.return_value(rule_log_level) + config.delete(rule_log_level) + config.set(log_options_base + ['level'], value=tmp) + +if config.exists(base + ['ipv6-name']): + for name in config.list_nodes(base + ['ipv6-name']): + if not config.exists(base + ['ipv6-name', name, 'rule']): + continue + + for rule in config.list_nodes(base + ['ipv6-name', name, 'rule']): + log_options_base = base + ['ipv6-name', name, 'rule', rule, 'log-options'] + rule_log_level = base + ['ipv6-name', name, 'rule', rule, 'log-level'] + + if config.exists(rule_log_level): + tmp = config.return_value(rule_log_level) + config.delete(rule_log_level) + config.set(log_options_base + ['level'], value=tmp) + +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)
\ No newline at end of file diff --git a/src/op_mode/accelppp.py b/src/op_mode/accelppp.py index 87a25bb96..00de45fc8 100755 --- a/src/op_mode/accelppp.py +++ b/src/op_mode/accelppp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 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 @@ -75,8 +75,8 @@ def _get_raw_statistics(accel_output, pattern, protocol): def _get_raw_sessions(port): - cmd_options = 'show sessions ifname,username,ip,ip6,ip6-dp,type,state,' \ - 'uptime-raw,calling-sid,called-sid,sid,comp,rx-bytes-raw,' \ + cmd_options = 'show sessions ifname,username,ip,ip6,ip6-dp,type,rate-limit,' \ + 'state,uptime-raw,calling-sid,called-sid,sid,comp,rx-bytes-raw,' \ 'tx-bytes-raw,rx-pkts,tx-pkts' output = vyos.accel_ppp.accel_cmd(port, cmd_options) parsed_data: list[dict[str, str]] = vyos.accel_ppp.accel_out_parse( diff --git a/src/op_mode/dns.py b/src/op_mode/dns.py index a0e47d7ad..f8863c530 100755 --- a/src/op_mode/dns.py +++ b/src/op_mode/dns.py @@ -17,7 +17,6 @@ import sys -from sys import exit from tabulate import tabulate from vyos.configquery import ConfigTreeQuery @@ -75,8 +74,7 @@ def show_forwarding_statistics(raw: bool): config = ConfigTreeQuery() if not config.exists('service dns forwarding'): - print("DNS forwarding is not configured") - exit(0) + raise vyos.opmode.UnconfiguredSubsystem('DNS forwarding is not configured') dns_data = _get_raw_forwarding_statistics() if raw: diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index 8e76f4cc0..6acde08ea 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -13,7 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - import re import sys import typing @@ -24,6 +23,7 @@ from tabulate import tabulate from vyos.util import convert_data from vyos.util import seconds_to_human +from vyos.configquery import ConfigTreeQuery import vyos.opmode import vyos.ipsec @@ -401,30 +401,91 @@ def _get_childsa_id_list(ike_sas: list) -> list: return list_childsa_id +def _get_all_sitetosite_peers_name_list() -> list: + """ + Return site-to-site peers configuration + :return: site-to-site peers configuration + :rtype: list + """ + conf: ConfigTreeQuery = ConfigTreeQuery() + config_path = ['vpn', 'ipsec', 'site-to-site', 'peer'] + peers_config = conf.get_config_dict(config_path, key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True) + peers_list: list = [] + for name in peers_config: + peers_list.append(name) + return peers_list + + def reset_peer(peer: str, tunnel: typing.Optional[str] = None): # Convert tunnel to Strongwan format of CHILD_SA + tunnel_sw = None if tunnel: if tunnel.isnumeric(): - tunnel = f'{peer}-tunnel-{tunnel}' + tunnel_sw = f'{peer}-tunnel-{tunnel}' elif tunnel == 'vti': - tunnel = f'{peer}-vti' + tunnel_sw = f'{peer}-vti' try: - sa_list: list = vyos.ipsec.get_vici_sas_by_name(peer, tunnel) - + sa_list: list = vyos.ipsec.get_vici_sas_by_name(peer, tunnel_sw) if not sa_list: - raise vyos.opmode.IncorrectValue('Peer not found, aborting') + raise vyos.opmode.IncorrectValue( + f'Peer\'s {peer} SA(s) not found, aborting') if tunnel and sa_list: childsa_id_list: list = _get_childsa_id_list(sa_list) if not childsa_id_list: raise vyos.opmode.IncorrectValue( - 'Peer or tunnel(s) not found, aborting') - vyos.ipsec.terminate_vici_by_name(peer, tunnel) - print('Peer reset result: success') + f'Peer {peer} tunnel {tunnel} SA(s) not found, aborting') + vyos.ipsec.terminate_vici_by_name(peer, tunnel_sw) + print(f'Peer {peer} reset result: success') except (vyos.ipsec.ViciInitiateError) as err: raise vyos.opmode.UnconfiguredSubsystem(err) - except (vyos.ipsec.ViciInitiateError) as err: + except (vyos.ipsec.ViciCommandError) as err: raise vyos.opmode.IncorrectValue(err) +def reset_all_peers(): + sitetosite_list = _get_all_sitetosite_peers_name_list() + if sitetosite_list: + for peer_name in sitetosite_list: + try: + reset_peer(peer_name) + except (vyos.opmode.IncorrectValue) as err: + print(err) + print('Peers reset result: success') + else: + raise vyos.opmode.UnconfiguredSubsystem( + 'VPN IPSec site-to-site is not configured, aborting') + +def _get_ra_session_list_by_username(username: typing.Optional[str] = None): + """ + Return list of remote-access IKE_SAs uniqueids + :param username: + :type username: + :return: + :rtype: + """ + list_sa_id = [] + sa_list = vyos.ipsec.get_vici_sas() + for sa_val in sa_list: + for sa in sa_val.values(): + if 'remote-eap-id' in sa: + if username: + if username == sa['remote-eap-id'].decode(): + list_sa_id.append(sa['uniqueid'].decode()) + else: + list_sa_id.append(sa['uniqueid'].decode()) + return list_sa_id + + +def reset_ra(username: typing.Optional[str] = None): + #Reset remote-access ipsec sessions + if username: + list_sa_id = _get_ra_session_list_by_username(username) + else: + list_sa_id = _get_ra_session_list_by_username() + if list_sa_id: + vyos.ipsec.terminate_vici_ikeid_list(list_sa_id) + def show_sa(raw: bool): sa_data = _get_raw_data_sas() diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index 8f88ab422..7ae49472e 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -53,7 +53,7 @@ def _get_tunnel_address(peer_host, peer_port, status_file): def _get_interface_status(mode: str, interface: str) -> dict: status_file = f'/run/openvpn/{interface}.status' - data = { + data: dict = { 'mode': mode, 'intf': interface, 'local_host': '', @@ -142,8 +142,8 @@ def _get_interface_status(mode: str, interface: str) -> dict: return data -def _get_raw_data(mode: str) -> dict: - data = {} +def _get_raw_data(mode: str) -> list: + data: list = [] conf = Config() conf_dict = conf.get_config_dict(['interfaces', 'openvpn'], get_first_key=True) @@ -152,8 +152,7 @@ def _get_raw_data(mode: str) -> dict: interfaces = [x for x in list(conf_dict) if conf_dict[x]['mode'] == mode] for intf in interfaces: - data[intf] = _get_interface_status(mode, intf) - d = data[intf] + d = _get_interface_status(mode, intf) d['local_host'] = conf_dict[intf].get('local-host', '') d['local_port'] = conf_dict[intf].get('local-port', '') if conf.exists(f'interfaces openvpn {intf} server client'): @@ -164,10 +163,11 @@ def _get_raw_data(mode: str) -> dict: client['name'] = 'None (PSK)' client['remote_host'] = conf_dict[intf].get('remote-host', [''])[0] client['remote_port'] = conf_dict[intf].get('remote-port', '1194') + data.append(d) return data -def _format_openvpn(data: dict) -> str: +def _format_openvpn(data: list) -> str: if not data: out = 'No OpenVPN interfaces configured' return out @@ -176,11 +176,12 @@ def _format_openvpn(data: dict) -> str: 'TX bytes', 'RX bytes', 'Connected Since'] out = '' - for intf in list(data): + for d in data: data_out = [] - l_host = data[intf]['local_host'] - l_port = data[intf]['local_port'] - for client in list(data[intf]['clients']): + intf = d['intf'] + l_host = d['local_host'] + l_port = d['local_port'] + for client in d['clients']: r_host = client['remote_host'] r_port = client['remote_port'] @@ -201,7 +202,7 @@ def _format_openvpn(data: dict) -> str: return out -def show(raw: bool, mode: ArgMode) -> str: +def show(raw: bool, mode: ArgMode) -> typing.Union[list,str]: openvpn_data = _get_raw_data(mode) if raw: diff --git a/src/op_mode/reset_vpn.py b/src/op_mode/reset_vpn.py index 3a0ad941c..46195d6cd 100755 --- a/src/op_mode/reset_vpn.py +++ b/src/op_mode/reset_vpn.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019 VyOS maintainers and contributors +# Copyright (C) 2022-2023 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 @@ -13,60 +13,49 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - import sys -import argparse +import typing from vyos.util import run +import vyos.opmode + cmd_dict = { - 'cmd_base' : '/usr/bin/accel-cmd -p {} terminate {} {}', - 'vpn_types' : { - 'pptp' : 2003, - 'l2tp' : 2004, - 'sstp' : 2005 + 'cmd_base': '/usr/bin/accel-cmd -p {} terminate {} {}', + 'vpn_types': { + 'pptp': 2003, + 'l2tp': 2004, + 'sstp': 2005 } } -def terminate_sessions(username='', interface='', protocol=''): - # Reset vpn connections by username +def reset_conn(protocol: str, username: typing.Optional[str] = None, + interface: typing.Optional[str] = None): if protocol in cmd_dict['vpn_types']: - if username == "all_users": - run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][protocol], 'all', '')) - else: - run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][protocol], 'username', username)) - - # Reset vpn connections by ifname - elif interface: - for proto in cmd_dict['vpn_types']: - run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][proto], 'if', interface)) - - elif username: - # Reset all vpn connections - if username == "all_users": - for proto in cmd_dict['vpn_types']: - run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][proto], 'all', '')) + # Reset by Interface + if interface: + run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][protocol], + 'if', interface)) + return + # Reset by username + if username: + run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][protocol], + 'username', username)) + # Reset all else: - for proto in cmd_dict['vpn_types']: - run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][proto], 'username', username)) - -def main(): - #parese args - parser = argparse.ArgumentParser() - parser.add_argument('--username', help='Terminate by username (all_users used for disconnect all users)', required=False) - parser.add_argument('--interface', help='Terminate by interface', required=False) - parser.add_argument('--protocol', help='Set protocol (pptp|l2tp|sstp)', required=False) - args = parser.parse_args() - - if args.username or args.interface: - terminate_sessions(username=args.username, interface=args.interface, protocol=args.protocol) + run(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][protocol], + 'all', + '')) else: - print("Param --username or --interface required") - sys.exit(1) - - terminate_sessions() + vyos.opmode.IncorrectValue('Unknown VPN Protocol, aborting') if __name__ == '__main__': - main() + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) |