diff options
95 files changed, 1371 insertions, 992 deletions
diff --git a/data/configd-include.json b/data/configd-include.json index 92d3863ce..6d7261b73 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -63,6 +63,7 @@ "service_ipoe-server.py", "service_mdns-repeater.py", "service_monitoring_telegraf.py", +"service_ndp-proxy.py", "service_pppoe-server.py", "service_router-advert.py", "service_upnp.py", diff --git a/data/templates/accel-ppp/config_ip_pool.j2 b/data/templates/accel-ppp/config_ip_pool.j2 index c567236a4..6ac04e1a1 100644 --- a/data/templates/accel-ppp/config_ip_pool.j2 +++ b/data/templates/accel-ppp/config_ip_pool.j2 @@ -12,10 +12,16 @@ gw-ip-address={{ gateway_address }} {% endif %} {% for pool in ordered_named_pools %} {% for pool_name, pool_config in pool.items() %} +{% set iprange_str = pool_config.range %} +{% set iprange_list = pool_config.range.split('-') %} +{% if iprange_list | length == 2 %} +{% set last_ip_oct = iprange_list[1].split('.') %} +{% set iprange_str = iprange_list[0] + '-' + last_ip_oct[last_ip_oct | length - 1] %} +{% endif %} {% if pool_config.next_pool is vyos_defined %} -{{ pool_config.range }},name={{ pool_name }},next={{ pool_config.next_pool }} +{{ iprange_str }},name={{ pool_name }},next={{ pool_config.next_pool }} {% else %} -{{ pool_config.range }},name={{ pool_name }} +{{ iprange_str }},name={{ pool_name }} {% endif %} {% endfor %} {% endfor %} diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2 index 4e891777f..daafd6e92 100644 --- a/data/templates/accel-ppp/pptp.config.j2 +++ b/data/templates/accel-ppp/pptp.config.j2 @@ -3,18 +3,22 @@ log_syslog pptp shaper -{% if auth_mode == 'local' %} -chap-secrets -{% elif auth_mode == 'radius' %} -radius -{% endif %} +{# Common authentication backend definitions #} +{% include 'accel-ppp/config_modules_auth_mode.j2' %} ippool -{% for proto in auth_proto %} -{{ proto }} -{% endfor %} +{# Common authentication protocols (pap, chap ...) #} +{% if authentication.require is vyos_defined %} +{% if authentication.require == 'chap' %} +auth_chap_md5 +{% elif authentication.require == 'mschap' %} +auth_mschap_v1 +{% else %} +auth_{{ authentication.require.replace('-', '_') }} +{% endif %} +{% endif %} [core] -thread-count={{ thread_cnt }} +thread-count={{ thread_count }} [common] {% if max_concurrent_sessions is vyos_defined %} @@ -26,16 +30,12 @@ syslog=accel-pptp,daemon copy=1 level=5 -{% if dnsv4 %} -[dns] -{% for dns in dnsv4 %} -dns{{ loop.index }}={{ dns }} -{% endfor %} -{% endif %} +{# Common DNS name-server definition #} +{% include 'accel-ppp/config_name_server.j2' %} -{% if wins %} +{% if wins_server is vyos_defined %} [wins] -{% for server in wins %} +{% for server in wins_server %} wins{{ loop.index }}={{ server }} {% endfor %} {% endif %} @@ -43,12 +43,12 @@ wins{{ loop.index }}={{ server }} [pptp] ifname=pptp%d -{% if outside_addr %} -bind={{ outside_addr }} +{% if outside_address is vyos_defined %} +bind={{ outside_address }} {% endif %} verbose=1 ppp-max-mtu={{ mtu }} -mppe={{ ppp_mppe }} +mppe={{ authentication.mppe }} echo-interval=10 echo-failure=3 {% if default_pool is vyos_defined %} @@ -66,52 +66,11 @@ verbose=5 check-ip=1 single-session=replace -{% if auth_mode == 'local' %} -[chap-secrets] -chap-secrets={{ chap_secrets_file }} -{% elif auth_mode == 'radius' %} -[radius] -verbose=1 -{% for r in radius_server %} -server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} -{% endfor %} -{% if radius_acct_interim_interval is vyos_defined %} -acct-interim-interval={{ radius_acct_interim_interval }} -{% endif %} -{% if radius_acct_inter_jitter %} -acct-interim-jitter={{ radius_acct_inter_jitter }} -{% endif %} -acct-timeout={{ radius_acct_tmo }} -timeout={{ radius_timeout }} -max-try={{ radius_max_try }} -{% if radius_nas_id %} -nas-identifier={{ radius_nas_id }} -{% endif %} -{% if radius_nas_ip %} -nas-ip-address={{ radius_nas_ip }} -{% endif %} -{% if radius_source_address %} -bind={{ radius_source_address }} -{% endif %} -{% endif %} -{# Both chap-secrets and radius block required the gw-ip-address #} -{% if gateway_address is vyos_defined %} -gw-ip-address={{ gateway_address }} -{% endif %} +{# Common chap-secrets and RADIUS server/option definitions #} +{% include 'accel-ppp/config_chap_secrets_radius.j2' %} -{% if radius_shaper_enable %} -[shaper] -verbose=1 -{% if radius_shaper_attr %} -attr={{ radius_shaper_attr }} -{% endif %} -{% if radius_shaper_multiplier %} -rate-multiplier={{ radius_shaper_multiplier }} -{% endif %} -{% if radius_shaper_vendor %} -vendor={{ radius_shaper_vendor }} -{% endif %} -{% endif %} +{# Common RADIUS shaper configuration #} +{% include 'accel-ppp/config_shaper_radius.j2' %} [cli] tcp=127.0.0.1:2003 diff --git a/data/templates/dhcp-server/10-override.conf.j2 b/data/templates/dhcp-server/10-override.conf.j2 new file mode 100644 index 000000000..6cf9e0a11 --- /dev/null +++ b/data/templates/dhcp-server/10-override.conf.j2 @@ -0,0 +1,2 @@ +[Unit] +ConditionFileNotEmpty= diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2 index d77e3f6e9..9e28899b0 100644 --- a/data/templates/firewall/nftables-policy.j2 +++ b/data/templates/firewall/nftables-policy.j2 @@ -28,6 +28,9 @@ table ip vyos_mangle { {{ rule_conf | nft_rule('route', route_text, rule_id, 'ip') }} {% endfor %} {% endif %} +{% if conf.default_log is vyos_defined %} + counter log prefix "[ipv4-{{ (route_text)[:19] }}-default]" +{% endif %} } {% endfor %} {% endif %} @@ -57,6 +60,9 @@ table ip6 vyos_mangle { {{ rule_conf | nft_rule('route6', route_text, rule_id, 'ip6') }} {% endfor %} {% endif %} +{% if conf.default_log is vyos_defined %} + counter log prefix "[ipv6-{{ (route_text)[:19] }}-default]" +{% endif %} } {% endfor %} {% endif %} diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl index c637e18bc..339b4e52f 100644 --- a/data/templates/frr/daemons.frr.tmpl +++ b/data/templates/frr/daemons.frr.tmpl @@ -101,7 +101,7 @@ pathd_options=" --daemon -A 127.0.0.1" # vtysh_enable=yes -watchfrr_enable=no +watchfrr_enable=yes valgrind_enable=no #watchfrr_options="" diff --git a/data/templates/lldp/lldpd.j2 b/data/templates/lldp/lldpd.j2 index 3c499197d..6ae063c4b 100644 --- a/data/templates/lldp/lldpd.j2 +++ b/data/templates/lldp/lldpd.j2 @@ -1,2 +1,2 @@ ### Autogenerated by lldp.py ### -DAEMON_ARGS="-M 4 {{ '-x' if snmp.enable is vyos_defined }} {{ '-c' if legacy_protocols.cdp is vyos_defined }} {{ '-e' if legacy_protocols.edp is vyos_defined }} {{ '-f' if legacy_protocols.fdp is vyos_defined }} {{ '-s' if legacy_protocols.sonmp is vyos_defined }}" +DAEMON_ARGS="-M 4 {{ '-x' if snmp is vyos_defined }} {{ '-c' if legacy_protocols.cdp is vyos_defined }} {{ '-e' if legacy_protocols.edp is vyos_defined }} {{ '-f' if legacy_protocols.fdp is vyos_defined }} {{ '-s' if legacy_protocols.sonmp is vyos_defined }}" diff --git a/data/templates/ndppd/ndppd.conf.j2 b/data/templates/ndppd/ndppd.conf.j2 index 1297f36be..6369dbdeb 100644 --- a/data/templates/ndppd/ndppd.conf.j2 +++ b/data/templates/ndppd/ndppd.conf.j2 @@ -1,44 +1,35 @@ -######################################################## -# -# autogenerated by nat66.py -# -# The configuration file must define one upstream -# interface. -# -# For some services, such as nat66, because it runs -# stateless, it needs to rely on NDP Proxy to respond -# to NDP requests. -# -# When using nat66 source rules, NDP Proxy needs -# to be enabled -# -######################################################## +# autogenerated by service_ndp-proxy.py -{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} -{% if source.rule is vyos_defined %} -{% for rule, config in source.rule.items() if config.disable is not defined %} -{% if config.outbound_interface.name is vyos_defined %} -{% if config.outbound_interface.name not in global.ndppd_interfaces %} -{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface.name] %} -{% endif %} -{% if config.translation.address is vyos_defined and config.translation.address | is_ip_network %} -{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface.name,'rule':config.translation.address}] %} -{% endif %} -{% endif %} -{% endfor %} -{% endif %} +# This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route +route-ttl {{ route_refresh }} + +{% if interface is vyos_defined %} +# This sets up a listener, that will listen for any Neighbor Solicitation +# messages, and respond to them according to a set of rules +{% for iface, iface_config in interface.items() if iface_config.disable is not vyos_defined %} +proxy {{ iface }} { + # Turn on or off the router flag for Neighbor Advertisements + router {{ 'yes' if iface_config.enable_router_bit is vyos_defined else 'no' }} + # Control how long to wait for a Neighbor Advertisment message before invalidating the entry (milliseconds) + timeout {{ iface_config.timeout }} + # Control how long a valid or invalid entry remains in the cache (milliseconds) + ttl {{ iface_config.ttl }} -{% for interface in global.ndppd_interfaces %} -proxy {{ interface }} { - router yes - timeout 500 - ttl 30000 -{% for map in global.ndppd_prefixs %} -{% if map.interface == interface %} - rule {{ map.rule }} { - static +{% if iface_config.prefix is vyos_defined %} + # This is a rule that the target address is to match against. If no netmask + # is provided, /128 is assumed. You may have several rule sections, and the + # addresses may or may not overlap. +{% for prefix, prefix_config in iface_config.prefix.items() if prefix_config.disable is not vyos_defined %} + rule {{ prefix }} { +{% if prefix_config.mode is vyos_defined('interface') %} + iface {{ prefix_config.interface }} +{% else %} + {{ prefix_config.mode }} +{% endif %} } -{% endif %} -{% endfor %} +{% endfor %} +{% endif %} } -{% endfor %} + +{% endfor %} +{% endif %} diff --git a/debian/control b/debian/control index 08adc8a68..af42202fc 100644 --- a/debian/control +++ b/debian/control @@ -152,7 +152,7 @@ Depends: console-data, dropbear, # End "service console-server" -# For "set service aws glb" +# For "service aws glb" aws-gwlbtun, # For "service dns dynamic" ddclient (>= 3.11.1), @@ -160,6 +160,9 @@ Depends: # # For "service ids" fastnetmon [amd64], # End "service ids" +# # For "service ndp-proxy" + ndppd, +# End "service ndp-proxy" # For "service router-advert" radvd, # End "service route-advert" @@ -251,18 +254,15 @@ Depends: # For "nat64" jool, # End "nat64" -# For nat66 - ndppd, -# End nat66 # For "system ntp" chrony, # End "system ntp" # For "vpn openconnect" ocserv, # End "vpn openconnect" -# For "set system flow-accounting" +# For "system flow-accounting" pmacct (>= 1.6.0), -# End "set system flow-accounting" +# End "system flow-accounting" # For container podman, netavark, diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 081f7ed42..8aaeeb29d 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -284,11 +284,11 @@ </tagNode> <tagNode name="static-mapping"> <properties> - <help>Name of static mapping</help> + <help>Hostname for static mapping reservation</help> <constraint> - <regex>[-_a-zA-Z0-9.]+</regex> + <validator name="fqdn"/> </constraint> - <constraintErrorMessage>Invalid static mapping name, may only be alphanumeric, dot and hyphen</constraintErrorMessage> + <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> </properties> <children> #include <include/generic-disable-node.xml.i> @@ -304,18 +304,8 @@ </constraint> </properties> </leafNode> - <leafNode name="mac-address"> - <properties> - <help>Media Access Control (MAC) address</help> - <valueHelp> - <format>macaddr</format> - <description>Hardware (MAC) address</description> - </valueHelp> - <constraint> - <validator name="mac-address"/> - </constraint> - </properties> - </leafNode> + #include <include/interface/mac.xml.i> + #include <include/interface/duid.xml.i> </children> </tagNode> <tagNode name="static-route"> diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index b37f79434..10fdbf3f7 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -301,27 +301,16 @@ </leafNode> <tagNode name="static-mapping"> <properties> - <help>Name of static mapping</help> + <help>Hostname for static mapping reservation</help> <constraint> - <regex>[-_a-zA-Z0-9.]+</regex> + <validator name="fqdn"/> </constraint> - <constraintErrorMessage>Invalid static mapping name. May only contain letters, numbers and .-_</constraintErrorMessage> + <constraintErrorMessage>Invalid static mapping hostname</constraintErrorMessage> </properties> <children> #include <include/generic-disable-node.xml.i> - <leafNode name="identifier"> - <properties> - <help>Client identifier (DUID) for this static mapping</help> - <valueHelp> - <format>h[[:h]...]</format> - <description>DUID: colon-separated hex list (as used by isc-dhcp option dhcpv6.client-id)</description> - </valueHelp> - <constraint> - <regex>([0-9A-Fa-f]{1,2}[:])*([0-9A-Fa-f]{1,2})</regex> - </constraint> - <constraintErrorMessage>Invalid DUID, must be in the format h[[:h]...]</constraintErrorMessage> - </properties> - </leafNode> + #include <include/interface/mac.xml.i> + #include <include/interface/duid.xml.i> <leafNode name="ipv6-address"> <properties> <help>Client IPv6 address for this static mapping</help> diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 70afdc995..a4023058f 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -368,7 +368,7 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> <leafNode name="default-action"> <properties> <help>Default-action for traffic coming into this zone</help> diff --git a/interface-definitions/include/firewall/bridge-custom-name.xml.i b/interface-definitions/include/firewall/bridge-custom-name.xml.i index a85fd5a19..654493c0e 100644 --- a/interface-definitions/include/firewall/bridge-custom-name.xml.i +++ b/interface-definitions/include/firewall/bridge-custom-name.xml.i @@ -8,7 +8,7 @@ </properties> <children> #include <include/firewall/default-action.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <leafNode name="default-jump-target"> <properties> @@ -36,4 +36,4 @@ </tagNode> </children> </tagNode> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/bridge-hook-forward.xml.i b/interface-definitions/include/firewall/bridge-hook-forward.xml.i index ff86bf466..99f66ec77 100644 --- a/interface-definitions/include/firewall/bridge-hook-forward.xml.i +++ b/interface-definitions/include/firewall/bridge-hook-forward.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -32,4 +32,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i index a27cae43b..6de770c79 100644 --- a/interface-definitions/include/firewall/common-rule-bridge.xml.i +++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i @@ -24,7 +24,7 @@ </properties> </leafNode> #include <include/firewall/log.xml.i> -#include <include/firewall/rule-log-options.xml.i> +#include <include/firewall/log-options.xml.i> <node name="source"> <properties> <help>Source parameters</help> @@ -36,4 +36,4 @@ #include <include/firewall/inbound-interface.xml.i> #include <include/firewall/outbound-interface.xml.i> #include <include/firewall/match-vlan.xml.i> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i index aabefcb27..6f56ecc85 100644 --- a/interface-definitions/include/firewall/common-rule-inet.xml.i +++ b/interface-definitions/include/firewall/common-rule-inet.xml.i @@ -83,7 +83,7 @@ </children> </node> #include <include/firewall/log.xml.i> -#include <include/firewall/rule-log-options.xml.i> +#include <include/firewall/log-options.xml.i> <node name="connection-status"> <properties> <help>Connection status</help> diff --git a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i index e040c9b13..0d749aa27 100644 --- a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i +++ b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i @@ -144,7 +144,7 @@ </constraint> </properties> </leafNode> -#include <include/firewall/rule-log-options.xml.i> +#include <include/firewall/log-options.xml.i> <node name="connection-status"> <properties> <help>Connection status</help> diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i deleted file mode 100644 index c62bf2c5f..000000000 --- a/interface-definitions/include/firewall/common-rule.xml.i +++ /dev/null @@ -1,387 +0,0 @@ -<!-- include start from firewall/common-rule.xml.i --> -#include <include/firewall/action.xml.i> -#include <include/generic-description.xml.i> -<node name="destination"> - <properties> - <help>Destination parameters</help> - </properties> - <children> - #include <include/firewall/mac-address.xml.i> - </children> -</node> -<leafNode name="disable"> - <properties> - <help>Option to disable firewall rule</help> - <valueless/> - </properties> -</leafNode> -<node name="fragment"> - <properties> - <help>IP fragment match</help> - </properties> - <children> - <leafNode name="match-frag"> - <properties> - <help>Second and further fragments of fragmented packets</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="match-non-frag"> - <properties> - <help>Head fragments or unfragmented packets</help> - <valueless/> - </properties> - </leafNode> - </children> -</node> -<node name="inbound-interface"> - <properties> - <help>Match inbound-interface</help> - </properties> - <children> - #include <include/firewall/match-interface.xml.i> - </children> -</node> -<node name="outbound-interface"> - <properties> - <help>Match outbound-interface</help> - </properties> - <children> - #include <include/firewall/match-interface.xml.i> - </children> -</node> -<node name="ipsec"> - <properties> - <help>Inbound IPsec packets</help> - </properties> - <children> - <leafNode name="match-ipsec"> - <properties> - <help>Inbound IPsec packets</help> - <valueless/> - </properties> - </leafNode> - <leafNode name="match-none"> - <properties> - <help>Inbound non-IPsec packets</help> - <valueless/> - </properties> - </leafNode> - </children> -</node> -<node name="limit"> - <properties> - <help>Rate limit using a token bucket filter</help> - </properties> - <children> - <leafNode name="burst"> - <properties> - <help>Maximum number of packets to allow in excess of rate</help> - <valueHelp> - <format>u32:0-4294967295</format> - <description>Maximum number of packets to allow in excess of rate</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-4294967295"/> - </constraint> - </properties> - </leafNode> - <leafNode name="rate"> - <properties> - <help>Maximum average matching rate</help> - <valueHelp> - <format>txt</format> - <description>integer/unit (Example: 5/minute)</description> - </valueHelp> - <constraint> - <regex>\d+/(second|minute|hour|day)</regex> - </constraint> - </properties> - </leafNode> - </children> -</node> -<leafNode name="log"> - <properties> - <help>Option to log packets matching rule</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable log</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable log</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> -</leafNode> -#include <include/firewall/rule-log-options.xml.i> -<node name="connection-status"> - <properties> - <help>Connection status</help> - </properties> - <children> - <leafNode name="nat"> - <properties> - <help>NAT connection status</help> - <completionHelp> - <list>destination source</list> - </completionHelp> - <valueHelp> - <format>destination</format> - <description>Match connections that are subject to destination NAT</description> - </valueHelp> - <valueHelp> - <format>source</format> - <description>Match connections that are subject to source NAT</description> - </valueHelp> - <constraint> - <regex>^(destination|source)$</regex> - </constraint> - </properties> - </leafNode> - </children> -</node> -<leafNode name="protocol"> - <properties> - <help>Protocol to match (protocol name, number, or "all")</help> - <completionHelp> - <script>${vyos_completion_dir}/list_protocols.sh</script> - <list>all tcp_udp</list> - </completionHelp> - <valueHelp> - <format>all</format> - <description>All IP protocols</description> - </valueHelp> - <valueHelp> - <format>tcp_udp</format> - <description>Both TCP and UDP</description> - </valueHelp> - <valueHelp> - <format>u32:0-255</format> - <description>IP protocol number</description> - </valueHelp> - <valueHelp> - <format><protocol></format> - <description>IP protocol name</description> - </valueHelp> - <valueHelp> - <format>!<protocol></format> - <description>IP protocol name</description> - </valueHelp> - <constraint> - <validator name="ip-protocol"/> - </constraint> - </properties> -</leafNode> -<node name="recent"> - <properties> - <help>Parameters for matching recently seen sources</help> - </properties> - <children> - <leafNode name="count"> - <properties> - <help>Source addresses seen more than N times</help> - <valueHelp> - <format>u32:1-255</format> - <description>Source addresses seen more than N times</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-255"/> - </constraint> - </properties> - </leafNode> - <leafNode name="time"> - <properties> - <help>Source addresses seen in the last second/minute/hour</help> - <completionHelp> - <list>second minute hour</list> - </completionHelp> - <valueHelp> - <format>second</format> - <description>Source addresses seen COUNT times in the last second</description> - </valueHelp> - <valueHelp> - <format>minute</format> - <description>Source addresses seen COUNT times in the last minute</description> - </valueHelp> - <valueHelp> - <format>hour</format> - <description>Source addresses seen COUNT times in the last hour</description> - </valueHelp> - <constraint> - <regex>(second|minute|hour)</regex> - </constraint> - </properties> - </leafNode> - </children> -</node> -<node name="source"> - <properties> - <help>Source parameters</help> - </properties> - <children> - #include <include/firewall/address.xml.i> - #include <include/firewall/source-destination-group.xml.i> - #include <include/firewall/mac-address.xml.i> - #include <include/firewall/port.xml.i> - </children> -</node> -<node name="state"> - <properties> - <help>Session state</help> - </properties> - <children> - <leafNode name="established"> - <properties> - <help>Established state</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="invalid"> - <properties> - <help>Invalid state</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="new"> - <properties> - <help>New state</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="related"> - <properties> - <help>Related state</help> - <completionHelp> - <list>enable disable</list> - </completionHelp> - <valueHelp> - <format>enable</format> - <description>Enable</description> - </valueHelp> - <valueHelp> - <format>disable</format> - <description>Disable</description> - </valueHelp> - <constraint> - <regex>(enable|disable)</regex> - </constraint> - </properties> - </leafNode> - </children> -</node> -#include <include/firewall/tcp-flags.xml.i> -#include <include/firewall/tcp-mss.xml.i> -<node name="time"> - <properties> - <help>Time to match rule</help> - </properties> - <children> - <leafNode name="startdate"> - <properties> - <help>Date to start matching rule</help> - <valueHelp> - <format>txt</format> - <description>Enter date using following notation - YYYY-MM-DD</description> - </valueHelp> - <constraint> - <regex>(\d{4}\-\d{2}\-\d{2})</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="starttime"> - <properties> - <help>Time of day to start matching rule</help> - <valueHelp> - <format>txt</format> - <description>Enter time using using 24 hour notation - hh:mm:ss</description> - </valueHelp> - <constraint> - <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="stopdate"> - <properties> - <help>Date to stop matching rule</help> - <valueHelp> - <format>txt</format> - <description>Enter date using following notation - YYYY-MM-DD</description> - </valueHelp> - <constraint> - <regex>(\d{4}\-\d{2}\-\d{2})</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="stoptime"> - <properties> - <help>Time of day to stop matching rule</help> - <valueHelp> - <format>txt</format> - <description>Enter time using using 24 hour notation - hh:mm:ss</description> - </valueHelp> - <constraint> - <regex>([0-2][0-9](\:[0-5][0-9]){1,2})</regex> - </constraint> - </properties> - </leafNode> - <leafNode name="weekdays"> - <properties> - <help>Comma separated weekdays to match rule on</help> - <valueHelp> - <format>txt</format> - <description>Name of day (Monday, Tuesday, Wednesday, Thursdays, Friday, Saturday, Sunday)</description> - </valueHelp> - <valueHelp> - <format>u32:0-6</format> - <description>Day number (0 = Sunday ... 6 = Saturday)</description> - </valueHelp> - </properties> - </leafNode> - </children> -</node> -<!-- include end --> diff --git a/interface-definitions/include/firewall/default-log.xml.i b/interface-definitions/include/firewall/default-log.xml.i new file mode 100644 index 000000000..dceacdb89 --- /dev/null +++ b/interface-definitions/include/firewall/default-log.xml.i @@ -0,0 +1,8 @@ +<!-- include start from firewall/default-log.xml.i --> +<leafNode name="default-log"> + <properties> + <help>Log packets hitting default-action</help> + <valueless/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/firewall/enable-default-log.xml.i b/interface-definitions/include/firewall/enable-default-log.xml.i deleted file mode 100644 index 0efd8341b..000000000 --- a/interface-definitions/include/firewall/enable-default-log.xml.i +++ /dev/null @@ -1,8 +0,0 @@ -<!-- include start from firewall/enable-default-log.xml.i --> -<leafNode name="enable-default-log"> - <properties> - <help>Log packets hitting default-action</help> - <valueless/> - </properties> -</leafNode> -<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/firewall/ipv4-custom-name.xml.i b/interface-definitions/include/firewall/ipv4-custom-name.xml.i index c6420fe1f..8199d15fe 100644 --- a/interface-definitions/include/firewall/ipv4-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv4-custom-name.xml.i @@ -8,7 +8,7 @@ </properties> <children> #include <include/firewall/default-action.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <leafNode name="default-jump-target"> <properties> @@ -39,4 +39,4 @@ </tagNode> </children> </tagNode> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i index 100f1c3d9..de2c70482 100644 --- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -36,4 +36,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv4-hook-input.xml.i b/interface-definitions/include/firewall/ipv4-hook-input.xml.i index 22546640b..5d32657ea 100644 --- a/interface-definitions/include/firewall/ipv4-hook-input.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-input.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -33,4 +33,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv4-hook-output.xml.i b/interface-definitions/include/firewall/ipv4-hook-output.xml.i index 80c30cdeb..2b537ce5e 100644 --- a/interface-definitions/include/firewall/ipv4-hook-output.xml.i +++ b/interface-definitions/include/firewall/ipv4-hook-output.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -33,4 +33,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv6-custom-name.xml.i b/interface-definitions/include/firewall/ipv6-custom-name.xml.i index 2cc45a60c..5748b3927 100644 --- a/interface-definitions/include/firewall/ipv6-custom-name.xml.i +++ b/interface-definitions/include/firewall/ipv6-custom-name.xml.i @@ -8,7 +8,7 @@ </properties> <children> #include <include/firewall/default-action.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <leafNode name="default-jump-target"> <properties> @@ -39,4 +39,4 @@ </tagNode> </children> </tagNode> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i index fb38267eb..b53f09f59 100644 --- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -36,4 +36,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv6-hook-input.xml.i b/interface-definitions/include/firewall/ipv6-hook-input.xml.i index 49d4493cc..493611fb1 100644 --- a/interface-definitions/include/firewall/ipv6-hook-input.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-input.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -33,4 +33,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/ipv6-hook-output.xml.i b/interface-definitions/include/firewall/ipv6-hook-output.xml.i index 452b9027f..ffe1c72b8 100644 --- a/interface-definitions/include/firewall/ipv6-hook-output.xml.i +++ b/interface-definitions/include/firewall/ipv6-hook-output.xml.i @@ -10,7 +10,7 @@ </properties> <children> #include <include/firewall/default-action-base-chains.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-description.xml.i> <tagNode name="rule"> <properties> @@ -33,4 +33,4 @@ </node> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/rule-log-options.xml.i b/interface-definitions/include/firewall/log-options.xml.i index e8b0cdec3..e8b0cdec3 100644 --- a/interface-definitions/include/firewall/rule-log-options.xml.i +++ b/interface-definitions/include/firewall/log-options.xml.i diff --git a/interface-definitions/include/firewall/log.xml.i b/interface-definitions/include/firewall/log.xml.i index 21b883e6a..21548f3fb 100644 --- a/interface-definitions/include/firewall/log.xml.i +++ b/interface-definitions/include/firewall/log.xml.i @@ -1,7 +1,7 @@ <!-- include start from firewall/log.xml.i --> <leafNode name="log"> <properties> - <help>Enable log</help> + <help>Log packets hitting this rule</help> <valueless/> </properties> </leafNode> diff --git a/interface-definitions/include/interface/dhcpv6-options.xml.i b/interface-definitions/include/interface/dhcpv6-options.xml.i index 5ca1d525f..68d1b172f 100644 --- a/interface-definitions/include/interface/dhcpv6-options.xml.i +++ b/interface-definitions/include/interface/dhcpv6-options.xml.i @@ -4,18 +4,7 @@ <help>DHCPv6 client settings/options</help> </properties> <children> - <leafNode name="duid"> - <properties> - <help>DHCP unique identifier (DUID) to be sent by dhcpv6 client</help> - <valueHelp> - <format>duid</format> - <description>DHCP unique identifier (DUID)</description> - </valueHelp> - <constraint> - <validator name="ipv6-duid"/> - </constraint> - </properties> - </leafNode> + #include <include/interface/duid.xml.i> <leafNode name="parameters-only"> <properties> <help>Acquire only config parameters, no address</help> diff --git a/interface-definitions/include/interface/duid.xml.i b/interface-definitions/include/interface/duid.xml.i new file mode 100644 index 000000000..8d808696e --- /dev/null +++ b/interface-definitions/include/interface/duid.xml.i @@ -0,0 +1,15 @@ +<!-- include start from interface/duid.xml.i --> +<leafNode name="duid"> + <properties> + <help>DHCP unique identifier (DUID) to be sent by client</help> + <valueHelp> + <format>duid</format> + <description>DHCP unique identifier</description> + </valueHelp> + <constraint> + <regex>([0-9A-Fa-f]{2}:){0,127}([0-9A-Fa-f]{2})</regex> + </constraint> + <constraintErrorMessage>Invalid DUID, must be in the format h[[:h]...]</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/version/dhcp-server-version.xml.i b/interface-definitions/include/version/dhcp-server-version.xml.i index 7c4b5633e..cc84ea8b9 100644 --- a/interface-definitions/include/version/dhcp-server-version.xml.i +++ b/interface-definitions/include/version/dhcp-server-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/dhcp-server-version.xml.i --> -<syntaxVersion component='dhcp-server' version='7'></syntaxVersion> +<syntaxVersion component='dhcp-server' version='8'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/dhcpv6-server-version.xml.i b/interface-definitions/include/version/dhcpv6-server-version.xml.i index ae4178c90..cb026a54a 100644 --- a/interface-definitions/include/version/dhcpv6-server-version.xml.i +++ b/interface-definitions/include/version/dhcpv6-server-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/dhcpv6-server-version.xml.i --> -<syntaxVersion component='dhcpv6-server' version='2'></syntaxVersion> +<syntaxVersion component='dhcpv6-server' version='3'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i index 299eebb00..6702ee041 100644 --- a/interface-definitions/include/version/firewall-version.xml.i +++ b/interface-definitions/include/version/firewall-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/firewall-version.xml.i --> -<syntaxVersion component='firewall' version='13'></syntaxVersion> +<syntaxVersion component='firewall' version='14'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/lldp-version.xml.i b/interface-definitions/include/version/lldp-version.xml.i index 0deb73279..b41d80451 100644 --- a/interface-definitions/include/version/lldp-version.xml.i +++ b/interface-definitions/include/version/lldp-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/lldp-version.xml.i --> -<syntaxVersion component='lldp' version='1'></syntaxVersion> +<syntaxVersion component='lldp' version='2'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/nat66-version.xml.i b/interface-definitions/include/version/nat66-version.xml.i index 478ca080f..43a54c969 100644 --- a/interface-definitions/include/version/nat66-version.xml.i +++ b/interface-definitions/include/version/nat66-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/nat66-version.xml.i --> -<syntaxVersion component='nat66' version='2'></syntaxVersion> +<syntaxVersion component='nat66' version='3'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i index 4fbe757f5..db727fea9 100644 --- a/interface-definitions/include/version/policy-version.xml.i +++ b/interface-definitions/include/version/policy-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/policy-version.xml.i --> -<syntaxVersion component='policy' version='7'></syntaxVersion> +<syntaxVersion component='policy' version='8'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/interfaces-virtual-ethernet.xml.in b/interface-definitions/interfaces-virtual-ethernet.xml.in index 5f205f354..0fc89efc0 100644 --- a/interface-definitions/interfaces-virtual-ethernet.xml.in +++ b/interface-definitions/interfaces-virtual-ethernet.xml.in @@ -9,7 +9,7 @@ <constraint> <regex>veth[0-9]+</regex> </constraint> - <constraintErrorMessage>Virutal Ethernet interface must be named vethN</constraintErrorMessage> + <constraintErrorMessage>Virtual Ethernet interface must be named vethN</constraintErrorMessage> <valueHelp> <format>vethN</format> <description>Virtual Ethernet interface name</description> diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 738bb11c1..25fb575b6 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -175,19 +175,12 @@ <multi/> </properties> </leafNode> - <node name="snmp"> + <leafNode name="snmp"> <properties> - <help>SNMP parameters for LLDP</help> + <help>Enable SNMP queries of the LLDP database</help> + <valueless/> </properties> - <children> - <leafNode name="enable"> - <properties> - <help>Enable SNMP queries of the LLDP database</help> - <valueless/> - </properties> - </leafNode> - </children> - </node> + </leafNode> </children> </node> </children> diff --git a/interface-definitions/nat64.xml.in b/interface-definitions/nat64.xml.in index baf13e6cb..dfdd295d2 100644 --- a/interface-definitions/nat64.xml.in +++ b/interface-definitions/nat64.xml.in @@ -26,6 +26,25 @@ <children> #include <include/generic-description.xml.i> #include <include/generic-disable-node.xml.i> + <node name="match"> + <properties> + <help>Match</help> + </properties> + <children> + <leafNode name="mark"> + <properties> + <help>Match fwmark value</help> + <valueHelp> + <format>u32:1-2147483647</format> + <description>Fwmark value to match against</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-2147483647"/> + </constraint> + </properties> + </leafNode> + </children> + </node> <node name="source"> <properties> <help>IPv6 source prefix options</help> diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in index d4ec75786..92e7a0cb4 100644 --- a/interface-definitions/policy-route.xml.in +++ b/interface-definitions/policy-route.xml.in @@ -12,7 +12,7 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> @@ -67,7 +67,7 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/firewall/enable-default-log.xml.i> + #include <include/firewall/default-log.xml.i> #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> diff --git a/interface-definitions/protocols-segment-routing.xml.in b/interface-definitions/protocols-segment-routing.xml.in index d461e9c5d..4308f0c91 100644 --- a/interface-definitions/protocols-segment-routing.xml.in +++ b/interface-definitions/protocols-segment-routing.xml.in @@ -8,6 +8,54 @@ <priority>900</priority> </properties> <children> + <tagNode name="interface"> + <properties> + <help>Interface specific Segment Routing options</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + </properties> + <children> + <node name="srv6"> + <properties> + <help>Accept SR-enabled IPv6 packets on this interface</help> + </properties> + <children> + <leafNode name="hmac"> + <properties> + <help>Define HMAC policy for ingress SR-enabled packets on this interface</help> + <completionHelp> + <list>accept drop ignore</list> + </completionHelp> + <valueHelp> + <format>accept</format> + <description>Accept packets without HMAC, validate packets with HMAC</description> + </valueHelp> + <valueHelp> + <format>drop</format> + <description>Drop packets without HMAC, validate packets with HMAC</description> + </valueHelp> + <valueHelp> + <format>ignore</format> + <description>Ignore HMAC field.</description> + </valueHelp> + <constraint> + <regex>(accept|drop|ignore)</regex> + </constraint> + </properties> + <defaultValue>accept</defaultValue> + </leafNode> + </children> + </node> + </children> + </tagNode> <node name="srv6"> <properties> <help>Segment-Routing SRv6 configuration</help> diff --git a/interface-definitions/service_ndp-proxy.xml.in b/interface-definitions/service_ndp-proxy.xml.in new file mode 100644 index 000000000..9801c99ab --- /dev/null +++ b/interface-definitions/service_ndp-proxy.xml.in @@ -0,0 +1,132 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="service"> + <children> + <node name="ndp-proxy" owner="${vyos_conf_scripts_dir}/service_ndp-proxy.py"> + <properties> + <help>Neighbor Discovery Protocol (NDP) Proxy</help> + </properties> + <children> + <leafNode name="route-refresh"> + <properties> + <help>Refresh interval for IPv6 routes</help> + <valueHelp> + <format>u32:10000-120000</format> + <description>Time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10000-120000"/> + </constraint> + <constraintErrorMessage>Route-refresh must be between 10000 and 120000 milliseconds</constraintErrorMessage> + </properties> + <defaultValue>30000</defaultValue> + </leafNode> + <tagNode name="interface"> + <properties> + <help>NDP proxy listener interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + <leafNode name="enable-router-bit"> + <properties> + <help>Enable router bit in Neighbor Advertisement messages</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="timeout"> + <properties> + <help>Timeout for Neighbor Advertisement after Neighbor Solicitation message</help> + <valueHelp> + <format>u32:500-120000</format> + <description>Timeout in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 500-120000"/> + </constraint> + <constraintErrorMessage>Timeout must be between 500 and 120000 milliseconds</constraintErrorMessage> + </properties> + <defaultValue>500</defaultValue> + </leafNode> + <leafNode name="ttl"> + <properties> + <help>Proxy entry cache Time-To-Live</help> + <valueHelp> + <format>u32:10000-120000</format> + <description>Time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10000-120000"/> + </constraint> + <constraintErrorMessage>TTL must be between 10000 and 120000 milliseconds</constraintErrorMessage> + </properties> + <defaultValue>30000</defaultValue> + </leafNode> + <tagNode name="prefix"> + <properties> + <help>Prefix target addresses are matched against</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 network prefix</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + <validator name="ipv6-address"/> + </constraint> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + <leafNode name="mode"> + <properties> + <help>Specify the running mode of the rule</help> + <completionHelp> + <list>static auto interface</list> + </completionHelp> + <valueHelp> + <format>static</format> + <description>Immediately answer any Neighbor Solicitation Messages</description> + </valueHelp> + <valueHelp> + <format>auto</format> + <description>Check for a matching route in /proc/net/ipv6_route</description> + </valueHelp> + <valueHelp> + <format>interface</format> + <description>Forward Neighbor Solicitation message through specified interface</description> + </valueHelp> + <constraint> + <regex>(static|auto|interface)</regex> + </constraint> + <constraintErrorMessage>Mode must be either one of: static, auto or interface</constraintErrorMessage> + </properties> + <defaultValue>static</defaultValue> + </leafNode> + <leafNode name="interface"> + <properties> + <help>Interface to forward Neighbor Solicitation message through. Required for "iface" mode</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <constraint> + #include <include/constraint/interface-name.xml.i> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/vpn-pptp.xml.in b/interface-definitions/vpn-pptp.xml.in index 96f87f3e2..7bb8db798 100644 --- a/interface-definitions/vpn-pptp.xml.in +++ b/interface-definitions/vpn-pptp.xml.in @@ -15,6 +15,9 @@ <children> #include <include/accel-ppp/max-concurrent-sessions.xml.i> #include <include/accel-ppp/mtu-128-16384.xml.i> + <leafNode name="mtu"> + <defaultValue>1436</defaultValue> + </leafNode> <leafNode name="outside-address"> <properties> <help>External IP address to which VPN clients will connect</help> @@ -35,6 +38,9 @@ <leafNode name="require"> <properties> <help>Authentication protocol for remote access peer PPTP VPN</help> + <completionHelp> + <list>pap chap mschap mschap-v2</list> + </completionHelp> <valueHelp> <format>pap</format> <description>Require the peer to authenticate itself using PAP [Password Authentication Protocol].</description> @@ -51,7 +57,11 @@ <format>mschap-v2</format> <description>Require the peer to authenticate itself using MS-CHAPv2 [Microsoft Challenge Handshake Authentication Protocol, Version 2].</description> </valueHelp> + <constraint> + <regex>(pap|chap|mschap|mschap-v2)</regex> + </constraint> </properties> + <defaultValue>mschap-v2</defaultValue> </leafNode> <leafNode name="mppe"> <properties> @@ -75,6 +85,7 @@ <list>deny prefer require</list> </completionHelp> </properties> + <defaultValue>prefer</defaultValue> </leafNode> #include <include/accel-ppp/auth-mode.xml.i> <node name="local-users"> @@ -97,6 +108,7 @@ <properties> <help>Static client IP address</help> </properties> + <defaultValue>*</defaultValue> </leafNode> </children> </tagNode> @@ -109,6 +121,16 @@ </node> #include <include/radius-auth-server-ipv4.xml.i> #include <include/accel-ppp/radius-additions.xml.i> + <node name="radius"> + <children> + <leafNode name="timeout"> + <defaultValue>30</defaultValue> + </leafNode> + <leafNode name="acct-timeout"> + <defaultValue>30</defaultValue> + </leafNode> + </children> + </node> </children> </node> #include <include/accel-ppp/default-pool.xml.i> diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in index 3a8118dcb..c03ec4cce 100644 --- a/op-mode-definitions/monitor-log.xml.in +++ b/op-mode-definitions/monitor-log.xml.in @@ -120,6 +120,12 @@ </properties> <command>journalctl --no-hostname --boot --follow --dmesg</command> </leafNode> + <leafNode name="ndp-proxy"> + <properties> + <help>Monitor last lines of Neighbor Discovery Protocol (NDP) Proxy</help> + </properties> + <command>journalctl --no-hostname --boot --follow --unit ndppd.service</command> + </leafNode> <leafNode name="nhrp"> <properties> <help>Monitor last lines of Next Hop Resolution Protocol log</help> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 399c6acf8..b013bdfe4 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -464,6 +464,12 @@ </properties> <command>egrep -i "kernel:.*\[NAT-[A-Z]{3,}-[0-9]+(-MASQ)?\]" $(find /var/log -maxdepth 1 -type f -name messages\* | sort -t. -k2nr)</command> </leafNode> + <leafNode name="ndp-proxy"> + <properties> + <help>Show log for Neighbor Discovery Protocol (NDP) Proxy</help> + </properties> + <command>journalctl --no-hostname --boot --unit ndppd.service</command> + </leafNode> <leafNode name="nhrp"> <properties> <help>Show log for Next Hop Resolution Protocol (NHRP)</help> diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 075ffe466..6a421485f 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -104,6 +104,10 @@ def list_diff(first, second): return [item for item in first if item not in second] def is_node_changed(conf, path): + """ + Check if any key under path has been changed and return True. + If nothing changed, return false + """ from vyos.configdiff import get_config_diff D = get_config_diff(conf, key_mangling=('-', '_')) return D.is_node_changed(path) @@ -139,17 +143,27 @@ def leaf_node_changed(conf, path): return None -def node_changed(conf, path, key_mangling=None, recursive=False): +def node_changed(conf, path, key_mangling=None, recursive=False, expand_nodes=None) -> list: """ - Check if a leaf node was altered. If it has been altered - values has been - changed, or it was added/removed, we will return the old value. If nothing - has been changed, None is returned + Check if node under path (or anything under path if recursive=True) was changed. By default + we only check if a node or subnode (recursive) was deleted from path. If expand_nodes + is set to Diff.ADD we can also check if something was added to the path. + + If nothing changed, an empty list is returned. """ - from vyos.configdiff import get_config_diff, Diff + from vyos.configdiff import get_config_diff + from vyos.configdiff import Diff + # to prevent circular dependencies we assign the default here + if not expand_nodes: expand_nodes = Diff.DELETE D = get_config_diff(conf, key_mangling) - # get_child_nodes() will return dict_keys(), mangle this into a list with PEP448 - keys = D.get_child_nodes_diff(path, expand_nodes=Diff.DELETE, recursive=recursive)['delete'].keys() - return list(keys) + # get_child_nodes_diff() will return dict_keys() + tmp = D.get_child_nodes_diff(path, expand_nodes=expand_nodes, recursive=recursive) + output = [] + if expand_nodes & Diff.DELETE: + output.extend(list(tmp['delete'].keys())) + if expand_nodes & Diff.ADD: + output.extend(list(tmp['add'].keys())) + return output def get_removed_vlans(conf, path, dict): """ diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index f19632719..ba638b280 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -23,6 +23,7 @@ from vyos.utils.process import popen _drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront', 'iavf', 'ice', 'i40e', 'hv_netvsc', 'veth', 'ixgbevf', 'tun'] +_drivers_without_eee = ['vmxnet3', 'virtio_net', 'xen_netfront', 'hv_netvsc'] class Ethtool: """ @@ -55,16 +56,18 @@ class Ethtool: _auto_negotiation_supported = None _flow_control = False _flow_control_enabled = None + _eee = False + _eee_enabled = None def __init__(self, ifname): # Get driver used for interface - out, err = popen(f'ethtool --driver {ifname}') + out, _ = popen(f'ethtool --driver {ifname}') driver = re.search(r'driver:\s(\w+)', out) if driver: self._driver_name = driver.group(1) # Build a dictinary of supported link-speed and dupley settings. - out, err = popen(f'ethtool {ifname}') + out, _ = popen(f'ethtool {ifname}') reading = False pattern = re.compile(r'\d+base.*') for line in out.splitlines()[1:]: @@ -95,7 +98,7 @@ class Ethtool: self._auto_negotiation = bool(tmp == 'on') # Now populate features dictionaty - out, err = popen(f'ethtool --show-features {ifname}') + out, _ = popen(f'ethtool --show-features {ifname}') # skip the first line, it only says: "Features for eth0": for line in out.splitlines()[1:]: if ":" in line: @@ -108,7 +111,7 @@ class Ethtool: 'fixed' : fixed } - out, err = popen(f'ethtool --show-ring {ifname}') + out, _ = popen(f'ethtool --show-ring {ifname}') # We are only interested in line 2-5 which contains the device maximum # ringbuffers for line in out.splitlines()[2:6]: @@ -133,13 +136,22 @@ class Ethtool: # Get current flow control settings, but this is not supported by # all NICs (e.g. vmxnet3 does not support is) - out, err = popen(f'ethtool --show-pause {ifname}') + out, _ = popen(f'ethtool --show-pause {ifname}') if len(out.splitlines()) > 1: self._flow_control = True # read current flow control setting, this returns: # ['Autonegotiate:', 'on'] self._flow_control_enabled = out.splitlines()[1].split()[-1] + # Get current Energy Efficient Ethernet (EEE) settings, but this is + # not supported by all NICs (e.g. vmxnet3 does not support is) + out, _ = popen(f'ethtool --show-eee {ifname}') + if len(out.splitlines()) > 1: + self._eee = True + # read current EEE setting, this returns: + # EEE status: disabled || EEE status: enabled - inactive || EEE status: enabled - active + self._eee_enabled = bool('enabled' in out.splitlines()[2]) + def check_auto_negotiation_supported(self): """ Check if the NIC supports changing auto-negotiation """ return self._auto_negotiation_supported @@ -227,3 +239,15 @@ class Ethtool: raise ValueError('Interface does not support changing '\ 'flow-control settings!') return self._flow_control_enabled + + def check_eee(self): + """ Check if the NIC supports eee """ + if self.get_driver_name() in _drivers_without_eee: + return False + return self._eee + + def get_eee(self): + if self._eee_enabled == None: + raise ValueError('Interface does not support changing '\ + 'EEE settings!') + return self._eee_enabled diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index aa1e87744..aaf903acd 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -399,6 +399,34 @@ class EthernetIf(Interface): print(f'could not set "{rx_tx}" ring-buffer for {ifname}') return output + def set_eee(self, enable): + """ + Enable/Disable Energy Efficient Ethernet (EEE) settings + + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_eee(enable=False) + """ + if not isinstance(enable, bool): + raise ValueError('Value out of range') + + if not self.ethtool.check_eee(): + self._debug_msg(f'NIC driver does not support changing EEE settings!') + return False + + current = self.ethtool.get_eee() + if current != enable: + # Assemble command executed on system. Unfortunately there is no way + # to change this setting via sysfs + cmd = f'ethtool --set-eee {self.ifname} eee ' + cmd += 'on' if enable else 'off' + output, code = self._popen(cmd) + if code: + Warning(f'could not change "{self.ifname}" EEE setting!') + return output + return None + def update(self, config): """ General helper function which works on a dictionary retrived by get_config_dict(). It's main intention is to consolidate the scattered @@ -409,6 +437,9 @@ class EthernetIf(Interface): value = 'off' if 'disable_flow_control' in config else 'on' self.set_flow_control(value) + # Always disable Energy Efficient Ethernet + self.set_eee(False) + # GRO (generic receive offload) self.set_gro(dict_search('offload.gro', config) != None) diff --git a/python/vyos/kea.py b/python/vyos/kea.py index 4a517da5f..819fe16a9 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -121,14 +121,20 @@ def kea_parse_subnet(subnet, config): if 'disable' in host_config: continue - obj = { - 'hw-address': host_config['mac_address'] + reservation = { + 'hostname': host, } + if 'mac' in host_config: + reservation['hw-address'] = host_config['mac'] + + if 'duid' in host_config: + reservation['duid'] = host_config['duid'] + if 'ip_address' in host_config: - obj['ip-address'] = host_config['ip_address'] + reservation['ip-address'] = host_config['ip_address'] - reservations.append(obj) + reservations.append(reservation) out['reservations'] = reservations unifi_controller = dict_search_args(config, 'vendor_option', 'ubiquiti', 'unifi_controller') @@ -178,7 +184,7 @@ def kea6_parse_options(config): if addrs: options.append({'name': 'sip-server-addr', 'data': ", ".join(addrs)}) - + if hosts: options.append({'name': 'sip-server-dns', 'data': ", ".join(hosts)}) @@ -234,10 +240,15 @@ def kea6_parse_subnet(subnet, config): if 'disable' in host_config: continue - reservation = {} + reservation = { + 'hostname': host + } + + if 'mac' in host_config: + reservation['hw-address'] = host_config['mac'] - if 'identifier' in host_config: - reservation['duid'] = host_config['identifier'] + if 'duid' in host_config: + reservation['duid'] = host_config['duid'] if 'ipv6_address' in host_config: reservation['ip-addresses'] = [ host_config['ipv6_address'] ] @@ -305,7 +316,7 @@ def kea_get_active_config(inet): ctrl_socket = f'/run/kea/dhcp{inet}-ctrl-socket' config = _ctrl_socket_command(ctrl_socket, 'config-get') - + if not config or 'result' not in config or config['result'] != 0: return None diff --git a/python/vyos/system/grub.py b/python/vyos/system/grub.py index 61a9c7749..a94729964 100644 --- a/python/vyos/system/grub.py +++ b/python/vyos/system/grub.py @@ -13,6 +13,8 @@ # 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 platform + from pathlib import Path from re import MULTILINE, compile as re_compile from typing import Union @@ -57,16 +59,22 @@ def install(drive_path: str, boot_dir: str, efi_dir: str, id: str = 'VyOS') -> N boot_dir (str): a path to '/boot' directory efi_dir (str): a path to '/boot/efi' directory """ - commands: list[str] = [ - f'grub-install --no-floppy --target=i386-pc --boot-directory={boot_dir} \ - {drive_path} --force', - f'grub-install --no-floppy --recheck --target=x86_64-efi \ + + efi_installation_arch = "x86_64" + if platform.machine() == "aarch64": + efi_installation_arch = "arm64" + elif platform.machine() == "x86_64": + cmd( + f'grub-install --no-floppy --target=i386-pc \ + --boot-directory={boot_dir} {drive_path} --force' + ) + + cmd( + f'grub-install --no-floppy --recheck --target={efi_installation_arch}-efi \ --force-extra-removable --boot-directory={boot_dir} \ --efi-directory={efi_dir} --bootloader-id="{id}" \ --no-uefi-secure-boot' - ] - for command in commands: - cmd(command) + ) def gen_version_uuid(version_name: str) -> str: diff --git a/python/vyos/template.py b/python/vyos/template.py index f0a50e728..77b6a5ab0 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -584,7 +584,7 @@ def nft_default_rule(fw_conf, fw_name, family): default_action = fw_conf['default_action'] #family = 'ipv6' if ipv6 else 'ipv4' - if 'enable_default_log' in fw_conf: + if 'default_log' in fw_conf: action_suffix = default_action[:1].upper() output.append(f'log prefix "[{family}-{fw_name[:19]}-default-{action_suffix}]"') diff --git a/scripts/override-default b/scripts/override-default index 0c49087c8..5058e79b3 100755 --- a/scripts/override-default +++ b/scripts/override-default @@ -41,6 +41,14 @@ if debug: else: logger.setLevel(logging.INFO) +def clear_empty_path(el): + # on the odd chance of interleaved comments + tmp = [l for l in el if isinstance(l.tag, str)] + if not tmp: + p = el.getparent() + p.remove(el) + clear_empty_path(p) + def override_element(l: list): """ Allow multiple override elements; use the final one (in document order). @@ -59,7 +67,9 @@ def override_element(l: list): # remove all but overridden element for el in parents: - el.getparent().remove(el) + tmp = el.getparent() + tmp.remove(el) + clear_empty_path(tmp) def merge_remaining(l: list, elementtree): """ @@ -81,7 +91,9 @@ def merge_remaining(l: list, elementtree): # override_element() has already run for child in el: rp[0].append(deepcopy(child)) - el.getparent().remove(el) + tmp = el.getparent() + tmp.remove(el) + clear_empty_path(tmp) def collect_and_override(dir_name): """ @@ -104,7 +116,7 @@ def collect_and_override(dir_name): for k, v in defv.items(): if len(v) > 1: - logger.info(f"overridding default in path '{k}'") + logger.info(f"overriding default in path '{k}'") override_element(v) to_merge = list(defv) diff --git a/smoketest/configs/ospf-small b/smoketest/configs/ospf-small index 767f4e21f..b3002b1af 100644 --- a/smoketest/configs/ospf-small +++ b/smoketest/configs/ospf-small @@ -81,6 +81,9 @@ service { lldp { interface all { } + snmp { + enable + } } snmp { community public { diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py index 682da44b0..682a0349a 100644 --- a/smoketest/scripts/cli/base_accel_ppp_test.py +++ b/smoketest/scripts/cli/base_accel_ppp_test.py @@ -393,6 +393,7 @@ class BasicAccelPPPTest: first_pool = "POOL1" second_pool = "POOL2" range = "192.0.2.10-192.0.2.20" + range_config = "192.0.2.10-20" self.set(["gateway-address", gateway]) self.set(["client-ip-pool", first_pool, "range", subnet]) @@ -410,7 +411,7 @@ class BasicAccelPPPTest: self.assertEqual( f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},name"] ) - self.assertEqual(second_pool, conf["ip-pool"][f"{range},name"]) + self.assertEqual(second_pool, conf["ip-pool"][f"{range_config},name"]) self.assertEqual(gateway, conf["ip-pool"]["gw-ip-address"]) self.assertEqual(first_pool, conf[self._protocol_section]["ip-pool"]) diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 5cfddb269..2be616da1 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -209,7 +209,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): conn_mark = '555' self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10']) @@ -226,7 +226,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'ttl', 'gt', '102']) self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'enable-default-log']) + self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-log']) self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'action', 'accept']) self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'protocol', 'tcp']) self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'destination', 'port', '22']) @@ -250,7 +250,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'connection-mark', conn_mark]) self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'enable-default-log']) + self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-log']) self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'action', 'drop']) self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'protocol', 'gre']) self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'outbound-interface', 'name', interface_inv]) @@ -292,7 +292,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): interface = 'eth0' self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'action', 'accept']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '64']) @@ -379,7 +379,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1']) self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'drop']) self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2']) @@ -413,7 +413,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop']) self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'source', 'address', '2002::1']) @@ -422,14 +422,14 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log-options', 'level', 'crit']) self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-action', 'accept']) - self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'enable-default-log']) + self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-log']) self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'protocol', 'tcp_udp']) self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'destination', 'port', '8888']) self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'inbound-interface', 'name', interface]) self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'enable-default-log']) + self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-log']) self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'action', 'return']) self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'protocol', 'gre']) self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'outbound-interface', 'name', interface]) @@ -472,7 +472,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): interface = 'eth0' self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'action', 'accept']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'packet-length', '65']) @@ -516,7 +516,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef']) self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop']) - self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'drop']) self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444']) @@ -596,7 +596,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): vlan_prior = '3' self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept']) - self.cli_set(['firewall', 'bridge', 'name', name, 'enable-default-log']) + self.cli_set(['firewall', 'bridge', 'name', name, 'default-log']) self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'source', 'mac-address', mac_address]) self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'inbound-interface', 'name', interface_in]) @@ -604,7 +604,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log-options', 'level', 'crit']) self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-action', 'drop']) - self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'enable-default-log']) + self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-log']) self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'action', 'accept']) self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'id', vlan_id]) self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'action', 'jump']) diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index 32e86ea7e..c0b7c1fe7 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -33,6 +33,9 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestPolicyRoute, cls).setUpClass() + # Clear out current configuration to allow running this test on a live system + cls.cli_delete(cls, ['policy', 'route']) + cls.cli_delete(cls, ['policy', 'route6']) cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip]) cls.cli_set(cls, ['protocols', 'static', 'table', table_id, 'route', '0.0.0.0/0', 'interface', interface]) @@ -189,6 +192,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): def test_pbr_matching_criteria(self): + self.cli_set(['policy', 'route', 'smoketest', 'default-log']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'udp']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'action', 'drop']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'mark', '2020']) @@ -216,6 +220,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'mark', '!456-500']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'set', 'table', table_id]) + self.cli_set(['policy', 'route6', 'smoketest6', 'default-log']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'protocol', 'tcp']) @@ -255,7 +260,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): ['tcp flags syn / syn,ack', 'meta mark 0x00000002-0x00000bb8', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark != 0x000001c8', 'meta mark set ' + mark_hex], ['log prefix "[ipv4-route-smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta pkttype other', 'meta mark set ' + mark_hex], - ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark != 0x000001c8-0x000001f4', 'meta mark set ' + mark_hex] + ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark != 0x000001c8-0x000001f4', 'meta mark set ' + mark_hex], + ['log prefix "[ipv4-smoketest-default]"'] ] self.verify_nftables(nftables_search, 'ip vyos_mangle') @@ -267,7 +273,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex], ['log prefix "[ipv6-route6-smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta pkttype multicast', 'meta mark set ' + mark_hex], - ['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex] + ['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex], + ['log prefix "[ipv6-smoketest6-default]"'] ] self.verify_nftables(nftables6_search, 'ip6 vyos_mangle') diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 4697d4970..ebc9eeaaa 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -16,7 +16,6 @@ import unittest -from subprocess import run from time import sleep from base_vyostest_shim import VyOSUnitTestSHIM @@ -25,6 +24,7 @@ from vyos.ifconfig import Section from vyos.configsession import ConfigSessionError from vyos.template import is_ipv6 from vyos.utils.process import process_named_running +from vyos.utils.process import cmd PROCESS_NAME = 'bgpd' ASN = '64512' @@ -1162,11 +1162,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): mirror_buffer = '32000000' bmp_path = base_path + ['bmp'] target_path = bmp_path + ['target', target_name] - command = ['/opt/vyatta/bin/vyatta-op-cmd-wrapper', 'restart', 'bgp'] + # by default the 'bmp' module not loaded for the bgpd expect Error self.cli_set(bmp_path) - # by default the 'bmp' module not loaded for the bgpd - # expect Error if not process_named_running('bgpd', 'bmp'): with self.assertRaises(ConfigSessionError): self.cli_commit() @@ -1177,7 +1175,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_commit() # restart bgpd to apply "-M bmp" and update PID - run(command, input='Y', text=True) + cmd(f'sudo kill -9 {self.daemon_pid}') # let the bgpd process recover sleep(10) # update daemon PID - this was a planned daemon restart diff --git a/smoketest/scripts/cli/test_protocols_segment_routing.py b/smoketest/scripts/cli/test_protocols_segment_routing.py index 81d42b925..403c05924 100755 --- a/smoketest/scripts/cli/test_protocols_segment_routing.py +++ b/smoketest/scripts/cli/test_protocols_segment_routing.py @@ -20,8 +20,10 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section from vyos.utils.process import cmd from vyos.utils.process import process_named_running +from vyos.utils.system import sysctl_read base_path = ['protocols', 'segment-routing'] PROCESS_NAME = 'zebra' @@ -45,6 +47,7 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME)) def test_srv6(self): + interfaces = Section.interfaces('ethernet', vlan=False) locators = { 'foo' : { 'prefix' : '2001:a::/64' }, 'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} }, @@ -55,8 +58,18 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): if 'usid' in locator_config: self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid']) + # verify() - SRv6 should be enabled on at least one interface! + with self.assertRaises(ConfigSessionError): + self.cli_commit() + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'srv6']) + self.cli_commit() + for interface in interfaces: + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '0') # default + frrconfig = self.getFRRconfig(f'segment-routing', daemon='zebra') self.assertIn(f'segment-routing', frrconfig) self.assertIn(f' srv6', frrconfig) @@ -65,6 +78,35 @@ class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): self.assertIn(f' locator {locator}', frrconfig) self.assertIn(f' prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', frrconfig) + def test_srv6_sysctl(self): + interfaces = Section.interfaces('ethernet', vlan=False) + + # HMAC accept + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'srv6']) + self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'ignore']) + self.cli_commit() + + for interface in interfaces: + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '-1') # ignore + + # HMAC drop + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'srv6']) + self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'drop']) + self.cli_commit() + + for interface in interfaces: + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1') + self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '1') # drop + + # Disable SRv6 on first interface + first_if = interfaces[-1] + self.cli_delete(base_path + ['interface', first_if]) + self.cli_commit() + + self.assertEqual(sysctl_read(f'net.ipv6.conf.{first_if}.seg6_enabled'), '0') if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 9f6e05ff3..bf0c09965 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -22,13 +22,10 @@ from json import loads from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError -from vyos.utils.dict import dict_search_recursive from vyos.utils.process import process_named_running from vyos.utils.file import read_file -from vyos.template import address_from_cidr from vyos.template import inc_ip from vyos.template import dec_ip -from vyos.template import netmask_from_cidr PROCESS_NAME = 'kea-dhcp4' CTRL_PROCESS_NAME = 'kea-ctrl-agent' @@ -45,6 +42,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestServiceDHCPServer, cls).setUpClass() + # Clear out current configuration to allow running this test on a live system + cls.cli_delete(cls, base_path) cidr_mask = subnet.split('/')[-1] cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}']) @@ -300,10 +299,16 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): client_base = 10 for client in ['client1', 'client2', 'client3']: mac = '00:50:00:00:00:{}'.format(client_base) - self.cli_set(pool + ['static-mapping', client, 'mac-address', mac]) + self.cli_set(pool + ['static-mapping', client, 'mac', mac]) self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)]) client_base += 1 + # cannot have both mac-address and duid set + with self.assertRaises(ConfigSessionError): + self.cli_set(pool + ['static-mapping', 'client1', 'duid', '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:11']) + self.cli_commit() + self.cli_delete(pool + ['static-mapping', 'client1', 'duid']) + # commit changes self.cli_commit() @@ -337,7 +342,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.verify_config_object( obj, ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'reservations'], - {'hw-address': mac, 'ip-address': ip}) + {'hostname': client, 'hw-address': mac, 'ip-address': ip}) client_base += 1 @@ -373,7 +378,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): client_base = 60 for client in ['client1', 'client2', 'client3', 'client4']: mac = '02:50:00:00:00:{}'.format(client_base) - self.cli_set(pool + ['static-mapping', client, 'mac-address', mac]) + self.cli_set(pool + ['static-mapping', client, 'mac', mac]) self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)]) client_base += 1 @@ -429,7 +434,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.verify_config_object( obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'reservations'], - {'hw-address': mac, 'ip-address': ip}) + {'hostname': client, 'hw-address': mac, 'ip-address': ip}) client_base += 1 diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py index 175a67537..f163cc69a 100755 --- a/smoketest/scripts/cli/test_service_dhcpv6-server.py +++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py @@ -41,6 +41,9 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestServiceDHCPv6Server, cls).setUpClass() + # Clear out current configuration to allow running this test on a live system + cls.cli_delete(cls, base_path) + cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_addr]) @classmethod @@ -122,12 +125,18 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): client_base = 1 for client in ['client1', 'client2', 'client3']: - cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base) - self.cli_set(pool + ['static-mapping', client, 'identifier', cid]) + duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}' + self.cli_set(pool + ['static-mapping', client, 'duid', duid]) self.cli_set(pool + ['static-mapping', client, 'ipv6-address', inc_ip(subnet, client_base)]) self.cli_set(pool + ['static-mapping', client, 'ipv6-prefix', inc_ip(subnet, client_base << 64) + '/64']) client_base += 1 + # cannot have both mac-address and duid set + with self.assertRaises(ConfigSessionError): + self.cli_set(pool + ['static-mapping', 'client1', 'mac', '00:50:00:00:00:11']) + self.cli_commit() + self.cli_delete(pool + ['static-mapping', 'client1', 'mac']) + # commit changes self.cli_commit() @@ -182,14 +191,14 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): client_base = 1 for client in ['client1', 'client2', 'client3']: - cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base) + duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}' ip = inc_ip(subnet, client_base) prefix = inc_ip(subnet, client_base << 64) + '/64' self.verify_config_object( obj, ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'reservations'], - {'duid': cid, 'ip-addresses': [ip], 'prefixes': [prefix]}) + {'hostname': client, 'duid': duid, 'ip-addresses': [ip], 'prefixes': [prefix]}) client_base += 1 diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py index 358668e0d..6e95b3bd1 100755 --- a/smoketest/scripts/cli/test_service_ipoe-server.py +++ b/smoketest/scripts/cli/test_service_ipoe-server.py @@ -117,6 +117,7 @@ class TestServiceIPoEServer(BasicAccelPPPTest.TestCase): first_pool = "POOL1" second_pool = "POOL2" range = "192.0.2.10-192.0.2.20" + range_config = "192.0.2.10-20" for gw in gateway: self.set(["gateway-address", gw]) @@ -141,7 +142,7 @@ class TestServiceIPoEServer(BasicAccelPPPTest.TestCase): self.assertIn( f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},name"] ) - self.assertIn(second_pool, conf["ip-pool"][f"{range},name"]) + self.assertIn(second_pool, conf["ip-pool"][f"{range_config},name"]) gw_pool_config_list = conf.get("ip-pool", "gw-ip-address") gw_ipoe_config_list = conf.get(self._protocol_section, "gw-ip-address") diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py index ee26844ab..7e30b43f5 100755 --- a/smoketest/scripts/cli/test_service_lldp.py +++ b/smoketest/scripts/cli/test_service_lldp.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 @@ -122,6 +122,20 @@ class TestServiceLLDP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'configure ports {interface} med location elin "{elin}"', config) self.assertIn(f'configure system interface pattern "{interface}"', config) + def test_06_lldp_snmp(self): + self.cli_set(base_path + ['snmp']) + + # verify - can not start lldp snmp without snmp beeing configured + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['service', 'snmp']) + self.cli_commit() + + # SNMP required process to be started with -x option + tmp = read_file('/etc/default/lldpd') + self.assertIn('-x', tmp) + + self.cli_delete(['service', 'snmp']) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_ndp-proxy.py b/smoketest/scripts/cli/test_service_ndp-proxy.py new file mode 100755 index 000000000..a947ec478 --- /dev/null +++ b/smoketest/scripts/cli/test_service_ndp-proxy.py @@ -0,0 +1,70 @@ +#!/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/>. + +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section +from vyos.utils.process import cmd +from vyos.utils.process import process_named_running + +PROCESS_NAME = 'ndppd' +NDPPD_CONF = '/run/ndppd/ndppd.conf' +base_path = ['service', 'ndp-proxy'] + +def getConfigSection(string=None, end=' {', endsection='^}'): + tmp = f'cat {NDPPD_CONF} | sed -n "/^{string}{end}/,/{endsection}/p"' + out = cmd(tmp) + return out + +class TestServiceNDPProxy(VyOSUnitTestSHIM.TestCase): + @classmethod + def setUpClass(cls): + super(TestServiceNDPProxy, cls).setUpClass() + + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + # delete testing SSH config + self.cli_delete(base_path) + self.cli_commit() + + self.assertFalse(process_named_running(PROCESS_NAME)) + + def test_basic(self): + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['interface', interface, 'enable-router-bit']) + + self.cli_commit() + + for interface in interfaces: + config = getConfigSection(f'proxy {interface}') + self.assertIn(f'proxy {interface}', config) + self.assertIn(f'router yes', config) + self.assertIn(f'timeout 500', config) # default value + self.assertIn(f'ttl 30000', config) # default value + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index bb91eddea..6207a1b41 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -30,6 +30,7 @@ from vyos.utils.process import cmd from vyos.utils.file import read_file from vyos.utils.network import get_interface_config from vyos.utils.network import is_intf_addr_assigned +from vyos.utils.system import sysctl_read base_path = ['vrf'] vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] @@ -58,6 +59,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() for vrf in vrfs: self.assertNotIn(vrf, interfaces()) + # If there is no VRF defined, strict_mode should be off + self.assertEqual(sysctl_read('net.vrf.strict_mode'), '0') def test_vrf_vni_and_table_id(self): base_table = '1000' @@ -130,8 +133,9 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): # Ensure VRF was created self.assertIn(vrf, interfaces()) # Verify IP forwarding is 1 (enabled) - self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '1') - self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '1') + self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '1') + self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '1') + # Test for proper loopback IP assignment for addr in loopbacks: self.assertTrue(is_intf_addr_assigned(vrf, addr)) @@ -149,10 +153,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify VRF configuration - tmp = read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept') - self.assertIn(tmp, '1') - tmp = read_file('/proc/sys/net/ipv4/udp_l3mdev_accept') - self.assertIn(tmp, '1') + self.assertEqual(sysctl_read('net.ipv4.tcp_l3mdev_accept'), '1') + self.assertEqual(sysctl_read('net.ipv4.udp_l3mdev_accept'), '1') + + # If there is any VRF defined, strict_mode should be on + self.assertEqual(sysctl_read('net.vrf.strict_mode'), '1') def test_vrf_table_id_is_unalterable(self): # Linux Kernel prohibits the change of a VRF table on the fly. @@ -290,8 +295,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): # Ensure VRF was created self.assertIn(vrf, interfaces()) # Verify IP forwarding is 0 (disabled) - self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '0') - self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '0') + self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '0') + self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '0') def test_vrf_ip_protocol_route_map(self): table = '6000' diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 958e90014..c1308cda7 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -18,7 +18,6 @@ import os from ipaddress import ip_address from ipaddress import ip_network -from netaddr import IPAddress from netaddr import IPRange from sys import exit @@ -41,6 +40,7 @@ ctrl_config_file = '/run/kea/kea-ctrl-agent.conf' ctrl_socket = '/run/kea/dhcp4-ctrl-socket' config_file = '/run/kea/kea-dhcp4.conf' lease_file = '/config/dhcp4.leases' +systemd_override = r'/run/systemd/system/kea-ctrl-agent.service.d/10-override.conf' ca_cert_file = '/run/kea/kea-failover-ca.pem' cert_file = '/run/kea/kea-failover.pem' @@ -141,7 +141,7 @@ def get_config(config=None): {'range' : new_range_dict}) if dict_search('failover.certificate', dhcp): - dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + dhcp['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) return dhcp @@ -226,9 +226,10 @@ def verify(dhcp): raise ConfigError(f'Configured static lease address for mapping "{mapping}" is\n' \ f'not within shared-network "{network}, {subnet}"!') - if 'mac_address' not in mapping_config: - raise ConfigError(f'MAC address required for static mapping "{mapping}"\n' \ - f'within shared-network "{network}, {subnet}"!') + if ('mac' not in mapping_config and 'duid' not in mapping_config) or \ + ('mac' in mapping_config and 'duid' in mapping_config): + raise ConfigError(f'Either MAC address or Client identifier (DUID) is required for ' + f'static mapping "{mapping}" within shared-network "{network}, {subnet}"!') # There must be one subnet connected to a listen interface. # This only counts if the network itself is not disabled! @@ -332,6 +333,8 @@ def generate(dhcp): dhcp['failover']['ca_cert_file'] = ca_cert_file + render(systemd_override, 'dhcp-server/10-override.conf.j2', dhcp) + render(ctrl_config_file, 'dhcp-server/kea-ctrl-agent.conf.j2', dhcp) render(config_file, 'dhcp-server/kea-dhcp4.conf.j2', dhcp) diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py index b01f510e5..f9da3d84a 100755 --- a/src/conf_mode/dhcpv6_server.py +++ b/src/conf_mode/dhcpv6_server.py @@ -135,6 +135,11 @@ def verify(dhcpv6): if ip_address(mapping_config['ipv6_address']) not in ip_network(subnet): raise ConfigError(f'static-mapping address for mapping "{mapping}" is not in subnet "{subnet}"!') + if ('mac' not in mapping_config and 'duid' not in mapping_config) or \ + ('mac' in mapping_config and 'duid' in mapping_config): + raise ConfigError(f'Either MAC address or Client identifier (DUID) is required for ' + f'static mapping "{mapping}" within shared-network "{network}, {subnet}"!') + if 'vendor_option' in subnet_config: if len(dict_search('vendor_option.cisco.tftp_server', subnet_config)) > 2: raise ConfigError(f'No more then two Cisco tftp-servers should be defined for subnet "{subnet}"!') diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py index 809c650d9..99fa8feee 100755 --- a/src/conf_mode/dns_dynamic.py +++ b/src/conf_mode/dns_dynamic.py @@ -15,7 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os - +import re from sys import exit from vyos.base import Warning @@ -103,6 +103,16 @@ def verify(dyndns): raise ConfigError(f'"web-options" is applicable only when using HTTP(S) ' f'web request to obtain the IP address') + # Warn if using checkip.dyndns.org, as it does not support HTTPS + # See: https://github.com/ddclient/ddclient/issues/597 + if 'web_options' in config: + if 'url' not in config['web_options']: + raise ConfigError(f'"url" in "web-options" {error_msg_req} ' + f'with protocol "{config["protocol"]}"') + elif re.search("^(https?://)?checkip\.dyndns\.org", config['web_options']['url']): + Warning(f'"checkip.dyndns.org" does not support HTTPS requests for IP address ' + f'lookup. Please use a different IP address lookup service.') + # RFC2136 uses 'key' instead of 'password' if config['protocol'] != 'nsupdate' and 'password' not in config: raise ConfigError(f'"password" {error_msg_req}') diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index ceed0cf31..da6724fde 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -23,7 +23,7 @@ from sys import exit from vyos.base import Warning from vyos.config import Config -from vyos.configdict import node_changed +from vyos.configdict import is_node_changed from vyos.configdiff import get_config_diff, Diff from vyos.configdep import set_dependents, call_dependents from vyos.configverify import verify_interface_exists @@ -133,7 +133,7 @@ def get_config(config=None): with_recursive_defaults=True) - firewall['group_resync'] = bool('group' in firewall or node_changed(conf, base + ['group'])) + firewall['group_resync'] = bool('group' in firewall or is_node_changed(conf, base + ['group'])) if firewall['group_resync']: # Update nat and policy-route as firewall groups were updated set_dependents('group_resync', conf) diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 40b7de557..3dc5dfc01 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -24,6 +24,7 @@ from time import sleep import vyos.defaults import vyos.certbot_util +from vyos.base import Warning from vyos.config import Config from vyos.configdiff import get_config_diff from vyos.configverify import verify_vrf @@ -193,6 +194,9 @@ def verify(https): if (not valid_keys_exist) and (not jwt_auth): raise ConfigError('At least one HTTPS API key is required unless GraphQL token authentication is enabled') + if (not valid_keys_exist) and jwt_auth: + Warning(f'API keys are not configured: the classic (non-GraphQL) API will be unavailable.') + return None def generate(https): diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py index 31508a3c5..29991e2da 100755 --- a/src/conf_mode/interfaces-bridge.py +++ b/src/conf_mode/interfaces-bridge.py @@ -49,7 +49,7 @@ def get_config(config=None): ifname, bridge = get_interface_dict(conf, base) # determine which members have been removed - tmp = node_changed(conf, base + [ifname, 'member', 'interface'], key_mangling=('-', '_')) + tmp = node_changed(conf, base + [ifname, 'member', 'interface']) if tmp: if 'member' in bridge: bridge['member'].update({'interface_remove' : tmp }) diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index c2e87d171..3c647a0e8 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -86,9 +86,9 @@ def verify(lldp): raise ConfigError(f'Must define both longitude and latitude for "{interface}" location!') # check options - if 'snmp' in lldp and 'enable' in lldp['snmp']: + if 'snmp' in lldp: if 'system_snmp_enabled' not in lldp: - raise ConfigError('SNMP must be configured to enable LLDP SNMP') + raise ConfigError('SNMP must be configured to enable LLDP SNMP!') def generate(lldp): @@ -121,4 +121,3 @@ if __name__ == '__main__': except ConfigError as e: print(e) exit(1) - diff --git a/src/conf_mode/nat64.py b/src/conf_mode/nat64.py index a8b90fb11..6026c61d0 100755 --- a/src/conf_mode/nat64.py +++ b/src/conf_mode/nat64.py @@ -148,6 +148,11 @@ def generate(nat64) -> None: if dict_search("translation.pool", instance): pool4 = [] + # mark + mark = '' + if dict_search("match.mark", instance): + mark = instance["match"]["mark"] + for pool in instance["translation"]["pool"].values(): if "disable" in pool: continue @@ -159,6 +164,8 @@ def generate(nat64) -> None: "prefix": pool["address"], "port range": pool["port"], } + if mark: + obj["mark"] = int(mark) if "description" in pool: obj["comment"] = pool["description"] diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 0ba08aef3..dee1551fe 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 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 @@ -36,7 +36,6 @@ airbag.enable() k_mod = ['nft_nat', 'nft_chain_nat'] nftables_nat66_config = '/run/nftables_nat66.nft' -ndppd_config = '/run/ndppd/ndppd.conf' def get_config(config=None): if config: @@ -101,7 +100,6 @@ def generate(nat): nat['first_install'] = True render(nftables_nat66_config, 'firewall/nftables-nat66.j2', nat, permission=0o755) - render(ndppd_config, 'ndppd/ndppd.conf.j2', nat, permission=0o755) return None def apply(nat): @@ -109,14 +107,6 @@ def apply(nat): return None cmd(f'nft -f {nftables_nat66_config}') - - if 'deleted' in nat or not dict_search('source.rule', nat): - cmd('systemctl stop ndppd') - if os.path.isfile(ndppd_config): - os.unlink(ndppd_config) - else: - cmd('systemctl restart ndppd') - call_dependents() return None diff --git a/src/conf_mode/netns.py b/src/conf_mode/netns.py index 95ab83dbc..7cee33bc6 100755 --- a/src/conf_mode/netns.py +++ b/src/conf_mode/netns.py @@ -77,8 +77,8 @@ def verify(netns): if 'netns_remove' in netns: for name, config in netns['netns_remove'].items(): if 'interface' in config: - raise ConfigError(f'Can not remove NETNS "{name}", it still has '\ - f'member interfaces!') + raise ConfigError(f'Can not remove network namespace "{name}", it '\ + f'still has member interfaces!') if 'name' in netns: for name, config in netns['name'].items(): @@ -87,7 +87,6 @@ def verify(netns): return None - def generate(netns): if not netns: return None diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py index 5ec0bc9e5..c339c6391 100755 --- a/src/conf_mode/protocols_nhrp.py +++ b/src/conf_mode/protocols_nhrp.py @@ -37,7 +37,7 @@ def get_config(config=None): nhrp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) - nhrp['del_tunnels'] = node_changed(conf, base + ['tunnel'], key_mangling=('-', '_')) + nhrp['del_tunnels'] = node_changed(conf, base + ['tunnel']) if not conf.exists(base): return nhrp diff --git a/src/conf_mode/protocols_segment_routing.py b/src/conf_mode/protocols_segment_routing.py index eb1653212..d865c2ac0 100755 --- a/src/conf_mode/protocols_segment_routing.py +++ b/src/conf_mode/protocols_segment_routing.py @@ -19,7 +19,10 @@ import os from sys import exit from vyos.config import Config +from vyos.configdict import node_changed from vyos.template import render_to_string +from vyos.utils.dict import dict_search +from vyos.utils.system import sysctl_write from vyos import ConfigError from vyos import frr from vyos import airbag @@ -32,33 +35,74 @@ def get_config(config=None): conf = Config() base = ['protocols', 'segment-routing'] - sr = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) + sr = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True, + with_recursive_defaults=True) - # We have gathered the dict representation of the CLI, but there are default - # options which we need to update into the dictionary retrived. - sr = conf.merge_defaults(sr, recursive=True) + # FRR has VRF support for different routing daemons. As interfaces belong + # to VRFs - or the global VRF, we need to check for changed interfaces so + # that they will be properly rendered for the FRR config. Also this eases + # removal of interfaces from the running configuration. + interfaces_removed = node_changed(conf, base + ['interface']) + if interfaces_removed: + sr['interface_removed'] = list(interfaces_removed) + import pprint + pprint.pprint(sr) return sr -def verify(static): +def verify(sr): + if 'srv6' in sr: + srv6_enable = False + if 'interface' in sr: + for interface, interface_config in sr['interface'].items(): + if 'srv6' in interface_config: + srv6_enable = True + break + if not srv6_enable: + raise ConfigError('SRv6 should be enabled on at least one interface!') return None -def generate(static): - if not static: +def generate(sr): + if not sr: return None - static['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', static) + sr['new_frr_config'] = render_to_string('frr/zebra.segment_routing.frr.j2', sr) return None -def apply(static): +def apply(sr): zebra_daemon = 'zebra' + if 'interface_removed' in sr: + for interface in sr['interface_removed']: + # Disable processing of IPv6-SR packets + sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0') + + if 'interface' in sr: + for interface, interface_config in sr['interface'].items(): + # Accept or drop SR-enabled IPv6 packets on this interface + if 'srv6' in interface_config: + sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '1') + # Define HMAC policy for ingress SR-enabled packets on this interface + # It's a redundant check as HMAC has a default value - but better safe + # then sorry + tmp = dict_search('srv6.hmac', interface_config) + if tmp == 'accept': + sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '0') + elif tmp == 'drop': + sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '1') + elif tmp == 'ignore': + sysctl_write(f'net.ipv6.conf.{interface}.seg6_require_hmac', '-1') + else: + sysctl_write(f'net.ipv6.conf.{interface}.seg6_enabled', '0') + # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() frr_cfg.load_configuration(zebra_daemon) frr_cfg.modify_section(r'^segment-routing') - if 'new_frr_config' in static: - frr_cfg.add_before(frr.default_add_before, static['new_frr_config']) + if 'new_frr_config' in sr: + frr_cfg.add_before(frr.default_add_before, sr['new_frr_config']) frr_cfg.commit_configuration(zebra_daemon) return None diff --git a/src/conf_mode/service_ndp-proxy.py b/src/conf_mode/service_ndp-proxy.py new file mode 100755 index 000000000..aa2374f4c --- /dev/null +++ b/src/conf_mode/service_ndp-proxy.py @@ -0,0 +1,91 @@ +#!/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/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configverify import verify_interface_exists +from vyos.utils.process import call +from vyos.template import render +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +systemd_service = 'ndppd.service' +ndppd_config = '/run/ndppd/ndppd.conf' + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['service', 'ndp-proxy'] + if not conf.exists(base): + return None + + ndpp = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True, + with_recursive_defaults=True) + + return ndpp + +def verify(ndpp): + if not ndpp: + return None + + if 'interface' in ndpp: + for interface, interface_config in ndpp['interface'].items(): + verify_interface_exists(interface) + + if 'rule' in interface_config: + for rule, rule_config in interface_config['rule'].items(): + if rule_config['mode'] == 'interface' and 'interface' not in rule_config: + raise ConfigError(f'Rule "{rule}" uses interface mode but no interface defined!') + + if rule_config['mode'] != 'interface' and 'interface' in rule_config: + if interface_config['mode'] != 'interface' and 'interface' in interface_config: + raise ConfigError(f'Rule "{rule}" does not use interface mode, thus interface can not be defined!') + + return None + +def generate(ndpp): + if not ndpp: + return None + + render(ndppd_config, 'ndppd/ndppd.conf.j2', ndpp) + return None + +def apply(ndpp): + if not ndpp: + call(f'systemctl stop {systemd_service}') + if os.path.isfile(ndppd_config): + os.unlink(ndppd_config) + return None + + call(f'systemctl reload-or-restart {systemd_service}') + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index d2ed5414f..6565ffd60 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2021 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -54,7 +54,7 @@ def get_config(config=None): if not conf.exists(base): snmp.update({'deleted' : ''}) - if conf.exists(['service', 'lldp', 'snmp', 'enable']): + if conf.exists(['service', 'lldp', 'snmp']): snmp.update({'lldp_snmp' : ''}) if 'deleted' in snmp: @@ -86,7 +86,7 @@ def get_config(config=None): return snmp def verify(snmp): - if not snmp: + if 'deleted' in snmp: return None if {'deleted', 'lldp_snmp'} <= set(snmp): @@ -178,8 +178,6 @@ def verify(snmp): return None def generate(snmp): - - # # As we are manipulating the snmpd user database we have to stop it first! # This is even save if service is going to be removed call(f'systemctl stop {systemd_service}') @@ -190,7 +188,7 @@ def generate(snmp): if os.path.isfile(file): os.unlink(file) - if not snmp: + if 'deleted' in snmp: return None if 'v3' in snmp: @@ -244,7 +242,7 @@ def apply(snmp): # Always reload systemd manager configuration call('systemctl daemon-reload') - if not snmp: + if 'deleted' in snmp: return None # start SNMP daemon @@ -256,9 +254,7 @@ def apply(snmp): # Following daemons from FRR 9.0/stable have SNMP module compiled in VyOS frr_daemons_list = ['zebra', 'bgpd', 'ospf6d', 'ospfd', 'ripd', 'isisd', 'ldpd'] for frr_daemon in frr_daemons_list: - call( - f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null' - ) + call(f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null') return None diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 6243c3ed3..f769be39f 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -15,21 +15,17 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import re - -from copy import deepcopy -from stat import S_IRUSR, S_IWUSR, S_IRGRP from sys import exit from vyos.config import Config from vyos.template import render -from vyos.utils.system import get_half_cpus from vyos.utils.process import call from vyos.utils.dict import dict_search from vyos.accel_ppp_util import verify_accel_ppp_ip_pool from vyos.accel_ppp_util import get_pools_in_order from vyos import ConfigError +from vyos.configdict import get_accel_dict from vyos import airbag airbag.enable() @@ -37,213 +33,24 @@ airbag.enable() pptp_conf = '/run/accel-pppd/pptp.conf' pptp_chap_secrets = '/run/accel-pppd/pptp.chap-secrets' -default_pptp = { - 'auth_mode' : 'local', - 'local_users' : [], - 'radius_server' : [], - 'radius_acct_inter_jitter': '', - 'radius_acct_interim_interval': None, - 'radius_acct_tmo' : '30', - 'radius_max_try' : '3', - 'radius_timeout' : '30', - 'radius_nas_id' : '', - 'radius_nas_ip' : '', - 'radius_source_address' : '', - 'radius_shaper_attr' : '', - 'radius_shaper_enable': False, - 'radius_shaper_multiplier': '', - 'radius_shaper_vendor': '', - 'radius_dynamic_author' : '', - 'chap_secrets_file': pptp_chap_secrets, # used in Jinja2 template - 'outside_addr': '', - 'dnsv4': [], - 'wins': [], - 'client_ip_pool': {}, - 'mtu': '1436', - 'auth_proto' : ['auth_mschap_v2'], - 'ppp_mppe' : 'prefer', - 'thread_cnt': get_half_cpus() -} def get_config(config=None): if config: conf = config else: conf = Config() - base_path = ['vpn', 'pptp', 'remote-access'] - if not conf.exists(base_path): + base = ['vpn', 'pptp', 'remote-access'] + if not conf.exists(base): return None - pptp = deepcopy(default_pptp) - conf.set_level(base_path) - - if conf.exists(['name-server']): - pptp['dnsv4'] = conf.return_values(['name-server']) - - if conf.exists(['wins-server']): - pptp['wins'] = conf.return_values(['wins-server']) - - if conf.exists(['outside-address']): - pptp['outside_addr'] = conf.return_value(['outside-address']) - - if conf.exists(['authentication', 'mode']): - pptp['auth_mode'] = conf.return_value(['authentication', 'mode']) - - # - # local auth - if conf.exists(['authentication', 'local-users']): - for username in conf.list_nodes(['authentication', 'local-users', 'username']): - user = { - 'name': username, - 'password' : '', - 'state' : 'enabled', - 'ip' : '*', - } - - conf.set_level(base_path + ['authentication', 'local-users', 'username', username]) - - if conf.exists(['password']): - user['password'] = conf.return_value(['password']) - - if conf.exists(['disable']): - user['state'] = 'disable' - - if conf.exists(['static-ip']): - user['ip'] = conf.return_value(['static-ip']) - - if not conf.exists(['disable']): - pptp['local_users'].append(user) - - # - # RADIUS auth and settings - conf.set_level(base_path + ['authentication', 'radius']) - if conf.exists(['server']): - for server in conf.list_nodes(['server']): - radius = { - 'server' : server, - 'key' : '', - 'fail_time' : 0, - 'port' : '1812', - 'acct_port' : '1813' - } - - conf.set_level(base_path + ['authentication', 'radius', 'server', server]) - - if conf.exists(['disable-accounting']): - radius['acct_port'] = '0' - - if conf.exists(['fail-time']): - radius['fail_time'] = conf.return_value(['fail-time']) - - if conf.exists(['port']): - radius['port'] = conf.return_value(['port']) - - if conf.exists(['acct-port']): - radius['acct_port'] = conf.return_value(['acct-port']) - - if conf.exists(['key']): - radius['key'] = conf.return_value(['key']) - - if not conf.exists(['disable']): - pptp['radius_server'].append(radius) - - # - # advanced radius-setting - conf.set_level(base_path + ['authentication', 'radius']) - - if conf.exists(['accounting-interim-interval']): - pptp['radius_acct_interim_interval'] = conf.return_value(['accounting-interim-interval']) - - if conf.exists(['acct-interim-jitter']): - pptp['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter']) - - if conf.exists(['acct-timeout']): - pptp['radius_acct_tmo'] = conf.return_value(['acct-timeout']) - - if conf.exists(['max-try']): - pptp['radius_max_try'] = conf.return_value(['max-try']) - - if conf.exists(['timeout']): - pptp['radius_timeout'] = conf.return_value(['timeout']) - - if conf.exists(['nas-identifier']): - pptp['radius_nas_id'] = conf.return_value(['nas-identifier']) - - if conf.exists(['nas-ip-address']): - pptp['radius_nas_ip'] = conf.return_value(['nas-ip-address']) - - if conf.exists(['source-address']): - pptp['radius_source_address'] = conf.return_value(['source-address']) - - # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA) - if conf.exists(['dae-server']): - dae = { - 'port' : '', - 'server' : '', - 'key' : '' - } - - if conf.exists(['dynamic-author', 'ip-address']): - dae['server'] = conf.return_value(['dynamic-author', 'ip-address']) - - if conf.exists(['dynamic-author', 'port']): - dae['port'] = conf.return_value(['dynamic-author', 'port']) - - if conf.exists(['dynamic-author', 'key']): - dae['key'] = conf.return_value(['dynamic-author', 'key']) - - pptp['radius_dynamic_author'] = dae - - # Rate limit - if conf.exists(['rate-limit', 'attribute']): - pptp['radius_shaper_attr'] = conf.return_value(['rate-limit', 'attribute']) - - if conf.exists(['rate-limit', 'enable']): - pptp['radius_shaper_enable'] = True - - if conf.exists(['rate-limit', 'multiplier']): - pptp['radius_shaper_multiplier'] = conf.return_value(['rate-limit', 'multiplier']) - - if conf.exists(['rate-limit', 'vendor']): - pptp['radius_shaper_vendor'] = conf.return_value(['rate-limit', 'vendor']) - - conf.set_level(base_path) - if conf.exists(['client-ip-pool']): - for pool_name in conf.list_nodes(['client-ip-pool']): - pptp['client_ip_pool'][pool_name] = {} - pptp['client_ip_pool'][pool_name]['range'] = conf.return_value(['client-ip-pool', pool_name, 'range']) - pptp['client_ip_pool'][pool_name]['next_pool'] = conf.return_value(['client-ip-pool', pool_name, 'next-pool']) + # retrieve common dictionary keys + pptp = get_accel_dict(conf, base, pptp_chap_secrets) if dict_search('client_ip_pool', pptp): # Multiple named pools require ordered values T5099 - pptp['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', pptp)) - - if conf.exists(['default-pool']): - pptp['default_pool'] = conf.return_value(['default-pool']) - - if conf.exists(['mtu']): - pptp['mtu'] = conf.return_value(['mtu']) - - # gateway address - if conf.exists(['gateway-address']): - pptp['gateway_address'] = conf.return_value(['gateway-address']) - - if conf.exists(['authentication', 'require']): - # clear default list content, now populate with actual CLI values - pptp['auth_proto'] = [] - auth_mods = { - 'pap': 'auth_pap', - 'chap': 'auth_chap_md5', - 'mschap': 'auth_mschap_v1', - 'mschap-v2': 'auth_mschap_v2' - } - - for proto in conf.return_values(['authentication', 'require']): - pptp['auth_proto'].append(auth_mods[proto]) - - if conf.exists(['authentication', 'mppe']): - pptp['ppp_mppe'] = conf.return_value(['authentication', 'mppe']) - + pptp['ordered_named_pools'] = get_pools_in_order( + dict_search('client_ip_pool', pptp)) + pptp['chap_secrets_file'] = pptp_chap_secrets pptp['server_type'] = 'pptp' return pptp @@ -251,34 +58,45 @@ def get_config(config=None): def verify(pptp): if not pptp: return None + auth_mode = dict_search('authentication.mode', pptp) + if auth_mode == 'local': + if not dict_search('authentication.local_users', pptp): + raise ConfigError( + 'PPTP local auth mode requires local users to be configured!') - if pptp['auth_mode'] == 'local': - if not pptp['local_users']: - raise ConfigError('PPTP local auth mode requires local users to be configured!') - for user in pptp['local_users']: - username = user['name'] - if not user['password']: - raise ConfigError(f'Password required for local user "{username}"') - elif pptp['auth_mode'] == 'radius': - if len(pptp['radius_server']) == 0: - raise ConfigError('RADIUS authentication requires at least one server') - for radius in pptp['radius_server']: - if not radius['key']: - server = radius['server'] - raise ConfigError(f'Missing RADIUS secret key for server "{ server }"') + for user in dict_search('authentication.local_users.username', pptp): + user_config = pptp['authentication']['local_users']['username'][ + user] + if 'password' not in user_config: + raise ConfigError(f'Password required for local user "{user}"') - if pptp['auth_mode'] == 'local' or pptp['auth_mode'] == 'noauth': - if not pptp['client_ip_pool']: + elif auth_mode == 'radius': + if not dict_search('authentication.radius.server', pptp): + raise ConfigError( + 'RADIUS authentication requires at least one server') + for server in dict_search('authentication.radius.server', pptp): + radius_config = pptp['authentication']['radius']['server'][server] + if 'key' not in radius_config: + raise ConfigError( + f'Missing RADIUS secret key for server "{server}"') + + if auth_mode == 'local' or auth_mode == 'noauth': + if not dict_search('client_ip_pool', pptp): raise ConfigError( - "PPTP local auth mode requires local client-ip-pool to be configured!") + 'PPTP local auth mode requires local client-ip-pool ' + 'to be configured!') verify_accel_ppp_ip_pool(pptp) - if len(pptp['dnsv4']) > 2: - raise ConfigError('Not more then two IPv4 DNS name-servers can be configured') + if 'name_server' in pptp: + if len(pptp['name_server']) > 2: + raise ConfigError( + 'Not more then two IPv4 DNS name-servers can be configured' + ) - if len(pptp['wins']) > 2: - raise ConfigError('Not more then two IPv4 WINS name-servers can be configured') + if 'wins_server' in pptp and len(pptp['wins_server']) > 2: + raise ConfigError( + 'Not more then two WINS name-servers can be configured') def generate(pptp): @@ -287,13 +105,11 @@ def generate(pptp): render(pptp_conf, 'accel-ppp/pptp.config.j2', pptp) - if pptp['local_users']: - render(pptp_chap_secrets, 'accel-ppp/chap-secrets.j2', pptp) - os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) - else: - if os.path.exists(pptp_chap_secrets): - os.unlink(pptp_chap_secrets) + if dict_search('authentication.mode', pptp) == 'local': + render(pptp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2', + pptp, permission=0o640) + return None def apply(pptp): if not pptp: @@ -306,6 +122,7 @@ def apply(pptp): call('systemctl restart accel-ppp@pptp.service') + if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index 37625142c..9b1b6355f 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -214,6 +214,18 @@ def apply(vrf): # Delete the VRF Kernel interface call(f'ip link delete dev {tmp}') + # Enable/Disable VRF strict mode + # When net.vrf.strict_mode=0 (default) it is possible to associate multiple + # VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a + # table can be associated to a single VRF device. + # + # A VRF table can be used by the VyOS CLI only once (ensured by verify()), + # this simply adds an additional Kernel safety net + strict_mode = '0' + # Set to 1 if any VRF is defined + if 'name' in vrf: strict_mode = '1' + sysctl_write('net.vrf.strict_mode', strict_mode) + if 'name' in vrf: # Separate VRFs in conntrack table # check if table already exists diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf index 67d96969e..6291be5f0 100644 --- a/src/etc/sysctl.d/30-vyos-router.conf +++ b/src/etc/sysctl.d/30-vyos-router.conf @@ -105,11 +105,6 @@ net.core.rps_sock_flow_entries = 32768 net.core.default_qdisc=fq_codel net.ipv4.tcp_congestion_control=bbr -# VRF - Virtual routing and forwarding -# When net.vrf.strict_mode=0 (default) it is possible to associate multiple -# VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a -# table can be associated to a single VRF device. -# -# A VRF table can be used by the VyOS CLI only once (ensured by verify()), -# this simply adds an additional Kernel safety net -net.vrf.strict_mode=1 +# Disable IPv6 Segment Routing packets by default +net.ipv6.conf.all.seg6_enabled = 0 +net.ipv6.conf.default.seg6_enabled = 0 diff --git a/src/migration-scripts/dhcp-server/7-to-8 b/src/migration-scripts/dhcp-server/7-to-8 new file mode 100755 index 000000000..151aa6d7b --- /dev/null +++ b/src/migration-scripts/dhcp-server/7-to-8 @@ -0,0 +1,65 @@ +#!/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/>. + +# T3316: +# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore) +# - Rename "service dhcp-server shared-network-name ... static-mapping <hostname> mac-address ..." +# to "service dhcp-server shared-network-name ... static-mapping <hostname> mac ..." + +import sys +import re +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['service', 'dhcp-server', 'shared-network-name'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + sys.exit(0) + +for network in config.list_nodes(base): + # Run this for every specified 'subnet' + if config.exists(base + [network, 'subnet']): + for subnet in config.list_nodes(base + [network, 'subnet']): + base_subnet = base + [network, 'subnet', subnet] + if config.exists(base_subnet + ['static-mapping']): + for hostname in config.list_nodes(base_subnet + ['static-mapping']): + base_mapping = base_subnet + ['static-mapping', hostname] + + # Rename the 'mac-address' node to 'mac' + if config.exists(base_mapping + ['mac-address']): + config.rename(base_mapping + ['mac-address'], 'mac') + + # Adjust hostname to have valid FQDN characters only + new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname) + if new_hostname != hostname: + config.rename(base_mapping, new_hostname) + +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) diff --git a/src/migration-scripts/dhcpv6-server/2-to-3 b/src/migration-scripts/dhcpv6-server/2-to-3 new file mode 100755 index 000000000..f4bdc1d1e --- /dev/null +++ b/src/migration-scripts/dhcpv6-server/2-to-3 @@ -0,0 +1,78 @@ +#!/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/>. + +# T3316: +# - Adjust hostname to have valid FQDN characters only (underscores aren't allowed anymore) +# - Adjust duid (old identifier) to comply with duid format +# - Rename "service dhcpv6-server shared-network-name ... static-mapping <hostname> identifier ..." +# to "service dhcpv6-server shared-network-name ... static-mapping <hostname> duid ..." +# - Rename "service dhcpv6-server shared-network-name ... static-mapping <hostname> mac-address ..." +# to "service dhcpv6-server shared-network-name ... static-mapping <hostname> mac ..." + +import sys +import re +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['service', 'dhcpv6-server', 'shared-network-name'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + sys.exit(0) + +for network in config.list_nodes(base): + # Run this for every specified 'subnet' + if config.exists(base + [network, 'subnet']): + for subnet in config.list_nodes(base + [network, 'subnet']): + base_subnet = base + [network, 'subnet', subnet] + if config.exists(base_subnet + ['static-mapping']): + for hostname in config.list_nodes(base_subnet + ['static-mapping']): + base_mapping = base_subnet + ['static-mapping', hostname] + if config.exists(base_mapping + ['identifier']): + + # Adjust duid to comply with duid format (a:3:b:04:... => 0a:03:0b:04:...) + duid = config.return_value(base_mapping + ['identifier']) + new_duid = ':'.join(x.rjust(2,'0') for x in duid.split(':')) + if new_duid != duid: + config.set(base_mapping + ['identifier'], new_duid) + + # Rename the 'identifier' node to 'duid' + config.rename(base_mapping + ['identifier'], 'duid') + + # Rename the 'mac-address' node to 'mac' + if config.exists(base_mapping + ['mac-address']): + config.rename(base_mapping + ['mac-address'], 'mac') + + # Adjust hostname to have valid FQDN characters only + new_hostname = re.sub(r'[^a-zA-Z0-9-.]', '-', hostname) + if new_hostname != hostname: + config.rename(base_mapping, new_hostname) + +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) diff --git a/src/migration-scripts/dns-dynamic/0-to-1 b/src/migration-scripts/dns-dynamic/0-to-1 index 4f6083eab..b7674a9c8 100755 --- a/src/migration-scripts/dns-dynamic/0-to-1 +++ b/src/migration-scripts/dns-dynamic/0-to-1 @@ -25,8 +25,10 @@ # to "service dns dynamic address <address> service <config> username ..." # - apply global 'ipv6-enable' to per <config> 'ip-version: ipv6' # - apply service protocol mapping upfront, they are not 'auto-detected' anymore +# - migrate web-options url to stricter format import sys +import re from vyos.configtree import ConfigTree service_protocol_mapping = { @@ -104,8 +106,17 @@ for address in config.list_nodes(new_base_path): new_base_path + ['web', svc_type, f'{svc_cfg}-{address}']) # Multiple web-options were not supported, so copy only the first one + # Also, migrate web-options url to stricter format and transition + # checkip.dyndns.org to https://domains.google.com/checkip for better + # TLS support (see: https://github.com/ddclient/ddclient/issues/597) if not config.exists(new_base_path + ['web', 'web-options']): config.copy(new_base_path + [address, 'use-web'], new_base_path + ['web', 'web-options']) + if config.exists(new_base_path + ['web', 'web-options', 'url']): + url = config.return_value(new_base_path + ['web', 'web-options', 'url']) + if re.search("^(https?://)?checkip\.dyndns\.org", url): + config.set(new_base_path + ['web', 'web-options', 'url'], 'https://domains.google.com/checkip') + if not url.startswith(('http://', 'https://')): + config.set(new_base_path + ['web', 'web-options', 'url'], f'https://{url}') config.delete(new_base_path + [address]) diff --git a/src/migration-scripts/dns-dynamic/2-to-3 b/src/migration-scripts/dns-dynamic/2-to-3 index e5910f7b4..4e0aa37d5 100755 --- a/src/migration-scripts/dns-dynamic/2-to-3 +++ b/src/migration-scripts/dns-dynamic/2-to-3 @@ -37,7 +37,7 @@ def normalize_name(name): # Normalize unicode characters to ASCII (NFKD) # Replace all separators with hypens, strip leading and trailing hyphens name = normalize('NFKD', name).encode('ascii', 'ignore').decode() - name = re.sub(r'(\s|\W)+', '-', name).strip('-') + name = re.sub(r'(\s|_|\W)+', '-', name).strip('-') return name diff --git a/src/migration-scripts/firewall/13-to-14 b/src/migration-scripts/firewall/13-to-14 new file mode 100755 index 000000000..f45ff0674 --- /dev/null +++ b/src/migration-scripts/firewall/13-to-14 @@ -0,0 +1,59 @@ +#!/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/>. + +# T5834: Rename 'enable-default-log' to 'default-log' +# From + # set firewall ... filter enable-default-log + # set firewall ... name <name> enable-default-log +# To + # set firewall ... filter default-log + # set firewall ... name <name> default-log + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if len(argv) < 2: + 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) + +for family in ['ipv4', 'ipv6', 'bridge']: + if config.exists(base + [family]): + for hook in ['forward', 'input', 'output', 'name']: + if config.exists(base + [family, hook]): + for priority in config.list_nodes(base + [family, hook]): + if config.exists(base + [family, hook, priority, 'enable-default-log']): + config.rename(base + [family, hook, priority, 'enable-default-log'], 'default-log') + +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) diff --git a/src/migration-scripts/lldp/0-to-1 b/src/migration-scripts/lldp/0-to-1 index a936cbdfc..a99356062 100755 --- a/src/migration-scripts/lldp/0-to-1 +++ b/src/migration-scripts/lldp/0-to-1 @@ -1,4 +1,18 @@ #!/usr/bin/env python3 +# +# Copyright (C) 2020 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/>. # Delete "set service lldp interface <interface> location civic-based" option # as it was broken most of the time anyways diff --git a/src/migration-scripts/lldp/1-to-2 b/src/migration-scripts/lldp/1-to-2 new file mode 100755 index 000000000..35efb25db --- /dev/null +++ b/src/migration-scripts/lldp/1-to-2 @@ -0,0 +1,48 @@ +#!/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/>. + +# T5855: migrate "set service lldp snmp enable" -> `set service lldp snmp" + +import sys + +from vyos.configtree import ConfigTree + +if len(sys.argv) < 2: + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +base = ['service', 'lldp'] +if not config.exists(base): + # Nothing to do + sys.exit(0) + +if config.exists(base + ['snmp']): + enabled = config.exists(base + ['snmp', 'enable']) + config.delete(base + ['snmp']) + if enabled: config.set(base + ['snmp']) + +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)) + sys.exit(1) diff --git a/src/migration-scripts/nat66/2-to-3 b/src/migration-scripts/nat66/2-to-3 new file mode 100755 index 000000000..f34f170b3 --- /dev/null +++ b/src/migration-scripts/nat66/2-to-3 @@ -0,0 +1,61 @@ +#!/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/>. + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if len(argv) < 2: + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['nat66', 'source'] +new_base = ['service', 'ndp-proxy', 'interface'] + +config = ConfigTree(config_file) +if not config.exists(base): + # Nothing to do + exit(0) + +for rule in config.list_nodes(base + ['rule']): + base_rule = base + ['rule', rule] + + interface = None + if config.exists(base_rule + ['outbound-interface', 'name']): + interface = config.return_value(base_rule + ['outbound-interface', 'name']) + else: + continue + + prefix_base = base_rule + ['source', 'prefix'] + if config.exists(prefix_base): + prefix = config.return_value(prefix_base) + config.set(new_base + [interface, 'prefix', prefix, 'mode'], value='static') + config.set_tag(new_base) + config.set_tag(new_base + [interface, 'prefix']) + + if config.exists(base_rule + ['disable']): + config.set(new_base + [interface, 'prefix', prefix, 'disable']) + +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) diff --git a/src/migration-scripts/policy/7-to-8 b/src/migration-scripts/policy/7-to-8 new file mode 100755 index 000000000..73eece1a6 --- /dev/null +++ b/src/migration-scripts/policy/7-to-8 @@ -0,0 +1,56 @@ +#!/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/>. + +# T5834: Rename 'enable-default-log' to 'default-log' +# From + # set policy [route | route 6] <route> enable-default-log +# To + # set policy [route | route 6] <route> default-log + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if len(argv) < 2: + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['policy'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for family in ['route', 'route6']: + if config.exists(base + [family]): + + for policy_name in config.list_nodes(base + [family]): + if config.exists(base + [family, policy_name, 'enable-default-log']): + config.rename(base + [family, policy_name, 'enable-default-log'], 'default-log') + +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) diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index 6a8797aec..529b388c8 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -441,10 +441,8 @@ def validate_signature(file_path: str, sign_type: str) -> None: signature_valid: bool = False # validate with minisig if sign_type == 'minisig': - for pubkey in [ - '/usr/share/vyos/keys/vyos-release.minisign.pub', - '/usr/share/vyos/keys/vyos-backup.minisign.pub' - ]: + pub_key_list = glob('/usr/share/vyos/keys/*.minisign.pub') + for pubkey in pub_key_list: if run(f'minisign -V -q -p {pubkey} -m {file_path} -x {file_path}.minisig' ) == 0: signature_valid = True diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server index bfd50cc80..b64e58132 100755 --- a/src/services/vyos-http-api-server +++ b/src/services/vyos-http-api-server @@ -872,13 +872,15 @@ def initialization(session: ConfigSession, app: FastAPI = app): global server try: server_config = load_server_config() - keys = flatten_keys(server_config) except Exception as e: logger.critical(f'Failed to load the HTTP API server config: {e}') sys.exit(1) app.state.vyos_session = session - app.state.vyos_keys = keys + app.state.vyos_keys = [] + + if 'keys' in server_config: + app.state.vyos_keys = flatten_keys(server_config) app.state.vyos_debug = bool('debug' in server_config) app.state.vyos_strict = bool('strict' in server_config) diff --git a/src/validators/ipv6-duid b/src/validators/ipv6-duid deleted file mode 100755 index fd4728e50..000000000 --- a/src/validators/ipv6-duid +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021 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/>. - -import re -import sys - -pattern = "^([0-9A-Fa-f]{2}:){,127}([0-9A-Fa-f]{2})$" - -if __name__ == '__main__': - if len(sys.argv) != 2: - sys.exit(1) - if not re.match(pattern, sys.argv[1]): - sys.exit(1) - sys.exit(0) |