summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--data/configd-include.json3
-rw-r--r--data/templates/dhcp-client/ipv4.tmpl7
-rw-r--r--data/templates/frr/bgp.frr.tmpl175
-rw-r--r--data/templates/frr/ldpd.frr.tmpl115
-rw-r--r--data/templates/https/nginx.default.tmpl2
-rw-r--r--data/templates/openvpn/client.conf.tmpl50
-rw-r--r--data/templates/openvpn/server.conf.tmpl363
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl42
-rw-r--r--data/templates/wifi/wpa_supplicant.conf.tmpl70
-rw-r--r--data/templates/wwan/chat.tmpl4
-rw-r--r--data/templates/wwan/peer.tmpl6
-rw-r--r--debian/control3
-rwxr-xr-xdebian/rules4
-rw-r--r--debian/vyos-1x-smoketest.install2
-rw-r--r--interface-definitions/dhcp-server.xml.in1
-rw-r--r--interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in7
-rw-r--r--interface-definitions/include/accel-radius-additions.xml.i1
-rw-r--r--interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i1
-rw-r--r--interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i7
-rw-r--r--interface-definitions/include/dhcp-options.xml.i12
-rw-r--r--interface-definitions/include/interface-ipv4-options.xml.i (renamed from interface-definitions/include/interface-ipv4.xml.i)5
-rw-r--r--interface-definitions/include/interface-ipv6-options.xml.i (renamed from interface-definitions/include/interface-ipv6.xml.i)2
-rw-r--r--interface-definitions/include/interface-mtu-1200-16000.xml.i16
-rw-r--r--interface-definitions/include/interface-mtu-1200-9000.xml.i16
-rw-r--r--interface-definitions/include/interface-mtu-1450-16000.xml.i16
-rw-r--r--interface-definitions/include/interface-mtu-1450-9000.xml.i16
-rw-r--r--interface-definitions/include/interface-mtu-64-8024.xml.i2
-rw-r--r--interface-definitions/include/interface-mtu-68-1500.xml.i2
-rw-r--r--interface-definitions/include/interface-mtu-68-16000.xml.i16
-rw-r--r--interface-definitions/include/interface-mtu-68-9000.xml.i16
-rw-r--r--interface-definitions/include/vif-s.xml.i4
-rw-r--r--interface-definitions/include/vif.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in23
-rw-r--r--interface-definitions/interfaces-bridge.xml.in53
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in27
-rw-r--r--interface-definitions/interfaces-geneve.xml.in19
-rw-r--r--interface-definitions/interfaces-l2tpv3.xml.in11
-rw-r--r--interface-definitions/interfaces-macsec.xml.in4
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in37
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in23
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in4
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in22
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in2
-rw-r--r--interface-definitions/interfaces-wireless.xml.in90
-rw-r--r--interface-definitions/interfaces-wirelessmodem.xml.in2
-rw-r--r--interface-definitions/protocols-bgp.xml.in27
-rw-r--r--interface-definitions/protocols-mpls.xml.in233
-rw-r--r--interface-definitions/system-ipv6.xml.in2
-rw-r--r--interface-definitions/system-options.xml.in29
-rw-r--r--interface-definitions/vpn_l2tp.xml.in1
-rw-r--r--op-mode-definitions/show-interfaces-bridge.xml6
-rw-r--r--python/vyos/configdict.py49
-rw-r--r--python/vyos/configverify.py64
-rw-r--r--python/vyos/ifconfig/bond.py8
-rw-r--r--python/vyos/ifconfig/bridge.py77
-rw-r--r--python/vyos/ifconfig/ethernet.py12
-rw-r--r--python/vyos/ifconfig/interface.py40
-rw-r--r--python/vyos/ifconfig/l2tpv3.py25
-rw-r--r--python/vyos/ifconfig/tunnel.py2
-rw-r--r--python/vyos/ifconfig/vtun.py44
-rw-r--r--python/vyos/ifconfig/wireguard.py2
-rw-r--r--python/vyos/template.py96
-rw-r--r--python/vyos/util.py73
-rw-r--r--python/vyos/validate.py79
-rwxr-xr-xsmoketest/bin/vyos-configtest82
-rw-r--r--smoketest/configs/pppoe-client62
-rw-r--r--smoketest/configs/pppoe-server94
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py22
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py36
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py92
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py396
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py328
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py80
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py20
-rwxr-xr-xsmoketest/scripts/cli/test_service_snmp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py32
-rwxr-xr-xsmoketest/scripts/cli/test_service_tftp-server.py2
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py8
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py5
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py8
-rwxr-xr-xsrc/conf_mode/intel_qat.py8
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py6
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py68
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py1135
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py8
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py22
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py14
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py8
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py16
-rwxr-xr-xsrc/conf_mode/protocols_mpls.py231
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py3
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py14
-rwxr-xr-xsrc/conf_mode/snmp.py3
-rwxr-xr-xsrc/conf_mode/ssh.py8
-rwxr-xr-xsrc/conf_mode/system-options.py13
-rwxr-xr-xsrc/conf_mode/system-wifi-regdom.py90
-rwxr-xr-xsrc/conf_mode/tftp_server.py2
-rwxr-xr-xsrc/conf_mode/vpn_l2tp.py12
-rwxr-xr-xsrc/conf_mode/vpn_pptp.py3
-rwxr-xr-xsrc/conf_mode/vpn_sstp.py10
-rw-r--r--src/etc/udev/rules.d/99-vyos-wwan.rules11
-rwxr-xr-xsrc/migration-scripts/interfaces/13-to-1461
-rwxr-xr-xsrc/op_mode/show_interfaces.py9
-rwxr-xr-xsrc/services/vyos-hostsd2
-rw-r--r--src/tests/helper.py3
-rw-r--r--src/tests/test_config_parser.py4
-rw-r--r--src/tests/test_configverify.py38
-rw-r--r--src/tests/test_dict_search.py (renamed from src/tests/test_vyos_dict_search.py)32
-rwxr-xr-xsrc/tests/test_find_device_file.py35
-rw-r--r--src/tests/test_jinja_filters.py69
-rw-r--r--src/tests/test_template.py46
-rw-r--r--src/tests/test_util.py6
-rw-r--r--src/tests/test_validate.py41
-rwxr-xr-xsrc/validators/ipv4-range4
-rw-r--r--vyos-configtest0
116 files changed, 3449 insertions, 2030 deletions
diff --git a/Makefile b/Makefile
index 622c856b3..d18676619 100644
--- a/Makefile
+++ b/Makefile
@@ -54,6 +54,7 @@ interface_definitions: $(BUILD_DIR) $(obj)
rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif-s/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif-s/node.tag/vif-c/node.tag/ip/node.def
rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif-s/node.tag/vif-c/node.tag/ipv6/node.def
+ rm -f $(TMPL_DIR)/interfaces/l2tpv3/node.tag/ip/node.def
rm -f $(TMPL_DIR)/interfaces/l2tpv3/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/interfaces/openvpn/node.tag/ipv6/node.def
rm -f $(TMPL_DIR)/interfaces/pppoe/node.tag/ip/node.def
diff --git a/data/configd-include.json b/data/configd-include.json
index 95aef65ad..da6fb915f 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -48,7 +48,6 @@
"system-options.py",
"system-syslog.py",
"system-timezone.py",
-"system-wifi-regdom.py",
"system_console.py",
"system_lcd.py",
"task_scheduler.py",
@@ -59,4 +58,4 @@
"vrf.py",
"vrrp.py",
"vyos_cert.py"
-] \ No newline at end of file
+]
diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl
index 8a44a9761..71b429db6 100644
--- a/data/templates/dhcp-client/ipv4.tmpl
+++ b/data/templates/dhcp-client/ipv4.tmpl
@@ -12,8 +12,13 @@ interface "{{ ifname }}" {
{% if dhcp_options.vendor_class_id is defined and dhcp_options.vendor_class_id is not none %}
send vendor-class-identifier "{{ dhcp_options.vendor_class_id }}";
{% endif %}
- request subnet-mask, broadcast-address, routers, domain-name-servers,
+ # The request statement causes the client to request that any server responding to the
+ # client send the client its values for the specified options.
+ request subnet-mask, broadcast-address,{{ " routers," if dhcp_options.no_default_route is not defined }} domain-name-servers,
rfc3442-classless-static-routes, domain-name, interface-mtu;
+
+ # The require statement lists options that must be sent in order for an offer to be
+ # accepted. Offers that do not contain all the listed options will be ignored!
require subnet-mask;
}
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
index d011a1e85..d0857ac2c 100644
--- a/data/templates/frr/bgp.frr.tmpl
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -14,10 +14,9 @@ router bgp {{ asn }}
{%- if type == "ipv4_unicast" %}
!
address-family ipv4 unicast
-{# need to check #}
{%- if 'aggregate_address' in bgp_afi[type] %}
{%- for ip in bgp_afi[type].aggregate_address %}
-{%- if ( ('as_set' and 'summary_only') in bgp_afi[type].aggregate_address[ip] ) %}
+{%- if ( ('as_set' in bgp_afi[type].aggregate_address[ip]) and ('summary_only' in bgp_afi[type].aggregate_address[ip] ) ) %}
aggregate-address {{ ip }} as-set summary-only
{%- elif 'as_set' in bgp_afi[type].aggregate_address[ip] %}
aggregate-address {{ ip }} as-set
@@ -28,23 +27,20 @@ router bgp {{ asn }}
{%- endif %}
{%- endfor %}
{%- endif %}
-{# END aggregate address#}
-{#- redistribute #}
-{# need to check. dont work.
- 'metric' and 'route_map' match also only 'route_map'
- 'table' parameter also include in protocol, its not what I want #}
+{#- END aggregate address ipv4 #}
+
+{#- redistribute afi ipv4 #}
{%- if 'redistribute' in bgp_afi[type] %}
-{%- if 'table' in bgp_afi[type].redistribute %}
- redistribute table {{bgp_afi[type].redistribute.table}}
-{%- endif %}
{%- for protocol in bgp_afi[type].redistribute %}
-{%- if ( ('metric' and 'route_map') in bgp_afi[type].redistribute[protocol] ) %}
+{%- if ( ('route_map' in bgp_afi[type].redistribute[protocol]) and ('metric' in bgp_afi[type].redistribute[protocol] ) ) %}
redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
{%- elif 'metric' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
+ redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
{%- elif 'route_map' in bgp_afi[type].redistribute[protocol] %}
redistribute {{protocol}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- else %}
+{%- elif 'table' in bgp_afi[type].redistribute %}
+ redistribute table {{bgp_afi[type].redistribute.table}}
+{%- else %}
redistribute {{protocol}}
{%- endif %}
{%- endfor %}
@@ -65,7 +61,7 @@ router bgp {{ asn }}
address-family ipv6 unicast
{%- if 'aggregate_address' in bgp_afi[type] %}
{%- for ip in bgp_afi[type].aggregate_address %}
-{%- if ( ('as_set' and 'summary_only') in bgp_afi[type].aggregate_address[ip] ) %}
+{%- if ( ('as_set' in bgp_afi[type].aggregate_address[ip]) and ('summary_only' in bgp_afi[type].aggregate_address[ip] ) ) %}
aggregate-address {{ ip }} as-set summary-only
{%- elif 'as_set' in bgp_afi[type].aggregate_address[ip] %}
aggregate-address {{ ip }} as-set
@@ -76,22 +72,20 @@ router bgp {{ asn }}
{%- endif %}
{%- endfor %}
{%- endif %}
-{# END aggregate address#}
+{#- END aggregate address ipv6 #}
-{#- redistribute #}
-{# need to check. doesn't work. 'metric' and 'route_map' match also only 'route_map' #}
+{#- redistribute afi ipv6 #}
{%- if 'redistribute' in bgp_afi[type] %}
-{%- if 'table' in bgp_afi[type].redistribute %}
- redistribute table {{bgp_afi[type].redistribute.table}}
-{%- endif %}
{%- for protocol in bgp_afi[type].redistribute %}
-{%- if ( ('metric' and 'route_map') in bgp_afi[type].redistribute[protocol] ) %}
+{%- if ( ('route_map' in bgp_afi[type].redistribute[protocol]) and ('metric' in bgp_afi[type].redistribute[protocol] ) ) %}
redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
{%- elif 'metric' in bgp_afi[type].redistribute[protocol] %}
- redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
+ redistribute {{protocol}} metric {{bgp_afi[type].redistribute[protocol].metric}}
{%- elif 'route_map' in bgp_afi[type].redistribute[protocol] %}
redistribute {{protocol}} route-map {{bgp_afi[type].redistribute[protocol].route_map}}
-{%- else %}
+{%- elif 'table' in bgp_afi[type].redistribute %}
+ redistribute table {{bgp_afi[type].redistribute.table}}
+{%- else %}
redistribute {{protocol}}
{%- endif %}
{%- endfor %}
@@ -206,7 +200,7 @@ router bgp {{ asn }}
neighbor {{ pr_group }} update-source {{ conf_peer_group.update_source }}
{%- endif %}
-{# START peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
+{#- START peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
{%- if 'address_family' in conf_peer_group %}
{%- for afi in conf_peer_group.address_family %}
{%- if afi == "ipv4_unicast" %}
@@ -236,10 +230,19 @@ router bgp {{ asn }}
{%- endif %}
{#- END single params for peer-group #}
-{#- Checks need to be done as-path|med|next-hop #}
{%- if 'attribute_unchanged' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
+{%- if ( ('as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged as-path med
+{%- elif ( ('as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged as-path next-hop
+{%- elif ( ('med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged med next-hop
+{%- elif 'as_path' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
neighbor {{ pr_group }} attribute-unchanged as-path
+{%- elif 'med' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
+ neighbor {{ pr_group }} attribute-unchanged med
+{%- elif 'next_hop' in conf_peer_group.address_family.ipv4_unicast.attribute_unchanged %}
+ neighbor {{ pr_group }} attribute-unchanged next-hop
{%- else %}
neighbor {{ pr_group }} attribute-unchanged as-path next-hop med
{%- endif %}
@@ -247,11 +250,13 @@ router bgp {{ asn }}
{#- END attribute-unchanged #}
{%- if 'capability' in conf_peer_group.address_family.ipv4_unicast %}
-{%- if 'receive' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
+{%- if 'orf' in conf_peer_group.address_family.ipv4_unicast.capability %}
+{%- if 'receive' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
neighbor {{ pr_group }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
+{%- endif %}
+{%- if 'send' in conf_peer_group.address_family.ipv4_unicast.capability.orf.prefix_list %}
neighbor {{ pr_group }} capability orf prefix-list send
+{%- endif %}
{%- endif %}
{%- endif %}
@@ -322,7 +327,6 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{#- Need to check. https://phabricator.vyos.net/T2387#73900 #}
{%- if 'unsuppress_map' in conf_peer_group.address_family.ipv4_unicast %}
neighbor {{ pr_group }} unsuppress-map {{conf_peer_group.address_family.ipv4_unicast.unsuppress_map}}
{%- endif %}
@@ -357,21 +361,39 @@ router bgp {{ asn }}
{%- endif %}
{#- END single params for peer-group afi6 #}
-{#- Checks need to be done as-path|med|next-hop #}
{%- if 'attribute_unchanged' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
+{%- if ( ('as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged as-path med
+{%- elif ( ('as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged as-path next-hop
+{%- elif ( ('med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ pr_group }} attribute-unchanged med next-hop
+{%- elif 'as_path' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
neighbor {{ pr_group }} attribute-unchanged as-path
+{%- elif 'med' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
+ neighbor {{ pr_group }} attribute-unchanged med
+{%- elif 'next_hop' in conf_peer_group.address_family.ipv6_unicast.attribute_unchanged %}
+ neighbor {{ pr_group }} attribute-unchanged next-hop
{%- else %}
neighbor {{ pr_group }} attribute-unchanged as-path next-hop med
{%- endif %}
{%- endif %}
+{#- END attribute-unchanged ipv6 #}
{%- if 'capability' in conf_peer_group.address_family.ipv6_unicast %}
-{%- if 'receive' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
+{%- if 'dynamic' in conf_peer_group.address_family.ipv6_unicast.capability %}
+{#- exit from afi ipv6 unicast because 'dynamic' its a global parameter for peer-group in afi6. Other checks are ongoing in afi6. Also related T3037 #}
+ exit-address-family
+ neighbor {{ pr_group }} capability dynamic
+ address-family ipv6 unicast
+{%- endif %}
+{%- if 'orf' in conf_peer_group.address_family.ipv6_unicast.capability %}
+{%- if 'receive' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
neighbor {{ pr_group }} capability orf prefix-list receive
{%- endif %}
-{%- if 'send' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
+{%- if 'send' in conf_peer_group.address_family.ipv6_unicast.capability.orf.prefix_list %}
neighbor {{ pr_group }} capability orf prefix-list send
+{%- endif %}
{%- endif %}
{%- endif %}
@@ -442,7 +464,6 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{#- Checks need to be done. https://phabricator.vyos.net/T2387#73900 #}
{%- if 'unsuppress_map' in conf_peer_group.address_family.ipv6_unicast %}
neighbor {{ pr_group }} unsuppress-map {{conf_peer_group.address_family.ipv6_unicast.unsuppress_map}}
{%- endif %}
@@ -453,7 +474,7 @@ router bgp {{ asn }}
{%- endfor %}
{%- endif %}
-{# END peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
+{#- END peer-group afi; set protocols bgp xxx peer-group FOO address-family #}
{%- endfor %}
{%- endif %}
@@ -464,7 +485,7 @@ router bgp {{ asn }}
{#- set peer-group as conf_peer #}
{%- set conf_peer = conf_bgp[asn].neighbor[peer] %}
-{#- First parameter for peer-group - remote-as #}
+{#- First parameter for peer neighbor - remote-as #}
{%- if 'remote_as' in conf_peer %}
neighbor {{ peer }} remote-as {{ conf_peer.remote_as }}
{%- endif %}
@@ -491,10 +512,6 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{%- if 'description' in conf_peer %}
- neighbor {{ peer }} description {{ conf_peer.description }}
-{%- endif %}
-
{%- if 'disable_capability_negotiation' in conf_peer %}
neighbor {{ peer }} disable-capability-negotiation
{%- endif %}
@@ -564,17 +581,21 @@ router bgp {{ asn }}
neighbor {{ peer }} strict-capability-match
{%- endif %}
-{#- Need to check #}
+{#- set protocols bgp xxx neighbor x.x.x.x timers #}
{%- if 'timers' in conf_peer %}
-{%- if ( ('connect' and 'holdtime' and 'keepalive') in conf_peer.timers ) %}
+{%- if ( ('connect' in conf_peer.timers) and ('holdtime' in conf_peer.timers) and ('keepalive' in conf_peer.timers ) ) %}
neighbor {{ peer }} timers {{conf_peer.timers.keepalive}} {{conf_peer.timers.holdtime}}
- neighbor {{ peer }} timers connect {{conf_peer.timers.connect}}
+ neighbor {{ peer }} timers connect {{conf_peer.timers.connect}}
+{%- elif ( ('holdtime' in conf_peer.timers) and ('keepalive' in conf_peer.timers ) ) %}
+ neighbor {{ peer }} timers {{conf_peer.timers.keepalive}} {{conf_peer.timers.holdtime}}
+{%- elif 'connect' in conf_peer.timers %}
+ neighbor {{ peer }} timers connect {{conf_peer.timers.connect}}
{%- endif %}
{%- endif %}
{%- if 'ttl_security' in conf_peer %}
{%- if 'hops' in conf_peer.ttl_security %}
- neighbor {{ peer }} ttl-security hops {{conf_peer.ttl_security.hops}}
+ neighbor {{ peer }} ttl-security hops {{conf_peer.ttl_security.hops}}
{%- endif %}
{%- endif %}
@@ -582,6 +603,10 @@ router bgp {{ asn }}
neighbor {{ peer }} update-source {{ conf_peer.update_source }}
{%- endif %}
+{%- if 'description' in conf_peer %}
+ neighbor {{ peer }} description {{ conf_peer.description }}
+{%- endif %}
+
{#- START address family for peer; set protocols bgp xxx neighbor x.x.x.x address-family ipvX-unicast #}
{%- if 'address_family' in conf_peer %}
{%- for afi in conf_peer.address_family %}
@@ -615,10 +640,19 @@ router bgp {{ asn }}
{%- endif %}
{#- END single params for neighbor #}
-{#- Checks need to be done as-path|med|next-hop #}
{%- if 'attribute_unchanged' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
+{%- if ( ('as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged as-path med
+{%- elif ( ('as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged as-path next-hop
+{%- elif ( ('med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged med next-hop
+{%- elif 'as_path' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
neighbor {{ peer }} attribute-unchanged as-path
+{%- elif 'med' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
+ neighbor {{ peer }} attribute-unchanged med
+{%- elif 'next_hop' in conf_peer.address_family.ipv4_unicast.attribute_unchanged %}
+ neighbor {{ peer }} attribute-unchanged next-hop
{%- else %}
neighbor {{ peer }} attribute-unchanged as-path next-hop med
{%- endif %}
@@ -626,11 +660,13 @@ router bgp {{ asn }}
{#- END attribute-unchanged #}
{%- if 'capability' in conf_peer.address_family.ipv4_unicast %}
-{%- if 'receive' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
+{%- if 'orf' in conf_peer.address_family.ipv4_unicast.capability %}
+{%- if 'receive' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
neighbor {{ peer }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
+{%- endif %}
+{%- if 'send' in conf_peer.address_family.ipv4_unicast.capability.orf.prefix_list %}
neighbor {{ peer }} capability orf prefix-list send
+{%- endif %}
{%- endif %}
{%- endif %}
@@ -701,7 +737,6 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{#- Checks need to be done. https://phabricator.vyos.net/T2387#73900 #}
{%- if 'unsuppress_map' in conf_peer.address_family.ipv4_unicast %}
neighbor {{ peer }} unsuppress-map {{conf_peer.address_family.ipv4_unicast.unsuppress_map}}
{%- endif %}
@@ -740,10 +775,19 @@ router bgp {{ asn }}
{%- endif %}
{#- END single params for neighbor #}
-{#- Checks need to be done as-path|med|next-hop #}
{%- if 'attribute_unchanged' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
+{%- if ( ('as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged as-path med
+{%- elif ( ('as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged as-path next-hop
+{%- elif ( ('med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) and ('next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged) ) %}
+ neighbor {{ peer }} attribute-unchanged med next-hop
+{%- elif 'as_path' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
neighbor {{ peer }} attribute-unchanged as-path
+{%- elif 'med' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
+ neighbor {{ peer }} attribute-unchanged med
+{%- elif 'next_hop' in conf_peer.address_family.ipv6_unicast.attribute_unchanged %}
+ neighbor {{ peer }} attribute-unchanged next-hop
{%- else %}
neighbor {{ peer }} attribute-unchanged as-path next-hop med
{%- endif %}
@@ -751,11 +795,13 @@ router bgp {{ asn }}
{#- END attribute-unchanged #}
{%- if 'capability' in conf_peer.address_family.ipv6_unicast %}
-{%- if 'receive' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
+{%- if 'orf' in conf_peer.address_family.ipv6_unicast.capability %}
+{%- if 'receive' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
neighbor {{ peer }} capability orf prefix-list receive
-{%- endif %}
-{%- if 'send' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
- neighbor {{ peer }} capability orf prefix-list send
+{%- endif %}
+{%- if 'send' in conf_peer.address_family.ipv6_unicast.capability.orf.prefix_list %}
+ neighbor {{ peer }} capability orf prefix-list send
+{%- endif %}
{%- endif %}
{%- endif %}
@@ -826,7 +872,6 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{#- Checks need to be done. https://phabricator.vyos.net/T2387#73900 #}
{%- if 'unsuppress_map' in conf_peer.address_family.ipv6_unicast %}
neighbor {{ peer }} unsuppress-map {{conf_peer.address_family.ipv6_unicast.unsuppress_map}}
{%- endif %}
@@ -863,7 +908,7 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
{%- if 'med' in bgp_params.bestpath %}
-{%- if ( ('confed' and 'missing_as_worst') in bgp_params.bestpath.med ) %}
+{%- if ( ('confed' in bgp_params.bestpath.med) and ('missing_as_worst' in bgp_params.bestpath.med ) ) %}
bgp bestpath med confed missing-as-worst
{%- elif 'confed' in bgp_params.bestpath.med %}
bgp bestpath med confed
@@ -886,9 +931,9 @@ router bgp {{ asn }}
{%- endif %}
{%- endif %}
-{#- Doesn't work in current FRR configuration (bgp dampening 16 751 2001 61) #}
+{#- Doesn't work in current FRR configuration; vtysh (bgp dampening 16 751 2001 61) #}
{%- if 'dampening' in bgp_params %}
-{%- if ( ('half_life' and 'max_suppress_time' and 're_use' and 'start_suppress_time') in bgp_params.dampening ) %}
+{%- if ( ('half_life' in bgp_params.dampening) and ('max_suppress_time' in bgp_params.dampening) and ('re_use' in bgp_params.dampening) and ('start_suppress_time' in bgp_params.dampening ) ) %}
bgp dampening {{ bgp_params.dampening.half_life }} {{ bgp_params.dampening.re_use }} {{ bgp_params.dampening.start_suppress_time }} {{ bgp_params.dampening.max_suppress_time }}
{%- endif %}
{%- endif %}
@@ -909,7 +954,7 @@ router bgp {{ asn }}
{%- if 'distance' in bgp_params %}
{%- if 'global' in bgp_params.distance %}
-{%- if ( ('external' and 'internal' and 'local') in bgp_params.distance.global ) %}
+{%- if ( ('external' in bgp_params.distance.global) and ('internal' in bgp_params.distance.global) and ('local' in bgp_params.distance.global ) ) %}
!
address-family ipv4 unicast
distance bgp {{ bgp_params.distance.global.external }} {{ bgp_params.distance.global.internal }} {{ bgp_params.distance.global.local }}
@@ -950,10 +995,14 @@ router bgp {{ asn }}
no bgp fast-external-failover
{%- endif %}
+{%- if 'router_id' in bgp_params %}
+ bgp router-id {{ bgp_params.router_id }}
+{%- endif %}
+
{#- END parameters; set protocols bgp xxx parameters #}
{%- if 'timers' in conf_bgp[asn] %}
-{%- if ( ('holdtime' and 'keepalive') in conf_bgp[asn].timers ) %}
+{%- if ( ('holdtime' in conf_bgp[asn].timers) and ('keepalive' in conf_bgp[asn].timers ) ) %}
timers bgp {{conf_bgp[asn].timers.keepalive}} {{conf_bgp[asn].timers.holdtime}}
{%- endif %}
{%- endif %}
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
index 5f080d75f..4b7e5c5ea 100644
--- a/data/templates/frr/ldpd.frr.tmpl
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -7,12 +7,45 @@ no router-id {{ old_router_id }}
{% if router_id -%}
router-id {{ router_id }}
{% endif -%}
+{% if old_ldp.cisco_interop_tlv -%}
+no dual-stack cisco-interop
+{% endif -%}
+{% if ldp.cisco_interop_tlv -%}
+dual-stack cisco-interop
+{% endif -%}
+{% if old_ldp.transport_prefer_ipv4 -%}
+no dual-stack transport-connection prefer ipv4
+{% endif -%}
+{% if ldp.transport_prefer_ipv4 -%}
+dual-stack transport-connection prefer ipv4
+{% endif -%}
{% for neighbor_id in old_ldp.neighbors -%}
no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
+{% if 'ttl_security' is defined -%}
+{% if 'disable' in old_ldp.neighbors[neighbor_id].ttl_security %}
+no neighbor {{neighbor_id}} ttl-security disable
+{% else -%}
+no neighbor {{neighbor_id}} ttl-security hops {{old_ldp.neighbors[neighbor_id].ttl_security}}
+{% endif -%}
+{% endif -%}
+{% if 'session_holdtime' is defined -%}
+no neighbor {{neighbor_id}} session holdtime {{old_ldp.neighbors[neighbor_id].session_holdtime}}
+{% endif -%}
{% endfor -%}
{% for neighbor_id in ldp.neighbors -%}
neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
+{% if 'ttl_security' is defined -%}
+{% if 'disable' in ldp.neighbors[neighbor_id].ttl_security %}
+neighbor {{neighbor_id}} ttl-security disable
+{% else -%}
+neighbor {{neighbor_id}} ttl-security hops {{ldp.neighbors[neighbor_id].ttl_security}}
+{% endif -%}
+{% endif -%}
+{% if 'session_holdtime' is defined -%}
+neighbor {{neighbor_id}} session holdtime {{ldp.neighbors[neighbor_id].session_holdtime}}
+{% endif -%}
{% endfor -%}
+!
address-family ipv4
label local allocate host-routes
{% if old_ldp.export_ipv4_exp -%}
@@ -27,24 +60,48 @@ no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
{% if ldp.d_transp_ipv4 -%}
discovery transport-address {{ ldp.d_transp_ipv4 }}
{% endif -%}
-{% if old_ldp.hello_holdtime -%}
-no discovery hello holdtime {{ old_ldp.hello_holdtime }}
+{% if old_ldp.hello_ipv4_holdtime -%}
+no discovery hello holdtime {{ old_ldp.hello_ipv4_holdtime }}
{% endif -%}
-{% if ldp.hello_holdtime -%}
-discovery hello holdtime {{ ldp.hello_holdtime }}
+{% if ldp.hello_ipv4_holdtime -%}
+discovery hello holdtime {{ ldp.hello_ipv4_holdtime }}
{% endif -%}
-{% if old_ldp.hello_interval -%}
-no discovery hello interval {{ old_ldp.hello_interval }}
+{% if old_ldp.hello_ipv4_interval -%}
+no discovery hello interval {{ old_ldp.hello_ipv4_interval }}
{% endif -%}
-{% if ldp.hello_interval -%}
-discovery hello interval {{ ldp.hello_interval }}
+{% if ldp.hello_ipv4_interval -%}
+discovery hello interval {{ ldp.hello_ipv4_interval }}
{% endif -%}
{% if old_ldp.ses_ipv4_hold -%}
-no session holdtime {{ old_ldp.ses_ipv4_hold }}
+no session holdtime {{ old_ldp.ses_ipv4_hold }}
{% endif -%}
{% if ldp.ses_ipv4_hold -%}
session holdtime {{ ldp.ses_ipv4_hold }}
{% endif -%}
+{% if old_ldp.target_ipv4_enable -%}
+no discovery targeted-hello accept
+{% endif -%}
+{% if ldp.target_ipv4_enable -%}
+discovery targeted-hello accept
+{% endif -%}
+{% if old_ldp.target_ipv4_hello_int -%}
+no discovery targeted-hello interval {{ old_ldp.target_ipv4_hello_int }}
+{% endif -%}
+{% if ldp.target_ipv4_hello_int -%}
+discovery targeted-hello interval {{ ldp.target_ipv4_hello_int }}
+{% endif -%}
+{% if old_ldp.target_ipv4_hello_hold -%}
+no discovery targeted-hello holdtime {{ old_ldp.target_ipv4_hello_hold }}
+{% endif -%}
+{% if ldp.target_ipv4_hello_hold -%}
+discovery targeted-hello holdtime {{ ldp.target_ipv4_hello_hold }}
+{% endif -%}
+{% for address in old_ldp.target_ipv4_addresses -%}
+no neighbor {{address}} targeted
+{% endfor -%}
+{% for address in ldp.target_ipv4_addresses -%}
+neighbor {{address}} targeted
+{% endfor -%}
{% for interface in old_ldp.interfaces -%}
no interface {{interface}}
{% endfor -%}
@@ -65,7 +122,7 @@ no label local advertise explicit-null
label local advertise explicit-null
{% endif -%}
{% if old_ldp.ses_ipv6_hold -%}
-no session holdtime {{ old_ldp.ses_ipv6_hold }}
+no session holdtime {{ old_ldp.ses_ipv6_hold }}
{% endif -%}
{% if ldp.ses_ipv6_hold -%}
session holdtime {{ ldp.ses_ipv6_hold }}
@@ -76,6 +133,42 @@ no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
{% if ldp.d_transp_ipv6 -%}
discovery transport-address {{ ldp.d_transp_ipv6 }}
{% endif -%}
+{% if old_ldp.hello_ipv6_holdtime -%}
+no discovery hello holdtime {{ old_ldp.hello_ipv6_holdtime }}
+{% endif -%}
+{% if ldp.hello_ipv6_holdtime -%}
+discovery hello holdtime {{ ldp.hello_ipv6_holdtime }}
+{% endif -%}
+{% if old_ldp.hello_ipv6_interval -%}
+no discovery hello interval {{ old_ldp.hello_ipv6_interval }}
+{% endif -%}
+{% if ldp.hello_ipv6_interval -%}
+discovery hello interval {{ ldp.hello_ipv6_interval }}
+{% endif -%}
+{% if old_ldp.target_ipv6_enable -%}
+no discovery targeted-hello accept
+{% endif -%}
+{% if ldp.target_ipv6_enable -%}
+discovery targeted-hello accept
+{% endif -%}
+{% if old_ldp.target_ipv6_hello_int -%}
+no discovery targeted-hello interval {{ old_ldp.target_ipv6_hello_int }}
+{% endif -%}
+{% if ldp.target_ipv6_hello_int -%}
+discovery targeted-hello interval {{ ldp.target_ipv6_hello_int }}
+{% endif -%}
+{% if old_ldp.target_ipv6_hello_hold -%}
+no discovery targeted-hello holdtime {{ old_ldp.target_ipv6_hello_hold }}
+{% endif -%}
+{% if ldp.target_ipv6_hello_hold -%}
+discovery targeted-hello holdtime {{ ldp.target_ipv6_hello_hold }}
+{% endif -%}
+{% for address in old_ldp.target_ipv6_addresses -%}
+no neighbor {{address}} targeted
+{% endfor -%}
+{% for address in ldp.target_ipv6_addresses -%}
+neighbor {{address}} targeted
+{% endfor -%}
{% for interface in old_ldp.interfaces -%}
no interface {{interface}}
{% endfor -%}
@@ -91,4 +184,4 @@ no address-family ipv6
{% else -%}
no mpls ldp
{% endif -%}
-!
+! \ No newline at end of file
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index a20be45ae..855ebff4f 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -5,7 +5,7 @@ server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
- return 301 https://$server_name$request_uri;
+ return 301 https://$host$request_uri;
}
{% for server in server_block_list %}
diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl
index 508d8da94..62387ef7c 100644
--- a/data/templates/openvpn/client.conf.tmpl
+++ b/data/templates/openvpn/client.conf.tmpl
@@ -1,35 +1,31 @@
### Autogenerated by interfaces-openvpn.py ###
-{% if ip -%}
-ifconfig-push {{ ip[0] }} {{ remote_netmask }}
-{% endif -%}
-
-{% for route in push_route -%}
-push "route {{ route }}"
-{% endfor -%}
-
-{% for net in subnet -%}
-iroute {{ net }}
-{% endfor -%}
-
+{% if ip %}
+ifconfig-push {{ ip[0] }} {{ server_subnet[0] | netmask_from_cidr }}
+{% endif %}
+{% if push_route is defined and push_route is not none %}
+{% for route in push_route %}
+push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}"
+{% endfor %}
+{% endif %}
+{% if subnet is defined and subnet is not none %}
+{% for network in subnet %}
+iroute {{ network | address_from_cidr }} {{ network | netmask_from_cidr }}
+{% endfor %}
+{% endif %}
{# ipv6_remote is only set when IPv6 server is enabled #}
-{% if ipv6_remote -%}
+{% if ipv6_remote %}
# IPv6
-
-{%- if ipv6_ip %}
+{% if ipv6_ip %}
ifconfig-ipv6-push {{ ipv6_ip[0] }} {{ ipv6_remote }}
-{%- endif %}
-
-{%- for route6 in ipv6_push_route %}
+{% endif %}
+{% for route6 in ipv6_push_route %}
push "route-ipv6 {{ route6 }}"
-{%- endfor %}
-
-{%- for net6 in ipv6_subnet %}
+{% endfor %}
+{% for net6 in ipv6_subnet %}
iroute {{ net6 }}
-{%- endfor %}
-
-{% endif -%}
-
-{% if disable -%}
+{% endfor %}
+{% endif %}
+{% if disable is defined %}
disable
-{% endif -%}
+{% endif %}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
index fea310236..a510c3a84 100644
--- a/data/templates/openvpn/server.conf.tmpl
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -2,246 +2,238 @@
#
# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
# for individual keyword definition
-
-{% if description -%}
-# {{ description }}
-
-{% endif -%}
+#
+# {{ description if description is defined and description is not none }}
+#
verb 3
-
-user {{ uid }}
-group {{ gid }}
-
-dev-type {{ type }}
-dev {{ intf }}
+user {{ daemon_user }}
+group {{ daemon_group }}
+dev-type {{ device_type }}
+dev {{ ifname }}
persist-key
iproute /usr/libexec/vyos/system/unpriv-ip
-
-proto {{ protocol_real }}
-
-{%- if local_host %}
+{% if protocol == 'tcp-active' %}
+proto tcp6-client
+{% elif protocol == 'tcp-passive' %}
+proto tcp6-server
+{% else %}
+proto udp6
+{% endif %}
+{% if local_host is defined and local_host is not none %}
local {{ local_host }}
-{%- endif %}
-
-{%- if mode == 'server' and protocol == 'udp' and not local_host %}
+{% endif %}
+{% if mode is defined and mode == 'server' and protocol == 'udp' and local_host is not defined %}
multihome
-{%- endif %}
-
-{%- if local_port %}
+{% endif %}
+{% if local_port is defined and local_port is not none %}
lport {{ local_port }}
-{%- endif %}
-
-{% if remote_port -%}
+{% endif %}
+{% if remote_port is defined and remote_port is not none %}
rport {{ remote_port }}
{% endif %}
-
-{%- if remote_host %}
-{%- for remote in remote_host -%}
+{% if remote_host is defined and remote_host is not none %}
+{% for remote in remote_host %}
remote {{ remote }}
-{% endfor -%}
-{% endif -%}
-
-{% if shared_secret_file %}
-secret {{ shared_secret_file }}
-{%- endif %}
-
-{%- if persistent_tunnel %}
+{% endfor %}
+{% endif %}
+{% if shared_secret_key_file is defined and shared_secret_key_file is not none %}
+secret {{ shared_secret_key_file }}
+{% endif %}
+{% if persistent_tunnel is defined %}
persist-tun
-{%- endif %}
-
-{%- if redirect_gateway %}
-push "redirect-gateway {{ redirect_gateway }}"
-{%- endif %}
-
-{%- if compress_lzo %}
+{% endif %}
+{% if replace_default_route is defined and replace_default_route.local is defined %}
+push "redirect-gateway local def1"
+{% elif replace_default_route is defined %}
+push "redirect-gateway def1"
+{% endif %}
+{% if use_lzo_compression is defined %}
compress lzo
-{%- endif %}
+{% endif %}
-{% if 'client' in mode -%}
+{% if 'client' in mode %}
#
# OpenVPN Client mode
#
client
nobind
-
-{% elif 'server' in mode -%}
+{% elif 'server' in mode %}
#
# OpenVPN Server mode
#
-
-{%- if server_topology %}
-topology {% if server_topology == 'point-to-point' %}p2p{% else %}{{ server_topology }}{% endif %}
-{%- endif %}
-
-{%- if is_bridge_member %}
mode server
tls-server
-{%- else %}
-server {{ server_subnet[0] }} nopool
-{%- endif %}
-
-{%- if server_pool %}
-ifconfig-pool {{ server_pool_start }} {{ server_pool_stop }}{% if server_pool_netmask %} {{ server_pool_netmask }}{% endif %}
-{%- endif %}
-
-{%- if server_max_conn %}
-max-clients {{ server_max_conn }}
-{%- endif %}
-
-{%- if client %}
-client-config-dir /run/openvpn/ccd/{{ intf }}
-{%- endif %}
-
-{%- if server_reject_unconfigured %}
-ccd-exclusive
-{%- endif %}
-
-keepalive {{ ping_interval }} {{ ping_restart }}
+{% if server is defined and server is not none %}
+{% if server.subnet is defined and server.subnet is not none %}
+{% if server.topology is defined and server.topology == 'point-to-point' %}
+topology p2p
+{% elif server.topology is defined and server.topology is not none %}
+topology {{ server.topology }}
+{% endif %}
+{% for subnet in server.subnet if subnet | is_ipv4 %}
+server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool
+{# OpenVPN assigns the first IP address to its local interface so the pool used #}
+{# in net30 topology - where each client receives a /30 must start from the second subnet #}
+{% if server.topology is defined and server.topology == 'net30' %}
+ifconfig-pool {{ subnet | inc_ip('4') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tap' else '' }}
+{% else %}
+{# OpenVPN assigns the first IP address to its local interface so the pool must #}
+{# start from the second address and end on the last address #}
+ifconfig-pool {{ subnet | first_host_address | inc_ip('1') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tun' else '' }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if server.client_ip_pool is defined and server.client_ip_pool is not none and server.client_ip_pool.disable is not defined %}
+ifconfig-pool {{ server.client_ip_pool.start }} {{ server.client_ip_pool.stop }}{{ server.client_ip_pool.subnet_mask if server.client_ip_pool.subnet_mask is defined and server.client_ip_pool.subnet_mask is not none }}
+{% endif %}
+{% if server.max_connections is defined and server.max_connections is not none %}
+max-clients {{ server.max_connections }}
+{% endif %}
+{% if server.client is defined and server.client is not none %}
+client-config-dir /run/openvpn/ccd/{{ ifname }}
+{% endif %}
+{% endif %}
+keepalive {{ keep_alive.interval }} {{ keep_alive.failure_count }}
management /run/openvpn/openvpn-mgmt-intf unix
-
-{% for route in server_push_route -%}
+{% if server is defined and server is not none %}
+{% if server.reject_unconfigured_clients is defined %}
+ccd-exclusive
+{% endif %}
+{% if server.push_route is defined and server.push_route is not none %}
+{% for route in server.push_route %}
push "route {{ route }}"
-{% endfor -%}
-
-{% for ns in server_dns_nameserver -%}
-push "dhcp-option DNS {{ ns }}"
-{% endfor -%}
-
-{%- if server_domain -%}
-push "dhcp-option DOMAIN {{ server_domain }}"
-{% endif -%}
-
-{%- if server_ipv6_local %}
+{% endfor %}
+{% endif %}
+{% if server.name_server is defined and server.name_server is not none %}
+{% for nameserver in server.name_server %}
+push "dhcp-option DNS {{ nameserver }}"
+{% endfor %}
+{% endif %}
+{% if server.domain_name is defined and server.domain_name is not none %}
+push "dhcp-option DOMAIN {{ server.domain_name }}"
+{% endif %}
+{% endif %}
+
+{% if subnet_v6 is defined and subnet_v6 is not none %}
# IPv6
push "tun-ipv6"
ifconfig-ipv6 {{ server_ipv6_local }}/{{ server_ipv6_prefixlen }} {{ server_ipv6_remote }}
-
-{%- if server_ipv6_pool %}
+{% if server_ipv6_pool %}
ifconfig-ipv6-pool {{ server_ipv6_pool_base }}/{{ server_ipv6_pool_prefixlen }}
-{%- endif %}
-
-{%- for route6 in server_ipv6_push_route %}
+{% endif %}
+{% for route6 in server_ipv6_push_route %}
push "route-ipv6 {{ route6 }}"
-{%- endfor %}
-
-{%- for ns6 in server_ipv6_dns_nameserver %}
+{% endfor %}
+{% for ns6 in server_ipv6_dns_nameserver %}
push "dhcp-option DNS6 {{ ns6 }}"
-{%- endfor %}
-
-{%- endif %}
-
-{% else -%}
+{% endfor %}
+{% endif %}
+{% else %}
#
# OpenVPN site-2-site mode
#
-ping {{ ping_interval }}
-ping-restart {{ ping_restart }}
-
-{% if local_address_subnet -%}
-ifconfig {{ local_address[0] }} {{ local_address_subnet }}
-{%- elif 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 -%}
+ping {{ keep_alive.interval }}
+ping-restart {{ keep_alive.failure_count }}
+
+{% for laddr, laddr_conf in local_address.items() if laddr | is_ipv4 %}
+{% if laddr_conf is defined and laddr_conf.subnet_mask is defined and laddr_conf.subnet_mask is not none %}
+ifconfig {{ laddr }} {{ laddr_conf.subnet_mask }}
+{% else %}
+{% for raddr in remote_address %}
+{% if raddr | is_ipv4 %}
+ifconfig {{ laddr }} {{ raddr }}
+{% else %}
+ifconfig-ipv6 {{ laddr }} {{ raddr }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
-{% if tls -%}
+{% if tls is defined and tls is not none %}
# TLS options
-{%- if tls_ca_cert %}
-ca {{ tls_ca_cert }}
-{%- endif %}
-
-{%- if tls_cert %}
-cert {{ tls_cert }}
-{%- endif %}
-
-{%- if tls_key %}
-key {{ tls_key }}
-{%- endif %}
-
-{%- if tls_crypt %}
-tls-crypt {{ tls_crypt }}
-{%- endif %}
-
-{%- if tls_crl %}
-crl-verify {{ tls_crl }}
-{%- endif %}
-
-{%- if tls_version_min %}
-tls-version-min {{tls_version_min}}
-{%- endif %}
-
-{%- if tls_dh %}
-dh {{ tls_dh }}
-{%- endif %}
-
-{%- if tls_auth %}
-{%- if mode == 'client' %}
-tls-auth {{tls_auth}} 1
-{%- elif mode == 'server' %}
-tls-auth {{tls_auth}} 0
-{%- endif %}
-{%- endif %}
-
-{%- if tls_role %}
-{%- if 'active' in tls_role %}
+{% if tls.ca_cert_file is defined and tls.ca_cert_file is not none %}
+ca {{ tls.ca_cert_file }}
+{% endif %}
+{% if tls.cert_file is defined and tls.cert_file is not none %}
+cert {{ tls.cert_file }}
+{% endif %}
+{% if tls.key_file is defined and tls.key_file is not none %}
+key {{ tls.key_file }}
+{% endif %}
+{% if tls.crypt_file is defined and tls.crypt_file is not none %}
+tls-crypt {{ tls.crypt_file }}
+{% endif %}
+{% if tls.crl_file is defined and tls.crl_file is not none %}
+crl-verify {{ tls.crl_file }}
+{% endif %}
+{% if tls.tls_version_min is defined and tls.tls_version_min is not none %}
+tls-version-min {{ tls.tls_version_min }}
+{% endif %}
+{% if tls.dh_file is defined and tls.dh_file is not none %}
+dh {{ tls.dh_file }}
+{% endif %}
+{% if tls.auth_file is defined and tls.auth_file is not none %}
+{% if mode == 'client' %}
+tls-auth {{ tls.auth_file }} 1
+{% elif mode == 'server' %}
+tls-auth {{ tls.auth_file }} 0
+{% endif %}
+{% endif %}
+{% if tls.role is defined and tls.role is not none %}
+{% if tls.role == 'active' %}
tls-client
-{%- elif 'passive' in tls_role %}
+{% elif tls.role == 'passive' %}
tls-server
-{%- endif %}
-{%- endif %}
-
-{%- endif %}
+{% endif %}
+{% endif %}
+{% endif %}
# Encryption options
-{%- if encryption %}
-{% if encryption == 'none' -%}
+{% if encryption is defined and encryption is not none %}
+{% if encryption.cipher is defined and encryption.cipher is not none %}
+{% if encryption.cipher == 'none' %}
cipher none
-{%- elif encryption == 'des' -%}
+{% elif encryption.cipher == 'des' %}
cipher des-cbc
-{%- elif encryption == '3des' -%}
+{% elif encryption.cipher == '3des' %}
cipher des-ede3-cbc
-{%- elif encryption == 'bf128' -%}
+{% elif encryption.cipher == 'bf128' %}
cipher bf-cbc
keysize 128
-{%- elif encryption == 'bf256' -%}
+{% elif encryption.cipher == 'bf256' %}
cipher bf-cbc
keysize 25
-{%- elif encryption == 'aes128gcm' -%}
+{% elif encryption.cipher == 'aes128gcm' %}
cipher aes-128-gcm
-{%- elif encryption == 'aes128' -%}
+{% elif encryption.cipher == 'aes128' %}
cipher aes-128-cbc
-{%- elif encryption == 'aes192gcm' -%}
+{% elif encryption.cipher == 'aes192gcm' %}
cipher aes-192-gcm
-{%- elif encryption == 'aes192' -%}
+{% elif encryption.cipher == 'aes192' %}
cipher aes-192-cbc
-{%- elif encryption == 'aes256gcm' -%}
+{% elif encryption.cipher == 'aes256gcm' %}
cipher aes-256-gcm
-{%- elif encryption == 'aes256' -%}
+{% elif encryption.cipher == 'aes256' %}
cipher aes-256-cbc
-{%- endif -%}
-{%- endif %}
-
-{%- if ncp_ciphers %}
-ncp-ciphers {{ncp_ciphers}}
-{%- endif %}
-{%- if disable_ncp %}
+{% endif %}
+{% endif %}
+{% if encryption.ncp_ciphers is defined and encryption.ncp_ciphers is not none %}
+ncp-ciphers {{ encryption.ncp_ciphers | join(':') }}
+{% elif encryption.disable_ncp is defined %}
ncp-disable
-{%- endif %}
+{% endif %}
+{% endif %}
-{% if hash -%}
+{% if hash is defined and hash is not none %}
auth {{ hash }}
-{%- endif -%}
+{% endif %}
-{%- if auth %}
+{% if authentication is defined and authentication is not none %}
auth-user-pass {{ auth_user_pass_file }}
auth-retry nointeract
-{%- endif %}
+{% endif %}
# DEPRECATED This option will be removed in OpenVPN 2.5
# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
@@ -257,12 +249,11 @@ auth-retry nointeract
# See https://phabricator.vyos.net/T1512
compat-names
-{% if options -%}
+{% if openvpn_option is defined and openvpn_option is not none %}
#
# Custom options added by user (not validated)
#
-
-{% for option in options -%}
+{% for option in openvpn_option %}
{{ option }}
-{% endfor -%}
-{%- endif %}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
index c5e4240d1..16d9f7c98 100644
--- a/data/templates/wifi/hostapd.conf.tmpl
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -23,7 +23,10 @@ interface={{ ifname }}
# added to the bridge automatically (brctl may refuse to do this before hostapd
# has been started to change the interface mode). If needed, the bridge
# interface is also created.
-bridge={{ is_bridge_member }}
+{# as there can only be one bridge interface it is save to loop #}
+{% for bridge in is_bridge_member %}
+bridge={{ bridge }}
+{% endfor %}
{% endif %}
# Driver interface type (hostap/wired/none/nl80211/bsd);
@@ -69,18 +72,18 @@ ssid={{ ssid }}
channel={{ channel }}
{% endif %}
-{% if mode %}
+{% if mode is defined and mode is not none %}
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
-# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
-# to be set to hw_mode=a. When using ACS (see channel parameter), a
+# needs to be set to hw_mode a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode a. When using ACS (see channel parameter), a
# special value "any" can be used to indicate that any support band can be used.
# This special case is currently supported only with drivers with which
# offloaded ACS is used.
-{% if 'n' in mode %}
+{% if mode == 'n' %}
hw_mode=g
-{% elif 'ac' in mode %}
+{% elif mode == 'ac' %}
hw_mode=a
ieee80211h=1
ieee80211ac=1
@@ -529,10 +532,13 @@ wep_key{{ loop.index -1 }}={{ security.wep.key }}
# and/or WPA2 (full IEEE 802.11i/RSN):
# bit0 = WPA
# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2.
+# In other words, for WPA3, wpa 2 is used the configuration (and
+# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
{% if security.wpa.mode is defined %}
-{% if security.wpa.mode == 'both' %}
+{% if security.wpa.mode == 'wpa+wpa2' %}
wpa=3
-{% elif security.wpa.mode == 'wpa2' %}
+{% elif security.wpa.mode == 'wpa2' or security.wpa.mode == 'wpa3' %}
wpa=2
{% elif security.wpa.mode == 'wpa' %}
wpa=1
@@ -592,7 +598,15 @@ wpa_passphrase={{ security.wpa.passphrase }}
# added to enable SHA256-based stronger algorithms.
# WPA-PSK = WPA-Personal / WPA2-Personal
# WPA-PSK-SHA256 = WPA2-Personal using SHA256
-wpa_key_mgmt=WPA-PSK
+# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+# SAE = SAE (WPA3-Personal)
+# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+wpa_key_mgmt=SAE
+{% else %}
+wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256
+{% endif %}
{% elif security.wpa.radius is defined %}
##### IEEE 802.1X-2004 related configuration ##################################
@@ -602,9 +616,17 @@ ieee8021x=1
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
# added to enable SHA256-based stronger algorithms.
+# WPA-PSK = WPA-Personal / WPA2-Personal
+# WPA-PSK-SHA256 = WPA2-Personal using SHA256
# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
-wpa_key_mgmt=WPA-EAP
+# SAE = SAE (WPA3-Personal)
+# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+wpa_key_mgmt=WPA-EAP-SUITE-B-192
+{% else %}
+wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
+{% endif %}
{% if security.wpa.radius.server is defined %}
# RADIUS client forced local IP address for the access point
diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl
index f84892dc0..20b4f7976 100644
--- a/data/templates/wifi/wpa_supplicant.conf.tmpl
+++ b/data/templates/wifi/wpa_supplicant.conf.tmpl
@@ -4,10 +4,78 @@
# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf
network={
+ # ssid: SSID (mandatory); network name in one of the optional formats:
+ # - an ASCII string with double quotation
+ # - a hex string (two characters per octet of SSID)
+ # - a printf-escaped ASCII string P"<escaped string>"
+ #
ssid="{{ ssid }}"
+
+ # scan_ssid:
+ # 0 = do not scan this SSID with specific Probe Request frames (default)
+ # 1 = scan with SSID-specific Probe Request frames (this can be used to
+ # find APs that do not accept broadcast SSID or use multiple SSIDs;
+ # this will add latency to scanning, so enable this only when needed)
scan_ssid=1
+
{% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %}
- key_mgmt=WPA-PSK
+ # ieee80211w: whether management frame protection is enabled
+ # 0 = disabled (default unless changed with the global pmf parameter)
+ # 1 = optional
+ # 2 = required
+ # The most common configuration options for this based on the PMF (protected
+ # management frames) certification program are:
+ # PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
+ # PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
+ # (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
+ # WPA3-Personal-only mode: ieee80211w=2 and key_mgmt=SAE
+ ieee80211w=1
+
+ # key_mgmt: list of accepted authenticated key management protocols
+ # WPA-PSK = WPA pre-shared key (this requires 'psk' field)
+ # WPA-EAP = WPA using EAP authentication
+ # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
+ # generated WEP keys
+ # NONE = WPA is not used; plaintext or static WEP could be used
+ # WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK
+ # instead)
+ # FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key
+ # FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+ # FT-EAP-SHA384 = Fast BSS Transition (IEEE 802.11r) with EAP authentication
+ # and using SHA384
+ # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
+ # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
+ # SAE = Simultaneous authentication of equals; pre-shared key/password -based
+ # authentication with stronger security than WPA-PSK especially when using
+ # not that strong password; a.k.a. WPA3-Personal
+ # FT-SAE = SAE with FT
+ # WPA-EAP-SUITE-B = Suite B 128-bit level
+ # WPA-EAP-SUITE-B-192 = Suite B 192-bit level
+ # OSEN = Hotspot 2.0 Rel 2 online signup connection
+ # FILS-SHA256 = Fast Initial Link Setup with SHA256
+ # FILS-SHA384 = Fast Initial Link Setup with SHA384
+ # FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+ # FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
+ # OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
+ # DPP = Device Provisioning Protocol
+ # If not set, this defaults to: WPA-PSK WPA-EAP
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa3' %}
+ key_mgmt=SAE
+{% else %}
+ key_mgmt=WPA-PSK WPA-PSK-SHA256
+{% endif %}
+
+ # psk: WPA preshared key; 256-bit pre-shared key
+ # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
+ # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
+ # generated using the passphrase and SSID). ASCII passphrase must be between
+ # 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
+ # be used to indicate that the PSK/passphrase is stored in external storage.
+ # This field is not needed, if WPA-EAP is used.
+ # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
+ # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
+ # startup and reconfiguration time can be optimized by generating the PSK only
+ # only when the passphrase or SSID has actually changed.
psk="{{ security.wpa.passphrase }}"
{% else %}
key_mgmt=NONE
diff --git a/data/templates/wwan/chat.tmpl b/data/templates/wwan/chat.tmpl
index a3395c057..386af37e6 100644
--- a/data/templates/wwan/chat.tmpl
+++ b/data/templates/wwan/chat.tmpl
@@ -1,6 +1,10 @@
ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
'' AT
OK ATZ
+{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %}
+OK 'AT+CGDCONT=1,"IPV4V6","{{ apn }}"'
+{% else %}
OK 'AT+CGDCONT=1,"IP","{{ apn }}"'
+{% endif %}
OK ATD*99#
CONNECT ''
diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl
index e23881bf8..2807a79a4 100644
--- a/data/templates/wwan/peer.tmpl
+++ b/data/templates/wwan/peer.tmpl
@@ -4,15 +4,19 @@
ifname {{ ifname }}
ipparam {{ ifname }}
linkname {{ ifname }}
+
{{ "usepeerdns" if no_peer_dns is defined }}
# physical device
{{ device }}
lcp-echo-failure 0
115200
debug
-debug
mtu {{ mtu }}
mru {{ mtu }}
+{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %}
++ipv6
+ipv6cp-use-ipaddr
+{% endif %}
nodefaultroute
ipcp-max-failure 4
ipcp-accept-local
diff --git a/debian/control b/debian/control
index ebcfc6c43..c5dda7883 100644
--- a/debian/control
+++ b/debian/control
@@ -10,6 +10,7 @@ Build-Depends:
python3,
python3-coverage,
python3-lxml,
+ python3-netifaces,
python3-nose,
python3-setuptools,
python3-xmltodict,
@@ -65,6 +66,7 @@ Depends:
ntpdate,
ocserv,
openssh-server,
+ openssl,
openvpn,
openvpn-auth-ldap,
openvpn-auth-radius,
@@ -104,6 +106,7 @@ Depends:
telnet,
tftpd-hpa,
traceroute,
+ tuned,
udp-broadcast-relay,
usb-modeswitch,
usbutils,
diff --git a/debian/rules b/debian/rules
index 6b982fd8e..a0cc7a99b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -97,6 +97,10 @@ override_dh_auto_install:
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/smoke/
cp -r smoketest/scripts/* $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/smoke
+ # Install smoke test configs
+ mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config/
+ cp -r smoketest/configs/* $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config
+
# Install system programs
mkdir -p $(DIR)/$(VYOS_BIN_DIR)
cp -r smoketest/bin/* $(DIR)/$(VYOS_BIN_DIR)
diff --git a/debian/vyos-1x-smoketest.install b/debian/vyos-1x-smoketest.install
index fdf949557..3739763b9 100644
--- a/debian/vyos-1x-smoketest.install
+++ b/debian/vyos-1x-smoketest.install
@@ -1,2 +1,4 @@
usr/bin/vyos-smoketest
+usr/bin/vyos-configtest
usr/libexec/vyos/tests/smoke
+usr/libexec/vyos/tests/config
diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in
index ca8abc036..978118b31 100644
--- a/interface-definitions/dhcp-server.xml.in
+++ b/interface-definitions/dhcp-server.xml.in
@@ -87,6 +87,7 @@
<constraint>
<validator name="ipv4-prefix"/>
</constraint>
+ <constraintErrorMessage>Invalid IPv4 subnet definition</constraintErrorMessage>
</properties>
<children>
<leafNode name="bootfile-name">
diff --git a/interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in b/interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in
new file mode 100644
index 000000000..026f67453
--- /dev/null
+++ b/interface-definitions/include/accel-radius-additions-disable-accounting.xlm.in
@@ -0,0 +1,7 @@
+<leafNode name="disable-accounting">
+ <properties>
+ <help>Disable accounting</help>
+ <valueless/>
+ </properties>
+</leafNode>
+
diff --git a/interface-definitions/include/accel-radius-additions.xml.i b/interface-definitions/include/accel-radius-additions.xml.i
index 598fb73f8..bf0f0ac94 100644
--- a/interface-definitions/include/accel-radius-additions.xml.i
+++ b/interface-definitions/include/accel-radius-additions.xml.i
@@ -29,6 +29,7 @@
</properties>
<defaultValue>1813</defaultValue>
</leafNode>
+ #include <include/accel-radius-additions-disable-accounting.xlm.in>
<leafNode name="fail-time">
<properties>
<help>Mark server unavailable for &lt;n&gt; seconds on failure</help>
diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i
index f1a61d669..155817838 100644
--- a/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i
+++ b/interface-definitions/include/bgp-neighbor-afi-ipv4-unicast.xml.i
@@ -267,7 +267,6 @@
<leafNode name="unsuppress-map">
<properties>
<help>Route-map to selectively unsuppress suppressed IPv4-routes</help>
- <valueless/>
</properties>
</leafNode>
<leafNode name="weight">
diff --git a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i b/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i
index dcdc0eb66..63bf582db 100644
--- a/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i
+++ b/interface-definitions/include/bgp-neighbor-afi-ipv6-unicast.xml.i
@@ -59,6 +59,13 @@
<help>Advertise capabilities to this neighbor (IPv6)</help>
</properties>
<children>
+ <!-- Capability dynamic in the afi ipv6 does nothing T3037 -->
+ <leafNode name="dynamic">
+ <properties>
+ <help>Advertise dynamic capability to this neighbor</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<node name="orf">
<properties>
<help>Advertise ORF capability to this neighbor</help>
diff --git a/interface-definitions/include/dhcp-options.xml.i b/interface-definitions/include/dhcp-options.xml.i
index e33022361..df9c7a97a 100644
--- a/interface-definitions/include/dhcp-options.xml.i
+++ b/interface-definitions/include/dhcp-options.xml.i
@@ -6,17 +6,23 @@
<children>
<leafNode name="client-id">
<properties>
- <help>DHCP client identifier</help>
+ <help>Identifier used by client to identify itself to the DHCP server</help>
</properties>
</leafNode>
<leafNode name="host-name">
<properties>
- <help>DHCP client host name (overrides system host name)</help>
+ <help>Override system host-name sent to DHCP server</help>
</properties>
</leafNode>
<leafNode name="vendor-class-id">
<properties>
- <help>DHCP client vendor type</help>
+ <help>Identify the vendor client type to the DHCP server</help>
+ </properties>
+ </leafNode>
+ <leafNode name="no-default-route">
+ <properties>
+ <help>Do not request routers from DHCP server</help>
+ <valueless/>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/include/interface-ipv4.xml.i b/interface-definitions/include/interface-ipv4-options.xml.i
index 66842ab9b..416e1adf5 100644
--- a/interface-definitions/include/interface-ipv4.xml.i
+++ b/interface-definitions/include/interface-ipv4-options.xml.i
@@ -1,14 +1,17 @@
-<!-- included start from interface-ipv4.xml.i -->
+<!-- included start from interface-ipv4-options.xml.i -->
<node name="ip">
<properties>
<help>IPv4 routing parameters</help>
</properties>
<children>
+ #include <include/interface-arp-cache-timeout.xml.i>
#include <include/interface-disable-arp-filter.xml.i>
#include <include/interface-disable-forwarding.xml.i>
#include <include/interface-enable-arp-accept.xml.i>
#include <include/interface-enable-arp-announce.xml.i>
#include <include/interface-enable-arp-ignore.xml.i>
+ #include <include/interface-enable-proxy-arp.xml.i>
+ #include <include/interface-proxy-arp-pvlan.xml.i>
</children>
</node>
<!-- included end -->
diff --git a/interface-definitions/include/interface-ipv6.xml.i b/interface-definitions/include/interface-ipv6-options.xml.i
index c9299890b..a94c6572b 100644
--- a/interface-definitions/include/interface-ipv6.xml.i
+++ b/interface-definitions/include/interface-ipv6-options.xml.i
@@ -1,4 +1,4 @@
-<!-- included start from interface-ipv6.xml.i -->
+<!-- included start from interface-ipv6-options.xml.i -->
<node name="ipv6">
<properties>
<help>IPv6 routing parameters</help>
diff --git a/interface-definitions/include/interface-mtu-1200-16000.xml.i b/interface-definitions/include/interface-mtu-1200-16000.xml.i
new file mode 100644
index 000000000..04b5ec8ac
--- /dev/null
+++ b/interface-definitions/include/interface-mtu-1200-16000.xml.i
@@ -0,0 +1,16 @@
+<!-- included start from interface-mtu-1200-16000.xml.i -->
+<leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>1200-16000</format>
+ <description>Maximum Transmission Unit in byte</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1200-16000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 1200 and 16000</constraintErrorMessage>
+ </properties>
+ <defaultValue>1500</defaultValue>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/interface-mtu-1200-9000.xml.i b/interface-definitions/include/interface-mtu-1200-9000.xml.i
deleted file mode 100644
index 387e60fa5..000000000
--- a/interface-definitions/include/interface-mtu-1200-9000.xml.i
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- included start from interface-mtu-1200-9000.xml.i -->
-<leafNode name="mtu">
- <properties>
- <help>Maximum Transmission Unit (MTU)</help>
- <valueHelp>
- <format>1200-9000</format>
- <description>Maximum Transmission Unit</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1200-9000"/>
- </constraint>
- <constraintErrorMessage>MTU must be between 1200 and 9000</constraintErrorMessage>
- </properties>
- <defaultValue>1500</defaultValue>
-</leafNode>
-<!-- included end -->
diff --git a/interface-definitions/include/interface-mtu-1450-16000.xml.i b/interface-definitions/include/interface-mtu-1450-16000.xml.i
new file mode 100644
index 000000000..41dd5fb00
--- /dev/null
+++ b/interface-definitions/include/interface-mtu-1450-16000.xml.i
@@ -0,0 +1,16 @@
+<!-- included start from interface-mtu-1450-16000.xml.i -->
+<leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>1450-16000</format>
+ <description>Maximum Transmission Unit in byte</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1450-16000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 1450 and 16000</constraintErrorMessage>
+ </properties>
+ <defaultValue>1500</defaultValue>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/interface-mtu-1450-9000.xml.i b/interface-definitions/include/interface-mtu-1450-9000.xml.i
deleted file mode 100644
index 3fc961051..000000000
--- a/interface-definitions/include/interface-mtu-1450-9000.xml.i
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- included start from interface-mtu-1450-9000.xml.i -->
-<leafNode name="mtu">
- <properties>
- <help>Maximum Transmission Unit (MTU)</help>
- <valueHelp>
- <format>1450-9000</format>
- <description>Maximum Transmission Unit</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1450-9000"/>
- </constraint>
- <constraintErrorMessage>MTU must be between 1450 and 9000</constraintErrorMessage>
- </properties>
- <defaultValue>1500</defaultValue>
-</leafNode>
-<!-- included end -->
diff --git a/interface-definitions/include/interface-mtu-64-8024.xml.i b/interface-definitions/include/interface-mtu-64-8024.xml.i
index f51e098c1..0a455bc64 100644
--- a/interface-definitions/include/interface-mtu-64-8024.xml.i
+++ b/interface-definitions/include/interface-mtu-64-8024.xml.i
@@ -4,7 +4,7 @@
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
<format>64-8024</format>
- <description>Maximum Transmission Unit</description>
+ <description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 64-8024"/>
diff --git a/interface-definitions/include/interface-mtu-68-1500.xml.i b/interface-definitions/include/interface-mtu-68-1500.xml.i
index 0563e0023..78c2c6920 100644
--- a/interface-definitions/include/interface-mtu-68-1500.xml.i
+++ b/interface-definitions/include/interface-mtu-68-1500.xml.i
@@ -4,7 +4,7 @@
<help>Maximum Transmission Unit (MTU)</help>
<valueHelp>
<format>68-1500</format>
- <description>Maximum Transmission Unit</description>
+ <description>Maximum Transmission Unit in byte</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 68-1500"/>
diff --git a/interface-definitions/include/interface-mtu-68-16000.xml.i b/interface-definitions/include/interface-mtu-68-16000.xml.i
new file mode 100644
index 000000000..9f18464bf
--- /dev/null
+++ b/interface-definitions/include/interface-mtu-68-16000.xml.i
@@ -0,0 +1,16 @@
+<!-- included start from interface-mtu-68-16000.xml.i -->
+<leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>68-16000</format>
+ <description>Maximum Transmission Unit in byte</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 68-16000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 68 and 16000</constraintErrorMessage>
+ </properties>
+ <defaultValue>1500</defaultValue>
+</leafNode>
+<!-- included end -->
diff --git a/interface-definitions/include/interface-mtu-68-9000.xml.i b/interface-definitions/include/interface-mtu-68-9000.xml.i
deleted file mode 100644
index 82d0ed82b..000000000
--- a/interface-definitions/include/interface-mtu-68-9000.xml.i
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- included start from interface-mtu-68-9000.xml.i -->
-<leafNode name="mtu">
- <properties>
- <help>Maximum Transmission Unit (MTU)</help>
- <valueHelp>
- <format>68-9000</format>
- <description>Maximum Transmission Unit</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 68-9000"/>
- </constraint>
- <constraintErrorMessage>MTU must be between 68 and 9000</constraintErrorMessage>
- </properties>
- <defaultValue>1500</defaultValue>
-</leafNode>
-<!-- included end -->
diff --git a/interface-definitions/include/vif-s.xml.i b/interface-definitions/include/vif-s.xml.i
index 3a04b10d9..ab556489f 100644
--- a/interface-definitions/include/vif-s.xml.i
+++ b/interface-definitions/include/vif-s.xml.i
@@ -44,7 +44,7 @@
</children>
</node>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<tagNode name="vif-c">
<properties>
<help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help>
@@ -61,7 +61,7 @@
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-disable.xml.i>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
#include <include/interface-vrf.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/include/vif.xml.i b/interface-definitions/include/vif.xml.i
index a0f7c0bc8..3369d0d7c 100644
--- a/interface-definitions/include/vif.xml.i
+++ b/interface-definitions/include/vif.xml.i
@@ -63,7 +63,7 @@
</children>
</node>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
</children>
</tagNode>
<!-- included end -->
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 4e2c61d07..66fc5f7a9 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -80,25 +80,8 @@
</properties>
<defaultValue>layer2</defaultValue>
</leafNode>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- #include <include/interface-proxy-arp-pvlan.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
<leafNode name="min-links">
<properties>
@@ -170,7 +153,7 @@
</leafNode>
</children>
</node>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<leafNode name="primary">
<properties>
<help>Primary device interface</help>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 0a777865b..778acda78 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -40,6 +40,7 @@
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-disable.xml.i>
#include <include/interface-vrf.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<leafNode name="forwarding-delay">
<properties>
<help>Forwarding delay</help>
@@ -81,23 +82,8 @@
</leafNode>
</children>
</node>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
<leafNode name="max-age">
<properties>
@@ -126,6 +112,37 @@
</completionHelp>
</properties>
<children>
+ <leafNode name="native-vlan">
+ <properties>
+ <help>Specify VLAN id which should natively be present on the link</help>
+ <valueHelp>
+ <format>1-4094</format>
+ <description>Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 1 and 4094</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="allowed-vlan">
+ <properties>
+ <help>Specify VLAN id which is allowed in this trunk interface</help>
+ <valueHelp>
+ <format>&lt;id&gt;</format>
+ <description>VLAN id allowed to pass this interface</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;idN&gt;-&lt;idM&gt;</format>
+ <description>VLAN id range allowed on this interface (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <regex>^([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})$</regex>
+ </constraint>
+ <constraintErrorMessage>not a valid VLAN ID value or range</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
<leafNode name="cost">
<properties>
<help>Bridge port cost</help>
@@ -178,6 +195,8 @@
<valueless/>
</properties>
</leafNode>
+ #include <include/vif-s.xml.i>
+ #include <include/vif.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index a19a766d3..83f3d9e46 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -14,10 +14,6 @@
<format>ethN</format>
<description>Ethernet interface name</description>
</valueHelp>
- <valueHelp>
- <format>en[ospx]N</format>
- <description>Ethernet interface name</description>
- </valueHelp>
</properties>
<children>
#include <include/address-ipv4-ipv6-dhcp.xml.i>
@@ -59,27 +55,10 @@
<defaultValue>auto</defaultValue>
</leafNode>
#include <include/interface-hw-id.xml.i>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- #include <include/interface-proxy-arp-pvlan.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<node name="offload-options">
<properties>
<help>Configurable offload options</help>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 320dfd64d..0c776e3c3 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -19,23 +19,10 @@
#include <include/address-ipv4-ipv6.xml.i>
#include <include/interface-description.xml.i>
#include <include/interface-disable.xml.i>
- <node name="ip">
- <properties>
- <help>IPv4 routing parameters</help>
- </properties>
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-1450-9000.xml.i>
+ #include <include/interface-mtu-1450-16000.xml.i>
<leafNode name="remote">
<properties>
<help>Remote address of GENEVE tunnel</help>
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index 3a878ad76..a8ddb74fb 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -53,13 +53,8 @@
</properties>
<defaultValue>udp</defaultValue>
</leafNode>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
<leafNode name="local-ip">
<properties>
<help>Local IP address for L2TPv3 tunnel</help>
@@ -76,7 +71,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<leafNode name="peer-session-id">
<properties>
<help>Peer session identifier</help>
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index 068e31449..4d2581906 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -17,6 +17,8 @@
</properties>
<children>
#include <include/address-ipv4-ipv6.xml.i>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
<node name="security">
<properties>
<help>Security/Encryption Settings</help>
@@ -107,7 +109,7 @@
</node>
#include <include/interface-description.xml.i>
#include <include/interface-disable.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
#include <include/source-interface-ethernet.xml.i>
#include <include/interface-vrf.xml.i>
</children>
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 5675379d5..56a35e537 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -36,7 +36,7 @@
#include <include/interface-description.xml.i>
<leafNode name="device-type">
<properties>
- <help>OpenVPN interface device-type</help>
+ <help>OpenVPN interface device-type (default: tun)</help>
<completionHelp>
<list>tun tap</list>
</completionHelp>
@@ -49,9 +49,10 @@
<description>TAP device, required for OSI layer 2</description>
</valueHelp>
<constraint>
- <regex>(tun|tap)</regex>
+ <regex>^(tun|tap)$</regex>
</constraint>
</properties>
+ <defaultValue>tun</defaultValue>
</leafNode>
#include <include/interface-disable.xml.i>
<node name="encryption">
@@ -110,7 +111,7 @@
<description>AES algorithm with 256-bit key GCM</description>
</valueHelp>
<constraint>
- <regex>(none|des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)</regex>
+ <regex>^(none|des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)$</regex>
</constraint>
</properties>
</leafNode>
@@ -157,7 +158,7 @@
<description>AES algorithm with 256-bit key GCM</description>
</valueHelp>
<constraint>
- <regex>(none|des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)</regex>
+ <regex>^(none|des|3des|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm)$</regex>
</constraint>
<multi/>
</properties>
@@ -204,7 +205,7 @@
<description>SHA-512 algorithm</description>
</valueHelp>
<constraint>
- <regex>(md5|sha1|sha256|sha384|sha512)</regex>
+ <regex>^(md5|sha1|sha256|sha384|sha512)$</regex>
</constraint>
</properties>
</leafNode>
@@ -215,7 +216,7 @@
<children>
<leafNode name="failure-count">
<properties>
- <help>Maximum number of keepalive packet failures [default 6]</help>
+ <help>Maximum number of keepalive packet failures (default: 60)</help>
<valueHelp>
<format>0-1000</format>
<description>Maximum number of keepalive packet failures</description>
@@ -224,10 +225,11 @@
<validator name="numeric" argument="--range 0-1000"/>
</constraint>
</properties>
+ <defaultValue>60</defaultValue>
</leafNode>
<leafNode name="interval">
<properties>
- <help>Keepalive packet interval (seconds) [default 10]</help>
+ <help>Keepalive packet interval in seconds (default: 10)</help>
<valueHelp>
<format>0-600</format>
<description>Keepalive packet interval (seconds)</description>
@@ -236,6 +238,7 @@
<validator name="numeric" argument="--range 0-600"/>
</constraint>
</properties>
+ <defaultValue>10</defaultValue>
</leafNode>
</children>
</node>
@@ -304,7 +307,7 @@
<description>Server in client-server mode</description>
</valueHelp>
<constraint>
- <regex>(site-to-site|client|server)</regex>
+ <regex>^(site-to-site|client|server)$</regex>
</constraint>
</properties>
</leafNode>
@@ -342,9 +345,10 @@
<description>TCP and initiates connections actively</description>
</valueHelp>
<constraint>
- <regex>(udp|tcp-passive|tcp-active)</regex>
+ <regex>^(udp|tcp-passive|tcp-active)$</regex>
</constraint>
</properties>
+ <defaultValue>udp</defaultValue>
</leafNode>
<leafNode name="remote-address">
<properties>
@@ -359,6 +363,7 @@
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
</constraint>
<multi/>
</properties>
@@ -613,6 +618,7 @@
<leafNode name="reject-unconfigured-clients">
<properties>
<help>Reject connections from clients that are not explicitly configured</help>
+ <valueless/>
</properties>
</leafNode>
<leafNode name="subnet">
@@ -634,7 +640,7 @@
</leafNode>
<leafNode name="topology">
<properties>
- <help>Topology for clients</help>
+ <help>Topology for clients (default: net30)</help>
<completionHelp>
<list>net30 point-to-point subnet</list>
</completionHelp>
@@ -651,9 +657,10 @@
<description>Subnet topology</description>
</valueHelp>
<constraint>
- <regex>(subnet|point-to-point|net30)</regex>
+ <regex>^(subnet|point-to-point|net30)$</regex>
</constraint>
</properties>
+ <defaultValue>net30</defaultValue>
</leafNode>
</children>
</node>
@@ -777,13 +784,13 @@
<description>TLS v1.2</description>
</valueHelp>
<constraint>
- <regex>(1.0|1.1|1.2)</regex>
+ <regex>^(1.0|1.1|1.2)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="role">
<properties>
- <help>Private key for this host</help>
+ <help>TLS negotiation role</help>
<completionHelp>
<list>active passive</list>
</completionHelp>
@@ -793,10 +800,10 @@
</valueHelp>
<valueHelp>
<format>passive</format>
- <description>Waiting for TLS connections passively</description>
+ <description>Wait for incoming TLS connection</description>
</valueHelp>
<constraint>
- <regex>(active|passive)</regex>
+ <regex>^(active|passive)$</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 3fceb70b6..32ba5ea01 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -23,25 +23,8 @@
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-disable.xml.i>
#include <include/interface-vrf.xml.i>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- #include <include/interface-proxy-arp-pvlan.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/source-interface-ethernet.xml.i>
#include <include/interface-mac.xml.i>
<leafNode name="mode">
@@ -73,7 +56,7 @@
</properties>
<defaultValue>private</defaultValue>
</leafNode>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
#include <include/vif-s.xml.i>
#include <include/vif.xml.i>
</children>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index 64520ce99..c3f178d59 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -22,8 +22,8 @@
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-vrf.xml.i>
#include <include/interface-mtu-64-8024.xml.i>
- #include <include/interface-ipv4.xml.i>
- #include <include/interface-ipv6.xml.i>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
<leafNode name="local-ip">
<properties>
<help>Local IP address for this tunnel</help>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 7fdead16a..c7725fed3 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -35,24 +35,8 @@
</constraint>
</properties>
</leafNode>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
<leafNode name="source-address">
<properties>
<help>VXLAN source address</help>
@@ -67,7 +51,7 @@
</leafNode>
#include <include/source-interface.xml.i>
#include <include/interface-mac.xml.i>
- #include <include/interface-mtu-1200-9000.xml.i>
+ #include <include/interface-mtu-1200-16000.xml.i>
<leafNode name="remote">
<properties>
<help>Remote address of VXLAN tunnel</help>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 981bce826..aa63e4ac7 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -21,7 +21,7 @@
#include <include/interface-disable.xml.i>
#include <include/interface-vrf.xml.i>
#include <include/port-number.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<leafNode name="fwmark">
<properties>
<help>A 32-bit fwmark value set on all outgoing packets</help>
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index 423ec7ba2..fdea1e3ab 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -5,7 +5,7 @@
<tagNode name="wireless" owner="${vyos_conf_scripts_dir}/interfaces-wireless.py">
<properties>
<help>Wireless (WiFi/WLAN) Network Interface</help>
- <priority>400</priority>
+ <priority>318</priority>
<constraint>
<regex>^wlan[0-9]+$</regex>
</constraint>
@@ -58,7 +58,7 @@
<description>Supported channel set width both 20 MHz and 40 MHz with secondary channel below primary channel</description>
</valueHelp>
<constraint>
- <regex>(ht20|ht40\+|ht40-)</regex>
+ <regex>^(ht20|ht40\+|ht40-)$</regex>
</constraint>
<multi/>
</properties>
@@ -108,7 +108,7 @@
<description>Set maximum A-MSDU length to 7935 octets</description>
</valueHelp>
<constraint>
- <regex>(3839|7935)</regex>
+ <regex>^(3839|7935)$</regex>
</constraint>
</properties>
</leafNode>
@@ -127,7 +127,7 @@
<description>Short GI for 40 MHz</description>
</valueHelp>
<constraint>
- <regex>(20|40)</regex>
+ <regex>^(20|40)$</regex>
</constraint>
<multi/>
</properties>
@@ -147,7 +147,7 @@
<description>DYNAMIC Spatial Multiplexing (SM) Power Save</description>
</valueHelp>
<constraint>
- <regex>(static|dynamic)</regex>
+ <regex>^(static|dynamic)$</regex>
</constraint>
</properties>
</leafNode>
@@ -164,7 +164,7 @@
<description>Number of spacial streams that can use RX STBC</description>
</valueHelp>
<constraint>
- <regex>[1-3]+</regex>
+ <regex>^[1-3]+$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -243,7 +243,7 @@
<description>Support for operation as multi user beamformee</description>
</valueHelp>
<constraint>
- <regex>(single-user-beamformer|single-user-beamformee|multi-user-beamformer|multi-user-beamformee)</regex>
+ <regex>^(single-user-beamformer|single-user-beamformee|multi-user-beamformer|multi-user-beamformee)$</regex>
</constraint>
<multi/>
</properties>
@@ -329,7 +329,7 @@
<description>Station can provide VHT MFB in response to VHT MRQ and unsolicited VHT MFB</description>
</valueHelp>
<constraint>
- <regex>(unsolicited|both)</regex>
+ <regex>^(unsolicited|both)$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -361,7 +361,7 @@
<description>ncrease Maximum MPDU length to 11454 octets</description>
</valueHelp>
<constraint>
- <regex>(7991|11454)</regex>
+ <regex>^(7991|11454)$</regex>
</constraint>
</properties>
</leafNode>
@@ -380,7 +380,7 @@
<description>Short GI for 160 MHz</description>
</valueHelp>
<constraint>
- <regex>(80|160)</regex>
+ <regex>^(80|160)$</regex>
</constraint>
<multi/>
</properties>
@@ -398,7 +398,7 @@
<description>Number of spacial streams that can use RX STBC</description>
</valueHelp>
<constraint>
- <regex>[1-4]+</regex>
+ <regex>^[1-4]+$</regex>
</constraint>
<constraintErrorMessage>Invalid capability item</constraintErrorMessage>
</properties>
@@ -443,6 +443,22 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="country-code">
+ <properties>
+ <help>Indicate country in which device is operating</help>
+ <completionHelp>
+ <list>US EU JP DE UK CN ES FR RU</list>
+ </completionHelp>
+ <valueHelp>
+ <format>&lt;code%gt;</format>
+ <description>ISO/IEC 3166-1 Country Code</description>
+ </valueHelp>
+ <constraint>
+ <regex>^[A-Z][A-Z]$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage>
+ </properties>
+ </leafNode>
#include <include/interface-description.xml.i>
#include <include/dhcp-options.xml.i>
#include <include/dhcpv6-options.xml.i>
@@ -461,25 +477,8 @@
<valueless/>
</properties>
</leafNode>
- <node name="ip">
- <children>
- #include <include/interface-arp-cache-timeout.xml.i>
- #include <include/interface-disable-arp-filter.xml.i>
- #include <include/interface-disable-forwarding.xml.i>
- #include <include/interface-enable-arp-accept.xml.i>
- #include <include/interface-enable-arp-announce.xml.i>
- #include <include/interface-enable-arp-ignore.xml.i>
- #include <include/interface-enable-proxy-arp.xml.i>
- #include <include/interface-proxy-arp-pvlan.xml.i>
- </children>
- </node>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4-options.xml.i>
+ #include <include/interface-ipv6-options.xml.i>
#include <include/interface-hw-id.xml.i>
<leafNode name="isolate-stations">
<properties>
@@ -520,7 +519,7 @@
<description>MFP enforced</description>
</valueHelp>
<constraint>
- <regex>(disabled|optional|required)</regex>
+ <regex>^(disabled|optional|required)$</regex>
</constraint>
</properties>
</leafNode>
@@ -681,7 +680,7 @@
<properties>
<help>WPA mode</help>
<completionHelp>
- <list>wpa wpa2 both</list>
+ <list>wpa wpa2 wpa+wpa2 wpa3</list>
</completionHelp>
<valueHelp>
<format>wpa</format>
@@ -692,15 +691,15 @@
<description>WPA2 (full IEEE 802.11i/RSN)</description>
</valueHelp>
<valueHelp>
- <format>both</format>
+ <format>wpa+wpa2</format>
<description>Allow both WPA and WPA2</description>
</valueHelp>
<constraint>
- <regex>^(wpa|wpa2|both)$</regex>
+ <regex>^(wpa|wpa2|wpa\+wpa2|wpa3)$</regex>
</constraint>
<constraintErrorMessage>Unknown WPA mode</constraintErrorMessage>
</properties>
- <defaultValue>both</defaultValue>
+ <defaultValue>wpa+wpa2</defaultValue>
</leafNode>
<leafNode name="passphrase">
<properties>
@@ -782,25 +781,4 @@
</tagNode>
</children>
</node>
- <node name="system">
- <children>
- <leafNode name="wifi-regulatory-domain" owner="${vyos_conf_scripts_dir}/system-wifi-regdom.py">
- <properties>
- <help>Wireless regulatory domain (mandatory)</help>
- <priority>305</priority>
- <completionHelp>
- <list>US EU JP DE UK CN</list>
- </completionHelp>
- <valueHelp>
- <format>&lt;code%gt;</format>
- <description>Country code (ISO/IEC 3166-1)</description>
- </valueHelp>
- <constraint>
- <regex>[A-Z][A-Z]$</regex>
- </constraint>
- <constraintErrorMessage>invalid country code</constraintErrorMessage>
- </properties>
- </leafNode>
- </children>
- </node>
</interfaceDefinition>
diff --git a/interface-definitions/interfaces-wirelessmodem.xml.in b/interface-definitions/interfaces-wirelessmodem.xml.in
index 96604ff00..a0e78a124 100644
--- a/interface-definitions/interfaces-wirelessmodem.xml.in
+++ b/interface-definitions/interfaces-wirelessmodem.xml.in
@@ -66,7 +66,7 @@
</properties>
</leafNode>
#include <include/interface-disable-link-detect.xml.i>
- #include <include/interface-mtu-68-9000.xml.i>
+ #include <include/interface-mtu-68-16000.xml.i>
<node name="ipv6">
<children>
#include <include/ipv6-address.xml.i>
diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in
index 3a4600753..6de8017f2 100644
--- a/interface-definitions/protocols-bgp.xml.in
+++ b/interface-definitions/protocols-bgp.xml.in
@@ -282,7 +282,7 @@
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
- <regex>(en|eth|br|bond|gnv|vxlan|wg|tun)[0-9]+</regex>
+ <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex>
</constraint>
</properties>
<children>
@@ -342,7 +342,6 @@
<leafNode name="description">
<properties>
<help>Description for this neighbor</help>
- <valueless/>
</properties>
</leafNode>
<leafNode name="disable-capability-negotiation">
@@ -629,7 +628,11 @@
<help>Source IP of routing updates</help>
<valueHelp>
<format>ipv4</format>
- <description>IP address of route source</description>
+ <description>IPv4 address of route source</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of route source</description>
</valueHelp>
<valueHelp>
<format>&lt;interface&gt;</format>
@@ -637,7 +640,8 @@
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
- <regex>(en|eth|br|bond|gnv|vxlan|wg|tun)[0-9]+</regex>
+ <validator name="ipv6-address"/>
+ <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex>
</constraint>
</properties>
</leafNode>
@@ -916,12 +920,6 @@
</tagNode>
</children>
</node>
- <leafNode name="enforce-first-as">
- <properties>
- <help>Require first AS in the path to match peer AS number</help>
- <valueless/>
- </properties>
- </leafNode>
<node name="graceful-restart">
<properties>
<help>Graceful restart capability parameters</help>
@@ -1141,7 +1139,11 @@
<help>Source IP of routing updates</help>
<valueHelp>
<format>ipv4</format>
- <description>IP address of route source</description>
+ <description>IPv4 address of route source</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of route source</description>
</valueHelp>
<valueHelp>
<format>&lt;interface&gt;</format>
@@ -1149,7 +1151,8 @@
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
- <regex>(en|eth|br|bond|gnv|vxlan|wg|tun)[0-9]+</regex>
+ <validator name="ipv6-address"/>
+ <regex>(br|bond|dum|en|eth|gnv|lo|peth|tun|vti|vxlan|wg|wlan)[0-9]+</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/protocols-mpls.xml.in b/interface-definitions/protocols-mpls.xml.in
index 3ea610d8b..4df2be4e7 100644
--- a/interface-definitions/protocols-mpls.xml.in
+++ b/interface-definitions/protocols-mpls.xml.in
@@ -11,15 +11,15 @@
<children>
<node name="ldp">
<properties>
- <help>LDP options</help>
+ <help>Label Distribution Protocol (LDP)</help>
</properties>
<children>
<leafNode name="router-id">
<properties>
- <help>x.x.x.x Label Switch Router (LSR) id</help>
+ <help>Label Distribution Protocol (LDP) router ID</help>
<valueHelp>
<format>ipv4</format>
- <description>LSR ipv4 id</description>
+ <description>LDP IPv4 ID</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
@@ -28,10 +28,10 @@
</leafNode>
<tagNode name="neighbor">
<properties>
- <help>LDP Id of neighbor</help>
+ <help>LDP neighbor parameters</help>
<valueHelp>
<format>ipv4</format>
- <description>neighbor IPv4 id</description>
+ <description>Neighbor IPv4 address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
@@ -40,7 +40,35 @@
<children>
<leafNode name="password">
<properties>
- <help>Peer password</help>
+ <help>Neighbor password</help>
+ </properties>
+ </leafNode>
+ <leafNode name="ttl-security">
+ <properties>
+ <help>Neighbor TTL security</help>
+ <completionHelp>
+ <list>disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>&lt;1-254&gt;</format>
+ <description>TTL</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable neighbor TTL security</description>
+ </valueHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="session-holdtime">
+ <properties>
+ <help>Session IPv4 hold time</help>
+ <valueHelp>
+ <format>15-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 15-65535"/>
+ </constraint>
</properties>
</leafNode>
</children>
@@ -54,9 +82,21 @@
</valueHelp>
</properties>
<children>
- <leafNode name="hello-holdtime">
+ <leafNode name="hello-ipv4-holdtime">
+ <properties>
+ <help>Hello IPv4 hold time</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-ipv4-interval">
<properties>
- <help>Hello holdtime</help>
+ <help>Hello IPv4 interval</help>
<valueHelp>
<format>1-65535</format>
<description>Time in seconds</description>
@@ -66,9 +106,21 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="hello-interval">
+ <leafNode name="hello-ipv6-holdtime">
<properties>
- <help>Hello interval</help>
+ <help>Hello IPv6 hold time</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-ipv6-interval">
+ <properties>
+ <help>Hello IPv6 interval</help>
<valueHelp>
<format>1-65535</format>
<description>Time in seconds</description>
@@ -80,7 +132,7 @@
</leafNode>
<leafNode name="session-ipv4-holdtime">
<properties>
- <help>Session ipv4 holdtime</help>
+ <help>Session IPv4 hold time</help>
<valueHelp>
<format>15-65535</format>
<description>Time in seconds</description>
@@ -92,7 +144,7 @@
</leafNode>
<leafNode name="session-ipv6-holdtime">
<properties>
- <help>Session ipv6 holdtime</help>
+ <help>Session IPv6 hold time</help>
<valueHelp>
<format>15-65535</format>
<description>Time in seconds</description>
@@ -104,7 +156,7 @@
</leafNode>
<leafNode name="transport-ipv4-address">
<properties>
- <help>Transport ipv4 address</help>
+ <help>Transport IPv4 address</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 bind as transport</description>
@@ -116,7 +168,7 @@
</leafNode>
<leafNode name="transport-ipv6-address">
<properties>
- <help>Transport ipv6 address</help>
+ <help>Transport IPv6 address</help>
<valueHelp>
<format>ipv6</format>
<description>IPv6 bind as transport</description>
@@ -128,6 +180,132 @@
</leafNode>
</children>
</node>
+ <node name="targeted-neighbor">
+ <properties>
+ <help>Targeted LDP neighbor/session parameters</help>
+ </properties>
+ <children>
+ <node name="ipv4">
+ <properties>
+ <help>Targeted IPv4 neighbor/session parameters</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>Neighbor/session address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Neighbor/session address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="enable">
+ <properties>
+ <help>Accept and respond to targeted hellos</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-interval">
+ <properties>
+ <help>Hello interval</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-holdtime">
+ <properties>
+ <help>Hello hold time</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Targeted IPv6 neighbor/session parameters</help>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>Neighbor/session address</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Neighbor/session address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="enable">
+ <properties>
+ <help>Accept and respond to targeted hellos</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-interval">
+ <properties>
+ <help>Hello interval</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="hello-holdtime">
+ <properties>
+ <help>Hello hold time</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="parameters">
+ <properties>
+ <help>Label Distribution Protocol (LDP) miscellaneous parameters</help>
+ </properties>
+ <children>
+ <leafNode name="cisco-interop-tlv">
+ <properties>
+ <help>Enable Cisco non-compliant format capability TLV</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="transport-prefer-ipv4">
+ <properties>
+ <help>Prefer IPv4 for TCP peer transport connection</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<node name="export">
<properties>
<help>Export parameters</help>
@@ -163,7 +341,7 @@
</node>
<leafNode name="interface">
<properties>
- <help>Listen interface for LDP</help>
+ <help>Enable LDP and neighbor discovery on interface</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
@@ -172,6 +350,31 @@
</leafNode>
</children>
</node>
+ <node name="parameters">
+ <properties>
+ <help>Multiprotocol Label Switching (MPLS) miscellaneous parameters</help>
+ </properties>
+ <children>
+ <leafNode name="no-propagate-ttl">
+ <properties>
+ <help>Disable copy of IP TTL to MPLS TTL</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="maximum-ttl">
+ <properties>
+ <help>Maximum TTL for MPLS packets</help>
+ <valueHelp>
+ <format>1-255</format>
+ <description>Maximum hops allowed</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/interface-definitions/system-ipv6.xml.in b/interface-definitions/system-ipv6.xml.in
index 47fbeb4e1..6ead747a1 100644
--- a/interface-definitions/system-ipv6.xml.in
+++ b/interface-definitions/system-ipv6.xml.in
@@ -22,7 +22,7 @@
</leafNode>
<node name="multipath">
<properties>
- <help>IPv4 multipath settings</help>
+ <help>IPv6 multipath settings</help>
</properties>
<children>
<leafNode name="layer4-hashing">
diff --git a/interface-definitions/system-options.xml.in b/interface-definitions/system-options.xml.in
index a5fec10db..297f5891e 100644
--- a/interface-definitions/system-options.xml.in
+++ b/interface-definitions/system-options.xml.in
@@ -38,11 +38,24 @@
<constraintErrorMessage>Must be ignore, reboot, or poweroff</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="reboot-on-panic">
- <properties>
- <help>Reboot system on kernel panic</help>
- <valueless/>
- </properties>
+ <leafNode name="performance">
+ <properties>
+ <help>Tune system performance</help>
+ <completionHelp>
+ <list>throughput latency</list>
+ </completionHelp>
+ <valueHelp>
+ <format>throughput</format>
+ <description>Tune for maximum network throughput</description>
+ </valueHelp>
+ <valueHelp>
+ <format>latency</format>
+ <description>Tune for low network latency</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(throughput|latency)$</regex>
+ </constraint>
+ </properties>
</leafNode>
<node name="http-client">
<properties>
@@ -53,6 +66,12 @@
#include <include/source-address-ipv4-ipv6.xml.i>
</children>
</node>
+ <leafNode name="reboot-on-panic">
+ <properties>
+ <help>Reboot system on kernel panic</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<node name="ssh-client">
<properties>
<help>Global options used for SSH client</help>
diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in
index 8802c0564..82af86470 100644
--- a/interface-definitions/vpn_l2tp.xml.in
+++ b/interface-definitions/vpn_l2tp.xml.in
@@ -225,6 +225,7 @@
<children>
<tagNode name="server">
<children>
+ #include <include/accel-radius-additions-disable-accounting.xlm.in>
<leafNode name="fail-time">
<properties>
<help>Mark server unavailable for &lt;n&gt; seconds on failure</help>
diff --git a/op-mode-definitions/show-interfaces-bridge.xml b/op-mode-definitions/show-interfaces-bridge.xml
index 85fde95b5..cc4b248b6 100644
--- a/op-mode-definitions/show-interfaces-bridge.xml
+++ b/op-mode-definitions/show-interfaces-bridge.xml
@@ -33,6 +33,12 @@
</properties>
<command>${vyos_op_scripts_dir}/show_interfaces.py --intf-type=bridge --action=show</command>
</leafNode>
+ <leafNode name="vlan">
+ <properties>
+ <help>View the VLAN filter settings of the bridge</help>
+ </properties>
+ <command>/usr/sbin/bridge -c vlan show</command>
+ </leafNode>
</children>
</node>
</children>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 62df3334c..b14f96364 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -18,7 +18,7 @@ A library for retrieving value dicts from VyOS configs in a declarative fashion.
"""
import os
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos.xml import defaults
from vyos import ConfigError
@@ -174,10 +174,10 @@ def T2665_set_dhcpv6pd_defaults(config_dict):
pd_defaults = defaults(['interfaces', 'ethernet', 'dhcpv6-options', 'pd'])
# Implant default dictionary for DHCPv6-PD instances
- if vyos_dict_search('dhcpv6_options.pd.length', config_dict):
+ if dict_search('dhcpv6_options.pd.length', config_dict):
del config_dict['dhcpv6_options']['pd']['length']
- for pd in (vyos_dict_search('dhcpv6_options.pd', config_dict) or []):
+ for pd in (dict_search('dhcpv6_options.pd', config_dict) or []):
config_dict['dhcpv6_options']['pd'][pd] = dict_merge(pd_defaults,
config_dict['dhcpv6_options']['pd'][pd])
@@ -219,6 +219,28 @@ def is_member(conf, interface, intftype=None):
old_level = conf.set_level(old_level)
return ret_val
+def has_vlan_subinterface_configured(conf, intf):
+ """
+ Checks if interface has an VLAN subinterface configured.
+ Checks the following config nodes:
+ 'vif', 'vif-s'
+
+ Returns True if interface has VLAN subinterface configured, False if it doesn't.
+ """
+ from vyos.ifconfig import Section
+ ret = False
+
+ old_level = conf.get_level()
+ conf.set_level([])
+
+ intfpath = 'interfaces ' + Section.get_config_path(intf)
+ if ( conf.exists(f'{intfpath} vif') or
+ conf.exists(f'{intfpath} vif-s')):
+ ret = True
+
+ conf.set_level(old_level)
+ return ret
+
def is_source_interface(conf, interface, intftype=None):
"""
Checks if passed interface is configured as source-interface of other
@@ -332,7 +354,7 @@ def get_interface_dict(config, base, ifname=''):
eui64 = leaf_node_changed(config, ['ipv6', 'address', 'eui64'])
if eui64:
- tmp = vyos_dict_search('ipv6.address', dict)
+ tmp = dict_search('ipv6.address', dict)
if not tmp:
dict.update({'ipv6': {'address': {'eui64_old': eui64}}})
else:
@@ -409,7 +431,7 @@ def get_accel_dict(config, base, chap_secrets):
Return a dictionary with the necessary interface config keys.
"""
from vyos.util import get_half_cpus
- from vyos.validate import is_ipv4
+ from vyos.template import is_ipv4
dict = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
@@ -419,12 +441,12 @@ def get_accel_dict(config, base, chap_secrets):
# defaults include RADIUS server specifics per TAG node which need to be
# added to individual RADIUS servers instead - so we can simply delete them
- if vyos_dict_search('authentication.radius.server', default_values):
+ if dict_search('authentication.radius.server', default_values):
del default_values['authentication']['radius']['server']
# defaults include static-ip address per TAG node which need to be added to
# individual local users instead - so we can simply delete them
- if vyos_dict_search('authentication.local_users.username', default_values):
+ if dict_search('authentication.local_users.username', default_values):
del default_values['authentication']['local_users']['username']
dict = dict_merge(default_values, dict)
@@ -448,18 +470,23 @@ def get_accel_dict(config, base, chap_secrets):
del dict['name_server']
# Add individual RADIUS server default values
- if vyos_dict_search('authentication.radius.server', dict):
+ if dict_search('authentication.radius.server', dict):
default_values = defaults(base + ['authentication', 'radius', 'server'])
- for server in vyos_dict_search('authentication.radius.server', dict):
+ for server in dict_search('authentication.radius.server', dict):
dict['authentication']['radius']['server'][server] = dict_merge(
default_values, dict['authentication']['radius']['server'][server])
+ # Check option "disable-accounting" per server and replace default value from '1813' to '0'
+ # set vpn sstp authentication radius server x.x.x.x disable-accounting
+ if 'disable_accounting' in dict['authentication']['radius']['server'][server]:
+ dict['authentication']['radius']['server'][server]['acct_port'] = '0'
+
# Add individual local-user default values
- if vyos_dict_search('authentication.local_users.username', dict):
+ if dict_search('authentication.local_users.username', dict):
default_values = defaults(base + ['authentication', 'local-users', 'username'])
- for username in vyos_dict_search('authentication.local_users.username', dict):
+ for username in dict_search('authentication.local_users.username', dict):
dict['authentication']['local_users']['username'][username] = dict_merge(
default_values, dict['authentication']['local_users']['username'][username])
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 422483663..2a5dc7af2 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -22,7 +22,7 @@
# makes use of it!
from vyos import ConfigError
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
def verify_mtu(config):
"""
@@ -51,7 +51,7 @@ def verify_mtu_ipv6(config):
recurring validation if the specified MTU can be used when IPv6 is
configured on the interface. IPv6 requires a 1280 bytes MTU.
"""
- from vyos.validate import is_ipv6
+ from vyos.template import is_ipv6
if 'mtu' in config:
# IPv6 minimum required link mtu
min_mtu = 1280
@@ -60,19 +60,19 @@ def verify_mtu_ipv6(config):
error_msg = f'IPv6 address will be configured on interface "{interface}" ' \
f'thus the minimum MTU requirement is {min_mtu}!'
- if not vyos_dict_search('ipv6.address.no_default_link_local', config):
- raise ConfigError('link-local ' + error_msg)
-
- for address in (vyos_dict_search('address', config) or []):
+ for address in (dict_search('address', config) or []):
if address in ['dhcpv6'] or is_ipv6(address):
raise ConfigError(error_msg)
- if vyos_dict_search('ipv6.address.autoconf', config):
- raise ConfigError(error_msg)
+ tmp = dict_search('ipv6.address', config)
+ if tmp and 'no_default_link_local' not in tmp:
+ raise ConfigError('link-local ' + error_msg)
- if vyos_dict_search('ipv6.address.eui64', config):
+ if tmp and 'autoconf' in tmp:
raise ConfigError(error_msg)
+ if tmp and 'eui64' in tmp:
+ raise ConfigError(error_msg)
def verify_vrf(config):
"""
@@ -154,7 +154,7 @@ def verify_dhcpv6(config):
recurring validation of DHCPv6 options which are mutually exclusive.
"""
if 'dhcpv6_options' in config:
- from vyos.util import vyos_dict_search
+ from vyos.util import dict_search
if {'parameters_only', 'temporary'} <= set(config['dhcpv6_options']):
raise ConfigError('DHCPv6 temporary and parameters-only options '
@@ -162,15 +162,15 @@ def verify_dhcpv6(config):
# It is not allowed to have duplicate SLA-IDs as those identify an
# assigned IPv6 subnet from a delegated prefix
- for pd in vyos_dict_search('dhcpv6_options.pd', config):
+ for pd in dict_search('dhcpv6_options.pd', config):
sla_ids = []
- if not vyos_dict_search(f'dhcpv6_options.pd.{pd}.interface', config):
+ if not dict_search(f'dhcpv6_options.pd.{pd}.interface', config):
raise ConfigError('DHCPv6-PD requires an interface where to assign '
'the delegated prefix!')
- for interface in vyos_dict_search(f'dhcpv6_options.pd.{pd}.interface', config):
- sla_id = vyos_dict_search(
+ for interface in dict_search(f'dhcpv6_options.pd.{pd}.interface', config):
+ sla_id = dict_search(
f'dhcpv6_options.pd.{pd}.interface.{interface}.sla_id', config)
sla_ids.append(sla_id)
@@ -211,11 +211,11 @@ def verify_accel_ppp_base_service(config):
on get_config_dict()
"""
# vertify auth settings
- if vyos_dict_search('authentication.mode', config) == 'local':
- if not vyos_dict_search('authentication.local_users', config):
+ if dict_search('authentication.mode', config) == 'local':
+ if not dict_search('authentication.local_users', config):
raise ConfigError('PPPoE local auth mode requires local users to be configured!')
- for user in vyos_dict_search('authentication.local_users.username', config):
+ for user in dict_search('authentication.local_users.username', config):
user_config = config['authentication']['local_users']['username'][user]
if 'password' not in user_config:
@@ -227,11 +227,11 @@ def verify_accel_ppp_base_service(config):
raise ConfigError(f'User "{user}" has rate-limit configured for only one ' \
'direction but both upload and download must be given!')
- elif vyos_dict_search('authentication.mode', config) == 'radius':
- if not vyos_dict_search('authentication.radius.server', config):
+ elif dict_search('authentication.mode', config) == 'radius':
+ if not dict_search('authentication.radius.server', config):
raise ConfigError('RADIUS authentication requires at least one server')
- for server in vyos_dict_search('authentication.radius.server', config):
+ for server in dict_search('authentication.radius.server', config):
radius_config = config['authentication']['radius']['server'][server]
if 'key' not in radius_config:
raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
@@ -259,3 +259,27 @@ def verify_accel_ppp_base_service(config):
if 'delegation_prefix' not in ipv6_pool['delegate'][delegate]:
raise ConfigError('delegation-prefix length required!')
+def verify_diffie_hellman_length(file, min_keysize):
+ """ Verify Diffie-Hellamn keypair length given via file. It must be greater
+ then or equal to min_keysize """
+
+ try:
+ keysize = str(min_keysize)
+ except:
+ return False
+
+ import os
+ import re
+ from vyos.util import cmd
+
+ if os.path.exists(file):
+
+ out = cmd(f'openssl dhparam -inform PEM -in {file} -text')
+ prog = re.compile('\d+\s+bit')
+ if prog.search(out):
+ bits = prog.search(out)[0].split()[0]
+ if int(min_keysize) >= int(bits):
+ return True
+
+ return False
+
diff --git a/python/vyos/ifconfig/bond.py b/python/vyos/ifconfig/bond.py
index 9108fc180..709222b09 100644
--- a/python/vyos/ifconfig/bond.py
+++ b/python/vyos/ifconfig/bond.py
@@ -17,7 +17,7 @@ import os
from vyos.ifconfig.interface import Interface
from vyos.util import cmd
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos.validate import assert_list
from vyos.validate import assert_positive
@@ -360,7 +360,7 @@ class BondIf(Interface):
self.set_arp_ip_target('-' + addr)
# Add configured ARP target addresses
- value = vyos_dict_search('arp_monitor.target', config)
+ value = dict_search('arp_monitor.target', config)
if isinstance(value, str):
value = [value]
if value:
@@ -384,7 +384,7 @@ class BondIf(Interface):
# Removing an interface from a bond will always place the underlaying
# physical interface in admin-down state! If physical interface is
# not disabled, re-enable it.
- if not vyos_dict_search(f'member.interface_remove.{interface}.disable', config):
+ if not dict_search(f'member.interface_remove.{interface}.disable', config):
Interface(interface).set_admin_state('up')
# Bonding policy/mode
@@ -392,7 +392,7 @@ class BondIf(Interface):
if value: self.set_mode(value)
# Add (enslave) interfaces to bond
- value = vyos_dict_search('member.interface', config)
+ value = dict_search('member.interface', config)
for interface in (value or []):
# if we've come here we already verified the interface
# does not have an addresses configured so just flush
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index bf78f8972..7eac9b886 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -19,7 +19,7 @@ from vyos.ifconfig.interface import Interface
from vyos.validate import assert_boolean
from vyos.validate import assert_positive
from vyos.util import cmd
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
@Interface.register
class BridgeIf(Interface):
@@ -41,6 +41,7 @@ class BridgeIf(Interface):
'section': 'bridge',
'prefixes': ['br', ],
'broadcast': True,
+ 'vlan': True,
},
}
@@ -73,6 +74,10 @@ class BridgeIf(Interface):
'validate': assert_boolean,
'location': '/sys/class/net/{ifname}/bridge/stp_state',
},
+ 'vlan_filter': {
+ 'validate': assert_boolean,
+ 'location': '/sys/class/net/{ifname}/bridge/vlan_filtering',
+ },
'multicast_querier': {
'validate': assert_boolean,
'location': '/sys/class/net/{ifname}/bridge/multicast_querier',
@@ -152,6 +157,16 @@ class BridgeIf(Interface):
>>> BridgeIf('br0').set_stp(1)
"""
self.set_interface('stp', state)
+
+ def set_vlan_filter(self, state):
+ """
+ Set bridge Vlan Filter state. 0 -> Vlan Filter disabled, 1 -> Vlan Filter enabled
+
+ Example:
+ >>> from vyos.ifconfig import BridgeIf
+ >>> BridgeIf('br0').set_vlan_filter(1)
+ """
+ self.set_interface('vlan_filter', state)
def set_multicast_querier(self, enable):
"""
@@ -177,8 +192,13 @@ class BridgeIf(Interface):
>>> BridgeIf('br0').add_port('eth0')
>>> BridgeIf('br0').add_port('eth1')
"""
+ # Bridge port handling of wireless interfaces is done by hostapd.
+ if 'wlan' in interface:
+ return
+
return self.set_interface('add_port', interface)
+
def del_port(self, interface):
"""
Remove member port from bridge instance.
@@ -197,6 +217,8 @@ class BridgeIf(Interface):
# call base class first
super().update(config)
+
+ ifname = config['ifname']
# Set ageing time
value = config.get('aging')
@@ -223,17 +245,18 @@ class BridgeIf(Interface):
self.set_stp(value)
# enable or disable IGMP querier
- tmp = vyos_dict_search('igmp.querier', config)
+ tmp = dict_search('igmp.querier', config)
value = '1' if (tmp != None) else '0'
self.set_multicast_querier(value)
# remove interface from bridge
- tmp = vyos_dict_search('member.interface_remove', config)
+ tmp = dict_search('member.interface_remove', config)
for member in (tmp or []):
if member in interfaces():
self.del_port(member)
+ vlan_filter = 0
- tmp = vyos_dict_search('member.interface', config)
+ tmp = dict_search('member.interface', config)
if tmp:
for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
@@ -260,7 +283,51 @@ class BridgeIf(Interface):
if 'priority' in interface_config:
value = interface_config.get('priority')
lower.set_path_priority(value)
-
+
+ tmp = dict_search('native_vlan_removed', interface_config)
+
+ if tmp and 'native_vlan_removed' not in interface_config:
+ vlan_id = tmp
+ cmd = f'bridge vlan add dev {interface} vid 1 pvid untagged master'
+ self._cmd(cmd)
+ cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
+ self._cmd(cmd)
+
+ tmp = dict_search('allowed_vlan_removed', interface_config)
+
+
+ for vlan_id in (tmp or []):
+ cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
+ self._cmd(cmd)
+
+ if 'native_vlan' in interface_config:
+ vlan_filter = 1
+ cmd = f'bridge vlan del dev {interface} vid 1'
+ self._cmd(cmd)
+ vlan_id = interface_config['native_vlan']
+ cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master'
+ self._cmd(cmd)
+ else:
+ cmd = f'bridge vlan del dev {interface} vid 1'
+ self._cmd(cmd)
+
+ if 'allowed_vlan' in interface_config:
+ vlan_filter = 1
+ for vlan in interface_config['allowed_vlan']:
+ cmd = f'bridge vlan add dev {interface} vid {vlan} master'
+ self._cmd(cmd)
+
+
+ vif = dict_search('vif', config)
+ if vif:
+ for vlan_id,vif_config in vif.items():
+ cmd = f'bridge vlan add dev {ifname} vid {vlan_id} self master'
+ self._cmd(cmd)
+
+ # enable/disable Vlan Filter
+ self.set_vlan_filter(vlan_filter)
+
+
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 1d48941f9..12d1ec265 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -19,7 +19,7 @@ import re
from vyos.ifconfig.interface import Interface
from vyos.validate import assert_list
from vyos.util import run
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
@Interface.register
class EthernetIf(Interface):
@@ -282,27 +282,27 @@ class EthernetIf(Interface):
self.set_flow_control(value)
# GRO (generic receive offload)
- tmp = vyos_dict_search('offload_options.generic_receive', config)
+ tmp = dict_search('offload_options.generic_receive', config)
value = tmp if (tmp != None) else 'off'
self.set_gro(value)
# GSO (generic segmentation offload)
- tmp = vyos_dict_search('offload_options.generic_segmentation', config)
+ tmp = dict_search('offload_options.generic_segmentation', config)
value = tmp if (tmp != None) else 'off'
self.set_gso(value)
# scatter-gather option
- tmp = vyos_dict_search('offload_options.scatter_gather', config)
+ tmp = dict_search('offload_options.scatter_gather', config)
value = tmp if (tmp != None) else 'off'
self.set_sg(value)
# TSO (TCP segmentation offloading)
- tmp = vyos_dict_search('offload_options.udp_fragmentation', config)
+ tmp = dict_search('offload_options.udp_fragmentation', config)
value = tmp if (tmp != None) else 'off'
self.set_tso(value)
# UDP fragmentation offloading
- tmp = vyos_dict_search('offload_options.udp_fragmentation', config)
+ tmp = dict_search('offload_options.udp_fragmentation', config)
value = tmp if (tmp != None) else 'off'
self.set_ufo(value)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index ae747e87c..893623284 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -34,9 +34,9 @@ from vyos.configdict import list_diff
from vyos.configdict import dict_merge
from vyos.template import render
from vyos.util import mac2eui64
-from vyos.util import vyos_dict_search
-from vyos.validate import is_ipv4
-from vyos.validate import is_ipv6
+from vyos.util import dict_search
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
from vyos.validate import is_intf_addr_assigned
from vyos.validate import assert_boolean
from vyos.validate import assert_list
@@ -880,7 +880,7 @@ class Interface(Control):
lease_file = f'{config_base}_{ifname}.leases'
if enable and 'disable' not in self._config:
- if vyos_dict_search('dhcp_options.host_name', self._config) == None:
+ if dict_search('dhcp_options.host_name', self._config) == None:
# read configured system hostname.
# maybe change to vyos hostd client ???
hostname = 'vyos'
@@ -959,7 +959,7 @@ class Interface(Control):
# always ensure DHCPv6 client is stopped (when not configured as client
# for IPv6 address or prefix delegation
- dhcpv6pd = vyos_dict_search('dhcpv6_options.pd', config)
+ dhcpv6pd = dict_search('dhcpv6_options.pd', config)
if 'dhcpv6' not in new_addr or dhcpv6pd == None:
self.del_addr('dhcpv6')
@@ -987,64 +987,64 @@ class Interface(Control):
self.set_vrf(config.get('vrf', ''))
# Configure ARP cache timeout in milliseconds - has default value
- tmp = vyos_dict_search('ip.arp_cache_timeout', config)
+ tmp = dict_search('ip.arp_cache_timeout', config)
value = tmp if (tmp != None) else '30'
self.set_arp_cache_tmo(value)
# Configure ARP filter configuration
- tmp = vyos_dict_search('ip.disable_arp_filter', config)
+ tmp = dict_search('ip.disable_arp_filter', config)
value = '0' if (tmp != None) else '1'
self.set_arp_filter(value)
# Configure ARP accept
- tmp = vyos_dict_search('ip.enable_arp_accept', config)
+ tmp = dict_search('ip.enable_arp_accept', config)
value = '1' if (tmp != None) else '0'
self.set_arp_accept(value)
# Configure ARP announce
- tmp = vyos_dict_search('ip.enable_arp_announce', config)
+ tmp = dict_search('ip.enable_arp_announce', config)
value = '1' if (tmp != None) else '0'
self.set_arp_announce(value)
# Configure ARP ignore
- tmp = vyos_dict_search('ip.enable_arp_ignore', config)
+ tmp = dict_search('ip.enable_arp_ignore', config)
value = '1' if (tmp != None) else '0'
self.set_arp_ignore(value)
# Enable proxy-arp on this interface
- tmp = vyos_dict_search('ip.enable_proxy_arp', config)
+ tmp = dict_search('ip.enable_proxy_arp', config)
value = '1' if (tmp != None) else '0'
self.set_proxy_arp(value)
# Enable private VLAN proxy ARP on this interface
- tmp = vyos_dict_search('ip.proxy_arp_pvlan', config)
+ tmp = dict_search('ip.proxy_arp_pvlan', config)
value = '1' if (tmp != None) else '0'
self.set_proxy_arp_pvlan(value)
# IPv4 forwarding
- tmp = vyos_dict_search('ip.disable_forwarding', config)
+ tmp = dict_search('ip.disable_forwarding', config)
value = '0' if (tmp != None) else '1'
self.set_ipv4_forwarding(value)
# IPv6 forwarding
- tmp = vyos_dict_search('ipv6.disable_forwarding', config)
+ tmp = dict_search('ipv6.disable_forwarding', config)
value = '0' if (tmp != None) else '1'
self.set_ipv6_forwarding(value)
# IPv6 router advertisements
- tmp = vyos_dict_search('ipv6.address.autoconf', config)
+ tmp = dict_search('ipv6.address.autoconf', config)
value = '2' if (tmp != None) else '1'
if 'dhcpv6' in new_addr:
value = '2'
self.set_ipv6_accept_ra(value)
# IPv6 address autoconfiguration
- tmp = vyos_dict_search('ipv6.address.autoconf', config)
+ tmp = dict_search('ipv6.address.autoconf', config)
value = '1' if (tmp != None) else '0'
self.set_ipv6_autoconf(value)
# IPv6 Duplicate Address Detection (DAD) tries
- tmp = vyos_dict_search('ipv6.dup_addr_detect_transmits', config)
+ tmp = dict_search('ipv6.dup_addr_detect_transmits', config)
value = tmp if (tmp != None) else '1'
self.set_ipv6_dad_messages(value)
@@ -1053,7 +1053,7 @@ class Interface(Control):
self.set_mtu(config.get('mtu'))
# Delete old IPv6 EUI64 addresses before changing MAC
- tmp = vyos_dict_search('ipv6.address.eui64_old', config)
+ tmp = dict_search('ipv6.address.eui64_old', config)
if tmp:
for addr in tmp:
self.del_ipv6_eui64_address(addr)
@@ -1068,7 +1068,7 @@ class Interface(Control):
self.set_mac(mac)
# Manage IPv6 link-local addresses
- tmp = vyos_dict_search('ipv6.address.no_default_link_local', config)
+ tmp = dict_search('ipv6.address.no_default_link_local', config)
# we must check explicitly for None type as if the key is set we will
# get an empty dict (<class 'dict'>)
if tmp is not None:
@@ -1077,7 +1077,7 @@ class Interface(Control):
self.add_ipv6_eui64_address('fe80::/64')
# Add IPv6 EUI-based addresses
- tmp = vyos_dict_search('ipv6.address.eui64', config)
+ tmp = dict_search('ipv6.address.eui64', config)
if tmp:
for addr in tmp:
self.add_ipv6_eui64_address(addr)
diff --git a/python/vyos/ifconfig/l2tpv3.py b/python/vyos/ifconfig/l2tpv3.py
index 5fd90f9cf..8ed3d5afb 100644
--- a/python/vyos/ifconfig/l2tpv3.py
+++ b/python/vyos/ifconfig/l2tpv3.py
@@ -68,8 +68,9 @@ class L2TPv3If(Interface):
cmd += ' peer_session_id {peer_session_id}'
self._cmd(cmd.format(**self.config))
- # interface is always A/D down. It needs to be enabled explicitly
- self.set_admin_state('down')
+ # No need for interface shut down. There exist no function to permanently enable tunnel.
+ # But you can disable interface permanently with shutdown/disable command.
+ self.set_admin_state('up')
def remove(self):
"""
@@ -93,4 +94,24 @@ class L2TPv3If(Interface):
if self.config['tunnel_id']:
cmd = 'ip l2tp del tunnel tunnel_id {tunnel_id}'
self._cmd(cmd.format(**self.config))
+
+
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+
+ # call base class first
+ super().update(config)
+
+ # Enable/Disable of an interface must always be done at the end of the
+ # derived class to make use of the ref-counting set_admin_state()
+ # function. We will only enable the interface if 'up' was called as
+ # often as 'down'. This is required by some interface implementations
+ # as certain parameters can only be changed when the interface is
+ # in admin-down state. This ensures the link does not flap during
+ # reconfiguration.
+ state = 'down' if 'disable' in config else 'up'
+ self.set_admin_state(state)
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 964ffe383..4122d1a2f 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -179,7 +179,7 @@ class GRETapIf(_Tunnel):
default = {'type': 'gretap'}
required = ['local', ]
- options = ['local', 'remote', ]
+ options = ['local', 'remote', 'ttl',]
updates = ['mtu', ]
create = 'ip link add {ifname} type {type}'
diff --git a/python/vyos/ifconfig/vtun.py b/python/vyos/ifconfig/vtun.py
index b25e32d63..99a592b3e 100644
--- a/python/vyos/ifconfig/vtun.py
+++ b/python/vyos/ifconfig/vtun.py
@@ -19,6 +19,7 @@ from vyos.ifconfig.interface import Interface
class VTunIf(Interface):
default = {
'type': 'vtun',
+ 'device_type': 'tun',
}
definition = {
**Interface.definition,
@@ -28,15 +29,44 @@ class VTunIf(Interface):
'bridgeable': True,
},
}
-
- # stub this interface is created in the configure script
+ options = Interface.options + ['device_type']
def _create(self):
- # we can not create this interface as it is managed outside
- # it requires configuring OpenVPN
+ """ Depending on OpenVPN operation mode the interface is created
+ immediately (e.g. Server mode) or once the connection to the server is
+ established (client mode). The latter will only be brought up once the
+ server can be reached, thus we might need to create this interface in
+ advance for the service to be operational. """
+ try:
+ cmd = 'openvpn --mktun --dev-type {device_type} --dev {ifname}'.format(**self.config)
+ return self._cmd(cmd)
+ except PermissionError:
+ # interface created by OpenVPN daemon in the meantime ...
+ pass
+
+ def add_addr(self, addr):
+ # IP addresses are managed by OpenVPN daemon
pass
- def _delete(self):
- # we can not create this interface as it is managed outside
- # it requires configuring OpenVPN
+ def del_addr(self, addr):
+ # IP addresses are managed by OpenVPN daemon
pass
+
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+
+ # call base class first
+ super().update(config)
+
+ # Enable/Disable of an interface must always be done at the end of the
+ # derived class to make use of the ref-counting set_admin_state()
+ # function. We will only enable the interface if 'up' was called as
+ # often as 'down'. This is required by some interface implementations
+ # as certain parameters can only be changed when the interface is
+ # in admin-down state. This ensures the link does not flap during
+ # reconfiguration.
+ state = 'down' if 'disable' in config else 'up'
+ self.set_admin_state(state)
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index d8e89229d..da3bd4e89 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -24,7 +24,7 @@ from hurry.filesize import alternative
from vyos.config import Config
from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
-from vyos.validate import is_ipv6
+from vyos.template import is_ipv6
class WireGuardOperational(Operational):
def _dump(self):
diff --git a/python/vyos/template.py b/python/vyos/template.py
index c88ab04a0..58ba75972 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -16,7 +16,6 @@
import functools
import os
-from ipaddress import ip_network
from jinja2 import Environment
from jinja2 import FileSystemLoader
from vyos.defaults import directories
@@ -124,20 +123,95 @@ def render(
# Custom template filters follow #
##################################
-
-@register_filter("address_from_cidr")
-def vyos_address_from_cidr(text):
+@register_filter('address_from_cidr')
+def address_from_cidr(text):
""" Take an IPv4/IPv6 CIDR prefix and convert the network to an "address".
Example:
192.0.2.0/24 -> 192.0.2.0, 2001:db8::/48 -> 2001:db8::
"""
- return ip_network(text).network_address
-
+ from ipaddress import ip_network
+ return str(ip_network(text).network_address)
-@register_filter("netmask_from_cidr")
-def vyos_netmask_from_cidr(text):
- """ Take an IPv4/IPv6 CIDR prefix and convert the prefix length to a "subnet mask".
+@register_filter('netmask_from_cidr')
+def netmask_from_cidr(text):
+ """ Take CIDR prefix and convert the prefix length to a "subnet mask".
+ Example:
+ - 192.0.2.0/24 -> 255.255.255.0
+ - 2001:db8::/48 -> ffff:ffff:ffff::
+ """
+ from ipaddress import ip_network
+ return str(ip_network(text).netmask)
+
+@register_filter('is_ip')
+def is_ip(addr):
+ """ Check addr if it is an IPv4 or IPv6 address """
+ return is_ipv4(addr) or is_ipv6(addr)
+
+@register_filter('is_ipv4')
+def is_ipv4(text):
+ """ Filter IP address, return True on IPv4 address, False otherwise """
+ from ipaddress import ip_interface
+ try: return ip_interface(text).version == 4
+ except: return False
+
+@register_filter('ipv6')
+def is_ipv6(text):
+ """ Filter IP address, return True on IPv6 address, False otherwise """
+ from ipaddress import ip_interface
+ try: return ip_interface(text).version == 6
+ except: return False
+
+@register_filter('first_host_address')
+def first_host_address(text):
+ """ Return first usable (host) IP address from given prefix.
Example:
- 192.0.2.0/24 -> 255.255.255.0, 2001:db8::/48 -> ffff:ffff:ffff::
+ - 10.0.0.0/24 -> 10.0.0.1
+ - 2001:db8::/64 -> 2001:db8::
+ """
+ from ipaddress import ip_interface
+ from ipaddress import IPv4Network
+ from ipaddress import IPv6Network
+
+ addr = ip_interface(text)
+ if addr.version == 4:
+ return str(addr.ip +1)
+ return str(addr.ip)
+
+@register_filter('last_host_address')
+def last_host_address(text):
+ """ Return first usable IP address from given prefix.
+ Example:
+ - 10.0.0.0/24 -> 10.0.0.254
+ - 2001:db8::/64 -> 2001:db8::ffff:ffff:ffff:ffff
+ """
+ from ipaddress import ip_interface
+ from ipaddress import IPv4Network
+ from ipaddress import IPv6Network
+
+ addr = ip_interface(text)
+ if addr.version == 4:
+ return str(IPv4Network(addr).broadcast_address - 1)
+
+ return str(IPv6Network(addr).broadcast_address)
+
+@register_filter('inc_ip')
+def inc_ip(address, increment):
+ """ Increment given IP address by 'increment'
+
+ Example (inc by 2):
+ - 10.0.0.0/24 -> 10.0.0.2
+ - 2001:db8::/64 -> 2001:db8::2
+ """
+ from ipaddress import ip_interface
+ return str(ip_interface(address).ip + int(increment))
+
+@register_filter('dec_ip')
+def dec_ip(address, decrement):
+ """ Decrement given IP address by 'decrement'
+
+ Example (inc by 2):
+ - 10.0.0.0/24 -> 10.0.0.2
+ - 2001:db8::/64 -> 2001:db8::2
"""
- return ip_network(text).netmask
+ from ipaddress import ip_interface
+ return str(ip_interface(address).ip - int(decrement))
diff --git a/python/vyos/util.py b/python/vyos/util.py
index b5f0ea36e..fc6915687 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -581,77 +581,6 @@ def get_half_cpus():
cpu /= 2
return int(cpu)
-def ifname_from_config(conf):
- """
- Gets interface name with VLANs from current config level.
- Level must be at the interface whose name we want.
-
- Example:
- >>> from vyos.util import ifname_from_config
- >>> from vyos.config import Config
- >>> conf = Config()
- >>> conf.set_level('interfaces ethernet eth0 vif-s 1 vif-c 2')
- >>> ifname_from_config(conf)
- 'eth0.1.2'
- """
- level = conf.get_level()
-
- # vlans
- if level[-2] == 'vif' or level[-2] == 'vif-s':
- return level[-3] + '.' + level[-1]
- if level[-2] == 'vif-c':
- return level[-5] + '.' + level[-3] + '.' + level[-1]
-
- # no vlans
- return level[-1]
-
-def get_bridge_member_config(conf, br, intf):
- """
- Gets bridge port (member) configuration
-
- Arguments:
- conf: Config
- br: bridge name
- intf: interface name
-
- Returns:
- dict with the configuration
- False if bridge or bridge port doesn't exist
- """
- old_level = conf.get_level()
- conf.set_level([])
-
- bridge = f'interfaces bridge {br}'
- member = f'{bridge} member interface {intf}'
- if not ( conf.exists(bridge) and conf.exists(member) ):
- return False
-
- # default bridge port configuration
- # cost and priority initialized with linux defaults
- # by reading /sys/devices/virtual/net/br0/brif/eth2/{path_cost,priority}
- # after adding interface to bridge after reboot
- memberconf = {
- 'cost': 100,
- 'priority': 32,
- 'arp_cache_tmo': 30,
- 'disable_link_detect': 1,
- }
-
- if conf.exists(f'{member} cost'):
- memberconf['cost'] = int(conf.return_value(f'{member} cost'))
-
- if conf.exists(f'{member} priority'):
- memberconf['priority'] = int(conf.return_value(f'{member} priority'))
-
- if conf.exists(f'{bridge} ip arp-cache-timeout'):
- memberconf['arp_cache_tmo'] = int(conf.return_value(f'{bridge} ip arp-cache-timeout'))
-
- if conf.exists(f'{bridge} disable-link-detect'):
- memberconf['disable_link_detect'] = 2
-
- conf.set_level(old_level)
- return memberconf
-
def check_kmod(k_mod):
""" Common utility function to load required kernel modules on demand """
from vyos import ConfigError
@@ -674,7 +603,7 @@ def find_device_file(device):
return None
-def vyos_dict_search(path, dict):
+def dict_search(path, dict):
""" Traverse Python dictionary (dict) delimited by dot (.).
Return value of key if found, None otherwise.
diff --git a/python/vyos/validate.py b/python/vyos/validate.py
index 691cf3c8e..84a7bc2de 100644
--- a/python/vyos/validate.py
+++ b/python/vyos/validate.py
@@ -1,4 +1,4 @@
-# Copyright 2018 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2018-2020 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
@@ -13,11 +13,7 @@
# 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 json
-import socket
import netifaces
-import ipaddress
-
from vyos.util import cmd
# Important note when you are adding new validation functions:
@@ -29,60 +25,26 @@ from vyos.util import cmd
# parameters with default will be left unset
# all other paramters will receive the value to check
-
-def is_ip(addr):
- """
- Check addr if it is an IPv4 or IPv6 address
- """
- return is_ipv4(addr) or is_ipv6(addr)
-
-def is_ipv4(addr):
- """
- Check addr if it is an IPv4 address/network. Returns True/False
- """
-
- # With the below statement we can check for IPv4 networks and host
- # addresses at the same time
- try:
- if ipaddress.ip_address(addr.split(r'/')[0]).version == 4:
- return True
- except:
- pass
-
- return False
-
-def is_ipv6(addr):
- """
- Check addr if it is an IPv6 address/network. Returns True/False
- """
-
- # With the below statement we can check for IPv4 networks and host
- # addresses at the same time
- try:
- if ipaddress.ip_network(addr.split(r'/')[0]).version == 6:
- return True
- except:
- pass
-
- return False
-
def is_ipv6_link_local(addr):
- """
- Check addr if it is an IPv6 link-local address/network. Returns True/False
- """
-
+ """ Check if addrsss is an IPv6 link-local address. Returns True/False """
+ from ipaddress import IPv6Address
+ from vyos.template import is_ipv6
addr = addr.split('%')[0]
if is_ipv6(addr):
- if ipaddress.IPv6Address(addr).is_link_local:
+ if IPv6Address(addr).is_link_local:
return True
return False
def _are_same_ip(one, two):
+ from socket import AF_INET
+ from socket import AF_INET6
+ from socket import inet_pton
+ from vyos.template import is_ipv4
# compare the binary representation of the IP
- f_one = socket.AF_INET if is_ipv4(one) else socket.AF_INET6
- s_two = socket.AF_INET if is_ipv4(two) else socket.AF_INET6
- return socket.inet_pton(f_one, one) == socket.inet_pton(f_one, two)
+ f_one = AF_INET if is_ipv4(one) else AF_INET6
+ s_two = AF_INET if is_ipv4(two) else AF_INET6
+ return inet_pton(f_one, one) == inet_pton(f_one, two)
def is_intf_addr_assigned(intf, addr):
if '/' in addr:
@@ -96,6 +58,7 @@ def _is_intf_addr_assigned(intf, address, netmask=''):
It can check both a single IP address (e.g. 192.0.2.1 or a assigned CIDR
address 192.0.2.1/24.
"""
+ from vyos.template import is_ipv4
# check if the requested address type is configured at all
# {
@@ -149,10 +112,9 @@ def is_addr_assigned(addr):
return False
def is_loopback_addr(addr):
- """
- Check if supplied IPv4/IPv6 address is a loopback address
- """
- return ipaddress.ip_address(addr).is_loopback
+ """ Check if supplied IPv4/IPv6 address is a loopback address """
+ from ipaddress import ip_address
+ return ip_address(addr).is_loopback
def is_subnet_connected(subnet, primary=False):
"""
@@ -165,6 +127,9 @@ def is_subnet_connected(subnet, primary=False):
Return True/False
"""
+ from ipaddress import ip_address
+ from ipaddress import ip_network
+ from vyos.template import is_ipv6
# determine IP version (AF_INET or AF_INET6) depending on passed address
addr_type = netifaces.AF_INET
@@ -180,7 +145,7 @@ def is_subnet_connected(subnet, primary=False):
# only support the primary address :(
if primary:
ip = netifaces.ifaddresses(interface)[addr_type][0]['addr']
- if ipaddress.ip_address(ip) in ipaddress.ip_network(subnet):
+ if ip_address(ip) in ip_network(subnet):
return True
else:
# Check every assigned IP address if it is connected to the subnet
@@ -188,7 +153,7 @@ def is_subnet_connected(subnet, primary=False):
for ip in netifaces.ifaddresses(interface)[addr_type]:
# remove interface extension (e.g. %eth0) that gets thrown on the end of _some_ addrs
addr = ip['addr'].split('%')[0]
- if ipaddress.ip_address(addr) in ipaddress.ip_network(subnet):
+ if ip_address(addr) in ip_network(subnet):
return True
return False
@@ -224,6 +189,7 @@ def assert_positive(n, smaller=0):
def assert_mtu(mtu, ifname):
assert_number(mtu)
+ import json
out = cmd(f'ip -j -d link show dev {ifname}')
# [{"ifindex":2,"ifname":"eth0","flags":["BROADCAST","MULTICAST","UP","LOWER_UP"],"mtu":1500,"qdisc":"pfifo_fast","operstate":"UP","linkmode":"DEFAULT","group":"default","txqlen":1000,"link_type":"ether","address":"08:00:27:d9:5b:04","broadcast":"ff:ff:ff:ff:ff:ff","promiscuity":0,"min_mtu":46,"max_mtu":16110,"inet6_addr_gen_mode":"none","num_tx_queues":1,"num_rx_queues":1,"gso_max_size":65536,"gso_max_segs":65535}]
parsed = json.loads(out)[0]
@@ -265,7 +231,6 @@ def assert_mac(m):
if octets[:5] == (0, 0, 94, 0, 1):
raise ValueError(f'{m} is a VRRP MAC address')
-
def has_address_configured(conf, intf):
"""
Checks if interface has an address configured.
diff --git a/smoketest/bin/vyos-configtest b/smoketest/bin/vyos-configtest
new file mode 100755
index 000000000..3e42b0380
--- /dev/null
+++ b/smoketest/bin/vyos-configtest
@@ -0,0 +1,82 @@
+#!/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 os
+import sys
+import time
+import logging
+import unittest
+
+from vyos.configsession import ConfigSession, ConfigSessionError
+from vyos import ConfigError
+
+config_dir = '/usr/libexec/vyos/tests/config'
+save_config = '/tmp/vyos-configtest-save'
+
+class DynamicClassBase(unittest.TestCase):
+ def setUp(self):
+ self._start_time = time.time()
+ self.session = ConfigSession(os.getpid())
+ self.session.save_config(save_config)
+
+ def tearDown(self):
+ self.session.migrate_and_load_config(save_config)
+ self.session.commit()
+ log.info(f" time: {time.time() - self._start_time:.3f}")
+ del self.session
+ try:
+ os.remove(save_config)
+ except OSError:
+ pass
+
+def make_test_function(filename):
+ def test_config_load(self):
+ config_path = os.path.join(config_dir, filename)
+ self.session.migrate_and_load_config(config_path)
+ try:
+ self.session.commit()
+ except (ConfigError, ConfigSessionError):
+ self.session.discard()
+ self.fail()
+ return test_config_load
+
+def class_name_from_func_name(s):
+ res = ''.join(str.capitalize(x) for x in s.split('_'))
+ return res
+
+if __name__ == '__main__':
+ logging.basicConfig(stream=sys.stdout, level=logging.DEBUG,
+ format='%(message)s')
+ log = logging.getLogger("TestConfigLog")
+
+ start_time = time.time()
+ log.info("Generating tests")
+
+ (_, _, config_list) = next(iter(os.walk(config_dir)))
+ config_list.sort()
+
+ for config in config_list:
+ test_func = make_test_function(config)
+
+ func_name = config.replace('-', '_')
+ klassname = f'TestConfig{class_name_from_func_name(func_name)}'
+ globals()[klassname] = type(klassname,
+ (DynamicClassBase,),
+ {f'test_{func_name}': test_func})
+
+ log.info(f"... completed: {time.time() - start_time:.6f}")
+
+ unittest.main(verbosity=2)
diff --git a/smoketest/configs/pppoe-client b/smoketest/configs/pppoe-client
new file mode 100644
index 000000000..ef6a26423
--- /dev/null
+++ b/smoketest/configs/pppoe-client
@@ -0,0 +1,62 @@
+interfaces {
+ ethernet eth0 {
+ }
+ loopback lo {
+ }
+ pppoe pppoe0 {
+ authentication {
+ password bar
+ user foo
+ }
+ connect-on-demand
+ default-route auto
+ mtu 1492
+ source-interface eth0
+ }
+}
+service {
+ ssh {
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/
+ plaintext-password ""
+ }
+ }
+ }
+ ntp {
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1"
+// Release version: 1.3-rolling-202010241631
diff --git a/smoketest/configs/pppoe-server b/smoketest/configs/pppoe-server
new file mode 100644
index 000000000..7e4ccc80e
--- /dev/null
+++ b/smoketest/configs/pppoe-server
@@ -0,0 +1,94 @@
+interfaces {
+ ethernet eth0 {
+ address dhcp
+ }
+ ethernet eth1 {
+ address 192.168.0.1/24
+ }
+ ethernet eth2 {
+ }
+ loopback lo {
+ }
+}
+nat {
+ source {
+ rule 100 {
+ outbound-interface eth0
+ source {
+ address 192.168.0.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ }
+}
+service {
+ pppoe-server {
+ access-concentrator ACN
+ authentication {
+ local-users {
+ username foo {
+ password bar
+ rate-limit {
+ download 20480
+ upload 10240
+ }
+ }
+ }
+ mode local
+ }
+ client-ip-pool {
+ start 192.168.0.100
+ stop 192.168.0.200
+ }
+ gateway-address 192.168.0.2
+ interface eth2 {
+ }
+ name-server 192.168.0.1
+ }
+ ssh {
+ }
+}
+system {
+ config-management {
+ commit-revisions 100
+ }
+ console {
+ device ttyS0 {
+ speed 115200
+ }
+ }
+ host-name vyos
+ login {
+ user vyos {
+ authentication {
+ encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0
+ plaintext-password ""
+ }
+ }
+ }
+ ntp {
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1"
+// Release version: 1.3-rolling-202010260127
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index 56cbf1dd4..e46a16137 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -20,10 +20,10 @@ from configparser import ConfigParser
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
+from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import get_half_cpus
from vyos.util import process_named_running
-from vyos.validate import is_ipv4
class BasicAccelPPPTest:
class BaseTest(unittest.TestCase):
@@ -192,3 +192,23 @@ class BasicAccelPPPTest:
# Check for running process
self.assertTrue(process_named_running(self._process_name))
+
+ #
+ # Disable Radius Accounting
+ #
+ self.delete(['authentication', 'radius', 'server', radius_server, 'acct-port'])
+ self.set(['authentication', 'radius', 'server', radius_server, 'disable-accounting'])
+
+ # commit changes
+ self.session.commit()
+
+ conf.read(self._config_file)
+
+ server = conf['radius']['server'].split(',')
+ self.assertEqual(radius_server, server[0])
+ self.assertEqual(radius_key, server[1])
+ self.assertEqual(f'auth-port={radius_port}', server[2])
+ self.assertEqual(f'acct-port=0', server[3])
+ self.assertEqual(f'req-limit=0', server[4])
+ self.assertEqual(f'fail-time=0', server[5])
+
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index c6bb5bd1a..e02424073 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -22,8 +22,9 @@ from vyos.configsession import ConfigSession
from vyos.ifconfig import Interface
from vyos.util import read_file
from vyos.util import cmd
-from vyos.util import vyos_dict_search
-from vyos.validate import is_intf_addr_assigned, is_ipv6_link_local
+from vyos.util import dict_search
+from vyos.validate import is_intf_addr_assigned
+from vyos.validate import is_ipv6_link_local
class BasicInterfaceTest:
class BaseTest(unittest.TestCase):
@@ -162,16 +163,45 @@ class BasicInterfaceTest:
""" Testcase if MTU can be changed on interface """
if not self._test_mtu:
return None
+
+ for intf in self._interfaces:
+ base = self._base_path + [intf]
+ self.session.set(base + ['mtu', self._mtu])
+ for option in self._options.get(intf, []):
+ self.session.set(base + option.split())
+
+ # commit interface changes
+ self.session.commit()
+
+ # verify changed MTU
+ for intf in self._interfaces:
+ self._mtu_test(intf)
+
+ def test_change_mtu_1200(self):
+ """ Testcase if MTU can be changed to 1200 on non IPv6 enabled interfaces """
+ if not self._test_mtu:
+ return None
+
+ old_mtu = self._mtu
+ self._mtu = '1200'
+
for intf in self._interfaces:
base = self._base_path + [intf]
self.session.set(base + ['mtu', self._mtu])
+ self.session.set(base + ['ipv6', 'address', 'no-default-link-local'])
+
for option in self._options.get(intf, []):
self.session.set(base + option.split())
+ # commit interface changes
self.session.commit()
+
+ # verify changed MTU
for intf in self._interfaces:
self._mtu_test(intf)
+ self._mtu = old_mtu
+
def test_8021q_vlan(self):
""" Testcase for 802.1q VLAN interfaces """
if not self._test_vlan:
@@ -219,7 +249,7 @@ class BasicInterfaceTest:
for interface in self._interfaces:
for vif_s in self._qinq_range:
tmp = json.loads(cmd(f'ip -d -j link show dev {interface}.{vif_s}'))[0]
- self.assertEqual(vyos_dict_search('linkinfo.info_data.protocol', tmp), '802.1ad')
+ self.assertEqual(dict_search('linkinfo.info_data.protocol', tmp), '802.1ad')
for vif_c in self._vlan_range:
vif = f'{interface}.{vif_s}.{vif_c}'
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index a1359680b..3b7f1bc9a 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -21,12 +21,16 @@ from base_interfaces_test import BasicInterfaceTest
from glob import glob
from netifaces import interfaces
from vyos.ifconfig import Section
+from vyos.util import cmd
+import json
class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
def setUp(self):
super().setUp()
self._test_ipv6 = True
+ self._test_vlan = True
+ self._test_qinq = True
self._base_path = ['interfaces', 'bridge']
self._interfaces = ['br0']
@@ -78,6 +82,94 @@ class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.delete(self._base_path + [interface, 'member'])
self.session.commit()
+
+ def test_vlan_filter(self):
+ """ Add member interface to bridge and set VLAN filter """
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ self.session.set(base + ['address', '192.0.2.1/24'])
+ self.session.set(base + ['vif', '2','address','192.0.3.1/24'])
+
+ vlan_id = 101
+ allowed_vlan = 2
+ allowed_vlan_range = '4-9'
+ # assign members to bridge interface
+ for member in self._members:
+ base_member = base + ['member', 'interface', member]
+ self.session.set(base_member + ['allowed-vlan', str(allowed_vlan)])
+ self.session.set(base_member + ['allowed-vlan', allowed_vlan_range])
+ self.session.set(base_member + ['native-vlan', str(vlan_id)])
+ vlan_id += 1
+
+ # commit config
+ self.session.commit()
+
+ # Detect the vlan filter function
+ for interface in self._interfaces:
+ with open(f'/sys/class/net/{interface}/bridge/vlan_filtering', 'r') as f:
+ flags = f.read()
+ self.assertEqual(int(flags), 1)
+
+ # Execute the program to obtain status information
+
+ json_data = cmd('bridge -j vlan show', shell=True)
+
+ vlan_filter_status = None
+
+ vlan_filter_status = json.loads(json_data)
+
+
+ if vlan_filter_status is not None:
+ for interface_status in vlan_filter_status:
+ ifname = interface_status['ifname']
+ for interface in self._members:
+ vlan_success = 0;
+ if interface == ifname:
+ vlans_status = interface_status['vlans']
+ for vlan_status in vlans_status:
+ vlan_id = vlan_status['vlan']
+ flag_num = 0
+ if 'flags' in vlan_status:
+ flags = vlan_status['flags']
+ for flag in flags:
+ flag_num = flag_num +1
+ if vlan_id == 2:
+ if flag_num == 0:
+ vlan_success = vlan_success + 1
+ else:
+ for id in range(4,10):
+ if vlan_id == id:
+ if flag_num == 0:
+ vlan_success = vlan_success + 1
+ if vlan_id >= 101:
+ if flag_num == 2:
+ vlan_success = vlan_success + 1
+ if vlan_success >= 7:
+ self.assertTrue(True)
+ else:
+ self.assertTrue(False)
+
+ else:
+ self.assertTrue(False)
+
+
+
+
+ # check member interfaces are added on the bridge
+
+ for interface in self._interfaces:
+ bridge_members = []
+ for tmp in glob(f'/sys/class/net/{interface}/lower_*'):
+ bridge_members.append(os.path.basename(tmp).replace('lower_', ''))
+
+ for member in self._members:
+ self.assertIn(member, bridge_members)
+
+ # delete all members
+ for interface in self._interfaces:
+ self.session.delete(self._base_path + [interface, 'member'])
+
+ self.session.commit()
def test_vlan_members(self):
""" T2945: ensure that VIFs are not dropped from bridge """
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index 5cc62e3e2..41e48c2f8 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -26,6 +26,11 @@ from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from vyos.util import process_named_running
from vyos.util import read_file
+from vyos.template import address_from_cidr
+from vyos.template import dec_ip
+from vyos.template import inc_ip
+from vyos.template import last_host_address
+from vyos.template import netmask_from_cidr
PROCESS_NAME = 'openvpn'
@@ -35,13 +40,15 @@ ssl_cert = '/config/auth/ovpn_test_server.pem'
ssl_key = '/config/auth/ovpn_test_server.key'
dh_pem = '/config/auth/ovpn_test_dh.pem'
s2s_key = '/config/auth/ovpn_test_site2site.key'
+auth_key = '/config/auth/ovpn_test_tls_auth.key'
remote_port = '1194'
protocol = 'udp'
path = []
interface = ''
remote_host = ''
-vrf_name = 'mgmt'
+vrf_name = 'orange'
+dummy_if = 'dum1301'
def get_vrf(interface):
for upper in glob(f'/sys/class/net/{interface}/upper*'):
@@ -54,20 +61,88 @@ def get_vrf(interface):
class TestInterfacesOpenVPN(unittest.TestCase):
def setUp(self):
self.session = ConfigSession(os.getpid())
- self.session.set(['interfaces', 'dummy', 'dum1328', 'address', '192.0.2.1/24'])
+ self.session.set(['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32'])
self.session.set(['vrf', 'name', vrf_name, 'table', '12345'])
def tearDown(self):
self.session.delete(base_path)
- self.session.delete(['interfaces', 'dummy', 'dum1328'])
- self.session.delete(['vrf', 'name', vrf_name])
+ self.session.delete(['interfaces', 'dummy', dummy_if])
+ self.session.delete(['vrf'])
self.session.commit()
del self.session
- def test_client_interfaces(self):
- """ Create OpenVPN client interfaces connecting to different
- server IP addresses. Validate configuration afterwards. """
+ def test_client_verify(self):
+ """
+ Create OpenVPN client interface and test verify() steps.
+ """
+ interface = 'vtun2000'
+ path = base_path + [interface]
+ self.session.set(path + ['mode', 'client'])
+
+ # check validate() - cannot specify both "encryption disable-ncp" and
+ # "encryption ncp-ciphers" at the same time
+ self.session.set(path + ['encryption', 'disable-ncp'])
+ self.session.set(path + ['encryption', 'ncp-ciphers', 'aes192gcm'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['encryption', 'ncp-ciphers'])
+
+ # check validate() - cannot specify local-port in client mode
+ self.session.set(path + ['local-port', '5000'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['local-port'])
+
+ # check validate() - cannot specify local-host in client mode
+ self.session.set(path + ['local-host', '127.0.0.1'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['local-host'])
+
+ # check validate() - cannot specify protocol tcp-passive in client mode
+ self.session.set(path + ['protocol', 'tcp-passive'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['protocol'])
+
+ # check validate() - remote-host must be set in client mode
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['remote-host', '192.0.9.9'])
+
+ # check validate() - cannot specify "tls dh-file" in client mode
+ self.session.set(path + ['tls', 'dh-file', dh_pem])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['tls'])
+
+ # check validate() - must specify one of "shared-secret-key-file" and "tls"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['shared-secret-key-file', s2s_key])
+
+ # check validate() - must specify one of "shared-secret-key-file" and "tls"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['shared-secret-key-file', s2s_key])
+
+ self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
+ self.session.set(path + ['tls', 'cert-file', ssl_cert])
+ self.session.set(path + ['tls', 'key-file', ssl_key])
+
+ # client commit must pass
+ self.session.commit()
+
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertIn(interface, interfaces())
+
+ def test_client_interfaces(self):
+ """
+ Create OpenVPN client interfaces connecting to different
+ server IP addresses. Validate configuration afterwards.
+ """
num_range = range(10, 15)
for ii in num_range:
interface = f'vtun{ii}'
@@ -122,11 +197,215 @@ class TestInterfacesOpenVPN(unittest.TestCase):
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
+ def test_server_verify(self):
+ """
+ Create one OpenVPN server interface and check required verify() stages
+ """
+ interface = 'vtun5000'
+ path = base_path + [interface]
+
+ # check validate() - must speciy operating mode
+ self.session.set(path)
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['mode', 'server'])
+
+ # check validate() - cannot specify protocol tcp-active in server mode
+ self.session.set(path + ['protocol', 'tcp-active'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['protocol'])
+
+ # check validate() - cannot specify local-port in client mode
+ self.session.set(path + ['remote-port', '5000'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['remote-port'])
+
+ # check validate() - cannot specify local-host in client mode
+ self.session.set(path + ['remote-host', '127.0.0.1'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['remote-host'])
+
+ # check validate() - must specify "tls dh-file" when not using EC keys
+ # in server mode
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['tls', 'dh-file', dh_pem])
+
+ # check validate() - must specify "server subnet" or add interface to
+ # bridge in server mode
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ # check validate() - server client-ip-pool is too large
+ # [100.64.0.4 -> 100.127.255.251 = 4194295], maximum is 65536 addresses.
+ self.session.set(path + ['server', 'subnet', '100.64.0.0/10'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ # check validate() - cannot specify more than 1 IPv4 and 1 IPv6 server subnet
+ self.session.set(path + ['server', 'subnet', '100.64.0.0/20'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['server', 'subnet', '100.64.0.0/10'])
+
+ # check validate() - must specify "tls ca-cert-file"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
+
+ # check validate() - must specify "tls cert-file"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['tls', 'cert-file', ssl_cert])
+
+ # check validate() - must specify "tls key-file"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['tls', 'key-file', ssl_key])
+
+ # check validate() - cannot specify "tls role" in client-server mode'
+ self.session.set(path + ['tls', 'role', 'active'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ # check validate() - cannot specify "tls role" in client-server mode'
+ self.session.set(path + ['tls', 'auth-file', auth_key])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ # check validate() - cannot specify "tcp-passive" when "tls role" is "active"
+ self.session.set(path + ['protocol', 'tcp-passive'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['protocol'])
+
+ # check validate() - cannot specify "tls dh-file" when "tls role" is "active"
+ self.session.set(path + ['tls', 'dh-file', dh_pem])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['tls', 'dh-file'])
+
+ # Now test the other path with tls role passive
+ self.session.set(path + ['tls', 'role', 'passive'])
+ # check validate() - cannot specify "tcp-active" when "tls role" is "passive"
+ self.session.set(path + ['protocol', 'tcp-active'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['protocol'])
+
+
+ # check validate() - must specify "tls dh-file" when "tls role" is "passive"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['tls', 'dh-file', dh_pem])
+
+ self.session.commit()
+
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertIn(interface, interfaces())
+
+ def test_server_subnet_topology(self):
+ """
+ Create OpenVPN server interfaces using different client subnets.
+ Validate configuration afterwards.
+ """
+ auth_hash = 'sha256'
+ num_range = range(20, 25)
+ port = ''
+ client1_routes = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
+ for ii in num_range:
+ interface = f'vtun{ii}'
+ subnet = f'192.0.{ii}.0/24'
+ client_ip = inc_ip(subnet, '5')
+ path = base_path + [interface]
+ port = str(2000 + ii)
+
+ self.session.set(path + ['device-type', 'tun'])
+ self.session.set(path + ['encryption', 'cipher', 'aes192'])
+ self.session.set(path + ['hash', auth_hash])
+ self.session.set(path + ['mode', 'server'])
+ self.session.set(path + ['local-port', port])
+ self.session.set(path + ['server', 'subnet', subnet])
+ self.session.set(path + ['server', 'topology', 'subnet'])
+
+ # clients
+ self.session.set(path + ['server', 'client', 'client1', 'ip', client_ip])
+ for route in client1_routes:
+ self.session.set(path + ['server', 'client', 'client1', 'subnet', route])
+
+ self.session.set(path + ['replace-default-route'])
+ self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
+ self.session.set(path + ['tls', 'cert-file', ssl_cert])
+ self.session.set(path + ['tls', 'key-file', ssl_key])
+ self.session.set(path + ['tls', 'dh-file', dh_pem])
+ self.session.set(path + ['vrf', vrf_name])
+
+ self.session.commit()
+
+ for ii in num_range:
+ interface = f'vtun{ii}'
+ subnet = f'192.0.{ii}.0/24'
+
+ start_addr = inc_ip(subnet, '2')
+ stop_addr = last_host_address(subnet)
+
+ client_ip = inc_ip(subnet, '5')
+ client_netmask = netmask_from_cidr(subnet)
+
+ port = str(2000 + ii)
+
+ config_file = f'/run/openvpn/{interface}.conf'
+ client_config_file = f'/run/openvpn/ccd/{interface}/client1'
+ config = read_file(config_file)
+
+ self.assertIn(f'dev {interface}', config)
+ self.assertIn(f'dev-type tun', config)
+ self.assertIn(f'persist-key', config)
+ self.assertIn(f'proto udp', config) # default protocol
+ self.assertIn(f'auth {auth_hash}', config)
+ self.assertIn(f'cipher aes-192-cbc', config)
+ self.assertIn(f'topology subnet', config)
+ self.assertIn(f'lport {port}', config)
+ self.assertIn(f'push "redirect-gateway def1"', config)
+
+ # TLS options
+ self.assertIn(f'ca {ca_cert}', config)
+ self.assertIn(f'cert {ssl_cert}', config)
+ self.assertIn(f'key {ssl_key}', config)
+ self.assertIn(f'dh {dh_pem}', config)
+
+ # IP pool configuration
+ netmask = IPv4Network(subnet).netmask
+ network = IPv4Network(subnet).network_address
+ self.assertIn(f'server {network} {netmask} nopool', config)
+
+ # Verify client
+ client_config = read_file(client_config_file)
+
+ self.assertIn(f'ifconfig-push {client_ip} {client_netmask}', client_config)
+ for route in client1_routes:
+ self.assertIn('iroute {} {}'.format(address_from_cidr(route), netmask_from_cidr(route)), client_config)
+
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertEqual(get_vrf(interface), vrf_name)
+ self.assertIn(interface, interfaces())
+
+ # check that no interface remained after deleting them
+ self.session.delete(base_path)
+ self.session.commit()
- def test_server_interfaces(self):
- """ Create OpenVPN server interfaces using different client subnets.
- Validate configuration afterwards. """
+ for ii in num_range:
+ interface = f'vtun{ii}'
+ self.assertNotIn(interface, interfaces())
+ def test_server_net30_topology(self):
+ """
+ Create OpenVPN server interfaces (net30) using different client
+ subnets. Validate configuration afterwards.
+ """
auth_hash = 'sha256'
num_range = range(20, 25)
port = ''
@@ -142,6 +421,8 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.session.set(path + ['mode', 'server'])
self.session.set(path + ['local-port', port])
self.session.set(path + ['server', 'subnet', subnet])
+ self.session.set(path + ['server', 'topology', 'net30'])
+ self.session.set(path + ['replace-default-route'])
self.session.set(path + ['tls', 'ca-cert-file', ca_cert])
self.session.set(path + ['tls', 'cert-file', ssl_cert])
self.session.set(path + ['tls', 'key-file', ssl_key])
@@ -153,6 +434,8 @@ class TestInterfacesOpenVPN(unittest.TestCase):
for ii in num_range:
interface = f'vtun{ii}'
subnet = f'192.0.{ii}.0/24'
+ start_addr = inc_ip(subnet, '4')
+ stop_addr = dec_ip(last_host_address(subnet), '1')
port = str(2000 + ii)
config_file = f'/run/openvpn/{interface}.conf'
@@ -166,6 +449,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
self.assertIn(f'cipher aes-192-cbc', config)
self.assertIn(f'topology net30', config)
self.assertIn(f'lport {port}', config)
+ self.assertIn(f'push "redirect-gateway def1"', config)
# TLS options
self.assertIn(f'ca {ca_cert}', config)
@@ -177,6 +461,7 @@ class TestInterfacesOpenVPN(unittest.TestCase):
netmask = IPv4Network(subnet).netmask
network = IPv4Network(subnet).network_address
self.assertIn(f'server {network} {netmask} nopool', config)
+ self.assertIn(f'ifconfig-pool {start_addr} {stop_addr}', config)
self.assertTrue(process_named_running(PROCESS_NAME))
self.assertEqual(get_vrf(interface), vrf_name)
@@ -190,9 +475,69 @@ class TestInterfacesOpenVPN(unittest.TestCase):
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
+ def test_site2site_verify(self):
+ """
+ Create one OpenVPN site2site interface and check required verify() stages
+ """
+ interface = 'vtun5000'
+ path = base_path + [interface]
+
+ self.session.set(path + ['mode', 'site-to-site'])
+
+ # check validate() - encryption ncp-ciphers cannot be specified in site-to-site mode
+ self.session.set(path + ['encryption', 'ncp-ciphers', 'aes192gcm'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['encryption'])
+
+ # check validate() - must specify "local-address" or add interface to bridge
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['local-address', '10.0.0.1'])
+ self.session.set(path + ['local-address', '2001:db8:1::1'])
+
+ # check validate() - cannot specify more than 1 IPv4 local-address
+ self.session.set(path + ['local-address', '10.0.0.2'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['local-address', '10.0.0.2'])
+
+ # check validate() - cannot specify more than 1 IPv6 local-address
+ self.session.set(path + ['local-address', '2001:db8:1::2'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['local-address', '2001:db8:1::2'])
+
+ # check validate() - IPv4 "local-address" requires IPv4 "remote-address"
+ # or IPv4 "local-address subnet"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['remote-address', '192.168.0.1'])
+ self.session.set(path + ['remote-address', '2001:db8:ffff::1'])
+
+ # check validate() - Cannot specify more than 1 IPv4 "remote-address"
+ self.session.set(path + ['remote-address', '192.168.0.2'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['remote-address', '192.168.0.2'])
+
+ # check validate() - Cannot specify more than 1 IPv6 "remote-address"
+ self.session.set(path + ['remote-address', '2001:db8:ffff::2'])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(path + ['remote-address', '2001:db8:ffff::2'])
+
+ # check validate() - Must specify one of "shared-secret-key-file" and "tls"
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(path + ['shared-secret-key-file', s2s_key])
+
+ self.session.commit()
def test_site2site_interfaces(self):
- """ Create two OpenVPN site-to-site interfaces """
+ """
+ Create two OpenVPN site-to-site interfaces
+ """
num_range = range(30, 35)
port = ''
local_address = ''
@@ -250,32 +595,33 @@ if __name__ == '__main__':
subject = '/C=DE/ST=BY/O=VyOS/localityName=Cloud/commonName=vyos/' \
'organizationalUnitName=VyOS/emailAddress=maintainers@vyos.io/'
- if (not os.path.isfile(ssl_key) and not os.path.isfile(ssl_cert) and
- not os.path.isfile(ca_cert) and not os.path.isfile(dh_pem) and
- not os.path.isfile(s2s_key)):
-
+ if not (os.path.isfile(ssl_key) and os.path.isfile(ssl_cert)):
# Generate mandatory SSL certificate
tmp = f'openssl req -newkey rsa:4096 -new -nodes -x509 -days 3650 '\
f'-keyout {ssl_key} -out {ssl_cert} -subj {subject}'
- out = cmd(tmp)
- print(out)
+ print(cmd(tmp))
+ if not os.path.isfile(ca_cert):
# Generate "CA"
tmp = f'openssl req -new -x509 -key {ssl_key} -out {ca_cert} -subj {subject}'
- out = cmd(tmp)
- print(out)
+ print(cmd(tmp))
+ if not os.path.isfile(dh_pem):
# Generate "DH" key
tmp = f'openssl dhparam -out {dh_pem} 2048'
- out = cmd(tmp)
- print(out)
+ print(cmd(tmp))
+ if not os.path.isfile(s2s_key):
# Generate site-2-site key
tmp = f'openvpn --genkey --secret {s2s_key}'
- out = cmd(tmp)
- print(out)
+ print(cmd(tmp))
+
+ if not os.path.isfile(auth_key):
+ # Generate TLS auth key
+ tmp = f'openvpn --genkey --secret {auth_key}'
+ print(cmd(tmp))
- for file in [ca_cert, ssl_cert, ssl_key, dh_pem, s2s_key]:
- cmd(f'sudo chown openvpn:openvpn {file}')
+ for file in [ca_cert, ssl_cert, ssl_key, dh_pem, s2s_key, auth_key]:
+ cmd(f'sudo chown openvpn:openvpn {file}')
unittest.main()
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 7611ffe26..1033196ce 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -14,95 +14,289 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
import unittest
+import json
from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
from base_interfaces_test import BasicInterfaceTest
-class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
- # encoding, tunnel endpoint (v4/v6), address (v4/v6)
- _valid = [
- ('gre', 4, 4),
- ('gre', 4, 6),
- ('ip6gre', 6, 4),
- ('ip6gre', 6, 6),
- ('gre-bridge', 4, 4),
- ('ipip', 4, 4),
- ('ipip', 4, 6),
- ('ipip6', 6, 4),
- ('ipip6', 6, 6),
- ('ip6ip6', 6, 6),
- ('sit', 4, 6),
- ]
-
- local = {
- 4: '10.100.{}.1/24',
- 6: '2001:db8:{}::1/64',
- }
-
- remote = {
- 4: '192.0.{}.1',
- 6: '2002::{}:1',
- }
-
- address = {
- 4: '10.100.{}.1/24',
- 6: '2001:db8:{}::1/64',
- }
+remote_ip4 = '192.0.2.100'
+remote_ip6 = '2001:db8::ffff'
+source_if = 'dum2222'
+mtu = 1476
+
+def tunnel_conf(interface):
+ tmp = cmd(f'ip -d -j link show {interface}')
+ # {'address': '2.2.2.2',
+ # 'broadcast': '192.0.2.10',
+ # 'flags': ['POINTOPOINT', 'NOARP', 'UP', 'LOWER_UP'],
+ # 'group': 'default',
+ # 'gso_max_segs': 65535,
+ # 'gso_max_size': 65536,
+ # 'ifindex': 10,
+ # 'ifname': 'tun10',
+ # 'inet6_addr_gen_mode': 'none',
+ # 'link': None,
+ # 'link_pointtopoint': True,
+ # 'link_type': 'gre',
+ # 'linkinfo': {'info_data': {'local': '2.2.2.2',
+ # 'pmtudisc': True,
+ # 'remote': '192.0.2.10',
+ # 'tos': '0x1',
+ # 'ttl': 255},
+ # 'info_kind': 'gre'},
+ # 'linkmode': 'DEFAULT',
+ # 'max_mtu': 65511,
+ # 'min_mtu': 68,
+ # 'mtu': 1476,
+ # 'num_rx_queues': 1,
+ # 'num_tx_queues': 1,
+ # 'operstate': 'UNKNOWN',
+ # 'promiscuity': 0,
+ # 'qdisc': 'noqueue',
+ # 'txqlen': 1000}
+ return json.loads(tmp)[0]
+class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
def setUp(self):
- local = {}
- remote = {}
- address = {}
+ super().setUp()
- self._intf_dummy = ['interfaces', 'dummy']
self._base_path = ['interfaces', 'tunnel']
- self._interfaces = ['tun{}'.format(n) for n in range(len(self._valid))]
-
self._test_mtu = True
- super().setUp()
- for number in range(len(self._valid)):
- dum4 = 'dum4{}'.format(number)
- dum6 = 'dum6{}'.format(number)
+ self.local_v4 = '192.0.2.1'
+ self.local_v6 = '2001:db8::1'
+
+ self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32'])
+ self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128'])
- ipv4 = self.local[4].format(number)
- ipv6 = self.local[6].format(number)
+ self._options = {
+ 'tun10': ['encapsulation ipip', 'remote-ip 192.0.2.10', 'local-ip ' + self.local_v4],
+ 'tun20': ['encapsulation gre', 'remote-ip 192.0.2.20', 'local-ip ' + self.local_v4],
+ }
- local.setdefault(4, {})[number] = ipv4
- local.setdefault(6, {})[number] = ipv6
+ self._interfaces = list(self._options)
- ipv4 = self.remote[4].format(number)
- ipv6 = self.remote[6].format(number)
+ def tearDown(self):
+ self.session.delete(['interfaces', 'dummy', source_if])
+ super().tearDown()
- remote.setdefault(4, {})[number] = ipv4
- remote.setdefault(6, {})[number] = ipv6
+ def test_ipip(self):
+ interface = 'tun100'
+ encapsulation = 'ipip'
+ local_if_addr = '10.10.10.1/24'
- ipv4 = self.address[4].format(number)
- ipv6 = self.address[6].format(number)
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
- address.setdefault(4, {})[number] = ipv4
- address.setdefault(6, {})[number] = ipv6
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
+
+ # missing required option remote for ipip
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
+
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
- self.session.set(self._intf_dummy + [dum4, 'address', ipv4])
- self.session.set(self._intf_dummy + [dum6, 'address', ipv6])
self.session.commit()
- for number, (encap, p2p, addr) in enumerate(self._valid):
- intf = 'tun%d' % number
- tunnel = {}
- tunnel['encapsulation'] = encap
- tunnel['local-ip'] = local[p2p][number].split('/')[0]
- tunnel['remote-ip'] = remote[p2p][number].split('/')[0]
- tunnel['address'] = address[addr][number]
- for name in tunnel:
- self.session.set(self._base_path + [intf, name, tunnel[name]])
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
- def tearDown(self):
- self.session.delete(self._intf_dummy)
- super().tearDown()
+ self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
+
+ def test_ipip6(self):
+ interface = 'tun110'
+ encapsulation = 'ipip6'
+ local_if_addr = '10.10.10.1/24'
+
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
+
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
+
+ # missing required option remote for ipip
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
+
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual('tunnel6', conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
+
+ self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+
+ def test_ip6ip6(self):
+ interface = 'tun120'
+ encapsulation = 'ip6ip6'
+ local_if_addr = '2001:db8:f00::1/24'
+
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
+
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
+
+ # missing required option remote for ipip
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
+
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual('tunnel6', conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
+
+ self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+
+ def test_gre_ipv4(self):
+ interface = 'tun200'
+ encapsulation = 'gre'
+ local_if_addr = '172.16.1.1/24'
+
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
+
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
+
+ # No assertion is raised for GRE remote-ip when missing
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
+
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
+
+ self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
+
+
+ def test_gre_ipv6(self):
+ interface = 'tun210'
+ encapsulation = 'ip6gre'
+ local_if_addr = '2001:db8:f01::1/24'
+
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
+
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
+
+ # No assertion is raised for GRE remote-ip when missing
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
+
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
+
+ self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+
+
+ def test_sit(self):
+ interface = 'tun300'
+ encapsulation = 'sit'
+ local_if_addr = '172.16.2.1/24'
+
+ self.session.set(self._base_path + [interface, 'address', local_if_addr])
+
+ # Must provide an "encapsulation" for tunnel tun10
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
+
+ # Must configure either local-ip or dhcp-interface
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
+
+ # No assertion is raised for GRE remote-ip when missing
+ self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
+
+ # Source interface can not be used with si
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(self._base_path + [interface, 'source-interface'])
+
+ self.session.commit()
+
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ self.assertEqual(mtu, conf['mtu'])
+
+ self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 0e93b6432..65cf127ce 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -18,14 +18,16 @@ import os
import re
import unittest
+from vyos.configsession import ConfigSessionError
from base_interfaces_test import BasicInterfaceTest
+
from vyos.util import process_named_running
from vyos.util import check_kmod
from vyos.util import read_file
def get_config_value(interface, key):
tmp = read_file(f'/run/hostapd/{interface}.conf')
- tmp = re.findall(r'\n?{}=+(.*)'.format(key), tmp)
+ tmp = re.findall(f'{key}=+(.*)', tmp)
return tmp[0]
class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
@@ -36,15 +38,14 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
self._options = {
'wlan0': ['physical-device phy0', 'ssid VyOS-WIFI-0',
'type station', 'address 192.0.2.1/30'],
- 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1',
+ 'wlan1': ['physical-device phy0', 'ssid VyOS-WIFI-1', 'country-code SE',
'type access-point', 'address 192.0.2.5/30', 'channel 0'],
'wlan10': ['physical-device phy1', 'ssid VyOS-WIFI-2',
'type station', 'address 192.0.2.9/30'],
- 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3',
+ 'wlan11': ['physical-device phy1', 'ssid VyOS-WIFI-3', 'country-code SE',
'type access-point', 'address 192.0.2.13/30', 'channel 0'],
}
self._interfaces = list(self._options)
- self.session.set(['system', 'wifi-regulatory-domain', 'SE'])
def test_add_address_single(self):
""" derived method to check if member interfaces are enslaved properly """
@@ -73,6 +74,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
self.session.set(self._base_path + [interface, 'ssid', ssid])
self.session.set(self._base_path + [interface, 'type', 'access-point'])
self.session.set(self._base_path + [interface, 'channel', channel])
+ self.session.set(self._base_path + [interface, 'country-code', 'SE'])
# auto-powersave is special
self.session.set(self._base_path + [interface, 'capabilities', 'ht', 'auto-powersave'])
@@ -114,6 +116,8 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
#
# Validate Config
#
+ tmp = get_config_value(interface, 'interface')
+ self.assertEqual(interface, tmp)
# ssid
tmp = get_config_value(interface, 'ssid')
@@ -138,6 +142,74 @@ class WirelessInterfaceTest(BasicInterfaceTest.BaseTest):
# Check for running process
self.assertTrue(process_named_running('hostapd'))
+ def test_hostapd_wpa_config(self):
+ """ Check if hostapd config is properly generated """
+
+ # Only set the hostapd (access-point) options
+ interface = 'wlan0'
+ phy = 'phy0'
+ ssid = 'ssid'
+ channel = '0'
+ wpa_key = 'VyOSVyOSVyOS'
+ mode = 'n'
+ country = 'DE'
+
+ self.session.set(self._base_path + [interface, 'physical-device', phy])
+ self.session.set(self._base_path + [interface, 'type', 'access-point'])
+ self.session.set(self._base_path + [interface, 'mode', mode])
+
+ # SSID must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'ssid', ssid])
+
+ # Channel must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'channel', channel])
+
+ # Country-Code must be set
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.set(self._base_path + [interface, 'country-code', country])
+
+ self.session.set(self._base_path + [interface, 'security', 'wpa', 'mode', 'wpa2'])
+ self.session.set(self._base_path + [interface, 'security', 'wpa', 'passphrase', wpa_key])
+
+ self.session.commit()
+
+ #
+ # Validate Config
+ #
+ tmp = get_config_value(interface, 'interface')
+ self.assertEqual(interface, tmp)
+
+ tmp = get_config_value(interface, 'hw_mode')
+ # rewrite special mode
+ if mode == 'n': mode = 'g'
+ self.assertEqual(mode, tmp)
+
+ # WPA key
+ tmp = get_config_value(interface, 'wpa')
+ self.assertEqual('2', tmp)
+ tmp = get_config_value(interface, 'wpa_passphrase')
+ self.assertEqual(wpa_key, tmp)
+
+ # SSID
+ tmp = get_config_value(interface, 'ssid')
+ self.assertEqual(ssid, tmp)
+
+ # channel
+ tmp = get_config_value(interface, 'channel')
+ self.assertEqual(channel, tmp)
+
+ # Country code
+ tmp = get_config_value(interface, 'country_code')
+ self.assertEqual(country, tmp)
+
+ # Check for running process
+ self.assertTrue(process_named_running('hostapd'))
+
if __name__ == '__main__':
check_kmod('mac80211_hwsim')
unittest.main()
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index b5bde743b..43392bde3 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -22,7 +22,7 @@ import unittest
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
base_path = ['nat']
src_path = base_path + ['source']
@@ -73,10 +73,10 @@ class TestNAT(unittest.TestCase):
self.assertEqual(data['family'], 'ip')
self.assertEqual(data['table'], 'nat')
- iface = vyos_dict_search('match.right', data['expr'][0])
- direction = vyos_dict_search('match.left.payload.field', data['expr'][1])
- address = vyos_dict_search('match.right.prefix.addr', data['expr'][1])
- mask = vyos_dict_search('match.right.prefix.len', data['expr'][1])
+ iface = dict_search('match.right', data['expr'][0])
+ direction = dict_search('match.left.payload.field', data['expr'][1])
+ address = dict_search('match.right.prefix.addr', data['expr'][1])
+ mask = dict_search('match.right.prefix.len', data['expr'][1])
if int(rule) < 200:
self.assertEqual(direction, 'saddr')
@@ -127,11 +127,11 @@ class TestNAT(unittest.TestCase):
self.assertEqual(data['family'], 'ip')
self.assertEqual(data['table'], 'nat')
- iface = vyos_dict_search('match.right', data['expr'][0])
- direction = vyos_dict_search('match.left.payload.field', data['expr'][1])
- protocol = vyos_dict_search('match.left.payload.protocol', data['expr'][1])
- dnat_addr = vyos_dict_search('dnat.addr', data['expr'][3])
- dnat_port = vyos_dict_search('dnat.port', data['expr'][3])
+ iface = dict_search('match.right', data['expr'][0])
+ direction = dict_search('match.left.payload.field', data['expr'][1])
+ protocol = dict_search('match.left.payload.protocol', data['expr'][1])
+ dnat_addr = dict_search('dnat.addr', data['expr'][3])
+ dnat_port = dict_search('dnat.port', data['expr'][3])
self.assertEqual(direction, 'sport')
self.assertEqual(dnat_addr, '192.0.2.1')
diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py
index 067a3c76b..2c2e2181b 100755
--- a/smoketest/scripts/cli/test_service_snmp.py
+++ b/smoketest/scripts/cli/test_service_snmp.py
@@ -18,10 +18,10 @@ import os
import re
import unittest
-from vyos.validate import is_ipv4
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
+from vyos.template import is_ipv4
from vyos.util import read_file
from vyos.util import process_named_running
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index 0cd00ccce..ea70d8e03 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -20,12 +20,14 @@ import unittest
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
-from vyos.util import read_file
+from vyos.util import cmd
from vyos.util import process_named_running
+from vyos.util import read_file
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/ssh/sshd_config'
base_path = ['service', 'ssh']
+vrf = 'ssh-test'
def get_config_value(key):
tmp = read_file(SSHD_CONF)
@@ -44,6 +46,8 @@ class TestServiceSSH(unittest.TestCase):
self.session.delete(base_path)
# restore "plain" SSH access
self.session.set(base_path)
+ # delete VRF
+ self.session.delete(['vrf', 'name', vrf])
self.session.commit()
del self.session
@@ -129,5 +133,31 @@ class TestServiceSSH(unittest.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ def test_ssh_vrf(self):
+ """ Check if SSH service can be bound to given VRF """
+ port = '22'
+ self.session.set(base_path + ['port', port])
+ self.session.set(base_path + ['vrf', vrf])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+
+ self.session.set(['vrf', 'name', vrf, 'table', '1001'])
+
+ # commit changes
+ self.session.commit()
+
+ # Check configured port
+ tmp = get_config_value('Port')
+ self.assertIn(port, tmp)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # Check for process in VRF
+ tmp = cmd(f'ip vrf pids {vrf}')
+ self.assertIn(PROCESS_NAME, tmp)
+
if __name__ == '__main__':
unittest.main()
diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py
index 92333392a..3210e622f 100755
--- a/smoketest/scripts/cli/test_service_tftp-server.py
+++ b/smoketest/scripts/cli/test_service_tftp-server.py
@@ -24,7 +24,7 @@ from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import read_file
from vyos.util import process_named_running
-from vyos.validate import is_ipv6
+from vyos.template import is_ipv6
PROCESS_NAME = 'in.tftpd'
base_path = ['service', 'tftp-server']
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index 4f62b62d5..822a9aff2 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -20,8 +20,8 @@ import unittest
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
-from vyos.template import vyos_address_from_cidr
-from vyos.template import vyos_netmask_from_cidr
+from vyos.template import address_from_cidr
+from vyos.template import netmask_from_cidr
from vyos.util import read_file
from vyos.util import process_named_running
@@ -86,8 +86,8 @@ class TestSystemNTP(unittest.TestCase):
# Check generated client address configuration
for network in networks:
- network_address = vyos_address_from_cidr(network)
- network_netmask = vyos_netmask_from_cidr(network)
+ network_address = address_from_cidr(network)
+ network_netmask = netmask_from_cidr(network)
tmp = get_config_value(f'restrict {network_address}')[0]
test = f'mask {network_netmask} nomodify notrap nopeer'
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index 1777d4db7..c2868e078 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -22,10 +22,11 @@ from copy import deepcopy
from vyos.config import Config
from vyos.template import render
+from vyos.template import is_ipv6
from vyos.util import call
-from vyos.validate import is_subnet_connected, is_ipv6
-from vyos import ConfigError
+from vyos.validate import is_subnet_connected
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 2187b3c73..ef52cbfd3 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -21,12 +21,12 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.hostsd_client import Client as hostsd_client
+from vyos.template import render
+from vyos.template import is_ipv6
from vyos.util import call
from vyos.util import chown
-from vyos.util import vyos_dict_search
-from vyos.template import render
+from vyos.util import dict_search
from vyos.xml import defaults
-from vyos.validate import is_ipv6
from vyos import ConfigError
from vyos import airbag
@@ -94,7 +94,7 @@ def verify(dns):
if 'allow_from' not in dns:
raise ConfigError('DNS forwarding requires an allow-from network')
- # we can not use vyos_dict_search() when testing for domain servers
+ # we can not use dict_search() when testing for domain servers
# as a domain will contains dot's which is out dictionary delimiter.
if 'domain' in dns:
for domain in dns['domain']:
diff --git a/src/conf_mode/intel_qat.py b/src/conf_mode/intel_qat.py
index ab98cbc03..dd04a002d 100755
--- a/src/conf_mode/intel_qat.py
+++ b/src/conf_mode/intel_qat.py
@@ -66,8 +66,14 @@ def verify(qat):
# Check if QAT device exist
output, err = popen('lspci -nn', decode='utf-8')
if not err:
+ # PCI id | Chipset
+ # 19e2 -> C3xx
+ # 37c8 -> C62x
+ # 0435 -> DH895
+ # 6f54 -> D15xx
+ # 18ee -> QAT_200XX
data = re.findall(
- '(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)|(8086:1f18)', output)
+ '(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)|(8086:18ee)', output)
# If QAT devices found
if not data:
raise ConfigError('No QAT acceleration device found')
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index ea9bd54d4..1a549f27d 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -33,7 +33,7 @@ from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
from vyos.ifconfig import Section
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos.validate import has_address_configured
from vyos import ConfigError
from vyos import airbag
@@ -101,7 +101,7 @@ def get_config(config=None):
# also present the interfaces to be removed from the bond as dictionary
bond['member'].update({'interface_remove': tmp})
- if vyos_dict_search('member.interface', bond):
+ if dict_search('member.interface', bond):
for interface, interface_config in bond['member']['interface'].items():
# Check if member interface is already member of another bridge
tmp = is_member(conf, interface, 'bridge')
@@ -151,7 +151,7 @@ def verify(bond):
verify_vlan_config(bond)
bond_name = bond['ifname']
- if vyos_dict_search('member.interface', bond):
+ if dict_search('member.interface', bond):
for interface, interface_config in bond['member']['interface'].items():
error_msg = f'Can not add interface "{interface}" to bond, '
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 4aeb8fc67..076bdb63e 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -18,12 +18,15 @@ import os
from sys import exit
from netifaces import interfaces
+import re
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import node_changed
+from vyos.configdict import leaf_node_changed
from vyos.configdict import is_member
from vyos.configdict import is_source_interface
+from vyos.configdict import has_vlan_subinterface_configured
from vyos.configdict import dict_merge
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_vrf
@@ -32,12 +35,32 @@ from vyos.validate import has_address_configured
from vyos.xml import defaults
from vyos.util import cmd
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
+def helper_check_removed_vlan(conf,bridge,key,key_mangling):
+ key_update = re.sub(key_mangling[0], key_mangling[1], key)
+ if dict_search('member.interface', bridge):
+ for interface in bridge['member']['interface']:
+ tmp = leaf_node_changed(conf, ['member', 'interface',interface,key])
+ if tmp:
+ if 'member' in bridge:
+ if 'interface' in bridge['member']:
+ if interface in bridge['member']['interface']:
+ bridge['member']['interface'][interface].update({f'{key_update}_removed': tmp })
+ else:
+ bridge['member']['interface'].update({interface: {f'{key_update}_removed': tmp }})
+ else:
+ bridge['member'].update({ 'interface': {interface: {f'{key_update}_removed': tmp }}})
+ else:
+ bridge.update({'member': { 'interface': {interface: {f'{key_update}_removed': tmp }}}})
+
+ return bridge
+
+
def get_config(config=None):
"""
Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
@@ -57,8 +80,14 @@ def get_config(config=None):
bridge['member'].update({'interface_remove': tmp })
else:
bridge.update({'member': {'interface_remove': tmp }})
-
- if vyos_dict_search('member.interface', bridge):
+
+
+ # determine which members vlan have been removed
+
+ bridge = helper_check_removed_vlan(conf,bridge,'native-vlan',('-', '_'))
+ bridge = helper_check_removed_vlan(conf,bridge,'allowed-vlan',('-', '_'))
+
+ if dict_search('member.interface', bridge):
# XXX: T2665: we need a copy of the dict keys for iteration, else we will get:
# RuntimeError: dictionary changed size during iteration
for interface in list(bridge['member']['interface']):
@@ -70,7 +99,8 @@ def get_config(config=None):
# the default dictionary is not properly paged into the dict (see T2665)
# thus we will ammend it ourself
default_member_values = defaults(base + ['member', 'interface'])
- for interface in bridge['member']['interface']:
+ vlan_aware = False
+ for interface,interface_config in bridge['member']['interface'].items():
bridge['member']['interface'][interface] = dict_merge(
default_member_values, bridge['member']['interface'][interface])
@@ -90,6 +120,19 @@ def get_config(config=None):
# Bridge members must not have an assigned address
tmp = has_address_configured(conf, interface)
if tmp: bridge['member']['interface'][interface].update({'has_address' : ''})
+
+ # VLAN-aware bridge members must not have VLAN interface configuration
+ if 'native_vlan' in interface_config:
+ if 'disable' not in interface_config['native_vlan']:
+ vlan_aware = True
+
+ if 'allowed_vlan' in interface_config:
+ vlan_aware = True
+
+ if vlan_aware:
+ tmp = has_vlan_subinterface_configured(conf,interface)
+ if tmp:
+ if tmp: bridge['member']['interface'][interface].update({'has_vlan' : ''})
return bridge
@@ -100,7 +143,7 @@ def verify(bridge):
verify_dhcpv6(bridge)
verify_vrf(bridge)
- if vyos_dict_search('member.interface', bridge):
+ if dict_search('member.interface', bridge):
for interface, interface_config in bridge['member']['interface'].items():
error_msg = f'Can not add interface "{interface}" to bridge, '
@@ -121,6 +164,21 @@ def verify(bridge):
if 'has_address' in interface_config:
raise ConfigError(error_msg + 'it has an address assigned!')
+
+ if 'has_vlan' in interface_config:
+ raise ConfigError(error_msg + 'it has an VLAN subinterface assigned!')
+
+ if 'allowed_vlan' in interface_config:
+ for vlan in interface_config['allowed_vlan']:
+ if re.search('[0-9]{1,4}-[0-9]{1,4}', vlan):
+ vlan_range = vlan.split('-')
+ if int(vlan_range[0]) <1 and int(vlan_range[0])>4094:
+ raise ConfigError('VLAN ID must be between 1 and 4094')
+ if int(vlan_range[1]) <1 and int(vlan_range[1])>4094:
+ raise ConfigError('VLAN ID must be between 1 and 4094')
+ else:
+ if int(vlan) <1 and int(vlan)>4094:
+ raise ConfigError('VLAN ID must be between 1 and 4094')
return None
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 3f4965029..7d5f7f3a0 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -17,110 +17,38 @@
import os
import re
-from copy import deepcopy
-from sys import exit,stderr
-from ipaddress import ip_address,ip_network,IPv4Address,IPv4Network,IPv6Address,IPv6Network,summarize_address_range
+from sys import exit
+from ipaddress import IPv4Address
+from ipaddress import IPv4Network
+from ipaddress import IPv6Address
+from ipaddress import IPv6Network
+from ipaddress import summarize_address_range
from netifaces import interfaces
from shutil import rmtree
from vyos.config import Config
-from vyos.configdict import list_diff
-from vyos.configdict import is_member
+from vyos.configdict import get_interface_dict
+from vyos.configverify import verify_vrf
+from vyos.configverify import verify_bridge_delete
+from vyos.configverify import verify_diffie_hellman_length
from vyos.ifconfig import VTunIf
from vyos.template import render
-from vyos.util import call, chown, chmod_600, chmod_755
-from vyos.validate import is_addr_assigned, is_ipv4
-from vyos import ConfigError
-from vyos.util import cmd
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+from vyos.util import call
+from vyos.util import chown
+from vyos.util import chmod_600
+from vyos.util import dict_search
+from vyos.validate import is_addr_assigned
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
user = 'openvpn'
group = 'openvpn'
-default_config_data = {
- 'address': [],
- 'auth_user': '',
- 'auth_pass': '',
- 'auth_user_pass_file': '',
- 'auth': False,
- 'compress_lzo': False,
- 'deleted': False,
- 'description': '',
- 'disable': False,
- 'disable_ncp': False,
- 'encryption': '',
- 'hash': '',
- 'intf': '',
- 'ipv6_accept_ra': 1,
- 'ipv6_autoconf': 0,
- 'ipv6_eui64_prefix': [],
- 'ipv6_eui64_prefix_remove': [],
- 'ipv6_forwarding': 1,
- 'ipv6_dup_addr_detect': 1,
- 'ipv6_local_address': [],
- 'ipv6_remote_address': [],
- 'is_bridge_member': False,
- 'ping_restart': '60',
- 'ping_interval': '10',
- 'local_address': [],
- 'local_address_subnet': '',
- 'local_host': '',
- 'local_port': '',
- 'mode': '',
- 'ncp_ciphers': '',
- 'options': [],
- 'persistent_tunnel': False,
- 'protocol': 'udp',
- 'protocol_real': '',
- 'redirect_gateway': '',
- 'remote_address': [],
- 'remote_host': [],
- 'remote_port': '',
- 'client': [],
- 'server_domain': '',
- 'server_max_conn': '',
- 'server_dns_nameserver': [],
- 'server_pool': True,
- 'server_pool_start': '',
- 'server_pool_stop': '',
- 'server_pool_netmask': '',
- 'server_push_route': [],
- 'server_reject_unconfigured': False,
- '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': '',
- 'tls_ca_cert': '',
- 'tls_cert': '',
- 'tls_crl': '',
- 'tls_dh': '',
- 'tls_key': '',
- 'tls_crypt': '',
- 'tls_role': '',
- 'tls_version_min': '',
- 'type': 'tun',
- 'uid': user,
- 'gid': group,
- 'vrf': ''
-}
-
-
-def get_config_name(intf):
- cfg_file = f'/run/openvpn/{intf}.conf'
- return cfg_file
-
+cfg_file = '/run/openvpn/{ifname}.conf'
def checkCertHeader(header, filename):
"""
@@ -137,600 +65,125 @@ def checkCertHeader(header, filename):
return False
-def getDefaultServer(network, topology, devtype):
+def get_config(config=None):
"""
- 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.
+ Retrive CLI config as dictionary. Dictionary can never be empty, as at least the
+ interface name will be added or a deleted flag
"""
- if not (devtype == 'tun' or devtype == 'tap'):
- return False
-
- if not network.version == 4:
- return False
- elif (devtype == 'tun' and network.prefixlen > 29) or (devtype == 'tap' and network.prefixlen > 30):
- return False
-
- server = {
- 'local': '',
- 'remote_netmask': '',
- 'client_remote_netmask': '',
- 'pool_start': '',
- 'pool_stop': '',
- 'pool_netmask': ''
- }
-
- if devtype == 'tun':
- if topology == 'net30' or topology == 'point-to-point':
- server['local'] = network[1]
- server['remote_netmask'] = network[2]
- server['client_remote_netmask'] = server['local']
-
- # pool start is 4th host IP in subnet (.4 in a /24)
- server['pool_start'] = network[4]
-
- if network.prefixlen == 29:
- server['pool_stop'] = network.broadcast_address
- else:
- # pool end is -4 from the broadcast address (.251 in a /24)
- server['pool_stop'] = network[-5]
-
- elif topology == 'subnet':
- server['local'] = network[1]
- server['remote_netmask'] = str(network.netmask)
- server['client_remote_netmask'] = server['remote_netmask']
- server['pool_start'] = network[2]
- server['pool_stop'] = network[-3]
- server['pool_netmask'] = server['remote_netmask']
-
- elif devtype == 'tap':
- server['local'] = network[1]
- server['remote_netmask'] = str(network.netmask)
- server['client_remote_netmask'] = server['remote_netmask']
- server['pool_start'] = network[2]
- server['pool_stop'] = network[-2]
- server['pool_netmask'] = server['remote_netmask']
-
- return server
-
-def get_config(config=None):
- openvpn = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
+ base = ['interfaces', 'openvpn']
+ openvpn = get_interface_dict(conf, base)
- # determine tagNode instance
- if 'VYOS_TAGNODE_VALUE' not in os.environ:
- raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
-
- openvpn['intf'] = os.environ['VYOS_TAGNODE_VALUE']
- openvpn['auth_user_pass_file'] = f"/run/openvpn/{openvpn['intf']}.pw"
-
- # check if interface is member of a bridge
- tmp = is_member(conf, openvpn['intf'], 'bridge')
- if tmp: openvpn['is_bridge_member'] = next(iter(tmp))
-
- # Check if interface instance has been removed
- if not conf.exists('interfaces openvpn ' + openvpn['intf']):
- openvpn['deleted'] = True
- return openvpn
-
- # bridged server should not have a pool by default (but can be specified manually)
- if openvpn['is_bridge_member']:
- openvpn['server_pool'] = False
- openvpn['server_ipv6_pool'] = False
-
- # set configuration level
- conf.set_level('interfaces openvpn ' + openvpn['intf'])
-
- # retrieve authentication options - username
- if conf.exists('authentication username'):
- openvpn['auth_user'] = conf.return_value('authentication username')
- openvpn['auth'] = True
-
- # retrieve authentication options - username
- if conf.exists('authentication password'):
- openvpn['auth_pass'] = conf.return_value('authentication password')
- openvpn['auth'] = True
-
- # retrieve interface description
- if conf.exists('description'):
- openvpn['description'] = conf.return_value('description')
-
- # interface device-type
- if conf.exists('device-type'):
- openvpn['type'] = conf.return_value('device-type')
-
- # disable interface
- if conf.exists('disable'):
- openvpn['disable'] = True
-
- # data encryption algorithm cipher
- if conf.exists('encryption cipher'):
- openvpn['encryption'] = conf.return_value('encryption cipher')
-
- # disable ncp-ciphers support
- if conf.exists('encryption disable-ncp'):
- openvpn['disable_ncp'] = True
-
- # data encryption algorithm ncp-list
- if conf.exists('encryption ncp-ciphers'):
- _ncp_ciphers = []
- for enc in conf.return_values('encryption ncp-ciphers'):
- if enc == 'none':
- _ncp_ciphers.append('none')
- _ncp_ciphers.append('NONE')
- elif enc == 'des':
- _ncp_ciphers.append('des-cbc')
- _ncp_ciphers.append('DES-CBC')
- elif enc == '3des':
- _ncp_ciphers.append('des-ede3-cbc')
- _ncp_ciphers.append('DES-EDE3-CBC')
- elif enc == 'aes128':
- _ncp_ciphers.append('aes-128-cbc')
- _ncp_ciphers.append('AES-128-CBC')
- elif enc == 'aes128gcm':
- _ncp_ciphers.append('aes-128-gcm')
- _ncp_ciphers.append('AES-128-GCM')
- elif enc == 'aes192':
- _ncp_ciphers.append('aes-192-cbc')
- _ncp_ciphers.append('AES-192-CBC')
- elif enc == 'aes192gcm':
- _ncp_ciphers.append('aes-192-gcm')
- _ncp_ciphers.append('AES-192-GCM')
- elif enc == 'aes256':
- _ncp_ciphers.append('aes-256-cbc')
- _ncp_ciphers.append('AES-256-CBC')
- elif enc == 'aes256gcm':
- _ncp_ciphers.append('aes-256-gcm')
- _ncp_ciphers.append('AES-256-GCM')
- openvpn['ncp_ciphers'] = ':'.join(_ncp_ciphers)
-
- # hash algorithm
- if conf.exists('hash'):
- openvpn['hash'] = conf.return_value('hash')
-
- # Maximum number of keepalive packet failures
- if conf.exists('keep-alive failure-count') and conf.exists('keep-alive interval'):
- fail_count = conf.return_value('keep-alive failure-count')
- interval = conf.return_value('keep-alive interval')
- openvpn['ping_interval' ] = interval
- openvpn['ping_restart' ] = int(interval) * int(fail_count)
-
- # 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'):
- 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'):
- openvpn['local_host'] = conf.return_value('local-host')
-
- # Local port number to accept connections
- if conf.exists('local-port'):
- openvpn['local_port'] = conf.return_value('local-port')
-
- # Enable acquisition of IPv6 address using stateless autoconfig (SLAAC)
- if conf.exists('ipv6 address autoconf'):
- openvpn['ipv6_autoconf'] = 1
-
- # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
- if conf.exists('ipv6 address eui64'):
- openvpn['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
-
- # Determine currently effective EUI64 addresses - to determine which
- # address is no longer valid and needs to be removed
- eff_addr = conf.return_effective_values('ipv6 address eui64')
- openvpn['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, openvpn['ipv6_eui64_prefix'])
-
- # Remove the default link-local address if set.
- if conf.exists('ipv6 address no-default-link-local'):
- openvpn['ipv6_eui64_prefix_remove'].append('fe80::/64')
- else:
- # add the link-local by default to make IPv6 work
- openvpn['ipv6_eui64_prefix'].append('fe80::/64')
-
- # Disable IPv6 forwarding on this interface
- if conf.exists('ipv6 disable-forwarding'):
- openvpn['ipv6_forwarding'] = 0
-
- # IPv6 Duplicate Address Detection (DAD) tries
- if conf.exists('ipv6 dup-addr-detect-transmits'):
- openvpn['ipv6_dup_addr_detect'] = int(conf.return_value('ipv6 dup-addr-detect-transmits'))
-
- # to make IPv6 SLAAC and DHCPv6 work with forwarding=1,
- # accept_ra must be 2
- if openvpn['ipv6_autoconf'] or 'dhcpv6' in openvpn['address']:
- openvpn['ipv6_accept_ra'] = 2
-
- # OpenVPN operation mode
- if conf.exists('mode'):
- openvpn['mode'] = conf.return_value('mode')
-
- # Additional OpenVPN options
- if conf.exists('openvpn-option'):
- openvpn['options'] = conf.return_values('openvpn-option')
-
- # Do not close and reopen interface
- if conf.exists('persistent-tunnel'):
- openvpn['persistent_tunnel'] = True
-
- # Communication protocol
- if conf.exists('protocol'):
- openvpn['protocol'] = conf.return_value('protocol')
-
- # IP address of remote end of tunnel
- if conf.exists('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'):
- openvpn['remote_host'] = conf.return_values('remote-host')
-
- # Remote port number to connect to
- if conf.exists('remote-port'):
- openvpn['remote_port'] = conf.return_value('remote-port')
-
- # OpenVPN tunnel to be used as the default route
- # see https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/
- # redirect-gateway flags
- if conf.exists('replace-default-route'):
- openvpn['redirect_gateway'] = 'def1'
-
- if conf.exists('replace-default-route local'):
- openvpn['redirect_gateway'] = 'local def1'
-
- # Topology for clients
- if conf.exists('server topology'):
- 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'):
- 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'):
- # set configuration level
- conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client ' + client)
- data = {
- 'name': client,
- 'disable': False,
- 'ip': [],
- 'ipv6_ip': [],
- 'ipv6_remote': '',
- 'ipv6_push_route': [],
- 'ipv6_subnet': [],
- 'push_route': [],
- 'subnet': [],
- 'remote_netmask': ''
- }
-
- # Option to disable client connection
- if conf.exists('disable'):
- data['disable'] = True
-
- # IP address of the client
- 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 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 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)
-
- # re-set configuration level
- conf.set_level('interfaces openvpn ' + openvpn['intf'])
-
- # Server client IP pool
- if conf.exists('server client-ip-pool'):
- 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('start'):
- openvpn['server_pool_start'] = conf.return_value('start')
-
- if conf.exists('stop'):
- openvpn['server_pool_stop'] = conf.return_value('stop')
-
- 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'):
- openvpn['server_domain'] = conf.return_value('server domain-name')
-
- # Number of maximum client connections
- if conf.exists('server max-connections'):
- openvpn['server_max_conn'] = conf.return_value('server max-connections')
-
- # Domain Name Server (DNS)
- if conf.exists('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 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'):
- openvpn['server_reject_unconfigured'] = True
-
- # File containing TLS auth static key
- if conf.exists('tls auth-file'):
- openvpn['tls_auth'] = conf.return_value('tls auth-file')
- openvpn['tls'] = True
-
- # File containing certificate for Certificate Authority (CA)
- if conf.exists('tls ca-cert-file'):
- openvpn['tls_ca_cert'] = conf.return_value('tls ca-cert-file')
- openvpn['tls'] = True
-
- # File containing certificate for this host
- if conf.exists('tls cert-file'):
- openvpn['tls_cert'] = conf.return_value('tls cert-file')
- openvpn['tls'] = True
-
- # File containing certificate revocation list (CRL) for this host
- if conf.exists('tls crl-file'):
- openvpn['tls_crl'] = conf.return_value('tls crl-file')
- openvpn['tls'] = True
-
- # File containing Diffie Hellman parameters (server only)
- if conf.exists('tls dh-file'):
- openvpn['tls_dh'] = conf.return_value('tls dh-file')
- openvpn['tls'] = True
-
- # File containing this host's private key
- if conf.exists('tls key-file'):
- openvpn['tls_key'] = conf.return_value('tls key-file')
- openvpn['tls'] = True
-
- # File containing key to encrypt control channel packets
- if conf.exists('tls crypt-file'):
- openvpn['tls_crypt'] = conf.return_value('tls crypt-file')
- openvpn['tls'] = True
-
- # Role in TLS negotiation
- if conf.exists('tls role'):
- openvpn['tls_role'] = conf.return_value('tls role')
- openvpn['tls'] = True
-
- # Minimum required TLS version
- if conf.exists('tls tls-version-min'):
- openvpn['tls_version_min'] = conf.return_value('tls tls-version-min')
- openvpn['tls'] = True
-
- if conf.exists('shared-secret-key-file'):
- openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file')
-
- if conf.exists('use-lzo-compression'):
- openvpn['compress_lzo'] = True
-
- # Special case when using EC certificates:
- # if key-file is EC and dh-file is unset, set tls_dh to 'none'
- 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'
-
- # Convert protocol to real protocol used by openvpn.
- # To make openvpn listen on both IPv4 and IPv6 we must use *6 protocols
- # (https://community.openvpn.net/openvpn/ticket/360), unless the local-host
- # or each of the remote-host in client mode is IPv4
- # in which case it must use the standard protocols.
- if openvpn['protocol'] == 'tcp-active':
- openvpn['protocol_real'] = 'tcp6-client'
- elif openvpn['protocol'] == 'tcp-passive':
- openvpn['protocol_real'] = 'tcp6-server'
- else:
- openvpn['protocol_real'] = 'udp6'
-
- if ( is_ipv4(openvpn['local_host']) or
- # in client mode test all the remotes instead
- (openvpn['mode'] == 'client' and all([is_ipv4(h) for h in openvpn['remote_host']])) ):
- # takes out the '6'
- openvpn['protocol_real'] = openvpn['protocol_real'][:3] + openvpn['protocol_real'][4:]
-
- # Set defaults where necessary.
- # If any of the input parameters are wrong,
- # this will return False and no defaults will be set.
- 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['is_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_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['ipv6_remote'] = openvpn['server_ipv6_local']
-
- if openvpn['redirect_gateway']:
- openvpn['redirect_gateway'] += ' ipv6'
-
- # retrieve VRF instance
- if conf.exists('vrf'):
- openvpn['vrf'] = conf.return_value('vrf')
+ openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
+ openvpn['daemon_user'] = user
+ openvpn['daemon_group'] = group
return openvpn
def verify(openvpn):
- if openvpn['deleted']:
- if openvpn['is_bridge_member']:
- raise ConfigError((
- f'Cannot delete interface "{openvpn["intf"]}" as it is a '
- f'member of bridge "{openvpn["is_bridge_menber"]}"!'))
+ if 'deleted' in openvpn:
+ verify_bridge_delete(openvpn)
return None
-
- if not openvpn['mode']:
- raise ConfigError('Must specify OpenVPN operation mode')
+ if 'mode' not in openvpn:
+ raise ConfigError('Must specify OpenVPN operation mode!')
# Check if we have disabled ncp and at the same time specified ncp-ciphers
- if openvpn['disable_ncp'] and openvpn['ncp_ciphers']:
- raise ConfigError('Cannot specify both "encryption disable-ncp" and "encryption ncp-ciphers"')
+ if 'encryption' in openvpn:
+ if {'disable_ncp', 'ncp_ciphers'} <= set(openvpn.get('encryption')):
+ raise ConfigError('Can not specify both "encryption disable-ncp" '\
+ 'and "encryption ncp-ciphers"')
+
#
# OpenVPN client mode - VERIFY
#
if openvpn['mode'] == 'client':
- if openvpn['local_port']:
+ if 'local_port' in openvpn:
raise ConfigError('Cannot specify "local-port" in client mode')
- if openvpn['local_host']:
+ if 'local_host' in openvpn:
raise ConfigError('Cannot specify "local-host" in client mode')
+ if 'remote_host' not in openvpn:
+ raise ConfigError('Must specify "remote-host" in client mode')
+
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Protocol "tcp-passive" is not valid in client mode')
- if not openvpn['remote_host']:
- raise ConfigError('Must specify "remote-host" in client mode')
-
- if openvpn['tls_dh'] and openvpn['tls_dh'] != 'none':
+ if dict_search('tls.dh_file', openvpn):
raise ConfigError('Cannot specify "tls dh-file" in client mode')
#
# OpenVPN site-to-site - VERIFY
#
- if openvpn['mode'] == 'site-to-site':
- if openvpn['ncp_ciphers']:
- 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['is_bridge_member']:
- if not (openvpn['local_address'] or openvpn['ipv6_local_address']):
+ elif openvpn['mode'] == 'site-to-site':
+ if not 'local_address' in openvpn:
raise ConfigError('Must specify "local-address" or add interface to bridge')
- 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([addr for addr in openvpn['local_address'] if is_ipv4(addr)]) > 1:
+ raise ConfigError('Only one IPv4 local-address can be specified')
- 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"')
+ if len([addr for addr in openvpn['local_address'] if is_ipv6(addr)]) > 1:
+ raise ConfigError('Only one IPv6 local-address can be specified')
- for host in openvpn['remote_host']:
- 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['device_type'] == 'tun':
+ if 'remote_address' not in openvpn:
+ raise ConfigError('Must specify "remote-address"')
- 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 'remote_address' in openvpn:
+ if len([addr for addr in openvpn['remote_address'] if is_ipv4(addr)]) > 1:
+ raise ConfigError('Only one IPv4 remote-address can be specified')
- if openvpn['remote_address'] and not openvpn['local_address']:
- raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
+ if len([addr for addr in openvpn['remote_address'] if is_ipv6(addr)]) > 1:
+ raise ConfigError('Only one IPv6 remote-address can be specified')
- if openvpn['ipv6_local_address'] and not openvpn['ipv6_remote_address']:
- raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
+ if not 'local_address' in openvpn:
+ raise ConfigError('"remote-address" requires "local-address"')
- if openvpn['ipv6_remote_address'] and not openvpn['ipv6_local_address']:
- raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
+ v4loAddr = [addr for addr in openvpn['local_address'] if is_ipv4(addr)]
+ v4remAddr = [addr for addr in openvpn['remote_address'] if is_ipv4(addr)]
+ if v4loAddr and not v4remAddr:
+ raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address"')
+ elif v4remAddr and not v4loAddr:
+ raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
- if openvpn['type'] == 'tun':
- if not (openvpn['remote_address'] or openvpn['ipv6_remote_address']):
- raise ConfigError('Must specify "remote-address"')
+ v6remAddr = [addr for addr in openvpn['remote_address'] if is_ipv6(addr)]
+ v6loAddr = [addr for addr in openvpn['local_address'] if is_ipv6(addr)]
+ if v6loAddr and not v6remAddr:
+ raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
+ elif v6remAddr and not v6loAddr:
+ raise ConfigError('IPv6 "remote-address" requires IPv6 "local-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']) ):
+ if (v4loAddr == v4remAddr) or (v6remAddr == v4remAddr):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
- if openvpn['local_host'] in openvpn['local_address'] or openvpn['local_host'] in openvpn['ipv6_local_address']:
+ if dict_search('local_host', openvpn) in dict_search('local_address', openvpn):
raise ConfigError('"local-address" cannot be the same as "local-host"')
+ if dict_search('remote_host', openvpn) in dict_search('remote_address', openvpn):
+ raise ConfigError('"remote-address" and "remote-host" can not be the same')
+
+
+ if 'local_address' in openvpn:
+ # we can only have one local_address, this is ensured above
+ v4addr = None
+ for laddr in openvpn['local_address']:
+ if is_ipv4(laddr): v4addr = laddr
+
+ if 'remote_address' not in openvpn and (v4addr not in openvpn['local_address'] or 'subnet_mask' not in openvpn['local_address'][v4addr]):
+ raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"')
+
+ if dict_search('encryption.ncp_ciphers', openvpn):
+ raise ConfigError('NCP ciphers can only be used in client or server mode')
+
else:
# checks for client-server or site-to-site bridged
- 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')
+ if 'local_address' in openvpn or 'remote_address' in openvpn:
+ raise ConfigError('Cannot specify "local-address" or "remote-address" ' \
+ 'in client/server or bridge mode')
#
# OpenVPN server mode - VERIFY
@@ -739,47 +192,57 @@ def verify(openvpn):
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Protocol "tcp-active" is not valid in server mode')
- if openvpn['remote_port']:
+ if 'remote_port' in openvpn:
raise ConfigError('Cannot specify "remote-port" in server mode')
- if openvpn['remote_host']:
+ if 'remote_host' in openvpn:
raise ConfigError('Cannot specify "remote-host" in server mode')
- if openvpn['protocol'] == 'tcp-passive' and len(openvpn['remote_host']) > 1:
- raise ConfigError('Cannot specify more than 1 "remote-host" with "tcp-passive"')
-
- 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 'tls' in openvpn:
+ if 'dh_file' not in openvpn['tls']:
+ if 'key_file' in openvpn['tls'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls']['key_file']):
+ raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
+
+ tmp = dict_search('server.subnet', openvpn)
+ if tmp:
+ v4_subnets = len([subnet for subnet in tmp if is_ipv4(subnet)])
+ v6_subnets = len([subnet for subnet in tmp if is_ipv6(subnet)])
+ if v4_subnets > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 server subnet')
+ if v6_subnets > 1:
+ raise ConfigError('Cannot specify more than 1 IPv6 server subnet')
+
+ if v6_subnets > 0 and v4_subnets == 0:
+ raise ConfigError('IPv6 server requires an IPv4 server subnet')
- if openvpn['server_subnet']:
- subnet = IPv4Network(openvpn['server_subnet'][0].replace(' ', '/'))
+ for subnet in tmp:
+ if is_ipv4(subnet):
+ subnet = IPv4Network(subnet)
- if openvpn['type'] == 'tun' and subnet.prefixlen > 29:
- raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
- elif openvpn['type'] == 'tap' and subnet.prefixlen > 30:
- raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
+ if openvpn['device_type'] == 'tun' and subnet.prefixlen > 29:
+ raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
+ elif openvpn['device_type'] == 'tap' and subnet.prefixlen > 30:
+ 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'][0]) in subnet:
- raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
+ for client in (dict_search('client', openvpn) or []):
+ 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['is_bridge_member']:
+ if 'is_bridge_member' not in openvpn:
raise ConfigError('Must specify "server subnet" or add interface to bridge 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')
+
+ for client in (dict_search('client', openvpn) or []):
+ 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 dict_search('server.client_ip_pool', openvpn):
+ if not (dict_search('server.client_ip_pool.start', openvpn) and dict_search('server.client_ip_pool.stop', openvpn)):
+ raise ConfigError('Server client-ip-pool requires both start and stop addresses')
else:
- v4PoolStart = IPv4Address(openvpn['server_pool_start'])
- v4PoolStop = IPv4Address(openvpn['server_pool_stop'])
+ v4PoolStart = IPv4Address(dict_search('server.client_ip_pool.start', openvpn))
+ v4PoolStop = IPv4Address(dict_search('server.client_ip_pool.stop', openvpn))
if v4PoolStart > v4PoolStop:
raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
@@ -788,59 +251,57 @@ def verify(openvpn):
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']:
+ for client in (dict_search('client', openvpn) or []):
if client['ip']:
for v4PoolNet in v4PoolNets:
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})')
+ print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.')
+
+ for subnet in (dict_search('server.subnet', openvpn) or []):
+ if is_ipv6(subnet):
+ tmp = dict_search('client_ipv6_pool.base', openvpn)
+ if tmp:
+ if not dict_search('server.client_ip_pool', openvpn):
+ raise ConfigError('IPv6 server pool requires an IPv4 server pool')
+
+ if int(tmp.split('/')[1]) >= 112:
+ raise ConfigError('IPv6 server pool must be larger than /112')
+
+ #
+ # todo - weird logic
+ #
+ v6PoolStart = IPv6Address(tmp)
+ 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 (dict_search('client', openvpn) or []):
+ 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.')
- 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:
+ for route in (dict_search('server.push_route', openvpn) or []):
+ if is_ipv6(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']:
- raise ConfigError('reject-unconfigured-clients is only supported in OpenVPN server mode')
+ if dict_search('server.reject_unconfigured_clients', openvpn):
+ raise ConfigError('Option reject-unconfigured-clients only supported in server mode')
- if openvpn['server_topology']:
- raise ConfigError('The "topology" option is only valid in server mode')
-
- if (not openvpn['remote_host']) and openvpn['redirect_gateway']:
+ if 'replace_default_route' in openvpn and 'remote_host' not in openvpn:
raise ConfigError('Cannot set "replace-default-route" without "remote-host"')
#
@@ -849,133 +310,135 @@ def verify(openvpn):
#
# verify specified IP address is present on any interface on this system
- if openvpn['local_host']:
+ if 'local_host' in openvpn:
if not is_addr_assigned(openvpn['local_host']):
- raise ConfigError('No interface on system with specified local-host IP address: {}'.format(openvpn['local_host']))
+ raise ConfigError('local-host IP address "{local_host}" not assigned' \
+ ' to any interface'.format(**openvpn))
# TCP active
if openvpn['protocol'] == 'tcp-active':
- if openvpn['local_port']:
+ if 'local_port' in openvpn:
raise ConfigError('Cannot specify "local-port" with "tcp-active"')
- if not openvpn['remote_host']:
+ if 'remote_host' in openvpn:
raise ConfigError('Must specify "remote-host" with "tcp-active"')
# shared secret and TLS
- if not (openvpn['shared_secret_file'] or openvpn['tls']):
+ if not ('shared_secret_key_file' in openvpn or 'tls' in openvpn):
raise ConfigError('Must specify one of "shared-secret-key-file" and "tls"')
- if openvpn['shared_secret_file'] and openvpn['tls']:
+ if {'shared_secret_key_file', 'tls'} <= set(openvpn):
raise ConfigError('Can only specify one of "shared-secret-key-file" and "tls"')
if openvpn['mode'] in ['client', 'server']:
- if not openvpn['tls']:
- raise ConfigError('Must specify "tls" in client-server mode')
+ if 'tls' not in openvpn:
+ raise ConfigError('Must specify "tls" for server and client mode')
#
# TLS/encryption
#
- if openvpn['shared_secret_file']:
- if openvpn['encryption'] in ['aes128gcm', 'aes192gcm', 'aes256gcm']:
- raise ConfigError('GCM encryption with shared-secret-key-file is not supported')
+ if 'shared_secret_key_file' in openvpn:
+ if dict_search('encryption.cipher', openvpn) in ['aes128gcm', 'aes192gcm', 'aes256gcm']:
+ raise ConfigError('GCM encryption with shared-secret-key-file not supported')
- if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['shared_secret_file']):
- raise ConfigError('Specified shared-secret-key-file "{}" is not valid'.format(openvpn['shared_secret_file']))
+ file = dict_search('shared_secret_key_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
+ raise ConfigError(f'Specified shared-secret-key-file "{file}" is not valid')
- if openvpn['tls']:
- if not openvpn['tls_ca_cert']:
+ if 'tls' in openvpn:
+ if 'ca_cert_file' not in openvpn['tls']:
raise ConfigError('Must specify "tls ca-cert-file"')
- if not (openvpn['mode'] == 'client' and openvpn['auth']):
- if not openvpn['tls_cert']:
- raise ConfigError('Must specify "tls cert-file"')
+ if not (openvpn['mode'] == 'client' and 'auth_file' in openvpn['tls']):
+ if 'cert_file' not in openvpn['tls']:
+ raise ConfigError('Missing "tls cert-file"')
- if not openvpn['tls_key']:
- raise ConfigError('Must specify "tls key-file"')
+ if 'key_file' not in openvpn['tls']:
+ raise ConfigError('Missing "tls key-file"')
- if openvpn['tls_auth'] and openvpn['tls_crypt']:
+ if {'auth_file', 'crypt_file'} <= set(openvpn['tls']):
raise ConfigError('TLS auth and crypt are mutually exclusive')
- if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_ca_cert']):
- raise ConfigError('Specified ca-cert-file "{}" is invalid'.format(openvpn['tls_ca_cert']))
+ file = dict_search('tls.ca_cert_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
+ raise ConfigError(f'Specified ca-cert-file "{file}" is invalid')
+
+ file = dict_search('tls.auth_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
+ raise ConfigError(f'Specified auth-file "{file}" is invalid')
- if openvpn['tls_auth']:
- if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['tls_auth']):
- raise ConfigError('Specified auth-file "{}" is invalid'.format(openvpn['tls_auth']))
+ file = dict_search('tls.cert_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN CERTIFICATE-----', file):
+ raise ConfigError(f'Specified cert-file "{file}" is invalid')
- if openvpn['tls_cert']:
- if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']):
- raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert']))
+ file = dict_search('tls.key_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN (?:RSA |EC )?PRIVATE KEY-----', file):
+ raise ConfigError(f'Specified key-file "{file}" is not valid')
- if openvpn['tls_key']:
- if not checkCertHeader('-----BEGIN (?:RSA |EC )?PRIVATE KEY-----', openvpn['tls_key']):
- raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key']))
+ file = dict_search('tls.crypt_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', file):
+ raise ConfigError(f'Specified TLS crypt-file "{file}" is invalid')
- if openvpn['tls_crypt']:
- if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['tls_crypt']):
- raise ConfigError('Specified TLS crypt-file "{}" is invalid'.format(openvpn['tls_crypt']))
+ file = dict_search('tls.crl_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN X509 CRL-----', file):
+ raise ConfigError(f'Specified crl-file "{file} not valid')
- if openvpn['tls_crl']:
- if not checkCertHeader('-----BEGIN X509 CRL-----', openvpn['tls_crl']):
- raise ConfigError('Specified crl-file "{} not valid'.format(openvpn['tls_crl']))
+ file = dict_search('tls.dh_file', openvpn)
+ if file and not checkCertHeader('-----BEGIN DH PARAMETERS-----', file):
+ raise ConfigError(f'Specified dh-file "{file}" is not valid')
- if openvpn['tls_dh'] and openvpn['tls_dh'] != 'none':
- if not checkCertHeader('-----BEGIN DH PARAMETERS-----', openvpn['tls_dh']):
- raise ConfigError('Specified dh-file "{}" is not valid'.format(openvpn['tls_dh']))
+ if file and not verify_diffie_hellman_length(file, 2048):
+ raise ConfigError(f'Minimum DH key-size is 2048 bits')
- if openvpn['tls_role']:
+ tmp = dict_search('tls.role', openvpn)
+ if tmp:
if openvpn['mode'] in ['client', 'server']:
- if not openvpn['tls_auth']:
+ if not dict_search('tls.auth_file', openvpn):
raise ConfigError('Cannot specify "tls role" in client-server mode')
- if openvpn['tls_role'] == 'active':
+ if tmp == 'active':
if openvpn['protocol'] == 'tcp-passive':
raise ConfigError('Cannot specify "tcp-passive" when "tls role" is "active"')
- if openvpn['tls_dh'] and openvpn['tls_dh'] != 'none':
+ if dict_search('tls.dh_file', openvpn):
raise ConfigError('Cannot specify "tls dh-file" when "tls role" is "active"')
- elif openvpn['tls_role'] == 'passive':
+ elif tmp == 'passive':
if openvpn['protocol'] == 'tcp-active':
raise ConfigError('Cannot specify "tcp-active" when "tls role" is "passive"')
- if not openvpn['tls_dh']:
+ if not dict_search('tls.dh_file', openvpn):
raise ConfigError('Must specify "tls dh-file" when "tls role" is "passive"')
- if openvpn['tls_key'] and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
- if openvpn['tls_dh'] and openvpn['tls_dh'] != 'none':
- print('Warning: using dh-file and EC keys simultaneously will lead to DH ciphers being used instead of ECDH')
- else:
- print('Diffie-Hellman prime file is unspecified, assuming ECDH')
+ file = dict_search('tls.key_file', openvpn)
+ if file and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', file):
+ if dict_search('tls.dh_file', openvpn):
+ print('Warning: using dh-file and EC keys simultaneously will ' \
+ 'lead to DH ciphers being used instead of ECDH')
- if openvpn['encryption'] == 'none':
- print('Warning: "encryption none" was specified. NO encryption will be performed and tunnelled data WILL be transmitted in clear text over the network!')
+ if dict_search('encryption.cipher', openvpn) == 'none':
+ print('Warning: "encryption none" was specified!')
+ print('No encryption will be performed and data is transmitted in ' \
+ 'plain text over the network!')
#
# Auth user/pass
#
- if openvpn['auth']:
- if not openvpn['auth_user']:
- raise ConfigError('Username for authentication is missing')
-
- if not openvpn['auth_pass']:
+ if (dict_search('authentication.username', openvpn) and not
+ dict_search('authentication.password', openvpn)):
raise ConfigError('Password for authentication is missing')
- if openvpn['vrf']:
- if openvpn['vrf'] not in interfaces():
- raise ConfigError(f'VRF "{openvpn["vrf"]}" does not exist')
+ if (dict_search('authentication.password', openvpn) and not
+ dict_search('authentication.username', openvpn)):
+ raise ConfigError('Username for authentication is missing')
- if openvpn['is_bridge_member']:
- raise ConfigError((
- f'Interface "{openvpn["intf"]}" cannot be member of VRF '
- f'"{openvpn["vrf"]}" and bridge "{openvpn["is_bridge_member"]}" '
- f'at the same time!'))
+ verify_vrf(openvpn)
return None
def generate(openvpn):
- interface = openvpn['intf']
- directory = os.path.dirname(get_config_name(interface))
+ interface = openvpn['ifname']
+ directory = os.path.dirname(cfg_file.format(**openvpn))
# we can't know in advance which clients have been removed,
# thus all client configs will be removed and re-added on demand
@@ -983,25 +446,25 @@ def generate(openvpn):
if os.path.isdir(ccd_dir):
rmtree(ccd_dir, ignore_errors=True)
- if openvpn['deleted'] or openvpn['disable']:
+ if 'deleted' in openvpn or 'disable' in openvpn:
return None
- # create config directory on demand
- directories = []
- directories.append(f'{directory}/status')
- directories.append(f'{directory}/ccd/{interface}')
- for onedir in directories:
- if not os.path.exists(onedir):
- os.makedirs(onedir, 0o755)
- chown(onedir, user, group)
+ # create client config directory on demand
+ if not os.path.exists(ccd_dir):
+ os.makedirs(ccd_dir, 0o755)
+ chown(ccd_dir, user, group)
# Fix file permissons for keys
fix_permissions = []
- fix_permissions.append(openvpn['shared_secret_file'])
- fix_permissions.append(openvpn['tls_key'])
+
+ tmp = dict_search('shared_secret_key_file', openvpn)
+ if tmp: fix_permissions.append(openvpn['shared_secret_key_file'])
+
+ tmp = dict_search('tls.key_file', openvpn)
+ if tmp: fix_permissions.append(tmp)
# Generate User/Password authentication file
- if openvpn['auth']:
+ if 'auth' in openvpn:
with open(openvpn['auth_user_pass_file'], 'w') as f:
f.write('{}\n{}'.format(openvpn['auth_user'], openvpn['auth_pass']))
# also change permission on auth file
@@ -1013,16 +476,24 @@ def generate(openvpn):
os.remove(openvpn['auth_user_pass_file'])
# Generate client specific configuration
- for client in openvpn['client']:
- client_file = os.path.join(ccd_dir, client['name'])
- render(client_file, 'openvpn/client.conf.tmpl', client)
- chown(client_file, user, group)
+ if dict_search('server.client', openvpn):
+ for client, client_config in dict_search('server.client', openvpn).items():
+ client_file = os.path.join(ccd_dir, client)
+
+ # Our client need's to know its subnet mask ...
+ client_config['server_subnet'] = dict_search('server.subnet', openvpn)
+
+ import pprint
+ pprint.pprint(client_config)
+
+ render(client_file, 'openvpn/client.conf.tmpl', client_config,
+ trim_blocks=True, user=user, group=group)
# we need to support quoting of raw parameters from OpenVPN CLI
# see https://phabricator.vyos.net/T1632
- render(get_config_name(interface), 'openvpn/server.conf.tmpl', openvpn,
- formater=lambda _: _.replace("&quot;", '"'))
- chown(get_config_name(interface), user, group)
+ render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn,
+ trim_blocks=True, formater=lambda _: _.replace("&quot;", '"'),
+ user=user, group=group)
# Fixup file permissions
for file in fix_permissions:
@@ -1031,75 +502,34 @@ def generate(openvpn):
return None
def apply(openvpn):
- interface = openvpn['intf']
+ interface = openvpn['ifname']
call(f'systemctl stop openvpn@{interface}.service')
- # On configuration change we need to wait for the 'old' interface to
- # vanish from the Kernel, if it is not gone, OpenVPN will report:
- # ERROR: Cannot ioctl TUNSETIFF vtun10: Device or resource busy (errno=16)
- if interface in interfaces():
- cmd(f'sudo ip link del {interface}')
-
# Do some cleanup when OpenVPN is disabled/deleted
- if openvpn['deleted'] or openvpn['disable']:
+ if 'deleted' in openvpn or 'disable' in openvpn:
# cleanup old configuration files
cleanup = []
- cleanup.append(get_config_name(interface))
+ cleanup.append(cfg_file.format(**openvpn))
cleanup.append(openvpn['auth_user_pass_file'])
for file in cleanup:
if os.path.isfile(file):
os.unlink(file)
+ if interface in interfaces():
+ VTunIf(interface).remove()
+
return None
# No matching OpenVPN process running - maybe it got killed or none
# existed - nevertheless, spawn new OpenVPN process
call(f'systemctl start openvpn@{interface}.service')
- if interface not in interfaces():
- try:
- dev_type = openvpn['type']
- cmd(f'sudo openvpn --mktun --dev-type {dev_type} --dev {interface}')
- except:
- pass
-
- # we need to catch the exception if the interface is not up due to
- # reason stated above
- o = VTunIf(interface)
- # update interface description used e.g. within SNMP
- o.set_alias(openvpn['description'])
- # IPv6 accept RA
- o.set_ipv6_accept_ra(openvpn['ipv6_accept_ra'])
- # IPv6 address autoconfiguration
- o.set_ipv6_autoconf(openvpn['ipv6_autoconf'])
- # IPv6 forwarding
- o.set_ipv6_forwarding(openvpn['ipv6_forwarding'])
- # IPv6 Duplicate Address Detection (DAD) tries
- o.set_ipv6_dad_messages(openvpn['ipv6_dup_addr_detect'])
-
- # IPv6 EUI-based addresses - only in TAP mode (TUN's have no MAC)
- # If MAC has changed, old EUI64 addresses won't get deleted,
- # but this isn't easy to solve, so leave them.
- # This is even more difficult as openvpn uses a random MAC for the
- # initial interface creation, unless set by 'lladdr'.
- # NOTE: right now the interface is always deleted. For future
- # compatibility when tap's are not deleted, leave the del_ in
- if openvpn['mode'] == 'tap':
- for addr in openvpn['ipv6_eui64_prefix_remove']:
- o.del_ipv6_eui64_address(addr)
- for addr in openvpn['ipv6_eui64_prefix']:
- o.add_ipv6_eui64_address(addr)
-
- # assign/remove VRF (ONLY when not a member of a bridge,
- # otherwise 'nomaster' removes it from it)
- if not openvpn['is_bridge_member']:
- o.set_vrf(openvpn['vrf'])
-
- # TAP interface needs to be brought up explicitly
- if openvpn['type'] == 'tap':
- if not openvpn['disable']:
- VTunIf(interface).set_admin_state('up')
+ conf = VTunIf.get_config()
+ conf['device_type'] = openvpn['device_type']
+
+ o = VTunIf(interface, **conf)
+ o.update(openvpn)
return None
@@ -1113,3 +543,4 @@ if __name__ == '__main__':
except ConfigError as e:
print(e)
exit(1)
+
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index 5561514bd..f1217b62d 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -23,12 +23,14 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import is_member
+from vyos.configdict import list_diff
+from vyos.dicts import FixedDict
from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf
from vyos.ifconfig.afi import IP4, IP6
-from vyos.configdict import list_diff
-from vyos.validate import is_ipv4, is_ipv6
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
from vyos import ConfigError
-from vyos.dicts import FixedDict
+
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index c1770771e..5d723bbfd 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -32,7 +32,7 @@ from vyos.configverify import verify_vrf
from vyos.ifconfig import WiFiIf
from vyos.template import render
from vyos.util import call
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -80,13 +80,13 @@ def get_config(config=None):
# Cleanup "delete" default values when required user selectable values are
# not defined at all
tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True)
- if not (vyos_dict_search('security.wpa.passphrase', tmp) or
- vyos_dict_search('security.wpa.radius', tmp)):
+ if not (dict_search('security.wpa.passphrase', tmp) or
+ dict_search('security.wpa.radius', tmp)):
del wifi['security']['wpa']
# defaults include RADIUS server specifics per TAG node which need to be
# added to individual RADIUS servers instead - so we can simply delete them
- if vyos_dict_search('security.wpa.radius.server.port', wifi):
+ if dict_search('security.wpa.radius.server.port', wifi):
del wifi['security']['wpa']['radius']['server']['port']
if not len(wifi['security']['wpa']['radius']['server']):
del wifi['security']['wpa']['radius']
@@ -109,20 +109,15 @@ def get_config(config=None):
if tmp: wifi = dict_merge(tmp, wifi)
- # retrieve configured regulatory domain
- conf.set_level(['system'])
- if conf.exists(['wifi-regulatory-domain']):
- wifi['country_code'] = conf.return_value(['wifi-regulatory-domain'])
-
# Only one wireless interface per phy can be in station mode
tmp = find_other_stations(conf, base, wifi['ifname'])
if tmp: wifi['station_interfaces'] = tmp
# Add individual RADIUS server default values
- if vyos_dict_search('security.wpa.radius.server', wifi):
+ if dict_search('security.wpa.radius.server', wifi):
default_values = defaults(base + ['security', 'wpa', 'radius', 'server'])
- for server in vyos_dict_search('security.wpa.radius.server', wifi):
+ for server in dict_search('security.wpa.radius.server', wifi):
wifi['security']['wpa']['radius']['server'][server] = dict_merge(
default_values, wifi['security']['wpa']['radius']['server'][server])
@@ -144,8 +139,7 @@ def verify(wifi):
if wifi['type'] == 'access-point':
if 'country_code' not in wifi:
- raise ConfigError('Wireless regulatory domain is mandatory,\n' \
- 'use "set system wifi-regulatory-domain" for configuration.')
+ raise ConfigError('Wireless country-code is mandatory')
if 'channel' not in wifi:
raise ConfigError('Wireless channel must be configured!')
@@ -241,7 +235,7 @@ def generate(wifi):
wifi['mac'] = str(mac)
# XXX: Jinja2 can not operate on a dictionary key when it starts of with a number
- if '40mhz_incapable' in (vyos_dict_search('capabilities.ht', wifi) or []):
+ if '40mhz_incapable' in (dict_search('capabilities.ht', wifi) or []):
wifi['capabilities']['ht']['fourtymhz_incapable'] = wifi['capabilities']['ht']['40mhz_incapable']
del wifi['capabilities']['ht']['40mhz_incapable']
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index 7d8110096..bce3405d0 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -42,6 +42,7 @@ def get_config(config=None):
conf = Config()
base = ['interfaces', 'wirelessmodem']
wwan = get_interface_dict(conf, base)
+
return wwan
def verify(wwan):
@@ -56,7 +57,8 @@ def verify(wwan):
# we can not use isfile() here as Linux device files are no regular files
# thus the check will return False
- if not os.path.exists(find_device_file(wwan['device'])):
+ dev_path = find_device_file(wwan['device'])
+ if dev_path is None or not os.path.exists(dev_path):
raise ConfigError('Device "{device}" does not exist'.format(**wwan))
verify_vrf(wwan)
@@ -89,21 +91,21 @@ def generate(wwan):
wwan['device'] = find_device_file(wwan['device'])
# Create PPP configuration files
- render(config_wwan, 'wwan/peer.tmpl', wwan)
+ render(config_wwan, 'wwan/peer.tmpl', wwan, trim_blocks=True)
# Create PPP chat script
- render(config_wwan_chat, 'wwan/chat.tmpl', wwan)
+ render(config_wwan_chat, 'wwan/chat.tmpl', wwan, trim_blocks=True)
# generated script file must be executable
# Create script for ip-pre-up.d
render(script_wwan_pre_up, 'wwan/ip-pre-up.script.tmpl',
- wwan, permission=0o755)
+ wwan, trim_blocks=True, permission=0o755)
# Create script for ip-up.d
render(script_wwan_ip_up, 'wwan/ip-up.script.tmpl',
- wwan, permission=0o755)
+ wwan, trim_blocks=True, permission=0o755)
# Create script for ip-down.d
render(script_wwan_ip_down, 'wwan/ip-down.script.tmpl',
- wwan, permission=0o755)
+ wwan, trim_blocks=True, permission=0o755)
return None
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index c8e791c78..d1e551cad 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -20,11 +20,11 @@ from sys import exit
from copy import deepcopy
from vyos.config import Config
-from vyos.validate import is_ipv6_link_local, is_ipv6
-from vyos import ConfigError
-from vyos.util import call
+from vyos.template import is_ipv6
from vyos.template import render
-
+from vyos.util import call
+from vyos.validate import is_ipv6_link_local
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 1978adff5..654874232 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -32,8 +32,13 @@ def get_config():
conf = Config()
base = ['protocols', 'nbgp']
bgp = conf.get_config_dict(base, key_mangling=('-', '_'))
+
if not conf.exists(base):
- return None
+ bgp = {}
+ call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
+
+ if not conf.exists(base + ['route-map']):
+ call('vtysh -c \"conf t\" -c \"no ip protocol bgp\" ')
from pprint import pprint
pprint(bgp)
@@ -44,10 +49,16 @@ def verify(bgp):
if not bgp:
return None
+ # Check if declared more than one ASN
+ for asn in bgp['nbgp'].items():
+ if len(bgp['nbgp']) > 1:
+ raise ConfigError('Only one bgp ASN process can be definded')
+
return None
def generate(bgp):
if not bgp:
+ bgp['new_frr_config'] = ''
return None
# render(config) not needed, its only for debug
@@ -58,9 +69,6 @@ def generate(bgp):
return None
def apply(bgp):
- if bgp is None:
- return None
-
# Save original configration prior to starting any commit actions
bgp['original_config'] = frr.get_configuration(daemon='bgpd')
bgp['modified_config'] = frr.replace_section(bgp['original_config'], bgp['new_frr_config'], from_re='router bgp .*')
diff --git a/src/conf_mode/protocols_mpls.py b/src/conf_mode/protocols_mpls.py
index 904d219e2..84948baf4 100755
--- a/src/conf_mode/protocols_mpls.py
+++ b/src/conf_mode/protocols_mpls.py
@@ -37,60 +37,124 @@ def get_config(config=None):
mpls_conf = {
'router_id' : None,
'mpls_ldp' : False,
+ 'old_parameters' : {
+ 'no_ttl_propagation' : False,
+ 'maximum_ttl' : None
+ },
+ 'parameters' : {
+ 'no_ttl_propagation' : False,
+ 'maximum_ttl' : None
+ },
'old_ldp' : {
- 'interfaces' : [],
- 'neighbors' : {},
- 'd_transp_ipv4' : None,
- 'd_transp_ipv6' : None,
- 'hello_holdtime' : None,
- 'hello_interval' : None,
- 'ses_ipv4_hold' : None,
- 'ses_ipv6_hold' : None,
- 'export_ipv4_exp' : False,
- 'export_ipv6_exp' : False
-
+ 'interfaces' : [],
+ 'neighbors' : {},
+ 'd_transp_ipv4' : None,
+ 'd_transp_ipv6' : None,
+ 'hello_ipv4_holdtime' : None,
+ 'hello_ipv4_interval' : None,
+ 'hello_ipv6_holdtime' : None,
+ 'hello_ipv6_interval' : None,
+ 'ses_ipv4_hold' : None,
+ 'ses_ipv6_hold' : None,
+ 'export_ipv4_exp' : False,
+ 'export_ipv6_exp' : False,
+ 'cisco_interop_tlv' : False,
+ 'transport_prefer_ipv4' : False,
+ 'target_ipv4_addresses' : [],
+ 'target_ipv6_addresses' : [],
+ 'target_ipv4_enable' : False,
+ 'target_ipv6_enable' : False,
+ 'target_ipv4_hello_int' : None,
+ 'target_ipv6_hello_int' : None,
+ 'target_ipv4_hello_hold' : None,
+ 'target_ipv6_hello_hold' : None
},
'ldp' : {
- 'interfaces' : [],
- 'neighbors' : {},
- 'd_transp_ipv4' : None,
- 'd_transp_ipv6' : None,
- 'hello_holdtime' : None,
- 'hello_interval' : None,
- 'ses_ipv4_hold' : None,
- 'ses_ipv6_hold' : None,
- 'export_ipv4_exp' : False,
- 'export_ipv6_exp' : False
-
+ 'interfaces' : [],
+ 'neighbors' : {},
+ 'd_transp_ipv4' : None,
+ 'd_transp_ipv6' : None,
+ 'hello_ipv4_holdtime' : None,
+ 'hello_ipv4_interval' : None,
+ 'hello_ipv6_holdtime' : None,
+ 'hello_ipv6_interval' : None,
+ 'ses_ipv4_hold' : None,
+ 'ses_ipv6_hold' : None,
+ 'export_ipv4_exp' : False,
+ 'export_ipv6_exp' : False,
+ 'cisco_interop_tlv' : False,
+ 'transport_prefer_ipv4' : False,
+ 'target_ipv4_addresses' : [],
+ 'target_ipv6_addresses' : [],
+ 'target_ipv4_enable' : False,
+ 'target_ipv6_enable' : False,
+ 'target_ipv4_hello_int' : None,
+ 'target_ipv6_hello_int' : None,
+ 'target_ipv4_hello_hold' : None,
+ 'target_ipv6_hello_hold' : None
}
}
if not (conf.exists('protocols mpls') or conf.exists_effective('protocols mpls')):
return None
-
+
+ # If LDP is defined then enable LDP portion of code
if conf.exists('protocols mpls ldp'):
mpls_conf['mpls_ldp'] = True
+ # Set to MPLS hierarchy configuration level
+ conf.set_level('protocols mpls')
+
+ # Get no_ttl_propagation
+ if conf.exists_effective('parameters no-propagate-ttl'):
+ mpls_conf['old_parameters']['no_ttl_propagation'] = True
+
+ if conf.exists('parameters no-propagate-ttl'):
+ mpls_conf['parameters']['no_ttl_propagation'] = True
+
+ # Get maximum_ttl
+ if conf.exists_effective('parameters maximum-ttl'):
+ mpls_conf['old_parameters']['maximum_ttl'] = conf.return_effective_value('parameters maximum-ttl')
+
+ if conf.exists('parameters maximum-ttl'):
+ mpls_conf['parameters']['maximum_ttl'] = conf.return_value('parameters maximum-ttl')
+
+ # Set to LDP hierarchy configuration level
conf.set_level('protocols mpls ldp')
# Get router-id
if conf.exists_effective('router-id'):
mpls_conf['old_router_id'] = conf.return_effective_value('router-id')
+
if conf.exists('router-id'):
mpls_conf['router_id'] = conf.return_value('router-id')
- # Get hello holdtime
- if conf.exists_effective('discovery hello-holdtime'):
- mpls_conf['old_ldp']['hello_holdtime'] = conf.return_effective_value('discovery hello-holdtime')
+ # Get hello-ipv4-holdtime
+ if conf.exists_effective('discovery hello-ipv4-holdtime'):
+ mpls_conf['old_ldp']['hello_ipv4_holdtime'] = conf.return_effective_value('discovery hello-ipv4-holdtime')
- if conf.exists('discovery hello-holdtime'):
- mpls_conf['ldp']['hello_holdtime'] = conf.return_value('discovery hello-holdtime')
+ if conf.exists('discovery hello-ipv4-holdtime'):
+ mpls_conf['ldp']['hello_ipv4_holdtime'] = conf.return_value('discovery hello-ipv4-holdtime')
- # Get hello interval
- if conf.exists_effective('discovery hello-interval'):
- mpls_conf['old_ldp']['hello_interval'] = conf.return_effective_value('discovery hello-interval')
+ # Get hello-ipv4-interval
+ if conf.exists_effective('discovery hello-ipv4-interval'):
+ mpls_conf['old_ldp']['hello_ipv4_interval'] = conf.return_effective_value('discovery hello-ipv4-interval')
- if conf.exists('discovery hello-interval'):
- mpls_conf['ldp']['hello_interval'] = conf.return_value('discovery hello-interval')
+ if conf.exists('discovery hello-ipv4-interval'):
+ mpls_conf['ldp']['hello_ipv4_interval'] = conf.return_value('discovery hello-ipv4-interval')
+
+ # Get hello-ipv6-holdtime
+ if conf.exists_effective('discovery hello-ipv6-holdtime'):
+ mpls_conf['old_ldp']['hello_ipv6_holdtime'] = conf.return_effective_value('discovery hello-ipv6-holdtime')
+
+ if conf.exists('discovery hello-ipv6-holdtime'):
+ mpls_conf['ldp']['hello_ipv6_holdtime'] = conf.return_value('discovery hello-ipv6-holdtime')
+
+ # Get hello-ipv6-interval
+ if conf.exists_effective('discovery hello-ipv6-interval'):
+ mpls_conf['old_ldp']['hello_ipv6_interval'] = conf.return_effective_value('discovery hello-ipv6-interval')
+
+ if conf.exists('discovery hello-ipv6-interval'):
+ mpls_conf['ldp']['hello_ipv6_interval'] = conf.return_value('discovery hello-ipv6-interval')
# Get session-ipv4-holdtime
if conf.exists_effective('discovery session-ipv4-holdtime'):
@@ -134,6 +198,76 @@ def get_config(config=None):
if conf.exists('export ipv6 explicit-null'):
mpls_conf['ldp']['export_ipv6_exp'] = True
+ # Get target_ipv4_addresses
+ if conf.exists_effective('targeted-neighbor ipv4 address'):
+ mpls_conf['old_ldp']['target_ipv4_addresses'] = conf.return_effective_values('targeted-neighbor ipv4 address')
+
+ if conf.exists('targeted-neighbor ipv4 address'):
+ mpls_conf['ldp']['target_ipv4_addresses'] = conf.return_values('targeted-neighbor ipv4 address')
+
+ # Get target_ipv4_enable
+ if conf.exists_effective('targeted-neighbor ipv4 enable'):
+ mpls_conf['old_ldp']['target_ipv4_enable'] = True
+
+ if conf.exists('targeted-neighbor ipv4 enable'):
+ mpls_conf['ldp']['target_ipv4_enable'] = True
+
+ # Get target_ipv4_hello_int
+ if conf.exists_effective('targeted-neighbor ipv4 hello-interval'):
+ mpls_conf['old_ldp']['target_ipv4_hello_int'] = conf.return_effective_value('targeted-neighbor ipv4 hello-interval')
+
+ if conf.exists('targeted-neighbor ipv4 hello-interval'):
+ mpls_conf['ldp']['target_ipv4_hello_int'] = conf.return_value('targeted-neighbor ipv4 hello-interval')
+
+ # Get target_ipv4_hello_hold
+ if conf.exists_effective('targeted-neighbor ipv4 hello-holdtime'):
+ mpls_conf['old_ldp']['target_ipv4_hello_hold'] = conf.return_effective_value('targeted-neighbor ipv4 hello-holdtime')
+
+ if conf.exists('targeted-neighbor ipv4 hello-holdtime'):
+ mpls_conf['ldp']['target_ipv4_hello_hold'] = conf.return_value('targeted-neighbor ipv4 hello-holdtime')
+
+ # Get target_ipv6_addresses
+ if conf.exists_effective('targeted-neighbor ipv6 address'):
+ mpls_conf['old_ldp']['target_ipv6_addresses'] = conf.return_effective_values('targeted-neighbor ipv6 address')
+
+ if conf.exists('targeted-neighbor ipv6 address'):
+ mpls_conf['ldp']['target_ipv6_addresses'] = conf.return_values('targeted-neighbor ipv6 address')
+
+ # Get target_ipv6_enable
+ if conf.exists_effective('targeted-neighbor ipv6 enable'):
+ mpls_conf['old_ldp']['target_ipv6_enable'] = True
+
+ if conf.exists('targeted-neighbor ipv6 enable'):
+ mpls_conf['ldp']['target_ipv6_enable'] = True
+
+ # Get target_ipv6_hello_int
+ if conf.exists_effective('targeted-neighbor ipv6 hello-interval'):
+ mpls_conf['old_ldp']['target_ipv6_hello_int'] = conf.return_effective_value('targeted-neighbor ipv6 hello-interval')
+
+ if conf.exists('targeted-neighbor ipv6 hello-interval'):
+ mpls_conf['ldp']['target_ipv6_hello_int'] = conf.return_value('targeted-neighbor ipv6 hello-interval')
+
+ # Get target_ipv6_hello_hold
+ if conf.exists_effective('targeted-neighbor ipv6 hello-holdtime'):
+ mpls_conf['old_ldp']['target_ipv6_hello_hold'] = conf.return_effective_value('targeted-neighbor ipv6 hello-holdtime')
+
+ if conf.exists('targeted-neighbor ipv6 hello-holdtime'):
+ mpls_conf['ldp']['target_ipv6_hello_hold'] = conf.return_value('targeted-neighbor ipv6 hello-holdtime')
+
+ # Get parameters cisco-interop-tlv
+ if conf.exists_effective('parameters cisco-interop-tlv'):
+ mpls_conf['old_ldp']['cisco_interop_tlv'] = True
+
+ if conf.exists('parameters cisco-interop-tlv'):
+ mpls_conf['ldp']['cisco_interop_tlv'] = True
+
+ # Get parameters transport-prefer-ipv4
+ if conf.exists_effective('parameters transport-prefer-ipv4'):
+ mpls_conf['old_ldp']['transport_prefer_ipv4'] = True
+
+ if conf.exists('parameters transport-prefer-ipv4'):
+ mpls_conf['ldp']['transport_prefer_ipv4'] = True
+
# Get interfaces
if conf.exists_effective('interface'):
mpls_conf['old_ldp']['interfaces'] = conf.return_effective_values('interface')
@@ -145,14 +279,18 @@ def get_config(config=None):
for neighbor in conf.list_effective_nodes('neighbor'):
mpls_conf['old_ldp']['neighbors'].update({
neighbor : {
- 'password' : conf.return_effective_value('neighbor {0} password'.format(neighbor))
+ 'password' : conf.return_effective_value('neighbor {0} password'.format(neighbor), default=''),
+ 'ttl_security' : conf.return_effective_value('neighbor {0} ttl-security'.format(neighbor), default=''),
+ 'session_holdtime' : conf.return_effective_value('neighbor {0} session-holdtime'.format(neighbor), default='')
}
})
for neighbor in conf.list_nodes('neighbor'):
mpls_conf['ldp']['neighbors'].update({
neighbor : {
- 'password' : conf.return_value('neighbor {0} password'.format(neighbor))
+ 'password' : conf.return_value('neighbor {0} password'.format(neighbor), default=''),
+ 'ttl_security' : conf.return_value('neighbor {0} ttl-security'.format(neighbor), default=''),
+ 'session_holdtime' : conf.return_value('neighbor {0} session-holdtime'.format(neighbor), default='')
}
})
@@ -172,15 +310,15 @@ def verify(mpls):
return None
if mpls['mpls_ldp']:
- # Requre router-id
+ # Require router-id
if not mpls['router_id']:
raise ConfigError(f"MPLS ldp router-id is mandatory!")
- # Requre discovery transport-address
+ # Require discovery transport-address
if not mpls['ldp']['d_transp_ipv4'] and not mpls['ldp']['d_transp_ipv6']:
raise ConfigError(f"MPLS ldp discovery transport address is mandatory!")
- # Requre interface
+ # Require interface
if not mpls['ldp']['interfaces']:
raise ConfigError(f"MPLS ldp interface is mandatory!")
@@ -195,15 +333,24 @@ def apply(mpls):
if mpls is None:
return None
- # Set number of entries in the platform label table
+ # Set number of entries in the platform label table
if mpls['mpls_ldp']:
sysctl('net.mpls.platform_labels', '1048575')
else:
sysctl('net.mpls.platform_labels', '0')
- # Do not copy IP TTL to MPLS header
- sysctl('net.mpls.ip_ttl_propagate', '0')
-
+ # Choose whether to copy IP TTL to MPLS header TTL
+ if mpls['parameters']['no_ttl_propagation']:
+ sysctl('net.mpls.ip_ttl_propagate', '0')
+ else:
+ sysctl('net.mpls.ip_ttl_propagate', '1')
+
+ # Choose whether to limit maximum MPLS header TTL
+ if mpls['parameters']['maximum_ttl']:
+ sysctl('net.mpls.default_ttl', '%s' %(mpls['parameters']['maximum_ttl']))
+ else:
+ sysctl('net.mpls.default_ttl', '255')
+
# Allow mpls on interfaces
operate_mpls_on_intfc(mpls['ldp']['interfaces'], 1)
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 87c7754f3..68c554360 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -23,8 +23,9 @@ from sys import exit
from vyos.config import Config
from vyos.template import render
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
from vyos.util import call, get_half_cpus
-from vyos.validate import is_ipv4
from vyos import ConfigError
from vyos import airbag
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index a520120f8..2260b3fe1 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -23,7 +23,7 @@ from vyos.configdict import get_accel_dict
from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
from vyos.util import call
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -57,13 +57,13 @@ def verify(pppoe):
raise ConfigError('At least one listen interface must be defined!')
# local ippool and gateway settings config checks
- if not (vyos_dict_search('client_ip_pool.subnet', pppoe) or
- (vyos_dict_search('client_ip_pool.start', pppoe) and
- vyos_dict_search('client_ip_pool.stop', pppoe))):
+ if not (dict_search('client_ip_pool.subnet', pppoe) or
+ (dict_search('client_ip_pool.start', pppoe) and
+ dict_search('client_ip_pool.stop', pppoe))):
print('Warning: No PPPoE client pool defined')
- if vyos_dict_search('authentication.radius.dynamic_author.server', pppoe):
- if not vyos_dict_search('authentication.radius.dynamic_author.key', pppoe):
+ if dict_search('authentication.radius.dynamic_author.server', pppoe):
+ if not dict_search('authentication.radius.dynamic_author.key', pppoe):
raise ConfigError('DA/CoE server key required!')
return None
@@ -75,7 +75,7 @@ def generate(pppoe):
render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe, trim_blocks=True)
- if vyos_dict_search('authentication.mode', pppoe) == 'local':
+ if dict_search('authentication.mode', pppoe) == 'local':
render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
pppoe, trim_blocks=True, permission=0o640)
else:
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 117bf0274..3990e5735 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -22,8 +22,9 @@ from vyos.config import Config
from vyos.configverify import verify_vrf
from vyos.snmpv3_hashgen import plaintext_to_md5, plaintext_to_sha1, random
from vyos.template import render
+from vyos.template import is_ipv4
from vyos.util import call, chmod_755
-from vyos.validate import is_ipv4, is_addr_assigned
+from vyos.validate import is_addr_assigned
from vyos.version import get_version_data
from vyos import ConfigError, airbag
airbag.enable()
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index a19fa72d8..e07745963 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -16,15 +16,15 @@
import os
-from netifaces import interfaces
from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
-from vyos import ConfigError
+from vyos.configverify import verify_vrf
from vyos.util import call
from vyos.template import render
from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -54,9 +54,7 @@ def verify(ssh):
if not ssh:
return None
- if 'vrf' in ssh.keys() and ssh['vrf'] not in interfaces():
- raise ConfigError('VRF "{vrf}" does not exist'.format(**ssh))
-
+ verify_vrf(ssh)
return None
def generate(ssh):
diff --git a/src/conf_mode/system-options.py b/src/conf_mode/system-options.py
index 6ac35a4ab..22765cef7 100755
--- a/src/conf_mode/system-options.py
+++ b/src/conf_mode/system-options.py
@@ -21,7 +21,7 @@ from sys import exit
from vyos.config import Config
from vyos.template import render
-from vyos.util import call
+from vyos.util import cmd
from vyos.validate import is_addr_assigned
from vyos import ConfigError
from vyos import airbag
@@ -71,9 +71,9 @@ def generate(options):
def apply(options):
# Beep action
if 'beep_if_fully_booted' in options.keys():
- call('systemctl enable vyos-beep.service')
+ cmd('systemctl enable vyos-beep.service')
else:
- call('systemctl disable vyos-beep.service')
+ cmd('systemctl disable vyos-beep.service')
# Ctrl-Alt-Delete action
if os.path.exists(systemd_action_file):
@@ -100,6 +100,13 @@ def apply(options):
else:
f.write('0')
+ # tuned - performance tuning
+ if 'performance' in options:
+ cmd('systemctl enable tuned.service')
+ cmd('tuned-adm profile network-{performance}'.format(**options))
+ else:
+ cmd('systemctl disable tuned.service')
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/system-wifi-regdom.py b/src/conf_mode/system-wifi-regdom.py
deleted file mode 100755
index 874f93923..000000000
--- a/src/conf_mode/system-wifi-regdom.py
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019-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 os
-
-from copy import deepcopy
-from sys import exit
-
-from vyos.config import Config
-from vyos import ConfigError
-from vyos.template import render
-
-from vyos import airbag
-airbag.enable()
-
-config_80211_file='/etc/modprobe.d/cfg80211.conf'
-config_crda_file='/etc/default/crda'
-
-default_config_data = {
- 'regdom' : '',
- 'deleted' : False
-}
-
-def get_config(config=None):
- regdom = deepcopy(default_config_data)
- if config:
- conf = config
- else:
- conf = Config()
- base = ['system', 'wifi-regulatory-domain']
-
- # Check if interface has been removed
- if not conf.exists(base):
- regdom['deleted'] = True
- return regdom
- else:
- regdom['regdom'] = conf.return_value(base)
-
- return regdom
-
-def verify(regdom):
- if regdom['deleted']:
- return None
-
- if not regdom['regdom']:
- raise ConfigError("Wireless regulatory domain is mandatory.")
-
- return None
-
-def generate(regdom):
- print("Changing the wireless regulatory domain requires a system reboot.")
-
- if regdom['deleted']:
- if os.path.isfile(config_80211_file):
- os.unlink(config_80211_file)
-
- if os.path.isfile(config_crda_file):
- os.unlink(config_crda_file)
-
- return None
-
- render(config_80211_file, 'wifi/cfg80211.conf.tmpl', regdom)
- render(config_crda_file, 'wifi/crda.tmpl', regdom)
- return None
-
-def apply(regdom):
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py
index cac95afe2..56e195b6a 100755
--- a/src/conf_mode/tftp_server.py
+++ b/src/conf_mode/tftp_server.py
@@ -25,9 +25,9 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render
+from vyos.template import is_ipv4
from vyos.util import call
from vyos.util import chmod_755
-from vyos.validate import is_ipv4
from vyos.validate import is_addr_assigned
from vyos.xml import defaults
from vyos import ConfigError
diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py
index da51b0d06..80eb8daf2 100755
--- a/src/conf_mode/vpn_l2tp.py
+++ b/src/conf_mode/vpn_l2tp.py
@@ -25,10 +25,10 @@ from time import sleep
from ipaddress import ip_network
from vyos.config import Config
+from vyos.template import is_ipv4
+from vyos.template import render
from vyos.util import call, get_half_cpus
-from vyos.validate import is_ipv4
from vyos import ConfigError
-from vyos.template import render
from vyos import airbag
airbag.enable()
@@ -100,7 +100,8 @@ def get_config(config=None):
if conf.exists(['authentication', 'mode']):
l2tp['auth_mode'] = conf.return_value(['authentication', 'mode'])
- if conf.exists(['authentication', 'protocols']):
+ if conf.exists(['authentication', 'require']):
+ l2tp['auth_proto'] = []
auth_mods = {
'pap': 'auth_pap',
'chap': 'auth_chap_md5',
@@ -108,7 +109,7 @@ def get_config(config=None):
'mschap-v2': 'auth_mschap_v2'
}
- for proto in conf.return_values(['authentication', 'protocols']):
+ for proto in conf.return_values(['authentication', 'require']):
l2tp['auth_proto'].append(auth_mods[proto])
if conf.exists(['authentication', 'mppe']):
@@ -161,6 +162,9 @@ def get_config(config=None):
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
+ if conf.exists(['disable-accounting']):
+ radius['acct_port'] = '0'
+
if conf.exists(['fail-time']):
radius['fail_time'] = conf.return_value(['fail-time'])
diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py
index 306d05c60..3125ee9d0 100755
--- a/src/conf_mode/vpn_pptp.py
+++ b/src/conf_mode/vpn_pptp.py
@@ -121,6 +121,9 @@ def get_config(config=None):
conf.set_level(base_path + ['authentication', 'radius', 'server', server])
+ if conf.exists(['disable-accounting']):
+ radius['acct_port'] = '0'
+
if conf.exists(['fail-time']):
radius['fail_time'] = conf.return_value(['fail-time'])
diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py
index 2597ba42f..1b2b80ce5 100755
--- a/src/conf_mode/vpn_sstp.py
+++ b/src/conf_mode/vpn_sstp.py
@@ -23,7 +23,7 @@ from vyos.configdict import get_accel_dict
from vyos.configverify import verify_accel_ppp_base_service
from vyos.template import render
from vyos.util import call
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -56,21 +56,21 @@ def verify(sstp):
#
# SSL certificate checks
#
- tmp = vyos_dict_search('ssl.ca_cert_file', sstp)
+ tmp = dict_search('ssl.ca_cert_file', sstp)
if not tmp:
raise ConfigError(f'SSL CA certificate file required!')
else:
if not os.path.isfile(tmp):
raise ConfigError(f'SSL CA certificate "{tmp}" does not exist!')
- tmp = vyos_dict_search('ssl.cert_file', sstp)
+ tmp = dict_search('ssl.cert_file', sstp)
if not tmp:
raise ConfigError(f'SSL public key file required!')
else:
if not os.path.isfile(tmp):
raise ConfigError(f'SSL public key "{tmp}" does not exist!')
- tmp = vyos_dict_search('ssl.key_file', sstp)
+ tmp = dict_search('ssl.key_file', sstp)
if not tmp:
raise ConfigError(f'SSL private key file required!')
else:
@@ -84,7 +84,7 @@ def generate(sstp):
# accel-cmd reload doesn't work so any change results in a restart of the daemon
render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp, trim_blocks=True)
- if vyos_dict_search('authentication.mode', sstp) == 'local':
+ if dict_search('authentication.mode', sstp) == 'local':
render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl',
sstp, trim_blocks=True, permission=0o640)
else:
diff --git a/src/etc/udev/rules.d/99-vyos-wwan.rules b/src/etc/udev/rules.d/99-vyos-wwan.rules
deleted file mode 100644
index 67f30a3dd..000000000
--- a/src/etc/udev/rules.d/99-vyos-wwan.rules
+++ /dev/null
@@ -1,11 +0,0 @@
-ACTION!="add|change", GOTO="mbim_to_qmi_rules_end"
-
-SUBSYSTEM!="usb", GOTO="mbim_to_qmi_rules_end"
-
-# ignore any device with only one configuration
-ATTR{bNumConfigurations}=="1", GOTO="mbim_to_qmi_rules_end"
-
-# force Sierra Wireless MC7710 to configuration #1
-ATTR{idVendor}=="1199",ATTR{idProduct}=="68a2",ATTR{bConfigurationValue}="1"
-
-LABEL="mbim_to_qmi_rules_end"
diff --git a/src/migration-scripts/interfaces/13-to-14 b/src/migration-scripts/interfaces/13-to-14
new file mode 100755
index 000000000..fc6d7f443
--- /dev/null
+++ b/src/migration-scripts/interfaces/13-to-14
@@ -0,0 +1,61 @@
+#!/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/>.
+
+# T3043: rename Wireless interface security mode 'both' to 'wpa+wpa2'
+# T3043: move "system wifi-regulatory-domain" to indicidual wireless interface
+
+import os
+
+from sys import exit, argv
+from vyos.configtree import ConfigTree
+
+if __name__ == '__main__':
+ if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+ file_name = argv[1]
+ with open(file_name, 'r') as f:
+ config_file = f.read()
+
+ config = ConfigTree(config_file)
+ base = ['interfaces', 'wireless']
+ if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+ country_code = ''
+ cc_cli = ['system', 'wifi-regulatory-domain']
+ if config.exists(cc_cli):
+ country_code = config.return_value(cc_cli)
+ config.delete(cc_cli)
+
+ for wifi in config.list_nodes(base):
+ sec_mode = base + [wifi, 'security', 'wpa', 'mode']
+ if config.exists(sec_mode):
+ mode = config.return_value(sec_mode)
+ if mode == 'both':
+ config.set(sec_mode, value='wpa+wpa2', replace=True)
+
+ if country_code:
+ config.set(base + [wifi, 'country-code'], value=country_code)
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py
index d4dae3cd1..de41274a7 100755
--- a/src/op_mode/show_interfaces.py
+++ b/src/op_mode/show_interfaces.py
@@ -217,8 +217,9 @@ def run_show_intf_brief(ifnames, iftypes, vif, vrrp):
admin_state = interface.get_admin_state()
intf = [interface.ifname,]
- oper = ['u', ] if oper_state in ('up', 'unknown') else ['A', ]
- admin = ['u', ] if oper_state in ('up', 'unknown') else ['D', ]
+
+ oper = ['u', ] if oper_state in ('up', 'unknown') else ['D', ]
+ admin = ['u', ] if admin_state in ('up', 'unknown') else ['A', ]
addrs = [_ for _ in interface.get_addr() if not _.startswith('fe80::')] or ['-', ]
descs = list(split_text(interface.get_alias(),0))
@@ -226,8 +227,8 @@ def run_show_intf_brief(ifnames, iftypes, vif, vrrp):
i = intf.pop(0) if intf else ''
a = addrs.pop(0) if addrs else ''
d = descs.pop(0) if descs else ''
- s = [oper.pop(0)] if oper else []
- l = [admin.pop(0)] if admin else []
+ s = [admin.pop(0)] if admin else []
+ l = [oper.pop(0)] if oper else []
if len(a) < 33:
print(format1 % (i, a, '/'.join(s+l), d))
else:
diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd
index 59dbeda17..4c4bb036e 100755
--- a/src/services/vyos-hostsd
+++ b/src/services/vyos-hostsd
@@ -589,7 +589,7 @@ if __name__ == '__main__':
socket = context.socket(zmq.REP)
# Set the right permissions on the socket, then change it back
- o_mask = os.umask(0o007)
+ o_mask = os.umask(0o000)
socket.bind(SOCKET_PATH)
os.umask(o_mask)
diff --git a/src/tests/helper.py b/src/tests/helper.py
index a7e4f201c..f7033148a 100644
--- a/src/tests/helper.py
+++ b/src/tests/helper.py
@@ -13,13 +13,10 @@
#
# 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 importlib.util
-
def prepare_module(file_path='', module_name=''):
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
diff --git a/src/tests/test_config_parser.py b/src/tests/test_config_parser.py
index 5b922e2dd..6e0a071f8 100644
--- a/src/tests/test_config_parser.py
+++ b/src/tests/test_config_parser.py
@@ -15,11 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import tempfile
-import unittest
+import vyos.configtree
from unittest import TestCase
-import vyos.configtree
class TestConfigParser(TestCase):
def setUp(self):
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
new file mode 100644
index 000000000..ad7e053db
--- /dev/null
+++ b/src/tests/test_configverify.py
@@ -0,0 +1,38 @@
+#!/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/>.
+
+from unittest import TestCase
+from vyos.configverify import verify_diffie_hellman_length
+from vyos.util import cmd
+
+dh_file = '/tmp/dh.pem'
+
+class TestDictSearch(TestCase):
+ def setUp(self):
+ pass
+
+ def test_dh_key_none(self):
+ self.assertFalse(verify_diffie_hellman_length('/tmp/non_existing_file', '1024'))
+
+ def test_dh_key_256(self):
+ key_len = '256'
+ cmd(f'openssl dhparam -out {dh_file} {key_len}')
+ self.assertTrue(verify_diffie_hellman_length(dh_file, key_len))
+
+ def test_dh_key_512(self):
+ key_len = '512'
+ cmd(f'openssl dhparam -out {dh_file} {key_len}')
+ self.assertTrue(verify_diffie_hellman_length(dh_file, key_len))
diff --git a/src/tests/test_vyos_dict_search.py b/src/tests/test_dict_search.py
index ef338d46f..6a0fc74ad 100644
--- a/src/tests/test_vyos_dict_search.py
+++ b/src/tests/test_dict_search.py
@@ -14,10 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import unittest
from unittest import TestCase
-
-from vyos.util import vyos_dict_search
+from vyos.util import dict_search
data = {
'string': 'fooo',
@@ -31,29 +29,29 @@ class TestDictSearch(TestCase):
pass
def test_non_existing_keys(self):
- """ TestDictSearch: Return False when querying for non-existent key """
- self.assertFalse(vyos_dict_search('non_existing', data))
+ # TestDictSearch: Return False when querying for non-existent key
+ self.assertFalse(dict_search('non_existing', data))
def test_string(self):
- """ TestDictSearch: Return value when querying string """
- self.assertEqual(vyos_dict_search('string', data), data['string'])
+ # TestDictSearch: Return value when querying string
+ self.assertEqual(dict_search('string', data), data['string'])
def test_list(self):
- """ TestDictSearch: Return list items when querying list """
- self.assertEqual(vyos_dict_search('list', data), data['list'])
+ # TestDictSearch: Return list items when querying list
+ self.assertEqual(dict_search('list', data), data['list'])
def test_dict_key_value(self):
- """ TestDictSearch: Return dictionary keys value when value is present """
- self.assertEqual(vyos_dict_search('dict.key_2', data), data['dict']['key_2'])
+ # TestDictSearch: Return dictionary keys value when value is present
+ self.assertEqual(dict_search('dict.key_2', data), data['dict']['key_2'])
def test_nested_dict_key_value(self):
- """ TestDictSearch: Return string value of last key when querying for a nested string """
- self.assertEqual(vyos_dict_search('nested.string', data), data['nested']['string'])
+ # TestDictSearch: Return string value of last key when querying for a nested string
+ self.assertEqual(dict_search('nested.string', data), data['nested']['string'])
def test_nested_dict_key_empty(self):
- """ TestDictSearch: Return False when querying for a nested string whose last key is empty """
- self.assertFalse(vyos_dict_search('nested.empty', data))
+ # TestDictSearch: Return False when querying for a nested string whose last key is empty
+ self.assertFalse(dict_search('nested.empty', data))
def test_nested_list(self):
- """ TestDictSearch: Return list items when querying nested list """
- self.assertEqual(vyos_dict_search('nested.list', data), data['nested']['list'])
+ # TestDictSearch: Return list items when querying nested list
+ self.assertEqual(dict_search('nested.list', data), data['nested']['list'])
diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py
new file mode 100755
index 000000000..43c80dc76
--- /dev/null
+++ b/src/tests/test_find_device_file.py
@@ -0,0 +1,35 @@
+#!/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/>.
+
+from unittest import TestCase
+from vyos.util import find_device_file
+
+class TestDeviceFile(TestCase):
+ """ used to find USB devices on target """
+ def setUp(self):
+ pass
+
+ def test_null(self):
+ self.assertEqual(find_device_file('null'), '/dev/null')
+
+ def test_zero(self):
+ self.assertEqual(find_device_file('zero'), '/dev/zero')
+
+ def test_input_event(self):
+ self.assertEqual(find_device_file('event0'), '/dev/input/event0')
+
+ def test_non_existing(self):
+ self.assertFalse(find_device_file('vyos'))
diff --git a/src/tests/test_jinja_filters.py b/src/tests/test_jinja_filters.py
new file mode 100644
index 000000000..8a7241fe3
--- /dev/null
+++ b/src/tests/test_jinja_filters.py
@@ -0,0 +1,69 @@
+#!/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/>.
+
+from unittest import TestCase
+
+from ipaddress import ip_network
+from vyos.template import address_from_cidr
+from vyos.template import netmask_from_cidr
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+from vyos.template import first_host_address
+from vyos.template import last_host_address
+from vyos.template import inc_ip
+
+class TestTeamplteHelpers(TestCase):
+ def setUp(self):
+ pass
+
+ def test_helpers_from_cidr(self):
+ network_v4 = '192.0.2.0/26'
+ self.assertEqual(address_from_cidr(network_v4), str(ip_network(network_v4).network_address))
+ self.assertEqual(netmask_from_cidr(network_v4), str(ip_network(network_v4).netmask))
+
+ def test_helpers_ipv4(self):
+ self.assertTrue(is_ipv4('192.0.2.1'))
+ self.assertTrue(is_ipv4('192.0.2.0/24'))
+ self.assertTrue(is_ipv4('192.0.2.1/32'))
+ self.assertTrue(is_ipv4('10.255.1.2'))
+ self.assertTrue(is_ipv4('10.255.1.0/24'))
+ self.assertTrue(is_ipv4('10.255.1.2/32'))
+ self.assertFalse(is_ipv4('2001:db8::'))
+ self.assertFalse(is_ipv4('2001:db8::1'))
+ self.assertFalse(is_ipv4('2001:db8::/64'))
+
+ def test_helpers_ipv6(self):
+ self.assertFalse(is_ipv6('192.0.2.1'))
+ self.assertFalse(is_ipv6('192.0.2.0/24'))
+ self.assertFalse(is_ipv6('192.0.2.1/32'))
+ self.assertFalse(is_ipv6('10.255.1.2'))
+ self.assertFalse(is_ipv6('10.255.1.0/24'))
+ self.assertFalse(is_ipv6('10.255.1.2/32'))
+ self.assertTrue(is_ipv6('2001:db8::'))
+ self.assertTrue(is_ipv6('2001:db8::1'))
+ self.assertTrue(is_ipv6('2001:db8::1/64'))
+ self.assertTrue(is_ipv6('2001:db8::/32'))
+ self.assertTrue(is_ipv6('2001:db8::/64'))
+
+ def test_helpers_first_host_address(self):
+ self.assertEqual(first_host_address('10.0.0.0/24'), '10.0.0.1')
+ self.assertEqual(first_host_address('10.0.0.128/25'), '10.0.0.129')
+ self.assertEqual(first_host_address('10.0.0.200/29'), '10.0.0.201')
+
+ self.assertEqual(first_host_address('2001:db8::/64'), '2001:db8::')
+ self.assertEqual(first_host_address('2001:db8::/112'), '2001:db8::')
+ self.assertEqual(first_host_address('2001:db8::10/112'), '2001:db8::10')
+ self.assertEqual(first_host_address('2001:db8::100/112'), '2001:db8::100')
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
new file mode 100644
index 000000000..6dc2f075e
--- /dev/null
+++ b/src/tests/test_template.py
@@ -0,0 +1,46 @@
+#!/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 vyos.template
+from unittest import TestCase
+
+class TestVyOSTemplate(TestCase):
+ def setUp(self):
+ pass
+
+ def test_is_ip(self):
+ self.assertTrue(vyos.template.is_ip('192.0.2.1'))
+ self.assertTrue(vyos.template.is_ip('2001:db8::1'))
+ self.assertFalse(vyos.template.is_ip('VyOS'))
+
+ def test_is_ipv4(self):
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.1'))
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.0/24'))
+ self.assertTrue(vyos.template.is_ipv4('192.0.2.1/32'))
+
+ self.assertFalse(vyos.template.is_ipv4('2001:db8::1'))
+ self.assertFalse(vyos.template.is_ipv4('2001:db8::/64'))
+ self.assertFalse(vyos.template.is_ipv4('VyOS'))
+
+ def test_is_ipv6(self):
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::1'))
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::/64'))
+ self.assertTrue(vyos.template.is_ipv6('2001:db8::1/64'))
+
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.1'))
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.0/24'))
+ self.assertFalse(vyos.template.is_ipv6('192.0.2.1/32'))
+ self.assertFalse(vyos.template.is_ipv6('VyOS'))
diff --git a/src/tests/test_util.py b/src/tests/test_util.py
index 09bf947b8..f7405cbde 100644
--- a/src/tests/test_util.py
+++ b/src/tests/test_util.py
@@ -14,10 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import unittest
from unittest import TestCase
-
-import vyos.util
+from vyos.util import mangle_dict_keys
class TestVyOSUtil(TestCase):
@@ -27,6 +25,6 @@ class TestVyOSUtil(TestCase):
def test_key_mangline(self):
data = {"foo-bar": {"baz-quux": None}}
expected_data = {"foo_bar": {"baz_quux": None}}
- new_data = vyos.util.mangle_dict_keys(data, '-', '_')
+ new_data = mangle_dict_keys(data, '-', '_')
self.assertEqual(new_data, expected_data)
diff --git a/src/tests/test_validate.py b/src/tests/test_validate.py
new file mode 100644
index 000000000..226e856a3
--- /dev/null
+++ b/src/tests/test_validate.py
@@ -0,0 +1,41 @@
+#!/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 vyos.validate
+from unittest import TestCase
+
+class TestVyOSValidate(TestCase):
+ def setUp(self):
+ pass
+
+ def test_is_ipv6_link_local(self):
+ self.assertFalse(vyos.validate.is_ipv6_link_local('169.254.0.1'))
+ self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::'))
+ self.assertTrue(vyos.validate.is_ipv6_link_local('fe80::affe:1'))
+ self.assertFalse(vyos.validate.is_ipv6_link_local('2001:db8::'))
+ self.assertFalse(vyos.validate.is_ipv6_link_local('VyOS'))
+
+ def test_is_ipv6_link_local(self):
+ self.assertTrue(vyos.validate.is_loopback_addr('127.0.0.1'))
+ self.assertTrue(vyos.validate.is_loopback_addr('127.0.1.1'))
+ self.assertTrue(vyos.validate.is_loopback_addr('127.1.1.1'))
+ self.assertTrue(vyos.validate.is_loopback_addr('::1'))
+
+ self.assertFalse(vyos.validate.is_loopback_addr('::2'))
+ self.assertFalse(vyos.validate.is_loopback_addr('192.0.2.1'))
+
+
+
diff --git a/src/validators/ipv4-range b/src/validators/ipv4-range
index ae3f3f163..cc59039f1 100755
--- a/src/validators/ipv4-range
+++ b/src/validators/ipv4-range
@@ -28,6 +28,8 @@ if [[ "$1" =~ "-" ]]; then
if [ $start -ge $stop ]; then
exit 1
fi
+
+ exit 0
fi
-exit 0
+exit 1
diff --git a/vyos-configtest b/vyos-configtest
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vyos-configtest