summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/ipoe-server/chap-secrets.tmpl32
-rw-r--r--data/templates/ipoe-server/ipoe.config.tmpl21
-rw-r--r--data/templates/openvpn/client.conf.tmpl21
-rw-r--r--data/templates/openvpn/server.conf.tmpl35
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in100
-rw-r--r--op-mode-definitions/ipoe-server.xml33
-rw-r--r--op-mode-definitions/l2tp-server.xml10
-rw-r--r--op-mode-definitions/pppoe-server.xml20
-rw-r--r--op-mode-definitions/pptp-server.xml10
-rw-r--r--op-mode-definitions/show-ip-bgp.xml342
-rw-r--r--op-mode-definitions/sstp-server.xml6
-rw-r--r--python/vyos/airbag.py5
-rw-r--r--python/vyos/debug.py182
-rw-r--r--python/vyos/ifconfig/control.py9
-rw-r--r--python/vyos/util.py86
-rwxr-xr-xsrc/completion/list_ipoe.py16
-rwxr-xr-xsrc/conf_mode/host_name.py16
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py290
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py7
-rwxr-xr-xsrc/conf_mode/service-ipoe.py93
-rwxr-xr-xsrc/conf_mode/snmp.py20
-rwxr-xr-xsrc/conf_mode/system-login.py8
-rw-r--r--src/etc/systemd/system/pdns-recursor.service.d/override.conf2
-rwxr-xr-xsrc/op_mode/dns_forwarding_reset.py6
-rwxr-xr-xsrc/op_mode/dns_forwarding_statistics.py7
-rwxr-xr-xsrc/op_mode/ipoe-control.py65
-rwxr-xr-xsrc/op_mode/ppp-server-ctrl.py71
-rwxr-xr-xsrc/op_mode/version.py4
-rwxr-xr-xsrc/system/on-dhcp-event.sh2
-rwxr-xr-xsrc/validators/ipv63
30 files changed, 1213 insertions, 309 deletions
diff --git a/data/templates/ipoe-server/chap-secrets.tmpl b/data/templates/ipoe-server/chap-secrets.tmpl
index 707718e94..5e35d5775 100644
--- a/data/templates/ipoe-server/chap-secrets.tmpl
+++ b/data/templates/ipoe-server/chap-secrets.tmpl
@@ -1,18 +1,18 @@
# username server password acceptable local IP addresses shaper
-{% for aifc in auth['auth_if'] %}
-{% for mac in auth['auth_if'][aifc] %}
-{% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %}
+{% for aifc in auth['auth_if'] -%}
+{% for mac in auth['auth_if'][aifc] -%}
+{% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) -%}
+{% if auth['auth_if'][aifc][mac]['vlan'] -%}
+{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}} * {{mac.lower()}} * {{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
+{% else -%}
+{{aifc}} * {{mac.lower()}} * {{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
+{% endif -%}
+{% else -%}
{% if auth['auth_if'][aifc][mac]['vlan'] %}
-{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
-{% else %}
-{{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}}
-{% endif %}
-{% else %}
-{% if auth['auth_if'][aifc][mac]['vlan'] %}
-{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*
-{% else %}
-{{aifc}}\t*\t{{mac.lower()}}\t*
-{% endif %}
-{% endif %}
-{% endfor %}
-{% endfor %}
+{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}} * {{mac.lower()}} *
+{% else -%}
+{{aifc}} * {{mac.lower()}} *
+{% endif -%}
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
diff --git a/data/templates/ipoe-server/ipoe.config.tmpl b/data/templates/ipoe-server/ipoe.config.tmpl
index 109bc0d92..0a5ee09a6 100644
--- a/data/templates/ipoe-server/ipoe.config.tmpl
+++ b/data/templates/ipoe-server/ipoe.config.tmpl
@@ -26,16 +26,7 @@ level=5
verbose=1
{% for intfc in interfaces %}
{% if interfaces[intfc]['vlan_mon'] %}
-interface=re:{{intfc}}\.\d+,\
-{% else %}
-interface={{intfc}},\
-{% endif %}
-shared={{interfaces[intfc]['shared']}},\
-mode={{interfaces[intfc]['mode']}},\
-ifcfg={{interfaces[intfc]['ifcfg']}},\
-range={{interfaces[intfc]['range']}},\
-start={{interfaces[intfc]['sess_start']}},\
-ipv6=1
+interface=re:{{intfc}}\.\d+,{% else %}interface={{intfc}},{% endif %}shared={{interfaces[intfc]['shared']}},mode={{interfaces[intfc]['mode']}},ifcfg={{interfaces[intfc]['ifcfg']}},range={{interfaces[intfc]['range']}},start={{interfaces[intfc]['sess_start']}},ipv6=1
{% endfor %}
{% if auth['mech'] == 'noauth' %}
noauth=1
@@ -86,20 +77,20 @@ delegate={{pd}}
{% if auth['mech'] == 'local' %}
[chap-secrets]
-chap-secrets=/etc/accel-ppp/ipoe/chap-secrets
+chap-secrets={{chap_secrets_file}}
{% endif %}
{% if auth['mech'] == 'radius' %}
[radius]
verbose=1
{% for srv in auth['radius'] %}
-server={{srv}},{{auth['radius'][srv]['secret']}},\
-req-limit={{auth['radius'][srv]['req-limit']}},\
+server={{srv}},{{auth['radius'][srv]['secret']}},
+req-limit={{auth['radius'][srv]['req-limit']}},
fail-time={{auth['radius'][srv]['fail-time']}}
{% endfor %}
{% if auth['radsettings']['dae-server']['ip-address'] %}
-dae-server={{auth['radsettings']['dae-server']['ip-address']}}:\
-{{auth['radsettings']['dae-server']['port']}},\
+dae-server={{auth['radsettings']['dae-server']['ip-address']}}:
+{{auth['radsettings']['dae-server']['port']}},
{{auth['radsettings']['dae-server']['secret']}}
{% endif -%}
{% if auth['radsettings']['acct-timeout'] %}
diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl
index 3099f2ca7..508d8da94 100644
--- a/data/templates/openvpn/client.conf.tmpl
+++ b/data/templates/openvpn/client.conf.tmpl
@@ -1,8 +1,9 @@
### Autogenerated by interfaces-openvpn.py ###
{% if ip -%}
-ifconfig-push {{ ip }} {{ remote_netmask }}
+ifconfig-push {{ ip[0] }} {{ remote_netmask }}
{% endif -%}
+
{% for route in push_route -%}
push "route {{ route }}"
{% endfor -%}
@@ -11,6 +12,24 @@ push "route {{ route }}"
iroute {{ net }}
{% endfor -%}
+{# ipv6_remote is only set when IPv6 server is enabled #}
+{% if ipv6_remote -%}
+# IPv6
+
+{%- if ipv6_ip %}
+ifconfig-ipv6-push {{ ipv6_ip[0] }} {{ ipv6_remote }}
+{%- endif %}
+
+{%- for route6 in ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for net6 in ipv6_subnet %}
+iroute {{ net6 }}
+{%- endfor %}
+
+{% endif -%}
+
{% if disable -%}
disable
{% endif -%}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index a9dacd36e..0f563dc2b 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -18,7 +18,7 @@ dev {{ intf }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
-proto {% if 'tcp-active' in protocol -%}tcp-client{% elif 'tcp-passive' in protocol -%}tcp-server{% else %}udp{% endif %}
+proto {% if 'tcp-active' in protocol -%}tcp6-client{% elif 'tcp-passive' in protocol -%}tcp6-server{% else %}udp6{% endif %}
{%- if local_host %}
local {{ local_host }}
@@ -78,10 +78,10 @@ topology {% if server_topology == 'point-to-point' %}p2p{% else %}{{ server_topo
mode server
tls-server
{%- else %}
-server {{ server_subnet }}{% if server_pool_start %} nopool{% endif %}
+server {{ server_subnet[0] }} nopool
{%- endif %}
-{%- if server_pool_start %}
+{%- if server_pool %}
ifconfig-pool {{ server_pool_start }} {{ server_pool_stop }}{% if server_pool_netmask %} {{ server_pool_netmask }}{% endif %}
{%- endif %}
@@ -110,7 +110,26 @@ push "dhcp-option DNS {{ ns }}"
{%- if server_domain -%}
push "dhcp-option DOMAIN {{ server_domain }}"
-{% endif %}
+{% endif -%}
+
+{%- if server_ipv6_local %}
+# IPv6
+push "tun-ipv6"
+ifconfig-ipv6 {{ server_ipv6_local }}/{{ server_ipv6_prefixlen }} {{ server_ipv6_remote }}
+
+{%- if server_ipv6_pool %}
+ifconfig-ipv6-pool {{ server_ipv6_pool_base }}/{{ server_ipv6_pool_prefixlen }}
+{%- endif %}
+
+{%- for route6 in server_ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for ns6 in server_ipv6_dns_nameserver %}
+push "dhcp-option DNS6 {{ ns6 }}"
+{%- endfor %}
+
+{%- endif %}
{% else -%}
#
@@ -120,9 +139,13 @@ ping {{ ping_interval }}
ping-restart {{ ping_restart }}
{% if local_address_subnet -%}
-ifconfig {{ local_address }} {{ local_address_subnet }}
+ifconfig {{ local_address[0] }} {{ local_address_subnet }}
{%- elif remote_address -%}
-ifconfig {{ local_address }} {{ remote_address }}
+ifconfig {{ local_address[0] }} {{ remote_address[0] }}
+{%- endif %}
+
+{% if ipv6_local_address -%}
+ifconfig-ipv6 {{ ipv6_local_address[0] }} {{ ipv6_remote_address[0] }}
{%- endif %}
{% endif -%}
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index d926876f7..b5da8cf76 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -233,15 +233,15 @@
</node>
<tagNode name="local-address">
<properties>
- <help>Local IP address of tunnel</help>
+ <help>Local IP address of tunnel (IPv4 or IPv6)</help>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
</properties>
<children>
<leafNode name="subnet-mask">
<properties>
- <help>Subnet-mask for local IP address of tunnel</help>
+ <help>Subnet-mask for local IP address of tunnel (IPv4 only)</help>
<constraint>
<validator name="ipv4-address"/>
</constraint>
@@ -256,8 +256,12 @@
<format>ipv4</format>
<description>Local IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Local IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
</properties>
</leafNode>
@@ -341,9 +345,14 @@
<format>ipv4</format>
<description>Remote end IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Remote end IPv6 address</description>
+ </valueHelp>
<constraint>
<validator name="ipv4-address"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="remote-host">
@@ -351,7 +360,11 @@
<help>Remote host to connect to (dynamic if not set)</help>
<valueHelp>
<format>ipv4</format>
- <description>IP address of remote host</description>
+ <description>IPv4 address of remote host</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of remote host</description>
</valueHelp>
<valueHelp>
<format>txt</format>
@@ -411,9 +424,14 @@
<format>ipv4</format>
<description>Client IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Client IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="push-route">
@@ -423,21 +441,29 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="subnet">
<properties>
- <help>Subnet belonging to the client</help>
+ <help>Subnet belonging to the client (iroute)</help>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 network and prefix length belonging to the client</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length belonging to the client</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
@@ -446,9 +472,15 @@
</tagNode>
<node name="client-ip-pool">
<properties>
- <help>Pool of client IP addresses</help>
+ <help>Pool of client IPv4 addresses</help>
</properties>
<children>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable client IP pool</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="start">
<properties>
<help>First IP address in the pool</help>
@@ -490,6 +522,31 @@
</leafNode>
</children>
</node>
+ <node name="client-ipv6-pool">
+ <properties>
+ <help>Pool of client IPv6 addresses</help>
+ </properties>
+ <children>
+ <leafNode name="base">
+ <properties>
+ <help>Client IPv6 pool base address with optional prefix length</help>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Client IPv6 pool base address with optional prefix length (defaults: base = server subnet + 0x1000, prefix length = server prefix length)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable client IPv6 pool</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="domain-name">
<properties>
<help>DNS suffix to be pushed to all clients</help>
@@ -518,8 +575,12 @@
<format>ipv4</format>
<description>DNS server IPv4 address</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>DNS server IPv6 address</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-address"/>
+ <validator name="ip-address"/>
</constraint>
<multi/>
</properties>
@@ -531,8 +592,12 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
@@ -549,9 +614,14 @@
<format>ipv4net</format>
<description>IPv4 network and prefix length</description>
</valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 network and prefix length</description>
+ </valueHelp>
<constraint>
- <validator name="ipv4-prefix"/>
+ <validator name="ip-prefix"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="topology">
@@ -562,7 +632,7 @@
</completionHelp>
<valueHelp>
<format>net30</format>
- <description>net30 topology</description>
+ <description>net30 topology (default)</description>
</valueHelp>
<valueHelp>
<format>point-to-point</format>
@@ -573,7 +643,7 @@
<description>Subnet topology</description>
</valueHelp>
<constraint>
- <regex>(subnet|point-to-point)</regex>
+ <regex>(subnet|point-to-point|net30)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/op-mode-definitions/ipoe-server.xml b/op-mode-definitions/ipoe-server.xml
index c05e2d2c1..c20d3aa2a 100644
--- a/op-mode-definitions/ipoe-server.xml
+++ b/op-mode-definitions/ipoe-server.xml
@@ -12,24 +12,33 @@
<help>Clear ipoe-server session</help>
</properties>
<children>
- <leafNode name="username">
+ <tagNode name="username">
<properties>
<help>Clear ipoe-server session by username</help>
<completionHelp>
- <script>/usr/bin/accel-cmd -p 2002 show sessions username | sed -e 's/ \r//g' | tail -n +3</script>
+ <script>${vyos_completion_dir}/list_ipoe.py --selector="username"</script>
</completionHelp>
</properties>
- <command>/usr/bin/accel-cmd -p 2002 terminate username $5</command>
- </leafNode>
- <leafNode name="sid">
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="terminate" --selector="username" --target="$5"</command>
+ </tagNode>
+ <tagNode name="sid">
<properties>
- <help>Clear ipoe-server session by sid</help>
+ <help>Clear ipoe-server session by Session ID</help>
<completionHelp>
- <script>/usr/bin/accel-cmd -p 2002 show sessions sid | sed -e 's/ \r//g' | tail -n +3</script>
+ <script>${vyos_completion_dir}/list_ipoe.py --selector="sid"</script>
</completionHelp>
</properties>
- <command>/usr/bin/accel-cmd -p 2002 terminate sid $5</command>
- </leafNode>
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="terminate" --selector="sid" --target="$5"</command>
+ </tagNode>
+ <tagNode name="interface">
+ <properties>
+ <help>Clear ipoe-server session by interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_ipoe.py --selector="ifname"</script>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="terminate" --selector="if" --target="$5"</command>
+ </tagNode>
</children>
</node>
</children>
@@ -47,13 +56,13 @@
<properties>
<help>Show active IPoE server sessions</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2002 show sessions ifname,username,called-sid,calling-sid,ip,ip6,ip6-dp,rate-limit,state,uptime,sid</command>
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="show_sessions"</command>
</leafNode>
<leafNode name="statistics">
<properties>
<help>Show IPoE server statistics</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2002 show stat</command>
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="show_stat"</command>
</leafNode>
</children>
</node>
@@ -65,7 +74,7 @@
<properties>
<help>show ipoe-server status</help>
</properties>
- <command>if [ -e /var/run/accel_ipoe.pid ]; then /usr/bin/accel-cmd restart -p 2002; else echo "ipoe-server not running"; fi</command>
+ <command>${vyos_op_scripts_dir}/ipoe-control.py --action="restart"</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/l2tp-server.xml b/op-mode-definitions/l2tp-server.xml
index fb1b85ce4..3e96b9365 100644
--- a/op-mode-definitions/l2tp-server.xml
+++ b/op-mode-definitions/l2tp-server.xml
@@ -4,14 +4,20 @@
<children>
<node name="l2tp-server">
<properties>
- <help>show l2tp-server status</help>
+ <help>Show L2TP server information</help>
</properties>
<children>
<leafNode name="sessions">
<properties>
<help>Show active L2TP server sessions</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2004 'show sessions'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="l2tp" --action="show sessions"</command>
+ </leafNode>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show L2TP server statistics</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="l2tp" --action="show stat"</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/pppoe-server.xml b/op-mode-definitions/pppoe-server.xml
index 0293c9502..5ac9d9497 100644
--- a/op-mode-definitions/pppoe-server.xml
+++ b/op-mode-definitions/pppoe-server.xml
@@ -4,26 +4,26 @@
<children>
<node name="pppoe-server">
<properties>
- <help>show pppoe-server status</help>
+ <help>Show pppoe-server status</help>
</properties>
<children>
<leafNode name="sessions">
<properties>
<help>Show active PPPoE server sessions</help>
</properties>
- <command>/usr/bin/accel-cmd 'show sessions ifname,username,ip,ip6,ip6-dp,calling-sid,rate-limit,state,uptime,rx-bytes,tx-bytes'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="show sessions"</command>
</leafNode>
<leafNode name="statistics">
<properties>
<help>Show PPPoE server statistics</help>
</properties>
- <command>/usr/bin/accel-cmd 'show stat'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="show stat"</command>
</leafNode>
<leafNode name="interfaces">
<properties>
<help>Show interfaces where pppoe-server listens on</help>
</properties>
- <command>/usr/bin/accel-cmd 'pppoe interface show'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="pppoe interface show"</command>
</leafNode>
</children>
</node>
@@ -35,7 +35,7 @@
<properties>
<help>Restarts pppoe-server</help>
</properties>
- <command>if [ -e /var/run/accel_pppoe.pid ]; then /usr/bin/accel-cmd restart -p 2001; else echo "pppoe-server not running"; fi</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="restart"</command>
</leafNode>
</children>
</node>
@@ -53,19 +53,19 @@
<properties>
<help>Terminate all pppoe-server users</help>
</properties>
- <command>/usr/bin/accel-cmd terminate all</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="terminate all"</command>
</leafNode>
<tagNode name="interface">
<properties>
<help>Terminate a ppp interface</help>
</properties>
- <command>/usr/bin/accel-cmd terminate if "$4"</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="terminate if $4"</command>
</tagNode>
<tagNode name="username">
<properties>
<help>Terminate specified users</help>
</properties>
- <command>/usr/bin/accel-cmd terminate match username "$4"</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="terminate username $4"</command>
</tagNode>
</children>
</node>
@@ -87,13 +87,13 @@
<properties>
<help>Deny new connections and stop to serve pppoe after disconnect last session</help>
</properties>
- <command>/usr/bin/accel-cmd shutdown soft</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="shutdown soft"</command>
</leafNode>
<leafNode name="cancel">
<properties>
<help>Cancel maintenance mode</help>
</properties>
- <command>/usr/bin/accel-cmd shutdown cancel</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pppoe" --action="shutdown cancel"</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/pptp-server.xml b/op-mode-definitions/pptp-server.xml
index 388063885..59be68611 100644
--- a/op-mode-definitions/pptp-server.xml
+++ b/op-mode-definitions/pptp-server.xml
@@ -4,14 +4,20 @@
<children>
<node name="pptp-server">
<properties>
- <help>show pptp-server status</help>
+ <help>Show PPTP server information</help>
</properties>
<children>
<leafNode name="sessions">
<properties>
<help>Show active PPTP server sessions</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2003 'show sessions'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pptp" --action="show sessions"</command>
+ </leafNode>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show PPTP server statistics</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="pptp" --action="show stat"</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/show-ip-bgp.xml b/op-mode-definitions/show-ip-bgp.xml
new file mode 100644
index 000000000..5eb2ae56e
--- /dev/null
+++ b/op-mode-definitions/show-ip-bgp.xml
@@ -0,0 +1,342 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="ip">
+ <children>
+ <node name="bgp">
+ <properties>
+ <help>Show Border Gateway Protocol (BGP) information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp"</command>
+ <children>
+ <leafNode name="attribute-info">
+ <properties>
+ <help>Show BGP attribute information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp attribute-info"</command>
+ </leafNode>
+ <leafNode name="cidr-only">
+ <properties>
+ <help>Display only routes with non-natural netmasks</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp cidr-only"</command>
+ </leafNode>
+ <node name="community">
+ <properties>
+ <help>Show BGP routes matching the communities</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp community"</command>
+ </node>
+ <tagNode name="community">
+ <properties>
+ <help>Display routes matching the specified communities</help>
+ <completionHelp>
+ <list>&lt;AA:NN&gt; local-AS no-advertise no-export</list>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp community $5"</command>
+ </tagNode>
+ <leafNode name="community-info">
+ <properties>
+ <help>List all bgp community information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp community-info"</command>
+ </leafNode>
+ <tagNode name="community-list">
+ <properties>
+ <help>Show BGP routes matching specified community list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp community-list $5"</command>
+ <children>
+ <leafNode name="exact-match">
+ <properties>
+ <help>Show BGP routes exactly matching specified community list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp community-list $5 exact-match"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="dampened-paths">
+ <properties>
+ <help>Show dampened BGP paths</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp dampening dampened-paths"</command>
+ </leafNode>
+ <tagNode name="filter-list">
+ <properties>
+ <help>Show BGP information for specified word</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp filter-list $5"</command>
+ </tagNode>
+ <leafNode name="flap-statistics">
+ <properties>
+ <help>Show flap statistics of routes</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp dampening flap-statistics"</command>
+ </leafNode>
+ <node name="ipv4">
+ <properties>
+ <help>Show BGP IPv4 information</help>
+ </properties>
+ <children>
+ <node name="unicast">
+ <properties>
+ <help>Show BGP IPv4 unicast information</help>
+ </properties>
+ <children>
+ <leafNode name="cidr-only">
+ <properties>
+ <help>Display only routes with non-natural netmasks</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast cidr-only"</command>
+ </leafNode>
+ <node name="community"> <!-- START new code -->
+ <properties>
+ <help>Show BGP routes matching the communities</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community"</command>
+ </node>
+ <tagNode name="community">
+ <properties>
+ <help>Display routes matching the specified communities</help>
+ <completionHelp>
+ <list>&lt;AA:NN&gt; local-AS no-advertise no-export</list>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community $7"</command>
+ </tagNode>
+ <tagNode name="community-list">
+ <properties>
+ <help>Show BGP routes matching specified community list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community-list $7"</command>
+ <children>
+ <leafNode name="exact-match">
+ <properties>
+ <help>Show BGP routes exactly matching specified community list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast community-list $7 exact-match"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="filter-list">
+ <properties>
+ <help>Show BGP information for specified word</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp filter-list $5"</command>
+ </tagNode>
+ <tagNode name="neighbors">
+ <properties>
+ <help>Show detailed BGP IPv4 unicast neighbor information</help>
+ <completionHelp>
+ <script>vtysh -c "show ip bgp ipv4 unicast summary" | awk '{print $1}' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbors $7"</command>
+ <children>
+ <leafNode name="advertised-routes">
+ <properties>
+ <help>Show routes advertised to a BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 advertised-routes"</command>
+ </leafNode>
+ <leafNode name="prefix-counts">
+ <properties>
+ <help>Show detailed prefix count information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 prefix-counts"</command>
+ </leafNode>
+ <leafNode name="received-routes">
+ <properties>
+ <help>Show the received routes from neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 received-routes"</command>
+ </leafNode>
+ <leafNode name="routes">
+ <properties>
+ <help>Show routes learned from neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast neighbor $7 routes"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="paths">
+ <properties>
+ <help>Show BGP path information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast paths"</command>
+ </leafNode>
+ <tagNode name="prefix-list">
+ <properties>
+ <help>Show BGP routes matching the specified prefix list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast prefix-list $7"</command>
+ </tagNode>
+ <tagNode name="regexp">
+ <properties>
+ <help>Show BGP routes matching the specified AS path regular expression</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp ipv4 unicast regexp $5"</command>
+ </tagNode>
+ <tagNode name="route-map">
+ <properties>
+ <help>Show BGP routes matching the specified route map</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp route-map $5"</command>
+ </tagNode>
+ <leafNode name="summary">
+ <properties>
+ <help>Show summary of BGP information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp summary"</command>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="unicast">
+ <properties>
+ <help>Show BGP information for specified IP address or prefix</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt; &lt;x.x.x.x/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp $6"</command>
+ </tagNode>
+ </children>
+ </node>
+ <node name="large-community">
+ <properties>
+ <help>Show BGP routes matching the specified large-communities</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp large-community"</command>
+ </node>
+ <leafNode name="large-community-info">
+ <properties>
+ <help>Show BGP large-community information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp large-community-info"</command>
+ </leafNode>
+ <tagNode name="large-community-list">
+ <properties>
+ <help>Show BGP routes matching the specified large-community list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp large-community-list $5"</command>
+ </tagNode>
+ <leafNode name="memory">
+ <properties>
+ <help>Show BGP memory usage</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp memory"</command>
+ </leafNode>
+ <tagNode name="neighbors">
+ <properties>
+ <help>Show detailed BGP IPv4 unicast neighbor information</help>
+ <completionHelp>
+ <script>vtysh -c "show ip bgp summary" | awk '{print $1}' | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbors $5"</command>
+ <children>
+ <leafNode name="advertised-routes">
+ <properties>
+ <help>Show routes advertised to a BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 advertised-routes"</command>
+ </leafNode>
+ <leafNode name="dampened-routes">
+ <properties>
+ <help>Show dampened routes received from BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 dampened-routes"</command>
+ </leafNode>
+ <leafNode name="flap-statistics">
+ <properties>
+ <help>Show flap statistics of the routes learned from BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 flap-statistics"</command>
+ </leafNode>
+ <leafNode name="prefix-counts">
+ <properties>
+ <help>Show detailed prefix count information for BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 prefix-counts"</command>
+ </leafNode>
+ <node name="received">
+ <properties>
+ <help>Show information received from BGP neighbor</help>
+ </properties>
+ <children>
+ <leafNode name="prefix-filter">
+ <properties>
+ <help>Show prefixlist filter</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 received prefix-filter"</command>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="received-routes">
+ <properties>
+ <help>Show received routes from BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 received-routes"</command>
+ </leafNode>
+ <leafNode name="routes">
+ <properties>
+ <help>Show routes learned from BGP neighbor</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp neighbor $5 routes"</command>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="paths">
+ <properties>
+ <help>Show BGP path information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp paths"</command>
+ </leafNode>
+ <tagNode name="prefix-list">
+ <properties>
+ <help>Show BGP routes matching the specified prefix list</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp prefix-list $5"</command>
+ </tagNode>
+ <tagNode name="regexp">
+ <properties>
+ <help>Show BGP routes matching the specified AS path regular expression</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp regexp $5"</command>
+ </tagNode>
+ <tagNode name="route-map">
+ <properties>
+ <help>Show BGP routes matching the specified route map</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp route-map $5"</command>
+ </tagNode>
+ <leafNode name="statistics">
+ <properties>
+ <help>Show summary of BGP information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp statistics"</command>
+ </leafNode>
+ <leafNode name="summary">
+ <properties>
+ <help>Show summary of BGP information</help>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp summary"</command>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="bgp">
+ <properties>
+ <help>Show BGP information for specified IP address or prefix</help>
+ <completionHelp>
+ <list>&lt;x.x.x.x&gt; &lt;x.x.x.x/x&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>/usr/bin/vtysh -c "show ip bgp $4"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/sstp-server.xml b/op-mode-definitions/sstp-server.xml
index 36d0b9985..03dfc4262 100644
--- a/op-mode-definitions/sstp-server.xml
+++ b/op-mode-definitions/sstp-server.xml
@@ -4,20 +4,20 @@
<children>
<node name="sstp-server">
<properties>
- <help>show sstp-server status</help>
+ <help>Show SSTP server information</help>
</properties>
<children>
<leafNode name="sessions">
<properties>
<help>Show active SSTP server sessions</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2005 'show sessions ifname,username,ip,ip6,ip6-dp,calling-sid,rate-limit,state,uptime,rx-bytes,tx-bytes'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="sstp" --action="show sessions"</command>
</leafNode>
<leafNode name="statistics">
<properties>
<help>Show SSTP server statistics</help>
</properties>
- <command>/usr/bin/accel-cmd -p 2005 'show stat'</command>
+ <command>${vyos_op_scripts_dir}/ppp-server-ctrl.py --proto="sstp" --action="show stat"</command>
</leafNode>
</children>
</node>
diff --git a/python/vyos/airbag.py b/python/vyos/airbag.py
index 664974d5f..b0565192d 100644
--- a/python/vyos/airbag.py
+++ b/python/vyos/airbag.py
@@ -19,10 +19,10 @@ import logging
import logging.handlers
from datetime import datetime
+from vyos import debug
from vyos.config import Config
from vyos.version import get_version
from vyos.util import run
-from vyos.util import debug
# we allow to disable the extra logging
@@ -77,8 +77,7 @@ def bug_report(dtype, value, trace):
# reach the end of __main__ and was not intercepted
def intercepter(dtype, value, trace):
bug_report(dtype, value, trace)
- # debug returns either '' or 'developer' if debuging is enabled
- if debug('developer'):
+ if debug.enabled('developer'):
import pdb
pdb.pm()
diff --git a/python/vyos/debug.py b/python/vyos/debug.py
new file mode 100644
index 000000000..20090fb85
--- /dev/null
+++ b/python/vyos/debug.py
@@ -0,0 +1,182 @@
+# Copyright 2019 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/>.
+
+import os
+import sys
+
+
+def message(message, flag='', destination=sys.stdout):
+ """
+ print a debug message line on stdout if debugging is enabled for the flag
+ also log it to a file if the flag 'log' is enabled
+
+ message: the message to print
+ flag: which flag must be set for it to print
+ destination: which file like object to write to (default: sys.stdout)
+
+ returns if any message was logged or not
+ """
+ enable = enabled(flag)
+ if enable:
+ destination.write(_format(flag,message))
+
+ # the log flag is special as it logs all the commands
+ # executed to a log
+ logfile = _logfile('log', '/tmp/developer-log')
+ if not logfile:
+ return enable
+
+ try:
+ # at boot the file is created as root:vyattacfg
+ # at runtime the file is created as user:vyattacfg
+ # the default permission are 644
+ mask = os.umask(0o113)
+
+ with open(logfile, 'a') as f:
+ f.write(_format('log', message))
+ finally:
+ os.umask(mask)
+
+ return enable
+
+
+def enabled(flag):
+ """
+ a flag can be set by touching the file in /tmp or /config
+
+ The current flags are:
+ - developer: the code will drop into PBD on un-handled exception
+ - log: the code will log all command to a file
+ - ifconfig: when modifying an interface,
+ prints command with result and sysfs access on stdout for interface
+ - command: print command run with result
+
+ Having the flag setup on the filesystem is required to have
+ debuging at boot time, however, setting the flag via environment
+ does not require a seek to the filesystem and is more efficient
+ it can be done on the shell on via .bashrc for the user
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns either the file or environment name used to set it up
+ """
+
+ # this is to force all new flags to be registered here to be
+ # documented both here and a reminder to update readthedocs :-)
+ if flag not in ['developer', 'log', 'ifconfig', 'command']:
+ return ''
+
+ return _fromenv(flag) or _fromfile(flag)
+
+
+def _format(flag, message):
+ """
+ format a log message
+ """
+ return f'DEBUG/{flag.upper():<7} {message}\n'
+
+
+def _fromenv(flag):
+ """
+ check if debugging is set for this flag via environment
+
+ For a given debug flag named "test"
+ The presence of the environment VYOS_TEST_DEBUG (uppercase) enables it
+
+ return empty string if not
+ return content of env value it is
+ """
+
+ flagname = f'VYOS_{flag.upper()}_DEBUG'
+ flagenv = os.environ.get(flagname, None)
+
+ if flagenv is None:
+ return ''
+ return flagenv
+
+
+def _fromfile(flag):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if os.path.isfile(flagfile):
+ return flagfile
+
+ return ''
+
+
+def _contentenv(flag):
+ return os.environ.get(f'VYOS_{flag.upper()}_DEBUG', '').strip()
+
+
+def _contentfile(flag):
+ """
+ Check if debug exist for a given debug flag name
+
+ Check is a debug flag was set by the user. the flag can be set either:
+ - in /tmp for a non-persistent presence between reboot
+ - in /config for always on (an existence at boot time)
+
+ For a given debug flag named "test"
+ The presence of the file vyos.test.debug (all lowercase) enables it
+
+ The function returns an empty string if the flag was not set otherwise
+ the function returns the full flagname
+ """
+
+ for folder in ('/tmp', '/config'):
+ flagfile = f'{folder}/vyos.{flag}.debug'
+ if not os.path.isfile(flagfile):
+ continue
+ with open(flagfile) as f:
+ return f.readline().strip()
+
+ return ''
+
+
+def _logfile(flag, default):
+ """
+ return the name of the file to use for logging when the flag 'log' is set
+ if it could not be established or the location is invalid it returns
+ an empty string
+ """
+
+ # For log we return the location of the log file
+ log_location = _contentenv(flag) or _contentfile(flag)
+
+ # it was not set
+ if not log_location:
+ return ''
+
+ # Make sure that the logs can only be in /tmp, /var/log, or /tmp
+ if not log_location.startswith('/tmp/') and \
+ not log_location.startswith('/config/') and \
+ not log_location.startswith('/var/log/'):
+ return default
+ if '..' in log_location:
+ return default
+ return log_location
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index 464cd585e..7bb63beed 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -16,8 +16,9 @@
import os
-from vyos.util import debug, debug_msg
-from vyos.util import popen, cmd
+from vyos import debug
+from vyos.util import popen
+from vyos.util import cmd
from vyos.ifconfig.section import Section
@@ -35,10 +36,10 @@ class Control(Section):
# if debug is not explicitely disabled the the config, enable it
self.debug = ''
if kargs.get('debug', True):
- self.debug = debug('ifconfig')
+ self.debug = debug.enabled('ifconfig')
def _debug_msg (self, message):
- return debug_msg(message, self.debug)
+ return debug.message(message, self.debug)
def _popen(self, command):
return popen(command, self.debug)
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 14020e2d9..eb78c4a26 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -14,61 +14,16 @@
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
-import sys
-from subprocess import Popen
-from subprocess import PIPE
-from subprocess import STDOUT
-from subprocess import DEVNULL
-
-
-def debug(flag):
- """
- Check is a debug flag was set by the user.
- a flag can be set by touching the file /tmp/vyos.flag.debug
- with flag being the flag name, the current flags are:
- - developer: the code will drop into PBD on un-handled exception
- - ifconfig: prints command and sysfs access on stdout for interface
- The function returns an empty string if the flag was not set,
- """
-
- # this is to force all new flags to be registered here to be documented:
- if flag not in ['developer', 'ifconfig']:
- return ''
- for folder in ('/tmp', '/config'):
- if os.path.isfile(f'{folder}/vyos.{flag}.debug'):
- return flag
- return ''
-
-
-def debug_msg(message, flag=''):
- """
- print a debug message line on stdout if debugging is enabled for the flag
- """
-
- if debug(flag):
- print(f'DEBUG/{flag:<6} {message}')
-
- if not debug('developer'):
- return
-
- logfile = '/tmp/full-log'
- existed = os.path.exists(logfile)
-
- with open(logfile, 'a') as f:
- f.write(f'DEBUG/{flag:<6} {message}\n')
- if not existed:
- # at boot the file is created as root:vyattacfg
- # at runtime the file is created as user:vyattacfg
- # do not use run/cmd to not have a recursive call to this code
- os.system(f'chmod g+w {logfile}')
+#
+# NOTE: Do not import full classes here, move your import to the function
+# where it is used so it is as local as possible to the execution
+#
# There is many (too many) ways to run command with python
# os.system, subprocess.Popen, subproces.{run,call,check_output}
# which all have slighty different behaviour
-
-
+from subprocess import Popen, PIPE, STDOUT, DEVNULL
def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
stdout=PIPE, stderr=None, decode=None):
"""
@@ -98,7 +53,14 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
to get both stdout, and stderr: popen('command', stdout=PIPE, stderr=STDOUT)
to discard stdout and get stderr: popen('command', stdout=DEVNUL, stderr=PIPE)
"""
- debug_msg(f"cmd '{command}'", flag)
+ from vyos import debug
+ # log if the flag is set, otherwise log if command is set
+ if not debug.enabled(flag):
+ flag = 'command'
+
+ cmd_msg = f"cmd '{command}'"
+ debug.message(cmd_msg, flag)
+
use_shell = shell
stdin = None
if shell is None:
@@ -129,7 +91,8 @@ def popen(command, flag='', shell=None, input=None, timeout=None, env=None,
nl = '\n' if decoded1 and decoded2 else ''
decoded = decoded1 + nl + decoded2
if decoded:
- debug_msg(f"returned:\n{decoded}", flag)
+ ret_msg = f"returned:\n{decoded}"
+ debug.message(ret_msg, flag)
return decoded, p.returncode
@@ -262,6 +225,7 @@ def colon_separated_to_dict(data_string, uniquekeys=False):
If uniquekeys=True, then dict entries are always strings,
otherwise they are always lists of strings.
"""
+ import re
key_value_re = re.compile('([^:]+)\s*\:\s*(.*)')
data_raw = re.split('\n', data_string)
@@ -301,6 +265,17 @@ def process_running(pid_file):
return pid_exists(int(pid))
+def process_named_running(name):
+ """ Checks if process with given name is running and returns its PID.
+ If Process is not running, return None
+ """
+ from psutil import process_iter
+ for p in process_iter():
+ if name in p.name():
+ return p.pid
+ return None
+
+
def seconds_to_human(s, separator=""):
""" Converts number of seconds passed to a human-readable
interval such as 1w4d18h35m59s
@@ -350,6 +325,7 @@ def get_cfg_group_id():
def file_is_persistent(path):
+ import re
if not re.match(r'^(/config|/opt/vyatta/etc/config)', os.path.dirname(path)):
warning = "Warning: file {0} is outside the /config directory\n".format(path)
warning += "It will not be automatically migrated to a new image on system update"
@@ -406,9 +382,10 @@ def wait_for_commit_lock():
def ask_yes_no(question, default=False) -> bool:
"""Ask a yes/no question via input() and return their answer."""
+ from sys import stdout
default_msg = "[Y/n]" if default else "[y/N]"
while True:
- sys.stdout.write("%s %s " % (question, default_msg))
+ stdout.write("%s %s " % (question, default_msg))
c = input().lower()
if c == '':
return default
@@ -417,7 +394,7 @@ def ask_yes_no(question, default=False) -> bool:
elif c in ("n", "no"):
return False
else:
- sys.stdout.write("Please respond with yes/y or no/n\n")
+ stdout.write("Please respond with yes/y or no/n\n")
def is_admin() -> bool:
@@ -435,6 +412,7 @@ def mac2eui64(mac, prefix=None):
IPv6 address.
Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
"""
+ import re
from ipaddress import ip_network
# http://tools.ietf.org/html/rfc4291#section-2.5.1
eui64 = re.sub(r'[.:-]', '', mac).lower()
diff --git a/src/completion/list_ipoe.py b/src/completion/list_ipoe.py
new file mode 100755
index 000000000..c386b46a2
--- /dev/null
+++ b/src/completion/list_ipoe.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+
+import argparse
+from vyos.util import popen
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--selector', help='Selector: username|ifname|sid', required=True)
+ args = parser.parse_args()
+
+ output, err = popen("accel-cmd -p 2002 show sessions {0}".format(args.selector))
+ if not err:
+ res = output.split("\r\n")
+ # Delete header from list
+ del res[:2]
+ print(' '.join(res))
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index dd5819f9f..a669580ae 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
"""
conf-mode script for 'system host-name' and 'system domain-name'.
@@ -33,10 +31,7 @@ import vyos.hostsd_client
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import cmd
-from vyos.util import call
-from vyos.util import run
-
+from vyos.util import cmd, call, run, process_named_running
default_config_data = {
'hostname': 'vyos',
@@ -166,12 +161,11 @@ def apply(config):
call("systemctl restart rsyslog.service")
# If SNMP is running, restart it too
- ret = run("pgrep snmpd")
- if ret == 0:
- call("systemctl restart snmpd.service")
+ if process_named_running('snmpd'):
+ call('systemctl restart snmpd.service')
# restart pdns if it is used
- ret = run('/usr/bin/rec_control ping')
+ ret = run('/usr/bin/rec_control --socket-dir=/run/powerdns ping')
if ret == 0:
call('systemctl restart pdns-recursor.service')
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index b42765586..836deb64b 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -19,7 +19,7 @@ import re
from copy import deepcopy
from sys import exit,stderr
-from ipaddress import IPv4Address,IPv4Network,summarize_address_range
+from ipaddress import ip_address,ip_network,IPv4Address,IPv4Network,IPv6Address,IPv6Network,summarize_address_range
from netifaces import interfaces
from time import sleep
from shutil import rmtree
@@ -53,9 +53,11 @@ default_config_data = {
'ipv6_eui64_prefix': '',
'ipv6_forwarding': 1,
'ipv6_dup_addr_detect': 1,
+ 'ipv6_local_address': [],
+ 'ipv6_remote_address': [],
'ping_restart': '60',
'ping_interval': '10',
- 'local_address': '',
+ 'local_address': [],
'local_address_subnet': '',
'local_host': '',
'local_port': '',
@@ -65,21 +67,30 @@ default_config_data = {
'persistent_tunnel': False,
'protocol': 'udp',
'redirect_gateway': '',
- 'remote_address': '',
+ 'remote_address': [],
'remote_host': [],
'remote_port': '',
'client': [],
'server_domain': '',
'server_max_conn': '',
'server_dns_nameserver': [],
- 'server_pool': False,
+ 'server_pool': True,
'server_pool_start': '',
'server_pool_stop': '',
'server_pool_netmask': '',
'server_push_route': [],
'server_reject_unconfigured': False,
- 'server_subnet': '',
- 'server_topology': 'net30',
+ 'server_subnet': [],
+ 'server_topology': '',
+ 'server_ipv6_dns_nameserver': [],
+ 'server_ipv6_local': '',
+ 'server_ipv6_prefixlen': '',
+ 'server_ipv6_remote': '',
+ 'server_ipv6_pool': True,
+ 'server_ipv6_pool_base': '',
+ 'server_ipv6_pool_prefixlen': '',
+ 'server_ipv6_push_route': [],
+ 'server_ipv6_subnet': [],
'shared_secret_file': '',
'tls': False,
'tls_auth': '',
@@ -119,18 +130,14 @@ def checkCertHeader(header, filename):
def getDefaultServer(network, topology, devtype):
"""
- Gets the default server parameters for a "server" directive.
- Currently only IPv4 routed but may be extended to support bridged and/or IPv6 in the future.
+ Gets the default server parameters for a IPv4 "server" directive.
Logic from openvpn's src/openvpn/helper.c.
Returns a dict with addresses or False if the input parameters were incorrect.
"""
- if not (topology and devtype):
- return False
-
if not (devtype == 'tun' or devtype == 'tap'):
return False
- if not network.prefixlen:
+ if not network.version == 4:
return False
elif (devtype == 'tun' and network.prefixlen > 29) or (devtype == 'tap' and network.prefixlen > 30):
return False
@@ -198,6 +205,11 @@ def get_config():
if intf == openvpn['intf']:
openvpn['bridge_member'].append(intf)
+ # bridged server should not have a pool by default (but can be specified manually)
+ if openvpn['bridge_member']:
+ openvpn['server_pool'] = False
+ openvpn['server_ipv6_pool'] = False
+
# set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'])
@@ -275,9 +287,15 @@ def get_config():
# Local IP address of tunnel - even as it is a tag node - we can only work
# on the first address
if conf.exists('local-address'):
- openvpn['local_address'] = conf.list_nodes('local-address')[0]
- if conf.exists('local-address {} subnet-mask'.format(openvpn['local_address'])):
- openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(openvpn['local_address']))
+ for tmp in conf.list_nodes('local-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['local_address'].append(tmp)
+ if conf.exists('local-address {} subnet-mask'.format(tmp)):
+ openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(tmp))
+ elif tmp_ip.version == 6:
+ # input IPv6 address could be expanded so get the compressed version
+ openvpn['ipv6_local_address'].append(str(tmp_ip))
# Local IP address to accept connections
if conf.exists('local-host'):
@@ -321,7 +339,12 @@ def get_config():
# IP address of remote end of tunnel
if conf.exists('remote-address'):
- openvpn['remote_address'] = conf.return_value('remote-address')
+ for tmp in conf.return_values('remote-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['remote_address'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['ipv6_remote_address'].append(str(tmp_ip))
# Remote host to connect to (dynamic if not set)
if conf.exists('remote-host'):
@@ -345,11 +368,18 @@ def get_config():
openvpn['server_topology'] = conf.return_value('server topology')
# Server-mode subnet (from which client IPs are allocated)
+ server_network_v4 = None
+ server_network_v6 = None
if conf.exists('server subnet'):
- # server_network is used later in this function
- server_network = IPv4Network(conf.return_value('server subnet'))
- # convert the network in format: "192.0.2.0 255.255.255.0" for later use in template
- openvpn['server_subnet'] = server_network.with_netmask.replace(r'/', ' ')
+ for tmp in conf.return_values('server subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ server_network_v4 = tmp_ip
+ # convert the network to format: "192.0.2.0 255.255.255.0" for later use in template
+ openvpn['server_subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ server_network_v6 = tmp_ip
+ openvpn['server_ipv6_subnet'].append(str(tmp_ip))
# Client-specific settings
for client in conf.list_nodes('server client'):
@@ -358,7 +388,11 @@ def get_config():
data = {
'name': client,
'disable': False,
- 'ip': '',
+ 'ip': [],
+ 'ipv6_ip': [],
+ 'ipv6_remote': '',
+ 'ipv6_push_route': [],
+ 'ipv6_subnet': [],
'push_route': [],
'subnet': [],
'remote_netmask': ''
@@ -369,16 +403,28 @@ def get_config():
data['disable'] = True
# IP address of the client
- if conf.exists('ip'):
- data['ip'] = conf.return_value('ip')
+ for tmp in conf.return_values('ip'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ data['ip'].append(tmp)
+ elif tmp_ip.version == 6:
+ data['ipv6_ip'].append(str(tmp_ip))
# Route to be pushed to the client
- for network in conf.return_values('push-route'):
- data['push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_push_route'].append(str(tmp_ip))
# Subnet belonging to the client
- for network in conf.return_values('subnet'):
- data['subnet'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_subnet'].append(str(tmp_ip))
# Append to global client list
openvpn['client'].append(data)
@@ -388,16 +434,34 @@ def get_config():
# Server client IP pool
if conf.exists('server client-ip-pool'):
- openvpn['server_pool'] = True
+ conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ip-pool')
+
+ # enable or disable server_pool where necessary
+ # default is enabled, or disabled in bridge mode
+ openvpn['server_pool'] = not conf.exists('disable')
- if conf.exists('server client-ip-pool start'):
- openvpn['server_pool_start'] = conf.return_value('server client-ip-pool start')
+ if conf.exists('start'):
+ openvpn['server_pool_start'] = conf.return_value('start')
- if conf.exists('server client-ip-pool stop'):
- openvpn['server_pool_stop'] = conf.return_value('server client-ip-pool stop')
+ if conf.exists('stop'):
+ openvpn['server_pool_stop'] = conf.return_value('stop')
- if conf.exists('server client-ip-pool netmask'):
- openvpn['server_pool_netmask'] = conf.return_value('server client-ip-pool netmask')
+ if conf.exists('netmask'):
+ openvpn['server_pool_netmask'] = conf.return_value('netmask')
+
+ conf.set_level('interfaces openvpn ' + openvpn['intf'])
+
+ # Server client IPv6 pool
+ if conf.exists('server client-ipv6-pool'):
+ conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ipv6-pool')
+ openvpn['server_ipv6_pool'] = not conf.exists('disable')
+ if conf.exists('base'):
+ tmp = conf.return_value('base').split('/')
+ openvpn['server_ipv6_pool_base'] = str(IPv6Address(tmp[0]))
+ if 1 < len(tmp):
+ openvpn['server_ipv6_pool_prefixlen'] = tmp[1]
+
+ conf.set_level('interfaces openvpn ' + openvpn['intf'])
# DNS suffix to be pushed to all clients
if conf.exists('server domain-name'):
@@ -409,12 +473,21 @@ def get_config():
# Domain Name Server (DNS)
if conf.exists('server name-server'):
- openvpn['server_dns_nameserver'] = conf.return_values('server name-server')
+ for tmp in conf.return_values('server name-server'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_dns_nameserver'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_dns_nameserver'].append(str(tmp_ip))
# Route to be pushed to all clients
if conf.exists('server push-route'):
- for network in conf.return_values('server push-route'):
- openvpn['server_push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('server push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_push_route'].append(str(tmp_ip))
# Reject connections from clients that are not explicitly configured
if conf.exists('server reject-unconfigured-clients'):
@@ -476,25 +549,50 @@ def get_config():
if not openvpn['tls_dh'] and openvpn['tls_key'] and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
openvpn['tls_dh'] = 'none'
+ # set default server topology to net30
+ if openvpn['mode'] == 'server' and not openvpn['server_topology']:
+ openvpn['server_topology'] = 'net30'
+
# Set defaults where necessary.
- # If any of the input parameters are missing or wrong,
+ # If any of the input parameters are wrong,
# this will return False and no defaults will be set.
- default_server = getDefaultServer(server_network, openvpn['server_topology'], openvpn['type'])
- if default_server:
- # server-bridge doesn't require a pool so don't set defaults for it
- if not openvpn['bridge_member']:
- openvpn['server_pool'] = True
- if not openvpn['server_pool_start']:
- openvpn['server_pool_start'] = default_server['pool_start']
+ if server_network_v4 and openvpn['server_topology'] and openvpn['type']:
+ default_server = None
+ default_server = getDefaultServer(server_network_v4, openvpn['server_topology'], openvpn['type'])
+ if default_server:
+ # server-bridge doesn't require a pool so don't set defaults for it
+ if openvpn['server_pool'] and not openvpn['bridge_member']:
+ if not openvpn['server_pool_start']:
+ openvpn['server_pool_start'] = default_server['pool_start']
- if not openvpn['server_pool_stop']:
- openvpn['server_pool_stop'] = default_server['pool_stop']
+ if not openvpn['server_pool_stop']:
+ openvpn['server_pool_stop'] = default_server['pool_stop']
- if not openvpn['server_pool_netmask']:
- openvpn['server_pool_netmask'] = default_server['pool_netmask']
+ if not openvpn['server_pool_netmask']:
+ openvpn['server_pool_netmask'] = default_server['pool_netmask']
+
+ for client in openvpn['client']:
+ client['remote_netmask'] = default_server['client_remote_netmask']
+
+ if server_network_v6:
+ if not openvpn['server_ipv6_local']:
+ openvpn['server_ipv6_local'] = server_network_v6[1]
+ if not openvpn['server_ipv6_prefixlen']:
+ openvpn['server_ipv6_prefixlen'] = server_network_v6.prefixlen
+ if not openvpn['server_ipv6_remote']:
+ openvpn['server_ipv6_remote'] = server_network_v6[2]
+
+ if openvpn['server_ipv6_pool'] and server_network_v6.prefixlen < 112:
+ if not openvpn['server_ipv6_pool_base']:
+ openvpn['server_ipv6_pool_base'] = server_network_v6[0x1000]
+ if not openvpn['server_ipv6_pool_prefixlen']:
+ openvpn['server_ipv6_pool_prefixlen'] = openvpn['server_ipv6_prefixlen']
for client in openvpn['client']:
- client['remote_netmask'] = default_server['client_remote_netmask']
+ client['ipv6_remote'] = openvpn['server_ipv6_local']
+
+ if openvpn['redirect_gateway']:
+ openvpn['redirect_gateway'] += ' ipv6'
return openvpn
@@ -549,26 +647,45 @@ def verify(openvpn):
raise ConfigError('encryption ncp-ciphers cannot be specified in site-to-site mode, only server or client')
if openvpn['mode'] == 'site-to-site' and not openvpn['bridge_member']:
- if not openvpn['local_address']:
+ if not (openvpn['local_address'] or openvpn['ipv6_local_address']):
raise ConfigError('Must specify "local-address" or "bridge member interface"')
+ if len(openvpn['local_address']) > 1 or len(openvpn['ipv6_local_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "local-address"')
+
+ if len(openvpn['remote_address']) > 1 or len(openvpn['ipv6_remote_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "remote-address"')
+
for host in openvpn['remote_host']:
- if host == openvpn['remote_address']:
+ if host in openvpn['remote_address'] or host in openvpn['ipv6_remote_address']:
raise ConfigError('"remote-address" cannot be the same as "remote-host"')
+ if openvpn['local_address'] and not (openvpn['remote_address'] or openvpn['local_address_subnet']):
+ raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"')
+
+ if openvpn['remote_address'] and not openvpn['local_address']:
+ raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
+
+ if openvpn['ipv6_local_address'] and not openvpn['ipv6_remote_address']:
+ raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
+
+ if openvpn['ipv6_remote_address'] and not openvpn['ipv6_local_address']:
+ raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
+
if openvpn['type'] == 'tun':
- if not openvpn['remote_address']:
+ if not (openvpn['remote_address'] or openvpn['ipv6_remote_address']):
raise ConfigError('Must specify "remote-address"')
- if openvpn['local_address'] == openvpn['remote_address']:
+ if ( (openvpn['local_address'] and openvpn['local_address'] == openvpn['remote_address']) or
+ (openvpn['ipv6_local_address'] and openvpn['ipv6_local_address'] == openvpn['ipv6_remote_address']) ):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
- if openvpn['local_address'] == openvpn['local_host']:
+ if openvpn['local_host'] in openvpn['local_address'] or openvpn['local_host'] in openvpn['ipv6_local_address']:
raise ConfigError('"local-address" cannot be the same as "local-host"')
else:
# checks for client-server or site-to-site bridged
- if openvpn['local_address'] or openvpn['remote_address']:
+ if openvpn['local_address'] or openvpn['ipv6_local_address'] or openvpn['remote_address'] or openvpn['ipv6_remote_address']:
raise ConfigError('Cannot specify "local-address" or "remote-address" in client-server or bridge mode')
#
@@ -590,8 +707,15 @@ def verify(openvpn):
if not openvpn['tls_dh'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
+ if len(openvpn['server_subnet']) > 1 or len(openvpn['server_ipv6_subnet']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 server subnet')
+
+ for client in openvpn['client']:
+ if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1:
+ raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP')
+
if openvpn['server_subnet']:
- subnet = IPv4Network(openvpn['server_subnet'].replace(' ', '/'))
+ subnet = IPv4Network(openvpn['server_subnet'][0].replace(' ', '/'))
if openvpn['type'] == 'tun' and subnet.prefixlen > 29:
raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
@@ -599,14 +723,13 @@ def verify(openvpn):
raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
for client in openvpn['client']:
- if client['ip'] and not IPv4Address(client['ip']) in subnet:
- raise ConfigError(f'Client IP "{client["ip"]}" not in server subnet "{subnet}"')
+ if client['ip'] and not IPv4Address(client['ip'][0]) in subnet:
+ raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
else:
if not openvpn['bridge_member']:
raise ConfigError('Must specify "server subnet" or "bridge member interface" in server mode')
-
if openvpn['server_pool']:
if not (openvpn['server_pool_start'] and openvpn['server_pool_stop']):
raise ConfigError('Server client-ip-pool requires both start and stop addresses in bridged mode')
@@ -615,17 +738,56 @@ def verify(openvpn):
v4PoolStop = IPv4Address(openvpn['server_pool_stop'])
if v4PoolStart > v4PoolStop:
raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
- if (int(v4PoolStop) - int(v4PoolStart) >= 65536):
- raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop}], maximum is 65536 addresses.')
+
+ v4PoolSize = int(v4PoolStop) - int(v4PoolStart)
+ if v4PoolSize >= 65536:
+ raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.')
v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop))
for client in openvpn['client']:
if client['ip']:
for v4PoolNet in v4PoolNets:
- if IPv4Address(client['ip']) in v4PoolNet:
- print(f'Warning: Client "{client["name"]}" IP {client["ip"]} is in server IP pool, it is not reserved for this client.',
+ if IPv4Address(client['ip'][0]) in v4PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.',
file=stderr)
+ if openvpn['server_ipv6_subnet']:
+ if not openvpn['server_subnet']:
+ raise ConfigError('IPv6 server requires an IPv4 server subnet')
+
+ if openvpn['server_ipv6_pool']:
+ if not openvpn['server_pool']:
+ raise ConfigError('IPv6 server pool requires an IPv4 server pool')
+
+ if int(openvpn['server_ipv6_pool_prefixlen']) >= 112:
+ raise ConfigError('IPv6 server pool must be larger than /112')
+
+ v6PoolStart = IPv6Address(openvpn['server_ipv6_pool_base'])
+ v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple
+ v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536
+ if v6PoolSize < v4PoolSize:
+ raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})')
+
+ v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop))
+ for client in openvpn['client']:
+ if client['ipv6_ip']:
+ for v6PoolNet in v6PoolNets:
+ if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.',
+ file=stderr)
+
+ else:
+ if openvpn['server_ipv6_push_route']:
+ raise ConfigError('IPv6 push-route requires an IPv6 server subnet')
+
+ for client in openvpn ['client']:
+ if client['ipv6_ip']:
+ raise ConfigError(f'Server client "{client["name"]}" IPv6 IP requires an IPv6 server subnet')
+ if client['ipv6_push_route']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 push-route requires an IPv6 server subnet"')
+ if client['ipv6_subnet']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 subnet requires an IPv6 server subnet"')
+
else:
# checks for both client and site-to-site go here
if openvpn['server_reject_unconfigured']:
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index c51048aeb..06c2ea29b 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -19,6 +19,7 @@ import netifaces
from sys import exit
from copy import deepcopy
+from netifaces import interfaces
from vyos.config import Config
from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf
@@ -506,6 +507,12 @@ def verify(conf):
if ipv6_count and not IP6 in kls.ip:
print(f'Should not use IPv6 addresses on tunnel {iftype} {ifname}')
+ # vrf check
+
+ vrf = options['vrf']
+ if vrf and vrf not in interfaces():
+ raise ConfigError(f'VRF "{vrf}" does not exist')
+
# tunnel encapsulation check
convert = {
diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py
index 3a14d92ef..76aa80a10 100755
--- a/src/conf_mode/service-ipoe.py
+++ b/src/conf_mode/service-ipoe.py
@@ -17,27 +17,18 @@
import os
import re
-from socket import socket, AF_INET, SOCK_STREAM
from sys import exit
from time import sleep
+from stat import S_IRUSR, S_IWUSR, S_IRGRP
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import run
+from vyos.util import call
from vyos.template import render
-ipoe_cnf_dir = r'/etc/accel-ppp/ipoe'
-ipoe_cnf = ipoe_cnf_dir + r'/ipoe.config'
-
-pidfile = r'/var/run/accel_ipoe.pid'
-cmd_port = r'2002'
-
-chap_secrets = ipoe_cnf_dir + '/chap-secrets'
-## accel-pppd -d -c /etc/accel-ppp/pppoe/pppoe.config -p /var/run/accel_pppoe.pid
-
-if not os.path.exists(ipoe_cnf_dir):
- os.makedirs(ipoe_cnf_dir)
+ipoe_conf = '/run/accel-pppd/ipoe.conf'
+ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
def _get_cpu():
@@ -49,33 +40,14 @@ def _get_cpu():
return cpu_cnt
-def _chk_con():
- cnt = 0
- s = socket(AF_INET, SOCK_STREAM)
- while True:
- try:
- s.connect(("127.0.0.1", int(cmd_port)))
- break
- except ConnectionRefusedError:
- sleep(0.5)
- cnt += 1
- if cnt == 100:
- raise("failed to start pppoe server")
- break
-
-
-def _accel_cmd(command):
- return run('/usr/bin/accel-cmd -p {cmd_port} {command}')
-
-##### Inline functions end ####
-
-
def get_config():
c = Config()
if not c.exists(['service', 'ipoe-server']):
return None
- config_data = {}
+ config_data = {
+ 'chap_secrets_file' : ipoe_chap_secrets
+ }
c.set_level(['service', 'ipoe-server'])
config_data['interfaces'] = {}
@@ -215,20 +187,26 @@ def get_config():
return config_data
-def generate(c):
- if c == None or not c:
+def generate(ipoe):
+ if not ipoe:
return None
- c['thread_cnt'] = _get_cpu()
+ dirname = os.path.dirname(ipoe_conf)
+ if not os.path.exists(dirname):
+ os.mkdir(dirname)
- if c['auth']['mech'] == 'local':
- old_umask = os.umask(0o077)
- render(chap_secrets, 'ipoe-server/chap-secrets.tmpl', c, trim_blocks=True)
- os.umask(old_umask)
+ ipoe['thread_cnt'] = _get_cpu()
+ render(ipoe_conf, 'ipoe-server/ipoe.config.tmpl', ipoe, trim_blocks=True)
- render(ipoe_cnf, 'ipoe-server/ipoe.config.tmpl', c, trim_blocks=True)
- # return c ??
- return c
+ if ipoe['auth']['mech'] == 'local':
+ render(ipoe_chap_secrets, 'ipoe-server/chap-secrets.tmpl', ipoe)
+ os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
+
+ else:
+ if os.path.exists(ipoe_chap_secrets):
+ os.unlink(ipoe_chap_secrets)
+
+ return None
def verify(c):
@@ -280,22 +258,19 @@ def verify(c):
return c
-def apply(c):
- if c == None:
- if os.path.exists(pidfile):
- _accel_cmd('shutdown hard')
- if os.path.exists(pidfile):
- os.remove(pidfile)
+def apply(ipoe):
+ if ipoe == None:
+ call('systemctl stop accel-ppp@ipoe.service')
+
+ if os.path.exists(ipoe_conf):
+ os.unlink(ipoe_conf)
+
+ if os.path.exists(ipoe_chap_secrets):
+ os.unlink(ipoe_chap_secrets)
+
return None
- if not os.path.exists(pidfile):
- ret = run(f'/usr/sbin/accel-pppd -c {ipoe_cnf} -p {pidfile} -d')
- _chk_con()
- if ret != 0 and os.path.exists(pidfile):
- os.remove(pidfile)
- raise ConfigError('accel-pppd failed to start')
- else:
- _accel_cmd('restart')
+ call('systemctl restart accel-ppp@ipoe.service')
if __name__ == '__main__':
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index d654dcb84..7530da2dc 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -535,23 +535,9 @@ def apply(snmp):
# start SNMP daemon
call("systemctl restart snmpd.service")
- # Passwords are not available immediately in the configuration file,
- # after daemon startup - we wait until they have been processed by
- # snmpd, which we see when a magic line appears in this file.
- while True:
- while not os.path.exists(config_file_user):
- sleep(0.5)
-
- try:
- with open(config_file_user, 'r') as f:
- for line in f:
- # Search for our magic string inside the file
- if 'usmUser' in line:
- break
- except IOError:
- continue
- else:
- break
+ while (call('systemctl -q is-active snmpd.service') != 0):
+ print("service not yet started")
+ sleep(0.5)
# net-snmp is now regenerating the configuration file in the background
# thus we need to re-open and re-read the file as the content changed.
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 6008ca0b3..91e2b369f 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -16,6 +16,7 @@
import os
+from crypt import crypt, METHOD_SHA512
from psutil import users
from pwd import getpwall, getpwnam
from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP
@@ -52,11 +53,6 @@ def get_local_users():
return local_users
-
-def get_crypt_pw(password):
- return cmd(f'/usr/bin/mkpasswd --method=sha-512 {password}')
-
-
def get_config():
login = default_config_data
conf = Config()
@@ -204,7 +200,7 @@ def generate(login):
# calculate users encrypted password
for user in login['add_users']:
if user['password_plaintext']:
- user['password_encrypted'] = get_crypt_pw(user['password_plaintext'])
+ user['password_encrypted'] = crypt(user['password_plaintext'], METHOD_SHA512)
user['password_plaintext'] = ''
# remove old plaintext password
diff --git a/src/etc/systemd/system/pdns-recursor.service.d/override.conf b/src/etc/systemd/system/pdns-recursor.service.d/override.conf
index 602d7b774..ef4dec303 100644
--- a/src/etc/systemd/system/pdns-recursor.service.d/override.conf
+++ b/src/etc/systemd/system/pdns-recursor.service.d/override.conf
@@ -2,4 +2,4 @@
WorkingDirectory=
WorkingDirectory=/run/powerdns
ExecStart=
-ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns
+ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns --socket-dir=/run/powerdns
diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py
index 8e2ee546c..bfc640a26 100755
--- a/src/op_mode/dns_forwarding_reset.py
+++ b/src/op_mode/dns_forwarding_reset.py
@@ -27,6 +27,8 @@ from sys import exit
from vyos.config import Config
from vyos.util import call
+PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
+
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--all", action="store_true", help="Reset all cache")
parser.add_argument("domain", type=str, nargs="?", help="Domain to reset cache entries for")
@@ -41,11 +43,11 @@ if __name__ == '__main__':
exit(0)
if args.all:
- call("rec_control wipe-cache \'.$\'")
+ call(f"{PDNS_CMD} wipe-cache \'.$\'")
exit(0)
elif args.domain:
- call("rec_control wipe-cache \'{0}$\'".format(args.domain))
+ call(f"{PDNS_CMD} wipe-cache \'{0}$\'".format(args.domain))
else:
parser.print_help()
diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py
index c400a72cd..8ae92beb7 100755
--- a/src/op_mode/dns_forwarding_statistics.py
+++ b/src/op_mode/dns_forwarding_statistics.py
@@ -1,12 +1,12 @@
#!/usr/bin/env python3
import jinja2
-import sys
+from sys import exit
from vyos.config import Config
from vyos.config import cmd
-PDNS_CMD='/usr/bin/rec_control'
+PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
OUT_TMPL_SRC = """
DNS forwarding statistics:
@@ -16,13 +16,12 @@ Cache size: {{ cache_size }} kbytes
"""
-
if __name__ == '__main__':
# Do nothing if service is not configured
c = Config()
if not c.exists_effective('service dns forwarding'):
print("DNS forwarding is not configured")
- sys.exit(0)
+ exit(0)
data = {}
diff --git a/src/op_mode/ipoe-control.py b/src/op_mode/ipoe-control.py
new file mode 100755
index 000000000..7111498b2
--- /dev/null
+++ b/src/op_mode/ipoe-control.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import argparse
+
+from vyos.config import Config
+from vyos.util import popen, run
+
+cmd_dict = {
+ 'cmd_base' : '/usr/bin/accel-cmd -p 2002 ',
+ 'selector' : ['if', 'username', 'sid'],
+ 'actions' : {
+ 'show_sessions' : 'show sessions',
+ 'show_stat' : 'show stat',
+ 'terminate' : 'teminate'
+ }
+}
+
+def is_ipoe_configured():
+ if not Config().exists_effective('service ipoe-server'):
+ print("Service IPoE is not configured")
+ sys.exit(1)
+
+def main():
+ #parese args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--action', help='Control action', required=True)
+ parser.add_argument('--selector', help='Selector username|ifname|sid', required=False)
+ parser.add_argument('--target', help='Target must contain username|ifname|sid', required=False)
+ args = parser.parse_args()
+
+
+ # Check is IPoE configured
+ is_ipoe_configured()
+
+ if args.action == "restart":
+ run(cmd_dict['cmd_base'] + "restart")
+ sys.exit(0)
+
+ if args.action in cmd_dict['actions']:
+ if args.selector in cmd_dict['selector'] and args.target:
+ run(cmd_dict['cmd_base'] + "{0} {1} {2}".format(args.action, args.selector, args.target))
+ else:
+ output, err = popen(cmd_dict['cmd_base'] + cmd_dict['actions'][args.action], decode='utf-8')
+ if not err:
+ print(output)
+ else:
+ print("IPoE server is not running")
+
+if __name__ == '__main__':
+ main()
diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py
new file mode 100755
index 000000000..171107b4a
--- /dev/null
+++ b/src/op_mode/ppp-server-ctrl.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 sys
+import argparse
+
+from vyos.config import Config
+from vyos.util import popen, DEVNULL
+
+cmd_dict = {
+ 'cmd_base' : '/usr/bin/accel-cmd -p {} ',
+ 'vpn_types' : {
+ 'pppoe' : 2001,
+ 'pptp' : 2003,
+ 'l2tp' : 2004,
+ 'sstp' : 2005
+ },
+ 'conf_proto' : {
+ 'pppoe' : 'service pppoe-server',
+ 'pptp' : 'vpn pptp remote-access',
+ 'l2tp' : 'vpn l2tp remote-access',
+ 'sstp' : 'vpn sstp'
+ }
+}
+
+def is_service_configured(proto):
+ if not Config().exists_effective(cmd_dict['conf_proto'][proto]):
+ print("Service {} is not configured".format(proto))
+ sys.exit(1)
+
+def main():
+ #parese args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--proto', help='Possible protocols pppoe|pptp|l2tp|sstp', required=True)
+ parser.add_argument('--action', help='Action command', required=True)
+ args = parser.parse_args()
+
+ if args.proto in cmd_dict['vpn_types'] and args.action:
+ # Check is service configured
+ is_service_configured(args.proto)
+
+ if args.action == "show sessions":
+ ses_pattern = " ifname,username,ip,ip6,ip6-dp,calling-sid,rate-limit,state,uptime,rx-bytes,tx-bytes"
+ else:
+ ses_pattern = ""
+
+ output, err = popen(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][args.proto]) + args.action + ses_pattern, stderr=DEVNULL, decode='utf-8')
+ if not err:
+ print(output)
+ else:
+ print("{} server is not running".format(args.proto))
+
+ else:
+ print("Param --proto and --action required")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/src/op_mode/version.py b/src/op_mode/version.py
index 8599c958f..2791f5e5c 100755
--- a/src/op_mode/version.py
+++ b/src/op_mode/version.py
@@ -53,6 +53,8 @@ def read_file(name):
version_output_tmpl = """
Version: VyOS {{version}}
+Release Train: {{release_train}}
+
Built by: {{built_by}}
Built on: {{built_on}}
Build UUID: {{build_uuid}}
@@ -102,7 +104,7 @@ if __name__ == '__main__':
elif run(""" grep '^overlay /' /proc/mounts >/dev/null """) != 0:
boot_via = "legacy non-image installation"
version_data['boot_via'] = boot_via
-
+
# Get hardware details from DMI
version_data['hardware_vendor'] = read_file('/sys/class/dmi/id/sys_vendor')
diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh
index 5046912a6..385ae460f 100755
--- a/src/system/on-dhcp-event.sh
+++ b/src/system/on-dhcp-event.sh
@@ -63,7 +63,7 @@ if [ $changes -gt 0 ]; then
echo Success
pid=`pgrep pdns_recursor`
if [ -n "$pid" ]; then
- sudo rec_control reload-zones
+ sudo rec_control --socket-dir=/run/powerdns reload-zones
fi
else
echo No changes made
diff --git a/src/validators/ipv6 b/src/validators/ipv6
new file mode 100755
index 000000000..f18d4a63e
--- /dev/null
+++ b/src/validators/ipv6
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv6 $1