diff options
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><AA:NN> 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><AA:NN> 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><x.x.x.x> <x.x.x.x/x></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><x.x.x.x> <x.x.x.x/x></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 |