diff --git a/data/templates/accel-ppp/l2tp.config.j2 b/data/templates/accel-ppp/l2tp.config.j2 index f041e278e..203a9772e 100644 --- a/data/templates/accel-ppp/l2tp.config.j2 +++ b/data/templates/accel-ppp/l2tp.config.j2 @@ -1,102 +1,79 @@ ### generated by accel_l2tp.py ### [modules] log_syslog l2tp shaper {# Common authentication backend definitions #} {% include 'accel-ppp/config_modules_auth_mode.j2' %} ippool {# Common IPv6 definitions #} {% include 'accel-ppp/config_modules_ipv6.j2' %} {# Common authentication protocols (pap, chap ...) #} {% include 'accel-ppp/config_modules_auth_protocols.j2' %} [core] thread-count={{ thread_count }} [common] {% if max_concurrent_sessions is vyos_defined %} max-starting={{ max_concurrent_sessions }} {% endif %} [log] syslog=accel-l2tp,daemon copy=1 level=5 {# Common DNS name-server definition #} {% include 'accel-ppp/config_name_server.j2' %} {% if wins_server is vyos_defined %} [wins] {% for server in wins_server %} wins{{ loop.index }}={{ server }} {% endfor %} {% endif %} [l2tp] verbose=1 ifname=l2tp%d ppp-max-mtu={{ mtu }} mppe={{ ppp_options.mppe }} {% if outside_address is vyos_defined %} bind={{ outside_address }} {% endif %} {% if lns.shared_secret is vyos_defined %} secret={{ lns.shared_secret }} {% endif %} {% if lns.host_name is vyos_defined %} host-name={{ lns.host_name }} {% endif %} {% if default_pool is vyos_defined %} ip-pool={{ default_pool }} {% endif %} {% if default_ipv6_pool is vyos_defined %} ipv6-pool={{ default_ipv6_pool }} ipv6-pool-delegate={{ default_ipv6_pool }} {% endif %} [client-ip-range] 0.0.0.0/0 {# Common IP pool definitions #} {% include 'accel-ppp/config_ip_pool.j2' %} {# Common chap-secrets and RADIUS server/option definitions #} {% include 'accel-ppp/config_chap_secrets_radius.j2' %} -[ppp] -verbose=1 -check-ip=1 -single-session=replace -lcp-echo-interval={{ ppp_options.lcp_echo_interval }} -lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }} -lcp-echo-failure={{ ppp_options.lcp_echo_failure }} -{# MTU #} -mtu={{ mtu }} -ipv6={{ 'allow' if ppp_options.ipv6 is vyos_defined("deny") and client_ipv6_pool is vyos_defined else ppp_options.ipv6 }} -ipv4={{ ppp_options.ipv4 }} -mppe={{ ppp_options.mppe }} -{% if ccp_disable is vyos_defined %} -ccp=0 -{% endif %} -unit-preallocate={{ "1" if authentication.radius.preallocate_vif is vyos_defined else "0" }} - -{% if ppp_options.ipv6_intf_id is vyos_defined %} -ipv6-intf-id={{ ppp_options.ipv6_intf_id }} -{% endif %} -{% if ppp_options.ipv6_peer_intf_id is vyos_defined %} -ipv6-peer-intf-id={{ ppp_options.ipv6_peer_intf_id }} -{% endif %} -ipv6-accept-peer-intf-id={{ "1" if ppp_options.ipv6_accept_peer_intf_id is vyos_defined else "0" }} +{# Common ppp-options definitions #} +{% include 'accel-ppp/ppp-options.j2' %} {# Common IPv6 pool definitions #} {% include 'accel-ppp/config_ipv6_pool.j2' %} {# Common RADIUS shaper configuration #} {% include 'accel-ppp/config_shaper_radius.j2' %} [cli] tcp=127.0.0.1:2004 -sessions-columns=ifname,username,calling-sid,ip,{{ ip6_column | join(',') }}{{ ',' if ip6_column }}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime diff --git a/data/templates/accel-ppp/ppp-options.j2 b/data/templates/accel-ppp/ppp-options.j2 new file mode 100644 index 000000000..f2d2519d9 --- /dev/null +++ b/data/templates/accel-ppp/ppp-options.j2 @@ -0,0 +1,39 @@ +#ppp options +[ppp] +verbose=1 +check-ip=1 +ccp={{ "0" if ppp_options.disable_ccp is vyos_defined else "1" }} +unit-preallocate={{ "1" if authentication.radius.preallocate_vif is vyos_defined else "0" }} +{% if ppp_options.min_mtu is vyos_defined %} +min-mtu={{ ppp_options.min_mtu }} +{% endif %} +{% if ppp_options.mru is vyos_defined %} +mru={{ ppp_options.mru }} +{% endif %} +mppe={{ ppp_options.mppe }} +lcp-echo-interval={{ ppp_options.lcp_echo_interval }} +lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }} +lcp-echo-failure={{ ppp_options.lcp_echo_failure }} +{% if ppp_options.ipv4 is vyos_defined %} +ipv4={{ ppp_options.ipv4 }} +{% endif %} +{# IPv6 #} +{% if ppp_options.ipv6 is vyos_defined %} +ipv6={{ ppp_options.ipv6 }} +{% if ppp_options.ipv6_interface_id is vyos_defined %} +ipv6-intf-id={{ ppp_options.ipv6_interface_id }} +{% endif %} +{% if ppp_options.ipv6_peer_interface_id is vyos_defined %} +{% if ppp_options.ipv6_peer_interface_id == 'ipv4-addr' %} +ipv6-peer-intf-id=ipv4 +{% else %} +ipv6-peer-intf-id={{ ppp_options.ipv6_peer_interface_id }} +{% endif %} +{% endif %} +ipv6-accept-peer-intf-id={{ "1" if ppp_options.ipv6_accept_peer_interface_id is vyos_defined else "0" }} +{% endif %} +{# MTU #} +mtu={{ mtu }} +{% if ppp_options.interface_cache is vyos_defined %} +unit-cache={{ ppp_options.interface_cache }} +{% endif %} diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2 index fb8a11366..bf7b2eb72 100644 --- a/data/templates/accel-ppp/pppoe.config.j2 +++ b/data/templates/accel-ppp/pppoe.config.j2 @@ -1,178 +1,146 @@ ### generated by accel_pppoe.py ### [modules] log_syslog pppoe shaper {# Common authentication backend definitions #} {% include 'accel-ppp/config_modules_auth_mode.j2' %} ippool {# Common IPv6 definitions #} {% include 'accel-ppp/config_modules_ipv6.j2' %} {# Common authentication protocols (pap, chap ...) #} {% include 'accel-ppp/config_modules_auth_protocols.j2' %} {% if snmp is vyos_defined %} net-snmp {% endif %} {% if limits is vyos_defined %} connlimit {% endif %} {% if extended_scripts is vyos_defined %} sigchld pppd_compat {% endif %} [core] thread-count={{ thread_count }} [log] syslog=accel-pppoe,daemon copy=1 level=5 {% if authentication.mode is vyos_defined("noauth") %} [auth] noauth=1 {% endif %} {% if snmp.master_agent is vyos_defined %} [snmp] master=1 {% endif %} [client-ip-range] disable {# Common IP pool definitions #} {% include 'accel-ppp/config_ip_pool.j2' %} {# Common IPv6 pool definitions #} {% include 'accel-ppp/config_ipv6_pool.j2' %} {# Common DNS name-server definition #} {% include 'accel-ppp/config_name_server.j2' %} {% if wins_server is vyos_defined %} [wins] {% for server in wins_server %} wins{{ loop.index }}={{ server }} {% endfor %} {% endif %} {# Common chap-secrets and RADIUS server/option definitions #} {% include 'accel-ppp/config_chap_secrets_radius.j2' %} [common] {% if session_control is vyos_defined and session_control is not vyos_defined('disable') %} single-session={{ session_control }} {% endif %} {% if max_concurrent_sessions is vyos_defined %} max-starting={{ max_concurrent_sessions }} {% endif %} -[ppp] -verbose=1 -check-ip=1 -ccp={{ "1" if ppp_options.ccp is vyos_defined else "0" }} -unit-preallocate={{ "1" if authentication.radius.preallocate_vif is vyos_defined else "0" }} -{% if ppp_options.min_mtu is vyos_defined %} -min-mtu={{ ppp_options.min_mtu }} -{% endif %} -{% if ppp_options.mru is vyos_defined %} -mru={{ ppp_options.mru }} -{% endif %} -mppe={{ ppp_options.mppe }} -lcp-echo-interval={{ ppp_options.lcp_echo_interval }} -lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }} -lcp-echo-failure={{ ppp_options.lcp_echo_failure }} -{% if ppp_options.ipv4 is vyos_defined %} -ipv4={{ ppp_options.ipv4 }} -{% endif %} -{# IPv6 #} -{% if ppp_options.ipv6 is vyos_defined %} -ipv6={{ ppp_options.ipv6 }} -{% if ppp_options.ipv6_intf_id is vyos_defined %} -ipv6-intf-id={{ ppp_options.ipv6_intf_id }} -{% endif %} -{% if ppp_options.ipv6_peer_intf_id is vyos_defined %} -ipv6-peer-intf-id={{ ppp_options.ipv6_peer_intf_id }} -{% endif %} -ipv6-accept-peer-intf-id={{ "1" if ppp_options.ipv6_accept_peer_intf_id is vyos_defined else "0" }} -{% endif %} -{# MTU #} -mtu={{ mtu }} -{% if ppp_options.interface_cache is vyos_defined %} -unit-cache={{ ppp_options.interface_cache }} -{% endif %} +{# Common ppp-options definitions #} +{% include 'accel-ppp/ppp-options.j2' %} [pppoe] verbose=1 ac-name={{ access_concentrator }} {% if interface is vyos_defined %} {% for iface, iface_config in interface.items() %} {% if iface_config.vlan is not vyos_defined %} interface={{ iface }} {% else %} {% for vlan in iface_config.vlan %} interface=re:^{{ iface }}\.{{ vlan | range_to_regex }}$ {% endfor %} vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }} {% endif %} {% endfor %} {% endif %} {% if service_name %} service-name={{ service_name | join(',') }} {% endif %} {% if pado_delay %} {% set pado_delay_param = namespace(value='0') %} {% for delay in pado_delay | sort(attribute='0') %} {% if not loop.last %} {% set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + pado_delay[delay].sessions %} {% else %} {% set pado_delay_param.value = pado_delay_param.value + ',-1:' + pado_delay[delay].sessions %} {% endif %} {% endfor %} pado-delay={{ pado_delay_param.value }} {% endif %} {% if authentication.radius.called_sid_format is vyos_defined %} called-sid={{ authentication.radius.called_sid_format }} {% endif %} {% if authentication.mode is vyos_defined("noauth") %} noauth=1 {% endif %} {% if default_pool is vyos_defined %} ip-pool={{ default_pool }} {% endif %} {% if default_ipv6_pool is vyos_defined %} ipv6-pool={{ default_ipv6_pool }} ipv6-pool-delegate={{ default_ipv6_pool }} {% endif %} {% if limits is vyos_defined %} [connlimit] {% if limits.connection_limit is vyos_defined %} limit={{ limits.connection_limit }} {% endif %} {% if limits.burst is vyos_defined %} burst={{ limits.burst }} {% endif %} {% if limits.timeout is vyos_defined %} timeout={{ limits.timeout }} {% endif %} {% endif %} {# Common RADIUS shaper configuration #} {% include 'accel-ppp/config_shaper_radius.j2' %} {% if extended_scripts is vyos_defined %} [pppd-compat] verbose=1 radattr-prefix=/run/accel-pppd/radattr {% set script_name = {'on_up': 'ip-up', 'on_down': 'ip-down', 'on_change':'ip-change', 'on_pre_up':'ip-pre-up'} %} {% for script in extended_scripts %} {{ script_name[script] }}={{ extended_scripts[script] }} {% endfor %} {% endif %} [cli] tcp=127.0.0.1:2001 diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2 index daafd6e92..7fe4b17bf 100644 --- a/data/templates/accel-ppp/pptp.config.j2 +++ b/data/templates/accel-ppp/pptp.config.j2 @@ -1,77 +1,83 @@ ### generated by accel_pptp.py ### [modules] log_syslog pptp shaper {# Common authentication backend definitions #} {% include 'accel-ppp/config_modules_auth_mode.j2' %} ippool +{# Common IPv6 definitions #} +{% include 'accel-ppp/config_modules_ipv6.j2' %} {# 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_count }} [common] {% if max_concurrent_sessions is vyos_defined %} max-starting={{ max_concurrent_sessions }} {% endif %} [log] syslog=accel-pptp,daemon copy=1 level=5 {# Common DNS name-server definition #} {% include 'accel-ppp/config_name_server.j2' %} {% if wins_server is vyos_defined %} [wins] {% for server in wins_server %} wins{{ loop.index }}={{ server }} {% endfor %} {% endif %} - [pptp] ifname=pptp%d {% if outside_address is vyos_defined %} bind={{ outside_address }} {% endif %} verbose=1 ppp-max-mtu={{ mtu }} mppe={{ authentication.mppe }} echo-interval=10 echo-failure=3 {% if default_pool is vyos_defined %} ip-pool={{ default_pool }} {% endif %} +{% if default_ipv6_pool is vyos_defined %} +ipv6-pool={{ default_ipv6_pool }} +ipv6-pool-delegate={{ default_ipv6_pool }} +{% endif %} [client-ip-range] 0.0.0.0/0 {# Common IP pool definitions #} {% include 'accel-ppp/config_ip_pool.j2' %} -[ppp] -verbose=5 -check-ip=1 -single-session=replace +{# Common IPv6 pool definitions #} +{% include 'accel-ppp/config_ipv6_pool.j2' %} + +{# Common ppp-options definitions #} +{% include 'accel-ppp/ppp-options.j2' %} {# Common chap-secrets and RADIUS server/option definitions #} {% include 'accel-ppp/config_chap_secrets_radius.j2' %} {# Common RADIUS shaper configuration #} {% include 'accel-ppp/config_shaper_radius.j2' %} [cli] tcp=127.0.0.1:2003 diff --git a/data/templates/accel-ppp/sstp.config.j2 b/data/templates/accel-ppp/sstp.config.j2 index 51f7dfca8..c0bc62d9f 100644 --- a/data/templates/accel-ppp/sstp.config.j2 +++ b/data/templates/accel-ppp/sstp.config.j2 @@ -1,76 +1,66 @@ ### generated by vpn_sstp.py ### [modules] log_syslog sstp shaper {# Common authentication backend definitions #} {% include 'accel-ppp/config_modules_auth_mode.j2' %} ippool {# Common IPv6 definitions #} {% include 'accel-ppp/config_modules_ipv6.j2' %} {# Common authentication protocols (pap, chap ...) #} {% include 'accel-ppp/config_modules_auth_protocols.j2' %} [core] thread-count={{ thread_count }} [common] single-session=replace {% if max_concurrent_sessions is vyos_defined %} max-starting={{ max_concurrent_sessions }} {% endif %} [log] syslog=accel-sstp,daemon copy=1 level=5 [client-ip-range] disable [sstp] verbose=1 ifname=sstp%d port={{ port }} accept=ssl ssl-ca-file=/run/accel-pppd/sstp-ca.pem ssl-pemfile=/run/accel-pppd/sstp-cert.pem ssl-keyfile=/run/accel-pppd/sstp-cert.key {% if default_pool is vyos_defined %} ip-pool={{ default_pool }} {% endif %} {% if default_ipv6_pool is vyos_defined %} ipv6-pool={{ default_ipv6_pool }} ipv6-pool-delegate={{ default_ipv6_pool }} {% endif %} {# Common IP pool definitions #} {% include 'accel-ppp/config_ip_pool.j2' %} {# Common IPv6 pool definitions #} {% include 'accel-ppp/config_ipv6_pool.j2' %} {# Common DNS name-server definition #} {% include 'accel-ppp/config_name_server.j2' %} {# Common chap-secrets and RADIUS server/option definitions #} {% include 'accel-ppp/config_chap_secrets_radius.j2' %} -[ppp] -verbose=1 -check-ip=1 -{# MTU #} -mtu={{ mtu }} -unit-preallocate={{ "1" if authentication.radius.preallocate_vif is vyos_defined else "0" }} -ipv6={{ 'allow' if ppp_options.ipv6 is vyos_defined("deny") and client_ipv6_pool is vyos_defined else ppp_options.ipv6 }} -ipv4={{ ppp_options.ipv4 }} -mppe={{ ppp_options.mppe }} -lcp-echo-interval={{ ppp_options.lcp_echo_interval }} -lcp-echo-timeout={{ ppp_options.lcp_echo_timeout }} -lcp-echo-failure={{ ppp_options.lcp_echo_failure }} +{# Common ppp-options definitions #} +{% include 'accel-ppp/ppp-options.j2' %} {# Common RADIUS shaper configuration #} {% include 'accel-ppp/config_shaper_radius.j2' %} [cli] tcp=127.0.0.1:2005 diff --git a/interface-definitions/include/accel-ppp/ppp-options-ipv6-interface-id.xml.i b/interface-definitions/include/accel-ppp/ppp-options-ipv6-interface-id.xml.i index 265f7f97c..c4cf0a458 100644 --- a/interface-definitions/include/accel-ppp/ppp-options-ipv6-interface-id.xml.i +++ b/interface-definitions/include/accel-ppp/ppp-options-ipv6-interface-id.xml.i @@ -1,54 +1,54 @@ <!-- include start from accel-ppp/ppp-options-ipv6-interface-id.xml.i --> -<leafNode name="ipv6-intf-id"> +<leafNode name="ipv6-interface-id"> <properties> <help>Fixed or random interface identifier for IPv6</help> <completionHelp> <list>random</list> </completionHelp> <valueHelp> <format>random</format> <description>Random interface identifier for IPv6</description> </valueHelp> <valueHelp> <format>x:x:x:x</format> <description>specify interface identifier for IPv6</description> </valueHelp> <constraint> <regex>(random|((\d+){1,4}:){3}(\d+){1,4})</regex> </constraint> </properties> </leafNode> -<leafNode name="ipv6-peer-intf-id"> +<leafNode name="ipv6-peer-interface-id"> <properties> <help>Peer interface identifier for IPv6</help> <completionHelp> - <list>random calling-sid ipv4</list> + <list>random calling-sid ipv4-addr</list> </completionHelp> <valueHelp> <format>x:x:x:x</format> <description>Interface identifier for IPv6</description> </valueHelp> <valueHelp> <format>random</format> <description>Use a random interface identifier for IPv6</description> </valueHelp> <valueHelp> - <format>ipv4</format> + <format>ipv4-addr</format> <description>Calculate interface identifier from IPv4 address, for example 192:168:0:1</description> </valueHelp> <valueHelp> <format>calling-sid</format> <description>Calculate interface identifier from calling-station-id</description> </valueHelp> <constraint> - <regex>(random|calling-sid|ipv4|((\d+){1,4}:){3}(\d+){1,4})</regex> + <regex>(random|calling-sid|ipv4-addr|((\d+){1,4}:){3}(\d+){1,4})</regex> </constraint> </properties> </leafNode> -<leafNode name="ipv6-accept-peer-intf-id"> +<leafNode name="ipv6-accept-peer-interface-id"> <properties> <help>Accept peer interface identifier</help> <valueless/> </properties> </leafNode> <!-- include end --> diff --git a/interface-definitions/include/accel-ppp/ppp-options.xml.i b/interface-definitions/include/accel-ppp/ppp-options.xml.i new file mode 100644 index 000000000..9b4f1d0ca --- /dev/null +++ b/interface-definitions/include/accel-ppp/ppp-options.xml.i @@ -0,0 +1,65 @@ +<!-- include start from accel-ppp/ppp-options.xml.i --> +<node name="ppp-options"> + <properties> + <help>Advanced protocol options</help> + </properties> + <children> + <leafNode name="min-mtu"> + <properties> + <help>Minimum acceptable MTU (68-65535)</help> + <constraint> + <validator name="numeric" argument="--range 68-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="mru"> + <properties> + <help>Preferred MRU (68-65535)</help> + <constraint> + <validator name="numeric" argument="--range 68-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="disable-ccp"> + <properties> + <help>Disable Compression Control Protocol (CCP)</help> + <valueless /> + </properties> + </leafNode> + #include <include/accel-ppp/ppp-mppe.xml.i> + #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> + #include <include/accel-ppp/lcp-echo-timeout.xml.i> + #include <include/accel-ppp/ppp-interface-cache.xml.i> + <leafNode name="ipv4"> + <properties> + <help>IPv4 (IPCP) negotiation algorithm</help> + <constraint> + <regex>(deny|allow|prefer|require)</regex> + </constraint> + <constraintErrorMessage>invalid value</constraintErrorMessage> + <valueHelp> + <format>deny</format> + <description>Do not negotiate IPv4</description> + </valueHelp> + <valueHelp> + <format>allow</format> + <description>Negotiate IPv4 only if client requests</description> + </valueHelp> + <valueHelp> + <format>prefer</format> + <description>Ask client for IPv4 negotiation, do not fail if it rejects</description> + </valueHelp> + <valueHelp> + <format>require</format> + <description>Require IPv4 negotiation</description> + </valueHelp> + <completionHelp> + <list>deny allow prefer require</list> + </completionHelp> + </properties> + </leafNode> + #include <include/accel-ppp/ppp-options-ipv6.xml.i> + #include <include/accel-ppp/ppp-options-ipv6-interface-id.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/version/l2tp-version.xml.i b/interface-definitions/include/version/l2tp-version.xml.i index 793cd5d0c..01004c5a0 100644 --- a/interface-definitions/include/version/l2tp-version.xml.i +++ b/interface-definitions/include/version/l2tp-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/l2tp-version.xml.i --> -<syntaxVersion component='l2tp' version='7'></syntaxVersion> +<syntaxVersion component='l2tp' version='8'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i index 02f98cc16..c253c58d9 100644 --- a/interface-definitions/include/version/pppoe-server-version.xml.i +++ b/interface-definitions/include/version/pppoe-server-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/pppoe-server-version.xml.i --> -<syntaxVersion component='pppoe-server' version='8'></syntaxVersion> +<syntaxVersion component='pppoe-server' version='9'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/pptp-version.xml.i b/interface-definitions/include/version/pptp-version.xml.i index 4386cedbd..3e1482ecc 100644 --- a/interface-definitions/include/version/pptp-version.xml.i +++ b/interface-definitions/include/version/pptp-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/pptp-version.xml.i --> -<syntaxVersion component='pptp' version='3'></syntaxVersion> +<syntaxVersion component='pptp' version='4'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 6fdc2a65a..477ed115f 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -1,282 +1,226 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="service"> <children> <node name="pppoe-server" owner="${vyos_conf_scripts_dir}/service_pppoe-server.py"> <properties> <help>Point to Point over Ethernet (PPPoE) Server</help> <priority>900</priority> </properties> <children> #include <include/pppoe-access-concentrator.xml.i> <leafNode name="access-concentrator"> <defaultValue>vyos-ac</defaultValue> </leafNode> <node name="authentication"> <properties> <help>Authentication for remote access PPPoE Server</help> </properties> <children> #include <include/accel-ppp/auth-local-users.xml.i> #include <include/accel-ppp/auth-mode.xml.i> #include <include/accel-ppp/auth-protocols.xml.i> #include <include/radius-auth-server-ipv4.xml.i> #include <include/accel-ppp/radius-additions.xml.i> <node name="radius"> <children> #include <include/accel-ppp/radius-additions-rate-limit.xml.i> <leafNode name="called-sid-format"> <properties> <help>Format of Called-Station-Id attribute</help> <completionHelp> <list>ifname ifname:mac</list> </completionHelp> <constraint> <regex>(ifname|ifname:mac)</regex> </constraint> <constraintErrorMessage>Invalid Called-Station-Id format</constraintErrorMessage> <valueHelp> <format>ifname</format> <description>NAS-Port-Id - should contain root interface name (NAS-Port-Id=eth1)</description> </valueHelp> <valueHelp> <format>ifname:mac</format> <description>NAS-Port-Id - should contain root interface name and mac address (NAS-Port-Id=eth1:00:00:00:00:00:00)</description> </valueHelp> </properties> </leafNode> </children> </node> </children> </node> #include <include/accel-ppp/client-ip-pool.xml.i> #include <include/accel-ppp/client-ipv6-pool.xml.i> #include <include/name-server-ipv4-ipv6.xml.i> <tagNode name="interface"> <properties> <help>interface(s) to listen on</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> </properties> <children> #include <include/accel-ppp/vlan.xml.i> </children> </tagNode> #include <include/accel-ppp/gateway-address.xml.i> #include <include/accel-ppp/max-concurrent-sessions.xml.i> #include <include/accel-ppp/mtu-128-16384.xml.i> <node name="limits"> <properties> <help>Limits the connection rate from a single source</help> </properties> <children> <leafNode name="connection-limit"> <properties> <help>Acceptable rate of connections (e.g. 1/min, 60/sec)</help> <constraint> <regex>[0-9]+\/(min|sec)</regex> </constraint> <constraintErrorMessage>illegal value</constraintErrorMessage> </properties> </leafNode> <leafNode name="burst"> <properties> <help>Burst count</help> </properties> </leafNode> <leafNode name="timeout"> <properties> <help>Timeout in seconds</help> </properties> </leafNode> </children> </node> <leafNode name="service-name"> <properties> <help>Service name</help> <constraint> <regex>[a-zA-Z0-9\-]{1,100}</regex> </constraint> <constraintErrorMessage>Service-name can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage> <multi/> </properties> </leafNode> #include <include/accel-ppp/wins-server.xml.i> + #include <include/accel-ppp/ppp-options.xml.i> <node name="ppp-options"> - <properties> - <help>Advanced protocol options</help> - </properties> <children> <leafNode name="min-mtu"> - <properties> - <help>Minimum acceptable MTU (68-65535)</help> - <constraint> - <validator name="numeric" argument="--range 68-65535"/> - </constraint> - </properties> <defaultValue>1280</defaultValue> </leafNode> - <leafNode name="mru"> - <properties> - <help>Preferred MRU (68-65535)</help> - <constraint> - <validator name="numeric" argument="--range 68-65535"/> - </constraint> - </properties> - </leafNode> - <leafNode name="ccp"> - <properties> - <help>CCP negotiation (default disabled)</help> - <valueless /> - </properties> - </leafNode> - #include <include/accel-ppp/ppp-mppe.xml.i> - #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> - #include <include/accel-ppp/lcp-echo-timeout.xml.i> - #include <include/accel-ppp/ppp-interface-cache.xml.i> - <leafNode name="ipv4"> - <properties> - <help>IPv4 (IPCP) negotiation algorithm</help> - <constraint> - <regex>(deny|allow|prefer|require)</regex> - </constraint> - <constraintErrorMessage>invalid value</constraintErrorMessage> - <valueHelp> - <format>deny</format> - <description>Do not negotiate IPv4</description> - </valueHelp> - <valueHelp> - <format>allow</format> - <description>Negotiate IPv4 only if client requests</description> - </valueHelp> - <valueHelp> - <format>prefer</format> - <description>Ask client for IPv4 negotiation, do not fail if it rejects</description> - </valueHelp> - <valueHelp> - <format>require</format> - <description>Require IPv4 negotiation</description> - </valueHelp> - <completionHelp> - <list>deny allow prefer require</list> - </completionHelp> - </properties> - </leafNode> - #include <include/accel-ppp/ppp-options-ipv6.xml.i> - #include <include/accel-ppp/ppp-options-ipv6-interface-id.xml.i> </children> </node> <tagNode name="pado-delay"> <properties> <help>PADO delays</help> <valueHelp> <format>u32:1-999999</format> <description>Number in ms</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Invalid PADO delay</constraintErrorMessage> </properties> <children> <leafNode name="sessions"> <properties> <help>Number of sessions</help> <valueHelp> <format>u32:1-999999</format> <description>Number of sessions</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 1-999999"/> </constraint> <constraintErrorMessage>Invalid number of delayed sessions</constraintErrorMessage> </properties> </leafNode> </children> </tagNode> <leafNode name="session-control"> <properties> <help>control sessions count</help> <constraint> <regex>(deny|disable|replace)</regex> </constraint> <constraintErrorMessage>Invalid value</constraintErrorMessage> <valueHelp> <format>disable</format> <description>Disables session control</description> </valueHelp> <valueHelp> <format>deny</format> <description>Deny second session authorization</description> </valueHelp> <valueHelp> <format>replace</format> <description>Terminate first session when second is authorized</description> </valueHelp> <completionHelp> <list>deny disable replace</list> </completionHelp> </properties> <defaultValue>replace</defaultValue> </leafNode> #include <include/accel-ppp/shaper.xml.i> <node name="snmp"> <properties> <help>Enable SNMP</help> </properties> <children> <leafNode name="master-agent"> <properties> <help>enable SNMP master agent mode</help> <valueless /> </properties> </leafNode> </children> </node> <node name="extended-scripts"> <properties> <help>Extended script execution</help> </properties> <children> <leafNode name="on-pre-up"> <properties> <help>Script to run before PPPoE session interface comes up</help> <constraint> <validator name="script"/> </constraint> </properties> </leafNode> <leafNode name="on-up"> <properties> <help>Script to run when PPPoE session interface is completely configured and started</help> <constraint> <validator name="script"/> </constraint> </properties> </leafNode> <leafNode name="on-down"> <properties> <help>Script to run when PPPoE session interface going to terminate</help> <constraint> <validator name="script"/> </constraint> </properties> </leafNode> <leafNode name="on-change"> <properties> <help>Script to run when PPPoE session interface changed by RADIUS CoA handling</help> <constraint> <validator name="script"/> </constraint> </properties> </leafNode> </children> </node> #include <include/accel-ppp/default-pool.xml.i> #include <include/accel-ppp/default-ipv6-pool.xml.i> </children> </node> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index d3fb58433..942690bca 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -1,164 +1,146 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="vpn"> <children> <node name="l2tp" owner="${vyos_conf_scripts_dir}/vpn_l2tp.py"> <properties> <help>L2TP Virtual Private Network (VPN)</help> <priority>902</priority> </properties> <children> <node name="remote-access"> <properties> <help>Remote access L2TP VPN</help> </properties> <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> <constraint> <validator name="ipv4-address"/> </constraint> </properties> </leafNode> #include <include/accel-ppp/gateway-address.xml.i> #include <include/name-server-ipv4-ipv6.xml.i> <node name="lns"> <properties> <help>L2TP Network Server (LNS)</help> </properties> <children> <leafNode name="shared-secret"> <properties> <help>Tunnel password used to authenticate the client (LAC)</help> </properties> </leafNode> <leafNode name="host-name"> <properties> <help>Sent to the client (LAC) in the Host-Name attribute</help> <constraint> #include <include/constraint/host-name.xml.i> </constraint> <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage> </properties> </leafNode> </children> </node> - <leafNode name="ccp-disable"> - <properties> - <help>Disable Compression Control Protocol (CCP)</help> - <valueless /> - </properties> - </leafNode> <node name="ipsec-settings"> <properties> <help>Internet Protocol Security (IPsec) for remote access L2TP VPN</help> </properties> <children> <node name="authentication"> <properties> <help>IPsec authentication settings</help> </properties> <children> <leafNode name="mode"> <properties> <help>Authentication mode for IPsec</help> <valueHelp> <format>pre-shared-secret</format> <description>Use pre-shared secret for IPsec authentication</description> </valueHelp> <valueHelp> <format>x509</format> <description>Use X.509 certificate for IPsec authentication</description> </valueHelp> <constraint> <regex>(pre-shared-secret|x509)</regex> </constraint> <completionHelp> <list>pre-shared-secret x509</list> </completionHelp> </properties> </leafNode> #include <include/ipsec/authentication-pre-shared-secret.xml.i> #include <include/ipsec/authentication-x509.xml.i> </children> </node> <leafNode name="ike-lifetime"> <properties> <help>IKE lifetime</help> <valueHelp> <format>u32:30-86400</format> <description>IKE lifetime in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 30-86400"/> </constraint> </properties> <defaultValue>3600</defaultValue> </leafNode> <leafNode name="lifetime"> <properties> <help>ESP lifetime</help> <valueHelp> <format>u32:30-86400</format> <description>IKE lifetime in seconds</description> </valueHelp> <constraint> <validator name="numeric" argument="--range 30-86400"/> </constraint> </properties> <defaultValue>3600</defaultValue> </leafNode> #include <include/ipsec/esp-group.xml.i> #include <include/ipsec/ike-group.xml.i> </children> </node> #include <include/accel-ppp/wins-server.xml.i> #include <include/accel-ppp/client-ip-pool.xml.i> #include <include/accel-ppp/client-ipv6-pool.xml.i> #include <include/generic-description.xml.i> #include <include/dhcp-interface.xml.i> <node name="authentication"> <properties> <help>Authentication for remote access L2TP VPN</help> </properties> <children> #include <include/accel-ppp/auth-protocols.xml.i> #include <include/accel-ppp/auth-mode.xml.i> #include <include/accel-ppp/auth-local-users.xml.i> #include <include/radius-auth-server-ipv4.xml.i> #include <include/accel-ppp/radius-additions.xml.i> <node name="radius"> <children> #include <include/accel-ppp/radius-additions-rate-limit.xml.i> </children> </node> </children> </node> - <node name="ppp-options"> - <properties> - <help>Advanced protocol options</help> - </properties> - <children> - #include <include/accel-ppp/ppp-mppe.xml.i> - #include <include/accel-ppp/ppp-options-ipv4.xml.i> - #include <include/accel-ppp/ppp-options-ipv6.xml.i> - #include <include/accel-ppp/ppp-options-ipv6-interface-id.xml.i> - #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> - #include <include/accel-ppp/lcp-echo-timeout.xml.i> - </children> - </node> + #include <include/accel-ppp/ppp-options.xml.i> #include <include/accel-ppp/default-pool.xml.i> #include <include/accel-ppp/default-ipv6-pool.xml.i> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in index ec622b5d0..d23086c02 100644 --- a/interface-definitions/vpn_pptp.xml.in +++ b/interface-definitions/vpn_pptp.xml.in @@ -1,144 +1,122 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="vpn"> <children> <node name="pptp" owner="${vyos_conf_scripts_dir}/vpn_pptp.py"> <properties> <help>Point to Point Tunneling Protocol (PPTP) Virtual Private Network (VPN)</help> <priority>901</priority> </properties> <children> <node name="remote-access"> <properties> <help>Remote access PPTP VPN</help> </properties> <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> <constraint> <validator name="ipv4-address"/> </constraint> </properties> </leafNode> #include <include/accel-ppp/gateway-address.xml.i> - #include <include/name-server-ipv4.xml.i> + #include <include/name-server-ipv4-ipv6.xml.i> #include <include/accel-ppp/wins-server.xml.i> #include <include/accel-ppp/client-ip-pool.xml.i> <node name="authentication"> <properties> <help>Authentication for remote access PPTP VPN</help> </properties> <children> <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> </valueHelp> <valueHelp> <format>chap</format> <description>Require the peer to authenticate itself using CHAP [Challenge Handshake Authentication Protocol].</description> </valueHelp> <valueHelp> <format>mschap</format> <description>Require the peer to authenticate itself using CHAP [Challenge Handshake Authentication Protocol].</description> </valueHelp> <valueHelp> <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> - <help>Specifies mppe negotioation preference. (default require mppe 128-bit stateless</help> - <valueHelp> - <format>deny</format> - <description>deny mppe</description> - </valueHelp> - <valueHelp> - <format>prefer</format> - <description>ask client for mppe, if it rejects do not fail</description> - </valueHelp> - <valueHelp> - <format>require</format> - <description>ask client for mppe, if it rejects drop connection</description> - </valueHelp> - <constraint> - <regex>(deny|prefer|require)</regex> - </constraint> - <completionHelp> - <list>deny prefer require</list> - </completionHelp> - </properties> - <defaultValue>prefer</defaultValue> - </leafNode> #include <include/accel-ppp/auth-mode.xml.i> <node name="local-users"> <properties> <help>Local user authentication for remote access PPTP VPN</help> </properties> <children> <tagNode name="username"> <properties> <help>User name for authentication</help> </properties> <children> #include <include/generic-disable-node.xml.i> <leafNode name="password"> <properties> <help>Password for authentication</help> </properties> </leafNode> <leafNode name="static-ip"> <properties> <help>Static client IP address</help> </properties> <defaultValue>*</defaultValue> </leafNode> </children> </tagNode> </children> </node> <node name="radius"> <children> #include <include/accel-ppp/radius-additions-rate-limit.xml.i> </children> </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> + #include <include/accel-ppp/client-ipv6-pool.xml.i> #include <include/accel-ppp/default-ipv6-pool.xml.i> + #include <include/accel-ppp/ppp-options.xml.i> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index 2727540be..0d5d53301 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -1,65 +1,54 @@ <?xml version="1.0"?> <interfaceDefinition> <node name="vpn"> <children> <node name="sstp" owner="${vyos_conf_scripts_dir}/vpn_sstp.py"> <properties> <help>Secure Socket Tunneling Protocol (SSTP) server</help> <priority>901</priority> </properties> <children> <node name="authentication"> <properties> <help>Authentication for remote access SSTP Server</help> </properties> <children> #include <include/accel-ppp/auth-local-users.xml.i> #include <include/accel-ppp/auth-mode.xml.i> #include <include/accel-ppp/auth-protocols.xml.i> #include <include/radius-auth-server-ipv4.xml.i> #include <include/accel-ppp/radius-additions.xml.i> <node name="radius"> <children> #include <include/accel-ppp/radius-additions-rate-limit.xml.i> </children> </node> </children> </node> #include <include/accel-ppp/max-concurrent-sessions.xml.i> #include <include/interface/mtu-68-1500.xml.i> #include <include/accel-ppp/gateway-address.xml.i> #include <include/name-server-ipv4-ipv6.xml.i> #include <include/accel-ppp/client-ip-pool.xml.i> #include <include/accel-ppp/client-ipv6-pool.xml.i> #include <include/port-number.xml.i> <leafNode name="port"> <defaultValue>443</defaultValue> </leafNode> #include <include/accel-ppp/default-pool.xml.i> #include <include/accel-ppp/default-ipv6-pool.xml.i> - <node name="ppp-options"> - <properties> - <help>PPP (Point-to-Point Protocol) settings</help> - </properties> - <children> - #include <include/accel-ppp/ppp-mppe.xml.i> - #include <include/accel-ppp/ppp-options-ipv4.xml.i> - #include <include/accel-ppp/ppp-options-ipv6.xml.i> - #include <include/accel-ppp/lcp-echo-interval-failure.xml.i> - #include <include/accel-ppp/lcp-echo-timeout.xml.i> - </children> - </node> + #include <include/accel-ppp/ppp-options.xml.i> <node name="ssl"> <properties> <help>SSL Certificate, SSL Key and CA</help> </properties> <children> #include <include/pki/ca-certificate.xml.i> #include <include/pki/certificate.xml.i> </children> </node> </children> </node> </children> </node> </interfaceDefinition> diff --git a/python/vyos/accel_ppp_util.py b/python/vyos/accel_ppp_util.py index 2f029e042..d60402e48 100644 --- a/python/vyos/accel_ppp_util.py +++ b/python/vyos/accel_ppp_util.py @@ -1,202 +1,202 @@ # Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # 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/>. # The sole purpose of this module is to hold common functions used in # all kinds of implementations to verify the CLI configuration. # It is started by migrating the interfaces to the new get_config_dict() # approach which will lead to a lot of code that can be reused. # NOTE: imports should be as local as possible to the function which # makes use of it! from vyos import ConfigError from vyos.base import Warning from vyos.utils.dict import dict_search def get_pools_in_order(data: dict) -> list: """Return a list of dictionaries representing pool data in the order in which they should be allocated. Pool must be defined before we can use it with 'next-pool' option. Args: data: A dictionary of pool data, where the keys are pool names and the values are dictionaries containing the 'subnet' key and the optional 'next_pool' key. Returns: list: A list of dictionaries Raises: ValueError: If a 'next_pool' key references a pool name that has not been defined. ValueError: If a circular reference is found in the 'next_pool' keys. Example: config_data = { ... 'first-pool': { ... 'next_pool': 'second-pool', ... 'subnet': '192.0.2.0/25' ... }, ... 'second-pool': { ... 'next_pool': 'third-pool', ... 'subnet': '203.0.113.0/25' ... }, ... 'third-pool': { ... 'subnet': '198.51.100.0/24' ... }, ... 'foo': { ... 'subnet': '100.64.0.0/24', ... 'next_pool': 'second-pool' ... } ... } % get_pools_in_order(config_data) [{'third-pool': {'subnet': '198.51.100.0/24'}}, {'second-pool': {'next_pool': 'third-pool', 'subnet': '203.0.113.0/25'}}, {'first-pool': {'next_pool': 'second-pool', 'subnet': '192.0.2.0/25'}}, {'foo': {'next_pool': 'second-pool', 'subnet': '100.64.0.0/24'}}] """ pools = [] unresolved_pools = {} for pool, pool_config in data.items(): if "next_pool" not in pool_config or not pool_config["next_pool"]: pools.insert(0, {pool: pool_config}) else: unresolved_pools[pool] = pool_config while unresolved_pools: resolved_pools = [] for pool, pool_config in unresolved_pools.items(): next_pool_name = pool_config["next_pool"] if any(p for p in pools if next_pool_name in p): index = next( (i for i, p in enumerate(pools) if next_pool_name in p), None ) pools.insert(index + 1, {pool: pool_config}) resolved_pools.append(pool) elif next_pool_name in unresolved_pools: # next pool not yet resolved pass else: raise ConfigError( f"Pool '{next_pool_name}' not defined in configuration data" ) if not resolved_pools: raise ConfigError("Circular reference in configuration data") for pool in resolved_pools: unresolved_pools.pop(pool) return pools def verify_accel_ppp_base_service(config, local_users=True): """ Common helper function which must be used by all Accel-PPP services based on get_config_dict() """ # vertify auth settings if local_users and dict_search("authentication.mode", config) == "local": if ( dict_search("authentication.local_users", config) is None or dict_search("authentication.local_users", config) == {} ): raise ConfigError( "Authentication mode local requires local users to be configured!" ) for user in dict_search("authentication.local_users.username", config): user_config = config["authentication"]["local_users"]["username"][user] if "password" not in user_config: raise ConfigError(f'Password required for local user "{user}"') if "rate_limit" in user_config: # if up/download is set, check that both have a value if not {"upload", "download"} <= set(user_config["rate_limit"]): raise ConfigError( f'User "{user}" has rate-limit configured for only one ' "direction but both upload and download must be given!" ) elif dict_search("authentication.mode", config) == "radius": if not dict_search("authentication.radius.server", config): raise ConfigError("RADIUS authentication requires at least one server") for server in dict_search("authentication.radius.server", config): radius_config = config["authentication"]["radius"]["server"][server] if "key" not in radius_config: raise ConfigError(f'Missing RADIUS secret key for server "{server}"') if "name_server_ipv4" in config: if len(config["name_server_ipv4"]) > 2: raise ConfigError( "Not more then two IPv4 DNS name-servers " "can be configured" ) if "name_server_ipv6" in config: if len(config["name_server_ipv6"]) > 3: raise ConfigError( "Not more then three IPv6 DNS name-servers " "can be configured" ) def verify_accel_ppp_ip_pool(vpn_config): """ Common helper function which must be used by Accel-PPP services (pptp, l2tp, sstp, pppoe) to verify client-ip-pool and client-ipv6-pool """ if dict_search("client_ip_pool", vpn_config): for pool_name, pool_config in vpn_config["client_ip_pool"].items(): next_pool = dict_search(f"next_pool", pool_config) if next_pool: if next_pool not in vpn_config["client_ip_pool"]: raise ConfigError( f'Next pool "{next_pool}" does not exist') if not dict_search(f"range", pool_config): raise ConfigError( f'Pool "{pool_name}" does not contain range but next-pool exists' ) if not dict_search("gateway_address", vpn_config): Warning("IPv4 Server requires gateway-address to be configured!") default_pool = dict_search("default_pool", vpn_config) if default_pool: if default_pool not in dict_search("client_ip_pool", vpn_config): raise ConfigError(f'Default pool "{default_pool}" does not exists') if 'client_ipv6_pool' in vpn_config: for ipv6_pool, ipv6_pool_config in vpn_config['client_ipv6_pool'].items(): if 'delegate' in ipv6_pool_config and 'prefix' not in ipv6_pool_config: raise ConfigError( - f'IPoE IPv6 deletate-prefix requires IPv6 prefix to be configured in "{ipv6_pool}"!') + f'IPv6 delegate-prefix requires IPv6 prefix to be configured in "{ipv6_pool}"!') if dict_search('authentication.mode', vpn_config) in ['local', 'noauth']: if not dict_search('client_ip_pool', vpn_config) and not dict_search( 'client_ipv6_pool', vpn_config): raise ConfigError( - "L2TP local auth mode requires local client-ip-pool or client-ipv6-pool to be configured!") + "Local auth mode requires local client-ip-pool or client-ipv6-pool to be configured!") if dict_search('client_ip_pool', vpn_config) and not dict_search( 'default_pool', vpn_config): Warning("'default-pool' is not defined") if dict_search('client_ipv6_pool', vpn_config) and not dict_search( 'default_ipv6_pool', vpn_config): Warning("'default-ipv6-pool' is not defined") diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py index 6219a0a4c..0e6e522b9 100644 --- a/smoketest/scripts/cli/base_accel_ppp_test.py +++ b/smoketest/scripts/cli/base_accel_ppp_test.py @@ -1,494 +1,561 @@ # Copyright (C) 2020-2024 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 unittest from base_vyostest_shim import VyOSUnitTestSHIM from configparser import ConfigParser from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError from vyos.template import is_ipv4 from vyos.utils.system import get_half_cpus from vyos.utils.process import process_named_running from vyos.utils.process import cmd class BasicAccelPPPTest: class TestCase(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): cls._process_name = "accel-pppd" super(BasicAccelPPPTest.TestCase, cls).setUpClass() # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, cls._base_path) def setUp(self): self._gateway = "192.0.2.1" # ensure we can also run this test on a live system - so lets clean # out the current configuration :) self.cli_delete(self._base_path) def tearDown(self): # Check for running process self.assertTrue(process_named_running(self._process_name)) self.cli_delete(self._base_path) self.cli_commit() # Check for running process self.assertFalse(process_named_running(self._process_name)) def set(self, path): self.cli_set(self._base_path + path) def delete(self, path): self.cli_delete(self._base_path + path) def basic_protocol_specific_config(self): """ An astract method. Initialize protocol scpecific configureations. """ self.assertFalse(True, msg="Function must be defined") def initial_auth_config(self): """ Initialization of default authentication for all protocols """ self.set( [ "authentication", "local-users", "username", "vyos", "password", "vyos", ] ) self.set(["authentication", "mode", "local"]) def initial_gateway_config(self): """ Initialization of default gateway """ self.set(["gateway-address", self._gateway]) def initial_pool_config(self): """ Initialization of default client ip pool """ first_pool = "SIMPLE-POOL" self.set(["client-ip-pool", first_pool, "range", "192.0.2.0/24"]) self.set(["default-pool", first_pool]) def basic_config(self, is_auth=True, is_gateway=True, is_client_pool=True): """ Initialization of basic configuration :param is_auth: authentication initialization :type is_auth: bool :param is_gateway: gateway initialization :type is_gateway: bool :param is_client_pool: client ip pool initialization :type is_client_pool: bool """ self.basic_protocol_specific_config() if is_auth: self.initial_auth_config() if is_gateway: self.initial_gateway_config() if is_client_pool: self.initial_pool_config() def getConfig(self, start, end="cli"): """ Return part of configuration from line where the first injection of start keyword to the line where the first injection of end keyowrd :param start: start keyword :type start: str :param end: end keyword :type end: str :return: part of config :rtype: str """ command = f'cat {self._config_file} | sed -n "/^\[{start}/,/^\[{end}/p"' out = cmd(command) return out def verify(self, conf): self.assertEqual(conf["core"]["thread-count"], str(get_half_cpus())) def test_accel_name_servers(self): # Verify proper Name-Server configuration for IPv4 and IPv6 self.basic_config() nameserver = ["192.0.2.1", "192.0.2.2", "2001:db8::1"] for ns in nameserver: self.set(["name-server", ns]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # IPv4 and IPv6 nameservers must be checked individually for ns in nameserver: if is_ipv4(ns): self.assertIn(ns, [conf["dns"]["dns1"], conf["dns"]["dns2"]]) else: self.assertEqual(conf["ipv6-dns"][ns], None) def test_accel_local_authentication(self): # Test configuration of local authentication self.basic_config() # upload / download limit user = "test" password = "test2" static_ip = "100.100.100.101" upload = "5000" download = "10000" self.set( [ "authentication", "local-users", "username", user, "password", password, ] ) self.set( [ "authentication", "local-users", "username", user, "static-ip", static_ip, ] ) self.set( [ "authentication", "local-users", "username", user, "rate-limit", "upload", upload, ] ) # upload rate-limit requires also download rate-limit with self.assertRaises(ConfigSessionError): self.cli_commit() self.set( [ "authentication", "local-users", "username", user, "rate-limit", "download", download, ] ) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # check proper path to chap-secrets file self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets) # basic verification self.verify(conf) # check local users tmp = cmd(f"sudo cat {self._chap_secrets}") regex = f"{user}\s+\*\s+{password}\s+{static_ip}\s+{download}/{upload}" tmp = re.findall(regex, tmp) self.assertTrue(tmp) # Check local-users default value(s) self.delete( ["authentication", "local-users", "username", user, "static-ip"] ) # commit changes self.cli_commit() # check local users tmp = cmd(f"sudo cat {self._chap_secrets}") regex = f"{user}\s+\*\s+{password}\s+\*\s+{download}/{upload}" tmp = re.findall(regex, tmp) self.assertTrue(tmp) def test_accel_radius_authentication(self): # Test configuration of RADIUS authentication for PPPoE server self.basic_config() radius_server = "192.0.2.22" radius_key = "secretVyOS" radius_port = "2000" radius_port_acc = "3000" acct_interim_jitter = '10' acct_interim_interval = '10' acct_timeout = '30' self.set(["authentication", "mode", "radius"]) self.set( ["authentication", "radius", "server", radius_server, "key", radius_key] ) self.set( [ "authentication", "radius", "server", radius_server, "port", radius_port, ] ) self.set( [ "authentication", "radius", "server", radius_server, "acct-port", radius_port_acc, ] ) self.set( [ "authentication", "radius", "acct-interim-jitter", acct_interim_jitter, ] ) self.set( [ "authentication", "radius", "accounting-interim-interval", acct_interim_interval, ] ) self.set( [ "authentication", "radius", "acct-timeout", acct_timeout, ] ) coa_server = "4.4.4.4" coa_key = "testCoA" self.set( ["authentication", "radius", "dynamic-author", "server", coa_server] ) self.set(["authentication", "radius", "dynamic-author", "key", coa_key]) nas_id = "VyOS-PPPoE" nas_ip = "7.7.7.7" self.set(["authentication", "radius", "nas-identifier", nas_id]) self.set(["authentication", "radius", "nas-ip-address", nas_ip]) source_address = "1.2.3.4" self.set(["authentication", "radius", "source-address", source_address]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # basic verification self.verify(conf) # check auth self.assertTrue(conf["radius"].getboolean("verbose")) self.assertEqual(conf["radius"]["acct-timeout"], acct_timeout) self.assertEqual(conf["radius"]["acct-interim-interval"], acct_interim_interval) self.assertEqual(conf["radius"]["acct-interim-jitter"], acct_interim_jitter) self.assertEqual(conf["radius"]["timeout"], "3") self.assertEqual(conf["radius"]["max-try"], "3") self.assertEqual( conf["radius"]["dae-server"], f"{coa_server}:1700,{coa_key}" ) self.assertEqual(conf["radius"]["nas-identifier"], nas_id) self.assertEqual(conf["radius"]["nas-ip-address"], nas_ip) self.assertEqual(conf["radius"]["bind"], source_address) server = conf["radius"]["server"].split(",") self.assertEqual(radius_server, server[0]) self.assertEqual(radius_key, server[1]) self.assertEqual(f"auth-port={radius_port}", server[2]) self.assertEqual(f"acct-port={radius_port_acc}", server[3]) self.assertEqual(f"req-limit=0", server[4]) self.assertEqual(f"fail-time=0", server[5]) # # Disable Radius Accounting # self.delete( ["authentication", "radius", "server", radius_server, "acct-port"] ) self.set( [ "authentication", "radius", "server", radius_server, "disable-accounting", ] ) # commit changes self.cli_commit() conf.read(self._config_file) server = conf["radius"]["server"].split(",") self.assertEqual(radius_server, server[0]) self.assertEqual(radius_key, server[1]) self.assertEqual(f"auth-port={radius_port}", server[2]) self.assertEqual(f"acct-port=0", server[3]) self.assertEqual(f"req-limit=0", server[4]) self.assertEqual(f"fail-time=0", server[5]) def test_accel_ipv4_pool(self): self.basic_config(is_gateway=False, is_client_pool=False) gateway = "192.0.2.1" subnet = "172.16.0.0/24" 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]) self.set(["client-ip-pool", first_pool, "next-pool", second_pool]) self.set(["client-ip-pool", second_pool, "range", range]) self.set(["default-pool", first_pool]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) self.assertEqual( f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},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"]) def test_accel_next_pool(self): # T5099 required specific order self.basic_config(is_gateway=False, is_client_pool=False) gateway = "192.0.2.1" first_pool = "VyOS-pool1" first_subnet = "192.0.2.0/25" second_pool = "Vyos-pool2" second_subnet = "203.0.113.0/25" third_pool = "Vyos-pool3" third_subnet = "198.51.100.0/24" self.set(["gateway-address", gateway]) self.set(["client-ip-pool", first_pool, "range", first_subnet]) self.set(["client-ip-pool", first_pool, "next-pool", second_pool]) self.set(["client-ip-pool", second_pool, "range", second_subnet]) self.set(["client-ip-pool", second_pool, "next-pool", third_pool]) self.set(["client-ip-pool", third_pool, "range", third_subnet]) # commit changes self.cli_commit() config = self.getConfig("ip-pool") pool_config = f"""gw-ip-address={gateway} {third_subnet},name={third_pool} {second_subnet},name={second_pool},next={third_pool} {first_subnet},name={first_pool},next={second_pool}""" self.assertIn(pool_config, config) def test_accel_ipv6_pool(self): # Test configuration of IPv6 client pools self.basic_config(is_gateway=False, is_client_pool=False) # Enable IPv6 allow_ipv6 = 'allow' self.set(['ppp-options', 'ipv6', allow_ipv6]) pool_name = 'ipv6_test_pool' prefix_1 = '2001:db8:fffe::/56' prefix_mask = '64' prefix_2 = '2001:db8:ffff::/56' client_prefix_1 = f'{prefix_1},{prefix_mask}' client_prefix_2 = f'{prefix_2},{prefix_mask}' self.set( ['client-ipv6-pool', pool_name, 'prefix', prefix_1, 'mask', prefix_mask]) self.set( ['client-ipv6-pool', pool_name, 'prefix', prefix_2, 'mask', prefix_mask]) delegate_1_prefix = '2001:db8:fff1::/56' delegate_2_prefix = '2001:db8:fff2::/56' delegate_mask = '64' self.set( ['client-ipv6-pool', pool_name, 'delegate', delegate_1_prefix, 'delegation-prefix', delegate_mask]) self.set( ['client-ipv6-pool', pool_name, 'delegate', delegate_2_prefix, 'delegation-prefix', delegate_mask]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False) conf.read(self._config_file) for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']: self.assertEqual(conf['modules'][tmp], None) self.assertEqual(conf['ppp']['ipv6'], allow_ipv6) config = self.getConfig("ipv6-pool") pool_config = f"""{client_prefix_1},name={pool_name} {client_prefix_2},name={pool_name} delegate={delegate_1_prefix},{delegate_mask},name={pool_name} delegate={delegate_2_prefix},{delegate_mask},name={pool_name}""" self.assertIn(pool_config, config) + + def test_accel_ppp_options(self): + # Test configuration of local authentication for PPPoE server + self.basic_config() + + # other settings + mppe = 'require' + self.set(['ppp-options', 'disable-ccp']) + self.set(['ppp-options', 'mppe', mppe]) + + # min-mtu + min_mtu = '1400' + self.set(['ppp-options', 'min-mtu', min_mtu]) + + # mru + mru = '9000' + self.set(['ppp-options', 'mru', mru]) + + # interface-cache + interface_cache = '128000' + self.set(['ppp-options', 'interface-cache', interface_cache]) + + # ipv6 + allow_ipv6 = 'allow' + allow_ipv4 = 'require' + random = 'random' + lcp_failure = '4' + lcp_interval = '40' + lcp_timeout = '100' + self.set(['ppp-options', 'ipv4', allow_ipv4]) + self.set(['ppp-options', 'ipv6', allow_ipv6]) + self.set(['ppp-options', 'ipv6-interface-id', random]) + self.set(['ppp-options', 'ipv6-accept-peer-interface-id']) + self.set(['ppp-options', 'ipv6-peer-interface-id', random]) + self.set(['ppp-options', 'lcp-echo-failure', lcp_failure]) + self.set(['ppp-options', 'lcp-echo-interval', lcp_interval]) + self.set(['ppp-options', 'lcp-echo-timeout', lcp_timeout]) + # commit changes + self.cli_commit() + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=') + conf.read(self._config_file) + + self.assertEqual(conf['chap-secrets']['gw-ip-address'], self._gateway) + + # check ppp + self.assertEqual(conf['ppp']['mppe'], mppe) + self.assertEqual(conf['ppp']['min-mtu'], min_mtu) + self.assertEqual(conf['ppp']['mru'], mru) + + self.assertEqual(conf['ppp']['ccp'],'0') + + # check interface-cache + self.assertEqual(conf['ppp']['unit-cache'], interface_cache) + + #check ipv6 + for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']: + self.assertEqual(conf['modules'][tmp], None) + + self.assertEqual(conf['ppp']['ipv6'], allow_ipv6) + self.assertEqual(conf['ppp']['ipv6-intf-id'], random) + self.assertEqual(conf['ppp']['ipv6-peer-intf-id'], random) + self.assertTrue(conf['ppp'].getboolean('ipv6-accept-peer-intf-id')) + self.assertEqual(conf['ppp']['lcp-echo-failure'], lcp_failure) + self.assertEqual(conf['ppp']['lcp-echo-interval'], lcp_interval) + self.assertEqual(conf['ppp']['lcp-echo-timeout'], lcp_timeout) \ No newline at end of file diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py index cec6adb09..20a168b58 100755 --- a/smoketest/scripts/cli/test_service_ipoe-server.py +++ b/smoketest/scripts/cli/test_service_ipoe-server.py @@ -1,232 +1,236 @@ #!/usr/bin/env python3 # # Copyright (C) 2022-2024 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 unittest from collections import OrderedDict from base_accel_ppp_test import BasicAccelPPPTest from vyos.configsession import ConfigSessionError from vyos.utils.process import cmd from configparser import ConfigParser from configparser import RawConfigParser ac_name = "ACN" interface = "eth0" class MultiOrderedDict(OrderedDict): # Accel-ppp has duplicate keys in config file (gw-ip-address) # This class is used to define dictionary which can contain multiple values # in one key. def __setitem__(self, key, value): if isinstance(value, list) and key in self: self[key].extend(value) else: super(OrderedDict, self).__setitem__(key, value) class TestServiceIPoEServer(BasicAccelPPPTest.TestCase): @classmethod def setUpClass(cls): cls._base_path = ["service", "ipoe-server"] cls._config_file = "/run/accel-pppd/ipoe.conf" cls._chap_secrets = "/run/accel-pppd/ipoe.chap-secrets" cls._protocol_section = "ipoe" # call base-classes classmethod super(TestServiceIPoEServer, cls).setUpClass() def verify(self, conf): super().verify(conf) # Validate configuration values accel_modules = list(conf["modules"].keys()) self.assertIn("log_syslog", accel_modules) self.assertIn("ipoe", accel_modules) self.assertIn("shaper", accel_modules) self.assertIn("ipv6pool", accel_modules) self.assertIn("ipv6_nd", accel_modules) self.assertIn("ipv6_dhcp", accel_modules) self.assertIn("ippool", accel_modules) def initial_gateway_config(self): self._gateway = "192.0.2.1/24" super().initial_gateway_config() def initial_auth_config(self): self.set(["authentication", "mode", "noauth"]) def basic_protocol_specific_config(self): self.set(["interface", interface, "client-subnet", "192.168.0.0/24"]) def test_accel_local_authentication(self): mac_address = "08:00:27:2f:d8:06" self.set(["authentication", "interface", interface, "mac", mac_address]) self.set(["authentication", "mode", "local"]) # No IPoE interface configured with self.assertRaises(ConfigSessionError): self.cli_commit() # Test configuration of local authentication for PPPoE server self.basic_config() # Rewrite authentication from basic_config self.set(["authentication", "interface", interface, "mac", mac_address]) self.set(["authentication", "mode", "local"]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # check proper path to chap-secrets file self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets) accel_modules = list(conf["modules"].keys()) self.assertIn("chap-secrets", accel_modules) # basic verification self.verify(conf) # check local users tmp = cmd(f"sudo cat {self._chap_secrets}") regex = f"{interface}\s+\*\s+{mac_address}\s+\*" tmp = re.findall(regex, tmp) self.assertTrue(tmp) def test_accel_ipv4_pool(self): self.basic_config(is_gateway=False, is_client_pool=False) gateway = ["172.16.0.1/25", "192.0.2.1/24"] subnet = "172.16.0.0/24" 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]) self.set(["client-ip-pool", first_pool, "range", subnet]) self.set(["client-ip-pool", first_pool, "next-pool", second_pool]) self.set(["client-ip-pool", second_pool, "range", range]) self.set(["default-pool", first_pool]) # commit changes self.cli_commit() # Validate configuration values conf = RawConfigParser( allow_no_value=True, delimiters="=", strict=False, dict_type=MultiOrderedDict, ) conf.read(self._config_file) self.assertIn( f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},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") for gw in gateway: self.assertIn(gw.split("/")[0], gw_pool_config_list) self.assertIn(gw, gw_ipoe_config_list) self.assertIn(first_pool, conf[self._protocol_section]["ip-pool"]) def test_accel_next_pool(self): self.basic_config(is_gateway=False, is_client_pool=False) first_pool = "VyOS-pool1" first_subnet = "192.0.2.0/25" first_gateway = "192.0.2.1/24" second_pool = "Vyos-pool2" second_subnet = "203.0.113.0/25" second_gateway = "203.0.113.1/24" third_pool = "Vyos-pool3" third_subnet = "198.51.100.0/24" third_gateway = "198.51.100.1/24" self.set(["gateway-address", f"{first_gateway}"]) self.set(["gateway-address", f"{second_gateway}"]) self.set(["gateway-address", f"{third_gateway}"]) self.set(["client-ip-pool", first_pool, "range", first_subnet]) self.set(["client-ip-pool", first_pool, "next-pool", second_pool]) self.set(["client-ip-pool", second_pool, "range", second_subnet]) self.set(["client-ip-pool", second_pool, "next-pool", third_pool]) self.set(["client-ip-pool", third_pool, "range", third_subnet]) # commit changes self.cli_commit() config = self.getConfig("ip-pool") # T5099 required specific order pool_config = f"""gw-ip-address={first_gateway.split('/')[0]} gw-ip-address={second_gateway.split('/')[0]} gw-ip-address={third_gateway.split('/')[0]} {third_subnet},name={third_pool} {second_subnet},name={second_pool},next={third_pool} {first_subnet},name={first_pool},next={second_pool}""" self.assertIn(pool_config, config) def test_accel_ipv6_pool(self): # Test configuration of IPv6 client pools self.basic_config(is_gateway=False, is_client_pool=False) pool_name = 'ipv6_test_pool' prefix_1 = '2001:db8:fffe::/56' prefix_mask = '64' prefix_2 = '2001:db8:ffff::/56' client_prefix_1 = f'{prefix_1},{prefix_mask}' client_prefix_2 = f'{prefix_2},{prefix_mask}' self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_1, 'mask', prefix_mask]) self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_2, 'mask', prefix_mask]) delegate_1_prefix = '2001:db8:fff1::/56' delegate_2_prefix = '2001:db8:fff2::/56' delegate_mask = '64' self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_1_prefix, 'delegation-prefix', delegate_mask]) self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_2_prefix, 'delegation-prefix', delegate_mask]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False) conf.read(self._config_file) for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']: self.assertEqual(conf['modules'][tmp], None) config = self.getConfig("ipv6-pool") pool_config = f"""{client_prefix_1},name={pool_name} {client_prefix_2},name={pool_name} delegate={delegate_1_prefix},{delegate_mask},name={pool_name} delegate={delegate_2_prefix},{delegate_mask},name={pool_name}""" self.assertIn(pool_config, config) + @unittest.skip("PPP is not a part of IPoE") + def test_accel_ppp_options(self): + pass + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 11d5b8b78..d7c7aa164 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -1,212 +1,153 @@ #!/usr/bin/env python3 # # Copyright (C) 2022-2024 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_accel_ppp_test import BasicAccelPPPTest from configparser import ConfigParser from vyos.utils.file import read_file from vyos.template import range_to_regex local_if = ['interfaces', 'dummy', 'dum667'] ac_name = 'ACN' interface = 'eth0' class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): @classmethod def setUpClass(cls): cls._base_path = ['service', 'pppoe-server'] cls._config_file = '/run/accel-pppd/pppoe.conf' cls._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets' cls._protocol_section = 'pppoe' # call base-classes classmethod super(TestServicePPPoEServer, cls).setUpClass() def tearDown(self): self.cli_delete(local_if) super().tearDown() def verify(self, conf): mtu = '1492' # validate some common values in the configuration for tmp in ['log_syslog', 'pppoe', 'ippool', 'auth_mschap_v2', 'auth_mschap_v1', 'auth_chap_md5', 'auth_pap', 'shaper']: # Settings without values provide None self.assertEqual(conf['modules'][tmp], None) # check Access Concentrator setting self.assertTrue(conf['pppoe']['ac-name'] == ac_name) self.assertTrue(conf['pppoe'].getboolean('verbose')) self.assertTrue(conf['pppoe']['interface'], interface) # check ppp self.assertTrue(conf['ppp'].getboolean('verbose')) self.assertTrue(conf['ppp'].getboolean('check-ip')) self.assertEqual(conf['ppp']['mtu'], mtu) - self.assertEqual(conf['ppp']['lcp-echo-interval'], '30') - self.assertEqual(conf['ppp']['lcp-echo-timeout'], '0') - self.assertEqual(conf['ppp']['lcp-echo-failure'], '3') super().verify(conf) def basic_protocol_specific_config(self): self.cli_set(local_if + ['address', '192.0.2.1/32']) self.set(['access-concentrator', ac_name]) self.set(['interface', interface]) - - def test_pppoe_server_ppp_options(self): - # Test configuration of local authentication for PPPoE server + def test_pppoe_limits(self): self.basic_config() - - # other settings - mppe = 'require' - self.set(['ppp-options', 'ccp']) - self.set(['ppp-options', 'mppe', mppe]) self.set(['limits', 'connection-limit', '20/min']) - - # min-mtu - min_mtu = '1400' - self.set(['ppp-options', 'min-mtu', min_mtu]) - - # mru - mru = '9000' - self.set(['ppp-options', 'mru', mru]) - - # interface-cache - interface_cache = '128000' - self.set(['ppp-options', 'interface-cache', interface_cache]) - - # ipv6 - allow_ipv6 = 'allow' - random = 'random' - self.set(['ppp-options', 'ipv6', allow_ipv6]) - self.set(['ppp-options', 'ipv6-intf-id', random]) - self.set(['ppp-options', 'ipv6-accept-peer-intf-id']) - self.set(['ppp-options', 'ipv6-peer-intf-id', random]) - # commit changes self.cli_commit() - - # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') conf.read(self._config_file) - - # basic verification - self.verify(conf) - - self.assertEqual(conf['chap-secrets']['gw-ip-address'], self._gateway) - - # check ppp - self.assertEqual(conf['ppp']['mppe'], mppe) - self.assertEqual(conf['ppp']['min-mtu'], min_mtu) - self.assertEqual(conf['ppp']['mru'], mru) - - self.assertTrue(conf['ppp'].getboolean('ccp')) - - # check other settings self.assertEqual(conf['connlimit']['limit'], '20/min') - # check interface-cache - self.assertEqual(conf['ppp']['unit-cache'], interface_cache) - - #check ipv6 - for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']: - self.assertEqual(conf['modules'][tmp], None) - - self.assertEqual(conf['ppp']['ipv6'], allow_ipv6) - self.assertEqual(conf['ppp']['ipv6-intf-id'], random) - self.assertEqual(conf['ppp']['ipv6-peer-intf-id'], random) - self.assertTrue(conf['ppp'].getboolean('ipv6-accept-peer-intf-id')) - def test_pppoe_server_authentication_protocols(self): # Test configuration of local authentication for PPPoE server self.basic_config() # explicitly test mschap-v2 - no special reason self.set( ['authentication', 'protocols', 'mschap-v2']) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True) conf.read(self._config_file) self.assertEqual(conf['modules']['auth_mschap_v2'], None) def test_pppoe_server_shaper(self): fwmark = '223' limiter = 'tbf' self.basic_config() self.set(['shaper', 'fwmark', fwmark]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') conf.read(self._config_file) # basic verification self.verify(conf) self.assertEqual(conf['shaper']['fwmark'], fwmark) self.assertEqual(conf['shaper']['down-limiter'], limiter) def test_accel_radius_authentication(self): radius_called_sid = 'ifname:mac' self.set(['authentication', 'radius', 'called-sid-format', radius_called_sid]) # run common tests super().test_accel_radius_authentication() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters='=') conf.read(self._config_file) # Validate configuration self.assertEqual(conf['pppoe']['called-sid'], radius_called_sid) def test_pppoe_server_vlan(self): vlans = ['100', '200', '300-310'] # Test configuration of local authentication for PPPoE server self.basic_config() for vlan in vlans: self.set(['interface', interface, 'vlan', vlan]) # commit changes self.cli_commit() # Validate configuration values config = read_file(self._config_file) for vlan in vlans: tmp = range_to_regex(vlan) self.assertIn(f'interface=re:^{interface}\.{tmp}$', config) tmp = ','.join(vlans) self.assertIn(f'vlan-mon={interface},{tmp}', config) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_vpn_l2tp.py b/smoketest/scripts/cli/test_vpn_l2tp.py index 129a9c602..3d9d94f52 100755 --- a/smoketest/scripts/cli/test_vpn_l2tp.py +++ b/smoketest/scripts/cli/test_vpn_l2tp.py @@ -1,111 +1,59 @@ #!/usr/bin/env python3 # # Copyright (C) 2023-2024 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 unittest from base_accel_ppp_test import BasicAccelPPPTest from configparser import ConfigParser from vyos.utils.process import cmd class TestVPNL2TPServer(BasicAccelPPPTest.TestCase): @classmethod def setUpClass(cls): cls._base_path = ['vpn', 'l2tp', 'remote-access'] cls._config_file = '/run/accel-pppd/l2tp.conf' cls._chap_secrets = '/run/accel-pppd/l2tp.chap-secrets' cls._protocol_section = 'l2tp' # call base-classes classmethod super(TestVPNL2TPServer, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestVPNL2TPServer, cls).tearDownClass() def basic_protocol_specific_config(self): pass - def test_l2tp_server_ppp_options(self): - # Test configuration of local authentication for PPPoE server - self.basic_config() - mtu = '1425' - lcp_echo_failure = '5' - lcp_echo_interval = '40' - lcp_echo_timeout = '3000' - # other settings - mppe = 'require' - self.set(['ccp-disable']) - self.set(['ppp-options', 'mppe', mppe]) - self.set(['authentication', 'radius', 'preallocate-vif']) - self.set(['mtu', mtu]) - self.set(['ppp-options', 'lcp-echo-failure', lcp_echo_failure]) - self.set(['ppp-options', 'lcp-echo-interval', lcp_echo_interval]) - self.set(['ppp-options', 'lcp-echo-timeout', lcp_echo_timeout]) - - allow_ipv6 = 'allow' - random = 'random' - self.set(['ppp-options', 'ipv6', allow_ipv6]) - self.set(['ppp-options', 'ipv6-intf-id', random]) - self.set(['ppp-options', 'ipv6-accept-peer-intf-id']) - self.set(['ppp-options', 'ipv6-peer-intf-id', random]) - - # commit changes - self.cli_commit() - - # Validate configuration values - conf = ConfigParser(allow_no_value=True, delimiters='=') - conf.read(self._config_file) - - # basic verification - self.verify(conf) - - # check ppp - self.assertEqual(conf['ppp']['mppe'], mppe) - self.assertFalse(conf['ppp'].getboolean('ccp')) - self.assertEqual(conf['ppp']['unit-preallocate'], '1') - self.assertTrue(conf['ppp'].getboolean('verbose')) - self.assertTrue(conf['ppp'].getboolean('check-ip')) - self.assertEqual(conf['ppp']['mtu'], mtu) - self.assertEqual(conf['ppp']['lcp-echo-interval'], lcp_echo_interval) - self.assertEqual(conf['ppp']['lcp-echo-timeout'], lcp_echo_timeout) - self.assertEqual(conf['ppp']['lcp-echo-failure'], lcp_echo_failure) - - for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']: - self.assertEqual(conf['modules'][tmp], None) - self.assertEqual(conf['ppp']['ipv6'], allow_ipv6) - self.assertEqual(conf['ppp']['ipv6-intf-id'], random) - self.assertEqual(conf['ppp']['ipv6-peer-intf-id'], random) - self.assertTrue(conf['ppp'].getboolean('ipv6-accept-peer-intf-id')) - def test_l2tp_server_authentication_protocols(self): # Test configuration of local authentication for PPPoE server self.basic_config() # explicitly test mschap-v2 - no special reason self.set( ['authentication', 'protocols', 'mschap-v2']) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True) conf.read(self._config_file) self.assertEqual(conf['modules']['auth_mschap_v2'], None) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_vpn_pptp.py b/smoketest/scripts/cli/test_vpn_pptp.py index f3fce822b..40dcb7f80 100755 --- a/smoketest/scripts/cli/test_vpn_pptp.py +++ b/smoketest/scripts/cli/test_vpn_pptp.py @@ -1,227 +1,204 @@ #!/usr/bin/env python3 # # Copyright (C) 2023-2024 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 unittest from configparser import ConfigParser from vyos.utils.process import cmd from base_accel_ppp_test import BasicAccelPPPTest from vyos.template import is_ipv4 class TestVPNPPTPServer(BasicAccelPPPTest.TestCase): @classmethod def setUpClass(cls): cls._base_path = ['vpn', 'pptp', 'remote-access'] cls._config_file = '/run/accel-pppd/pptp.conf' cls._chap_secrets = '/run/accel-pppd/pptp.chap-secrets' cls._protocol_section = 'pptp' # call base-classes classmethod super(TestVPNPPTPServer, cls).setUpClass() @classmethod def tearDownClass(cls): super(TestVPNPPTPServer, cls).tearDownClass() def basic_protocol_specific_config(self): pass - def test_accel_name_servers(self): - # Verify proper Name-Server configuration for IPv4 - self.basic_config() - - nameserver = ["192.0.2.1", "192.0.2.2"] - for ns in nameserver: - self.set(["name-server", ns]) - - # commit changes - self.cli_commit() - - # Validate configuration values - conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) - conf.read(self._config_file) - - # IPv4 and IPv6 nameservers must be checked individually - for ns in nameserver: - self.assertIn(ns, [conf["dns"]["dns1"], conf["dns"]["dns2"]]) - def test_accel_local_authentication(self): # Test configuration of local authentication self.basic_config() # upload / download limit user = "test" password = "test2" static_ip = "100.100.100.101" upload = "5000" download = "10000" self.set( [ "authentication", "local-users", "username", user, "password", password, ] ) self.set( [ "authentication", "local-users", "username", user, "static-ip", static_ip, ] ) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # check proper path to chap-secrets file self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets) # basic verification self.verify(conf) # check local users tmp = cmd(f"sudo cat {self._chap_secrets}") regex = f"{user}\s+\*\s+{password}\s+{static_ip}\s" tmp = re.findall(regex, tmp) self.assertTrue(tmp) # Check local-users default value(s) self.delete(["authentication", "local-users", "username", user, "static-ip"]) # commit changes self.cli_commit() # check local users tmp = cmd(f"sudo cat {self._chap_secrets}") regex = f"{user}\s+\*\s+{password}\s+\*\s" tmp = re.findall(regex, tmp) self.assertTrue(tmp) def test_accel_radius_authentication(self): # Test configuration of RADIUS authentication for PPPoE server self.basic_config() radius_server = "192.0.2.22" radius_key = "secretVyOS" radius_port = "2000" radius_port_acc = "3000" self.set(["authentication", "mode", "radius"]) self.set( ["authentication", "radius", "server", radius_server, "key", radius_key] ) self.set( [ "authentication", "radius", "server", radius_server, "port", radius_port, ] ) self.set( [ "authentication", "radius", "server", radius_server, "acct-port", radius_port_acc, ] ) nas_id = "VyOS-PPPoE" nas_ip = "7.7.7.7" self.set(["authentication", "radius", "nas-identifier", nas_id]) self.set(["authentication", "radius", "nas-ip-address", nas_ip]) source_address = "1.2.3.4" self.set(["authentication", "radius", "source-address", source_address]) # commit changes self.cli_commit() # Validate configuration values conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False) conf.read(self._config_file) # basic verification self.verify(conf) # check auth self.assertTrue(conf["radius"].getboolean("verbose")) self.assertEqual(conf["radius"]["acct-timeout"], "30") self.assertEqual(conf["radius"]["timeout"], "30") self.assertEqual(conf["radius"]["max-try"], "3") self.assertEqual(conf["radius"]["nas-identifier"], nas_id) self.assertEqual(conf["radius"]["nas-ip-address"], nas_ip) self.assertEqual(conf["radius"]["bind"], source_address) server = conf["radius"]["server"].split(",") self.assertEqual(radius_server, server[0]) self.assertEqual(radius_key, server[1]) self.assertEqual(f"auth-port={radius_port}", server[2]) self.assertEqual(f"acct-port={radius_port_acc}", server[3]) self.assertEqual(f"req-limit=0", server[4]) self.assertEqual(f"fail-time=0", server[5]) # # Disable Radius Accounting # self.delete(["authentication", "radius", "server", radius_server, "acct-port"]) self.set( [ "authentication", "radius", "server", radius_server, "disable-accounting", ] ) # commit changes self.cli_commit() conf.read(self._config_file) server = conf["radius"]["server"].split(",") self.assertEqual(radius_server, server[0]) self.assertEqual(radius_key, server[1]) self.assertEqual(f"auth-port={radius_port}", server[2]) self.assertEqual(f"acct-port=0", server[3]) self.assertEqual(f"req-limit=0", server[4]) self.assertEqual(f"fail-time=0", server[5]) - @unittest.skip("IPv6 is not implemented in PPTP") - def test_accel_ipv6_pool(self): - pass - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index b569ca140..36b3d2a30 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -1,117 +1,112 @@ #!/usr/bin/env python3 # # Copyright (C) 2019-2024 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.configdict import get_accel_dict from vyos.template import render from vyos.utils.process import call from vyos.utils.dict import dict_search from vyos.accel_ppp_util import verify_accel_ppp_base_service from vyos.accel_ppp_util import verify_accel_ppp_ip_pool from vyos.accel_ppp_util import get_pools_in_order from vyos.base import Warning from vyos import ConfigError from vyos import airbag airbag.enable() l2tp_conf = '/run/accel-pppd/l2tp.conf' l2tp_chap_secrets = '/run/accel-pppd/l2tp.chap-secrets' def get_config(config=None): if config: conf = config else: conf = Config() base = ['vpn', 'l2tp', 'remote-access'] if not conf.exists(base): return None # retrieve common dictionary keys l2tp = get_accel_dict(conf, base, l2tp_chap_secrets) if dict_search('client_ip_pool', l2tp): # Multiple named pools require ordered values T5099 l2tp['ordered_named_pools'] = get_pools_in_order( dict_search('client_ip_pool', l2tp)) - l2tp['ip6_column'] = [] - if dict_search('client_ipv6_pool.prefix', l2tp): - l2tp['ip6_column'].append('ipv6') - if dict_search('client_ipv6_pool.delegate', l2tp): - l2tp['ip6_column'].append('ip6-db') l2tp['server_type'] = 'l2tp' return l2tp def verify(l2tp): if not l2tp: return None verify_accel_ppp_base_service(l2tp) if dict_search('authentication.radius.dynamic_author.server', l2tp): if not dict_search('authentication.radius.dynamic_author.key', l2tp): raise ConfigError('DA/CoE server key required!') verify_accel_ppp_ip_pool(l2tp) if 'wins_server' in l2tp and len(l2tp['wins_server']) > 2: raise ConfigError( 'Not more then two WINS name-servers can be configured') return None def generate(l2tp): if not l2tp: return None render(l2tp_conf, 'accel-ppp/l2tp.config.j2', l2tp) if dict_search('authentication.mode', l2tp) == 'local': render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2', l2tp, permission=0o640) return None def apply(l2tp): if not l2tp: call('systemctl stop accel-ppp@l2tp.service') for file in [l2tp_chap_secrets, l2tp_conf]: if os.path.exists(file): os.unlink(file) return None call('systemctl restart accel-ppp@l2tp.service') 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/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 0629625bf..b1d5067d5 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -1,128 +1,104 @@ #!/usr/bin/env python3 # # 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 # 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.template import render from vyos.utils.process import call from vyos.utils.dict import dict_search +from vyos.accel_ppp_util import verify_accel_ppp_base_service 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() pptp_conf = '/run/accel-pppd/pptp.conf' pptp_chap_secrets = '/run/accel-pppd/pptp.chap-secrets' def get_config(config=None): if config: conf = config else: conf = Config() base = ['vpn', 'pptp', 'remote-access'] if not conf.exists(base): return None # 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)) pptp['chap_secrets_file'] = pptp_chap_secrets pptp['server_type'] = 'pptp' return pptp 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!') - - 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}"') - - 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}"') + verify_accel_ppp_base_service(pptp) verify_accel_ppp_ip_pool(pptp) - 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 '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): if not pptp: return None render(pptp_conf, 'accel-ppp/pptp.config.j2', pptp) 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: call('systemctl stop accel-ppp@pptp.service') for file in [pptp_conf, pptp_chap_secrets]: if os.path.exists(file): os.unlink(file) return None call('systemctl restart accel-ppp@pptp.service') 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/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index a84513a0f..5c229fe62 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -1,167 +1,166 @@ #!/usr/bin/env python3 # # Copyright (C) 2018-2024 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.configdict import get_accel_dict -from vyos.configdict import dict_merge from vyos.pki import wrap_certificate from vyos.pki import wrap_private_key from vyos.template import render from vyos.utils.process import call from vyos.utils.network import check_port_availability from vyos.utils.dict import dict_search from vyos.accel_ppp_util import verify_accel_ppp_base_service from vyos.accel_ppp_util import verify_accel_ppp_ip_pool from vyos.accel_ppp_util import get_pools_in_order from vyos.utils.network import is_listen_port_bind_service from vyos.utils.file import write_file from vyos import ConfigError from vyos import airbag airbag.enable() cfg_dir = '/run/accel-pppd' sstp_conf = '/run/accel-pppd/sstp.conf' sstp_chap_secrets = '/run/accel-pppd/sstp.chap-secrets' cert_file_path = os.path.join(cfg_dir, 'sstp-cert.pem') cert_key_path = os.path.join(cfg_dir, 'sstp-cert.key') ca_cert_file_path = os.path.join(cfg_dir, 'sstp-ca.pem') def get_config(config=None): if config: conf = config else: conf = Config() base = ['vpn', 'sstp'] if not conf.exists(base): return None # retrieve common dictionary keys sstp = get_accel_dict(conf, base, sstp_chap_secrets, with_pki=True) if dict_search('client_ip_pool', sstp): # Multiple named pools require ordered values T5099 sstp['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', sstp)) sstp['server_type'] = 'sstp' return sstp def verify(sstp): if not sstp: return None port = sstp.get('port') proto = 'tcp' if check_port_availability('0.0.0.0', int(port), proto) is not True and \ not is_listen_port_bind_service(int(port), 'accel-pppd'): raise ConfigError(f'"{proto}" port "{port}" is used by another service') verify_accel_ppp_base_service(sstp) verify_accel_ppp_ip_pool(sstp) # # SSL certificate checks # if not sstp['pki']: raise ConfigError('PKI is not configured') if 'ssl' not in sstp: raise ConfigError('SSL missing on SSTP config') ssl = sstp['ssl'] # CA if 'ca_certificate' not in ssl: raise ConfigError('SSL CA certificate missing on SSTP config') ca_name = ssl['ca_certificate'] if ca_name not in sstp['pki']['ca']: raise ConfigError('Invalid CA certificate on SSTP config') if 'certificate' not in sstp['pki']['ca'][ca_name]: raise ConfigError('Missing certificate data for CA certificate on SSTP config') # Certificate if 'certificate' not in ssl: raise ConfigError('SSL certificate missing on SSTP config') cert_name = ssl['certificate'] if cert_name not in sstp['pki']['certificate']: raise ConfigError('Invalid certificate on SSTP config') pki_cert = sstp['pki']['certificate'][cert_name] if 'certificate' not in pki_cert: raise ConfigError('Missing certificate data for certificate on SSTP config') if 'private' not in pki_cert or 'key' not in pki_cert['private']: raise ConfigError('Missing private key for certificate on SSTP config') if 'password_protected' in pki_cert['private']: raise ConfigError('Encrypted private key is not supported on SSTP config') def generate(sstp): if not sstp: return None # accel-cmd reload doesn't work so any change results in a restart of the daemon render(sstp_conf, 'accel-ppp/sstp.config.j2', sstp) cert_name = sstp['ssl']['certificate'] pki_cert = sstp['pki']['certificate'][cert_name] ca_cert_name = sstp['ssl']['ca_certificate'] pki_ca = sstp['pki']['ca'][ca_cert_name] write_file(cert_file_path, wrap_certificate(pki_cert['certificate'])) write_file(cert_key_path, wrap_private_key(pki_cert['private']['key'])) write_file(ca_cert_file_path, wrap_certificate(pki_ca['certificate'])) if dict_search('authentication.mode', sstp) == 'local': render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2', sstp, permission=0o640) else: if os.path.exists(sstp_chap_secrets): os.unlink(sstp_chap_secrets) return sstp def apply(sstp): if not sstp: call('systemctl stop accel-ppp@sstp.service') for file in [sstp_chap_secrets, sstp_conf]: if os.path.exists(file): os.unlink(file) return None call('systemctl restart accel-ppp@sstp.service') 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/migration-scripts/l2tp/7-to-8 b/src/migration-scripts/l2tp/7-to-8 new file mode 100755 index 000000000..4956e1155 --- /dev/null +++ b/src/migration-scripts/l2tp/7-to-8 @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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/>. + +# Migrate from 'ccp-disable' to 'ppp-options.disable-ccp' +# Migration ipv6 options + +import os + +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() + +config = ConfigTree(config_file) +base = ['vpn', 'l2tp', 'remote-access'] +if not config.exists(base): + exit(0) + +#CCP migration +if config.exists(base + ['ccp-disable']): + config.delete(base + ['ccp-disable']) + config.set(base + ['ppp-options', 'disable-ccp']) + +#IPV6 options migrations +if config.exists(base + ['ppp-options','ipv6-peer-intf-id']): + intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id']) + if intf_peer_id == 'ipv4': + intf_peer_id = 'ipv4-addr' + config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True) + config.delete(base + ['ppp-options','ipv6-peer-intf-id']) + +if config.exists(base + ['ppp-options','ipv6-intf-id']): + intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id']) + config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True) + config.delete(base + ['ppp-options','ipv6-intf-id']) + +if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']): + config.set(base + ['ppp-options','ipv6-accept-peer-interface-id']) + config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id']) + +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/pppoe-server/8-to-9 b/src/migration-scripts/pppoe-server/8-to-9 new file mode 100755 index 000000000..ad75c28a1 --- /dev/null +++ b/src/migration-scripts/pppoe-server/8-to-9 @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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/>. + +# Change from 'ccp' to 'disable-ccp' in ppp-option section +# Migration ipv6 options + +import os + +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() + +config = ConfigTree(config_file) +base = ['service', 'pppoe-server'] +if not config.exists(base): + exit(0) + +#CCP migration +if config.exists(base + ['ppp-options', 'ccp']): + config.delete(base + ['ppp-options', 'ccp']) +else: + config.set(base + ['ppp-options', 'disable-ccp']) + +#IPV6 options migrations +if config.exists(base + ['ppp-options','ipv6-peer-intf-id']): + intf_peer_id = config.return_value(base + ['ppp-options','ipv6-peer-intf-id']) + if intf_peer_id == 'ipv4': + intf_peer_id = 'ipv4-addr' + config.set(base + ['ppp-options','ipv6-peer-interface-id'], value=intf_peer_id, replace=True) + config.delete(base + ['ppp-options','ipv6-peer-intf-id']) + +if config.exists(base + ['ppp-options','ipv6-intf-id']): + intf_id = config.return_value(base + ['ppp-options','ipv6-intf-id']) + config.set(base + ['ppp-options','ipv6-interface-id'], value=intf_id, replace=True) + config.delete(base + ['ppp-options','ipv6-intf-id']) + +if config.exists(base + ['ppp-options','ipv6-accept-peer-intf-id']): + config.set(base + ['ppp-options','ipv6-accept-peer-interface-id']) + config.delete(base + ['ppp-options','ipv6-accept-peer-intf-id']) + +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/pptp/3-to-4 b/src/migration-scripts/pptp/3-to-4 new file mode 100755 index 000000000..0a8dad2f4 --- /dev/null +++ b/src/migration-scripts/pptp/3-to-4 @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 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/>. + +# - Move 'mppe' from 'authentication' node to 'ppp-options' + +import os + +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() + +config = ConfigTree(config_file) +base = ['vpn', 'pptp', 'remote-access'] + +if not config.exists(base): + exit(0) + +if config.exists(base + ['authentication','mppe']): + mppe = config.return_value(base + ['authentication','mppe']) + config.set(base + ['ppp-options', 'mppe'], value=mppe, replace=True) + config.delete(base + ['authentication','mppe']) + +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)